@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
@@ -26,7 +26,7 @@ __export(debounced_exports, {
26
26
  module.exports = __toCommonJS(debounced_exports);
27
27
 
28
28
  // src/version.generated.ts
29
- var VERSION = "1.0.0-alpha.104";
29
+ var VERSION = "1.0.0-alpha.106";
30
30
 
31
31
  // src/lib/signal/graph/state.ts
32
32
  var MAJOR = VERSION.split(".")[0];
@@ -168,6 +168,7 @@ var Computed = class extends Source {
168
168
  super();
169
169
  this._threw = false;
170
170
  this._initialized = false;
171
+ this._disposed = false;
171
172
  this.state = DIRTY;
172
173
  /** Nodes this computation read on its last run. Rebuilt on every run. */
173
174
  this.sources = null;
@@ -183,8 +184,23 @@ var Computed = class extends Source {
183
184
  if (this._threw) throw this._error;
184
185
  return this._value;
185
186
  }
187
+ /**
188
+ * Unlink from every source, for good. Without this, a source outlives the
189
+ * computation and keeps it (and everything its function captured) reachable
190
+ * through its observers set: a disposed list row stayed in memory that way.
191
+ * The last value stays readable; the node simply never updates again.
192
+ */
193
+ dispose() {
194
+ if (this._disposed) return;
195
+ this._disposed = true;
196
+ if (this.sources) {
197
+ for (const s of this.sources) s.observers?.delete(this);
198
+ this.sources = null;
199
+ }
200
+ }
186
201
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
187
202
  updateIfNecessary() {
203
+ if (this._disposed) return;
188
204
  if (this.state === CHECK) {
189
205
  if (this.sources) {
190
206
  for (const s of this.sources) {
@@ -228,7 +244,7 @@ var Computed = class extends Source {
228
244
  reportReactiveRun(this);
229
245
  if (changed) {
230
246
  this.version++;
231
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
247
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
232
248
  notifyWatchers(this);
233
249
  }
234
250
  }
@@ -242,7 +258,7 @@ var Computed = class extends Source {
242
258
  const wasClean = this.state === CLEAN;
243
259
  this.state = level;
244
260
  if (wasClean) {
245
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
261
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
246
262
  notifyWatchers(this);
247
263
  }
248
264
  }
@@ -281,6 +297,10 @@ var Watcher = class {
281
297
  this._pending.clear();
282
298
  return out;
283
299
  }
300
+ /** Drain pending without the array, for callers that only need it empty. */
301
+ clearPending() {
302
+ this._pending.clear();
303
+ }
284
304
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
285
305
  _notify(node) {
286
306
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -398,14 +418,14 @@ var EffectNode = class {
398
418
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
399
419
  });
400
420
  });
401
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
421
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
402
422
  this.watcher = new Watcher(() => enqueue(this));
403
423
  this.run();
404
424
  this.watcher.watch(this.computed);
405
425
  }
406
426
  run() {
407
427
  if (this.disposed) return;
408
- this.watcher.getPending();
428
+ this.watcher.clearPending();
409
429
  try {
410
430
  this.computed.get();
411
431
  } catch (e) {
@@ -422,6 +442,7 @@ var EffectNode = class {
422
442
  this.disposed = true;
423
443
  reportReactiveDispose(this.computed);
424
444
  this.watcher.unwatch(this.computed);
445
+ this.computed.dispose();
425
446
  disposeOwner(this.owner);
426
447
  }
427
448
  };
@@ -456,13 +477,15 @@ function createSignal(value, options) {
456
477
  },
457
478
  enumerable: false
458
479
  });
459
- reportReactiveNode({
460
- kind: "signal",
461
- node: state,
462
- handle: read,
463
- name: options?.name,
464
- set: write
465
- });
480
+ if (isObserved()) {
481
+ reportReactiveNode({
482
+ kind: "signal",
483
+ node: state,
484
+ handle: read,
485
+ name: options?.name,
486
+ set: write
487
+ });
488
+ }
466
489
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
467
490
  return [read, write];
468
491
  }
@@ -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];
@@ -141,6 +141,7 @@ var Computed = class extends Source {
141
141
  super();
142
142
  this._threw = false;
143
143
  this._initialized = false;
144
+ this._disposed = false;
144
145
  this.state = DIRTY;
145
146
  /** Nodes this computation read on its last run. Rebuilt on every run. */
146
147
  this.sources = null;
@@ -156,8 +157,23 @@ var Computed = class extends Source {
156
157
  if (this._threw) throw this._error;
157
158
  return this._value;
158
159
  }
160
+ /**
161
+ * Unlink from every source, for good. Without this, a source outlives the
162
+ * computation and keeps it (and everything its function captured) reachable
163
+ * through its observers set: a disposed list row stayed in memory that way.
164
+ * The last value stays readable; the node simply never updates again.
165
+ */
166
+ dispose() {
167
+ if (this._disposed) return;
168
+ this._disposed = true;
169
+ if (this.sources) {
170
+ for (const s of this.sources) s.observers?.delete(this);
171
+ this.sources = null;
172
+ }
173
+ }
159
174
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
160
175
  updateIfNecessary() {
176
+ if (this._disposed) return;
161
177
  if (this.state === CHECK) {
162
178
  if (this.sources) {
163
179
  for (const s of this.sources) {
@@ -201,7 +217,7 @@ var Computed = class extends Source {
201
217
  reportReactiveRun(this);
202
218
  if (changed) {
203
219
  this.version++;
204
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
220
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
205
221
  notifyWatchers(this);
206
222
  }
207
223
  }
@@ -215,7 +231,7 @@ var Computed = class extends Source {
215
231
  const wasClean = this.state === CLEAN;
216
232
  this.state = level;
217
233
  if (wasClean) {
218
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
234
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
219
235
  notifyWatchers(this);
220
236
  }
221
237
  }
@@ -254,6 +270,10 @@ var Watcher = class {
254
270
  this._pending.clear();
255
271
  return out;
256
272
  }
273
+ /** Drain pending without the array, for callers that only need it empty. */
274
+ clearPending() {
275
+ this._pending.clear();
276
+ }
257
277
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
258
278
  _notify(node) {
259
279
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -371,14 +391,14 @@ var EffectNode = class {
371
391
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
372
392
  });
373
393
  });
374
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
394
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
375
395
  this.watcher = new Watcher(() => enqueue(this));
376
396
  this.run();
377
397
  this.watcher.watch(this.computed);
378
398
  }
379
399
  run() {
380
400
  if (this.disposed) return;
381
- this.watcher.getPending();
401
+ this.watcher.clearPending();
382
402
  try {
383
403
  this.computed.get();
384
404
  } catch (e) {
@@ -395,6 +415,7 @@ var EffectNode = class {
395
415
  this.disposed = true;
396
416
  reportReactiveDispose(this.computed);
397
417
  this.watcher.unwatch(this.computed);
418
+ this.computed.dispose();
398
419
  disposeOwner(this.owner);
399
420
  }
400
421
  };
@@ -429,13 +450,15 @@ function createSignal(value, options) {
429
450
  },
430
451
  enumerable: false
431
452
  });
432
- reportReactiveNode({
433
- kind: "signal",
434
- node: state,
435
- handle: read,
436
- name: options?.name,
437
- set: write
438
- });
453
+ if (isObserved()) {
454
+ reportReactiveNode({
455
+ kind: "signal",
456
+ node: state,
457
+ handle: read,
458
+ name: options?.name,
459
+ set: write
460
+ });
461
+ }
439
462
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
440
463
  return [read, write];
441
464
  }
@@ -27,7 +27,7 @@ __export(derived_exports, {
27
27
  module.exports = __toCommonJS(derived_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];
@@ -169,6 +169,7 @@ var Computed = class extends Source {
169
169
  super();
170
170
  this._threw = false;
171
171
  this._initialized = false;
172
+ this._disposed = false;
172
173
  this.state = DIRTY;
173
174
  /** Nodes this computation read on its last run. Rebuilt on every run. */
174
175
  this.sources = null;
@@ -184,8 +185,23 @@ var Computed = class extends Source {
184
185
  if (this._threw) throw this._error;
185
186
  return this._value;
186
187
  }
188
+ /**
189
+ * Unlink from every source, for good. Without this, a source outlives the
190
+ * computation and keeps it (and everything its function captured) reachable
191
+ * through its observers set: a disposed list row stayed in memory that way.
192
+ * The last value stays readable; the node simply never updates again.
193
+ */
194
+ dispose() {
195
+ if (this._disposed) return;
196
+ this._disposed = true;
197
+ if (this.sources) {
198
+ for (const s of this.sources) s.observers?.delete(this);
199
+ this.sources = null;
200
+ }
201
+ }
187
202
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
188
203
  updateIfNecessary() {
204
+ if (this._disposed) return;
189
205
  if (this.state === CHECK) {
190
206
  if (this.sources) {
191
207
  for (const s of this.sources) {
@@ -229,7 +245,7 @@ var Computed = class extends Source {
229
245
  reportReactiveRun(this);
230
246
  if (changed) {
231
247
  this.version++;
232
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
248
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
233
249
  notifyWatchers(this);
234
250
  }
235
251
  }
@@ -243,7 +259,7 @@ var Computed = class extends Source {
243
259
  const wasClean = this.state === CLEAN;
244
260
  this.state = level;
245
261
  if (wasClean) {
246
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
262
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
247
263
  notifyWatchers(this);
248
264
  }
249
265
  }
@@ -282,6 +298,10 @@ var Watcher = class {
282
298
  this._pending.clear();
283
299
  return out;
284
300
  }
301
+ /** Drain pending without the array, for callers that only need it empty. */
302
+ clearPending() {
303
+ this._pending.clear();
304
+ }
285
305
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
286
306
  _notify(node) {
287
307
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -382,14 +402,14 @@ var EffectNode = class {
382
402
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
383
403
  });
384
404
  });
385
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
405
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
386
406
  this.watcher = new Watcher(() => enqueue(this));
387
407
  this.run();
388
408
  this.watcher.watch(this.computed);
389
409
  }
390
410
  run() {
391
411
  if (this.disposed) return;
392
- this.watcher.getPending();
412
+ this.watcher.clearPending();
393
413
  try {
394
414
  this.computed.get();
395
415
  } catch (e) {
@@ -406,6 +426,7 @@ var EffectNode = class {
406
426
  this.disposed = true;
407
427
  reportReactiveDispose(this.computed);
408
428
  this.watcher.unwatch(this.computed);
429
+ this.computed.dispose();
409
430
  disposeOwner(this.owner);
410
431
  }
411
432
  };
@@ -440,13 +461,15 @@ function createSignal(value, options) {
440
461
  },
441
462
  enumerable: false
442
463
  });
443
- reportReactiveNode({
444
- kind: "signal",
445
- node: state,
446
- handle: read,
447
- name: options?.name,
448
- set: write
449
- });
464
+ if (isObserved()) {
465
+ reportReactiveNode({
466
+ kind: "signal",
467
+ node: state,
468
+ handle: read,
469
+ name: options?.name,
470
+ set: write
471
+ });
472
+ }
450
473
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
451
474
  return [read, write];
452
475
  }
@@ -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];
@@ -141,6 +141,7 @@ var Computed = class extends Source {
141
141
  super();
142
142
  this._threw = false;
143
143
  this._initialized = false;
144
+ this._disposed = false;
144
145
  this.state = DIRTY;
145
146
  /** Nodes this computation read on its last run. Rebuilt on every run. */
146
147
  this.sources = null;
@@ -156,8 +157,23 @@ var Computed = class extends Source {
156
157
  if (this._threw) throw this._error;
157
158
  return this._value;
158
159
  }
160
+ /**
161
+ * Unlink from every source, for good. Without this, a source outlives the
162
+ * computation and keeps it (and everything its function captured) reachable
163
+ * through its observers set: a disposed list row stayed in memory that way.
164
+ * The last value stays readable; the node simply never updates again.
165
+ */
166
+ dispose() {
167
+ if (this._disposed) return;
168
+ this._disposed = true;
169
+ if (this.sources) {
170
+ for (const s of this.sources) s.observers?.delete(this);
171
+ this.sources = null;
172
+ }
173
+ }
159
174
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
160
175
  updateIfNecessary() {
176
+ if (this._disposed) return;
161
177
  if (this.state === CHECK) {
162
178
  if (this.sources) {
163
179
  for (const s of this.sources) {
@@ -201,7 +217,7 @@ var Computed = class extends Source {
201
217
  reportReactiveRun(this);
202
218
  if (changed) {
203
219
  this.version++;
204
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
220
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
205
221
  notifyWatchers(this);
206
222
  }
207
223
  }
@@ -215,7 +231,7 @@ var Computed = class extends Source {
215
231
  const wasClean = this.state === CLEAN;
216
232
  this.state = level;
217
233
  if (wasClean) {
218
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
234
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
219
235
  notifyWatchers(this);
220
236
  }
221
237
  }
@@ -254,6 +270,10 @@ var Watcher = class {
254
270
  this._pending.clear();
255
271
  return out;
256
272
  }
273
+ /** Drain pending without the array, for callers that only need it empty. */
274
+ clearPending() {
275
+ this._pending.clear();
276
+ }
257
277
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
258
278
  _notify(node) {
259
279
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -354,14 +374,14 @@ var EffectNode = class {
354
374
  if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
355
375
  });
356
376
  });
357
- reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
377
+ if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
358
378
  this.watcher = new Watcher(() => enqueue(this));
359
379
  this.run();
360
380
  this.watcher.watch(this.computed);
361
381
  }
362
382
  run() {
363
383
  if (this.disposed) return;
364
- this.watcher.getPending();
384
+ this.watcher.clearPending();
365
385
  try {
366
386
  this.computed.get();
367
387
  } catch (e) {
@@ -378,6 +398,7 @@ var EffectNode = class {
378
398
  this.disposed = true;
379
399
  reportReactiveDispose(this.computed);
380
400
  this.watcher.unwatch(this.computed);
401
+ this.computed.dispose();
381
402
  disposeOwner(this.owner);
382
403
  }
383
404
  };
@@ -412,13 +433,15 @@ function createSignal(value, options) {
412
433
  },
413
434
  enumerable: false
414
435
  });
415
- reportReactiveNode({
416
- kind: "signal",
417
- node: state,
418
- handle: read,
419
- name: options?.name,
420
- set: write
421
- });
436
+ if (isObserved()) {
437
+ reportReactiveNode({
438
+ kind: "signal",
439
+ node: state,
440
+ handle: read,
441
+ name: options?.name,
442
+ set: write
443
+ });
444
+ }
422
445
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
423
446
  return [read, write];
424
447
  }
@@ -32,7 +32,7 @@ __export(detect_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(detect_exports);
34
34
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
35
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
35
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
36
36
  var $SIGNAL = /* @__PURE__ */ Symbol.for("signal");
37
37
  var $MEMO = /* @__PURE__ */ Symbol.for("memo");
38
38
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
@@ -1,6 +1,6 @@
1
1
  // src/lib/signal/graph/detect.ts
2
2
  var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
3
- var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : false;
3
+ var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
4
4
  var $SIGNAL = /* @__PURE__ */ Symbol.for("signal");
5
5
  var $MEMO = /* @__PURE__ */ Symbol.for("memo");
6
6
  var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
@@ -27,7 +27,7 @@ __export(error_boundary_exports, {
27
27
  module.exports = __toCommonJS(error_boundary_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];
@@ -280,13 +280,15 @@ function createSignal(value, options) {
280
280
  },
281
281
  enumerable: false
282
282
  });
283
- reportReactiveNode({
284
- kind: "signal",
285
- node: state,
286
- handle: read,
287
- name: options?.name,
288
- set: write
289
- });
283
+ if (isObserved()) {
284
+ reportReactiveNode({
285
+ kind: "signal",
286
+ node: state,
287
+ handle: read,
288
+ name: options?.name,
289
+ set: write
290
+ });
291
+ }
290
292
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
291
293
  return [read, write];
292
294
  }
@@ -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];
@@ -252,13 +252,15 @@ function createSignal(value, options) {
252
252
  },
253
253
  enumerable: false
254
254
  });
255
- reportReactiveNode({
256
- kind: "signal",
257
- node: state,
258
- handle: read,
259
- name: options?.name,
260
- set: write
261
- });
255
+ if (isObserved()) {
256
+ reportReactiveNode({
257
+ kind: "signal",
258
+ node: state,
259
+ handle: read,
260
+ name: options?.name,
261
+ set: write
262
+ });
263
+ }
262
264
  if (isObserved()) onCleanup(() => reportReactiveDispose(state));
263
265
  return [read, write];
264
266
  }
@@ -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;
@@ -29,7 +29,7 @@ __export(index_exports, {
29
29
  module.exports = __toCommonJS(index_exports);
30
30
 
31
31
  // src/version.generated.ts
32
- var VERSION = "1.0.0-alpha.104";
32
+ var VERSION = "1.0.0-alpha.106";
33
33
 
34
34
  // src/lib/signal/graph/state.ts
35
35
  var MAJOR = VERSION.split(".")[0];
@@ -155,6 +155,7 @@ var Computed = class extends Source {
155
155
  super();
156
156
  this._threw = false;
157
157
  this._initialized = false;
158
+ this._disposed = false;
158
159
  this.state = DIRTY;
159
160
  /** Nodes this computation read on its last run. Rebuilt on every run. */
160
161
  this.sources = null;
@@ -170,8 +171,23 @@ var Computed = class extends Source {
170
171
  if (this._threw) throw this._error;
171
172
  return this._value;
172
173
  }
174
+ /**
175
+ * Unlink from every source, for good. Without this, a source outlives the
176
+ * computation and keeps it (and everything its function captured) reachable
177
+ * through its observers set: a disposed list row stayed in memory that way.
178
+ * The last value stays readable; the node simply never updates again.
179
+ */
180
+ dispose() {
181
+ if (this._disposed) return;
182
+ this._disposed = true;
183
+ if (this.sources) {
184
+ for (const s of this.sources) s.observers?.delete(this);
185
+ this.sources = null;
186
+ }
187
+ }
173
188
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
174
189
  updateIfNecessary() {
190
+ if (this._disposed) return;
175
191
  if (this.state === CHECK) {
176
192
  if (this.sources) {
177
193
  for (const s of this.sources) {
@@ -215,7 +231,7 @@ var Computed = class extends Source {
215
231
  reportReactiveRun(this);
216
232
  if (changed) {
217
233
  this.version++;
218
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
234
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
219
235
  notifyWatchers(this);
220
236
  }
221
237
  }
@@ -229,7 +245,7 @@ var Computed = class extends Source {
229
245
  const wasClean = this.state === CLEAN;
230
246
  this.state = level;
231
247
  if (wasClean) {
232
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
248
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
233
249
  notifyWatchers(this);
234
250
  }
235
251
  }
@@ -268,6 +284,10 @@ var Watcher = class {
268
284
  this._pending.clear();
269
285
  return out;
270
286
  }
287
+ /** Drain pending without the array, for callers that only need it empty. */
288
+ clearPending() {
289
+ this._pending.clear();
290
+ }
271
291
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
272
292
  _notify(node) {
273
293
  if (!this._watched.has(node) || this._pending.has(node)) return;
@@ -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/signal/graph/state.ts
7
7
  var MAJOR = VERSION.split(".")[0];
@@ -127,6 +127,7 @@ var Computed = class extends Source {
127
127
  super();
128
128
  this._threw = false;
129
129
  this._initialized = false;
130
+ this._disposed = false;
130
131
  this.state = DIRTY;
131
132
  /** Nodes this computation read on its last run. Rebuilt on every run. */
132
133
  this.sources = null;
@@ -142,8 +143,23 @@ var Computed = class extends Source {
142
143
  if (this._threw) throw this._error;
143
144
  return this._value;
144
145
  }
146
+ /**
147
+ * Unlink from every source, for good. Without this, a source outlives the
148
+ * computation and keeps it (and everything its function captured) reachable
149
+ * through its observers set: a disposed list row stayed in memory that way.
150
+ * The last value stays readable; the node simply never updates again.
151
+ */
152
+ dispose() {
153
+ if (this._disposed) return;
154
+ this._disposed = true;
155
+ if (this.sources) {
156
+ for (const s of this.sources) s.observers?.delete(this);
157
+ this.sources = null;
158
+ }
159
+ }
145
160
  /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
146
161
  updateIfNecessary() {
162
+ if (this._disposed) return;
147
163
  if (this.state === CHECK) {
148
164
  if (this.sources) {
149
165
  for (const s of this.sources) {
@@ -187,7 +203,7 @@ var Computed = class extends Source {
187
203
  reportReactiveRun(this);
188
204
  if (changed) {
189
205
  this.version++;
190
- if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
206
+ if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
191
207
  notifyWatchers(this);
192
208
  }
193
209
  }
@@ -201,7 +217,7 @@ var Computed = class extends Source {
201
217
  const wasClean = this.state === CLEAN;
202
218
  this.state = level;
203
219
  if (wasClean) {
204
- if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
220
+ if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
205
221
  notifyWatchers(this);
206
222
  }
207
223
  }
@@ -240,6 +256,10 @@ var Watcher = class {
240
256
  this._pending.clear();
241
257
  return out;
242
258
  }
259
+ /** Drain pending without the array, for callers that only need it empty. */
260
+ clearPending() {
261
+ this._pending.clear();
262
+ }
243
263
  /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
244
264
  _notify(node) {
245
265
  if (!this._watched.has(node) || this._pending.has(node)) return;