@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/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;
@@ -305,6 +305,11 @@ var lowPriorityQueue = [];
305
305
  var enqueueMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : (fn) => {
306
306
  Promise.resolve().then(fn);
307
307
  };
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");
308
313
  function link(dep, sub, version) {
309
314
  const prevDep = sub.depsTail;
310
315
  if (prevDep !== void 0 && prevDep.dep === dep) return;
@@ -428,13 +433,21 @@ function checkDirty(firstLink, sub) {
428
433
  dirty = true;
429
434
  }
430
435
  } else if ((depFlags & MutablePending) === MutablePending) {
431
- if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
432
- 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;
433
450
  }
434
- link2 = dep.deps;
435
- sub = dep;
436
- ++checkDepth;
437
- continue;
438
451
  }
439
452
  if (!dirty) {
440
453
  const nextDep = link2.nextDep;
@@ -512,8 +525,12 @@ function disposeNode(node) {
512
525
  node.depsTail = void 0;
513
526
  node.flags = 0;
514
527
  purgeDeps(node);
515
- const sub = node.subs;
516
- 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
+ }
517
534
  }
518
535
  function updateSignal(s) {
519
536
  s.flags = Mutable;
@@ -550,7 +567,15 @@ function updateComputed(c) {
550
567
  }
551
568
  function runEffect(e) {
552
569
  const flags = e.flags;
553
- if (flags & Dirty || flags & Pending && e.deps && checkDirty(e.deps, e)) {
570
+ if (flags & Dirty) {
571
+ if (e.runCleanup) {
572
+ inCleanup = true;
573
+ try {
574
+ e.runCleanup();
575
+ } finally {
576
+ inCleanup = false;
577
+ }
578
+ }
554
579
  ++cycle;
555
580
  effectRunDevtools(e);
556
581
  e.depsTail = void 0;
@@ -567,6 +592,35 @@ function runEffect(e) {
567
592
  e.flags = Watching;
568
593
  throw err;
569
594
  }
595
+ } else if (flags & Pending && e.deps) {
596
+ if (e.runCleanup) {
597
+ inCleanup = true;
598
+ try {
599
+ e.runCleanup();
600
+ } finally {
601
+ inCleanup = false;
602
+ }
603
+ }
604
+ if (checkDirty(e.deps, e)) {
605
+ ++cycle;
606
+ effectRunDevtools(e);
607
+ e.depsTail = void 0;
608
+ e.flags = WatchingRunning;
609
+ const prevSub = activeSub;
610
+ activeSub = e;
611
+ try {
612
+ e.fn();
613
+ activeSub = prevSub;
614
+ e.flags = Watching;
615
+ purgeDeps(e);
616
+ } catch (err) {
617
+ activeSub = prevSub;
618
+ e.flags = Watching;
619
+ throw err;
620
+ }
621
+ } else {
622
+ e.flags = Watching;
623
+ }
570
624
  } else {
571
625
  e.flags = Watching;
572
626
  }
@@ -645,7 +699,9 @@ function signal(initialValue) {
645
699
  __id: void 0
646
700
  };
647
701
  registerSignalDevtools(initialValue, s);
648
- return signalOper.bind(s);
702
+ const accessor = signalOper.bind(s);
703
+ accessor[SIGNAL_MARKER] = true;
704
+ return accessor;
649
705
  }
650
706
  function signalOper(value) {
651
707
  if (arguments.length > 0) {
@@ -662,7 +718,7 @@ function signalOper(value) {
662
718
  return;
663
719
  }
664
720
  const flags = this.flags;
665
- if (flags & Dirty) {
721
+ if (flags & Dirty && !inCleanup) {
666
722
  if (updateSignal(this)) {
667
723
  const subs = this.subs;
668
724
  if (subs !== void 0) shallowPropagate(subs);
@@ -690,6 +746,7 @@ function computed(getter) {
690
746
  getter
691
747
  };
692
748
  const bound = computedOper.bind(c);
749
+ bound[COMPUTED_MARKER] = true;
693
750
  return bound;
694
751
  }
695
752
  function computedOper() {
@@ -742,7 +799,35 @@ function effect(fn) {
742
799
  activeSub = prevSub;
743
800
  e.flags &= ~Running;
744
801
  }
745
- return effectOper.bind(e);
802
+ const disposer = effectOper.bind(e);
803
+ disposer[EFFECT_MARKER] = true;
804
+ return disposer;
805
+ }
806
+ function effectWithCleanup(fn, cleanupRunner) {
807
+ const e = {
808
+ fn,
809
+ subs: void 0,
810
+ subsTail: void 0,
811
+ deps: void 0,
812
+ depsTail: void 0,
813
+ flags: WatchingRunning,
814
+ runCleanup: cleanupRunner,
815
+ __id: void 0
816
+ };
817
+ registerEffectDevtools(e);
818
+ const prevSub = activeSub;
819
+ if (prevSub !== void 0) link(e, prevSub, 0);
820
+ activeSub = e;
821
+ try {
822
+ effectRunDevtools(e);
823
+ fn();
824
+ } finally {
825
+ activeSub = prevSub;
826
+ e.flags &= ~Running;
827
+ }
828
+ const disposer = effectOper.bind(e);
829
+ disposer[EFFECT_MARKER] = true;
830
+ return disposer;
746
831
  }
747
832
  function effectOper() {
748
833
  disposeNode(this);
@@ -757,7 +842,9 @@ function effectScope(fn) {
757
842
  } finally {
758
843
  activeSub = prevSub;
759
844
  }
760
- return effectScopeOper.bind(e);
845
+ const disposer = effectScopeOper.bind(e);
846
+ disposer[EFFECT_SCOPE_MARKER] = true;
847
+ return disposer;
761
848
  }
762
849
  function effectScopeOper() {
763
850
  disposeNode(this);
@@ -858,8 +945,11 @@ function createMemo(fn) {
858
945
  function createEffect(fn) {
859
946
  let cleanups = [];
860
947
  const rootForError = getCurrentRoot();
861
- const run = () => {
948
+ const doCleanup = () => {
862
949
  runCleanupList(cleanups);
950
+ cleanups = [];
951
+ };
952
+ const run = () => {
863
953
  const bucket = [];
864
954
  withEffectCleanups(bucket, () => {
865
955
  try {
@@ -876,7 +966,7 @@ function createEffect(fn) {
876
966
  });
877
967
  cleanups = bucket;
878
968
  };
879
- const disposeEffect = effect(run);
969
+ const disposeEffect = effectWithCleanup(run, doCleanup);
880
970
  const teardown = () => {
881
971
  runCleanupList(cleanups);
882
972
  disposeEffect();
@@ -887,11 +977,13 @@ function createEffect(fn) {
887
977
  function createRenderEffect(fn) {
888
978
  let cleanup;
889
979
  const rootForError = getCurrentRoot();
890
- const run = () => {
980
+ const doCleanup = () => {
891
981
  if (cleanup) {
892
982
  cleanup();
893
983
  cleanup = void 0;
894
984
  }
985
+ };
986
+ const run = () => {
895
987
  try {
896
988
  const maybeCleanup = fn();
897
989
  if (typeof maybeCleanup === "function") {
@@ -905,7 +997,7 @@ function createRenderEffect(fn) {
905
997
  throw err;
906
998
  }
907
999
  };
908
- const disposeEffect = effect(run);
1000
+ const disposeEffect = effectWithCleanup(run, doCleanup);
909
1001
  const teardown = () => {
910
1002
  if (cleanup) {
911
1003
  cleanup();
@@ -1437,9 +1529,6 @@ function mergeProps(...sources) {
1437
1529
  };
1438
1530
  return new Proxy({}, {
1439
1531
  get(_, prop) {
1440
- if (typeof prop === "symbol") {
1441
- return void 0;
1442
- }
1443
1532
  for (let i = validSources.length - 1; i >= 0; i--) {
1444
1533
  const src = validSources[i];
1445
1534
  const raw = resolveSource(src);
@@ -1720,7 +1809,8 @@ function applyRef(el, value) {
1720
1809
  if (typeof value === "function") {
1721
1810
  const refFn = value;
1722
1811
  refFn(el);
1723
- if (getCurrentRoot()) {
1812
+ const root = getCurrentRoot();
1813
+ if (root) {
1724
1814
  registerRootCleanup(() => {
1725
1815
  refFn(null);
1726
1816
  });
@@ -1728,7 +1818,8 @@ function applyRef(el, value) {
1728
1818
  } else if (value && typeof value === "object" && "current" in value) {
1729
1819
  const refObj = value;
1730
1820
  refObj.current = el;
1731
- if (getCurrentRoot()) {
1821
+ const root = getCurrentRoot();
1822
+ if (root) {
1732
1823
  registerRootCleanup(() => {
1733
1824
  refObj.current = null;
1734
1825
  });
@@ -2139,8 +2230,6 @@ function reconcileArrays(parentNode, a, b) {
2139
2230
  }
2140
2231
  }
2141
2232
  }
2142
-
2143
- // src/list-helpers.ts
2144
2233
  function moveNodesBefore(parent, nodes, anchor) {
2145
2234
  for (let i = nodes.length - 1; i >= 0; i--) {
2146
2235
  const node = nodes[i];
@@ -2202,6 +2291,7 @@ function removeBlockRange(block) {
2202
2291
  cursor = next;
2203
2292
  }
2204
2293
  }
2294
+ var MAX_SAFE_VERSION = 9007199254740991;
2205
2295
  function createVersionedSignalAccessor(initialValue) {
2206
2296
  let current = initialValue;
2207
2297
  let version = 0;
@@ -2212,7 +2302,7 @@ function createVersionedSignalAccessor(initialValue) {
2212
2302
  return current;
2213
2303
  }
2214
2304
  current = value;
2215
- version++;
2305
+ version = version >= MAX_SAFE_VERSION ? 1 : version + 1;
2216
2306
  track(version);
2217
2307
  }
2218
2308
  return accessor;
@@ -2268,15 +2358,21 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2268
2358
  });
2269
2359
  const root = createRootContext(hostRoot);
2270
2360
  const prevRoot = pushRoot(root);
2271
- const prevSub = setActiveSub(void 0);
2272
2361
  let nodes = [];
2362
+ let scopeDispose;
2363
+ const prevSub = setActiveSub(void 0);
2273
2364
  try {
2274
- const rendered = render2(itemSig, indexSig, key);
2275
- if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2276
- nodes = toNodeArray(rendered);
2277
- } else {
2278
- const element = createElement(rendered);
2279
- 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);
2280
2376
  }
2281
2377
  } finally {
2282
2378
  setActiveSub(prevSub);
@@ -2295,6 +2391,86 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2295
2391
  function getFirstNodeAfter(marker) {
2296
2392
  return marker.nextSibling;
2297
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
+ }
2298
2474
  function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
2299
2475
  const resolvedNeedsIndex = arguments.length >= 4 ? !!needsIndex : renderItem.length > 1;
2300
2476
  return createFineGrainedKeyedList(getItems, keyFn, renderItem, resolvedNeedsIndex);
@@ -2327,10 +2503,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2327
2503
  const prevOrderedBlocks = container.orderedBlocks;
2328
2504
  const nextOrderedBlocks = container.nextOrderedBlocks;
2329
2505
  const orderedIndexByKey = container.orderedIndexByKey;
2330
- newBlocks.clear();
2331
- nextOrderedBlocks.length = 0;
2332
- orderedIndexByKey.clear();
2333
- const createdBlocks = [];
2334
2506
  const newItems = getItems();
2335
2507
  if (newItems.length === 0) {
2336
2508
  if (oldBlocks.size > 0) {
@@ -2353,8 +2525,44 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2353
2525
  return;
2354
2526
  }
2355
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 = [];
2356
2560
  let appendCandidate = prevCount > 0 && newItems.length >= prevCount;
2357
2561
  const appendedBlocks = [];
2562
+ let mismatchCount = 0;
2563
+ let mismatchFirst = -1;
2564
+ let mismatchSecond = -1;
2565
+ let hasDuplicateKey = false;
2358
2566
  newItems.forEach((item, index) => {
2359
2567
  const key = keyFn(item, index);
2360
2568
  let block = oldBlocks.get(key);
@@ -2386,6 +2594,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2386
2594
  const position = orderedIndexByKey.get(key);
2387
2595
  if (position !== void 0) {
2388
2596
  appendCandidate = false;
2597
+ hasDuplicateKey = true;
2389
2598
  const prior = nextOrderedBlocks[position];
2390
2599
  if (prior && prior !== resolvedBlock) {
2391
2600
  destroyRoot(prior.root);
@@ -2402,8 +2611,17 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2402
2611
  appendCandidate = false;
2403
2612
  }
2404
2613
  }
2405
- orderedIndexByKey.set(key, nextOrderedBlocks.length);
2614
+ const nextIndex = nextOrderedBlocks.length;
2615
+ orderedIndexByKey.set(key, nextIndex);
2406
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
+ }
2407
2625
  }
2408
2626
  if (appendCandidate && index >= prevCount) {
2409
2627
  appendedBlocks.push(resolvedBlock);
@@ -2444,7 +2662,26 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2444
2662
  }
2445
2663
  oldBlocks.clear();
2446
2664
  }
2447
- 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)) {
2448
2685
  const prevNodes = container.currentNodes;
2449
2686
  const nextNodes = container.nextNodes;
2450
2687
  nextNodes.length = 0;
@@ -2459,6 +2696,20 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2459
2696
  reconcileArrays(parent, prevNodes, nextNodes);
2460
2697
  container.currentNodes = nextNodes;
2461
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;
2462
2713
  }
2463
2714
  container.blocks = newBlocks;
2464
2715
  container.nextBlocks = oldBlocks;
@@ -3053,7 +3304,9 @@ function bindEvent(el, eventName, handler, options2) {
3053
3304
  const fn = resolveHandler();
3054
3305
  callEventHandler(fn, args[0], el);
3055
3306
  } catch (err) {
3056
- handleError(err, { source: "event", eventName }, rootRef);
3307
+ if (!handleError(err, { source: "event", eventName }, rootRef)) {
3308
+ throw err;
3309
+ }
3057
3310
  }
3058
3311
  };
3059
3312
  return () => {