@fluixi/reactive 1.0.0-alpha.104 → 1.0.0-alpha.106

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.
Files changed (82) hide show
  1. package/dist/cdn/reactive.cjs +74 -34
  2. package/dist/cdn/reactive.global.js +2 -2
  3. package/dist/cdn/reactive.mjs +74 -34
  4. package/dist/cdn/signal.cjs +23 -3
  5. package/dist/cdn/signal.global.js +2 -2
  6. package/dist/cdn/signal.mjs +23 -3
  7. package/dist/index.cjs +74 -34
  8. package/dist/index.mjs +74 -34
  9. package/dist/lib/index.cjs +75 -35
  10. package/dist/lib/index.mjs +75 -35
  11. package/dist/lib/observable/index.cjs +1 -1
  12. package/dist/lib/observable/index.mjs +1 -1
  13. package/dist/lib/signal/api.cjs +40 -14
  14. package/dist/lib/signal/api.mjs +40 -14
  15. package/dist/lib/signal/cached-resource.cjs +36 -13
  16. package/dist/lib/signal/cached-resource.mjs +36 -13
  17. package/dist/lib/signal/graph/children.cjs +25 -6
  18. package/dist/lib/signal/graph/children.mjs +25 -6
  19. package/dist/lib/signal/graph/compat.cjs +41 -15
  20. package/dist/lib/signal/graph/compat.d.ts.map +1 -1
  21. package/dist/lib/signal/graph/compat.js +20 -11
  22. package/dist/lib/signal/graph/compat.mjs +41 -15
  23. package/dist/lib/signal/graph/context-children.cjs +1 -1
  24. package/dist/lib/signal/graph/context-children.mjs +1 -1
  25. package/dist/lib/signal/graph/core.cjs +24 -4
  26. package/dist/lib/signal/graph/core.d.ts +10 -0
  27. package/dist/lib/signal/graph/core.d.ts.map +1 -1
  28. package/dist/lib/signal/graph/core.js +27 -2
  29. package/dist/lib/signal/graph/core.mjs +24 -4
  30. package/dist/lib/signal/graph/debounced.cjs +35 -12
  31. package/dist/lib/signal/graph/debounced.mjs +35 -12
  32. package/dist/lib/signal/graph/derived.cjs +35 -12
  33. package/dist/lib/signal/graph/derived.mjs +35 -12
  34. package/dist/lib/signal/graph/detect.cjs +1 -1
  35. package/dist/lib/signal/graph/detect.mjs +1 -1
  36. package/dist/lib/signal/graph/error-boundary.cjs +10 -8
  37. package/dist/lib/signal/graph/error-boundary.mjs +10 -8
  38. package/dist/lib/signal/graph/index.cjs +24 -4
  39. package/dist/lib/signal/graph/index.mjs +24 -4
  40. package/dist/lib/signal/graph/observe.cjs +1 -1
  41. package/dist/lib/signal/graph/observe.mjs +1 -1
  42. package/dist/lib/signal/graph/resource.cjs +35 -12
  43. package/dist/lib/signal/graph/resource.mjs +35 -12
  44. package/dist/lib/signal/graph/runtime.cjs +29 -5
  45. package/dist/lib/signal/graph/runtime.d.ts.map +1 -1
  46. package/dist/lib/signal/graph/runtime.js +5 -3
  47. package/dist/lib/signal/graph/runtime.mjs +29 -5
  48. package/dist/lib/signal/graph/signal-next.cjs +67 -29
  49. package/dist/lib/signal/graph/signal-next.mjs +67 -29
  50. package/dist/lib/signal/graph/state.cjs +1 -1
  51. package/dist/lib/signal/graph/state.mjs +1 -1
  52. package/dist/lib/signal/graph/suspense.cjs +10 -8
  53. package/dist/lib/signal/graph/suspense.mjs +10 -8
  54. package/dist/lib/signal/index-legacy.cjs +67 -29
  55. package/dist/lib/signal/index-legacy.mjs +67 -29
  56. package/dist/lib/signal/index.cjs +67 -29
  57. package/dist/lib/signal/index.mjs +67 -29
  58. package/dist/lib/signal/list.cjs +58 -25
  59. package/dist/lib/signal/list.d.ts +3 -8
  60. package/dist/lib/signal/list.d.ts.map +1 -1
  61. package/dist/lib/signal/list.js +40 -18
  62. package/dist/lib/signal/list.mjs +58 -25
  63. package/dist/lib/signal/resource-api.cjs +35 -12
  64. package/dist/lib/signal/resource-api.mjs +35 -12
  65. package/dist/lib/signal/signal.cjs +41 -15
  66. package/dist/lib/signal/signal.mjs +41 -15
  67. package/dist/lib/signal/utilities.cjs +41 -15
  68. package/dist/lib/signal/utilities.mjs +41 -15
  69. package/dist/lib/store/api.cjs +9 -7
  70. package/dist/lib/store/api.mjs +9 -7
  71. package/dist/lib/store/index.cjs +10 -8
  72. package/dist/lib/store/index.mjs +10 -8
  73. package/dist/lib/store/store.cjs +9 -7
  74. package/dist/lib/store/store.d.ts.map +1 -1
  75. package/dist/lib/store/store.js +8 -6
  76. package/dist/lib/store/store.mjs +9 -7
  77. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  78. package/dist/version.generated.cjs +2 -2
  79. package/dist/version.generated.d.ts +1 -1
  80. package/dist/version.generated.js +1 -1
  81. package/dist/version.generated.mjs +2 -2
  82. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- /*! @fluixi/reactive v1.0.0-alpha.104 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/reactive v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -460,7 +460,7 @@ var Observable = class extends Subscription {
460
460
 
461
461
  // src/lib/signal/graph/detect.ts
462
462
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
463
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
463
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
464
464
  var $SIGNAL = /* @__PURE__ */ Symbol.for("signal");
465
465
  var $MEMO = /* @__PURE__ */ Symbol.for("memo");
466
466
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
@@ -494,7 +494,7 @@ function makeArrayFlat(values) {
494
494
  }
495
495
 
496
496
  // src/version.generated.ts
497
- var VERSION = "1.0.0-alpha.104";
497
+ var VERSION = "1.0.0-alpha.106";
498
498
 
499
499
  // src/lib/signal/graph/state.ts
500
500
  var MAJOR = VERSION.split(".")[0];
@@ -636,6 +636,7 @@ var Computed = class extends Source {
636
636
  super();
637
637
  this._threw = false;
638
638
  this._initialized = false;
639
+ this._disposed = false;
639
640
  this.state = DIRTY;
640
641
  /** Nodes this computation read on its last run. Rebuilt on every run. */
641
642
  this.sources = null;
@@ -651,8 +652,23 @@ var Computed = class extends Source {
651
652
  if (this._threw) throw this._error;
652
653
  return this._value;
653
654
  }
655
+ /**
656
+ * Unlink from every source, for good. Without this, a source outlives the
657
+ * computation and keeps it (and everything its function captured) reachable
658
+ * through its observers set: a disposed list row stayed in memory that way.
659
+ * The last value stays readable; the node simply never updates again.
660
+ */
661
+ dispose() {
662
+ if (this._disposed) return;
663
+ this._disposed = true;
664
+ if (this.sources) {
665
+ for (const s of this.sources) s.observers?.delete(this);
666
+ this.sources = null;
667
+ }
668
+ }
654
669
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
655
670
  updateIfNecessary() {
671
+ if (this._disposed) return;
656
672
  if (this.state === CHECK) {
657
673
  if (this.sources) {
658
674
  for (const s of this.sources) {
@@ -696,7 +712,7 @@ var Computed = class extends Source {
696
712
  reportReactiveRun(this);
697
713
  if (changed) {
698
714
  this.version++;
699
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
715
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
700
716
  notifyWatchers(this);
701
717
  }
702
718
  }
@@ -710,7 +726,7 @@ var Computed = class extends Source {
710
726
  const wasClean = this.state === CLEAN;
711
727
  this.state = level;
712
728
  if (wasClean) {
713
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
729
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
714
730
  notifyWatchers(this);
715
731
  }
716
732
  }
@@ -749,6 +765,10 @@ var Watcher = class {
749
765
  this._pending.clear();
750
766
  return out;
751
767
  }
768
+ /** Drain pending without the array, for callers that only need it empty. */
769
+ clearPending() {
770
+ this._pending.clear();
771
+ }
752
772
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
753
773
  _notify(node) {
754
774
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -887,14 +907,14 @@ var EffectNode = class {
887
907
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
888
908
  });
889
909
  });
890
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
910
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
891
911
  this.watcher = new Watcher(() => enqueue(this));
892
912
  this.run();
893
913
  this.watcher.watch(this.computed);
894
914
  }
895
915
  run() {
896
916
  if (this.disposed) return;
897
- this.watcher.getPending();
917
+ this.watcher.clearPending();
898
918
  try {
899
919
  this.computed.get();
900
920
  } catch (e) {
@@ -911,6 +931,7 @@ var EffectNode = class {
911
931
  this.disposed = true;
912
932
  reportReactiveDispose(this.computed);
913
933
  this.watcher.unwatch(this.computed);
934
+ this.computed.dispose();
914
935
  disposeOwner(this.owner);
915
936
  }
916
937
  };
@@ -949,13 +970,15 @@ function createSignal(value, options) {
949
970
  },
950
971
  enumerable: false
951
972
  });
952
- reportReactiveNode({
953
- kind: "signal",
954
- node: state,
955
- handle: read,
956
- name: options?.name,
957
- set: write
958
- });
973
+ if (isObserved()) {
974
+ reportReactiveNode({
975
+ kind: "signal",
976
+ node: state,
977
+ handle: read,
978
+ name: options?.name,
979
+ set: write
980
+ });
981
+ }
959
982
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
960
983
  return [read, write];
961
984
  }
@@ -979,8 +1002,11 @@ function createMemo(fn, value, options) {
979
1002
  const read = (() => c.get());
980
1003
  Object.defineProperty(read, $SIGNAL2, { value: true, enumerable: true });
981
1004
  Object.defineProperty(read, $MEMO2, { value: true, enumerable: true });
982
- reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
983
- if (isObserved()) onCleanup(() => reportReactiveDispose(c));
1005
+ if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1006
+ onCleanup(() => {
1007
+ c.dispose();
1008
+ if (isObserved()) reportReactiveDispose(c);
1009
+ });
984
1010
  return read;
985
1011
  }
986
1012
  function createEffect(fn, value, options) {
@@ -2448,6 +2474,20 @@ function clearCachedResources(name, cache = currentResourceCache()) {
2448
2474
  }
2449
2475
 
2450
2476
  // src/lib/signal/list.ts
2477
+ function lazyIndex(initial) {
2478
+ let current = initial;
2479
+ let state = null;
2480
+ return {
2481
+ index: () => {
2482
+ if (!state) state = createSignal(current, { name: "row index" });
2483
+ return state[0]();
2484
+ },
2485
+ set: (n) => {
2486
+ current = n;
2487
+ if (state) state[1](n);
2488
+ }
2489
+ };
2490
+ }
2451
2491
  function mapArray(list, mapFn, options) {
2452
2492
  let items = [];
2453
2493
  onCleanup(() => {
@@ -2477,9 +2517,9 @@ function mapArray(list, mapFn, options) {
2477
2517
  let rendered, setIndex, dispose2;
2478
2518
  createRoot((d) => {
2479
2519
  dispose2 = d;
2480
- const [index, setIdx] = createSignal(i, { name: "row index" });
2481
- setIndex = setIdx;
2482
- rendered = mapFn(value, index);
2520
+ const idx = lazyIndex(i);
2521
+ setIndex = idx.set;
2522
+ rendered = mapFn(value, idx.index);
2483
2523
  });
2484
2524
  const entry = { item: value, rendered, dispose: dispose2, setIndex };
2485
2525
  result.push(entry);
@@ -2519,21 +2559,19 @@ function keyArray(list, keyFn, mapFn, options) {
2519
2559
  const reuse = prevByKey.get(key);
2520
2560
  if (reuse && !usedKeys.has(key)) {
2521
2561
  reuse.setIndex(i);
2562
+ reuse.setItem(next[i]);
2522
2563
  resultEntries.push(reuse);
2523
2564
  } else {
2524
- let rendered, setIndex, dispose2;
2565
+ let rendered, setIndex, setItem, dispose2;
2525
2566
  createRoot((d) => {
2526
2567
  dispose2 = d;
2527
- const [index, setIdx] = createSignal(i, { name: "row index" });
2528
- setIndex = setIdx;
2529
- const item = createMemo((prev) => {
2530
- const cur = list() || [];
2531
- const v = cur[index()];
2532
- return v !== void 0 && v !== null ? v : prev;
2533
- }, next[i]);
2534
- rendered = mapFn(item, index);
2568
+ const idx = lazyIndex(i);
2569
+ setIndex = idx.set;
2570
+ const [item, writeItem] = createSignal(next[i]);
2571
+ setItem = (v) => writeItem(() => v);
2572
+ rendered = mapFn(item, idx.index);
2535
2573
  });
2536
- resultEntries.push({ rendered, dispose: dispose2, setIndex });
2574
+ resultEntries.push({ rendered, dispose: dispose2, setIndex, setItem });
2537
2575
  }
2538
2576
  resultKeys.push(key);
2539
2577
  usedKeys.add(key);
@@ -1,4 +1,4 @@
1
- /*! @fluixi/reactive v1.0.0-alpha.104 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/reactive v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/lib/predicates.ts
4
4
  function isFunction(value) {
@@ -358,7 +358,7 @@ var Observable = class extends Subscription {
358
358
 
359
359
  // src/lib/signal/graph/detect.ts
360
360
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
361
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
361
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
362
362
  var $SIGNAL = /* @__PURE__ */ Symbol.for("signal");
363
363
  var $MEMO = /* @__PURE__ */ Symbol.for("memo");
364
364
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
@@ -392,7 +392,7 @@ function makeArrayFlat(values) {
392
392
  }
393
393
 
394
394
  // src/version.generated.ts
395
- var VERSION = "1.0.0-alpha.104";
395
+ var VERSION = "1.0.0-alpha.106";
396
396
 
397
397
  // src/lib/signal/graph/state.ts
398
398
  var MAJOR = VERSION.split(".")[0];
@@ -534,6 +534,7 @@ var Computed = class extends Source {
534
534
  super();
535
535
  this._threw = false;
536
536
  this._initialized = false;
537
+ this._disposed = false;
537
538
  this.state = DIRTY;
538
539
  /** Nodes this computation read on its last run. Rebuilt on every run. */
539
540
  this.sources = null;
@@ -549,8 +550,23 @@ var Computed = class extends Source {
549
550
  if (this._threw) throw this._error;
550
551
  return this._value;
551
552
  }
553
+ /**
554
+ * Unlink from every source, for good. Without this, a source outlives the
555
+ * computation and keeps it (and everything its function captured) reachable
556
+ * through its observers set: a disposed list row stayed in memory that way.
557
+ * The last value stays readable; the node simply never updates again.
558
+ */
559
+ dispose() {
560
+ if (this._disposed) return;
561
+ this._disposed = true;
562
+ if (this.sources) {
563
+ for (const s of this.sources) s.observers?.delete(this);
564
+ this.sources = null;
565
+ }
566
+ }
552
567
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
553
568
  updateIfNecessary() {
569
+ if (this._disposed) return;
554
570
  if (this.state === CHECK) {
555
571
  if (this.sources) {
556
572
  for (const s of this.sources) {
@@ -594,7 +610,7 @@ var Computed = class extends Source {
594
610
  reportReactiveRun(this);
595
611
  if (changed) {
596
612
  this.version++;
597
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
613
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
598
614
  notifyWatchers(this);
599
615
  }
600
616
  }
@@ -608,7 +624,7 @@ var Computed = class extends Source {
608
624
  const wasClean = this.state === CLEAN;
609
625
  this.state = level;
610
626
  if (wasClean) {
611
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
627
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
612
628
  notifyWatchers(this);
613
629
  }
614
630
  }
@@ -647,6 +663,10 @@ var Watcher = class {
647
663
  this._pending.clear();
648
664
  return out;
649
665
  }
666
+ /** Drain pending without the array, for callers that only need it empty. */
667
+ clearPending() {
668
+ this._pending.clear();
669
+ }
650
670
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
651
671
  _notify(node) {
652
672
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -785,14 +805,14 @@ var EffectNode = class {
785
805
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
786
806
  });
787
807
  });
788
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
808
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
789
809
  this.watcher = new Watcher(() => enqueue(this));
790
810
  this.run();
791
811
  this.watcher.watch(this.computed);
792
812
  }
793
813
  run() {
794
814
  if (this.disposed) return;
795
- this.watcher.getPending();
815
+ this.watcher.clearPending();
796
816
  try {
797
817
  this.computed.get();
798
818
  } catch (e) {
@@ -809,6 +829,7 @@ var EffectNode = class {
809
829
  this.disposed = true;
810
830
  reportReactiveDispose(this.computed);
811
831
  this.watcher.unwatch(this.computed);
832
+ this.computed.dispose();
812
833
  disposeOwner(this.owner);
813
834
  }
814
835
  };
@@ -847,13 +868,15 @@ function createSignal(value, options) {
847
868
  },
848
869
  enumerable: false
849
870
  });
850
- reportReactiveNode({
851
- kind: "signal",
852
- node: state,
853
- handle: read,
854
- name: options?.name,
855
- set: write
856
- });
871
+ if (isObserved()) {
872
+ reportReactiveNode({
873
+ kind: "signal",
874
+ node: state,
875
+ handle: read,
876
+ name: options?.name,
877
+ set: write
878
+ });
879
+ }
857
880
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
858
881
  return [read, write];
859
882
  }
@@ -877,8 +900,11 @@ function createMemo(fn, value, options) {
877
900
  const read = (() => c.get());
878
901
  Object.defineProperty(read, $SIGNAL2, { value: true, enumerable: true });
879
902
  Object.defineProperty(read, $MEMO2, { value: true, enumerable: true });
880
- reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
881
- if (isObserved()) onCleanup(() => reportReactiveDispose(c));
903
+ if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
904
+ onCleanup(() => {
905
+ c.dispose();
906
+ if (isObserved()) reportReactiveDispose(c);
907
+ });
882
908
  return read;
883
909
  }
884
910
  function createEffect(fn, value, options) {
@@ -2346,6 +2372,20 @@ function clearCachedResources(name, cache = currentResourceCache()) {
2346
2372
  }
2347
2373
 
2348
2374
  // src/lib/signal/list.ts
2375
+ function lazyIndex(initial) {
2376
+ let current = initial;
2377
+ let state = null;
2378
+ return {
2379
+ index: () => {
2380
+ if (!state) state = createSignal(current, { name: "row index" });
2381
+ return state[0]();
2382
+ },
2383
+ set: (n) => {
2384
+ current = n;
2385
+ if (state) state[1](n);
2386
+ }
2387
+ };
2388
+ }
2349
2389
  function mapArray(list, mapFn, options) {
2350
2390
  let items = [];
2351
2391
  onCleanup(() => {
@@ -2375,9 +2415,9 @@ function mapArray(list, mapFn, options) {
2375
2415
  let rendered, setIndex, dispose2;
2376
2416
  createRoot((d) => {
2377
2417
  dispose2 = d;
2378
- const [index, setIdx] = createSignal(i, { name: "row index" });
2379
- setIndex = setIdx;
2380
- rendered = mapFn(value, index);
2418
+ const idx = lazyIndex(i);
2419
+ setIndex = idx.set;
2420
+ rendered = mapFn(value, idx.index);
2381
2421
  });
2382
2422
  const entry = { item: value, rendered, dispose: dispose2, setIndex };
2383
2423
  result.push(entry);
@@ -2417,21 +2457,19 @@ function keyArray(list, keyFn, mapFn, options) {
2417
2457
  const reuse = prevByKey.get(key);
2418
2458
  if (reuse && !usedKeys.has(key)) {
2419
2459
  reuse.setIndex(i);
2460
+ reuse.setItem(next[i]);
2420
2461
  resultEntries.push(reuse);
2421
2462
  } else {
2422
- let rendered, setIndex, dispose2;
2463
+ let rendered, setIndex, setItem, dispose2;
2423
2464
  createRoot((d) => {
2424
2465
  dispose2 = d;
2425
- const [index, setIdx] = createSignal(i, { name: "row index" });
2426
- setIndex = setIdx;
2427
- const item = createMemo((prev) => {
2428
- const cur = list() || [];
2429
- const v = cur[index()];
2430
- return v !== void 0 && v !== null ? v : prev;
2431
- }, next[i]);
2432
- rendered = mapFn(item, index);
2466
+ const idx = lazyIndex(i);
2467
+ setIndex = idx.set;
2468
+ const [item, writeItem] = createSignal(next[i]);
2469
+ setItem = (v) => writeItem(() => v);
2470
+ rendered = mapFn(item, idx.index);
2433
2471
  });
2434
- resultEntries.push({ rendered, dispose: dispose2, setIndex });
2472
+ resultEntries.push({ rendered, dispose: dispose2, setIndex, setItem });
2435
2473
  }
2436
2474
  resultKeys.push(key);
2437
2475
  usedKeys.add(key);
@@ -1,4 +1,4 @@
1
- /*! @fluixi/reactive v1.0.0-alpha.104 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/reactive v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -106,7 +106,7 @@ __export(index_exports, {
106
106
  module.exports = __toCommonJS(index_exports);
107
107
 
108
108
  // src/version.generated.ts
109
- var VERSION = "1.0.0-alpha.104";
109
+ var VERSION = "1.0.0-alpha.106";
110
110
 
111
111
  // src/lib/signal/graph/state.ts
112
112
  var MAJOR = VERSION.split(".")[0];
@@ -302,6 +302,7 @@ var Computed = class extends Source {
302
302
  super();
303
303
  this._threw = false;
304
304
  this._initialized = false;
305
+ this._disposed = false;
305
306
  this.state = DIRTY;
306
307
  /** Nodes this computation read on its last run. Rebuilt on every run. */
307
308
  this.sources = null;
@@ -317,8 +318,23 @@ var Computed = class extends Source {
317
318
  if (this._threw) throw this._error;
318
319
  return this._value;
319
320
  }
321
+ /**
322
+ * Unlink from every source, for good. Without this, a source outlives the
323
+ * computation and keeps it (and everything its function captured) reachable
324
+ * through its observers set: a disposed list row stayed in memory that way.
325
+ * The last value stays readable; the node simply never updates again.
326
+ */
327
+ dispose() {
328
+ if (this._disposed) return;
329
+ this._disposed = true;
330
+ if (this.sources) {
331
+ for (const s of this.sources) s.observers?.delete(this);
332
+ this.sources = null;
333
+ }
334
+ }
320
335
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
321
336
  updateIfNecessary() {
337
+ if (this._disposed) return;
322
338
  if (this.state === CHECK) {
323
339
  if (this.sources) {
324
340
  for (const s of this.sources) {
@@ -362,7 +378,7 @@ var Computed = class extends Source {
362
378
  reportReactiveRun(this);
363
379
  if (changed) {
364
380
  this.version++;
365
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
381
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
366
382
  notifyWatchers(this);
367
383
  }
368
384
  }
@@ -376,7 +392,7 @@ var Computed = class extends Source {
376
392
  const wasClean = this.state === CLEAN;
377
393
  this.state = level;
378
394
  if (wasClean) {
379
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
395
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
380
396
  notifyWatchers(this);
381
397
  }
382
398
  }
@@ -415,6 +431,10 @@ var Watcher = class {
415
431
  this._pending.clear();
416
432
  return out;
417
433
  }
434
+ /** Drain pending without the array, for callers that only need it empty. */
435
+ clearPending() {
436
+ this._pending.clear();
437
+ }
418
438
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
419
439
  _notify(node) {
420
440
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -632,14 +652,14 @@ var EffectNode = class {
632
652
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
633
653
  });
634
654
  });
635
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
655
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
636
656
  this.watcher = new Watcher(() => enqueue(this));
637
657
  this.run();
638
658
  this.watcher.watch(this.computed);
639
659
  }
640
660
  run() {
641
661
  if (this.disposed) return;
642
- this.watcher.getPending();
662
+ this.watcher.clearPending();
643
663
  try {
644
664
  this.computed.get();
645
665
  } catch (e) {
@@ -656,6 +676,7 @@ var EffectNode = class {
656
676
  this.disposed = true;
657
677
  reportReactiveDispose(this.computed);
658
678
  this.watcher.unwatch(this.computed);
679
+ this.computed.dispose();
659
680
  disposeOwner(this.owner);
660
681
  }
661
682
  };
@@ -694,13 +715,15 @@ function createSignal(value, options) {
694
715
  },
695
716
  enumerable: false
696
717
  });
697
- reportReactiveNode({
698
- kind: "signal",
699
- node: state,
700
- handle: read,
701
- name: options?.name,
702
- set: write
703
- });
718
+ if (isObserved()) {
719
+ reportReactiveNode({
720
+ kind: "signal",
721
+ node: state,
722
+ handle: read,
723
+ name: options?.name,
724
+ set: write
725
+ });
726
+ }
704
727
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
705
728
  return [read, write];
706
729
  }
@@ -724,8 +747,11 @@ function createMemo(fn, value, options) {
724
747
  const read = (() => c.get());
725
748
  Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
726
749
  Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
727
- reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
728
- if (isObserved()) onCleanup(() => reportReactiveDispose(c));
750
+ if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
751
+ onCleanup(() => {
752
+ c.dispose();
753
+ if (isObserved()) reportReactiveDispose(c);
754
+ });
729
755
  return read;
730
756
  }
731
757
  function createEffect(fn, value, options) {
@@ -782,6 +808,20 @@ function on(deps, fn, options) {
782
808
  }
783
809
 
784
810
  // src/lib/signal/list.ts
811
+ function lazyIndex(initial) {
812
+ let current = initial;
813
+ let state = null;
814
+ return {
815
+ index: () => {
816
+ if (!state) state = createSignal(current, { name: "row index" });
817
+ return state[0]();
818
+ },
819
+ set: (n) => {
820
+ current = n;
821
+ if (state) state[1](n);
822
+ }
823
+ };
824
+ }
785
825
  function mapArray(list, mapFn, options) {
786
826
  let items = [];
787
827
  onCleanup(() => {
@@ -811,9 +851,9 @@ function mapArray(list, mapFn, options) {
811
851
  let rendered, setIndex, dispose;
812
852
  createRoot((d) => {
813
853
  dispose = d;
814
- const [index, setIdx] = createSignal(i, { name: "row index" });
815
- setIndex = setIdx;
816
- rendered = mapFn(value, index);
854
+ const idx = lazyIndex(i);
855
+ setIndex = idx.set;
856
+ rendered = mapFn(value, idx.index);
817
857
  });
818
858
  const entry = { item: value, rendered, dispose, setIndex };
819
859
  result.push(entry);
@@ -853,21 +893,19 @@ function keyArray(list, keyFn, mapFn, options) {
853
893
  const reuse = prevByKey.get(key);
854
894
  if (reuse && !usedKeys.has(key)) {
855
895
  reuse.setIndex(i);
896
+ reuse.setItem(next[i]);
856
897
  resultEntries.push(reuse);
857
898
  } else {
858
- let rendered, setIndex, dispose;
899
+ let rendered, setIndex, setItem, dispose;
859
900
  createRoot((d) => {
860
901
  dispose = d;
861
- const [index, setIdx] = createSignal(i, { name: "row index" });
862
- setIndex = setIdx;
863
- const item = createMemo((prev) => {
864
- const cur = list() || [];
865
- const v = cur[index()];
866
- return v !== void 0 && v !== null ? v : prev;
867
- }, next[i]);
868
- rendered = mapFn(item, index);
902
+ const idx = lazyIndex(i);
903
+ setIndex = idx.set;
904
+ const [item, writeItem] = createSignal(next[i]);
905
+ setItem = (v) => writeItem(() => v);
906
+ rendered = mapFn(item, idx.index);
869
907
  });
870
- resultEntries.push({ rendered, dispose, setIndex });
908
+ resultEntries.push({ rendered, dispose, setIndex, setItem });
871
909
  }
872
910
  resultKeys.push(key);
873
911
  usedKeys.add(key);
@@ -917,7 +955,7 @@ function indexArray(list, mapFn, options) {
917
955
 
918
956
  // src/lib/signal/graph/detect.ts
919
957
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
920
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
958
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
921
959
  var $SIGNAL2 = /* @__PURE__ */ Symbol.for("signal");
922
960
  var $MEMO2 = /* @__PURE__ */ Symbol.for("memo");
923
961
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");