@rlabs-inc/signals 1.2.1 → 1.4.0
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/README.md +114 -0
- package/dist/collections/map.d.ts.map +1 -1
- package/dist/collections/set.d.ts.map +1 -1
- package/dist/core/constants.d.ts +6 -0
- package/dist/core/constants.d.ts.map +1 -1
- package/dist/deep/proxy.d.ts.map +1 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +504 -61
- package/dist/index.mjs +504 -61
- package/dist/primitives/bind.d.ts.map +1 -1
- package/dist/primitives/derived.d.ts.map +1 -1
- package/dist/primitives/effect.d.ts.map +1 -1
- package/dist/primitives/linked.d.ts +73 -0
- package/dist/primitives/linked.d.ts.map +1 -0
- package/dist/primitives/scope.d.ts +96 -0
- package/dist/primitives/scope.d.ts.map +1 -0
- package/dist/primitives/selector.d.ts +46 -0
- package/dist/primitives/selector.d.ts.map +1 -0
- package/dist/primitives/signal.d.ts +5 -0
- package/dist/primitives/signal.d.ts.map +1 -1
- package/dist/reactivity/scheduling.d.ts.map +1 -1
- package/dist/reactivity/tracking.d.ts +5 -0
- package/dist/reactivity/tracking.d.ts.map +1 -1
- package/dist/v2/bench-compare.d.ts +2 -0
- package/dist/v2/bench-compare.d.ts.map +1 -0
- package/dist/v2/bench.d.ts +5 -0
- package/dist/v2/bench.d.ts.map +1 -0
- package/dist/v2/bind.d.ts +94 -0
- package/dist/v2/bind.d.ts.map +1 -0
- package/dist/v2/collections.d.ts +133 -0
- package/dist/v2/collections.d.ts.map +1 -0
- package/dist/v2/compat-test.d.ts +2 -0
- package/dist/v2/compat-test.d.ts.map +1 -0
- package/dist/v2/debug-array.d.ts +2 -0
- package/dist/v2/debug-array.d.ts.map +1 -0
- package/dist/v2/debug-diamond.d.ts +2 -0
- package/dist/v2/debug-diamond.d.ts.map +1 -0
- package/dist/v2/index.d.ts +7 -0
- package/dist/v2/index.d.ts.map +1 -0
- package/dist/v2/primitives.d.ts +120 -0
- package/dist/v2/primitives.d.ts.map +1 -0
- package/dist/v2/proxy.d.ts +10 -0
- package/dist/v2/proxy.d.ts.map +1 -0
- package/dist/v2/registry.d.ts +35 -0
- package/dist/v2/registry.d.ts.map +1 -0
- package/dist/v2/stress.d.ts +7 -0
- package/dist/v2/stress.d.ts.map +1 -0
- package/dist/v2/test-suite.d.ts +2 -0
- package/dist/v2/test-suite.d.ts.map +1 -0
- package/dist/v2/test-v1.d.ts +2 -0
- package/dist/v2/test-v1.d.ts.map +1 -0
- package/package.json +7 -1
package/dist/index.mjs
CHANGED
|
@@ -63,6 +63,9 @@ var UNINITIALIZED = Symbol.for("rlabs.signals.uninitialized");
|
|
|
63
63
|
var STALE_REACTION = Symbol.for("rlabs.signals.stale_reaction");
|
|
64
64
|
var STATE_SYMBOL = Symbol.for("rlabs.signals.state");
|
|
65
65
|
var REACTIVE_MARKER = Symbol.for("rlabs.signals.reactive");
|
|
66
|
+
var BINDING_SYMBOL = Symbol.for("rlabs.signals.binding");
|
|
67
|
+
var LINKED_SYMBOL = Symbol.for("rlabs.signals.linked");
|
|
68
|
+
var SOURCE = 1 << 0;
|
|
66
69
|
var STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN);
|
|
67
70
|
|
|
68
71
|
// src/core/globals.ts
|
|
@@ -187,6 +190,9 @@ function setUpdateEffectImpl(impl) {
|
|
|
187
190
|
function flushEffects() {
|
|
188
191
|
const roots = clearQueuedRootEffects();
|
|
189
192
|
for (const root of roots) {
|
|
193
|
+
if ((root.f & INERT) !== 0) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
190
196
|
if (isDirty(root)) {
|
|
191
197
|
updateEffectImpl(root);
|
|
192
198
|
}
|
|
@@ -197,7 +203,7 @@ function processEffectTree(effect) {
|
|
|
197
203
|
let child = effect.first;
|
|
198
204
|
while (child !== null) {
|
|
199
205
|
const next = child.next;
|
|
200
|
-
if (isDirty(child)) {
|
|
206
|
+
if ((child.f & INERT) === 0 && isDirty(child)) {
|
|
201
207
|
updateEffectImpl(child);
|
|
202
208
|
}
|
|
203
209
|
if (child.first !== null) {
|
|
@@ -210,6 +216,9 @@ function flushPendingReactions() {
|
|
|
210
216
|
const reactions = [...pendingReactions];
|
|
211
217
|
clearPendingReactions();
|
|
212
218
|
for (const reaction of reactions) {
|
|
219
|
+
if ((reaction.f & INERT) !== 0) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
213
222
|
if (isDirty(reaction)) {
|
|
214
223
|
if ("parent" in reaction) {
|
|
215
224
|
updateEffectImpl(reaction);
|
|
@@ -241,6 +250,9 @@ function flushSync(fn) {
|
|
|
241
250
|
continue;
|
|
242
251
|
}
|
|
243
252
|
for (const root of roots) {
|
|
253
|
+
if ((root.f & INERT) !== 0) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
244
256
|
if (isDirty(root)) {
|
|
245
257
|
updateEffectImpl(root);
|
|
246
258
|
}
|
|
@@ -287,13 +299,63 @@ function get(signal) {
|
|
|
287
299
|
}
|
|
288
300
|
}
|
|
289
301
|
if ((signal.f & DERIVED) !== 0) {
|
|
290
|
-
|
|
291
|
-
if (isDirty(derived)) {
|
|
292
|
-
updateDerived(derived);
|
|
293
|
-
}
|
|
302
|
+
updateDerivedChain(signal);
|
|
294
303
|
}
|
|
295
304
|
return signal.v;
|
|
296
305
|
}
|
|
306
|
+
var updateCycleId = 0;
|
|
307
|
+
var processedInCycle = new WeakMap;
|
|
308
|
+
function updateDerivedChain(target) {
|
|
309
|
+
if ((target.f & (DIRTY | MAYBE_DIRTY)) === 0) {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
const cycleId = ++updateCycleId;
|
|
313
|
+
const chain = [target];
|
|
314
|
+
processedInCycle.set(target, cycleId);
|
|
315
|
+
let idx = 0;
|
|
316
|
+
while (idx < chain.length) {
|
|
317
|
+
const current = chain[idx];
|
|
318
|
+
idx++;
|
|
319
|
+
if ((current.f & (DIRTY | MAYBE_DIRTY)) === 0) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
const deps = current.deps;
|
|
323
|
+
if (deps !== null) {
|
|
324
|
+
for (let i = 0;i < deps.length; i++) {
|
|
325
|
+
const dep = deps[i];
|
|
326
|
+
if ((dep.f & DERIVED) !== 0 && (dep.f & (DIRTY | MAYBE_DIRTY)) !== 0 && processedInCycle.get(dep) !== cycleId) {
|
|
327
|
+
chain.push(dep);
|
|
328
|
+
processedInCycle.set(dep, cycleId);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (let i = chain.length - 1;i >= 0; i--) {
|
|
334
|
+
const current = chain[i];
|
|
335
|
+
if ((current.f & (DIRTY | MAYBE_DIRTY)) === 0) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if ((current.f & DIRTY) !== 0) {
|
|
339
|
+
updateDerived(current);
|
|
340
|
+
} else {
|
|
341
|
+
const deps = current.deps;
|
|
342
|
+
let needsUpdate = false;
|
|
343
|
+
if (deps !== null) {
|
|
344
|
+
for (let j = 0;j < deps.length; j++) {
|
|
345
|
+
if (deps[j].wv > current.wv) {
|
|
346
|
+
needsUpdate = true;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (needsUpdate) {
|
|
352
|
+
updateDerived(current);
|
|
353
|
+
} else {
|
|
354
|
+
setSignalStatus(current, CLEAN);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
297
359
|
function set(signal, value) {
|
|
298
360
|
if (activeReaction !== null && (activeReaction.f & DERIVED) !== 0) {
|
|
299
361
|
throw new Error("Cannot write to signals inside a derived. " + "Deriveds should be pure computations with no side effects.");
|
|
@@ -309,20 +371,24 @@ function set(signal, value) {
|
|
|
309
371
|
return value;
|
|
310
372
|
}
|
|
311
373
|
function markReactions(signal, status) {
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
374
|
+
const stack = [{ signal, status }];
|
|
375
|
+
while (stack.length > 0) {
|
|
376
|
+
const { signal: currentSignal, status: currentStatus } = stack.pop();
|
|
377
|
+
const reactions = currentSignal.reactions;
|
|
378
|
+
if (reactions === null)
|
|
379
|
+
continue;
|
|
380
|
+
for (let i = 0;i < reactions.length; i++) {
|
|
381
|
+
const reaction = reactions[i];
|
|
382
|
+
const flags = reaction.f;
|
|
383
|
+
const notDirty = (flags & DIRTY) === 0;
|
|
384
|
+
if (notDirty) {
|
|
385
|
+
setSignalStatus(reaction, currentStatus);
|
|
386
|
+
}
|
|
387
|
+
if ((flags & DERIVED) !== 0) {
|
|
388
|
+
stack.push({ signal: reaction, status: MAYBE_DIRTY });
|
|
389
|
+
} else if (notDirty) {
|
|
390
|
+
scheduleEffect(reaction);
|
|
391
|
+
}
|
|
326
392
|
}
|
|
327
393
|
}
|
|
328
394
|
}
|
|
@@ -333,22 +399,66 @@ function isDirty(reaction) {
|
|
|
333
399
|
if ((reaction.f & DIRTY) !== 0) {
|
|
334
400
|
return true;
|
|
335
401
|
}
|
|
336
|
-
if ((reaction.f & MAYBE_DIRTY)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
402
|
+
if ((reaction.f & MAYBE_DIRTY) === 0) {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
const toCheck = [reaction];
|
|
406
|
+
const toUpdate = [];
|
|
407
|
+
let idx = 0;
|
|
408
|
+
while (idx < toCheck.length) {
|
|
409
|
+
const current = toCheck[idx];
|
|
410
|
+
idx++;
|
|
411
|
+
if ((current.f & DIRTY) !== 0) {
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if ((current.f & MAYBE_DIRTY) === 0) {
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
const deps2 = current.deps;
|
|
418
|
+
if (deps2 !== null) {
|
|
419
|
+
for (let i = 0;i < deps2.length; i++) {
|
|
420
|
+
const dep = deps2[i];
|
|
421
|
+
if ((dep.f & DERIVED) !== 0 && (dep.f & (DIRTY | MAYBE_DIRTY)) !== 0) {
|
|
422
|
+
toCheck.push(dep);
|
|
345
423
|
}
|
|
346
|
-
|
|
347
|
-
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
for (let i = toCheck.length - 1;i >= 0; i--) {
|
|
428
|
+
const current = toCheck[i];
|
|
429
|
+
if ((current.f & DERIVED) === 0) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if ((current.f & DIRTY) !== 0) {
|
|
433
|
+
updateDerived(current);
|
|
434
|
+
} else if ((current.f & MAYBE_DIRTY) !== 0) {
|
|
435
|
+
const deps2 = current.deps;
|
|
436
|
+
let needsUpdate = false;
|
|
437
|
+
if (deps2 !== null) {
|
|
438
|
+
for (let j = 0;j < deps2.length; j++) {
|
|
439
|
+
if (deps2[j].wv > current.wv) {
|
|
440
|
+
needsUpdate = true;
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
348
443
|
}
|
|
349
444
|
}
|
|
445
|
+
if (needsUpdate) {
|
|
446
|
+
updateDerived(current);
|
|
447
|
+
} else {
|
|
448
|
+
setSignalStatus(current, CLEAN);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if ((reaction.f & DIRTY) !== 0) {
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
const deps = reaction.deps;
|
|
456
|
+
if (deps !== null) {
|
|
457
|
+
for (let i = 0;i < deps.length; i++) {
|
|
458
|
+
if (deps[i].wv > reaction.wv) {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
350
461
|
}
|
|
351
|
-
setSignalStatus(reaction, CLEAN);
|
|
352
462
|
}
|
|
353
463
|
return false;
|
|
354
464
|
}
|
|
@@ -452,6 +562,7 @@ function updateReaction(reaction) {
|
|
|
452
562
|
}
|
|
453
563
|
|
|
454
564
|
// src/primitives/signal.ts
|
|
565
|
+
var SOURCE_SYMBOL = Symbol("signal.source");
|
|
455
566
|
function source(initialValue, options) {
|
|
456
567
|
return {
|
|
457
568
|
f: 0,
|
|
@@ -465,9 +576,13 @@ function source(initialValue, options) {
|
|
|
465
576
|
function mutableSource(initialValue) {
|
|
466
577
|
return source(initialValue, { equals: safeEquals });
|
|
467
578
|
}
|
|
579
|
+
var signalCleanup = new FinalizationRegistry((src) => {
|
|
580
|
+
src.reactions = null;
|
|
581
|
+
});
|
|
468
582
|
function signal(initialValue, options) {
|
|
469
583
|
const src = source(initialValue, options);
|
|
470
|
-
|
|
584
|
+
const sig = {
|
|
585
|
+
[SOURCE_SYMBOL]: src,
|
|
471
586
|
get value() {
|
|
472
587
|
return get(src);
|
|
473
588
|
},
|
|
@@ -475,6 +590,11 @@ function signal(initialValue, options) {
|
|
|
475
590
|
set(src, newValue);
|
|
476
591
|
}
|
|
477
592
|
};
|
|
593
|
+
signalCleanup.register(sig, src);
|
|
594
|
+
return sig;
|
|
595
|
+
}
|
|
596
|
+
function getSource(sig) {
|
|
597
|
+
return sig[SOURCE_SYMBOL];
|
|
478
598
|
}
|
|
479
599
|
var proxyFn = null;
|
|
480
600
|
function setProxyFn(fn) {
|
|
@@ -491,10 +611,21 @@ function stateRaw(initialValue) {
|
|
|
491
611
|
}
|
|
492
612
|
|
|
493
613
|
// src/deep/proxy.ts
|
|
614
|
+
var proxyCache = new WeakMap;
|
|
615
|
+
var proxyCleanup = new FinalizationRegistry((data) => {
|
|
616
|
+
data.version.reactions = null;
|
|
617
|
+
for (const src of data.sources.values()) {
|
|
618
|
+
src.reactions = null;
|
|
619
|
+
}
|
|
620
|
+
data.sources.clear();
|
|
621
|
+
});
|
|
494
622
|
function shouldProxy(value) {
|
|
495
623
|
if (value === null || typeof value !== "object") {
|
|
496
624
|
return false;
|
|
497
625
|
}
|
|
626
|
+
if (BINDING_SYMBOL in value) {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
498
629
|
const proto = Object.getPrototypeOf(value);
|
|
499
630
|
return proto === Object.prototype || proto === Array.prototype || proto === null;
|
|
500
631
|
}
|
|
@@ -509,6 +640,10 @@ function proxy(value) {
|
|
|
509
640
|
if (!shouldProxy(value) || isProxy(value)) {
|
|
510
641
|
return value;
|
|
511
642
|
}
|
|
643
|
+
const cached = proxyCache.get(value);
|
|
644
|
+
if (cached) {
|
|
645
|
+
return cached;
|
|
646
|
+
}
|
|
512
647
|
const sources = new Map;
|
|
513
648
|
const version = source(0);
|
|
514
649
|
const isArray = Array.isArray(value);
|
|
@@ -528,7 +663,7 @@ function proxy(value) {
|
|
|
528
663
|
setActiveReaction(prevReaction);
|
|
529
664
|
}
|
|
530
665
|
};
|
|
531
|
-
const
|
|
666
|
+
const getSource2 = (prop, initialValue) => {
|
|
532
667
|
let s = sources.get(prop);
|
|
533
668
|
if (s === undefined) {
|
|
534
669
|
s = withParent(() => {
|
|
@@ -551,7 +686,7 @@ function proxy(value) {
|
|
|
551
686
|
return currentValue.bind(proxyObj);
|
|
552
687
|
}
|
|
553
688
|
if (exists || isWritable(target, prop)) {
|
|
554
|
-
const s =
|
|
689
|
+
const s = getSource2(prop, currentValue);
|
|
555
690
|
const val = get(s);
|
|
556
691
|
if (val === UNINITIALIZED) {
|
|
557
692
|
return;
|
|
@@ -560,7 +695,7 @@ function proxy(value) {
|
|
|
560
695
|
}
|
|
561
696
|
return currentValue;
|
|
562
697
|
},
|
|
563
|
-
set(target, prop, newValue
|
|
698
|
+
set(target, prop, newValue) {
|
|
564
699
|
const exists = prop in target;
|
|
565
700
|
let s = sources.get(prop);
|
|
566
701
|
if (s === undefined) {
|
|
@@ -570,29 +705,35 @@ function proxy(value) {
|
|
|
570
705
|
s = withParent(() => source(undefined));
|
|
571
706
|
sources.set(prop, s);
|
|
572
707
|
}
|
|
573
|
-
|
|
708
|
+
let proxied;
|
|
709
|
+
if (shouldProxy(newValue)) {
|
|
710
|
+
proxied = withParent(() => proxy(newValue));
|
|
711
|
+
} else {
|
|
712
|
+
proxied = newValue;
|
|
713
|
+
}
|
|
574
714
|
set(s, proxied);
|
|
575
|
-
|
|
576
|
-
if (isArray
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
715
|
+
target[prop] = newValue;
|
|
716
|
+
if (isArray) {
|
|
717
|
+
if (prop === "length") {
|
|
718
|
+
const oldLength = s.v;
|
|
719
|
+
const newLength = newValue;
|
|
720
|
+
for (let i = newLength;i < oldLength; i++) {
|
|
721
|
+
const indexKey = String(i);
|
|
722
|
+
const indexSource = sources.get(indexKey);
|
|
723
|
+
if (indexSource !== undefined) {
|
|
724
|
+
set(indexSource, UNINITIALIZED);
|
|
725
|
+
} else if (i in target) {
|
|
726
|
+
const deletedSource = withParent(() => source(UNINITIALIZED));
|
|
727
|
+
sources.set(indexKey, deletedSource);
|
|
728
|
+
}
|
|
587
729
|
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
set(lengthSource, index + 1);
|
|
730
|
+
} else if (typeof prop === "string") {
|
|
731
|
+
const index = Number(prop);
|
|
732
|
+
if (Number.isInteger(index) && index >= 0) {
|
|
733
|
+
const lengthSource = sources.get("length");
|
|
734
|
+
if (lengthSource !== undefined && index >= lengthSource.v) {
|
|
735
|
+
set(lengthSource, index + 1);
|
|
736
|
+
}
|
|
596
737
|
}
|
|
597
738
|
}
|
|
598
739
|
}
|
|
@@ -613,7 +754,7 @@ function proxy(value) {
|
|
|
613
754
|
}
|
|
614
755
|
set(version, get(version) + 1);
|
|
615
756
|
}
|
|
616
|
-
return
|
|
757
|
+
return delete target[prop];
|
|
617
758
|
},
|
|
618
759
|
has(target, prop) {
|
|
619
760
|
if (prop === STATE_SYMBOL) {
|
|
@@ -651,6 +792,8 @@ function proxy(value) {
|
|
|
651
792
|
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
652
793
|
}
|
|
653
794
|
});
|
|
795
|
+
proxyCleanup.register(proxyObj, { sources, version });
|
|
796
|
+
proxyCache.set(value, proxyObj);
|
|
654
797
|
return proxyObj;
|
|
655
798
|
}
|
|
656
799
|
function toRaw(value) {
|
|
@@ -663,6 +806,18 @@ function isReactive(value) {
|
|
|
663
806
|
return isProxy(value);
|
|
664
807
|
}
|
|
665
808
|
// src/primitives/derived.ts
|
|
809
|
+
var derivedCleanup = new FinalizationRegistry((d) => {
|
|
810
|
+
removeReactions(d, 0);
|
|
811
|
+
d.deps = null;
|
|
812
|
+
d.reactions = null;
|
|
813
|
+
const effects = d.effects;
|
|
814
|
+
if (effects !== null) {
|
|
815
|
+
d.effects = null;
|
|
816
|
+
for (let i = 0;i < effects.length; i++) {
|
|
817
|
+
destroyEffectImpl(effects[i]);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
});
|
|
666
821
|
function createDerived(fn, options) {
|
|
667
822
|
let flags = DERIVED | DIRTY;
|
|
668
823
|
const parentDerived = activeReaction !== null && (activeReaction.f & DERIVED) !== 0 ? activeReaction : null;
|
|
@@ -713,11 +868,13 @@ function destroyDerivedEffects(derived) {
|
|
|
713
868
|
}
|
|
714
869
|
function derived(fn, options) {
|
|
715
870
|
const d = createDerived(fn, options);
|
|
716
|
-
|
|
871
|
+
const wrapper = {
|
|
717
872
|
get value() {
|
|
718
873
|
return get(d);
|
|
719
874
|
}
|
|
720
875
|
};
|
|
876
|
+
derivedCleanup.register(wrapper, d);
|
|
877
|
+
return wrapper;
|
|
721
878
|
}
|
|
722
879
|
derived.by = derived;
|
|
723
880
|
function disconnectDerived(derived2) {
|
|
@@ -726,6 +883,123 @@ function disconnectDerived(derived2) {
|
|
|
726
883
|
derived2.reactions = null;
|
|
727
884
|
destroyDerivedEffects(derived2);
|
|
728
885
|
}
|
|
886
|
+
// src/primitives/scope.ts
|
|
887
|
+
var activeScope = null;
|
|
888
|
+
function getCurrentScope() {
|
|
889
|
+
return activeScope;
|
|
890
|
+
}
|
|
891
|
+
function setActiveScope(scope) {
|
|
892
|
+
const prev = activeScope;
|
|
893
|
+
activeScope = scope;
|
|
894
|
+
return prev;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
class EffectScopeImpl {
|
|
898
|
+
_active = true;
|
|
899
|
+
_paused = false;
|
|
900
|
+
effects = [];
|
|
901
|
+
cleanups = [];
|
|
902
|
+
parent = null;
|
|
903
|
+
scopes = null;
|
|
904
|
+
constructor(detached = false) {
|
|
905
|
+
this.parent = activeScope;
|
|
906
|
+
if (!detached && this.parent) {
|
|
907
|
+
if (this.parent.scopes === null) {
|
|
908
|
+
this.parent.scopes = [];
|
|
909
|
+
}
|
|
910
|
+
this.parent.scopes.push(this);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
get active() {
|
|
914
|
+
return this._active;
|
|
915
|
+
}
|
|
916
|
+
get paused() {
|
|
917
|
+
return this._paused;
|
|
918
|
+
}
|
|
919
|
+
run(fn) {
|
|
920
|
+
if (!this._active) {
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
const prevScope = setActiveScope(this);
|
|
924
|
+
try {
|
|
925
|
+
return fn();
|
|
926
|
+
} finally {
|
|
927
|
+
setActiveScope(prevScope);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
stop() {
|
|
931
|
+
if (!this._active)
|
|
932
|
+
return;
|
|
933
|
+
for (const effect of [...this.effects]) {
|
|
934
|
+
destroyEffect(effect);
|
|
935
|
+
}
|
|
936
|
+
this.effects.length = 0;
|
|
937
|
+
for (const cleanup of this.cleanups) {
|
|
938
|
+
try {
|
|
939
|
+
cleanup();
|
|
940
|
+
} catch {}
|
|
941
|
+
}
|
|
942
|
+
this.cleanups.length = 0;
|
|
943
|
+
if (this.scopes) {
|
|
944
|
+
for (const scope of this.scopes) {
|
|
945
|
+
scope.stop();
|
|
946
|
+
}
|
|
947
|
+
this.scopes.length = 0;
|
|
948
|
+
}
|
|
949
|
+
if (this.parent?.scopes) {
|
|
950
|
+
const idx = this.parent.scopes.indexOf(this);
|
|
951
|
+
if (idx !== -1) {
|
|
952
|
+
this.parent.scopes.splice(idx, 1);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
this._active = false;
|
|
956
|
+
}
|
|
957
|
+
pause() {
|
|
958
|
+
if (!this._active || this._paused)
|
|
959
|
+
return;
|
|
960
|
+
this._paused = true;
|
|
961
|
+
for (const effect of this.effects) {
|
|
962
|
+
effect.f |= INERT;
|
|
963
|
+
}
|
|
964
|
+
if (this.scopes) {
|
|
965
|
+
for (const scope of this.scopes) {
|
|
966
|
+
scope.pause();
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
resume() {
|
|
971
|
+
if (!this._active || !this._paused)
|
|
972
|
+
return;
|
|
973
|
+
this._paused = false;
|
|
974
|
+
for (const effect of this.effects) {
|
|
975
|
+
effect.f &= ~INERT;
|
|
976
|
+
if ((effect.f & DIRTY) !== 0) {
|
|
977
|
+
scheduleEffect(effect);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
if (this.scopes) {
|
|
981
|
+
for (const scope of this.scopes) {
|
|
982
|
+
scope.resume();
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
function effectScope(detached) {
|
|
988
|
+
return new EffectScopeImpl(detached);
|
|
989
|
+
}
|
|
990
|
+
function onScopeDispose(fn) {
|
|
991
|
+
if (activeScope) {
|
|
992
|
+
activeScope.cleanups.push(fn);
|
|
993
|
+
} else {
|
|
994
|
+
console.warn("onScopeDispose() called outside of scope context");
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
function registerEffectWithScope(effect) {
|
|
998
|
+
if (activeScope) {
|
|
999
|
+
activeScope.effects.push(effect);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
729
1003
|
// src/primitives/effect.ts
|
|
730
1004
|
function createEffect(type, fn, sync, push = true) {
|
|
731
1005
|
const parent = activeEffect;
|
|
@@ -741,6 +1015,7 @@ function createEffect(type, fn, sync, push = true) {
|
|
|
741
1015
|
next: null,
|
|
742
1016
|
wv: 0
|
|
743
1017
|
};
|
|
1018
|
+
registerEffectWithScope(effect);
|
|
744
1019
|
if (sync) {
|
|
745
1020
|
updateEffect(effect);
|
|
746
1021
|
effect.f |= EFFECT_RAN;
|
|
@@ -850,7 +1125,9 @@ effect.tracking = function effectTracking() {
|
|
|
850
1125
|
return activeEffect !== null;
|
|
851
1126
|
};
|
|
852
1127
|
// src/primitives/bind.ts
|
|
853
|
-
var
|
|
1128
|
+
var bindingCleanup = new FinalizationRegistry((internalSource) => {
|
|
1129
|
+
internalSource.reactions = null;
|
|
1130
|
+
});
|
|
854
1131
|
function isBinding(value) {
|
|
855
1132
|
return value !== null && typeof value === "object" && BINDING_SYMBOL in value;
|
|
856
1133
|
}
|
|
@@ -870,10 +1147,13 @@ function bind(source2) {
|
|
|
870
1147
|
};
|
|
871
1148
|
}
|
|
872
1149
|
let actualSource;
|
|
1150
|
+
let internalSource = null;
|
|
873
1151
|
if (isBinding(source2) || isSignal(source2)) {
|
|
874
1152
|
actualSource = source2;
|
|
875
1153
|
} else {
|
|
876
|
-
|
|
1154
|
+
const sig = signal(source2);
|
|
1155
|
+
actualSource = sig;
|
|
1156
|
+
internalSource = getSource(sig) ?? null;
|
|
877
1157
|
}
|
|
878
1158
|
const binding = {
|
|
879
1159
|
[BINDING_SYMBOL]: true,
|
|
@@ -884,6 +1164,9 @@ function bind(source2) {
|
|
|
884
1164
|
actualSource.value = v;
|
|
885
1165
|
}
|
|
886
1166
|
};
|
|
1167
|
+
if (internalSource !== null) {
|
|
1168
|
+
bindingCleanup.register(binding, internalSource);
|
|
1169
|
+
}
|
|
887
1170
|
return binding;
|
|
888
1171
|
}
|
|
889
1172
|
function bindReadonly(source2) {
|
|
@@ -930,7 +1213,140 @@ function untrack(fn) {
|
|
|
930
1213
|
}
|
|
931
1214
|
}
|
|
932
1215
|
var peek = untrack;
|
|
1216
|
+
|
|
1217
|
+
// src/primitives/linked.ts
|
|
1218
|
+
function linkedSignal(config) {
|
|
1219
|
+
let sourceFn;
|
|
1220
|
+
let computation;
|
|
1221
|
+
let equalsFn = Object.is;
|
|
1222
|
+
if (typeof config === "function") {
|
|
1223
|
+
const fn = config;
|
|
1224
|
+
sourceFn = fn;
|
|
1225
|
+
computation = (s) => s;
|
|
1226
|
+
} else {
|
|
1227
|
+
sourceFn = config.source;
|
|
1228
|
+
computation = config.computation;
|
|
1229
|
+
if (config.equal) {
|
|
1230
|
+
equalsFn = config.equal;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
let prevSource = undefined;
|
|
1234
|
+
let prevValue = undefined;
|
|
1235
|
+
let initialized = false;
|
|
1236
|
+
const valueSignal = signal(undefined, { equals: equalsFn });
|
|
1237
|
+
const sourceTracker = derived(() => {
|
|
1238
|
+
const newSource = sourceFn();
|
|
1239
|
+
return newSource;
|
|
1240
|
+
});
|
|
1241
|
+
let manualOverride = false;
|
|
1242
|
+
let lastKnownSource = undefined;
|
|
1243
|
+
const dispose = effect.pre(() => {
|
|
1244
|
+
const currentSource = sourceTracker.value;
|
|
1245
|
+
const sourceChanged = initialized && !Object.is(lastKnownSource, currentSource);
|
|
1246
|
+
if (!initialized || sourceChanged) {
|
|
1247
|
+
const previous = initialized ? { source: prevSource, value: prevValue } : undefined;
|
|
1248
|
+
const newValue = computation(currentSource, previous);
|
|
1249
|
+
prevSource = currentSource;
|
|
1250
|
+
prevValue = newValue;
|
|
1251
|
+
lastKnownSource = currentSource;
|
|
1252
|
+
initialized = true;
|
|
1253
|
+
manualOverride = false;
|
|
1254
|
+
untrack(() => {
|
|
1255
|
+
valueSignal.value = newValue;
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
const accessor = {
|
|
1260
|
+
[LINKED_SYMBOL]: true,
|
|
1261
|
+
get value() {
|
|
1262
|
+
sourceTracker.value;
|
|
1263
|
+
const currentSource = untrack(() => sourceTracker.value);
|
|
1264
|
+
if (initialized && !Object.is(lastKnownSource, currentSource)) {
|
|
1265
|
+
flushSync();
|
|
1266
|
+
}
|
|
1267
|
+
return valueSignal.value;
|
|
1268
|
+
},
|
|
1269
|
+
set value(newValue) {
|
|
1270
|
+
manualOverride = true;
|
|
1271
|
+
prevValue = newValue;
|
|
1272
|
+
valueSignal.value = newValue;
|
|
1273
|
+
},
|
|
1274
|
+
get peek() {
|
|
1275
|
+
return untrack(() => valueSignal.value);
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
return accessor;
|
|
1279
|
+
}
|
|
1280
|
+
function isLinkedSignal(value) {
|
|
1281
|
+
return value !== null && typeof value === "object" && LINKED_SYMBOL in value;
|
|
1282
|
+
}
|
|
1283
|
+
// src/primitives/selector.ts
|
|
1284
|
+
function createSelector(source2, fn = (k, v) => k === v) {
|
|
1285
|
+
const subscribers = new Map;
|
|
1286
|
+
const reactionKeys = new WeakMap;
|
|
1287
|
+
let prevValue;
|
|
1288
|
+
let initialized = false;
|
|
1289
|
+
effect.pre(() => {
|
|
1290
|
+
const value = source2();
|
|
1291
|
+
if (initialized && !Object.is(prevValue, value)) {
|
|
1292
|
+
for (const [key, reactions] of subscribers) {
|
|
1293
|
+
const wasSelected = fn(key, prevValue);
|
|
1294
|
+
const isSelected = fn(key, value);
|
|
1295
|
+
if (wasSelected !== isSelected) {
|
|
1296
|
+
const toRemove = [];
|
|
1297
|
+
for (const reaction of reactions) {
|
|
1298
|
+
if ((reaction.f & DESTROYED) !== 0) {
|
|
1299
|
+
toRemove.push(reaction);
|
|
1300
|
+
continue;
|
|
1301
|
+
}
|
|
1302
|
+
setSignalStatus(reaction, DIRTY);
|
|
1303
|
+
if ((reaction.f & EFFECT) !== 0 && (reaction.f & DERIVED) === 0) {
|
|
1304
|
+
scheduleEffect(reaction);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
for (const r of toRemove) {
|
|
1308
|
+
reactions.delete(r);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
prevValue = value;
|
|
1314
|
+
initialized = true;
|
|
1315
|
+
});
|
|
1316
|
+
return (key) => {
|
|
1317
|
+
const currentValue = prevValue;
|
|
1318
|
+
if (activeReaction !== null) {
|
|
1319
|
+
const reaction = activeReaction;
|
|
1320
|
+
if ((reaction.f & DESTROYED) === 0) {
|
|
1321
|
+
let keySubscribers = subscribers.get(key);
|
|
1322
|
+
if (!keySubscribers) {
|
|
1323
|
+
keySubscribers = new Set;
|
|
1324
|
+
subscribers.set(key, keySubscribers);
|
|
1325
|
+
}
|
|
1326
|
+
if (!keySubscribers.has(reaction)) {
|
|
1327
|
+
keySubscribers.add(reaction);
|
|
1328
|
+
let keys = reactionKeys.get(reaction);
|
|
1329
|
+
if (!keys) {
|
|
1330
|
+
keys = new Set;
|
|
1331
|
+
reactionKeys.set(reaction, keys);
|
|
1332
|
+
}
|
|
1333
|
+
keys.add(key);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
return fn(key, currentValue);
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
933
1340
|
// src/collections/map.ts
|
|
1341
|
+
var mapCleanup = new FinalizationRegistry((data) => {
|
|
1342
|
+
data.version.reactions = null;
|
|
1343
|
+
data.size.reactions = null;
|
|
1344
|
+
for (const sig of data.keySignals.values()) {
|
|
1345
|
+
sig.reactions = null;
|
|
1346
|
+
}
|
|
1347
|
+
data.keySignals.clear();
|
|
1348
|
+
});
|
|
1349
|
+
|
|
934
1350
|
class ReactiveMap extends Map {
|
|
935
1351
|
#keySignals = new Map;
|
|
936
1352
|
#version = source(0);
|
|
@@ -938,6 +1354,11 @@ class ReactiveMap extends Map {
|
|
|
938
1354
|
constructor(entries) {
|
|
939
1355
|
super(entries);
|
|
940
1356
|
this.#size = source(super.size);
|
|
1357
|
+
mapCleanup.register(this, {
|
|
1358
|
+
keySignals: this.#keySignals,
|
|
1359
|
+
version: this.#version,
|
|
1360
|
+
size: this.#size
|
|
1361
|
+
});
|
|
941
1362
|
}
|
|
942
1363
|
#getKeySignal(key) {
|
|
943
1364
|
let sig = this.#keySignals.get(key);
|
|
@@ -1048,6 +1469,15 @@ class ReactiveMap extends Map {
|
|
|
1048
1469
|
}
|
|
1049
1470
|
}
|
|
1050
1471
|
// src/collections/set.ts
|
|
1472
|
+
var setCleanup = new FinalizationRegistry((data) => {
|
|
1473
|
+
data.version.reactions = null;
|
|
1474
|
+
data.size.reactions = null;
|
|
1475
|
+
for (const sig of data.itemSignals.values()) {
|
|
1476
|
+
sig.reactions = null;
|
|
1477
|
+
}
|
|
1478
|
+
data.itemSignals.clear();
|
|
1479
|
+
});
|
|
1480
|
+
|
|
1051
1481
|
class ReactiveSet extends Set {
|
|
1052
1482
|
#itemSignals = new Map;
|
|
1053
1483
|
#version = source(0);
|
|
@@ -1055,6 +1485,11 @@ class ReactiveSet extends Set {
|
|
|
1055
1485
|
constructor(values) {
|
|
1056
1486
|
super(values);
|
|
1057
1487
|
this.#size = source(super.size);
|
|
1488
|
+
setCleanup.register(this, {
|
|
1489
|
+
itemSignals: this.#itemSignals,
|
|
1490
|
+
version: this.#version,
|
|
1491
|
+
size: this.#size
|
|
1492
|
+
});
|
|
1058
1493
|
}
|
|
1059
1494
|
#getItemSignal(item) {
|
|
1060
1495
|
let sig = this.#itemSignals.get(item);
|
|
@@ -1412,10 +1847,13 @@ export {
|
|
|
1412
1847
|
readVersion,
|
|
1413
1848
|
proxy,
|
|
1414
1849
|
peek,
|
|
1850
|
+
onScopeDispose,
|
|
1415
1851
|
neverEquals,
|
|
1416
1852
|
mutableSource,
|
|
1417
1853
|
markReactions,
|
|
1854
|
+
linkedSignal,
|
|
1418
1855
|
isReactive,
|
|
1856
|
+
isLinkedSignal,
|
|
1419
1857
|
isDirty,
|
|
1420
1858
|
isBinding,
|
|
1421
1859
|
incrementWriteVersion,
|
|
@@ -1423,15 +1861,18 @@ export {
|
|
|
1423
1861
|
incrementBatchDepth,
|
|
1424
1862
|
getWriteVersion,
|
|
1425
1863
|
getReadVersion,
|
|
1864
|
+
getCurrentScope,
|
|
1426
1865
|
getBatchDepth,
|
|
1427
1866
|
get,
|
|
1428
1867
|
flushSync,
|
|
1429
1868
|
equals,
|
|
1869
|
+
effectScope,
|
|
1430
1870
|
effect,
|
|
1431
1871
|
disconnectDerived,
|
|
1432
1872
|
destroyEffect,
|
|
1433
1873
|
derived,
|
|
1434
1874
|
decrementBatchDepth,
|
|
1875
|
+
createSelector,
|
|
1435
1876
|
createEquals,
|
|
1436
1877
|
createEffect,
|
|
1437
1878
|
createDerived,
|
|
@@ -1455,6 +1896,7 @@ export {
|
|
|
1455
1896
|
REACTIVE_MARKER,
|
|
1456
1897
|
REACTION_IS_UPDATING,
|
|
1457
1898
|
MAYBE_DIRTY,
|
|
1899
|
+
LINKED_SYMBOL,
|
|
1458
1900
|
INERT,
|
|
1459
1901
|
EFFECT_RAN,
|
|
1460
1902
|
EFFECT_PRESERVED,
|
|
@@ -1465,5 +1907,6 @@ export {
|
|
|
1465
1907
|
DERIVED,
|
|
1466
1908
|
CLEAN,
|
|
1467
1909
|
BRANCH_EFFECT,
|
|
1468
|
-
BLOCK_EFFECT
|
|
1910
|
+
BLOCK_EFFECT,
|
|
1911
|
+
BINDING_SYMBOL
|
|
1469
1912
|
};
|