@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.js CHANGED
@@ -300,7 +300,7 @@ function handleError(err, info, startRoot) {
300
300
  }
301
301
  }
302
302
  }
303
- throw error;
303
+ return false;
304
304
  }
305
305
  function handleSuspend(token, startRoot) {
306
306
  let root = startRoot ?? currentRoot;
@@ -347,6 +347,10 @@ var enqueueMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : (
347
347
  Promise.resolve().then(fn);
348
348
  };
349
349
  var inCleanup = false;
350
+ var SIGNAL_MARKER = Symbol.for("fict:signal");
351
+ var COMPUTED_MARKER = Symbol.for("fict:computed");
352
+ var EFFECT_MARKER = Symbol.for("fict:effect");
353
+ var EFFECT_SCOPE_MARKER = Symbol.for("fict:effectScope");
350
354
  function link(dep, sub, version) {
351
355
  const prevDep = sub.depsTail;
352
356
  if (prevDep !== void 0 && prevDep.dep === dep) return;
@@ -470,13 +474,21 @@ function checkDirty(firstLink, sub) {
470
474
  dirty = true;
471
475
  }
472
476
  } else if ((depFlags & MutablePending) === MutablePending) {
473
- if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
474
- stack = { value: link2, prev: stack };
477
+ if (!dep.deps) {
478
+ const nextDep = link2.nextDep;
479
+ if (nextDep !== void 0) {
480
+ link2 = nextDep;
481
+ continue;
482
+ }
483
+ } else {
484
+ if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
485
+ stack = { value: link2, prev: stack };
486
+ }
487
+ link2 = dep.deps;
488
+ sub = dep;
489
+ ++checkDepth;
490
+ continue;
475
491
  }
476
- link2 = dep.deps;
477
- sub = dep;
478
- ++checkDepth;
479
- continue;
480
492
  }
481
493
  if (!dirty) {
482
494
  const nextDep = link2.nextDep;
@@ -554,8 +566,12 @@ function disposeNode(node) {
554
566
  node.depsTail = void 0;
555
567
  node.flags = 0;
556
568
  purgeDeps(node);
557
- const sub = node.subs;
558
- if (sub !== void 0) unlink(sub, node);
569
+ let sub = node.subs;
570
+ while (sub !== void 0) {
571
+ const next = sub.nextSub;
572
+ unlink(sub);
573
+ sub = next;
574
+ }
559
575
  }
560
576
  function updateSignal(s) {
561
577
  s.flags = Mutable;
@@ -724,7 +740,9 @@ function signal(initialValue) {
724
740
  __id: void 0
725
741
  };
726
742
  registerSignalDevtools(initialValue, s);
727
- return signalOper.bind(s);
743
+ const accessor = signalOper.bind(s);
744
+ accessor[SIGNAL_MARKER] = true;
745
+ return accessor;
728
746
  }
729
747
  function signalOper(value) {
730
748
  if (arguments.length > 0) {
@@ -769,6 +787,7 @@ function computed(getter) {
769
787
  getter
770
788
  };
771
789
  const bound = computedOper.bind(c);
790
+ bound[COMPUTED_MARKER] = true;
772
791
  return bound;
773
792
  }
774
793
  function computedOper() {
@@ -821,7 +840,9 @@ function effect(fn) {
821
840
  activeSub = prevSub;
822
841
  e.flags &= ~Running;
823
842
  }
824
- return effectOper.bind(e);
843
+ const disposer = effectOper.bind(e);
844
+ disposer[EFFECT_MARKER] = true;
845
+ return disposer;
825
846
  }
826
847
  function effectWithCleanup(fn, cleanupRunner) {
827
848
  const e = {
@@ -845,7 +866,9 @@ function effectWithCleanup(fn, cleanupRunner) {
845
866
  activeSub = prevSub;
846
867
  e.flags &= ~Running;
847
868
  }
848
- return effectOper.bind(e);
869
+ const disposer = effectOper.bind(e);
870
+ disposer[EFFECT_MARKER] = true;
871
+ return disposer;
849
872
  }
850
873
  function effectOper() {
851
874
  disposeNode(this);
@@ -860,7 +883,9 @@ function effectScope(fn) {
860
883
  } finally {
861
884
  activeSub = prevSub;
862
885
  }
863
- return effectScopeOper.bind(e);
886
+ const disposer = effectScopeOper.bind(e);
887
+ disposer[EFFECT_SCOPE_MARKER] = true;
888
+ return disposer;
864
889
  }
865
890
  function effectScopeOper() {
866
891
  disposeNode(this);
@@ -1677,9 +1702,6 @@ function mergeProps(...sources) {
1677
1702
  };
1678
1703
  return new Proxy({}, {
1679
1704
  get(_, prop) {
1680
- if (typeof prop === "symbol") {
1681
- return void 0;
1682
- }
1683
1705
  for (let i = validSources.length - 1; i >= 0; i--) {
1684
1706
  const src = validSources[i];
1685
1707
  const raw = resolveSource(src);
@@ -1752,8 +1774,8 @@ function startTransition(fn) {
1752
1774
  function useTransition() {
1753
1775
  const pending = signal(false);
1754
1776
  const start = (fn) => {
1755
- pending(true);
1756
1777
  startTransition(() => {
1778
+ pending(true);
1757
1779
  try {
1758
1780
  fn();
1759
1781
  } finally {
@@ -1998,7 +2020,8 @@ function applyRef(el, value) {
1998
2020
  if (typeof value === "function") {
1999
2021
  const refFn = value;
2000
2022
  refFn(el);
2001
- if (getCurrentRoot()) {
2023
+ const root = getCurrentRoot();
2024
+ if (root) {
2002
2025
  registerRootCleanup(() => {
2003
2026
  refFn(null);
2004
2027
  });
@@ -2006,7 +2029,8 @@ function applyRef(el, value) {
2006
2029
  } else if (value && typeof value === "object" && "current" in value) {
2007
2030
  const refObj = value;
2008
2031
  refObj.current = el;
2009
- if (getCurrentRoot()) {
2032
+ const root = getCurrentRoot();
2033
+ if (root) {
2010
2034
  registerRootCleanup(() => {
2011
2035
  refObj.current = null;
2012
2036
  });
@@ -2417,8 +2441,6 @@ function reconcileArrays(parentNode, a, b) {
2417
2441
  }
2418
2442
  }
2419
2443
  }
2420
-
2421
- // src/list-helpers.ts
2422
2444
  function moveNodesBefore(parent, nodes, anchor) {
2423
2445
  for (let i = nodes.length - 1; i >= 0; i--) {
2424
2446
  const node = nodes[i];
@@ -2480,6 +2502,7 @@ function removeBlockRange(block) {
2480
2502
  cursor = next;
2481
2503
  }
2482
2504
  }
2505
+ var MAX_SAFE_VERSION = 9007199254740991;
2483
2506
  function createVersionedSignalAccessor(initialValue) {
2484
2507
  let current = initialValue;
2485
2508
  let version = 0;
@@ -2490,7 +2513,7 @@ function createVersionedSignalAccessor(initialValue) {
2490
2513
  return current;
2491
2514
  }
2492
2515
  current = value;
2493
- version++;
2516
+ version = version >= MAX_SAFE_VERSION ? 1 : version + 1;
2494
2517
  track2(version);
2495
2518
  }
2496
2519
  return accessor;
@@ -2546,15 +2569,21 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2546
2569
  });
2547
2570
  const root = createRootContext(hostRoot);
2548
2571
  const prevRoot = pushRoot(root);
2549
- const prevSub = setActiveSub(void 0);
2550
2572
  let nodes = [];
2573
+ let scopeDispose;
2574
+ const prevSub = setActiveSub(void 0);
2551
2575
  try {
2552
- const rendered = render2(itemSig, indexSig, key);
2553
- if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2554
- nodes = toNodeArray(rendered);
2555
- } else {
2556
- const element = createElement(rendered);
2557
- nodes = toNodeArray(element);
2576
+ scopeDispose = effectScope(() => {
2577
+ const rendered = render2(itemSig, indexSig, key);
2578
+ if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2579
+ nodes = toNodeArray(rendered);
2580
+ } else {
2581
+ const element = createElement(rendered);
2582
+ nodes = toNodeArray(element);
2583
+ }
2584
+ });
2585
+ if (scopeDispose) {
2586
+ root.cleanups.push(scopeDispose);
2558
2587
  }
2559
2588
  } finally {
2560
2589
  setActiveSub(prevSub);
@@ -2581,6 +2610,86 @@ function isNodeBetweenMarkers(node, startMarker, endMarker) {
2581
2610
  }
2582
2611
  return false;
2583
2612
  }
2613
+ function reorderBySwap(parent, first, second) {
2614
+ if (first === second) return false;
2615
+ const firstNodes = first.nodes;
2616
+ const secondNodes = second.nodes;
2617
+ if (firstNodes.length === 0 || secondNodes.length === 0) return false;
2618
+ const lastFirst = firstNodes[firstNodes.length - 1];
2619
+ const lastSecond = secondNodes[secondNodes.length - 1];
2620
+ const afterFirst = lastFirst.nextSibling;
2621
+ const afterSecond = lastSecond.nextSibling;
2622
+ moveNodesBefore(parent, firstNodes, afterSecond);
2623
+ moveNodesBefore(parent, secondNodes, afterFirst);
2624
+ return true;
2625
+ }
2626
+ function getLISIndices(sequence) {
2627
+ const predecessors = new Array(sequence.length);
2628
+ const result = [];
2629
+ for (let i = 0; i < sequence.length; i++) {
2630
+ const value = sequence[i];
2631
+ if (value < 0) {
2632
+ predecessors[i] = -1;
2633
+ continue;
2634
+ }
2635
+ let low = 0;
2636
+ let high = result.length;
2637
+ while (low < high) {
2638
+ const mid = low + high >> 1;
2639
+ if (sequence[result[mid]] < value) {
2640
+ low = mid + 1;
2641
+ } else {
2642
+ high = mid;
2643
+ }
2644
+ }
2645
+ predecessors[i] = low > 0 ? result[low - 1] : -1;
2646
+ if (low === result.length) {
2647
+ result.push(i);
2648
+ } else {
2649
+ result[low] = i;
2650
+ }
2651
+ }
2652
+ const lis = new Array(result.length);
2653
+ let k = result.length > 0 ? result[result.length - 1] : -1;
2654
+ for (let i = result.length - 1; i >= 0; i--) {
2655
+ lis[i] = k;
2656
+ k = predecessors[k];
2657
+ }
2658
+ return lis;
2659
+ }
2660
+ function reorderByLIS(parent, endMarker, prev, next) {
2661
+ const positions = /* @__PURE__ */ new Map();
2662
+ for (let i = 0; i < prev.length; i++) {
2663
+ positions.set(prev[i], i);
2664
+ }
2665
+ const sequence = new Array(next.length);
2666
+ for (let i = 0; i < next.length; i++) {
2667
+ const position = positions.get(next[i]);
2668
+ if (position === void 0) return false;
2669
+ sequence[i] = position;
2670
+ }
2671
+ const lisIndices = getLISIndices(sequence);
2672
+ if (lisIndices.length === sequence.length) return true;
2673
+ const inLIS = new Array(sequence.length).fill(false);
2674
+ for (let i = 0; i < lisIndices.length; i++) {
2675
+ inLIS[lisIndices[i]] = true;
2676
+ }
2677
+ let anchor = endMarker;
2678
+ let moved = false;
2679
+ for (let i = next.length - 1; i >= 0; i--) {
2680
+ const block = next[i];
2681
+ const nodes = block.nodes;
2682
+ if (nodes.length === 0) continue;
2683
+ if (inLIS[i]) {
2684
+ anchor = nodes[0];
2685
+ continue;
2686
+ }
2687
+ moveNodesBefore(parent, nodes, anchor);
2688
+ anchor = nodes[0];
2689
+ moved = true;
2690
+ }
2691
+ return moved;
2692
+ }
2584
2693
  function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
2585
2694
  const resolvedNeedsIndex = arguments.length >= 4 ? !!needsIndex : renderItem.length > 1;
2586
2695
  return createFineGrainedKeyedList(getItems, keyFn, renderItem, resolvedNeedsIndex);
@@ -2613,10 +2722,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2613
2722
  const prevOrderedBlocks = container.orderedBlocks;
2614
2723
  const nextOrderedBlocks = container.nextOrderedBlocks;
2615
2724
  const orderedIndexByKey = container.orderedIndexByKey;
2616
- newBlocks.clear();
2617
- nextOrderedBlocks.length = 0;
2618
- orderedIndexByKey.clear();
2619
- const createdBlocks = [];
2620
2725
  const newItems = getItems();
2621
2726
  if (newItems.length === 0) {
2622
2727
  if (oldBlocks.size > 0) {
@@ -2639,8 +2744,44 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2639
2744
  return;
2640
2745
  }
2641
2746
  const prevCount = prevOrderedBlocks.length;
2747
+ if (prevCount > 0 && newItems.length === prevCount && orderedIndexByKey.size === prevCount) {
2748
+ let stableOrder = true;
2749
+ const seen = /* @__PURE__ */ new Set();
2750
+ for (let i = 0; i < prevCount; i++) {
2751
+ const item = newItems[i];
2752
+ const key = keyFn(item, i);
2753
+ if (seen.has(key) || prevOrderedBlocks[i].key !== key) {
2754
+ stableOrder = false;
2755
+ break;
2756
+ }
2757
+ seen.add(key);
2758
+ }
2759
+ if (stableOrder) {
2760
+ for (let i = 0; i < prevCount; i++) {
2761
+ const item = newItems[i];
2762
+ const block = prevOrderedBlocks[i];
2763
+ if (block.rawItem !== item) {
2764
+ block.rawItem = item;
2765
+ block.item(item);
2766
+ }
2767
+ if (needsIndex && block.rawIndex !== i) {
2768
+ block.rawIndex = i;
2769
+ block.index(i);
2770
+ }
2771
+ }
2772
+ return;
2773
+ }
2774
+ }
2775
+ newBlocks.clear();
2776
+ nextOrderedBlocks.length = 0;
2777
+ orderedIndexByKey.clear();
2778
+ const createdBlocks = [];
2642
2779
  let appendCandidate = prevCount > 0 && newItems.length >= prevCount;
2643
2780
  const appendedBlocks = [];
2781
+ let mismatchCount = 0;
2782
+ let mismatchFirst = -1;
2783
+ let mismatchSecond = -1;
2784
+ let hasDuplicateKey = false;
2644
2785
  newItems.forEach((item, index) => {
2645
2786
  const key = keyFn(item, index);
2646
2787
  let block = oldBlocks.get(key);
@@ -2672,6 +2813,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2672
2813
  const position = orderedIndexByKey.get(key);
2673
2814
  if (position !== void 0) {
2674
2815
  appendCandidate = false;
2816
+ hasDuplicateKey = true;
2675
2817
  const prior = nextOrderedBlocks[position];
2676
2818
  if (prior && prior !== resolvedBlock) {
2677
2819
  destroyRoot(prior.root);
@@ -2688,8 +2830,17 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2688
2830
  appendCandidate = false;
2689
2831
  }
2690
2832
  }
2691
- orderedIndexByKey.set(key, nextOrderedBlocks.length);
2833
+ const nextIndex = nextOrderedBlocks.length;
2834
+ orderedIndexByKey.set(key, nextIndex);
2692
2835
  nextOrderedBlocks.push(resolvedBlock);
2836
+ if (mismatchCount < 3 && (nextIndex >= prevCount || prevOrderedBlocks[nextIndex] !== resolvedBlock)) {
2837
+ if (mismatchCount === 0) {
2838
+ mismatchFirst = nextIndex;
2839
+ } else if (mismatchCount === 1) {
2840
+ mismatchSecond = nextIndex;
2841
+ }
2842
+ mismatchCount++;
2843
+ }
2693
2844
  }
2694
2845
  if (appendCandidate && index >= prevCount) {
2695
2846
  appendedBlocks.push(resolvedBlock);
@@ -2730,7 +2881,26 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2730
2881
  }
2731
2882
  oldBlocks.clear();
2732
2883
  }
2733
- if (newBlocks.size > 0 || container.currentNodes.length > 0) {
2884
+ const canReorderInPlace = createdBlocks.length === 0 && oldBlocks.size === 0 && nextOrderedBlocks.length === prevOrderedBlocks.length;
2885
+ let skipReconcile = false;
2886
+ let updateNodeBuffer = true;
2887
+ if (canReorderInPlace && nextOrderedBlocks.length > 0 && !hasDuplicateKey) {
2888
+ if (mismatchCount === 0) {
2889
+ skipReconcile = true;
2890
+ updateNodeBuffer = false;
2891
+ } else if (mismatchCount === 2 && prevOrderedBlocks[mismatchFirst] === nextOrderedBlocks[mismatchSecond] && prevOrderedBlocks[mismatchSecond] === nextOrderedBlocks[mismatchFirst]) {
2892
+ if (reorderBySwap(
2893
+ parent,
2894
+ prevOrderedBlocks[mismatchFirst],
2895
+ prevOrderedBlocks[mismatchSecond]
2896
+ )) {
2897
+ skipReconcile = true;
2898
+ }
2899
+ } else if (reorderByLIS(parent, container.endMarker, prevOrderedBlocks, nextOrderedBlocks)) {
2900
+ skipReconcile = true;
2901
+ }
2902
+ }
2903
+ if (!skipReconcile && (newBlocks.size > 0 || container.currentNodes.length > 0)) {
2734
2904
  const prevNodes = container.currentNodes;
2735
2905
  const nextNodes = container.nextNodes;
2736
2906
  nextNodes.length = 0;
@@ -2745,6 +2915,20 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2745
2915
  reconcileArrays(parent, prevNodes, nextNodes);
2746
2916
  container.currentNodes = nextNodes;
2747
2917
  container.nextNodes = prevNodes;
2918
+ } else if (skipReconcile && updateNodeBuffer) {
2919
+ const prevNodes = container.currentNodes;
2920
+ const nextNodes = container.nextNodes;
2921
+ nextNodes.length = 0;
2922
+ nextNodes.push(container.startMarker);
2923
+ for (let i = 0; i < nextOrderedBlocks.length; i++) {
2924
+ const nodes = nextOrderedBlocks[i].nodes;
2925
+ for (let j = 0; j < nodes.length; j++) {
2926
+ nextNodes.push(nodes[j]);
2927
+ }
2928
+ }
2929
+ nextNodes.push(container.endMarker);
2930
+ container.currentNodes = nextNodes;
2931
+ container.nextNodes = prevNodes;
2748
2932
  }
2749
2933
  container.blocks = newBlocks;
2750
2934
  container.nextBlocks = oldBlocks;
@@ -3386,7 +3570,9 @@ function bindEvent(el, eventName, handler, options2) {
3386
3570
  const fn = resolveHandler();
3387
3571
  callEventHandler(fn, args[0], el);
3388
3572
  } catch (err) {
3389
- handleError(err, { source: "event", eventName }, rootRef);
3573
+ if (!handleError(err, { source: "event", eventName }, rootRef)) {
3574
+ throw err;
3575
+ }
3390
3576
  }
3391
3577
  };
3392
3578
  return () => {
@@ -4164,10 +4350,12 @@ function ErrorBoundary(props) {
4164
4350
  renderingFallback = true;
4165
4351
  try {
4166
4352
  renderValue(toView(err));
4167
- } finally {
4168
4353
  renderingFallback = false;
4354
+ props.onError?.(err);
4355
+ } catch (fallbackErr) {
4356
+ props.onError?.(err);
4357
+ throw fallbackErr;
4169
4358
  }
4170
- props.onError?.(err);
4171
4359
  return;
4172
4360
  }
4173
4361
  popRoot(prev);
@@ -4262,7 +4450,9 @@ function Suspense(props) {
4262
4450
  popRoot(prev);
4263
4451
  flushOnMount(root);
4264
4452
  destroyRoot(root);
4265
- handleError(err, { source: "render" });
4453
+ if (!handleError(err, { source: "render" }, hostRoot)) {
4454
+ throw err;
4455
+ }
4266
4456
  return;
4267
4457
  }
4268
4458
  popRoot(prev);
@@ -4292,18 +4482,26 @@ function Suspense(props) {
4292
4482
  if (thenable) {
4293
4483
  thenable.then(
4294
4484
  () => {
4295
- if (epoch !== tokenEpoch) return;
4296
- pending(Math.max(0, pending() - 1));
4297
- if (pending() === 0) {
4485
+ if (epoch !== tokenEpoch) {
4486
+ return;
4487
+ }
4488
+ const newPending = Math.max(0, pending() - 1);
4489
+ pending(newPending);
4490
+ if (newPending === 0) {
4298
4491
  switchView(props.children ?? null);
4299
4492
  onResolveMaybe();
4300
4493
  }
4301
4494
  },
4302
4495
  (err) => {
4303
- if (epoch !== tokenEpoch) return;
4304
- pending(Math.max(0, pending() - 1));
4496
+ if (epoch !== tokenEpoch) {
4497
+ return;
4498
+ }
4499
+ const newPending = Math.max(0, pending() - 1);
4500
+ pending(newPending);
4305
4501
  props.onReject?.(err);
4306
- handleError(err, { source: "render" }, hostRoot);
4502
+ if (!handleError(err, { source: "render" }, hostRoot)) {
4503
+ throw err;
4504
+ }
4307
4505
  }
4308
4506
  );
4309
4507
  return true;