@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,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);
@@ -27,7 +27,7 @@ __export(signal_exports, {
27
27
  module.exports = __toCommonJS(signal_exports);
28
28
 
29
29
  // src/version.generated.ts
30
- var VERSION = "1.0.0-alpha.104";
30
+ var VERSION = "1.0.0-alpha.106";
31
31
 
32
32
  // src/lib/signal/graph/state.ts
33
33
  var MAJOR = VERSION.split(".")[0];
@@ -153,6 +153,7 @@ var Computed = class extends Source {
153
153
  super();
154
154
  this._threw = false;
155
155
  this._initialized = false;
156
+ this._disposed = false;
156
157
  this.state = DIRTY;
157
158
  /** Nodes this computation read on its last run. Rebuilt on every run. */
158
159
  this.sources = null;
@@ -168,8 +169,23 @@ var Computed = class extends Source {
168
169
  if (this._threw) throw this._error;
169
170
  return this._value;
170
171
  }
172
+ /**
173
+ * Unlink from every source, for good. Without this, a source outlives the
174
+ * computation and keeps it (and everything its function captured) reachable
175
+ * through its observers set: a disposed list row stayed in memory that way.
176
+ * The last value stays readable; the node simply never updates again.
177
+ */
178
+ dispose() {
179
+ if (this._disposed) return;
180
+ this._disposed = true;
181
+ if (this.sources) {
182
+ for (const s of this.sources) s.observers?.delete(this);
183
+ this.sources = null;
184
+ }
185
+ }
171
186
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
172
187
  updateIfNecessary() {
188
+ if (this._disposed) return;
173
189
  if (this.state === CHECK) {
174
190
  if (this.sources) {
175
191
  for (const s of this.sources) {
@@ -213,7 +229,7 @@ var Computed = class extends Source {
213
229
  reportReactiveRun(this);
214
230
  if (changed) {
215
231
  this.version++;
216
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
232
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
217
233
  notifyWatchers(this);
218
234
  }
219
235
  }
@@ -227,7 +243,7 @@ var Computed = class extends Source {
227
243
  const wasClean = this.state === CLEAN;
228
244
  this.state = level;
229
245
  if (wasClean) {
230
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
246
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
231
247
  notifyWatchers(this);
232
248
  }
233
249
  }
@@ -266,6 +282,10 @@ var Watcher = class {
266
282
  this._pending.clear();
267
283
  return out;
268
284
  }
285
+ /** Drain pending without the array, for callers that only need it empty. */
286
+ clearPending() {
287
+ this._pending.clear();
288
+ }
269
289
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
270
290
  _notify(node) {
271
291
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -1,2 +1,2 @@
1
- /*! @fluixi/reactive v1.0.0-alpha.104 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
- "use strict";var Signal=(()=>{var b=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var z=(e,t)=>{for(var n in t)b(e,n,{get:t[n],enumerable:!0})},A=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of G(t))!K.call(e,s)&&s!==n&&b(e,s,{get:()=>t[s],enumerable:!(r=D(t,s))||r.enumerable});return e};var H=e=>A(b({},"__esModule",{value:!0}),e);var U={};z(U,{Computed:()=>p,State:()=>l,subtle:()=>Y});var a="1.0.0-alpha.104";var R=a.split(".")[0],_=`__fluixi_signal_state_v${R}__`,P=`__fluixi_signal_builds_v${R}__`,V=e=>{typeof queueMicrotask=="function"?queueMicrotask(e):Promise.resolve().then(e)};function m(){return{consumer:null,untracked:!1,owner:null,queue:[],scheduled:!1,flushing:!1,batchDepth:0,hostSchedule:V,onSuspend:null,onError:null,observer:null}}var w=globalThis;function F(e){let t=m(),n=e;for(let r of Object.keys(t))r in e||(n[r]=t[r]);return e}function L(){let e=w[_];return e?F(e):w[_]=m()}var o=L();(w[P]??=[]).push(a);function M(e){return o.observer=e,()=>{o.observer===e&&(o.observer=null)}}var S=[];function g(e){return o.observer?(S.push(e),!0):!1}function x(){S.pop()}function T(e){o.observer?.ran?.(e)}function O(e,t){o.observer?.wrote?.(e,t)}var W="__FLUIXI_DEVTOOLS_HOOK__",y=globalThis[W];if(typeof y?.inject=="function")try{y.inject({observeReactiveNodes:M,version:a})}catch{}var j=0,N=1,c=2,v=class{constructor(){this.observers=null;this.watchers=null;this.version=0}updateIfNecessary(){}track(){if(o.untracked)return;let t=o.consumer;t&&((this.observers??=new Set).add(t),(t.sources??=new Set).add(this))}},l=class extends v{constructor(t,n){super(),this._value=t,this._equals=n?.equals===!1?()=>!1:n?.equals??Object.is}get(){return this.track(),this._value}set(t){if(!this._equals(this._value,t)){if(this._value=t,this.version++,O(this,o.consumer),this.observers)for(let n of[...this.observers])n.markDirty(c);k(this)}}peek(){return this._value}},p=class extends v{constructor(n,r){super();this._threw=!1;this._initialized=!1;this.state=c;this.sources=null;this._fn=n,this._equals=r?.equals===!1?()=>!1:r?.equals??Object.is}get(){if(o.consumer===this)throw new Error("Signal.Computed cannot read itself");if(this.track(),this.updateIfNecessary(),this._threw)throw this._error;return this._value}updateIfNecessary(){if(this.state===N&&this.sources){for(let n of this.sources)if(n.updateIfNecessary(),this.state===c)break}(this.state===c||!this._initialized)&&this.recompute(),this.state=j}recompute(){if(this.sources){for(let i of this.sources)i.observers?.delete(this);this.sources.clear()}let n=o.consumer,r=o.untracked,s=g(this);o.consumer=this,o.untracked=!1;let u,d=!1,h;try{u=this._fn()}catch(i){d=!0,h=i,u=this._value}finally{o.consumer=n,o.untracked=r,s&&x()}let I=!this._initialized||this._threw!==d||(d?h!==this._error:!this._equals(this._value,u));if(this._initialized=!0,this._threw=d,this._error=h,this._value=u,T(this),I){if(this.version++,this.observers)for(let i of[...this.observers])i.markDirty(c);k(this)}}markDirty(n){if(this.state>=n)return;let r=this.state===j;if(this.state=n,r){if(this.observers)for(let s of[...this.observers])s.markDirty(N);k(this)}}peek(){if(this.updateIfNecessary(),this._threw)throw this._error;return this._value}};function k(e){if(e.watchers)for(let t of[...e.watchers])t._notify(e)}var f=class{constructor(t){this._watched=new Set;this._pending=new Set;this._notifyFn=t}watch(...t){for(let n of t)this._watched.add(n),(n.watchers??=new Set).add(this)}unwatch(...t){for(let n of t)this._watched.delete(n),n.watchers?.delete(this),this._pending.delete(n)}getPending(){let t=[...this._pending];return this._pending.clear(),t}_notify(t){if(!this._watched.has(t)||this._pending.has(t))return;let n=this._pending.size===0;this._pending.add(t),n&&this._notifyFn()}};function C(e){if(o.untracked)return e();o.untracked=!0;try{return e()}finally{o.untracked=!1}}function E(){return o.consumer}function q(e){return!!(e.observers&&e.observers.size)||!!(e.watchers&&e.watchers.size)}var Y={Watcher:f,untrack:C,currentComputed:E,hasSinks:q};return H(U);})();
1
+ /*! @fluixi/reactive v1.0.0-alpha.106 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
+ "use strict";var Signal=(()=>{var b=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var G=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var z=(e,t)=>{for(var n in t)b(e,n,{get:t[n],enumerable:!0})},P=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of G(t))!K.call(e,s)&&s!==n&&b(e,s,{get:()=>t[s],enumerable:!(r=D(t,s))||r.enumerable});return e};var A=e=>P(b({},"__esModule",{value:!0}),e);var U={};z(U,{Computed:()=>p,State:()=>l,subtle:()=>Y});var a="1.0.0-alpha.106";var R=a.split(".")[0],_=`__fluixi_signal_state_v${R}__`,H=`__fluixi_signal_builds_v${R}__`,V=e=>{typeof queueMicrotask=="function"?queueMicrotask(e):Promise.resolve().then(e)};function m(){return{consumer:null,untracked:!1,owner:null,queue:[],scheduled:!1,flushing:!1,batchDepth:0,hostSchedule:V,onSuspend:null,onError:null,observer:null}}var w=globalThis;function F(e){let t=m(),n=e;for(let r of Object.keys(t))r in e||(n[r]=t[r]);return e}function L(){let e=w[_];return e?F(e):w[_]=m()}var o=L();(w[H]??=[]).push(a);function M(e){return o.observer=e,()=>{o.observer===e&&(o.observer=null)}}var y=[];function S(e){return o.observer?(y.push(e),!0):!1}function x(){y.pop()}function T(e){o.observer?.ran?.(e)}function O(e,t){o.observer?.wrote?.(e,t)}var W="__FLUIXI_DEVTOOLS_HOOK__",g=globalThis[W];if(typeof g?.inject=="function")try{g.inject({observeReactiveNodes:M,version:a})}catch{}var j=0,N=1,c=2,v=class{constructor(){this.observers=null;this.watchers=null;this.version=0}updateIfNecessary(){}track(){if(o.untracked)return;let t=o.consumer;t&&((this.observers??=new Set).add(t),(t.sources??=new Set).add(this))}},l=class extends v{constructor(t,n){super(),this._value=t,this._equals=n?.equals===!1?()=>!1:n?.equals??Object.is}get(){return this.track(),this._value}set(t){if(!this._equals(this._value,t)){if(this._value=t,this.version++,O(this,o.consumer),this.observers)for(let n of[...this.observers])n.markDirty(c);k(this)}}peek(){return this._value}},p=class extends v{constructor(n,r){super();this._threw=!1;this._initialized=!1;this._disposed=!1;this.state=c;this.sources=null;this._fn=n,this._equals=r?.equals===!1?()=>!1:r?.equals??Object.is}get(){if(o.consumer===this)throw new Error("Signal.Computed cannot read itself");if(this.track(),this.updateIfNecessary(),this._threw)throw this._error;return this._value}dispose(){if(!this._disposed&&(this._disposed=!0,this.sources)){for(let n of this.sources)n.observers?.delete(this);this.sources=null}}updateIfNecessary(){if(!this._disposed){if(this.state===N&&this.sources){for(let n of this.sources)if(n.updateIfNecessary(),this.state===c)break}(this.state===c||!this._initialized)&&this.recompute(),this.state=j}}recompute(){if(this.sources){for(let i of this.sources)i.observers?.delete(this);this.sources.clear()}let n=o.consumer,r=o.untracked,s=S(this);o.consumer=this,o.untracked=!1;let u,d=!1,h;try{u=this._fn()}catch(i){d=!0,h=i,u=this._value}finally{o.consumer=n,o.untracked=r,s&&x()}let I=!this._initialized||this._threw!==d||(d?h!==this._error:!this._equals(this._value,u));if(this._initialized=!0,this._threw=d,this._error=h,this._value=u,T(this),I){if(this.version++,this.observers)for(let i of this.observers)i.markDirty(c);k(this)}}markDirty(n){if(this.state>=n)return;let r=this.state===j;if(this.state=n,r){if(this.observers)for(let s of this.observers)s.markDirty(N);k(this)}}peek(){if(this.updateIfNecessary(),this._threw)throw this._error;return this._value}};function k(e){if(e.watchers)for(let t of[...e.watchers])t._notify(e)}var f=class{constructor(t){this._watched=new Set;this._pending=new Set;this._notifyFn=t}watch(...t){for(let n of t)this._watched.add(n),(n.watchers??=new Set).add(this)}unwatch(...t){for(let n of t)this._watched.delete(n),n.watchers?.delete(this),this._pending.delete(n)}getPending(){let t=[...this._pending];return this._pending.clear(),t}clearPending(){this._pending.clear()}_notify(t){if(!this._watched.has(t)||this._pending.has(t))return;let n=this._pending.size===0;this._pending.add(t),n&&this._notifyFn()}};function C(e){if(o.untracked)return e();o.untracked=!0;try{return e()}finally{o.untracked=!1}}function E(){return o.consumer}function q(e){return!!(e.observers&&e.observers.size)||!!(e.watchers&&e.watchers.size)}var Y={Watcher:f,untrack:C,currentComputed:E,hasSinks:q};return A(U);})();
@@ -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/signal/graph/state.ts
5
5
  var MAJOR = VERSION.split(".")[0];
@@ -125,6 +125,7 @@ var Computed = class extends Source {
125
125
  super();
126
126
  this._threw = false;
127
127
  this._initialized = false;
128
+ this._disposed = false;
128
129
  this.state = DIRTY;
129
130
  /** Nodes this computation read on its last run. Rebuilt on every run. */
130
131
  this.sources = null;
@@ -140,8 +141,23 @@ var Computed = class extends Source {
140
141
  if (this._threw) throw this._error;
141
142
  return this._value;
142
143
  }
144
+ /**
145
+ * Unlink from every source, for good. Without this, a source outlives the
146
+ * computation and keeps it (and everything its function captured) reachable
147
+ * through its observers set: a disposed list row stayed in memory that way.
148
+ * The last value stays readable; the node simply never updates again.
149
+ */
150
+ dispose() {
151
+ if (this._disposed) return;
152
+ this._disposed = true;
153
+ if (this.sources) {
154
+ for (const s of this.sources) s.observers?.delete(this);
155
+ this.sources = null;
156
+ }
157
+ }
143
158
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
144
159
  updateIfNecessary() {
160
+ if (this._disposed) return;
145
161
  if (this.state === CHECK) {
146
162
  if (this.sources) {
147
163
  for (const s of this.sources) {
@@ -185,7 +201,7 @@ var Computed = class extends Source {
185
201
  reportReactiveRun(this);
186
202
  if (changed) {
187
203
  this.version++;
188
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
204
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
189
205
  notifyWatchers(this);
190
206
  }
191
207
  }
@@ -199,7 +215,7 @@ var Computed = class extends Source {
199
215
  const wasClean = this.state === CLEAN;
200
216
  this.state = level;
201
217
  if (wasClean) {
202
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
218
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
203
219
  notifyWatchers(this);
204
220
  }
205
221
  }
@@ -238,6 +254,10 @@ var Watcher = class {
238
254
  this._pending.clear();
239
255
  return out;
240
256
  }
257
+ /** Drain pending without the array, for callers that only need it empty. */
258
+ clearPending() {
259
+ this._pending.clear();
260
+ }
241
261
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
242
262
  _notify(node) {
243
263
  if (!this._watched.has(node) || this._pending.has(node)) return;
package/dist/index.cjs CHANGED
@@ -158,7 +158,7 @@ __export(index_exports, {
158
158
  module.exports = __toCommonJS(index_exports);
159
159
 
160
160
  // src/version.generated.ts
161
- var VERSION = "1.0.0-alpha.104";
161
+ var VERSION = "1.0.0-alpha.106";
162
162
 
163
163
  // src/lib/predicates.ts
164
164
  function isFunction(value) {
@@ -795,6 +795,7 @@ var Computed = class extends Source {
795
795
  super();
796
796
  this._threw = false;
797
797
  this._initialized = false;
798
+ this._disposed = false;
798
799
  this.state = DIRTY;
799
800
  /** Nodes this computation read on its last run. Rebuilt on every run. */
800
801
  this.sources = null;
@@ -810,8 +811,23 @@ var Computed = class extends Source {
810
811
  if (this._threw) throw this._error;
811
812
  return this._value;
812
813
  }
814
+ /**
815
+ * Unlink from every source, for good. Without this, a source outlives the
816
+ * computation and keeps it (and everything its function captured) reachable
817
+ * through its observers set: a disposed list row stayed in memory that way.
818
+ * The last value stays readable; the node simply never updates again.
819
+ */
820
+ dispose() {
821
+ if (this._disposed) return;
822
+ this._disposed = true;
823
+ if (this.sources) {
824
+ for (const s of this.sources) s.observers?.delete(this);
825
+ this.sources = null;
826
+ }
827
+ }
813
828
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
814
829
  updateIfNecessary() {
830
+ if (this._disposed) return;
815
831
  if (this.state === CHECK) {
816
832
  if (this.sources) {
817
833
  for (const s of this.sources) {
@@ -855,7 +871,7 @@ var Computed = class extends Source {
855
871
  reportReactiveRun(this);
856
872
  if (changed) {
857
873
  this.version++;
858
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
874
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
859
875
  notifyWatchers(this);
860
876
  }
861
877
  }
@@ -869,7 +885,7 @@ var Computed = class extends Source {
869
885
  const wasClean = this.state === CLEAN;
870
886
  this.state = level;
871
887
  if (wasClean) {
872
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
888
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
873
889
  notifyWatchers(this);
874
890
  }
875
891
  }
@@ -908,6 +924,10 @@ var Watcher = class {
908
924
  this._pending.clear();
909
925
  return out;
910
926
  }
927
+ /** Drain pending without the array, for callers that only need it empty. */
928
+ clearPending() {
929
+ this._pending.clear();
930
+ }
911
931
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
912
932
  _notify(node) {
913
933
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -1125,14 +1145,14 @@ var EffectNode = class {
1125
1145
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
1126
1146
  });
1127
1147
  });
1128
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
1148
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
1129
1149
  this.watcher = new Watcher(() => enqueue(this));
1130
1150
  this.run();
1131
1151
  this.watcher.watch(this.computed);
1132
1152
  }
1133
1153
  run() {
1134
1154
  if (this.disposed) return;
1135
- this.watcher.getPending();
1155
+ this.watcher.clearPending();
1136
1156
  try {
1137
1157
  this.computed.get();
1138
1158
  } catch (e) {
@@ -1149,6 +1169,7 @@ var EffectNode = class {
1149
1169
  this.disposed = true;
1150
1170
  reportReactiveDispose(this.computed);
1151
1171
  this.watcher.unwatch(this.computed);
1172
+ this.computed.dispose();
1152
1173
  disposeOwner(this.owner);
1153
1174
  }
1154
1175
  };
@@ -1187,13 +1208,15 @@ function createSignal(value, options) {
1187
1208
  },
1188
1209
  enumerable: false
1189
1210
  });
1190
- reportReactiveNode({
1191
- kind: "signal",
1192
- node: state,
1193
- handle: read,
1194
- name: options?.name,
1195
- set: write
1196
- });
1211
+ if (isObserved()) {
1212
+ reportReactiveNode({
1213
+ kind: "signal",
1214
+ node: state,
1215
+ handle: read,
1216
+ name: options?.name,
1217
+ set: write
1218
+ });
1219
+ }
1197
1220
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
1198
1221
  return [read, write];
1199
1222
  }
@@ -1217,8 +1240,11 @@ function createMemo(fn, value, options) {
1217
1240
  const read = (() => c.get());
1218
1241
  Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
1219
1242
  Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
1220
- reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1221
- if (isObserved()) onCleanup(() => reportReactiveDispose(c));
1243
+ if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
1244
+ onCleanup(() => {
1245
+ c.dispose();
1246
+ if (isObserved()) reportReactiveDispose(c);
1247
+ });
1222
1248
  return read;
1223
1249
  }
1224
1250
  function createEffect(fn, value, options) {
@@ -1275,6 +1301,20 @@ function on(deps, fn, options) {
1275
1301
  }
1276
1302
 
1277
1303
  // src/lib/signal/list.ts
1304
+ function lazyIndex(initial) {
1305
+ let current = initial;
1306
+ let state = null;
1307
+ return {
1308
+ index: () => {
1309
+ if (!state) state = createSignal(current, { name: "row index" });
1310
+ return state[0]();
1311
+ },
1312
+ set: (n) => {
1313
+ current = n;
1314
+ if (state) state[1](n);
1315
+ }
1316
+ };
1317
+ }
1278
1318
  function mapArray(list, mapFn, options) {
1279
1319
  let items = [];
1280
1320
  onCleanup(() => {
@@ -1304,9 +1344,9 @@ function mapArray(list, mapFn, options) {
1304
1344
  let rendered, setIndex, dispose;
1305
1345
  createRoot((d) => {
1306
1346
  dispose = d;
1307
- const [index, setIdx] = createSignal(i, { name: "row index" });
1308
- setIndex = setIdx;
1309
- rendered = mapFn(value, index);
1347
+ const idx = lazyIndex(i);
1348
+ setIndex = idx.set;
1349
+ rendered = mapFn(value, idx.index);
1310
1350
  });
1311
1351
  const entry = { item: value, rendered, dispose, setIndex };
1312
1352
  result.push(entry);
@@ -1346,21 +1386,19 @@ function keyArray(list, keyFn, mapFn, options) {
1346
1386
  const reuse = prevByKey.get(key);
1347
1387
  if (reuse && !usedKeys.has(key)) {
1348
1388
  reuse.setIndex(i);
1389
+ reuse.setItem(next[i]);
1349
1390
  resultEntries.push(reuse);
1350
1391
  } else {
1351
- let rendered, setIndex, dispose;
1392
+ let rendered, setIndex, setItem, dispose;
1352
1393
  createRoot((d) => {
1353
1394
  dispose = d;
1354
- const [index, setIdx] = createSignal(i, { name: "row index" });
1355
- setIndex = setIdx;
1356
- const item = createMemo((prev) => {
1357
- const cur = list() || [];
1358
- const v = cur[index()];
1359
- return v !== void 0 && v !== null ? v : prev;
1360
- }, next[i]);
1361
- rendered = mapFn(item, index);
1395
+ const idx = lazyIndex(i);
1396
+ setIndex = idx.set;
1397
+ const [item, writeItem] = createSignal(next[i]);
1398
+ setItem = (v) => writeItem(() => v);
1399
+ rendered = mapFn(item, idx.index);
1362
1400
  });
1363
- resultEntries.push({ rendered, dispose, setIndex });
1401
+ resultEntries.push({ rendered, dispose, setIndex, setItem });
1364
1402
  }
1365
1403
  resultKeys.push(key);
1366
1404
  usedKeys.add(key);
@@ -1410,7 +1448,7 @@ function indexArray(list, mapFn, options) {
1410
1448
 
1411
1449
  // src/lib/signal/graph/detect.ts
1412
1450
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
1413
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
1451
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
1414
1452
  var $SIGNAL2 = /* @__PURE__ */ Symbol.for("signal");
1415
1453
  var $MEMO2 = /* @__PURE__ */ Symbol.for("memo");
1416
1454
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
@@ -2222,12 +2260,14 @@ var Store = class {
2222
2260
  OWNERS.set(initialState, { store: this, name: options.name ?? "store" });
2223
2261
  ROOTS.set(initialState, initialState);
2224
2262
  }
2225
- reportReactiveNode({
2226
- kind: "store",
2227
- node: this,
2228
- handle: () => this.root,
2229
- name: options.name
2230
- });
2263
+ if (isObserved()) {
2264
+ reportReactiveNode({
2265
+ kind: "store",
2266
+ node: this,
2267
+ handle: () => this.root,
2268
+ name: options.name
2269
+ });
2270
+ }
2231
2271
  }
2232
2272
  get state() {
2233
2273
  return box(this.root);