@fluixi/reactive 1.0.0-alpha.105 → 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.
- package/dist/cdn/reactive.cjs +74 -34
- package/dist/cdn/reactive.global.js +2 -2
- package/dist/cdn/reactive.mjs +74 -34
- package/dist/cdn/signal.cjs +23 -3
- package/dist/cdn/signal.global.js +2 -2
- package/dist/cdn/signal.mjs +23 -3
- package/dist/index.cjs +74 -34
- package/dist/index.mjs +74 -34
- package/dist/lib/index.cjs +75 -35
- package/dist/lib/index.mjs +75 -35
- package/dist/lib/observable/index.cjs +1 -1
- package/dist/lib/observable/index.mjs +1 -1
- package/dist/lib/signal/api.cjs +40 -14
- package/dist/lib/signal/api.mjs +40 -14
- package/dist/lib/signal/cached-resource.cjs +36 -13
- package/dist/lib/signal/cached-resource.mjs +36 -13
- package/dist/lib/signal/graph/children.cjs +25 -6
- package/dist/lib/signal/graph/children.mjs +25 -6
- package/dist/lib/signal/graph/compat.cjs +41 -15
- package/dist/lib/signal/graph/compat.d.ts.map +1 -1
- package/dist/lib/signal/graph/compat.js +20 -11
- package/dist/lib/signal/graph/compat.mjs +41 -15
- package/dist/lib/signal/graph/context-children.cjs +1 -1
- package/dist/lib/signal/graph/context-children.mjs +1 -1
- package/dist/lib/signal/graph/core.cjs +24 -4
- package/dist/lib/signal/graph/core.d.ts +10 -0
- package/dist/lib/signal/graph/core.d.ts.map +1 -1
- package/dist/lib/signal/graph/core.js +27 -2
- package/dist/lib/signal/graph/core.mjs +24 -4
- package/dist/lib/signal/graph/debounced.cjs +35 -12
- package/dist/lib/signal/graph/debounced.mjs +35 -12
- package/dist/lib/signal/graph/derived.cjs +35 -12
- package/dist/lib/signal/graph/derived.mjs +35 -12
- package/dist/lib/signal/graph/detect.cjs +1 -1
- package/dist/lib/signal/graph/detect.mjs +1 -1
- package/dist/lib/signal/graph/error-boundary.cjs +10 -8
- package/dist/lib/signal/graph/error-boundary.mjs +10 -8
- package/dist/lib/signal/graph/index.cjs +24 -4
- package/dist/lib/signal/graph/index.mjs +24 -4
- package/dist/lib/signal/graph/observe.cjs +1 -1
- package/dist/lib/signal/graph/observe.mjs +1 -1
- package/dist/lib/signal/graph/resource.cjs +35 -12
- package/dist/lib/signal/graph/resource.mjs +35 -12
- package/dist/lib/signal/graph/runtime.cjs +29 -5
- package/dist/lib/signal/graph/runtime.d.ts.map +1 -1
- package/dist/lib/signal/graph/runtime.js +5 -3
- package/dist/lib/signal/graph/runtime.mjs +29 -5
- package/dist/lib/signal/graph/signal-next.cjs +67 -29
- package/dist/lib/signal/graph/signal-next.mjs +67 -29
- package/dist/lib/signal/graph/state.cjs +1 -1
- package/dist/lib/signal/graph/state.mjs +1 -1
- package/dist/lib/signal/graph/suspense.cjs +10 -8
- package/dist/lib/signal/graph/suspense.mjs +10 -8
- package/dist/lib/signal/index-legacy.cjs +67 -29
- package/dist/lib/signal/index-legacy.mjs +67 -29
- package/dist/lib/signal/index.cjs +67 -29
- package/dist/lib/signal/index.mjs +67 -29
- package/dist/lib/signal/list.cjs +58 -25
- package/dist/lib/signal/list.d.ts +3 -8
- package/dist/lib/signal/list.d.ts.map +1 -1
- package/dist/lib/signal/list.js +40 -18
- package/dist/lib/signal/list.mjs +58 -25
- package/dist/lib/signal/resource-api.cjs +35 -12
- package/dist/lib/signal/resource-api.mjs +35 -12
- package/dist/lib/signal/signal.cjs +41 -15
- package/dist/lib/signal/signal.mjs +41 -15
- package/dist/lib/signal/utilities.cjs +41 -15
- package/dist/lib/signal/utilities.mjs +41 -15
- package/dist/lib/store/api.cjs +9 -7
- package/dist/lib/store/api.mjs +9 -7
- package/dist/lib/store/index.cjs +10 -8
- package/dist/lib/store/index.mjs +10 -8
- package/dist/lib/store/store.cjs +9 -7
- package/dist/lib/store/store.d.ts.map +1 -1
- package/dist/lib/store/store.js +8 -6
- package/dist/lib/store/store.mjs +9 -7
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/version.generated.cjs +2 -2
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/dist/version.generated.mjs +2 -2
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*! @fluixi/reactive v1.0.0-alpha.
|
|
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.
|
|
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];
|
|
@@ -197,6 +197,7 @@ var Computed = class extends Source {
|
|
|
197
197
|
super();
|
|
198
198
|
this._threw = false;
|
|
199
199
|
this._initialized = false;
|
|
200
|
+
this._disposed = false;
|
|
200
201
|
this.state = DIRTY;
|
|
201
202
|
/** Nodes this computation read on its last run. Rebuilt on every run. */
|
|
202
203
|
this.sources = null;
|
|
@@ -212,8 +213,23 @@ var Computed = class extends Source {
|
|
|
212
213
|
if (this._threw) throw this._error;
|
|
213
214
|
return this._value;
|
|
214
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Unlink from every source, for good. Without this, a source outlives the
|
|
218
|
+
* computation and keeps it (and everything its function captured) reachable
|
|
219
|
+
* through its observers set: a disposed list row stayed in memory that way.
|
|
220
|
+
* The last value stays readable; the node simply never updates again.
|
|
221
|
+
*/
|
|
222
|
+
dispose() {
|
|
223
|
+
if (this._disposed) return;
|
|
224
|
+
this._disposed = true;
|
|
225
|
+
if (this.sources) {
|
|
226
|
+
for (const s of this.sources) s.observers?.delete(this);
|
|
227
|
+
this.sources = null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
215
230
|
/** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
|
|
216
231
|
updateIfNecessary() {
|
|
232
|
+
if (this._disposed) return;
|
|
217
233
|
if (this.state === CHECK) {
|
|
218
234
|
if (this.sources) {
|
|
219
235
|
for (const s of this.sources) {
|
|
@@ -257,7 +273,7 @@ var Computed = class extends Source {
|
|
|
257
273
|
reportReactiveRun(this);
|
|
258
274
|
if (changed) {
|
|
259
275
|
this.version++;
|
|
260
|
-
if (this.observers) for (const o of
|
|
276
|
+
if (this.observers) for (const o of this.observers) o.markDirty(DIRTY);
|
|
261
277
|
notifyWatchers(this);
|
|
262
278
|
}
|
|
263
279
|
}
|
|
@@ -271,7 +287,7 @@ var Computed = class extends Source {
|
|
|
271
287
|
const wasClean = this.state === CLEAN;
|
|
272
288
|
this.state = level;
|
|
273
289
|
if (wasClean) {
|
|
274
|
-
if (this.observers) for (const o of
|
|
290
|
+
if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
|
|
275
291
|
notifyWatchers(this);
|
|
276
292
|
}
|
|
277
293
|
}
|
|
@@ -310,6 +326,10 @@ var Watcher = class {
|
|
|
310
326
|
this._pending.clear();
|
|
311
327
|
return out;
|
|
312
328
|
}
|
|
329
|
+
/** Drain pending without the array, for callers that only need it empty. */
|
|
330
|
+
clearPending() {
|
|
331
|
+
this._pending.clear();
|
|
332
|
+
}
|
|
313
333
|
/** @internal, a watched node became non-clean. Edge-triggered: notify once. */
|
|
314
334
|
_notify(node) {
|
|
315
335
|
if (!this._watched.has(node) || this._pending.has(node)) return;
|
|
@@ -527,14 +547,14 @@ var EffectNode = class {
|
|
|
527
547
|
if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
|
|
528
548
|
});
|
|
529
549
|
});
|
|
530
|
-
reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
|
|
550
|
+
if (isObserved()) reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report, ...name ? { name } : {} });
|
|
531
551
|
this.watcher = new Watcher(() => enqueue(this));
|
|
532
552
|
this.run();
|
|
533
553
|
this.watcher.watch(this.computed);
|
|
534
554
|
}
|
|
535
555
|
run() {
|
|
536
556
|
if (this.disposed) return;
|
|
537
|
-
this.watcher.
|
|
557
|
+
this.watcher.clearPending();
|
|
538
558
|
try {
|
|
539
559
|
this.computed.get();
|
|
540
560
|
} catch (e) {
|
|
@@ -551,6 +571,7 @@ var EffectNode = class {
|
|
|
551
571
|
this.disposed = true;
|
|
552
572
|
reportReactiveDispose(this.computed);
|
|
553
573
|
this.watcher.unwatch(this.computed);
|
|
574
|
+
this.computed.dispose();
|
|
554
575
|
disposeOwner(this.owner);
|
|
555
576
|
}
|
|
556
577
|
};
|
|
@@ -589,13 +610,15 @@ function createSignal(value, options) {
|
|
|
589
610
|
},
|
|
590
611
|
enumerable: false
|
|
591
612
|
});
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
613
|
+
if (isObserved()) {
|
|
614
|
+
reportReactiveNode({
|
|
615
|
+
kind: "signal",
|
|
616
|
+
node: state,
|
|
617
|
+
handle: read,
|
|
618
|
+
name: options?.name,
|
|
619
|
+
set: write
|
|
620
|
+
});
|
|
621
|
+
}
|
|
599
622
|
if (isObserved()) onCleanup(() => reportReactiveDispose(state));
|
|
600
623
|
return [read, write];
|
|
601
624
|
}
|
|
@@ -619,8 +642,11 @@ function createMemo(fn, value, options) {
|
|
|
619
642
|
const read = (() => c.get());
|
|
620
643
|
Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
|
|
621
644
|
Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
|
|
622
|
-
reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
|
|
623
|
-
|
|
645
|
+
if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
|
|
646
|
+
onCleanup(() => {
|
|
647
|
+
c.dispose();
|
|
648
|
+
if (isObserved()) reportReactiveDispose(c);
|
|
649
|
+
});
|
|
624
650
|
return read;
|
|
625
651
|
}
|
|
626
652
|
function createEffect(fn, value, options) {
|
|
@@ -677,6 +703,20 @@ function on(deps, fn, options) {
|
|
|
677
703
|
}
|
|
678
704
|
|
|
679
705
|
// src/lib/signal/list.ts
|
|
706
|
+
function lazyIndex(initial) {
|
|
707
|
+
let current = initial;
|
|
708
|
+
let state = null;
|
|
709
|
+
return {
|
|
710
|
+
index: () => {
|
|
711
|
+
if (!state) state = createSignal(current, { name: "row index" });
|
|
712
|
+
return state[0]();
|
|
713
|
+
},
|
|
714
|
+
set: (n) => {
|
|
715
|
+
current = n;
|
|
716
|
+
if (state) state[1](n);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
}
|
|
680
720
|
function mapArray(list, mapFn, options) {
|
|
681
721
|
let items = [];
|
|
682
722
|
onCleanup(() => {
|
|
@@ -706,9 +746,9 @@ function mapArray(list, mapFn, options) {
|
|
|
706
746
|
let rendered, setIndex, dispose;
|
|
707
747
|
createRoot((d) => {
|
|
708
748
|
dispose = d;
|
|
709
|
-
const
|
|
710
|
-
setIndex =
|
|
711
|
-
rendered = mapFn(value, index);
|
|
749
|
+
const idx = lazyIndex(i);
|
|
750
|
+
setIndex = idx.set;
|
|
751
|
+
rendered = mapFn(value, idx.index);
|
|
712
752
|
});
|
|
713
753
|
const entry = { item: value, rendered, dispose, setIndex };
|
|
714
754
|
result.push(entry);
|
|
@@ -748,21 +788,19 @@ function keyArray(list, keyFn, mapFn, options) {
|
|
|
748
788
|
const reuse = prevByKey.get(key);
|
|
749
789
|
if (reuse && !usedKeys.has(key)) {
|
|
750
790
|
reuse.setIndex(i);
|
|
791
|
+
reuse.setItem(next[i]);
|
|
751
792
|
resultEntries.push(reuse);
|
|
752
793
|
} else {
|
|
753
|
-
let rendered, setIndex, dispose;
|
|
794
|
+
let rendered, setIndex, setItem, dispose;
|
|
754
795
|
createRoot((d) => {
|
|
755
796
|
dispose = d;
|
|
756
|
-
const
|
|
757
|
-
setIndex =
|
|
758
|
-
const item =
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
return v !== void 0 && v !== null ? v : prev;
|
|
762
|
-
}, next[i]);
|
|
763
|
-
rendered = mapFn(item, index);
|
|
797
|
+
const idx = lazyIndex(i);
|
|
798
|
+
setIndex = idx.set;
|
|
799
|
+
const [item, writeItem] = createSignal(next[i]);
|
|
800
|
+
setItem = (v) => writeItem(() => v);
|
|
801
|
+
rendered = mapFn(item, idx.index);
|
|
764
802
|
});
|
|
765
|
-
resultEntries.push({ rendered, dispose, setIndex });
|
|
803
|
+
resultEntries.push({ rendered, dispose, setIndex, setItem });
|
|
766
804
|
}
|
|
767
805
|
resultKeys.push(key);
|
|
768
806
|
usedKeys.add(key);
|
|
@@ -812,7 +850,7 @@ function indexArray(list, mapFn, options) {
|
|
|
812
850
|
|
|
813
851
|
// src/lib/signal/graph/detect.ts
|
|
814
852
|
var __BROWSER_PROD__ = typeof window === "undefined" ? false : window.__FLUIXI_PROD__ === true;
|
|
815
|
-
var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ :
|
|
853
|
+
var __DEV__ = typeof process === "undefined" ? __BROWSER_PROD__ : process.env.NODE_ENV !== "production";
|
|
816
854
|
var $SIGNAL2 = /* @__PURE__ */ Symbol.for("signal");
|
|
817
855
|
var $MEMO2 = /* @__PURE__ */ Symbol.for("memo");
|
|
818
856
|
var $RESOURCE = /* @__PURE__ */ Symbol.for("resource");
|
package/dist/lib/signal/list.cjs
CHANGED
|
@@ -27,7 +27,7 @@ __export(list_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(list_exports);
|
|
28
28
|
|
|
29
29
|
// src/version.generated.ts
|
|
30
|
-
var VERSION = "1.0.0-alpha.
|
|
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
|
|
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
|
|
262
|
+
if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
|
|
247
263
|
notifyWatchers(this);
|
|
248
264
|
}
|
|
249
265
|
}
|
|
@@ -388,13 +404,15 @@ function createSignal(value, options) {
|
|
|
388
404
|
},
|
|
389
405
|
enumerable: false
|
|
390
406
|
});
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
407
|
+
if (isObserved()) {
|
|
408
|
+
reportReactiveNode({
|
|
409
|
+
kind: "signal",
|
|
410
|
+
node: state,
|
|
411
|
+
handle: read,
|
|
412
|
+
name: options?.name,
|
|
413
|
+
set: write
|
|
414
|
+
});
|
|
415
|
+
}
|
|
398
416
|
if (isObserved()) onCleanup(() => reportReactiveDispose(state));
|
|
399
417
|
return [read, write];
|
|
400
418
|
}
|
|
@@ -418,8 +436,11 @@ function createMemo(fn, value, options) {
|
|
|
418
436
|
const read = (() => c.get());
|
|
419
437
|
Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
|
|
420
438
|
Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
|
|
421
|
-
reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
|
|
422
|
-
|
|
439
|
+
if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
|
|
440
|
+
onCleanup(() => {
|
|
441
|
+
c.dispose();
|
|
442
|
+
if (isObserved()) reportReactiveDispose(c);
|
|
443
|
+
});
|
|
423
444
|
return read;
|
|
424
445
|
}
|
|
425
446
|
function untrack2(fn) {
|
|
@@ -427,6 +448,20 @@ function untrack2(fn) {
|
|
|
427
448
|
}
|
|
428
449
|
|
|
429
450
|
// src/lib/signal/list.ts
|
|
451
|
+
function lazyIndex(initial) {
|
|
452
|
+
let current = initial;
|
|
453
|
+
let state = null;
|
|
454
|
+
return {
|
|
455
|
+
index: () => {
|
|
456
|
+
if (!state) state = createSignal(current, { name: "row index" });
|
|
457
|
+
return state[0]();
|
|
458
|
+
},
|
|
459
|
+
set: (n) => {
|
|
460
|
+
current = n;
|
|
461
|
+
if (state) state[1](n);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
}
|
|
430
465
|
function mapArray(list, mapFn, options) {
|
|
431
466
|
let items = [];
|
|
432
467
|
onCleanup(() => {
|
|
@@ -456,9 +491,9 @@ function mapArray(list, mapFn, options) {
|
|
|
456
491
|
let rendered, setIndex, dispose;
|
|
457
492
|
createRoot((d) => {
|
|
458
493
|
dispose = d;
|
|
459
|
-
const
|
|
460
|
-
setIndex =
|
|
461
|
-
rendered = mapFn(value, index);
|
|
494
|
+
const idx = lazyIndex(i);
|
|
495
|
+
setIndex = idx.set;
|
|
496
|
+
rendered = mapFn(value, idx.index);
|
|
462
497
|
});
|
|
463
498
|
const entry = { item: value, rendered, dispose, setIndex };
|
|
464
499
|
result.push(entry);
|
|
@@ -498,21 +533,19 @@ function keyArray(list, keyFn, mapFn, options) {
|
|
|
498
533
|
const reuse = prevByKey.get(key);
|
|
499
534
|
if (reuse && !usedKeys.has(key)) {
|
|
500
535
|
reuse.setIndex(i);
|
|
536
|
+
reuse.setItem(next[i]);
|
|
501
537
|
resultEntries.push(reuse);
|
|
502
538
|
} else {
|
|
503
|
-
let rendered, setIndex, dispose;
|
|
539
|
+
let rendered, setIndex, setItem, dispose;
|
|
504
540
|
createRoot((d) => {
|
|
505
541
|
dispose = d;
|
|
506
|
-
const
|
|
507
|
-
setIndex =
|
|
508
|
-
const item =
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
return v !== void 0 && v !== null ? v : prev;
|
|
512
|
-
}, next[i]);
|
|
513
|
-
rendered = mapFn(item, index);
|
|
542
|
+
const idx = lazyIndex(i);
|
|
543
|
+
setIndex = idx.set;
|
|
544
|
+
const [item, writeItem] = createSignal(next[i]);
|
|
545
|
+
setItem = (v) => writeItem(() => v);
|
|
546
|
+
rendered = mapFn(item, idx.index);
|
|
514
547
|
});
|
|
515
|
-
resultEntries.push({ rendered, dispose, setIndex });
|
|
548
|
+
resultEntries.push({ rendered, dispose, setIndex, setItem });
|
|
516
549
|
}
|
|
517
550
|
resultKeys.push(key);
|
|
518
551
|
usedKeys.add(key);
|
|
@@ -14,18 +14,13 @@ interface ListOptions {
|
|
|
14
14
|
/** Returned (as-is, not wrapped) when the list is empty/nullish. */
|
|
15
15
|
fallback?: () => any;
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Reference-keyed map. Reuses an item's mapped value while the same value (by
|
|
19
|
-
* `===`) stays in the list; new values get a fresh root + index signal; removed
|
|
20
|
-
* values are disposed. The map fn receives the plain value (a snapshot) and a
|
|
21
|
-
* reactive index accessor. Backs `<For>`.
|
|
22
|
-
*/
|
|
23
17
|
export declare function mapArray<T, U>(list: ListSource<T>, mapFn: (item: T, index: Accessor<number>) => U, options?: ListOptions): Accessor<U[]>;
|
|
24
18
|
/**
|
|
25
19
|
* Key-function map. Like `mapArray` but reuse is keyed by `keyFn(item)` (so the
|
|
26
20
|
* same key survives object recreation), and the map fn receives a *live* item
|
|
27
|
-
* accessor
|
|
28
|
-
*
|
|
21
|
+
* accessor: a per-entry signal the reconciliation sets, so a new object under
|
|
22
|
+
* the same key propagates without the whole list being re-read per row.
|
|
23
|
+
* Backs `<For by={...}>`.
|
|
29
24
|
*/
|
|
30
25
|
export declare function keyArray<T, U>(list: ListSource<T>, keyFn: (item: T) => any, mapFn: (item: Accessor<T>, index: Accessor<number>) => U, options?: ListOptions): Accessor<U[]>;
|
|
31
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/lib/signal/list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/lib/signal/list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,QAAQ,EAAyE,MAAM,mBAAmB,CAAC;AAEzH,KAAK,UAAU,CAAC,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,CAAC,CAAC;AAEvE,UAAU,WAAW;IACnB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC;CACtB;AA4BD,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAC3B,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EACnB,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EAC9C,OAAO,CAAC,EAAE,WAAW,GACpB,QAAQ,CAAC,CAAC,EAAE,CAAC,CA6Cf;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAC3B,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EACnB,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,GAAG,EACvB,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,EACxD,OAAO,CAAC,EAAE,WAAW,GACpB,QAAQ,CAAC,CAAC,EAAE,CAAC,CAuDf;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAC7B,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EACnB,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,CAAC,EAC9C,OAAO,CAAC,EAAE,WAAW,GACpB,QAAQ,CAAC,CAAC,EAAE,CAAC,CAuCf"}
|
package/dist/lib/signal/list.js
CHANGED
|
@@ -15,6 +15,27 @@ import { createMemo, createRoot, createSignal, onCleanup, untrack } from './grap
|
|
|
15
15
|
* values are disposed. The map fn receives the plain value (a snapshot) and a
|
|
16
16
|
* reactive index accessor. Backs `<For>`.
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* A row index that builds its signal on first read. Most rows never read
|
|
20
|
+
* their index, and an unread index signal is a State plus its closure per row.
|
|
21
|
+
* `set` keeps the plain number current until (and unless) that read happens.
|
|
22
|
+
*/
|
|
23
|
+
function lazyIndex(initial) {
|
|
24
|
+
let current = initial;
|
|
25
|
+
let state = null;
|
|
26
|
+
return {
|
|
27
|
+
index: () => {
|
|
28
|
+
if (!state)
|
|
29
|
+
state = createSignal(current, { name: 'row index' });
|
|
30
|
+
return state[0]();
|
|
31
|
+
},
|
|
32
|
+
set: (n) => {
|
|
33
|
+
current = n;
|
|
34
|
+
if (state)
|
|
35
|
+
state[1](n);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
18
39
|
export function mapArray(list, mapFn, options) {
|
|
19
40
|
let items = [];
|
|
20
41
|
onCleanup(() => { for (const e of items)
|
|
@@ -46,9 +67,9 @@ export function mapArray(list, mapFn, options) {
|
|
|
46
67
|
let rendered, setIndex, dispose;
|
|
47
68
|
createRoot((d) => {
|
|
48
69
|
dispose = d;
|
|
49
|
-
const
|
|
50
|
-
setIndex =
|
|
51
|
-
rendered = mapFn(value, index);
|
|
70
|
+
const idx = lazyIndex(i);
|
|
71
|
+
setIndex = idx.set;
|
|
72
|
+
rendered = mapFn(value, idx.index);
|
|
52
73
|
});
|
|
53
74
|
const entry = { item: value, rendered, dispose, setIndex };
|
|
54
75
|
result.push(entry);
|
|
@@ -66,8 +87,9 @@ export function mapArray(list, mapFn, options) {
|
|
|
66
87
|
/**
|
|
67
88
|
* Key-function map. Like `mapArray` but reuse is keyed by `keyFn(item)` (so the
|
|
68
89
|
* same key survives object recreation), and the map fn receives a *live* item
|
|
69
|
-
* accessor
|
|
70
|
-
*
|
|
90
|
+
* accessor: a per-entry signal the reconciliation sets, so a new object under
|
|
91
|
+
* the same key propagates without the whole list being re-read per row.
|
|
92
|
+
* Backs `<For by={...}>`.
|
|
71
93
|
*/
|
|
72
94
|
export function keyArray(list, keyFn, mapFn, options) {
|
|
73
95
|
let keys = [];
|
|
@@ -95,25 +117,25 @@ export function keyArray(list, keyFn, mapFn, options) {
|
|
|
95
117
|
const reuse = prevByKey.get(key);
|
|
96
118
|
if (reuse && !usedKeys.has(key)) {
|
|
97
119
|
reuse.setIndex(i);
|
|
120
|
+
reuse.setItem(next[i]);
|
|
98
121
|
resultEntries.push(reuse);
|
|
99
122
|
}
|
|
100
123
|
else {
|
|
101
|
-
let rendered, setIndex, dispose;
|
|
124
|
+
let rendered, setIndex, setItem, dispose;
|
|
102
125
|
createRoot((d) => {
|
|
103
126
|
dispose = d;
|
|
104
|
-
const
|
|
105
|
-
setIndex =
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
rendered = mapFn(item, index);
|
|
127
|
+
const idx = lazyIndex(i);
|
|
128
|
+
setIndex = idx.set;
|
|
129
|
+
// The entry's own signal, set during reconciliation. A memo that
|
|
130
|
+
// read the whole list made every list write recompute every row;
|
|
131
|
+
// this way only a row whose item actually changed is notified.
|
|
132
|
+
// The signal also holds the last value while the array is
|
|
133
|
+
// mid-shrink, so render fns don't read undefined before disposal.
|
|
134
|
+
const [item, writeItem] = createSignal(next[i]);
|
|
135
|
+
setItem = (v) => writeItem(() => v);
|
|
136
|
+
rendered = mapFn(item, idx.index);
|
|
115
137
|
});
|
|
116
|
-
resultEntries.push({ rendered, dispose, setIndex });
|
|
138
|
+
resultEntries.push({ rendered, dispose, setIndex, setItem });
|
|
117
139
|
}
|
|
118
140
|
resultKeys.push(key);
|
|
119
141
|
usedKeys.add(key);
|
package/dist/lib/signal/list.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/version.generated.ts
|
|
2
|
-
var VERSION = "1.0.0-alpha.
|
|
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
|
|
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
|
|
234
|
+
if (this.observers) for (const o of this.observers) o.markDirty(CHECK);
|
|
219
235
|
notifyWatchers(this);
|
|
220
236
|
}
|
|
221
237
|
}
|
|
@@ -360,13 +376,15 @@ function createSignal(value, options) {
|
|
|
360
376
|
},
|
|
361
377
|
enumerable: false
|
|
362
378
|
});
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
379
|
+
if (isObserved()) {
|
|
380
|
+
reportReactiveNode({
|
|
381
|
+
kind: "signal",
|
|
382
|
+
node: state,
|
|
383
|
+
handle: read,
|
|
384
|
+
name: options?.name,
|
|
385
|
+
set: write
|
|
386
|
+
});
|
|
387
|
+
}
|
|
370
388
|
if (isObserved()) onCleanup(() => reportReactiveDispose(state));
|
|
371
389
|
return [read, write];
|
|
372
390
|
}
|
|
@@ -390,8 +408,11 @@ function createMemo(fn, value, options) {
|
|
|
390
408
|
const read = (() => c.get());
|
|
391
409
|
Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
|
|
392
410
|
Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
|
|
393
|
-
reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
|
|
394
|
-
|
|
411
|
+
if (isObserved()) reportReactiveNode({ kind: "memo", node: c, handle: read, name: options?.name });
|
|
412
|
+
onCleanup(() => {
|
|
413
|
+
c.dispose();
|
|
414
|
+
if (isObserved()) reportReactiveDispose(c);
|
|
415
|
+
});
|
|
395
416
|
return read;
|
|
396
417
|
}
|
|
397
418
|
function untrack2(fn) {
|
|
@@ -399,6 +420,20 @@ function untrack2(fn) {
|
|
|
399
420
|
}
|
|
400
421
|
|
|
401
422
|
// src/lib/signal/list.ts
|
|
423
|
+
function lazyIndex(initial) {
|
|
424
|
+
let current = initial;
|
|
425
|
+
let state = null;
|
|
426
|
+
return {
|
|
427
|
+
index: () => {
|
|
428
|
+
if (!state) state = createSignal(current, { name: "row index" });
|
|
429
|
+
return state[0]();
|
|
430
|
+
},
|
|
431
|
+
set: (n) => {
|
|
432
|
+
current = n;
|
|
433
|
+
if (state) state[1](n);
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
402
437
|
function mapArray(list, mapFn, options) {
|
|
403
438
|
let items = [];
|
|
404
439
|
onCleanup(() => {
|
|
@@ -428,9 +463,9 @@ function mapArray(list, mapFn, options) {
|
|
|
428
463
|
let rendered, setIndex, dispose;
|
|
429
464
|
createRoot((d) => {
|
|
430
465
|
dispose = d;
|
|
431
|
-
const
|
|
432
|
-
setIndex =
|
|
433
|
-
rendered = mapFn(value, index);
|
|
466
|
+
const idx = lazyIndex(i);
|
|
467
|
+
setIndex = idx.set;
|
|
468
|
+
rendered = mapFn(value, idx.index);
|
|
434
469
|
});
|
|
435
470
|
const entry = { item: value, rendered, dispose, setIndex };
|
|
436
471
|
result.push(entry);
|
|
@@ -470,21 +505,19 @@ function keyArray(list, keyFn, mapFn, options) {
|
|
|
470
505
|
const reuse = prevByKey.get(key);
|
|
471
506
|
if (reuse && !usedKeys.has(key)) {
|
|
472
507
|
reuse.setIndex(i);
|
|
508
|
+
reuse.setItem(next[i]);
|
|
473
509
|
resultEntries.push(reuse);
|
|
474
510
|
} else {
|
|
475
|
-
let rendered, setIndex, dispose;
|
|
511
|
+
let rendered, setIndex, setItem, dispose;
|
|
476
512
|
createRoot((d) => {
|
|
477
513
|
dispose = d;
|
|
478
|
-
const
|
|
479
|
-
setIndex =
|
|
480
|
-
const item =
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
return v !== void 0 && v !== null ? v : prev;
|
|
484
|
-
}, next[i]);
|
|
485
|
-
rendered = mapFn(item, index);
|
|
514
|
+
const idx = lazyIndex(i);
|
|
515
|
+
setIndex = idx.set;
|
|
516
|
+
const [item, writeItem] = createSignal(next[i]);
|
|
517
|
+
setItem = (v) => writeItem(() => v);
|
|
518
|
+
rendered = mapFn(item, idx.index);
|
|
486
519
|
});
|
|
487
|
-
resultEntries.push({ rendered, dispose, setIndex });
|
|
520
|
+
resultEntries.push({ rendered, dispose, setIndex, setItem });
|
|
488
521
|
}
|
|
489
522
|
resultKeys.push(key);
|
|
490
523
|
usedKeys.add(key);
|