@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
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.generated.ts
2
- var VERSION = "1.0.0-alpha.104";
2
+ var VERSION = "1.0.0-alpha.106";
3
3
 
4
4
  // src/lib/predicates.ts
5
5
  function isFunction(value) {
@@ -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;
@@ -966,14 +986,14 @@ var EffectNode = class {
966
986
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
967
987
  });
968
988
  });
969
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
989
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
970
990
  this.watcher = new Watcher(() => enqueue(this));
971
991
  this.run();
972
992
  this.watcher.watch(this.computed);
973
993
  }
974
994
  run() {
975
995
  if (this.disposed) return;
976
- this.watcher.getPending();
996
+ this.watcher.clearPending();
977
997
  try {
978
998
  this.computed.get();
979
999
  } catch (e) {
@@ -990,6 +1010,7 @@ var EffectNode = class {
990
1010
  this.disposed = true;
991
1011
  reportReactiveDispose(this.computed);
992
1012
  this.watcher.unwatch(this.computed);
1013
+ this.computed.dispose();
993
1014
  disposeOwner(this.owner);
994
1015
  }
995
1016
  };
@@ -1028,13 +1049,15 @@ function createSignal(value, options) {
1028
1049
  },
1029
1050
  enumerable: false
1030
1051
  });
1031
- reportReactiveNode({
1032
- kind: "signal",
1033
- node: state,
1034
- handle: read,
1035
- name: options?.name,
1036
- set: write
1037
- });
1052
+ if (isObserved()) {
1053
+ reportReactiveNode({
1054
+ kind: "signal",
1055
+ node: state,
1056
+ handle: read,
1057
+ name: options?.name,
1058
+ set: write
1059
+ });
1060
+ }
1038
1061
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
1039
1062
  return [read, write];
1040
1063
  }
@@ -1058,8 +1081,11 @@ function createMemo(fn, value, options) {
1058
1081
  const read = (() => c.get());
1059
1082
  Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
1060
1083
  Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
1061
- reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1062
- if (isObserved()) onCleanup(() => reportReactiveDispose(c));
1084
+ if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1085
+ onCleanup(() => {
1086
+ c.dispose();
1087
+ if (isObserved()) reportReactiveDispose(c);
1088
+ });
1063
1089
  return read;
1064
1090
  }
1065
1091
  function createEffect(fn, value, options) {
@@ -1116,6 +1142,20 @@ function on(deps, fn, options) {
1116
1142
  }
1117
1143
 
1118
1144
  // src/lib/signal/list.ts
1145
+ function lazyIndex(initial) {
1146
+ let current = initial;
1147
+ let state = null;
1148
+ return {
1149
+ index: () => {
1150
+ if (!state) state = createSignal(current, { name: "row index" });
1151
+ return state[0]();
1152
+ },
1153
+ set: (n) => {
1154
+ current = n;
1155
+ if (state) state[1](n);
1156
+ }
1157
+ };
1158
+ }
1119
1159
  function mapArray(list, mapFn, options) {
1120
1160
  let items = [];
1121
1161
  onCleanup(() => {
@@ -1145,9 +1185,9 @@ function mapArray(list, mapFn, options) {
1145
1185
  let rendered, setIndex, dispose;
1146
1186
  createRoot((d) => {
1147
1187
  dispose = d;
1148
- const [index, setIdx] = createSignal(i, { name: "row index" });
1149
- setIndex = setIdx;
1150
- rendered = mapFn(value, index);
1188
+ const idx = lazyIndex(i);
1189
+ setIndex = idx.set;
1190
+ rendered = mapFn(value, idx.index);
1151
1191
  });
1152
1192
  const entry = { item: value, rendered, dispose, setIndex };
1153
1193
  result.push(entry);
@@ -1187,21 +1227,19 @@ function keyArray(list, keyFn, mapFn, options) {
1187
1227
  const reuse = prevByKey.get(key);
1188
1228
  if (reuse && !usedKeys.has(key)) {
1189
1229
  reuse.setIndex(i);
1230
+ reuse.setItem(next[i]);
1190
1231
  resultEntries.push(reuse);
1191
1232
  } else {
1192
- let rendered, setIndex, dispose;
1233
+ let rendered, setIndex, setItem, dispose;
1193
1234
  createRoot((d) => {
1194
1235
  dispose = d;
1195
- const [index, setIdx] = createSignal(i, { name: "row index" });
1196
- setIndex = setIdx;
1197
- const item = createMemo((prev) => {
1198
- const cur = list() || [];
1199
- const v = cur[index()];
1200
- return v !== void 0 && v !== null ? v : prev;
1201
- }, next[i]);
1202
- rendered = mapFn(item, index);
1236
+ const idx = lazyIndex(i);
1237
+ setIndex = idx.set;
1238
+ const [item, writeItem] = createSignal(next[i]);
1239
+ setItem = (v) => writeItem(() => v);
1240
+ rendered = mapFn(item, idx.index);
1203
1241
  });
1204
- resultEntries.push({ rendered, dispose, setIndex });
1242
+ resultEntries.push({ rendered, dispose, setIndex, setItem });
1205
1243
  }
1206
1244
  resultKeys.push(key);
1207
1245
  usedKeys.add(key);
@@ -1251,7 +1289,7 @@ function indexArray(list, mapFn, options) {
1251
1289
 
1252
1290
  // src/lib/signal/graph/detect.ts
1253
1291
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
1254
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
1292
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
1255
1293
  var $SIGNAL2 = /* @__PURE__ */ Symbol.for("signal");
1256
1294
  var $MEMO2 = /* @__PURE__ */ Symbol.for("memo");
1257
1295
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
@@ -2070,12 +2108,14 @@ var Store = class {
2070
2108
  OWNERS.set(initialState, { store: this, name: options.name ?? "store" });
2071
2109
  ROOTS.set(initialState, initialState);
2072
2110
  }
2073
- reportReactiveNode({
2074
- kind: "store",
2075
- node: this,
2076
- handle: () => this.root,
2077
- name: options.name
2078
- });
2111
+ if (isObserved()) {
2112
+ reportReactiveNode({
2113
+ kind: "store",
2114
+ node: this,
2115
+ handle: () => this.root,
2116
+ name: options.name
2117
+ });
2118
+ }
2079
2119
  }
2080
2120
  get state() {
2081
2121
  return box(this.root);
@@ -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;
@@ -159,7 +159,7 @@ __export(index_exports, {
159
159
  module.exports = __toCommonJS(index_exports);
160
160
 
161
161
  // src/version.generated.ts
162
- var VERSION = "1.0.0-alpha.104";
162
+ var VERSION = "1.0.0-alpha.106";
163
163
 
164
164
  // src/lib/predicates.ts
165
165
  function isFunction(value) {
@@ -796,6 +796,7 @@ var Computed = class extends Source {
796
796
  super();
797
797
  this._threw = false;
798
798
  this._initialized = false;
799
+ this._disposed = false;
799
800
  this.state = DIRTY;
800
801
  /** Nodes this computation read on its last run. Rebuilt on every run. */
801
802
  this.sources = null;
@@ -811,8 +812,23 @@ var Computed = class extends Source {
811
812
  if (this._threw) throw this._error;
812
813
  return this._value;
813
814
  }
815
+ /**
816
+ * Unlink from every source, for good. Without this, a source outlives the
817
+ * computation and keeps it (and everything its function captured) reachable
818
+ * through its observers set: a disposed list row stayed in memory that way.
819
+ * The last value stays readable; the node simply never updates again.
820
+ */
821
+ dispose() {
822
+ if (this._disposed) return;
823
+ this._disposed = true;
824
+ if (this.sources) {
825
+ for (const s of this.sources) s.observers?.delete(this);
826
+ this.sources = null;
827
+ }
828
+ }
814
829
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
815
830
  updateIfNecessary() {
831
+ if (this._disposed) return;
816
832
  if (this.state === CHECK) {
817
833
  if (this.sources) {
818
834
  for (const s of this.sources) {
@@ -856,7 +872,7 @@ var Computed = class extends Source {
856
872
  reportReactiveRun(this);
857
873
  if (changed) {
858
874
  this.version++;
859
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
875
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
860
876
  notifyWatchers(this);
861
877
  }
862
878
  }
@@ -870,7 +886,7 @@ var Computed = class extends Source {
870
886
  const wasClean = this.state === CLEAN;
871
887
  this.state = level;
872
888
  if (wasClean) {
873
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
889
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
874
890
  notifyWatchers(this);
875
891
  }
876
892
  }
@@ -909,6 +925,10 @@ var Watcher = class {
909
925
  this._pending.clear();
910
926
  return out;
911
927
  }
928
+ /** Drain pending without the array, for callers that only need it empty. */
929
+ clearPending() {
930
+ this._pending.clear();
931
+ }
912
932
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
913
933
  _notify(node) {
914
934
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -1126,14 +1146,14 @@ var EffectNode = class {
1126
1146
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
1127
1147
  });
1128
1148
  });
1129
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
1149
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
1130
1150
  this.watcher = new Watcher(() => enqueue(this));
1131
1151
  this.run();
1132
1152
  this.watcher.watch(this.computed);
1133
1153
  }
1134
1154
  run() {
1135
1155
  if (this.disposed) return;
1136
- this.watcher.getPending();
1156
+ this.watcher.clearPending();
1137
1157
  try {
1138
1158
  this.computed.get();
1139
1159
  } catch (e) {
@@ -1150,6 +1170,7 @@ var EffectNode = class {
1150
1170
  this.disposed = true;
1151
1171
  reportReactiveDispose(this.computed);
1152
1172
  this.watcher.unwatch(this.computed);
1173
+ this.computed.dispose();
1153
1174
  disposeOwner(this.owner);
1154
1175
  }
1155
1176
  };
@@ -1188,13 +1209,15 @@ function createSignal(value, options) {
1188
1209
  },
1189
1210
  enumerable: false
1190
1211
  });
1191
- reportReactiveNode({
1192
- kind: "signal",
1193
- node: state,
1194
- handle: read,
1195
- name: options?.name,
1196
- set: write
1197
- });
1212
+ if (isObserved()) {
1213
+ reportReactiveNode({
1214
+ kind: "signal",
1215
+ node: state,
1216
+ handle: read,
1217
+ name: options?.name,
1218
+ set: write
1219
+ });
1220
+ }
1198
1221
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
1199
1222
  return [read, write];
1200
1223
  }
@@ -1218,8 +1241,11 @@ function createMemo(fn, value, options) {
1218
1241
  const read = (() => c.get());
1219
1242
  Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
1220
1243
  Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
1221
- reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1222
- if (isObserved()) onCleanup(() => reportReactiveDispose(c));
1244
+ if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1245
+ onCleanup(() => {
1246
+ c.dispose();
1247
+ if (isObserved()) reportReactiveDispose(c);
1248
+ });
1223
1249
  return read;
1224
1250
  }
1225
1251
  function createEffect(fn, value, options) {
@@ -1276,6 +1302,20 @@ function on(deps, fn, options) {
1276
1302
  }
1277
1303
 
1278
1304
  // src/lib/signal/list.ts
1305
+ function lazyIndex(initial) {
1306
+ let current = initial;
1307
+ let state = null;
1308
+ return {
1309
+ index: () => {
1310
+ if (!state) state = createSignal(current, { name: "row index" });
1311
+ return state[0]();
1312
+ },
1313
+ set: (n) => {
1314
+ current = n;
1315
+ if (state) state[1](n);
1316
+ }
1317
+ };
1318
+ }
1279
1319
  function mapArray(list, mapFn, options) {
1280
1320
  let items = [];
1281
1321
  onCleanup(() => {
@@ -1305,9 +1345,9 @@ function mapArray(list, mapFn, options) {
1305
1345
  let rendered, setIndex, dispose;
1306
1346
  createRoot((d) => {
1307
1347
  dispose = d;
1308
- const [index, setIdx] = createSignal(i, { name: "row index" });
1309
- setIndex = setIdx;
1310
- rendered = mapFn(value, index);
1348
+ const idx = lazyIndex(i);
1349
+ setIndex = idx.set;
1350
+ rendered = mapFn(value, idx.index);
1311
1351
  });
1312
1352
  const entry = { item: value, rendered, dispose, setIndex };
1313
1353
  result.push(entry);
@@ -1347,21 +1387,19 @@ function keyArray(list, keyFn, mapFn, options) {
1347
1387
  const reuse = prevByKey.get(key);
1348
1388
  if (reuse && !usedKeys.has(key)) {
1349
1389
  reuse.setIndex(i);
1390
+ reuse.setItem(next[i]);
1350
1391
  resultEntries.push(reuse);
1351
1392
  } else {
1352
- let rendered, setIndex, dispose;
1393
+ let rendered, setIndex, setItem, dispose;
1353
1394
  createRoot((d) => {
1354
1395
  dispose = d;
1355
- const [index, setIdx] = createSignal(i, { name: "row index" });
1356
- setIndex = setIdx;
1357
- const item = createMemo((prev) => {
1358
- const cur = list() || [];
1359
- const v = cur[index()];
1360
- return v !== void 0 && v !== null ? v : prev;
1361
- }, next[i]);
1362
- rendered = mapFn(item, index);
1396
+ const idx = lazyIndex(i);
1397
+ setIndex = idx.set;
1398
+ const [item, writeItem] = createSignal(next[i]);
1399
+ setItem = (v) => writeItem(() => v);
1400
+ rendered = mapFn(item, idx.index);
1363
1401
  });
1364
- resultEntries.push({ rendered, dispose, setIndex });
1402
+ resultEntries.push({ rendered, dispose, setIndex, setItem });
1365
1403
  }
1366
1404
  resultKeys.push(key);
1367
1405
  usedKeys.add(key);
@@ -1411,7 +1449,7 @@ function indexArray(list, mapFn, options) {
1411
1449
 
1412
1450
  // src/lib/signal/graph/detect.ts
1413
1451
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
1414
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
1452
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
1415
1453
  var $SIGNAL2 = /* @__PURE__ */ Symbol.for("signal");
1416
1454
  var $MEMO2 = /* @__PURE__ */ Symbol.for("memo");
1417
1455
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
@@ -2223,12 +2261,14 @@ var Store = class {
2223
2261
  OWNERS.set(initialState, { store: this, name: options.name ?? "store" });
2224
2262
  ROOTS.set(initialState, initialState);
2225
2263
  }
2226
- reportReactiveNode({
2227
- kind: "store",
2228
- node: this,
2229
- handle: () => this.root,
2230
- name: options.name
2231
- });
2264
+ if (isObserved()) {
2265
+ reportReactiveNode({
2266
+ kind: "store",
2267
+ node: this,
2268
+ handle: () => this.root,
2269
+ name: options.name
2270
+ });
2271
+ }
2232
2272
  }
2233
2273
  get state() {
2234
2274
  return box(this.root);
@@ -1,7 +1,7 @@
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/version.generated.ts
4
- var VERSION = "1.0.0-alpha.104";
4
+ var VERSION = "1.0.0-alpha.106";
5
5
 
6
6
  // src/lib/predicates.ts
7
7
  function isFunction(value) {
@@ -638,6 +638,7 @@ var Computed = class extends Source {
638
638
  super();
639
639
  this._threw = false;
640
640
  this._initialized = false;
641
+ this._disposed = false;
641
642
  this.state = DIRTY;
642
643
  /** Nodes this computation read on its last run. Rebuilt on every run. */
643
644
  this.sources = null;
@@ -653,8 +654,23 @@ var Computed = class extends Source {
653
654
  if (this._threw) throw this._error;
654
655
  return this._value;
655
656
  }
657
+ /**
658
+ * Unlink from every source, for good. Without this, a source outlives the
659
+ * computation and keeps it (and everything its function captured) reachable
660
+ * through its observers set: a disposed list row stayed in memory that way.
661
+ * The last value stays readable; the node simply never updates again.
662
+ */
663
+ dispose() {
664
+ if (this._disposed) return;
665
+ this._disposed = true;
666
+ if (this.sources) {
667
+ for (const s of this.sources) s.observers?.delete(this);
668
+ this.sources = null;
669
+ }
670
+ }
656
671
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
657
672
  updateIfNecessary() {
673
+ if (this._disposed) return;
658
674
  if (this.state === CHECK) {
659
675
  if (this.sources) {
660
676
  for (const s of this.sources) {
@@ -698,7 +714,7 @@ var Computed = class extends Source {
698
714
  reportReactiveRun(this);
699
715
  if (changed) {
700
716
  this.version++;
701
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
717
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
702
718
  notifyWatchers(this);
703
719
  }
704
720
  }
@@ -712,7 +728,7 @@ var Computed = class extends Source {
712
728
  const wasClean = this.state === CLEAN;
713
729
  this.state = level;
714
730
  if (wasClean) {
715
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
731
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
716
732
  notifyWatchers(this);
717
733
  }
718
734
  }
@@ -751,6 +767,10 @@ var Watcher = class {
751
767
  this._pending.clear();
752
768
  return out;
753
769
  }
770
+ /** Drain pending without the array, for callers that only need it empty. */
771
+ clearPending() {
772
+ this._pending.clear();
773
+ }
754
774
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
755
775
  _notify(node) {
756
776
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -968,14 +988,14 @@ var EffectNode = class {
968
988
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
969
989
  });
970
990
  });
971
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
991
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
972
992
  this.watcher = new Watcher(() => enqueue(this));
973
993
  this.run();
974
994
  this.watcher.watch(this.computed);
975
995
  }
976
996
  run() {
977
997
  if (this.disposed) return;
978
- this.watcher.getPending();
998
+ this.watcher.clearPending();
979
999
  try {
980
1000
  this.computed.get();
981
1001
  } catch (e) {
@@ -992,6 +1012,7 @@ var EffectNode = class {
992
1012
  this.disposed = true;
993
1013
  reportReactiveDispose(this.computed);
994
1014
  this.watcher.unwatch(this.computed);
1015
+ this.computed.dispose();
995
1016
  disposeOwner(this.owner);
996
1017
  }
997
1018
  };
@@ -1030,13 +1051,15 @@ function createSignal(value, options) {
1030
1051
  },
1031
1052
  enumerable: false
1032
1053
  });
1033
- reportReactiveNode({
1034
- kind: "signal",
1035
- node: state,
1036
- handle: read,
1037
- name: options?.name,
1038
- set: write
1039
- });
1054
+ if (isObserved()) {
1055
+ reportReactiveNode({
1056
+ kind: "signal",
1057
+ node: state,
1058
+ handle: read,
1059
+ name: options?.name,
1060
+ set: write
1061
+ });
1062
+ }
1040
1063
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
1041
1064
  return [read, write];
1042
1065
  }
@@ -1060,8 +1083,11 @@ function createMemo(fn, value, options) {
1060
1083
  const read = (() => c.get());
1061
1084
  Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
1062
1085
  Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
1063
- reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1064
- if (isObserved()) onCleanup(() => reportReactiveDispose(c));
1086
+ if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1087
+ onCleanup(() => {
1088
+ c.dispose();
1089
+ if (isObserved()) reportReactiveDispose(c);
1090
+ });
1065
1091
  return read;
1066
1092
  }
1067
1093
  function createEffect(fn, value, options) {
@@ -1118,6 +1144,20 @@ function on(deps, fn, options) {
1118
1144
  }
1119
1145
 
1120
1146
  // src/lib/signal/list.ts
1147
+ function lazyIndex(initial) {
1148
+ let current = initial;
1149
+ let state = null;
1150
+ return {
1151
+ index: () => {
1152
+ if (!state) state = createSignal(current, { name: "row index" });
1153
+ return state[0]();
1154
+ },
1155
+ set: (n) => {
1156
+ current = n;
1157
+ if (state) state[1](n);
1158
+ }
1159
+ };
1160
+ }
1121
1161
  function mapArray(list, mapFn, options) {
1122
1162
  let items = [];
1123
1163
  onCleanup(() => {
@@ -1147,9 +1187,9 @@ function mapArray(list, mapFn, options) {
1147
1187
  let rendered, setIndex, dispose;
1148
1188
  createRoot((d) => {
1149
1189
  dispose = d;
1150
- const [index, setIdx] = createSignal(i, { name: "row index" });
1151
- setIndex = setIdx;
1152
- rendered = mapFn(value, index);
1190
+ const idx = lazyIndex(i);
1191
+ setIndex = idx.set;
1192
+ rendered = mapFn(value, idx.index);
1153
1193
  });
1154
1194
  const entry = { item: value, rendered, dispose, setIndex };
1155
1195
  result.push(entry);
@@ -1189,21 +1229,19 @@ function keyArray(list, keyFn, mapFn, options) {
1189
1229
  const reuse = prevByKey.get(key);
1190
1230
  if (reuse && !usedKeys.has(key)) {
1191
1231
  reuse.setIndex(i);
1232
+ reuse.setItem(next[i]);
1192
1233
  resultEntries.push(reuse);
1193
1234
  } else {
1194
- let rendered, setIndex, dispose;
1235
+ let rendered, setIndex, setItem, dispose;
1195
1236
  createRoot((d) => {
1196
1237
  dispose = d;
1197
- const [index, setIdx] = createSignal(i, { name: "row index" });
1198
- setIndex = setIdx;
1199
- const item = createMemo((prev) => {
1200
- const cur = list() || [];
1201
- const v = cur[index()];
1202
- return v !== void 0 && v !== null ? v : prev;
1203
- }, next[i]);
1204
- rendered = mapFn(item, index);
1238
+ const idx = lazyIndex(i);
1239
+ setIndex = idx.set;
1240
+ const [item, writeItem] = createSignal(next[i]);
1241
+ setItem = (v) => writeItem(() => v);
1242
+ rendered = mapFn(item, idx.index);
1205
1243
  });
1206
- resultEntries.push({ rendered, dispose, setIndex });
1244
+ resultEntries.push({ rendered, dispose, setIndex, setItem });
1207
1245
  }
1208
1246
  resultKeys.push(key);
1209
1247
  usedKeys.add(key);
@@ -1253,7 +1291,7 @@ function indexArray(list, mapFn, options) {
1253
1291
 
1254
1292
  // src/lib/signal/graph/detect.ts
1255
1293
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
1256
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
1294
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
1257
1295
  var $SIGNAL2 = /* @__PURE__ */ Symbol.for("signal");
1258
1296
  var $MEMO2 = /* @__PURE__ */ Symbol.for("memo");
1259
1297
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
@@ -2072,12 +2110,14 @@ var Store = class {
2072
2110
  OWNERS.set(initialState, { store: this, name: options.name ?? "store" });
2073
2111
  ROOTS.set(initialState, initialState);
2074
2112
  }
2075
- reportReactiveNode({
2076
- kind: "store",
2077
- node: this,
2078
- handle: () => this.root,
2079
- name: options.name
2080
- });
2113
+ if (isObserved()) {
2114
+ reportReactiveNode({
2115
+ kind: "store",
2116
+ node: this,
2117
+ handle: () => this.root,
2118
+ name: options.name
2119
+ });
2120
+ }
2081
2121
  }
2082
2122
  get state() {
2083
2123
  return box(this.root);
@@ -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;
@@ -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) {