@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.d.cts CHANGED
@@ -1431,6 +1431,10 @@ declare const UnitlessStyles: Set<string>;
1431
1431
  * @param a - The old array of nodes (currently in DOM)
1432
1432
  * @param b - The new array of nodes (target state)
1433
1433
  *
1434
+ * **Note:** This function may mutate the input array `a` during the swap
1435
+ * optimization (step 5a). If you need to preserve the original array,
1436
+ * pass a shallow copy: `reconcileArrays(parent, [...oldNodes], newNodes)`.
1437
+ *
1434
1438
  * @example
1435
1439
  * ```ts
1436
1440
  * const oldNodes = [node1, node2, node3]
package/dist/index.d.ts CHANGED
@@ -1431,6 +1431,10 @@ declare const UnitlessStyles: Set<string>;
1431
1431
  * @param a - The old array of nodes (currently in DOM)
1432
1432
  * @param b - The new array of nodes (target state)
1433
1433
  *
1434
+ * **Note:** This function may mutate the input array `a` during the swap
1435
+ * optimization (step 5a). If you need to preserve the original array,
1436
+ * pass a shallow copy: `reconcileArrays(parent, [...oldNodes], newNodes)`.
1437
+ *
1434
1438
  * @example
1435
1439
  * ```ts
1436
1440
  * const oldNodes = [node1, node2, node3]
package/dist/index.dev.js CHANGED
@@ -301,7 +301,7 @@ function handleError(err, info, startRoot) {
301
301
  }
302
302
  }
303
303
  }
304
- throw error;
304
+ return false;
305
305
  }
306
306
  function handleSuspend(token, startRoot) {
307
307
  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);
@@ -1034,8 +1121,11 @@ var $memo = createMemo;
1034
1121
  function createEffect(fn) {
1035
1122
  let cleanups = [];
1036
1123
  const rootForError = getCurrentRoot();
1037
- const run = () => {
1124
+ const doCleanup = () => {
1038
1125
  runCleanupList(cleanups);
1126
+ cleanups = [];
1127
+ };
1128
+ const run = () => {
1039
1129
  const bucket = [];
1040
1130
  withEffectCleanups(bucket, () => {
1041
1131
  try {
@@ -1052,7 +1142,7 @@ function createEffect(fn) {
1052
1142
  });
1053
1143
  cleanups = bucket;
1054
1144
  };
1055
- const disposeEffect = effect(run);
1145
+ const disposeEffect = effectWithCleanup(run, doCleanup);
1056
1146
  const teardown = () => {
1057
1147
  runCleanupList(cleanups);
1058
1148
  disposeEffect();
@@ -1064,11 +1154,13 @@ var $effect = createEffect;
1064
1154
  function createRenderEffect(fn) {
1065
1155
  let cleanup;
1066
1156
  const rootForError = getCurrentRoot();
1067
- const run = () => {
1157
+ const doCleanup = () => {
1068
1158
  if (cleanup) {
1069
1159
  cleanup();
1070
1160
  cleanup = void 0;
1071
1161
  }
1162
+ };
1163
+ const run = () => {
1072
1164
  try {
1073
1165
  const maybeCleanup = fn();
1074
1166
  if (typeof maybeCleanup === "function") {
@@ -1082,7 +1174,7 @@ function createRenderEffect(fn) {
1082
1174
  throw err;
1083
1175
  }
1084
1176
  };
1085
- const disposeEffect = effect(run);
1177
+ const disposeEffect = effectWithCleanup(run, doCleanup);
1086
1178
  const teardown = () => {
1087
1179
  if (cleanup) {
1088
1180
  cleanup();
@@ -1614,9 +1706,6 @@ function mergeProps(...sources) {
1614
1706
  };
1615
1707
  return new Proxy({}, {
1616
1708
  get(_, prop) {
1617
- if (typeof prop === "symbol") {
1618
- return void 0;
1619
- }
1620
1709
  for (let i = validSources.length - 1; i >= 0; i--) {
1621
1710
  const src = validSources[i];
1622
1711
  const raw = resolveSource(src);
@@ -1689,8 +1778,8 @@ function startTransition(fn) {
1689
1778
  function useTransition() {
1690
1779
  const pending = signal(false);
1691
1780
  const start = (fn) => {
1692
- pending(true);
1693
1781
  startTransition(() => {
1782
+ pending(true);
1694
1783
  try {
1695
1784
  fn();
1696
1785
  } finally {
@@ -1725,6 +1814,7 @@ function untrack2(fn) {
1725
1814
  // src/dom.ts
1726
1815
  var SVG_NS = "http://www.w3.org/2000/svg";
1727
1816
  var MATHML_NS = "http://www.w3.org/1998/Math/MathML";
1817
+ var isDev = true ? true : typeof process === "undefined" || process.env?.NODE_ENV !== "production";
1728
1818
  function render(view, container) {
1729
1819
  const root = createRootContext();
1730
1820
  const prev = pushRoot(root);
@@ -1935,18 +2025,28 @@ function applyRef(el, value) {
1935
2025
  if (typeof value === "function") {
1936
2026
  const refFn = value;
1937
2027
  refFn(el);
1938
- if (getCurrentRoot()) {
2028
+ const root = getCurrentRoot();
2029
+ if (root) {
1939
2030
  registerRootCleanup(() => {
1940
2031
  refFn(null);
1941
2032
  });
2033
+ } else if (isDev) {
2034
+ console.warn(
2035
+ "[fict] Ref applied outside of a root context. The ref cleanup (setting to null) will not run automatically. Consider using createRoot() or ensure the element is created within a component."
2036
+ );
1942
2037
  }
1943
2038
  } else if (value && typeof value === "object" && "current" in value) {
1944
2039
  const refObj = value;
1945
2040
  refObj.current = el;
1946
- if (getCurrentRoot()) {
2041
+ const root = getCurrentRoot();
2042
+ if (root) {
1947
2043
  registerRootCleanup(() => {
1948
2044
  refObj.current = null;
1949
2045
  });
2046
+ } else if (isDev) {
2047
+ console.warn(
2048
+ "[fict] Ref applied outside of a root context. The ref cleanup (setting to null) will not run automatically. Consider using createRoot() or ensure the element is created within a component."
2049
+ );
1950
2050
  }
1951
2051
  }
1952
2052
  }
@@ -2360,6 +2460,7 @@ function reconcileArrays(parentNode, a, b) {
2360
2460
  }
2361
2461
 
2362
2462
  // src/list-helpers.ts
2463
+ var isDev2 = true ? true : typeof process === "undefined" || process.env?.NODE_ENV !== "production";
2363
2464
  function moveNodesBefore(parent, nodes, anchor) {
2364
2465
  for (let i = nodes.length - 1; i >= 0; i--) {
2365
2466
  const node = nodes[i];
@@ -2421,6 +2522,7 @@ function removeBlockRange(block) {
2421
2522
  cursor = next;
2422
2523
  }
2423
2524
  }
2525
+ var MAX_SAFE_VERSION = 9007199254740991;
2424
2526
  function createVersionedSignalAccessor(initialValue) {
2425
2527
  let current = initialValue;
2426
2528
  let version = 0;
@@ -2431,7 +2533,7 @@ function createVersionedSignalAccessor(initialValue) {
2431
2533
  return current;
2432
2534
  }
2433
2535
  current = value;
2434
- version++;
2536
+ version = version >= MAX_SAFE_VERSION ? 1 : version + 1;
2435
2537
  track2(version);
2436
2538
  }
2437
2539
  return accessor;
@@ -2487,15 +2589,21 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2487
2589
  });
2488
2590
  const root = createRootContext(hostRoot);
2489
2591
  const prevRoot = pushRoot(root);
2490
- const prevSub = setActiveSub(void 0);
2491
2592
  let nodes = [];
2593
+ let scopeDispose;
2594
+ const prevSub = setActiveSub(void 0);
2492
2595
  try {
2493
- const rendered = render2(itemSig, indexSig, key);
2494
- if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2495
- nodes = toNodeArray(rendered);
2496
- } else {
2497
- const element = createElement(rendered);
2498
- nodes = toNodeArray(element);
2596
+ scopeDispose = effectScope(() => {
2597
+ const rendered = render2(itemSig, indexSig, key);
2598
+ if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2599
+ nodes = toNodeArray(rendered);
2600
+ } else {
2601
+ const element = createElement(rendered);
2602
+ nodes = toNodeArray(element);
2603
+ }
2604
+ });
2605
+ if (scopeDispose) {
2606
+ root.cleanups.push(scopeDispose);
2499
2607
  }
2500
2608
  } finally {
2501
2609
  setActiveSub(prevSub);
@@ -2522,6 +2630,86 @@ function isNodeBetweenMarkers(node, startMarker, endMarker) {
2522
2630
  }
2523
2631
  return false;
2524
2632
  }
2633
+ function reorderBySwap(parent, first, second) {
2634
+ if (first === second) return false;
2635
+ const firstNodes = first.nodes;
2636
+ const secondNodes = second.nodes;
2637
+ if (firstNodes.length === 0 || secondNodes.length === 0) return false;
2638
+ const lastFirst = firstNodes[firstNodes.length - 1];
2639
+ const lastSecond = secondNodes[secondNodes.length - 1];
2640
+ const afterFirst = lastFirst.nextSibling;
2641
+ const afterSecond = lastSecond.nextSibling;
2642
+ moveNodesBefore(parent, firstNodes, afterSecond);
2643
+ moveNodesBefore(parent, secondNodes, afterFirst);
2644
+ return true;
2645
+ }
2646
+ function getLISIndices(sequence) {
2647
+ const predecessors = new Array(sequence.length);
2648
+ const result = [];
2649
+ for (let i = 0; i < sequence.length; i++) {
2650
+ const value = sequence[i];
2651
+ if (value < 0) {
2652
+ predecessors[i] = -1;
2653
+ continue;
2654
+ }
2655
+ let low = 0;
2656
+ let high = result.length;
2657
+ while (low < high) {
2658
+ const mid = low + high >> 1;
2659
+ if (sequence[result[mid]] < value) {
2660
+ low = mid + 1;
2661
+ } else {
2662
+ high = mid;
2663
+ }
2664
+ }
2665
+ predecessors[i] = low > 0 ? result[low - 1] : -1;
2666
+ if (low === result.length) {
2667
+ result.push(i);
2668
+ } else {
2669
+ result[low] = i;
2670
+ }
2671
+ }
2672
+ const lis = new Array(result.length);
2673
+ let k = result.length > 0 ? result[result.length - 1] : -1;
2674
+ for (let i = result.length - 1; i >= 0; i--) {
2675
+ lis[i] = k;
2676
+ k = predecessors[k];
2677
+ }
2678
+ return lis;
2679
+ }
2680
+ function reorderByLIS(parent, endMarker, prev, next) {
2681
+ const positions = /* @__PURE__ */ new Map();
2682
+ for (let i = 0; i < prev.length; i++) {
2683
+ positions.set(prev[i], i);
2684
+ }
2685
+ const sequence = new Array(next.length);
2686
+ for (let i = 0; i < next.length; i++) {
2687
+ const position = positions.get(next[i]);
2688
+ if (position === void 0) return false;
2689
+ sequence[i] = position;
2690
+ }
2691
+ const lisIndices = getLISIndices(sequence);
2692
+ if (lisIndices.length === sequence.length) return true;
2693
+ const inLIS = new Array(sequence.length).fill(false);
2694
+ for (let i = 0; i < lisIndices.length; i++) {
2695
+ inLIS[lisIndices[i]] = true;
2696
+ }
2697
+ let anchor = endMarker;
2698
+ let moved = false;
2699
+ for (let i = next.length - 1; i >= 0; i--) {
2700
+ const block = next[i];
2701
+ const nodes = block.nodes;
2702
+ if (nodes.length === 0) continue;
2703
+ if (inLIS[i]) {
2704
+ anchor = nodes[0];
2705
+ continue;
2706
+ }
2707
+ moveNodesBefore(parent, nodes, anchor);
2708
+ anchor = nodes[0];
2709
+ moved = true;
2710
+ }
2711
+ return moved;
2712
+ }
2525
2713
  function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
2526
2714
  const resolvedNeedsIndex = arguments.length >= 4 ? !!needsIndex : renderItem.length > 1;
2527
2715
  return createFineGrainedKeyedList(getItems, keyFn, renderItem, resolvedNeedsIndex);
@@ -2554,10 +2742,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2554
2742
  const prevOrderedBlocks = container.orderedBlocks;
2555
2743
  const nextOrderedBlocks = container.nextOrderedBlocks;
2556
2744
  const orderedIndexByKey = container.orderedIndexByKey;
2557
- newBlocks.clear();
2558
- nextOrderedBlocks.length = 0;
2559
- orderedIndexByKey.clear();
2560
- const createdBlocks = [];
2561
2745
  const newItems = getItems();
2562
2746
  if (newItems.length === 0) {
2563
2747
  if (oldBlocks.size > 0) {
@@ -2580,8 +2764,44 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2580
2764
  return;
2581
2765
  }
2582
2766
  const prevCount = prevOrderedBlocks.length;
2767
+ if (prevCount > 0 && newItems.length === prevCount && orderedIndexByKey.size === prevCount) {
2768
+ let stableOrder = true;
2769
+ const seen = /* @__PURE__ */ new Set();
2770
+ for (let i = 0; i < prevCount; i++) {
2771
+ const item = newItems[i];
2772
+ const key = keyFn(item, i);
2773
+ if (seen.has(key) || prevOrderedBlocks[i].key !== key) {
2774
+ stableOrder = false;
2775
+ break;
2776
+ }
2777
+ seen.add(key);
2778
+ }
2779
+ if (stableOrder) {
2780
+ for (let i = 0; i < prevCount; i++) {
2781
+ const item = newItems[i];
2782
+ const block = prevOrderedBlocks[i];
2783
+ if (block.rawItem !== item) {
2784
+ block.rawItem = item;
2785
+ block.item(item);
2786
+ }
2787
+ if (needsIndex && block.rawIndex !== i) {
2788
+ block.rawIndex = i;
2789
+ block.index(i);
2790
+ }
2791
+ }
2792
+ return;
2793
+ }
2794
+ }
2795
+ newBlocks.clear();
2796
+ nextOrderedBlocks.length = 0;
2797
+ orderedIndexByKey.clear();
2798
+ const createdBlocks = [];
2583
2799
  let appendCandidate = prevCount > 0 && newItems.length >= prevCount;
2584
2800
  const appendedBlocks = [];
2801
+ let mismatchCount = 0;
2802
+ let mismatchFirst = -1;
2803
+ let mismatchSecond = -1;
2804
+ let hasDuplicateKey = false;
2585
2805
  newItems.forEach((item, index) => {
2586
2806
  const key = keyFn(item, index);
2587
2807
  let block = oldBlocks.get(key);
@@ -2602,6 +2822,11 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2602
2822
  } else {
2603
2823
  const existingBlock = newBlocks.get(key);
2604
2824
  if (existingBlock) {
2825
+ if (isDev2) {
2826
+ console.warn(
2827
+ `[fict] Duplicate key "${String(key)}" detected in list rendering. Each item should have a unique key. The previous item with this key will be replaced.`
2828
+ );
2829
+ }
2605
2830
  destroyRoot(existingBlock.root);
2606
2831
  removeNodes(existingBlock.nodes);
2607
2832
  }
@@ -2613,6 +2838,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2613
2838
  const position = orderedIndexByKey.get(key);
2614
2839
  if (position !== void 0) {
2615
2840
  appendCandidate = false;
2841
+ hasDuplicateKey = true;
2616
2842
  const prior = nextOrderedBlocks[position];
2617
2843
  if (prior && prior !== resolvedBlock) {
2618
2844
  destroyRoot(prior.root);
@@ -2629,8 +2855,17 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2629
2855
  appendCandidate = false;
2630
2856
  }
2631
2857
  }
2632
- orderedIndexByKey.set(key, nextOrderedBlocks.length);
2858
+ const nextIndex = nextOrderedBlocks.length;
2859
+ orderedIndexByKey.set(key, nextIndex);
2633
2860
  nextOrderedBlocks.push(resolvedBlock);
2861
+ if (mismatchCount < 3 && (nextIndex >= prevCount || prevOrderedBlocks[nextIndex] !== resolvedBlock)) {
2862
+ if (mismatchCount === 0) {
2863
+ mismatchFirst = nextIndex;
2864
+ } else if (mismatchCount === 1) {
2865
+ mismatchSecond = nextIndex;
2866
+ }
2867
+ mismatchCount++;
2868
+ }
2634
2869
  }
2635
2870
  if (appendCandidate && index >= prevCount) {
2636
2871
  appendedBlocks.push(resolvedBlock);
@@ -2671,7 +2906,26 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2671
2906
  }
2672
2907
  oldBlocks.clear();
2673
2908
  }
2674
- if (newBlocks.size > 0 || container.currentNodes.length > 0) {
2909
+ const canReorderInPlace = createdBlocks.length === 0 && oldBlocks.size === 0 && nextOrderedBlocks.length === prevOrderedBlocks.length;
2910
+ let skipReconcile = false;
2911
+ let updateNodeBuffer = true;
2912
+ if (canReorderInPlace && nextOrderedBlocks.length > 0 && !hasDuplicateKey) {
2913
+ if (mismatchCount === 0) {
2914
+ skipReconcile = true;
2915
+ updateNodeBuffer = false;
2916
+ } else if (mismatchCount === 2 && prevOrderedBlocks[mismatchFirst] === nextOrderedBlocks[mismatchSecond] && prevOrderedBlocks[mismatchSecond] === nextOrderedBlocks[mismatchFirst]) {
2917
+ if (reorderBySwap(
2918
+ parent,
2919
+ prevOrderedBlocks[mismatchFirst],
2920
+ prevOrderedBlocks[mismatchSecond]
2921
+ )) {
2922
+ skipReconcile = true;
2923
+ }
2924
+ } else if (reorderByLIS(parent, container.endMarker, prevOrderedBlocks, nextOrderedBlocks)) {
2925
+ skipReconcile = true;
2926
+ }
2927
+ }
2928
+ if (!skipReconcile && (newBlocks.size > 0 || container.currentNodes.length > 0)) {
2675
2929
  const prevNodes = container.currentNodes;
2676
2930
  const nextNodes = container.nextNodes;
2677
2931
  nextNodes.length = 0;
@@ -2686,6 +2940,20 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2686
2940
  reconcileArrays(parent, prevNodes, nextNodes);
2687
2941
  container.currentNodes = nextNodes;
2688
2942
  container.nextNodes = prevNodes;
2943
+ } else if (skipReconcile && updateNodeBuffer) {
2944
+ const prevNodes = container.currentNodes;
2945
+ const nextNodes = container.nextNodes;
2946
+ nextNodes.length = 0;
2947
+ nextNodes.push(container.startMarker);
2948
+ for (let i = 0; i < nextOrderedBlocks.length; i++) {
2949
+ const nodes = nextOrderedBlocks[i].nodes;
2950
+ for (let j = 0; j < nodes.length; j++) {
2951
+ nextNodes.push(nodes[j]);
2952
+ }
2953
+ }
2954
+ nextNodes.push(container.endMarker);
2955
+ container.currentNodes = nextNodes;
2956
+ container.nextNodes = prevNodes;
2689
2957
  }
2690
2958
  container.blocks = newBlocks;
2691
2959
  container.nextBlocks = oldBlocks;
@@ -3332,7 +3600,9 @@ function bindEvent(el, eventName, handler, options2) {
3332
3600
  const fn = resolveHandler();
3333
3601
  callEventHandler(fn, args[0], el);
3334
3602
  } catch (err) {
3335
- handleError(err, { source: "event", eventName }, rootRef);
3603
+ if (!handleError(err, { source: "event", eventName }, rootRef)) {
3604
+ throw err;
3605
+ }
3336
3606
  }
3337
3607
  };
3338
3608
  return () => {
@@ -4119,10 +4389,12 @@ function ErrorBoundary(props) {
4119
4389
  renderingFallback = true;
4120
4390
  try {
4121
4391
  renderValue(toView(err));
4122
- } finally {
4123
4392
  renderingFallback = false;
4393
+ props.onError?.(err);
4394
+ } catch (fallbackErr) {
4395
+ props.onError?.(err);
4396
+ throw fallbackErr;
4124
4397
  }
4125
- props.onError?.(err);
4126
4398
  return;
4127
4399
  }
4128
4400
  popRoot(prev);
@@ -4217,7 +4489,9 @@ function Suspense(props) {
4217
4489
  popRoot(prev);
4218
4490
  flushOnMount(root);
4219
4491
  destroyRoot(root);
4220
- handleError(err, { source: "render" });
4492
+ if (!handleError(err, { source: "render" }, hostRoot)) {
4493
+ throw err;
4494
+ }
4221
4495
  return;
4222
4496
  }
4223
4497
  popRoot(prev);
@@ -4247,18 +4521,26 @@ function Suspense(props) {
4247
4521
  if (thenable) {
4248
4522
  thenable.then(
4249
4523
  () => {
4250
- if (epoch !== tokenEpoch) return;
4251
- pending(Math.max(0, pending() - 1));
4252
- if (pending() === 0) {
4524
+ if (epoch !== tokenEpoch) {
4525
+ return;
4526
+ }
4527
+ const newPending = Math.max(0, pending() - 1);
4528
+ pending(newPending);
4529
+ if (newPending === 0) {
4253
4530
  switchView(props.children ?? null);
4254
4531
  onResolveMaybe();
4255
4532
  }
4256
4533
  },
4257
4534
  (err) => {
4258
- if (epoch !== tokenEpoch) return;
4259
- pending(Math.max(0, pending() - 1));
4535
+ if (epoch !== tokenEpoch) {
4536
+ return;
4537
+ }
4538
+ const newPending = Math.max(0, pending() - 1);
4539
+ pending(newPending);
4260
4540
  props.onReject?.(err);
4261
- handleError(err, { source: "render" }, hostRoot);
4541
+ if (!handleError(err, { source: "render" }, hostRoot)) {
4542
+ throw err;
4543
+ }
4262
4544
  }
4263
4545
  );
4264
4546
  return true;