@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/slim.js CHANGED
@@ -260,7 +260,7 @@ function handleError(err, info, startRoot) {
260
260
  }
261
261
  }
262
262
  }
263
- throw error;
263
+ return false;
264
264
  }
265
265
  function handleSuspend(token, startRoot) {
266
266
  let root = startRoot ?? currentRoot;
@@ -306,6 +306,10 @@ var enqueueMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : (
306
306
  Promise.resolve().then(fn);
307
307
  };
308
308
  var inCleanup = false;
309
+ var SIGNAL_MARKER = Symbol.for("fict:signal");
310
+ var COMPUTED_MARKER = Symbol.for("fict:computed");
311
+ var EFFECT_MARKER = Symbol.for("fict:effect");
312
+ var EFFECT_SCOPE_MARKER = Symbol.for("fict:effectScope");
309
313
  function link(dep, sub, version) {
310
314
  const prevDep = sub.depsTail;
311
315
  if (prevDep !== void 0 && prevDep.dep === dep) return;
@@ -429,13 +433,21 @@ function checkDirty(firstLink, sub) {
429
433
  dirty = true;
430
434
  }
431
435
  } else if ((depFlags & MutablePending) === MutablePending) {
432
- if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
433
- stack = { value: link2, prev: stack };
436
+ if (!dep.deps) {
437
+ const nextDep = link2.nextDep;
438
+ if (nextDep !== void 0) {
439
+ link2 = nextDep;
440
+ continue;
441
+ }
442
+ } else {
443
+ if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
444
+ stack = { value: link2, prev: stack };
445
+ }
446
+ link2 = dep.deps;
447
+ sub = dep;
448
+ ++checkDepth;
449
+ continue;
434
450
  }
435
- link2 = dep.deps;
436
- sub = dep;
437
- ++checkDepth;
438
- continue;
439
451
  }
440
452
  if (!dirty) {
441
453
  const nextDep = link2.nextDep;
@@ -513,8 +525,12 @@ function disposeNode(node) {
513
525
  node.depsTail = void 0;
514
526
  node.flags = 0;
515
527
  purgeDeps(node);
516
- const sub = node.subs;
517
- if (sub !== void 0) unlink(sub, node);
528
+ let sub = node.subs;
529
+ while (sub !== void 0) {
530
+ const next = sub.nextSub;
531
+ unlink(sub);
532
+ sub = next;
533
+ }
518
534
  }
519
535
  function updateSignal(s) {
520
536
  s.flags = Mutable;
@@ -683,7 +699,9 @@ function signal(initialValue) {
683
699
  __id: void 0
684
700
  };
685
701
  registerSignalDevtools(initialValue, s);
686
- return signalOper.bind(s);
702
+ const accessor = signalOper.bind(s);
703
+ accessor[SIGNAL_MARKER] = true;
704
+ return accessor;
687
705
  }
688
706
  function signalOper(value) {
689
707
  if (arguments.length > 0) {
@@ -728,6 +746,7 @@ function computed(getter) {
728
746
  getter
729
747
  };
730
748
  const bound = computedOper.bind(c);
749
+ bound[COMPUTED_MARKER] = true;
731
750
  return bound;
732
751
  }
733
752
  function computedOper() {
@@ -780,7 +799,9 @@ function effect(fn) {
780
799
  activeSub = prevSub;
781
800
  e.flags &= ~Running;
782
801
  }
783
- return effectOper.bind(e);
802
+ const disposer = effectOper.bind(e);
803
+ disposer[EFFECT_MARKER] = true;
804
+ return disposer;
784
805
  }
785
806
  function effectWithCleanup(fn, cleanupRunner) {
786
807
  const e = {
@@ -804,7 +825,9 @@ function effectWithCleanup(fn, cleanupRunner) {
804
825
  activeSub = prevSub;
805
826
  e.flags &= ~Running;
806
827
  }
807
- return effectOper.bind(e);
828
+ const disposer = effectOper.bind(e);
829
+ disposer[EFFECT_MARKER] = true;
830
+ return disposer;
808
831
  }
809
832
  function effectOper() {
810
833
  disposeNode(this);
@@ -819,7 +842,9 @@ function effectScope(fn) {
819
842
  } finally {
820
843
  activeSub = prevSub;
821
844
  }
822
- return effectScopeOper.bind(e);
845
+ const disposer = effectScopeOper.bind(e);
846
+ disposer[EFFECT_SCOPE_MARKER] = true;
847
+ return disposer;
823
848
  }
824
849
  function effectScopeOper() {
825
850
  disposeNode(this);
@@ -1504,9 +1529,6 @@ function mergeProps(...sources) {
1504
1529
  };
1505
1530
  return new Proxy({}, {
1506
1531
  get(_, prop) {
1507
- if (typeof prop === "symbol") {
1508
- return void 0;
1509
- }
1510
1532
  for (let i = validSources.length - 1; i >= 0; i--) {
1511
1533
  const src = validSources[i];
1512
1534
  const raw = resolveSource(src);
@@ -1787,7 +1809,8 @@ function applyRef(el, value) {
1787
1809
  if (typeof value === "function") {
1788
1810
  const refFn = value;
1789
1811
  refFn(el);
1790
- if (getCurrentRoot()) {
1812
+ const root = getCurrentRoot();
1813
+ if (root) {
1791
1814
  registerRootCleanup(() => {
1792
1815
  refFn(null);
1793
1816
  });
@@ -1795,7 +1818,8 @@ function applyRef(el, value) {
1795
1818
  } else if (value && typeof value === "object" && "current" in value) {
1796
1819
  const refObj = value;
1797
1820
  refObj.current = el;
1798
- if (getCurrentRoot()) {
1821
+ const root = getCurrentRoot();
1822
+ if (root) {
1799
1823
  registerRootCleanup(() => {
1800
1824
  refObj.current = null;
1801
1825
  });
@@ -2206,8 +2230,6 @@ function reconcileArrays(parentNode, a, b) {
2206
2230
  }
2207
2231
  }
2208
2232
  }
2209
-
2210
- // src/list-helpers.ts
2211
2233
  function moveNodesBefore(parent, nodes, anchor) {
2212
2234
  for (let i = nodes.length - 1; i >= 0; i--) {
2213
2235
  const node = nodes[i];
@@ -2269,6 +2291,7 @@ function removeBlockRange(block) {
2269
2291
  cursor = next;
2270
2292
  }
2271
2293
  }
2294
+ var MAX_SAFE_VERSION = 9007199254740991;
2272
2295
  function createVersionedSignalAccessor(initialValue) {
2273
2296
  let current = initialValue;
2274
2297
  let version = 0;
@@ -2279,7 +2302,7 @@ function createVersionedSignalAccessor(initialValue) {
2279
2302
  return current;
2280
2303
  }
2281
2304
  current = value;
2282
- version++;
2305
+ version = version >= MAX_SAFE_VERSION ? 1 : version + 1;
2283
2306
  track(version);
2284
2307
  }
2285
2308
  return accessor;
@@ -2335,15 +2358,21 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2335
2358
  });
2336
2359
  const root = createRootContext(hostRoot);
2337
2360
  const prevRoot = pushRoot(root);
2338
- const prevSub = setActiveSub(void 0);
2339
2361
  let nodes = [];
2362
+ let scopeDispose;
2363
+ const prevSub = setActiveSub(void 0);
2340
2364
  try {
2341
- const rendered = render2(itemSig, indexSig, key);
2342
- if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2343
- nodes = toNodeArray(rendered);
2344
- } else {
2345
- const element = createElement(rendered);
2346
- nodes = toNodeArray(element);
2365
+ scopeDispose = effectScope(() => {
2366
+ const rendered = render2(itemSig, indexSig, key);
2367
+ if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2368
+ nodes = toNodeArray(rendered);
2369
+ } else {
2370
+ const element = createElement(rendered);
2371
+ nodes = toNodeArray(element);
2372
+ }
2373
+ });
2374
+ if (scopeDispose) {
2375
+ root.cleanups.push(scopeDispose);
2347
2376
  }
2348
2377
  } finally {
2349
2378
  setActiveSub(prevSub);
@@ -2362,6 +2391,86 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2362
2391
  function getFirstNodeAfter(marker) {
2363
2392
  return marker.nextSibling;
2364
2393
  }
2394
+ function reorderBySwap(parent, first, second) {
2395
+ if (first === second) return false;
2396
+ const firstNodes = first.nodes;
2397
+ const secondNodes = second.nodes;
2398
+ if (firstNodes.length === 0 || secondNodes.length === 0) return false;
2399
+ const lastFirst = firstNodes[firstNodes.length - 1];
2400
+ const lastSecond = secondNodes[secondNodes.length - 1];
2401
+ const afterFirst = lastFirst.nextSibling;
2402
+ const afterSecond = lastSecond.nextSibling;
2403
+ moveNodesBefore(parent, firstNodes, afterSecond);
2404
+ moveNodesBefore(parent, secondNodes, afterFirst);
2405
+ return true;
2406
+ }
2407
+ function getLISIndices(sequence) {
2408
+ const predecessors = new Array(sequence.length);
2409
+ const result = [];
2410
+ for (let i = 0; i < sequence.length; i++) {
2411
+ const value = sequence[i];
2412
+ if (value < 0) {
2413
+ predecessors[i] = -1;
2414
+ continue;
2415
+ }
2416
+ let low = 0;
2417
+ let high = result.length;
2418
+ while (low < high) {
2419
+ const mid = low + high >> 1;
2420
+ if (sequence[result[mid]] < value) {
2421
+ low = mid + 1;
2422
+ } else {
2423
+ high = mid;
2424
+ }
2425
+ }
2426
+ predecessors[i] = low > 0 ? result[low - 1] : -1;
2427
+ if (low === result.length) {
2428
+ result.push(i);
2429
+ } else {
2430
+ result[low] = i;
2431
+ }
2432
+ }
2433
+ const lis = new Array(result.length);
2434
+ let k = result.length > 0 ? result[result.length - 1] : -1;
2435
+ for (let i = result.length - 1; i >= 0; i--) {
2436
+ lis[i] = k;
2437
+ k = predecessors[k];
2438
+ }
2439
+ return lis;
2440
+ }
2441
+ function reorderByLIS(parent, endMarker, prev, next) {
2442
+ const positions = /* @__PURE__ */ new Map();
2443
+ for (let i = 0; i < prev.length; i++) {
2444
+ positions.set(prev[i], i);
2445
+ }
2446
+ const sequence = new Array(next.length);
2447
+ for (let i = 0; i < next.length; i++) {
2448
+ const position = positions.get(next[i]);
2449
+ if (position === void 0) return false;
2450
+ sequence[i] = position;
2451
+ }
2452
+ const lisIndices = getLISIndices(sequence);
2453
+ if (lisIndices.length === sequence.length) return true;
2454
+ const inLIS = new Array(sequence.length).fill(false);
2455
+ for (let i = 0; i < lisIndices.length; i++) {
2456
+ inLIS[lisIndices[i]] = true;
2457
+ }
2458
+ let anchor = endMarker;
2459
+ let moved = false;
2460
+ for (let i = next.length - 1; i >= 0; i--) {
2461
+ const block = next[i];
2462
+ const nodes = block.nodes;
2463
+ if (nodes.length === 0) continue;
2464
+ if (inLIS[i]) {
2465
+ anchor = nodes[0];
2466
+ continue;
2467
+ }
2468
+ moveNodesBefore(parent, nodes, anchor);
2469
+ anchor = nodes[0];
2470
+ moved = true;
2471
+ }
2472
+ return moved;
2473
+ }
2365
2474
  function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
2366
2475
  const resolvedNeedsIndex = arguments.length >= 4 ? !!needsIndex : renderItem.length > 1;
2367
2476
  return createFineGrainedKeyedList(getItems, keyFn, renderItem, resolvedNeedsIndex);
@@ -2394,10 +2503,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2394
2503
  const prevOrderedBlocks = container.orderedBlocks;
2395
2504
  const nextOrderedBlocks = container.nextOrderedBlocks;
2396
2505
  const orderedIndexByKey = container.orderedIndexByKey;
2397
- newBlocks.clear();
2398
- nextOrderedBlocks.length = 0;
2399
- orderedIndexByKey.clear();
2400
- const createdBlocks = [];
2401
2506
  const newItems = getItems();
2402
2507
  if (newItems.length === 0) {
2403
2508
  if (oldBlocks.size > 0) {
@@ -2420,8 +2525,44 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2420
2525
  return;
2421
2526
  }
2422
2527
  const prevCount = prevOrderedBlocks.length;
2528
+ if (prevCount > 0 && newItems.length === prevCount && orderedIndexByKey.size === prevCount) {
2529
+ let stableOrder = true;
2530
+ const seen = /* @__PURE__ */ new Set();
2531
+ for (let i = 0; i < prevCount; i++) {
2532
+ const item = newItems[i];
2533
+ const key = keyFn(item, i);
2534
+ if (seen.has(key) || prevOrderedBlocks[i].key !== key) {
2535
+ stableOrder = false;
2536
+ break;
2537
+ }
2538
+ seen.add(key);
2539
+ }
2540
+ if (stableOrder) {
2541
+ for (let i = 0; i < prevCount; i++) {
2542
+ const item = newItems[i];
2543
+ const block = prevOrderedBlocks[i];
2544
+ if (block.rawItem !== item) {
2545
+ block.rawItem = item;
2546
+ block.item(item);
2547
+ }
2548
+ if (needsIndex && block.rawIndex !== i) {
2549
+ block.rawIndex = i;
2550
+ block.index(i);
2551
+ }
2552
+ }
2553
+ return;
2554
+ }
2555
+ }
2556
+ newBlocks.clear();
2557
+ nextOrderedBlocks.length = 0;
2558
+ orderedIndexByKey.clear();
2559
+ const createdBlocks = [];
2423
2560
  let appendCandidate = prevCount > 0 && newItems.length >= prevCount;
2424
2561
  const appendedBlocks = [];
2562
+ let mismatchCount = 0;
2563
+ let mismatchFirst = -1;
2564
+ let mismatchSecond = -1;
2565
+ let hasDuplicateKey = false;
2425
2566
  newItems.forEach((item, index) => {
2426
2567
  const key = keyFn(item, index);
2427
2568
  let block = oldBlocks.get(key);
@@ -2453,6 +2594,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2453
2594
  const position = orderedIndexByKey.get(key);
2454
2595
  if (position !== void 0) {
2455
2596
  appendCandidate = false;
2597
+ hasDuplicateKey = true;
2456
2598
  const prior = nextOrderedBlocks[position];
2457
2599
  if (prior && prior !== resolvedBlock) {
2458
2600
  destroyRoot(prior.root);
@@ -2469,8 +2611,17 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2469
2611
  appendCandidate = false;
2470
2612
  }
2471
2613
  }
2472
- orderedIndexByKey.set(key, nextOrderedBlocks.length);
2614
+ const nextIndex = nextOrderedBlocks.length;
2615
+ orderedIndexByKey.set(key, nextIndex);
2473
2616
  nextOrderedBlocks.push(resolvedBlock);
2617
+ if (mismatchCount < 3 && (nextIndex >= prevCount || prevOrderedBlocks[nextIndex] !== resolvedBlock)) {
2618
+ if (mismatchCount === 0) {
2619
+ mismatchFirst = nextIndex;
2620
+ } else if (mismatchCount === 1) {
2621
+ mismatchSecond = nextIndex;
2622
+ }
2623
+ mismatchCount++;
2624
+ }
2474
2625
  }
2475
2626
  if (appendCandidate && index >= prevCount) {
2476
2627
  appendedBlocks.push(resolvedBlock);
@@ -2511,7 +2662,26 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2511
2662
  }
2512
2663
  oldBlocks.clear();
2513
2664
  }
2514
- if (newBlocks.size > 0 || container.currentNodes.length > 0) {
2665
+ const canReorderInPlace = createdBlocks.length === 0 && oldBlocks.size === 0 && nextOrderedBlocks.length === prevOrderedBlocks.length;
2666
+ let skipReconcile = false;
2667
+ let updateNodeBuffer = true;
2668
+ if (canReorderInPlace && nextOrderedBlocks.length > 0 && !hasDuplicateKey) {
2669
+ if (mismatchCount === 0) {
2670
+ skipReconcile = true;
2671
+ updateNodeBuffer = false;
2672
+ } else if (mismatchCount === 2 && prevOrderedBlocks[mismatchFirst] === nextOrderedBlocks[mismatchSecond] && prevOrderedBlocks[mismatchSecond] === nextOrderedBlocks[mismatchFirst]) {
2673
+ if (reorderBySwap(
2674
+ parent,
2675
+ prevOrderedBlocks[mismatchFirst],
2676
+ prevOrderedBlocks[mismatchSecond]
2677
+ )) {
2678
+ skipReconcile = true;
2679
+ }
2680
+ } else if (reorderByLIS(parent, container.endMarker, prevOrderedBlocks, nextOrderedBlocks)) {
2681
+ skipReconcile = true;
2682
+ }
2683
+ }
2684
+ if (!skipReconcile && (newBlocks.size > 0 || container.currentNodes.length > 0)) {
2515
2685
  const prevNodes = container.currentNodes;
2516
2686
  const nextNodes = container.nextNodes;
2517
2687
  nextNodes.length = 0;
@@ -2526,6 +2696,20 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2526
2696
  reconcileArrays(parent, prevNodes, nextNodes);
2527
2697
  container.currentNodes = nextNodes;
2528
2698
  container.nextNodes = prevNodes;
2699
+ } else if (skipReconcile && updateNodeBuffer) {
2700
+ const prevNodes = container.currentNodes;
2701
+ const nextNodes = container.nextNodes;
2702
+ nextNodes.length = 0;
2703
+ nextNodes.push(container.startMarker);
2704
+ for (let i = 0; i < nextOrderedBlocks.length; i++) {
2705
+ const nodes = nextOrderedBlocks[i].nodes;
2706
+ for (let j = 0; j < nodes.length; j++) {
2707
+ nextNodes.push(nodes[j]);
2708
+ }
2709
+ }
2710
+ nextNodes.push(container.endMarker);
2711
+ container.currentNodes = nextNodes;
2712
+ container.nextNodes = prevNodes;
2529
2713
  }
2530
2714
  container.blocks = newBlocks;
2531
2715
  container.nextBlocks = oldBlocks;
@@ -3120,7 +3304,9 @@ function bindEvent(el, eventName, handler, options2) {
3120
3304
  const fn = resolveHandler();
3121
3305
  callEventHandler(fn, args[0], el);
3122
3306
  } catch (err) {
3123
- handleError(err, { source: "event", eventName }, rootRef);
3307
+ if (!handleError(err, { source: "event", eventName }, rootRef)) {
3308
+ throw err;
3309
+ }
3124
3310
  }
3125
3311
  };
3126
3312
  return () => {