@fictjs/runtime 0.0.10 → 0.0.12

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/index.cjs CHANGED
@@ -302,7 +302,7 @@ function handleError(err, info, startRoot) {
302
302
  }
303
303
  }
304
304
  }
305
- throw error;
305
+ return false;
306
306
  }
307
307
  function handleSuspend(token, startRoot) {
308
308
  let root = startRoot ?? currentRoot;
@@ -348,6 +348,11 @@ var isInTransition = false;
348
348
  var enqueueMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : (fn) => {
349
349
  Promise.resolve().then(fn);
350
350
  };
351
+ var inCleanup = false;
352
+ var SIGNAL_MARKER = Symbol.for("fict:signal");
353
+ var COMPUTED_MARKER = Symbol.for("fict:computed");
354
+ var EFFECT_MARKER = Symbol.for("fict:effect");
355
+ var EFFECT_SCOPE_MARKER = Symbol.for("fict:effectScope");
351
356
  function link(dep, sub, version) {
352
357
  const prevDep = sub.depsTail;
353
358
  if (prevDep !== void 0 && prevDep.dep === dep) return;
@@ -471,13 +476,21 @@ function checkDirty(firstLink, sub) {
471
476
  dirty = true;
472
477
  }
473
478
  } else if ((depFlags & MutablePending) === MutablePending) {
474
- if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
475
- stack = { value: link2, prev: stack };
479
+ if (!dep.deps) {
480
+ const nextDep = link2.nextDep;
481
+ if (nextDep !== void 0) {
482
+ link2 = nextDep;
483
+ continue;
484
+ }
485
+ } else {
486
+ if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
487
+ stack = { value: link2, prev: stack };
488
+ }
489
+ link2 = dep.deps;
490
+ sub = dep;
491
+ ++checkDepth;
492
+ continue;
476
493
  }
477
- link2 = dep.deps;
478
- sub = dep;
479
- ++checkDepth;
480
- continue;
481
494
  }
482
495
  if (!dirty) {
483
496
  const nextDep = link2.nextDep;
@@ -555,8 +568,12 @@ function disposeNode(node) {
555
568
  node.depsTail = void 0;
556
569
  node.flags = 0;
557
570
  purgeDeps(node);
558
- const sub = node.subs;
559
- if (sub !== void 0) unlink(sub, node);
571
+ let sub = node.subs;
572
+ while (sub !== void 0) {
573
+ const next = sub.nextSub;
574
+ unlink(sub);
575
+ sub = next;
576
+ }
560
577
  }
561
578
  function updateSignal(s) {
562
579
  s.flags = Mutable;
@@ -593,7 +610,15 @@ function updateComputed(c) {
593
610
  }
594
611
  function runEffect(e) {
595
612
  const flags = e.flags;
596
- if (flags & Dirty || flags & Pending && e.deps && checkDirty(e.deps, e)) {
613
+ if (flags & Dirty) {
614
+ if (e.runCleanup) {
615
+ inCleanup = true;
616
+ try {
617
+ e.runCleanup();
618
+ } finally {
619
+ inCleanup = false;
620
+ }
621
+ }
597
622
  ++cycle;
598
623
  effectRunDevtools(e);
599
624
  e.depsTail = void 0;
@@ -610,6 +635,35 @@ function runEffect(e) {
610
635
  e.flags = Watching;
611
636
  throw err;
612
637
  }
638
+ } else if (flags & Pending && e.deps) {
639
+ if (e.runCleanup) {
640
+ inCleanup = true;
641
+ try {
642
+ e.runCleanup();
643
+ } finally {
644
+ inCleanup = false;
645
+ }
646
+ }
647
+ if (checkDirty(e.deps, e)) {
648
+ ++cycle;
649
+ effectRunDevtools(e);
650
+ e.depsTail = void 0;
651
+ e.flags = WatchingRunning;
652
+ const prevSub = activeSub;
653
+ activeSub = e;
654
+ try {
655
+ e.fn();
656
+ activeSub = prevSub;
657
+ e.flags = Watching;
658
+ purgeDeps(e);
659
+ } catch (err) {
660
+ activeSub = prevSub;
661
+ e.flags = Watching;
662
+ throw err;
663
+ }
664
+ } else {
665
+ e.flags = Watching;
666
+ }
613
667
  } else {
614
668
  e.flags = Watching;
615
669
  }
@@ -688,7 +742,9 @@ function signal(initialValue) {
688
742
  __id: void 0
689
743
  };
690
744
  registerSignalDevtools(initialValue, s);
691
- return signalOper.bind(s);
745
+ const accessor = signalOper.bind(s);
746
+ accessor[SIGNAL_MARKER] = true;
747
+ return accessor;
692
748
  }
693
749
  function signalOper(value) {
694
750
  if (arguments.length > 0) {
@@ -705,7 +761,7 @@ function signalOper(value) {
705
761
  return;
706
762
  }
707
763
  const flags = this.flags;
708
- if (flags & Dirty) {
764
+ if (flags & Dirty && !inCleanup) {
709
765
  if (updateSignal(this)) {
710
766
  const subs = this.subs;
711
767
  if (subs !== void 0) shallowPropagate(subs);
@@ -733,6 +789,7 @@ function computed(getter) {
733
789
  getter
734
790
  };
735
791
  const bound = computedOper.bind(c);
792
+ bound[COMPUTED_MARKER] = true;
736
793
  return bound;
737
794
  }
738
795
  function computedOper() {
@@ -785,7 +842,35 @@ function effect(fn) {
785
842
  activeSub = prevSub;
786
843
  e.flags &= ~Running;
787
844
  }
788
- return effectOper.bind(e);
845
+ const disposer = effectOper.bind(e);
846
+ disposer[EFFECT_MARKER] = true;
847
+ return disposer;
848
+ }
849
+ function effectWithCleanup(fn, cleanupRunner) {
850
+ const e = {
851
+ fn,
852
+ subs: void 0,
853
+ subsTail: void 0,
854
+ deps: void 0,
855
+ depsTail: void 0,
856
+ flags: WatchingRunning,
857
+ runCleanup: cleanupRunner,
858
+ __id: void 0
859
+ };
860
+ registerEffectDevtools(e);
861
+ const prevSub = activeSub;
862
+ if (prevSub !== void 0) link(e, prevSub, 0);
863
+ activeSub = e;
864
+ try {
865
+ effectRunDevtools(e);
866
+ fn();
867
+ } finally {
868
+ activeSub = prevSub;
869
+ e.flags &= ~Running;
870
+ }
871
+ const disposer = effectOper.bind(e);
872
+ disposer[EFFECT_MARKER] = true;
873
+ return disposer;
789
874
  }
790
875
  function effectOper() {
791
876
  disposeNode(this);
@@ -800,7 +885,9 @@ function effectScope(fn) {
800
885
  } finally {
801
886
  activeSub = prevSub;
802
887
  }
803
- return effectScopeOper.bind(e);
888
+ const disposer = effectScopeOper.bind(e);
889
+ disposer[EFFECT_SCOPE_MARKER] = true;
890
+ return disposer;
804
891
  }
805
892
  function effectScopeOper() {
806
893
  disposeNode(this);
@@ -1032,8 +1119,11 @@ var $memo = createMemo;
1032
1119
  function createEffect(fn) {
1033
1120
  let cleanups = [];
1034
1121
  const rootForError = getCurrentRoot();
1035
- const run = () => {
1122
+ const doCleanup = () => {
1036
1123
  runCleanupList(cleanups);
1124
+ cleanups = [];
1125
+ };
1126
+ const run = () => {
1037
1127
  const bucket = [];
1038
1128
  withEffectCleanups(bucket, () => {
1039
1129
  try {
@@ -1050,7 +1140,7 @@ function createEffect(fn) {
1050
1140
  });
1051
1141
  cleanups = bucket;
1052
1142
  };
1053
- const disposeEffect = effect(run);
1143
+ const disposeEffect = effectWithCleanup(run, doCleanup);
1054
1144
  const teardown = () => {
1055
1145
  runCleanupList(cleanups);
1056
1146
  disposeEffect();
@@ -1062,11 +1152,13 @@ var $effect = createEffect;
1062
1152
  function createRenderEffect(fn) {
1063
1153
  let cleanup;
1064
1154
  const rootForError = getCurrentRoot();
1065
- const run = () => {
1155
+ const doCleanup = () => {
1066
1156
  if (cleanup) {
1067
1157
  cleanup();
1068
1158
  cleanup = void 0;
1069
1159
  }
1160
+ };
1161
+ const run = () => {
1070
1162
  try {
1071
1163
  const maybeCleanup = fn();
1072
1164
  if (typeof maybeCleanup === "function") {
@@ -1080,7 +1172,7 @@ function createRenderEffect(fn) {
1080
1172
  throw err;
1081
1173
  }
1082
1174
  };
1083
- const disposeEffect = effect(run);
1175
+ const disposeEffect = effectWithCleanup(run, doCleanup);
1084
1176
  const teardown = () => {
1085
1177
  if (cleanup) {
1086
1178
  cleanup();
@@ -1612,9 +1704,6 @@ function mergeProps(...sources) {
1612
1704
  };
1613
1705
  return new Proxy({}, {
1614
1706
  get(_, prop) {
1615
- if (typeof prop === "symbol") {
1616
- return void 0;
1617
- }
1618
1707
  for (let i = validSources.length - 1; i >= 0; i--) {
1619
1708
  const src = validSources[i];
1620
1709
  const raw = resolveSource(src);
@@ -1687,8 +1776,8 @@ function startTransition(fn) {
1687
1776
  function useTransition() {
1688
1777
  const pending = signal(false);
1689
1778
  const start = (fn) => {
1690
- pending(true);
1691
1779
  startTransition(() => {
1780
+ pending(true);
1692
1781
  try {
1693
1782
  fn();
1694
1783
  } finally {
@@ -1933,7 +2022,8 @@ function applyRef(el, value) {
1933
2022
  if (typeof value === "function") {
1934
2023
  const refFn = value;
1935
2024
  refFn(el);
1936
- if (getCurrentRoot()) {
2025
+ const root = getCurrentRoot();
2026
+ if (root) {
1937
2027
  registerRootCleanup(() => {
1938
2028
  refFn(null);
1939
2029
  });
@@ -1941,7 +2031,8 @@ function applyRef(el, value) {
1941
2031
  } else if (value && typeof value === "object" && "current" in value) {
1942
2032
  const refObj = value;
1943
2033
  refObj.current = el;
1944
- if (getCurrentRoot()) {
2034
+ const root = getCurrentRoot();
2035
+ if (root) {
1945
2036
  registerRootCleanup(() => {
1946
2037
  refObj.current = null;
1947
2038
  });
@@ -2352,8 +2443,6 @@ function reconcileArrays(parentNode, a, b) {
2352
2443
  }
2353
2444
  }
2354
2445
  }
2355
-
2356
- // src/list-helpers.ts
2357
2446
  function moveNodesBefore(parent, nodes, anchor) {
2358
2447
  for (let i = nodes.length - 1; i >= 0; i--) {
2359
2448
  const node = nodes[i];
@@ -2415,6 +2504,7 @@ function removeBlockRange(block) {
2415
2504
  cursor = next;
2416
2505
  }
2417
2506
  }
2507
+ var MAX_SAFE_VERSION = 9007199254740991;
2418
2508
  function createVersionedSignalAccessor(initialValue) {
2419
2509
  let current = initialValue;
2420
2510
  let version = 0;
@@ -2425,7 +2515,7 @@ function createVersionedSignalAccessor(initialValue) {
2425
2515
  return current;
2426
2516
  }
2427
2517
  current = value;
2428
- version++;
2518
+ version = version >= MAX_SAFE_VERSION ? 1 : version + 1;
2429
2519
  track2(version);
2430
2520
  }
2431
2521
  return accessor;
@@ -2481,15 +2571,21 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2481
2571
  });
2482
2572
  const root = createRootContext(hostRoot);
2483
2573
  const prevRoot = pushRoot(root);
2484
- const prevSub = setActiveSub(void 0);
2485
2574
  let nodes = [];
2575
+ let scopeDispose;
2576
+ const prevSub = setActiveSub(void 0);
2486
2577
  try {
2487
- const rendered = render2(itemSig, indexSig, key);
2488
- if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2489
- nodes = toNodeArray(rendered);
2490
- } else {
2491
- const element = createElement(rendered);
2492
- nodes = toNodeArray(element);
2578
+ scopeDispose = effectScope(() => {
2579
+ const rendered = render2(itemSig, indexSig, key);
2580
+ if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2581
+ nodes = toNodeArray(rendered);
2582
+ } else {
2583
+ const element = createElement(rendered);
2584
+ nodes = toNodeArray(element);
2585
+ }
2586
+ });
2587
+ if (scopeDispose) {
2588
+ root.cleanups.push(scopeDispose);
2493
2589
  }
2494
2590
  } finally {
2495
2591
  setActiveSub(prevSub);
@@ -2516,6 +2612,86 @@ function isNodeBetweenMarkers(node, startMarker, endMarker) {
2516
2612
  }
2517
2613
  return false;
2518
2614
  }
2615
+ function reorderBySwap(parent, first, second) {
2616
+ if (first === second) return false;
2617
+ const firstNodes = first.nodes;
2618
+ const secondNodes = second.nodes;
2619
+ if (firstNodes.length === 0 || secondNodes.length === 0) return false;
2620
+ const lastFirst = firstNodes[firstNodes.length - 1];
2621
+ const lastSecond = secondNodes[secondNodes.length - 1];
2622
+ const afterFirst = lastFirst.nextSibling;
2623
+ const afterSecond = lastSecond.nextSibling;
2624
+ moveNodesBefore(parent, firstNodes, afterSecond);
2625
+ moveNodesBefore(parent, secondNodes, afterFirst);
2626
+ return true;
2627
+ }
2628
+ function getLISIndices(sequence) {
2629
+ const predecessors = new Array(sequence.length);
2630
+ const result = [];
2631
+ for (let i = 0; i < sequence.length; i++) {
2632
+ const value = sequence[i];
2633
+ if (value < 0) {
2634
+ predecessors[i] = -1;
2635
+ continue;
2636
+ }
2637
+ let low = 0;
2638
+ let high = result.length;
2639
+ while (low < high) {
2640
+ const mid = low + high >> 1;
2641
+ if (sequence[result[mid]] < value) {
2642
+ low = mid + 1;
2643
+ } else {
2644
+ high = mid;
2645
+ }
2646
+ }
2647
+ predecessors[i] = low > 0 ? result[low - 1] : -1;
2648
+ if (low === result.length) {
2649
+ result.push(i);
2650
+ } else {
2651
+ result[low] = i;
2652
+ }
2653
+ }
2654
+ const lis = new Array(result.length);
2655
+ let k = result.length > 0 ? result[result.length - 1] : -1;
2656
+ for (let i = result.length - 1; i >= 0; i--) {
2657
+ lis[i] = k;
2658
+ k = predecessors[k];
2659
+ }
2660
+ return lis;
2661
+ }
2662
+ function reorderByLIS(parent, endMarker, prev, next) {
2663
+ const positions = /* @__PURE__ */ new Map();
2664
+ for (let i = 0; i < prev.length; i++) {
2665
+ positions.set(prev[i], i);
2666
+ }
2667
+ const sequence = new Array(next.length);
2668
+ for (let i = 0; i < next.length; i++) {
2669
+ const position = positions.get(next[i]);
2670
+ if (position === void 0) return false;
2671
+ sequence[i] = position;
2672
+ }
2673
+ const lisIndices = getLISIndices(sequence);
2674
+ if (lisIndices.length === sequence.length) return true;
2675
+ const inLIS = new Array(sequence.length).fill(false);
2676
+ for (let i = 0; i < lisIndices.length; i++) {
2677
+ inLIS[lisIndices[i]] = true;
2678
+ }
2679
+ let anchor = endMarker;
2680
+ let moved = false;
2681
+ for (let i = next.length - 1; i >= 0; i--) {
2682
+ const block = next[i];
2683
+ const nodes = block.nodes;
2684
+ if (nodes.length === 0) continue;
2685
+ if (inLIS[i]) {
2686
+ anchor = nodes[0];
2687
+ continue;
2688
+ }
2689
+ moveNodesBefore(parent, nodes, anchor);
2690
+ anchor = nodes[0];
2691
+ moved = true;
2692
+ }
2693
+ return moved;
2694
+ }
2519
2695
  function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
2520
2696
  const resolvedNeedsIndex = arguments.length >= 4 ? !!needsIndex : renderItem.length > 1;
2521
2697
  return createFineGrainedKeyedList(getItems, keyFn, renderItem, resolvedNeedsIndex);
@@ -2548,10 +2724,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2548
2724
  const prevOrderedBlocks = container.orderedBlocks;
2549
2725
  const nextOrderedBlocks = container.nextOrderedBlocks;
2550
2726
  const orderedIndexByKey = container.orderedIndexByKey;
2551
- newBlocks.clear();
2552
- nextOrderedBlocks.length = 0;
2553
- orderedIndexByKey.clear();
2554
- const createdBlocks = [];
2555
2727
  const newItems = getItems();
2556
2728
  if (newItems.length === 0) {
2557
2729
  if (oldBlocks.size > 0) {
@@ -2574,8 +2746,44 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2574
2746
  return;
2575
2747
  }
2576
2748
  const prevCount = prevOrderedBlocks.length;
2749
+ if (prevCount > 0 && newItems.length === prevCount && orderedIndexByKey.size === prevCount) {
2750
+ let stableOrder = true;
2751
+ const seen = /* @__PURE__ */ new Set();
2752
+ for (let i = 0; i < prevCount; i++) {
2753
+ const item = newItems[i];
2754
+ const key = keyFn(item, i);
2755
+ if (seen.has(key) || prevOrderedBlocks[i].key !== key) {
2756
+ stableOrder = false;
2757
+ break;
2758
+ }
2759
+ seen.add(key);
2760
+ }
2761
+ if (stableOrder) {
2762
+ for (let i = 0; i < prevCount; i++) {
2763
+ const item = newItems[i];
2764
+ const block = prevOrderedBlocks[i];
2765
+ if (block.rawItem !== item) {
2766
+ block.rawItem = item;
2767
+ block.item(item);
2768
+ }
2769
+ if (needsIndex && block.rawIndex !== i) {
2770
+ block.rawIndex = i;
2771
+ block.index(i);
2772
+ }
2773
+ }
2774
+ return;
2775
+ }
2776
+ }
2777
+ newBlocks.clear();
2778
+ nextOrderedBlocks.length = 0;
2779
+ orderedIndexByKey.clear();
2780
+ const createdBlocks = [];
2577
2781
  let appendCandidate = prevCount > 0 && newItems.length >= prevCount;
2578
2782
  const appendedBlocks = [];
2783
+ let mismatchCount = 0;
2784
+ let mismatchFirst = -1;
2785
+ let mismatchSecond = -1;
2786
+ let hasDuplicateKey = false;
2579
2787
  newItems.forEach((item, index) => {
2580
2788
  const key = keyFn(item, index);
2581
2789
  let block = oldBlocks.get(key);
@@ -2607,6 +2815,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2607
2815
  const position = orderedIndexByKey.get(key);
2608
2816
  if (position !== void 0) {
2609
2817
  appendCandidate = false;
2818
+ hasDuplicateKey = true;
2610
2819
  const prior = nextOrderedBlocks[position];
2611
2820
  if (prior && prior !== resolvedBlock) {
2612
2821
  destroyRoot(prior.root);
@@ -2623,8 +2832,17 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2623
2832
  appendCandidate = false;
2624
2833
  }
2625
2834
  }
2626
- orderedIndexByKey.set(key, nextOrderedBlocks.length);
2835
+ const nextIndex = nextOrderedBlocks.length;
2836
+ orderedIndexByKey.set(key, nextIndex);
2627
2837
  nextOrderedBlocks.push(resolvedBlock);
2838
+ if (mismatchCount < 3 && (nextIndex >= prevCount || prevOrderedBlocks[nextIndex] !== resolvedBlock)) {
2839
+ if (mismatchCount === 0) {
2840
+ mismatchFirst = nextIndex;
2841
+ } else if (mismatchCount === 1) {
2842
+ mismatchSecond = nextIndex;
2843
+ }
2844
+ mismatchCount++;
2845
+ }
2628
2846
  }
2629
2847
  if (appendCandidate && index >= prevCount) {
2630
2848
  appendedBlocks.push(resolvedBlock);
@@ -2665,7 +2883,26 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2665
2883
  }
2666
2884
  oldBlocks.clear();
2667
2885
  }
2668
- if (newBlocks.size > 0 || container.currentNodes.length > 0) {
2886
+ const canReorderInPlace = createdBlocks.length === 0 && oldBlocks.size === 0 && nextOrderedBlocks.length === prevOrderedBlocks.length;
2887
+ let skipReconcile = false;
2888
+ let updateNodeBuffer = true;
2889
+ if (canReorderInPlace && nextOrderedBlocks.length > 0 && !hasDuplicateKey) {
2890
+ if (mismatchCount === 0) {
2891
+ skipReconcile = true;
2892
+ updateNodeBuffer = false;
2893
+ } else if (mismatchCount === 2 && prevOrderedBlocks[mismatchFirst] === nextOrderedBlocks[mismatchSecond] && prevOrderedBlocks[mismatchSecond] === nextOrderedBlocks[mismatchFirst]) {
2894
+ if (reorderBySwap(
2895
+ parent,
2896
+ prevOrderedBlocks[mismatchFirst],
2897
+ prevOrderedBlocks[mismatchSecond]
2898
+ )) {
2899
+ skipReconcile = true;
2900
+ }
2901
+ } else if (reorderByLIS(parent, container.endMarker, prevOrderedBlocks, nextOrderedBlocks)) {
2902
+ skipReconcile = true;
2903
+ }
2904
+ }
2905
+ if (!skipReconcile && (newBlocks.size > 0 || container.currentNodes.length > 0)) {
2669
2906
  const prevNodes = container.currentNodes;
2670
2907
  const nextNodes = container.nextNodes;
2671
2908
  nextNodes.length = 0;
@@ -2680,6 +2917,20 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2680
2917
  reconcileArrays(parent, prevNodes, nextNodes);
2681
2918
  container.currentNodes = nextNodes;
2682
2919
  container.nextNodes = prevNodes;
2920
+ } else if (skipReconcile && updateNodeBuffer) {
2921
+ const prevNodes = container.currentNodes;
2922
+ const nextNodes = container.nextNodes;
2923
+ nextNodes.length = 0;
2924
+ nextNodes.push(container.startMarker);
2925
+ for (let i = 0; i < nextOrderedBlocks.length; i++) {
2926
+ const nodes = nextOrderedBlocks[i].nodes;
2927
+ for (let j = 0; j < nodes.length; j++) {
2928
+ nextNodes.push(nodes[j]);
2929
+ }
2930
+ }
2931
+ nextNodes.push(container.endMarker);
2932
+ container.currentNodes = nextNodes;
2933
+ container.nextNodes = prevNodes;
2683
2934
  }
2684
2935
  container.blocks = newBlocks;
2685
2936
  container.nextBlocks = oldBlocks;
@@ -3321,7 +3572,9 @@ function bindEvent(el, eventName, handler, options2) {
3321
3572
  const fn = resolveHandler();
3322
3573
  callEventHandler(fn, args[0], el);
3323
3574
  } catch (err) {
3324
- handleError(err, { source: "event", eventName }, rootRef);
3575
+ if (!handleError(err, { source: "event", eventName }, rootRef)) {
3576
+ throw err;
3577
+ }
3325
3578
  }
3326
3579
  };
3327
3580
  return () => {
@@ -4099,10 +4352,12 @@ function ErrorBoundary(props) {
4099
4352
  renderingFallback = true;
4100
4353
  try {
4101
4354
  renderValue(toView(err));
4102
- } finally {
4103
4355
  renderingFallback = false;
4356
+ props.onError?.(err);
4357
+ } catch (fallbackErr) {
4358
+ props.onError?.(err);
4359
+ throw fallbackErr;
4104
4360
  }
4105
- props.onError?.(err);
4106
4361
  return;
4107
4362
  }
4108
4363
  popRoot(prev);
@@ -4197,7 +4452,9 @@ function Suspense(props) {
4197
4452
  popRoot(prev);
4198
4453
  flushOnMount(root);
4199
4454
  destroyRoot(root);
4200
- handleError(err, { source: "render" });
4455
+ if (!handleError(err, { source: "render" }, hostRoot)) {
4456
+ throw err;
4457
+ }
4201
4458
  return;
4202
4459
  }
4203
4460
  popRoot(prev);
@@ -4227,18 +4484,26 @@ function Suspense(props) {
4227
4484
  if (thenable) {
4228
4485
  thenable.then(
4229
4486
  () => {
4230
- if (epoch !== tokenEpoch) return;
4231
- pending(Math.max(0, pending() - 1));
4232
- if (pending() === 0) {
4487
+ if (epoch !== tokenEpoch) {
4488
+ return;
4489
+ }
4490
+ const newPending = Math.max(0, pending() - 1);
4491
+ pending(newPending);
4492
+ if (newPending === 0) {
4233
4493
  switchView(props.children ?? null);
4234
4494
  onResolveMaybe();
4235
4495
  }
4236
4496
  },
4237
4497
  (err) => {
4238
- if (epoch !== tokenEpoch) return;
4239
- pending(Math.max(0, pending() - 1));
4498
+ if (epoch !== tokenEpoch) {
4499
+ return;
4500
+ }
4501
+ const newPending = Math.max(0, pending() - 1);
4502
+ pending(newPending);
4240
4503
  props.onReject?.(err);
4241
- handleError(err, { source: "render" }, hostRoot);
4504
+ if (!handleError(err, { source: "render" }, hostRoot)) {
4505
+ throw err;
4506
+ }
4242
4507
  }
4243
4508
  );
4244
4509
  return true;