@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.cjs CHANGED
@@ -262,7 +262,7 @@ function handleError(err, info, startRoot) {
262
262
  }
263
263
  }
264
264
  }
265
- throw error;
265
+ return false;
266
266
  }
267
267
  function handleSuspend(token, startRoot) {
268
268
  let root = startRoot ?? currentRoot;
@@ -307,6 +307,11 @@ var lowPriorityQueue = [];
307
307
  var enqueueMicrotask = typeof queueMicrotask === "function" ? queueMicrotask : (fn) => {
308
308
  Promise.resolve().then(fn);
309
309
  };
310
+ var inCleanup = false;
311
+ var SIGNAL_MARKER = Symbol.for("fict:signal");
312
+ var COMPUTED_MARKER = Symbol.for("fict:computed");
313
+ var EFFECT_MARKER = Symbol.for("fict:effect");
314
+ var EFFECT_SCOPE_MARKER = Symbol.for("fict:effectScope");
310
315
  function link(dep, sub, version) {
311
316
  const prevDep = sub.depsTail;
312
317
  if (prevDep !== void 0 && prevDep.dep === dep) return;
@@ -430,13 +435,21 @@ function checkDirty(firstLink, sub) {
430
435
  dirty = true;
431
436
  }
432
437
  } else if ((depFlags & MutablePending) === MutablePending) {
433
- if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
434
- stack = { value: link2, prev: stack };
438
+ if (!dep.deps) {
439
+ const nextDep = link2.nextDep;
440
+ if (nextDep !== void 0) {
441
+ link2 = nextDep;
442
+ continue;
443
+ }
444
+ } else {
445
+ if (link2.nextSub !== void 0 || link2.prevSub !== void 0) {
446
+ stack = { value: link2, prev: stack };
447
+ }
448
+ link2 = dep.deps;
449
+ sub = dep;
450
+ ++checkDepth;
451
+ continue;
435
452
  }
436
- link2 = dep.deps;
437
- sub = dep;
438
- ++checkDepth;
439
- continue;
440
453
  }
441
454
  if (!dirty) {
442
455
  const nextDep = link2.nextDep;
@@ -514,8 +527,12 @@ function disposeNode(node) {
514
527
  node.depsTail = void 0;
515
528
  node.flags = 0;
516
529
  purgeDeps(node);
517
- const sub = node.subs;
518
- if (sub !== void 0) unlink(sub, node);
530
+ let sub = node.subs;
531
+ while (sub !== void 0) {
532
+ const next = sub.nextSub;
533
+ unlink(sub);
534
+ sub = next;
535
+ }
519
536
  }
520
537
  function updateSignal(s) {
521
538
  s.flags = Mutable;
@@ -552,7 +569,15 @@ function updateComputed(c) {
552
569
  }
553
570
  function runEffect(e) {
554
571
  const flags = e.flags;
555
- if (flags & Dirty || flags & Pending && e.deps && checkDirty(e.deps, e)) {
572
+ if (flags & Dirty) {
573
+ if (e.runCleanup) {
574
+ inCleanup = true;
575
+ try {
576
+ e.runCleanup();
577
+ } finally {
578
+ inCleanup = false;
579
+ }
580
+ }
556
581
  ++cycle;
557
582
  effectRunDevtools(e);
558
583
  e.depsTail = void 0;
@@ -569,6 +594,35 @@ function runEffect(e) {
569
594
  e.flags = Watching;
570
595
  throw err;
571
596
  }
597
+ } else if (flags & Pending && e.deps) {
598
+ if (e.runCleanup) {
599
+ inCleanup = true;
600
+ try {
601
+ e.runCleanup();
602
+ } finally {
603
+ inCleanup = false;
604
+ }
605
+ }
606
+ if (checkDirty(e.deps, e)) {
607
+ ++cycle;
608
+ effectRunDevtools(e);
609
+ e.depsTail = void 0;
610
+ e.flags = WatchingRunning;
611
+ const prevSub = activeSub;
612
+ activeSub = e;
613
+ try {
614
+ e.fn();
615
+ activeSub = prevSub;
616
+ e.flags = Watching;
617
+ purgeDeps(e);
618
+ } catch (err) {
619
+ activeSub = prevSub;
620
+ e.flags = Watching;
621
+ throw err;
622
+ }
623
+ } else {
624
+ e.flags = Watching;
625
+ }
572
626
  } else {
573
627
  e.flags = Watching;
574
628
  }
@@ -647,7 +701,9 @@ function signal(initialValue) {
647
701
  __id: void 0
648
702
  };
649
703
  registerSignalDevtools(initialValue, s);
650
- return signalOper.bind(s);
704
+ const accessor = signalOper.bind(s);
705
+ accessor[SIGNAL_MARKER] = true;
706
+ return accessor;
651
707
  }
652
708
  function signalOper(value) {
653
709
  if (arguments.length > 0) {
@@ -664,7 +720,7 @@ function signalOper(value) {
664
720
  return;
665
721
  }
666
722
  const flags = this.flags;
667
- if (flags & Dirty) {
723
+ if (flags & Dirty && !inCleanup) {
668
724
  if (updateSignal(this)) {
669
725
  const subs = this.subs;
670
726
  if (subs !== void 0) shallowPropagate(subs);
@@ -692,6 +748,7 @@ function computed(getter) {
692
748
  getter
693
749
  };
694
750
  const bound = computedOper.bind(c);
751
+ bound[COMPUTED_MARKER] = true;
695
752
  return bound;
696
753
  }
697
754
  function computedOper() {
@@ -744,7 +801,35 @@ function effect(fn) {
744
801
  activeSub = prevSub;
745
802
  e.flags &= ~Running;
746
803
  }
747
- return effectOper.bind(e);
804
+ const disposer = effectOper.bind(e);
805
+ disposer[EFFECT_MARKER] = true;
806
+ return disposer;
807
+ }
808
+ function effectWithCleanup(fn, cleanupRunner) {
809
+ const e = {
810
+ fn,
811
+ subs: void 0,
812
+ subsTail: void 0,
813
+ deps: void 0,
814
+ depsTail: void 0,
815
+ flags: WatchingRunning,
816
+ runCleanup: cleanupRunner,
817
+ __id: void 0
818
+ };
819
+ registerEffectDevtools(e);
820
+ const prevSub = activeSub;
821
+ if (prevSub !== void 0) link(e, prevSub, 0);
822
+ activeSub = e;
823
+ try {
824
+ effectRunDevtools(e);
825
+ fn();
826
+ } finally {
827
+ activeSub = prevSub;
828
+ e.flags &= ~Running;
829
+ }
830
+ const disposer = effectOper.bind(e);
831
+ disposer[EFFECT_MARKER] = true;
832
+ return disposer;
748
833
  }
749
834
  function effectOper() {
750
835
  disposeNode(this);
@@ -759,7 +844,9 @@ function effectScope(fn) {
759
844
  } finally {
760
845
  activeSub = prevSub;
761
846
  }
762
- return effectScopeOper.bind(e);
847
+ const disposer = effectScopeOper.bind(e);
848
+ disposer[EFFECT_SCOPE_MARKER] = true;
849
+ return disposer;
763
850
  }
764
851
  function effectScopeOper() {
765
852
  disposeNode(this);
@@ -860,8 +947,11 @@ function createMemo(fn) {
860
947
  function createEffect(fn) {
861
948
  let cleanups = [];
862
949
  const rootForError = getCurrentRoot();
863
- const run = () => {
950
+ const doCleanup = () => {
864
951
  runCleanupList(cleanups);
952
+ cleanups = [];
953
+ };
954
+ const run = () => {
865
955
  const bucket = [];
866
956
  withEffectCleanups(bucket, () => {
867
957
  try {
@@ -878,7 +968,7 @@ function createEffect(fn) {
878
968
  });
879
969
  cleanups = bucket;
880
970
  };
881
- const disposeEffect = effect(run);
971
+ const disposeEffect = effectWithCleanup(run, doCleanup);
882
972
  const teardown = () => {
883
973
  runCleanupList(cleanups);
884
974
  disposeEffect();
@@ -889,11 +979,13 @@ function createEffect(fn) {
889
979
  function createRenderEffect(fn) {
890
980
  let cleanup;
891
981
  const rootForError = getCurrentRoot();
892
- const run = () => {
982
+ const doCleanup = () => {
893
983
  if (cleanup) {
894
984
  cleanup();
895
985
  cleanup = void 0;
896
986
  }
987
+ };
988
+ const run = () => {
897
989
  try {
898
990
  const maybeCleanup = fn();
899
991
  if (typeof maybeCleanup === "function") {
@@ -907,7 +999,7 @@ function createRenderEffect(fn) {
907
999
  throw err;
908
1000
  }
909
1001
  };
910
- const disposeEffect = effect(run);
1002
+ const disposeEffect = effectWithCleanup(run, doCleanup);
911
1003
  const teardown = () => {
912
1004
  if (cleanup) {
913
1005
  cleanup();
@@ -1439,9 +1531,6 @@ function mergeProps(...sources) {
1439
1531
  };
1440
1532
  return new Proxy({}, {
1441
1533
  get(_, prop) {
1442
- if (typeof prop === "symbol") {
1443
- return void 0;
1444
- }
1445
1534
  for (let i = validSources.length - 1; i >= 0; i--) {
1446
1535
  const src = validSources[i];
1447
1536
  const raw = resolveSource(src);
@@ -1722,7 +1811,8 @@ function applyRef(el, value) {
1722
1811
  if (typeof value === "function") {
1723
1812
  const refFn = value;
1724
1813
  refFn(el);
1725
- if (getCurrentRoot()) {
1814
+ const root = getCurrentRoot();
1815
+ if (root) {
1726
1816
  registerRootCleanup(() => {
1727
1817
  refFn(null);
1728
1818
  });
@@ -1730,7 +1820,8 @@ function applyRef(el, value) {
1730
1820
  } else if (value && typeof value === "object" && "current" in value) {
1731
1821
  const refObj = value;
1732
1822
  refObj.current = el;
1733
- if (getCurrentRoot()) {
1823
+ const root = getCurrentRoot();
1824
+ if (root) {
1734
1825
  registerRootCleanup(() => {
1735
1826
  refObj.current = null;
1736
1827
  });
@@ -2141,8 +2232,6 @@ function reconcileArrays(parentNode, a, b) {
2141
2232
  }
2142
2233
  }
2143
2234
  }
2144
-
2145
- // src/list-helpers.ts
2146
2235
  function moveNodesBefore(parent, nodes, anchor) {
2147
2236
  for (let i = nodes.length - 1; i >= 0; i--) {
2148
2237
  const node = nodes[i];
@@ -2204,6 +2293,7 @@ function removeBlockRange(block) {
2204
2293
  cursor = next;
2205
2294
  }
2206
2295
  }
2296
+ var MAX_SAFE_VERSION = 9007199254740991;
2207
2297
  function createVersionedSignalAccessor(initialValue) {
2208
2298
  let current = initialValue;
2209
2299
  let version = 0;
@@ -2214,7 +2304,7 @@ function createVersionedSignalAccessor(initialValue) {
2214
2304
  return current;
2215
2305
  }
2216
2306
  current = value;
2217
- version++;
2307
+ version = version >= MAX_SAFE_VERSION ? 1 : version + 1;
2218
2308
  track(version);
2219
2309
  }
2220
2310
  return accessor;
@@ -2270,15 +2360,21 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2270
2360
  });
2271
2361
  const root = createRootContext(hostRoot);
2272
2362
  const prevRoot = pushRoot(root);
2273
- const prevSub = setActiveSub(void 0);
2274
2363
  let nodes = [];
2364
+ let scopeDispose;
2365
+ const prevSub = setActiveSub(void 0);
2275
2366
  try {
2276
- const rendered = render2(itemSig, indexSig, key);
2277
- if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2278
- nodes = toNodeArray(rendered);
2279
- } else {
2280
- const element = createElement(rendered);
2281
- nodes = toNodeArray(element);
2367
+ scopeDispose = effectScope(() => {
2368
+ const rendered = render2(itemSig, indexSig, key);
2369
+ if (rendered instanceof Node || Array.isArray(rendered) && rendered.every((n) => n instanceof Node)) {
2370
+ nodes = toNodeArray(rendered);
2371
+ } else {
2372
+ const element = createElement(rendered);
2373
+ nodes = toNodeArray(element);
2374
+ }
2375
+ });
2376
+ if (scopeDispose) {
2377
+ root.cleanups.push(scopeDispose);
2282
2378
  }
2283
2379
  } finally {
2284
2380
  setActiveSub(prevSub);
@@ -2297,6 +2393,86 @@ function createKeyedBlock(key, item, index, render2, needsIndex = true, hostRoot
2297
2393
  function getFirstNodeAfter(marker) {
2298
2394
  return marker.nextSibling;
2299
2395
  }
2396
+ function reorderBySwap(parent, first, second) {
2397
+ if (first === second) return false;
2398
+ const firstNodes = first.nodes;
2399
+ const secondNodes = second.nodes;
2400
+ if (firstNodes.length === 0 || secondNodes.length === 0) return false;
2401
+ const lastFirst = firstNodes[firstNodes.length - 1];
2402
+ const lastSecond = secondNodes[secondNodes.length - 1];
2403
+ const afterFirst = lastFirst.nextSibling;
2404
+ const afterSecond = lastSecond.nextSibling;
2405
+ moveNodesBefore(parent, firstNodes, afterSecond);
2406
+ moveNodesBefore(parent, secondNodes, afterFirst);
2407
+ return true;
2408
+ }
2409
+ function getLISIndices(sequence) {
2410
+ const predecessors = new Array(sequence.length);
2411
+ const result = [];
2412
+ for (let i = 0; i < sequence.length; i++) {
2413
+ const value = sequence[i];
2414
+ if (value < 0) {
2415
+ predecessors[i] = -1;
2416
+ continue;
2417
+ }
2418
+ let low = 0;
2419
+ let high = result.length;
2420
+ while (low < high) {
2421
+ const mid = low + high >> 1;
2422
+ if (sequence[result[mid]] < value) {
2423
+ low = mid + 1;
2424
+ } else {
2425
+ high = mid;
2426
+ }
2427
+ }
2428
+ predecessors[i] = low > 0 ? result[low - 1] : -1;
2429
+ if (low === result.length) {
2430
+ result.push(i);
2431
+ } else {
2432
+ result[low] = i;
2433
+ }
2434
+ }
2435
+ const lis = new Array(result.length);
2436
+ let k = result.length > 0 ? result[result.length - 1] : -1;
2437
+ for (let i = result.length - 1; i >= 0; i--) {
2438
+ lis[i] = k;
2439
+ k = predecessors[k];
2440
+ }
2441
+ return lis;
2442
+ }
2443
+ function reorderByLIS(parent, endMarker, prev, next) {
2444
+ const positions = /* @__PURE__ */ new Map();
2445
+ for (let i = 0; i < prev.length; i++) {
2446
+ positions.set(prev[i], i);
2447
+ }
2448
+ const sequence = new Array(next.length);
2449
+ for (let i = 0; i < next.length; i++) {
2450
+ const position = positions.get(next[i]);
2451
+ if (position === void 0) return false;
2452
+ sequence[i] = position;
2453
+ }
2454
+ const lisIndices = getLISIndices(sequence);
2455
+ if (lisIndices.length === sequence.length) return true;
2456
+ const inLIS = new Array(sequence.length).fill(false);
2457
+ for (let i = 0; i < lisIndices.length; i++) {
2458
+ inLIS[lisIndices[i]] = true;
2459
+ }
2460
+ let anchor = endMarker;
2461
+ let moved = false;
2462
+ for (let i = next.length - 1; i >= 0; i--) {
2463
+ const block = next[i];
2464
+ const nodes = block.nodes;
2465
+ if (nodes.length === 0) continue;
2466
+ if (inLIS[i]) {
2467
+ anchor = nodes[0];
2468
+ continue;
2469
+ }
2470
+ moveNodesBefore(parent, nodes, anchor);
2471
+ anchor = nodes[0];
2472
+ moved = true;
2473
+ }
2474
+ return moved;
2475
+ }
2300
2476
  function createKeyedList(getItems, keyFn, renderItem, needsIndex) {
2301
2477
  const resolvedNeedsIndex = arguments.length >= 4 ? !!needsIndex : renderItem.length > 1;
2302
2478
  return createFineGrainedKeyedList(getItems, keyFn, renderItem, resolvedNeedsIndex);
@@ -2329,10 +2505,6 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2329
2505
  const prevOrderedBlocks = container.orderedBlocks;
2330
2506
  const nextOrderedBlocks = container.nextOrderedBlocks;
2331
2507
  const orderedIndexByKey = container.orderedIndexByKey;
2332
- newBlocks.clear();
2333
- nextOrderedBlocks.length = 0;
2334
- orderedIndexByKey.clear();
2335
- const createdBlocks = [];
2336
2508
  const newItems = getItems();
2337
2509
  if (newItems.length === 0) {
2338
2510
  if (oldBlocks.size > 0) {
@@ -2355,8 +2527,44 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2355
2527
  return;
2356
2528
  }
2357
2529
  const prevCount = prevOrderedBlocks.length;
2530
+ if (prevCount > 0 && newItems.length === prevCount && orderedIndexByKey.size === prevCount) {
2531
+ let stableOrder = true;
2532
+ const seen = /* @__PURE__ */ new Set();
2533
+ for (let i = 0; i < prevCount; i++) {
2534
+ const item = newItems[i];
2535
+ const key = keyFn(item, i);
2536
+ if (seen.has(key) || prevOrderedBlocks[i].key !== key) {
2537
+ stableOrder = false;
2538
+ break;
2539
+ }
2540
+ seen.add(key);
2541
+ }
2542
+ if (stableOrder) {
2543
+ for (let i = 0; i < prevCount; i++) {
2544
+ const item = newItems[i];
2545
+ const block = prevOrderedBlocks[i];
2546
+ if (block.rawItem !== item) {
2547
+ block.rawItem = item;
2548
+ block.item(item);
2549
+ }
2550
+ if (needsIndex && block.rawIndex !== i) {
2551
+ block.rawIndex = i;
2552
+ block.index(i);
2553
+ }
2554
+ }
2555
+ return;
2556
+ }
2557
+ }
2558
+ newBlocks.clear();
2559
+ nextOrderedBlocks.length = 0;
2560
+ orderedIndexByKey.clear();
2561
+ const createdBlocks = [];
2358
2562
  let appendCandidate = prevCount > 0 && newItems.length >= prevCount;
2359
2563
  const appendedBlocks = [];
2564
+ let mismatchCount = 0;
2565
+ let mismatchFirst = -1;
2566
+ let mismatchSecond = -1;
2567
+ let hasDuplicateKey = false;
2360
2568
  newItems.forEach((item, index) => {
2361
2569
  const key = keyFn(item, index);
2362
2570
  let block = oldBlocks.get(key);
@@ -2388,6 +2596,7 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2388
2596
  const position = orderedIndexByKey.get(key);
2389
2597
  if (position !== void 0) {
2390
2598
  appendCandidate = false;
2599
+ hasDuplicateKey = true;
2391
2600
  const prior = nextOrderedBlocks[position];
2392
2601
  if (prior && prior !== resolvedBlock) {
2393
2602
  destroyRoot(prior.root);
@@ -2404,8 +2613,17 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2404
2613
  appendCandidate = false;
2405
2614
  }
2406
2615
  }
2407
- orderedIndexByKey.set(key, nextOrderedBlocks.length);
2616
+ const nextIndex = nextOrderedBlocks.length;
2617
+ orderedIndexByKey.set(key, nextIndex);
2408
2618
  nextOrderedBlocks.push(resolvedBlock);
2619
+ if (mismatchCount < 3 && (nextIndex >= prevCount || prevOrderedBlocks[nextIndex] !== resolvedBlock)) {
2620
+ if (mismatchCount === 0) {
2621
+ mismatchFirst = nextIndex;
2622
+ } else if (mismatchCount === 1) {
2623
+ mismatchSecond = nextIndex;
2624
+ }
2625
+ mismatchCount++;
2626
+ }
2409
2627
  }
2410
2628
  if (appendCandidate && index >= prevCount) {
2411
2629
  appendedBlocks.push(resolvedBlock);
@@ -2446,7 +2664,26 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2446
2664
  }
2447
2665
  oldBlocks.clear();
2448
2666
  }
2449
- if (newBlocks.size > 0 || container.currentNodes.length > 0) {
2667
+ const canReorderInPlace = createdBlocks.length === 0 && oldBlocks.size === 0 && nextOrderedBlocks.length === prevOrderedBlocks.length;
2668
+ let skipReconcile = false;
2669
+ let updateNodeBuffer = true;
2670
+ if (canReorderInPlace && nextOrderedBlocks.length > 0 && !hasDuplicateKey) {
2671
+ if (mismatchCount === 0) {
2672
+ skipReconcile = true;
2673
+ updateNodeBuffer = false;
2674
+ } else if (mismatchCount === 2 && prevOrderedBlocks[mismatchFirst] === nextOrderedBlocks[mismatchSecond] && prevOrderedBlocks[mismatchSecond] === nextOrderedBlocks[mismatchFirst]) {
2675
+ if (reorderBySwap(
2676
+ parent,
2677
+ prevOrderedBlocks[mismatchFirst],
2678
+ prevOrderedBlocks[mismatchSecond]
2679
+ )) {
2680
+ skipReconcile = true;
2681
+ }
2682
+ } else if (reorderByLIS(parent, container.endMarker, prevOrderedBlocks, nextOrderedBlocks)) {
2683
+ skipReconcile = true;
2684
+ }
2685
+ }
2686
+ if (!skipReconcile && (newBlocks.size > 0 || container.currentNodes.length > 0)) {
2450
2687
  const prevNodes = container.currentNodes;
2451
2688
  const nextNodes = container.nextNodes;
2452
2689
  nextNodes.length = 0;
@@ -2461,6 +2698,20 @@ function createFineGrainedKeyedList(getItems, keyFn, renderItem, needsIndex) {
2461
2698
  reconcileArrays(parent, prevNodes, nextNodes);
2462
2699
  container.currentNodes = nextNodes;
2463
2700
  container.nextNodes = prevNodes;
2701
+ } else if (skipReconcile && updateNodeBuffer) {
2702
+ const prevNodes = container.currentNodes;
2703
+ const nextNodes = container.nextNodes;
2704
+ nextNodes.length = 0;
2705
+ nextNodes.push(container.startMarker);
2706
+ for (let i = 0; i < nextOrderedBlocks.length; i++) {
2707
+ const nodes = nextOrderedBlocks[i].nodes;
2708
+ for (let j = 0; j < nodes.length; j++) {
2709
+ nextNodes.push(nodes[j]);
2710
+ }
2711
+ }
2712
+ nextNodes.push(container.endMarker);
2713
+ container.currentNodes = nextNodes;
2714
+ container.nextNodes = prevNodes;
2464
2715
  }
2465
2716
  container.blocks = newBlocks;
2466
2717
  container.nextBlocks = oldBlocks;
@@ -3055,7 +3306,9 @@ function bindEvent(el, eventName, handler, options2) {
3055
3306
  const fn = resolveHandler();
3056
3307
  callEventHandler(fn, args[0], el);
3057
3308
  } catch (err) {
3058
- handleError(err, { source: "event", eventName }, rootRef);
3309
+ if (!handleError(err, { source: "event", eventName }, rootRef)) {
3310
+ throw err;
3311
+ }
3059
3312
  }
3060
3313
  };
3061
3314
  return () => {