@rlabs-inc/signals 1.3.0 → 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.
Files changed (48) hide show
  1. package/README.md +114 -0
  2. package/dist/core/constants.d.ts +6 -0
  3. package/dist/core/constants.d.ts.map +1 -1
  4. package/dist/deep/proxy.d.ts.map +1 -1
  5. package/dist/index.d.ts +7 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +430 -56
  8. package/dist/index.mjs +430 -56
  9. package/dist/primitives/bind.d.ts.map +1 -1
  10. package/dist/primitives/effect.d.ts.map +1 -1
  11. package/dist/primitives/linked.d.ts +73 -0
  12. package/dist/primitives/linked.d.ts.map +1 -0
  13. package/dist/primitives/scope.d.ts +96 -0
  14. package/dist/primitives/scope.d.ts.map +1 -0
  15. package/dist/primitives/selector.d.ts +46 -0
  16. package/dist/primitives/selector.d.ts.map +1 -0
  17. package/dist/reactivity/scheduling.d.ts.map +1 -1
  18. package/dist/reactivity/tracking.d.ts +5 -0
  19. package/dist/reactivity/tracking.d.ts.map +1 -1
  20. package/dist/v2/bench-compare.d.ts +2 -0
  21. package/dist/v2/bench-compare.d.ts.map +1 -0
  22. package/dist/v2/bench.d.ts +5 -0
  23. package/dist/v2/bench.d.ts.map +1 -0
  24. package/dist/v2/bind.d.ts +94 -0
  25. package/dist/v2/bind.d.ts.map +1 -0
  26. package/dist/v2/collections.d.ts +133 -0
  27. package/dist/v2/collections.d.ts.map +1 -0
  28. package/dist/v2/compat-test.d.ts +2 -0
  29. package/dist/v2/compat-test.d.ts.map +1 -0
  30. package/dist/v2/debug-array.d.ts +2 -0
  31. package/dist/v2/debug-array.d.ts.map +1 -0
  32. package/dist/v2/debug-diamond.d.ts +2 -0
  33. package/dist/v2/debug-diamond.d.ts.map +1 -0
  34. package/dist/v2/index.d.ts +7 -0
  35. package/dist/v2/index.d.ts.map +1 -0
  36. package/dist/v2/primitives.d.ts +120 -0
  37. package/dist/v2/primitives.d.ts.map +1 -0
  38. package/dist/v2/proxy.d.ts +10 -0
  39. package/dist/v2/proxy.d.ts.map +1 -0
  40. package/dist/v2/registry.d.ts +35 -0
  41. package/dist/v2/registry.d.ts.map +1 -0
  42. package/dist/v2/stress.d.ts +7 -0
  43. package/dist/v2/stress.d.ts.map +1 -0
  44. package/dist/v2/test-suite.d.ts +2 -0
  45. package/dist/v2/test-suite.d.ts.map +1 -0
  46. package/dist/v2/test-v1.d.ts +2 -0
  47. package/dist/v2/test-v1.d.ts.map +1 -0
  48. package/package.json +7 -1
package/dist/index.js CHANGED
@@ -54,10 +54,13 @@ __export(exports_src, {
54
54
  readVersion: () => readVersion,
55
55
  proxy: () => proxy,
56
56
  peek: () => peek,
57
+ onScopeDispose: () => onScopeDispose,
57
58
  neverEquals: () => neverEquals,
58
59
  mutableSource: () => mutableSource,
59
60
  markReactions: () => markReactions,
61
+ linkedSignal: () => linkedSignal,
60
62
  isReactive: () => isReactive,
63
+ isLinkedSignal: () => isLinkedSignal,
61
64
  isDirty: () => isDirty,
62
65
  isBinding: () => isBinding,
63
66
  incrementWriteVersion: () => incrementWriteVersion,
@@ -65,15 +68,18 @@ __export(exports_src, {
65
68
  incrementBatchDepth: () => incrementBatchDepth,
66
69
  getWriteVersion: () => getWriteVersion,
67
70
  getReadVersion: () => getReadVersion,
71
+ getCurrentScope: () => getCurrentScope,
68
72
  getBatchDepth: () => getBatchDepth,
69
73
  get: () => get,
70
74
  flushSync: () => flushSync,
71
75
  equals: () => equals,
76
+ effectScope: () => effectScope,
72
77
  effect: () => effect,
73
78
  disconnectDerived: () => disconnectDerived,
74
79
  destroyEffect: () => destroyEffect,
75
80
  derived: () => derived,
76
81
  decrementBatchDepth: () => decrementBatchDepth,
82
+ createSelector: () => createSelector,
77
83
  createEquals: () => createEquals,
78
84
  createEffect: () => createEffect,
79
85
  createDerived: () => createDerived,
@@ -97,6 +103,7 @@ __export(exports_src, {
97
103
  REACTIVE_MARKER: () => REACTIVE_MARKER,
98
104
  REACTION_IS_UPDATING: () => REACTION_IS_UPDATING,
99
105
  MAYBE_DIRTY: () => MAYBE_DIRTY,
106
+ LINKED_SYMBOL: () => LINKED_SYMBOL,
100
107
  INERT: () => INERT,
101
108
  EFFECT_RAN: () => EFFECT_RAN,
102
109
  EFFECT_PRESERVED: () => EFFECT_PRESERVED,
@@ -107,7 +114,8 @@ __export(exports_src, {
107
114
  DERIVED: () => DERIVED,
108
115
  CLEAN: () => CLEAN,
109
116
  BRANCH_EFFECT: () => BRANCH_EFFECT,
110
- BLOCK_EFFECT: () => BLOCK_EFFECT
117
+ BLOCK_EFFECT: () => BLOCK_EFFECT,
118
+ BINDING_SYMBOL: () => BINDING_SYMBOL
111
119
  });
112
120
  module.exports = __toCommonJS(exports_src);
113
121
 
@@ -176,6 +184,9 @@ var UNINITIALIZED = Symbol.for("rlabs.signals.uninitialized");
176
184
  var STALE_REACTION = Symbol.for("rlabs.signals.stale_reaction");
177
185
  var STATE_SYMBOL = Symbol.for("rlabs.signals.state");
178
186
  var REACTIVE_MARKER = Symbol.for("rlabs.signals.reactive");
187
+ var BINDING_SYMBOL = Symbol.for("rlabs.signals.binding");
188
+ var LINKED_SYMBOL = Symbol.for("rlabs.signals.linked");
189
+ var SOURCE = 1 << 0;
179
190
  var STATUS_MASK = ~(DIRTY | MAYBE_DIRTY | CLEAN);
180
191
 
181
192
  // src/core/globals.ts
@@ -300,6 +311,9 @@ function setUpdateEffectImpl(impl) {
300
311
  function flushEffects() {
301
312
  const roots = clearQueuedRootEffects();
302
313
  for (const root of roots) {
314
+ if ((root.f & INERT) !== 0) {
315
+ continue;
316
+ }
303
317
  if (isDirty(root)) {
304
318
  updateEffectImpl(root);
305
319
  }
@@ -310,7 +324,7 @@ function processEffectTree(effect) {
310
324
  let child = effect.first;
311
325
  while (child !== null) {
312
326
  const next = child.next;
313
- if (isDirty(child)) {
327
+ if ((child.f & INERT) === 0 && isDirty(child)) {
314
328
  updateEffectImpl(child);
315
329
  }
316
330
  if (child.first !== null) {
@@ -323,6 +337,9 @@ function flushPendingReactions() {
323
337
  const reactions = [...pendingReactions];
324
338
  clearPendingReactions();
325
339
  for (const reaction of reactions) {
340
+ if ((reaction.f & INERT) !== 0) {
341
+ continue;
342
+ }
326
343
  if (isDirty(reaction)) {
327
344
  if ("parent" in reaction) {
328
345
  updateEffectImpl(reaction);
@@ -354,6 +371,9 @@ function flushSync(fn) {
354
371
  continue;
355
372
  }
356
373
  for (const root of roots) {
374
+ if ((root.f & INERT) !== 0) {
375
+ continue;
376
+ }
357
377
  if (isDirty(root)) {
358
378
  updateEffectImpl(root);
359
379
  }
@@ -400,13 +420,63 @@ function get(signal) {
400
420
  }
401
421
  }
402
422
  if ((signal.f & DERIVED) !== 0) {
403
- const derived = signal;
404
- if (isDirty(derived)) {
405
- updateDerived(derived);
406
- }
423
+ updateDerivedChain(signal);
407
424
  }
408
425
  return signal.v;
409
426
  }
427
+ var updateCycleId = 0;
428
+ var processedInCycle = new WeakMap;
429
+ function updateDerivedChain(target) {
430
+ if ((target.f & (DIRTY | MAYBE_DIRTY)) === 0) {
431
+ return;
432
+ }
433
+ const cycleId = ++updateCycleId;
434
+ const chain = [target];
435
+ processedInCycle.set(target, cycleId);
436
+ let idx = 0;
437
+ while (idx < chain.length) {
438
+ const current = chain[idx];
439
+ idx++;
440
+ if ((current.f & (DIRTY | MAYBE_DIRTY)) === 0) {
441
+ continue;
442
+ }
443
+ const deps = current.deps;
444
+ if (deps !== null) {
445
+ for (let i = 0;i < deps.length; i++) {
446
+ const dep = deps[i];
447
+ if ((dep.f & DERIVED) !== 0 && (dep.f & (DIRTY | MAYBE_DIRTY)) !== 0 && processedInCycle.get(dep) !== cycleId) {
448
+ chain.push(dep);
449
+ processedInCycle.set(dep, cycleId);
450
+ }
451
+ }
452
+ }
453
+ }
454
+ for (let i = chain.length - 1;i >= 0; i--) {
455
+ const current = chain[i];
456
+ if ((current.f & (DIRTY | MAYBE_DIRTY)) === 0) {
457
+ continue;
458
+ }
459
+ if ((current.f & DIRTY) !== 0) {
460
+ updateDerived(current);
461
+ } else {
462
+ const deps = current.deps;
463
+ let needsUpdate = false;
464
+ if (deps !== null) {
465
+ for (let j = 0;j < deps.length; j++) {
466
+ if (deps[j].wv > current.wv) {
467
+ needsUpdate = true;
468
+ break;
469
+ }
470
+ }
471
+ }
472
+ if (needsUpdate) {
473
+ updateDerived(current);
474
+ } else {
475
+ setSignalStatus(current, CLEAN);
476
+ }
477
+ }
478
+ }
479
+ }
410
480
  function set(signal, value) {
411
481
  if (activeReaction !== null && (activeReaction.f & DERIVED) !== 0) {
412
482
  throw new Error("Cannot write to signals inside a derived. " + "Deriveds should be pure computations with no side effects.");
@@ -422,20 +492,24 @@ function set(signal, value) {
422
492
  return value;
423
493
  }
424
494
  function markReactions(signal, status) {
425
- const reactions = signal.reactions;
426
- if (reactions === null)
427
- return;
428
- for (let i = 0;i < reactions.length; i++) {
429
- const reaction = reactions[i];
430
- const flags = reaction.f;
431
- const notDirty = (flags & DIRTY) === 0;
432
- if (notDirty) {
433
- setSignalStatus(reaction, status);
434
- }
435
- if ((flags & DERIVED) !== 0) {
436
- markReactions(reaction, MAYBE_DIRTY);
437
- } else if (notDirty) {
438
- scheduleEffect(reaction);
495
+ const stack = [{ signal, status }];
496
+ while (stack.length > 0) {
497
+ const { signal: currentSignal, status: currentStatus } = stack.pop();
498
+ const reactions = currentSignal.reactions;
499
+ if (reactions === null)
500
+ continue;
501
+ for (let i = 0;i < reactions.length; i++) {
502
+ const reaction = reactions[i];
503
+ const flags = reaction.f;
504
+ const notDirty = (flags & DIRTY) === 0;
505
+ if (notDirty) {
506
+ setSignalStatus(reaction, currentStatus);
507
+ }
508
+ if ((flags & DERIVED) !== 0) {
509
+ stack.push({ signal: reaction, status: MAYBE_DIRTY });
510
+ } else if (notDirty) {
511
+ scheduleEffect(reaction);
512
+ }
439
513
  }
440
514
  }
441
515
  }
@@ -446,22 +520,66 @@ function isDirty(reaction) {
446
520
  if ((reaction.f & DIRTY) !== 0) {
447
521
  return true;
448
522
  }
449
- if ((reaction.f & MAYBE_DIRTY) !== 0) {
450
- const deps = reaction.deps;
451
- if (deps !== null) {
452
- for (let i = 0;i < deps.length; i++) {
453
- const dep = deps[i];
454
- if ((dep.f & DERIVED) !== 0) {
455
- if (isDirty(dep)) {
456
- updateDerived(dep);
457
- }
523
+ if ((reaction.f & MAYBE_DIRTY) === 0) {
524
+ return false;
525
+ }
526
+ const toCheck = [reaction];
527
+ const toUpdate = [];
528
+ let idx = 0;
529
+ while (idx < toCheck.length) {
530
+ const current = toCheck[idx];
531
+ idx++;
532
+ if ((current.f & DIRTY) !== 0) {
533
+ continue;
534
+ }
535
+ if ((current.f & MAYBE_DIRTY) === 0) {
536
+ continue;
537
+ }
538
+ const deps2 = current.deps;
539
+ if (deps2 !== null) {
540
+ for (let i = 0;i < deps2.length; i++) {
541
+ const dep = deps2[i];
542
+ if ((dep.f & DERIVED) !== 0 && (dep.f & (DIRTY | MAYBE_DIRTY)) !== 0) {
543
+ toCheck.push(dep);
458
544
  }
459
- if (dep.wv > reaction.wv) {
460
- return true;
545
+ }
546
+ }
547
+ }
548
+ for (let i = toCheck.length - 1;i >= 0; i--) {
549
+ const current = toCheck[i];
550
+ if ((current.f & DERIVED) === 0) {
551
+ continue;
552
+ }
553
+ if ((current.f & DIRTY) !== 0) {
554
+ updateDerived(current);
555
+ } else if ((current.f & MAYBE_DIRTY) !== 0) {
556
+ const deps2 = current.deps;
557
+ let needsUpdate = false;
558
+ if (deps2 !== null) {
559
+ for (let j = 0;j < deps2.length; j++) {
560
+ if (deps2[j].wv > current.wv) {
561
+ needsUpdate = true;
562
+ break;
563
+ }
461
564
  }
462
565
  }
566
+ if (needsUpdate) {
567
+ updateDerived(current);
568
+ } else {
569
+ setSignalStatus(current, CLEAN);
570
+ }
571
+ }
572
+ }
573
+ if ((reaction.f & DIRTY) !== 0) {
574
+ return true;
575
+ }
576
+ const deps = reaction.deps;
577
+ if (deps !== null) {
578
+ for (let i = 0;i < deps.length; i++) {
579
+ if (deps[i].wv > reaction.wv) {
580
+ return true;
581
+ }
463
582
  }
464
- setSignalStatus(reaction, CLEAN);
465
583
  }
466
584
  return false;
467
585
  }
@@ -614,6 +732,7 @@ function stateRaw(initialValue) {
614
732
  }
615
733
 
616
734
  // src/deep/proxy.ts
735
+ var proxyCache = new WeakMap;
617
736
  var proxyCleanup = new FinalizationRegistry((data) => {
618
737
  data.version.reactions = null;
619
738
  for (const src of data.sources.values()) {
@@ -625,6 +744,9 @@ function shouldProxy(value) {
625
744
  if (value === null || typeof value !== "object") {
626
745
  return false;
627
746
  }
747
+ if (BINDING_SYMBOL in value) {
748
+ return false;
749
+ }
628
750
  const proto = Object.getPrototypeOf(value);
629
751
  return proto === Object.prototype || proto === Array.prototype || proto === null;
630
752
  }
@@ -639,6 +761,10 @@ function proxy(value) {
639
761
  if (!shouldProxy(value) || isProxy(value)) {
640
762
  return value;
641
763
  }
764
+ const cached = proxyCache.get(value);
765
+ if (cached) {
766
+ return cached;
767
+ }
642
768
  const sources = new Map;
643
769
  const version = source(0);
644
770
  const isArray = Array.isArray(value);
@@ -690,7 +816,7 @@ function proxy(value) {
690
816
  }
691
817
  return currentValue;
692
818
  },
693
- set(target, prop, newValue, receiver) {
819
+ set(target, prop, newValue) {
694
820
  const exists = prop in target;
695
821
  let s = sources.get(prop);
696
822
  if (s === undefined) {
@@ -700,29 +826,35 @@ function proxy(value) {
700
826
  s = withParent(() => source(undefined));
701
827
  sources.set(prop, s);
702
828
  }
703
- const proxied = withParent(() => shouldProxy(newValue) ? proxy(newValue) : newValue);
829
+ let proxied;
830
+ if (shouldProxy(newValue)) {
831
+ proxied = withParent(() => proxy(newValue));
832
+ } else {
833
+ proxied = newValue;
834
+ }
704
835
  set(s, proxied);
705
- Reflect.set(target, prop, newValue, receiver);
706
- if (isArray && prop === "length") {
707
- const oldLength = s.v;
708
- const newLength = newValue;
709
- for (let i = newLength;i < oldLength; i++) {
710
- const indexKey = String(i);
711
- const indexSource = sources.get(indexKey);
712
- if (indexSource !== undefined) {
713
- set(indexSource, UNINITIALIZED);
714
- } else if (i in target) {
715
- const deletedSource = withParent(() => source(UNINITIALIZED));
716
- sources.set(indexKey, deletedSource);
836
+ target[prop] = newValue;
837
+ if (isArray) {
838
+ if (prop === "length") {
839
+ const oldLength = s.v;
840
+ const newLength = newValue;
841
+ for (let i = newLength;i < oldLength; i++) {
842
+ const indexKey = String(i);
843
+ const indexSource = sources.get(indexKey);
844
+ if (indexSource !== undefined) {
845
+ set(indexSource, UNINITIALIZED);
846
+ } else if (i in target) {
847
+ const deletedSource = withParent(() => source(UNINITIALIZED));
848
+ sources.set(indexKey, deletedSource);
849
+ }
717
850
  }
718
- }
719
- }
720
- if (isArray && typeof prop === "string") {
721
- const index = Number(prop);
722
- if (Number.isInteger(index) && index >= 0) {
723
- const lengthSource = sources.get("length");
724
- if (lengthSource !== undefined && index >= lengthSource.v) {
725
- set(lengthSource, index + 1);
851
+ } else if (typeof prop === "string") {
852
+ const index = Number(prop);
853
+ if (Number.isInteger(index) && index >= 0) {
854
+ const lengthSource = sources.get("length");
855
+ if (lengthSource !== undefined && index >= lengthSource.v) {
856
+ set(lengthSource, index + 1);
857
+ }
726
858
  }
727
859
  }
728
860
  }
@@ -743,7 +875,7 @@ function proxy(value) {
743
875
  }
744
876
  set(version, get(version) + 1);
745
877
  }
746
- return Reflect.deleteProperty(target, prop);
878
+ return delete target[prop];
747
879
  },
748
880
  has(target, prop) {
749
881
  if (prop === STATE_SYMBOL) {
@@ -782,6 +914,7 @@ function proxy(value) {
782
914
  }
783
915
  });
784
916
  proxyCleanup.register(proxyObj, { sources, version });
917
+ proxyCache.set(value, proxyObj);
785
918
  return proxyObj;
786
919
  }
787
920
  function toRaw(value) {
@@ -871,6 +1004,123 @@ function disconnectDerived(derived2) {
871
1004
  derived2.reactions = null;
872
1005
  destroyDerivedEffects(derived2);
873
1006
  }
1007
+ // src/primitives/scope.ts
1008
+ var activeScope = null;
1009
+ function getCurrentScope() {
1010
+ return activeScope;
1011
+ }
1012
+ function setActiveScope(scope) {
1013
+ const prev = activeScope;
1014
+ activeScope = scope;
1015
+ return prev;
1016
+ }
1017
+
1018
+ class EffectScopeImpl {
1019
+ _active = true;
1020
+ _paused = false;
1021
+ effects = [];
1022
+ cleanups = [];
1023
+ parent = null;
1024
+ scopes = null;
1025
+ constructor(detached = false) {
1026
+ this.parent = activeScope;
1027
+ if (!detached && this.parent) {
1028
+ if (this.parent.scopes === null) {
1029
+ this.parent.scopes = [];
1030
+ }
1031
+ this.parent.scopes.push(this);
1032
+ }
1033
+ }
1034
+ get active() {
1035
+ return this._active;
1036
+ }
1037
+ get paused() {
1038
+ return this._paused;
1039
+ }
1040
+ run(fn) {
1041
+ if (!this._active) {
1042
+ return;
1043
+ }
1044
+ const prevScope = setActiveScope(this);
1045
+ try {
1046
+ return fn();
1047
+ } finally {
1048
+ setActiveScope(prevScope);
1049
+ }
1050
+ }
1051
+ stop() {
1052
+ if (!this._active)
1053
+ return;
1054
+ for (const effect of [...this.effects]) {
1055
+ destroyEffect(effect);
1056
+ }
1057
+ this.effects.length = 0;
1058
+ for (const cleanup of this.cleanups) {
1059
+ try {
1060
+ cleanup();
1061
+ } catch {}
1062
+ }
1063
+ this.cleanups.length = 0;
1064
+ if (this.scopes) {
1065
+ for (const scope of this.scopes) {
1066
+ scope.stop();
1067
+ }
1068
+ this.scopes.length = 0;
1069
+ }
1070
+ if (this.parent?.scopes) {
1071
+ const idx = this.parent.scopes.indexOf(this);
1072
+ if (idx !== -1) {
1073
+ this.parent.scopes.splice(idx, 1);
1074
+ }
1075
+ }
1076
+ this._active = false;
1077
+ }
1078
+ pause() {
1079
+ if (!this._active || this._paused)
1080
+ return;
1081
+ this._paused = true;
1082
+ for (const effect of this.effects) {
1083
+ effect.f |= INERT;
1084
+ }
1085
+ if (this.scopes) {
1086
+ for (const scope of this.scopes) {
1087
+ scope.pause();
1088
+ }
1089
+ }
1090
+ }
1091
+ resume() {
1092
+ if (!this._active || !this._paused)
1093
+ return;
1094
+ this._paused = false;
1095
+ for (const effect of this.effects) {
1096
+ effect.f &= ~INERT;
1097
+ if ((effect.f & DIRTY) !== 0) {
1098
+ scheduleEffect(effect);
1099
+ }
1100
+ }
1101
+ if (this.scopes) {
1102
+ for (const scope of this.scopes) {
1103
+ scope.resume();
1104
+ }
1105
+ }
1106
+ }
1107
+ }
1108
+ function effectScope(detached) {
1109
+ return new EffectScopeImpl(detached);
1110
+ }
1111
+ function onScopeDispose(fn) {
1112
+ if (activeScope) {
1113
+ activeScope.cleanups.push(fn);
1114
+ } else {
1115
+ console.warn("onScopeDispose() called outside of scope context");
1116
+ }
1117
+ }
1118
+ function registerEffectWithScope(effect) {
1119
+ if (activeScope) {
1120
+ activeScope.effects.push(effect);
1121
+ }
1122
+ }
1123
+
874
1124
  // src/primitives/effect.ts
875
1125
  function createEffect(type, fn, sync, push = true) {
876
1126
  const parent = activeEffect;
@@ -886,6 +1136,7 @@ function createEffect(type, fn, sync, push = true) {
886
1136
  next: null,
887
1137
  wv: 0
888
1138
  };
1139
+ registerEffectWithScope(effect);
889
1140
  if (sync) {
890
1141
  updateEffect(effect);
891
1142
  effect.f |= EFFECT_RAN;
@@ -998,7 +1249,6 @@ effect.tracking = function effectTracking() {
998
1249
  var bindingCleanup = new FinalizationRegistry((internalSource) => {
999
1250
  internalSource.reactions = null;
1000
1251
  });
1001
- var BINDING_SYMBOL = Symbol("binding");
1002
1252
  function isBinding(value) {
1003
1253
  return value !== null && typeof value === "object" && BINDING_SYMBOL in value;
1004
1254
  }
@@ -1084,6 +1334,130 @@ function untrack(fn) {
1084
1334
  }
1085
1335
  }
1086
1336
  var peek = untrack;
1337
+
1338
+ // src/primitives/linked.ts
1339
+ function linkedSignal(config) {
1340
+ let sourceFn;
1341
+ let computation;
1342
+ let equalsFn = Object.is;
1343
+ if (typeof config === "function") {
1344
+ const fn = config;
1345
+ sourceFn = fn;
1346
+ computation = (s) => s;
1347
+ } else {
1348
+ sourceFn = config.source;
1349
+ computation = config.computation;
1350
+ if (config.equal) {
1351
+ equalsFn = config.equal;
1352
+ }
1353
+ }
1354
+ let prevSource = undefined;
1355
+ let prevValue = undefined;
1356
+ let initialized = false;
1357
+ const valueSignal = signal(undefined, { equals: equalsFn });
1358
+ const sourceTracker = derived(() => {
1359
+ const newSource = sourceFn();
1360
+ return newSource;
1361
+ });
1362
+ let manualOverride = false;
1363
+ let lastKnownSource = undefined;
1364
+ const dispose = effect.pre(() => {
1365
+ const currentSource = sourceTracker.value;
1366
+ const sourceChanged = initialized && !Object.is(lastKnownSource, currentSource);
1367
+ if (!initialized || sourceChanged) {
1368
+ const previous = initialized ? { source: prevSource, value: prevValue } : undefined;
1369
+ const newValue = computation(currentSource, previous);
1370
+ prevSource = currentSource;
1371
+ prevValue = newValue;
1372
+ lastKnownSource = currentSource;
1373
+ initialized = true;
1374
+ manualOverride = false;
1375
+ untrack(() => {
1376
+ valueSignal.value = newValue;
1377
+ });
1378
+ }
1379
+ });
1380
+ const accessor = {
1381
+ [LINKED_SYMBOL]: true,
1382
+ get value() {
1383
+ sourceTracker.value;
1384
+ const currentSource = untrack(() => sourceTracker.value);
1385
+ if (initialized && !Object.is(lastKnownSource, currentSource)) {
1386
+ flushSync();
1387
+ }
1388
+ return valueSignal.value;
1389
+ },
1390
+ set value(newValue) {
1391
+ manualOverride = true;
1392
+ prevValue = newValue;
1393
+ valueSignal.value = newValue;
1394
+ },
1395
+ get peek() {
1396
+ return untrack(() => valueSignal.value);
1397
+ }
1398
+ };
1399
+ return accessor;
1400
+ }
1401
+ function isLinkedSignal(value) {
1402
+ return value !== null && typeof value === "object" && LINKED_SYMBOL in value;
1403
+ }
1404
+ // src/primitives/selector.ts
1405
+ function createSelector(source2, fn = (k, v) => k === v) {
1406
+ const subscribers = new Map;
1407
+ const reactionKeys = new WeakMap;
1408
+ let prevValue;
1409
+ let initialized = false;
1410
+ effect.pre(() => {
1411
+ const value = source2();
1412
+ if (initialized && !Object.is(prevValue, value)) {
1413
+ for (const [key, reactions] of subscribers) {
1414
+ const wasSelected = fn(key, prevValue);
1415
+ const isSelected = fn(key, value);
1416
+ if (wasSelected !== isSelected) {
1417
+ const toRemove = [];
1418
+ for (const reaction of reactions) {
1419
+ if ((reaction.f & DESTROYED) !== 0) {
1420
+ toRemove.push(reaction);
1421
+ continue;
1422
+ }
1423
+ setSignalStatus(reaction, DIRTY);
1424
+ if ((reaction.f & EFFECT) !== 0 && (reaction.f & DERIVED) === 0) {
1425
+ scheduleEffect(reaction);
1426
+ }
1427
+ }
1428
+ for (const r of toRemove) {
1429
+ reactions.delete(r);
1430
+ }
1431
+ }
1432
+ }
1433
+ }
1434
+ prevValue = value;
1435
+ initialized = true;
1436
+ });
1437
+ return (key) => {
1438
+ const currentValue = prevValue;
1439
+ if (activeReaction !== null) {
1440
+ const reaction = activeReaction;
1441
+ if ((reaction.f & DESTROYED) === 0) {
1442
+ let keySubscribers = subscribers.get(key);
1443
+ if (!keySubscribers) {
1444
+ keySubscribers = new Set;
1445
+ subscribers.set(key, keySubscribers);
1446
+ }
1447
+ if (!keySubscribers.has(reaction)) {
1448
+ keySubscribers.add(reaction);
1449
+ let keys = reactionKeys.get(reaction);
1450
+ if (!keys) {
1451
+ keys = new Set;
1452
+ reactionKeys.set(reaction, keys);
1453
+ }
1454
+ keys.add(key);
1455
+ }
1456
+ }
1457
+ }
1458
+ return fn(key, currentValue);
1459
+ };
1460
+ }
1087
1461
  // src/collections/map.ts
1088
1462
  var mapCleanup = new FinalizationRegistry((data) => {
1089
1463
  data.version.reactions = null;