@fictjs/runtime 0.0.11 → 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;
@@ -349,6 +349,10 @@ var enqueueMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : (
349
349
  Promise.resolve().then(fn);
350
350
  };
351
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");
352
356
  function link(dep, sub, version) {
353
357
  const prevDep = sub.depsTail;
354
358
  if (prevDep !== void 0 && prevDep.dep === dep) return;
@@ -472,13 +476,21 @@ function checkDirty(firstLink, sub) {
472
476
  dirty = true;
473
477
  }
474
478
  } else if ((depFlags & MutablePending) === MutablePending) {
475
- if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
476
- 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;
477
493
  }
478
- link2 = dep.deps;
479
- sub = dep;
480
- ++checkDepth;
481
- continue;
482
494
  }
483
495
  if (!dirty) {
484
496
  const nextDep = link2.nextDep;
@@ -556,8 +568,12 @@ function disposeNode(node) {
556
568
  node.depsTail = void 0;
557
569
  node.flags = 0;
558
570
  purgeDeps(node);
559
- const sub = node.subs;
560
- 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
+ }
561
577
  }
562
578
  function updateSignal(s) {
563
579
  s.flags = Mutable;
@@ -726,7 +742,9 @@ function signal(initialValue) {
726
742
  __id: void 0
727
743
  };
728
744
  registerSignalDevtools(initialValue, s);
729
- return signalOper.bind(s);
745
+ const accessor = signalOper.bind(s);
746
+ accessor[SIGNAL_MARKER] = true;
747
+ return accessor;
730
748
  }
731
749
  function signalOper(value) {
732
750
  if (arguments.length > 0) {
@@ -771,6 +789,7 @@ function computed(getter) {
771
789
  getter
772
790
  };
773
791
  const bound = computedOper.bind(c);
792
+ bound[COMPUTED_MARKER] = true;
774
793
  return bound;
775
794
  }
776
795
  function computedOper() {
@@ -823,7 +842,9 @@ function effect(fn) {
823
842
  activeSub = prevSub;
824
843
  e.flags &= ~Running;
825
844
  }
826
- return effectOper.bind(e);
845
+ const disposer = effectOper.bind(e);
846
+ disposer[EFFECT_MARKER] = true;
847
+ return disposer;
827
848
  }
828
849
  function effectWithCleanup(fn, cleanupRunner) {
829
850
  const e = {
@@ -847,7 +868,9 @@ function effectWithCleanup(fn, cleanupRunner) {
847
868
  activeSub = prevSub;
848
869
  e.flags &= ~Running;
849
870
  }
850
- return effectOper.bind(e);
871
+ const disposer = effectOper.bind(e);
872
+ disposer[EFFECT_MARKER] = true;
873
+ return disposer;
851
874
  }
852
875
  function effectOper() {
853
876
  disposeNode(this);
@@ -862,7 +885,9 @@ function effectScope(fn) {
862
885
  } finally {
863
886
  activeSub = prevSub;
864
887
  }
865
- return effectScopeOper.bind(e);
888
+ const disposer = effectScopeOper.bind(e);
889
+ disposer[EFFECT_SCOPE_MARKER] = true;
890
+ return disposer;
866
891
  }
867
892
  function effectScopeOper() {
868
893
  disposeNode(this);
@@ -1679,9 +1704,6 @@ function mergeProps(...sources) {
1679
1704
  };
1680
1705
  return new Proxy({}, {
1681
1706
  get(_, prop) {
1682
- if (typeof prop === "symbol") {
1683
- return void 0;
1684
- }
1685
1707
  for (let i = validSources.length - 1; i >= 0; i--) {
1686
1708
  const src = validSources[i];
1687
1709
  const raw = resolveSource(src);
@@ -1754,8 +1776,8 @@ function startTransition(fn) {
1754
1776
  function useTransition() {
1755
1777
  const pending = signal(false);
1756
1778
  const start = (fn) => {
1757
- pending(true);
1758
1779
  startTransition(() => {
1780
+ pending(true);
1759
1781
  try {
1760
1782
  fn();
1761
1783
  } finally {
@@ -2000,7 +2022,8 @@ function applyRef(el, value) {
2000
2022
  if (typeof value === "function") {
2001
2023
  const refFn = value;
2002
2024
  refFn(el);
2003
- if (getCurrentRoot()) {
2025
+ const root = getCurrentRoot();
2026
+ if (root) {
2004
2027
  registerRootCleanup(() => {
2005
2028
  refFn(null);
2006
2029
  });
@@ -2008,7 +2031,8 @@ function applyRef(el, value) {
2008
2031
  } else if (value && typeof value === "object" && "current" in value) {
2009
2032
  const refObj = value;
2010
2033
  refObj.current = el;
2011
- if (getCurrentRoot()) {
2034
+ const root = getCurrentRoot();
2035
+ if (root) {
2012
2036
  registerRootCleanup(() => {
2013
2037
  refObj.current = null;
2014
2038
  });
@@ -2419,8 +2443,6 @@ function reconcileArrays(parentNode, a, b) {
2419
2443
  }
2420
2444
  }
2421
2445
  }
2422
-
2423
- // src/list-helpers.ts
2424
2446
  function moveNodesBefore(parent, nodes, anchor) {
2425
2447
  for (let i = nodes.length - 1; i >= 0; i--) {
2426
2448
  const node = nodes[i];
@@ -2482,6 +2504,7 @@ function removeBlockRange(block) {
2482
2504
  cursor = next;
2483
2505
  }
2484
2506
  }
2507
+ var MAX_SAFE_VERSION = 9007199254740991;
2485
2508
  function createVersionedSignalAccessor(initialValue) {
2486
2509
  let current = initialValue;
2487
2510
  let version = 0;
@@ -2492,7 +2515,7 @@ function createVersionedSignalAccessor(initialValue) {
2492
2515
  return current;
2493
2516
  }
2494
2517
  current = value;
2495
- version++;
2518
+ version = version >= MAX_SAFE_VERSION ? 1 : version + 1;
2496
2519
  track2(version);
2497
2520
  }
2498
2521
  return accessor;
@@ -2548,15 +2571,21 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2548
2571
  });
2549
2572
  const root = createRootContext(hostRoot);
2550
2573
  const prevRoot = pushRoot(root);
2551
- const prevSub = setActiveSub(void 0);
2552
2574
  let nodes = [];
2575
+ let scopeDispose;
2576
+ const prevSub = setActiveSub(void 0);
2553
2577
  try {
2554
- const rendered = render2(itemSig, indexSig, key);
2555
- if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2556
- nodes = toNodeArray(rendered);
2557
- } else {
2558
- const element = createElement(rendered);
2559
- 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);
2560
2589
  }
2561
2590
  } finally {
2562
2591
  setActiveSub(prevSub);
@@ -2583,6 +2612,86 @@ function isNodeBetweenMarkers(node, startMarker, endMarker) {
2583
2612
  }
2584
2613
  return false;
2585
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
+ }
2586
2695
  function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
2587
2696
  const resolvedNeedsIndex = arguments.length >= 4 ? !!needsIndex : renderItem.length > 1;
2588
2697
  return createFineGrainedKeyedList(getItems, keyFn, renderItem, resolvedNeedsIndex);
@@ -2615,10 +2724,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2615
2724
  const prevOrderedBlocks = container.orderedBlocks;
2616
2725
  const nextOrderedBlocks = container.nextOrderedBlocks;
2617
2726
  const orderedIndexByKey = container.orderedIndexByKey;
2618
- newBlocks.clear();
2619
- nextOrderedBlocks.length = 0;
2620
- orderedIndexByKey.clear();
2621
- const createdBlocks = [];
2622
2727
  const newItems = getItems();
2623
2728
  if (newItems.length === 0) {
2624
2729
  if (oldBlocks.size > 0) {
@@ -2641,8 +2746,44 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2641
2746
  return;
2642
2747
  }
2643
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 = [];
2644
2781
  let appendCandidate = prevCount > 0 && newItems.length >= prevCount;
2645
2782
  const appendedBlocks = [];
2783
+ let mismatchCount = 0;
2784
+ let mismatchFirst = -1;
2785
+ let mismatchSecond = -1;
2786
+ let hasDuplicateKey = false;
2646
2787
  newItems.forEach((item, index) => {
2647
2788
  const key = keyFn(item, index);
2648
2789
  let block = oldBlocks.get(key);
@@ -2674,6 +2815,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2674
2815
  const position = orderedIndexByKey.get(key);
2675
2816
  if (position !== void 0) {
2676
2817
  appendCandidate = false;
2818
+ hasDuplicateKey = true;
2677
2819
  const prior = nextOrderedBlocks[position];
2678
2820
  if (prior && prior !== resolvedBlock) {
2679
2821
  destroyRoot(prior.root);
@@ -2690,8 +2832,17 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2690
2832
  appendCandidate = false;
2691
2833
  }
2692
2834
  }
2693
- orderedIndexByKey.set(key, nextOrderedBlocks.length);
2835
+ const nextIndex = nextOrderedBlocks.length;
2836
+ orderedIndexByKey.set(key, nextIndex);
2694
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
+ }
2695
2846
  }
2696
2847
  if (appendCandidate && index >= prevCount) {
2697
2848
  appendedBlocks.push(resolvedBlock);
@@ -2732,7 +2883,26 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2732
2883
  }
2733
2884
  oldBlocks.clear();
2734
2885
  }
2735
- 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)) {
2736
2906
  const prevNodes = container.currentNodes;
2737
2907
  const nextNodes = container.nextNodes;
2738
2908
  nextNodes.length = 0;
@@ -2747,6 +2917,20 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2747
2917
  reconcileArrays(parent, prevNodes, nextNodes);
2748
2918
  container.currentNodes = nextNodes;
2749
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;
2750
2934
  }
2751
2935
  container.blocks = newBlocks;
2752
2936
  container.nextBlocks = oldBlocks;
@@ -3388,7 +3572,9 @@ function bindEvent(el, eventName, handler, options2) {
3388
3572
  const fn = resolveHandler();
3389
3573
  callEventHandler(fn, args[0], el);
3390
3574
  } catch (err) {
3391
- handleError(err, { source: "event", eventName }, rootRef);
3575
+ if (!handleError(err, { source: "event", eventName }, rootRef)) {
3576
+ throw err;
3577
+ }
3392
3578
  }
3393
3579
  };
3394
3580
  return () => {
@@ -4166,10 +4352,12 @@ function ErrorBoundary(props) {
4166
4352
  renderingFallback = true;
4167
4353
  try {
4168
4354
  renderValue(toView(err));
4169
- } finally {
4170
4355
  renderingFallback = false;
4356
+ props.onError?.(err);
4357
+ } catch (fallbackErr) {
4358
+ props.onError?.(err);
4359
+ throw fallbackErr;
4171
4360
  }
4172
- props.onError?.(err);
4173
4361
  return;
4174
4362
  }
4175
4363
  popRoot(prev);
@@ -4264,7 +4452,9 @@ function Suspense(props) {
4264
4452
  popRoot(prev);
4265
4453
  flushOnMount(root);
4266
4454
  destroyRoot(root);
4267
- handleError(err, { source: "render" });
4455
+ if (!handleError(err, { source: "render" }, hostRoot)) {
4456
+ throw err;
4457
+ }
4268
4458
  return;
4269
4459
  }
4270
4460
  popRoot(prev);
@@ -4294,18 +4484,26 @@ function Suspense(props) {
4294
4484
  if (thenable) {
4295
4485
  thenable.then(
4296
4486
  () => {
4297
- if (epoch !== tokenEpoch) return;
4298
- pending(Math.max(0, pending() - 1));
4299
- 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) {
4300
4493
  switchView(props.children ?? null);
4301
4494
  onResolveMaybe();
4302
4495
  }
4303
4496
  },
4304
4497
  (err) => {
4305
- if (epoch !== tokenEpoch) return;
4306
- pending(Math.max(0, pending() - 1));
4498
+ if (epoch !== tokenEpoch) {
4499
+ return;
4500
+ }
4501
+ const newPending = Math.max(0, pending() - 1);
4502
+ pending(newPending);
4307
4503
  props.onReject?.(err);
4308
- handleError(err, { source: "render" }, hostRoot);
4504
+ if (!handleError(err, { source: "render" }, hostRoot)) {
4505
+ throw err;
4506
+ }
4309
4507
  }
4310
4508
  );
4311
4509
  return true;