valdres 1.0.0-beta.3 → 1.0.0-beta.4

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/index.js CHANGED
@@ -157,16 +157,20 @@ var isProd = () => {
157
157
  // src/lib/setValueInData.ts
158
158
  var trackScopeValue = (key, data) => {
159
159
  const parent = data.parent;
160
+ const indexKeys = data.scopeIndexKeys;
161
+ if (!parent || !indexKeys) {
162
+ throw new Error("trackScopeValue called on a root store");
163
+ }
160
164
  let set = parent.scopeValueIndex.get(key);
161
165
  if (!set) {
162
166
  set = new Set;
163
167
  parent.scopeValueIndex.set(key, set);
164
168
  }
165
169
  set.add(data);
166
- data.scopeIndexKeys.add(key);
170
+ indexKeys.add(key);
167
171
  };
168
172
  var setValueInData = (atom, value, data) => {
169
- const isNewAtomInScope = "parent" in data && Object.hasOwn(atom, "defaultValue") && !data.values.has(atom);
173
+ const isNewAtomInScope = data.parent && Object.hasOwn(atom, "defaultValue") && !data.values.has(atom);
170
174
  let written;
171
175
  if (atom.mutable || isProd()) {
172
176
  data.values.set(atom, value);
@@ -245,14 +249,14 @@ var initFamilyIndex = (family, data) => {
245
249
  if (data.values.has(family))
246
250
  return data.values.get(family).__index;
247
251
  let parentIndex;
248
- if ("parent" in data) {
252
+ if (data.parent) {
249
253
  parentIndex = initFamilyIndex(family, data.parent);
250
254
  if (!parentIndex)
251
255
  throw new Error("Parent index is missing");
252
256
  }
253
257
  const index = createAtomFamilyIndex(parentIndex);
254
258
  data.values.set(family, renderAtomFamilyIndex(index));
255
- if ("parent" in data) {
259
+ if (data.parent) {
256
260
  trackScopeValue(family, data);
257
261
  }
258
262
  return index;
@@ -343,7 +347,6 @@ ${generateSelectorTrace(this.selectors)}`;
343
347
 
344
348
  // src/lib/asyncDependencyTracking.ts
345
349
  var pendingAsyncDeps = new WeakMap;
346
- var latestEvalContext = new WeakMap;
347
350
 
348
351
  class SuspendAndWaitForResolveError extends Error {
349
352
  promise;
@@ -379,7 +382,8 @@ var lateGet = (state, selector, data) => {
379
382
  try {
380
383
  return getState(state, data, lateInitSet);
381
384
  } finally {
382
- if (isNewDep && isTransitivelySubscribed(selector, data)) {
385
+ if (isNewDep && isLive(selector, data)) {
386
+ onLiveDependencyAdded(state, data);
383
387
  mountTransitiveDeps(state, data);
384
388
  }
385
389
  }
@@ -391,23 +395,22 @@ var cleanUpRejectedPromise = (selector, data, promise) => {
391
395
  };
392
396
 
393
397
  // src/lib/initSelector.ts
394
- class NeedsInitError {
395
- selector;
396
- constructor(selector) {
397
- this.selector = selector;
398
- }
399
- }
400
- var _evalDepth = 0;
401
- var _inTrampoline = false;
402
- var MAX_EVAL_DEPTH = 100;
403
- var sharedCircularDepSet = new WeakSet;
404
398
  var neverAbortedSignal = new AbortController().signal;
405
399
  var syncOptionsCache = new WeakMap;
406
- var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencySet = sharedCircularDepSet, addedDepsOut, removedDepsOut) => {
400
+ var getSyncOptions = (data) => {
401
+ let cached = syncOptionsCache.get(data);
402
+ if (!cached) {
403
+ cached = { signal: neverAbortedSignal, storeId: data.id };
404
+ syncOptionsCache.set(data, cached);
405
+ }
406
+ return cached;
407
+ };
408
+ var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencySet = data.circularDepSet, depsChangeOut) => {
407
409
  const currentDependencies = data.stateDependencies.get(selector);
408
410
  const updatedDepsArray = [];
409
411
  let depsChanged = false;
410
412
  let evaluationComplete = false;
413
+ const latestEvalContext = data.latestEvalContext;
411
414
  const prevCtx = latestEvalContext.get(selector);
412
415
  if (prevCtx)
413
416
  prevCtx.revoked = true;
@@ -418,34 +421,33 @@ var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencyS
418
421
  }
419
422
  circularDependencySet.add(selector);
420
423
  try {
421
- const prev = data.abortControllers.get(selector);
422
424
  let options;
423
- if (prev === false) {
424
- let cached = syncOptionsCache.get(data);
425
- if (!cached) {
426
- cached = { signal: neverAbortedSignal, storeId: data.id };
427
- syncOptionsCache.set(data, cached);
428
- }
429
- options = cached;
425
+ if (selector.get.length < 2) {
426
+ options = getSyncOptions(data);
430
427
  } else {
431
- if (prev)
432
- prev.abort();
433
- let controller;
434
- const myEvalCtx = evalCtx;
435
- options = {
436
- storeId: data.id,
437
- get signal() {
438
- if (!controller) {
439
- controller = new AbortController;
440
- if (myEvalCtx.revoked) {
441
- controller.abort();
442
- } else {
443
- data.abortControllers.set(selector, controller);
428
+ const prev = data.abortControllers.get(selector);
429
+ if (prev === false) {
430
+ options = getSyncOptions(data);
431
+ } else {
432
+ if (prev)
433
+ prev.abort();
434
+ let controller;
435
+ const myEvalCtx = evalCtx;
436
+ options = {
437
+ storeId: data.id,
438
+ get signal() {
439
+ if (!controller) {
440
+ controller = new AbortController;
441
+ if (myEvalCtx.revoked) {
442
+ controller.abort();
443
+ } else {
444
+ data.abortControllers.set(selector, controller);
445
+ }
444
446
  }
447
+ return controller.signal;
445
448
  }
446
- return controller.signal;
447
- }
448
- };
449
+ };
450
+ }
449
451
  }
450
452
  let allDepsThisEval;
451
453
  let result;
@@ -470,8 +472,6 @@ var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencyS
470
472
  return value;
471
473
  }, options);
472
474
  } catch (error) {
473
- if (error instanceof NeedsInitError)
474
- throw error;
475
475
  if (error instanceof SuspendAndWaitForResolveError) {
476
476
  result = error;
477
477
  } else if (error instanceof SelectorEvaluationError) {
@@ -492,22 +492,28 @@ var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencyS
492
492
  updatedDependencies.add(dep);
493
493
  }
494
494
  }
495
- const prev2 = currentDependencies ?? new Set;
495
+ const prev = currentDependencies ?? new Set;
496
496
  for (const state of updatedDependencies) {
497
- if (!prev2.has(state)) {
497
+ if (!prev.has(state)) {
498
498
  const set = getOrInitDependentsSet(state, data);
499
499
  set.add(selector);
500
- if (addedDepsOut)
501
- addedDepsOut.add(state);
500
+ if (depsChangeOut) {
501
+ if (!depsChangeOut.added)
502
+ depsChangeOut.added = new Set;
503
+ depsChangeOut.added.add(state);
504
+ }
502
505
  }
503
506
  }
504
507
  if (!isAsyncResult) {
505
- for (const state of prev2) {
508
+ for (const state of prev) {
506
509
  if (!updatedDependencies.has(state)) {
507
510
  const set = getOrInitDependentsSet(state, data);
508
511
  set.delete(selector);
509
- if (removedDepsOut)
510
- removedDepsOut.add(state);
512
+ if (depsChangeOut) {
513
+ if (!depsChangeOut.removed)
514
+ depsChangeOut.removed = new Set;
515
+ depsChangeOut.removed.add(state);
516
+ }
511
517
  }
512
518
  }
513
519
  }
@@ -534,7 +540,7 @@ var handleSelectorResult = (value, selector, data) => {
534
540
  const initializedAtomsSet = new Set;
535
541
  const res = initSelector(selector, data, initializedAtomsSet);
536
542
  if (initializedAtomsSet.size > 0) {
537
- propagateUpdatedAtoms([...initializedAtomsSet], data);
543
+ propagateAtomUpdate([...initializedAtomsSet], data);
538
544
  }
539
545
  return res;
540
546
  }).catch(() => {
@@ -556,12 +562,16 @@ var handleSelectorResult = (value, selector, data) => {
556
562
  pendingAsyncDeps.delete(value);
557
563
  const currentDeps = data.stateDependencies.get(selector);
558
564
  if (currentDeps) {
565
+ const selectorIsLive = isLive(selector, data);
559
566
  for (const dep of currentDeps) {
560
567
  if (!evalDeps.has(dep)) {
561
568
  currentDeps.delete(dep);
562
569
  const dependents2 = data.stateDependents.get(dep);
563
570
  if (dependents2)
564
571
  dependents2.delete(selector);
572
+ if (selectorIsLive) {
573
+ onLiveDependencyRemoved(dep, data);
574
+ }
565
575
  unmountOrphanedDeps(dep, data);
566
576
  }
567
577
  }
@@ -579,11 +589,13 @@ var handleSelectorResult = (value, selector, data) => {
579
589
  });
580
590
  return value;
581
591
  } else {
582
- data.abortControllers.set(selector, false);
592
+ if (selector.get.length >= 2) {
593
+ data.abortControllers.set(selector, false);
594
+ }
583
595
  return value;
584
596
  }
585
597
  };
586
- var initSelectorDirect = (selector, data, initializedAtomsSet, circularDependencySet) => {
598
+ var initSelector = (selector, data, initializedAtomsSet, circularDependencySet = data.circularDepSet) => {
587
599
  const existingValue = data.values.get(selector);
588
600
  const updatedValue = evaluate(selector, data, initializedAtomsSet, circularDependencySet);
589
601
  const areEqual = isPromiseLike(existingValue) || isPromiseLike(updatedValue) ? existingValue === updatedValue : selector.equal(existingValue, updatedValue);
@@ -594,56 +606,6 @@ var initSelectorDirect = (selector, data, initializedAtomsSet, circularDependenc
594
606
  return true;
595
607
  }
596
608
  };
597
- var initSelectorTrampoline = (selector, data, initializedAtomsSet, circularDependencySet) => {
598
- const stack = [selector];
599
- const inStack = new Set([selector]);
600
- while (stack.length > 0) {
601
- const current = stack[stack.length - 1];
602
- if (data.values.has(current)) {
603
- stack.pop();
604
- inStack.delete(current);
605
- continue;
606
- }
607
- try {
608
- initSelectorDirect(current, data, initializedAtomsSet, circularDependencySet);
609
- stack.pop();
610
- inStack.delete(current);
611
- } catch (e) {
612
- if (e instanceof NeedsInitError) {
613
- if (inStack.has(e.selector)) {
614
- throw new SelectorCircularDependencyError;
615
- }
616
- stack.push(e.selector);
617
- inStack.add(e.selector);
618
- } else {
619
- throw e;
620
- }
621
- }
622
- }
623
- };
624
- var initSelector = (selector, data, initializedAtomsSet, circularDependencySet = sharedCircularDepSet) => {
625
- const isTopLevel = _evalDepth === 0 && !_inTrampoline;
626
- _evalDepth++;
627
- const existingValue = data.values.get(selector);
628
- try {
629
- return initSelectorDirect(selector, data, initializedAtomsSet, circularDependencySet);
630
- } catch (e) {
631
- if (e instanceof NeedsInitError && isTopLevel) {
632
- _inTrampoline = true;
633
- try {
634
- initSelectorTrampoline(selector, data, initializedAtomsSet, circularDependencySet);
635
- } finally {
636
- _inTrampoline = false;
637
- }
638
- const newValue = data.values.get(selector);
639
- const areEqual = isPromiseLike(existingValue) || isPromiseLike(newValue) ? existingValue === newValue : selector.equal(existingValue, newValue);
640
- return !areEqual;
641
- }
642
- throw e;
643
- } finally {
644
- _evalDepth--;
645
- }
646
- };
647
609
  var evaluate = (selector, data, initializedAtomsSet, circularDependencySet) => {
648
610
  let tmpValue;
649
611
  try {
@@ -657,23 +619,18 @@ var evaluate = (selector, data, initializedAtomsSet, circularDependencySet) => {
657
619
  };
658
620
 
659
621
  // src/lib/propagateUpdatedAtoms.ts
660
- var reEvaluteSelector = (selector, data, updatedAtoms) => {
661
- const existingValue = data.values.get(selector);
662
- const addedDeps = new Set;
663
- const removedDeps = new Set;
622
+ var reEvaluateSelector = (selector, data, updatedAtoms, depsChange, existingValue) => {
664
623
  try {
665
- const rawValue = evaluateSelector(selector, data, updatedAtoms, undefined, addedDeps, removedDeps);
666
- const udpatedValue = handleSelectorResult(rawValue, selector, data);
667
- const areEqual = isPromiseLike(existingValue) || isPromiseLike(udpatedValue) ? existingValue === udpatedValue : selector.equal(existingValue, udpatedValue, updatedAtoms);
668
- if (areEqual) {
669
- return [false, false, undefined, addedDeps, removedDeps];
670
- } else {
671
- setValueInData(selector, udpatedValue, data);
672
- return [true, false, undefined, addedDeps, removedDeps];
673
- }
674
- } catch (error) {
624
+ const rawValue = evaluateSelector(selector, data, updatedAtoms, undefined, depsChange);
625
+ const updatedValue = handleSelectorResult(rawValue, selector, data);
626
+ const areEqual = isPromiseLike(existingValue) || isPromiseLike(updatedValue) ? existingValue === updatedValue : selector.equal(existingValue, updatedValue, updatedAtoms);
627
+ if (areEqual)
628
+ return false;
629
+ setValueInData(selector, updatedValue, data);
630
+ return true;
631
+ } catch {
675
632
  data.values.delete(selector);
676
- return [true, true, error, addedDeps, removedDeps];
633
+ return true;
677
634
  }
678
635
  };
679
636
  var callSubscribers = (subscriptions, families) => {
@@ -753,12 +710,12 @@ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, families = ne
753
710
  }
754
711
  }
755
712
  for (const [scope, familiesInScope] of scopeFamilies) {
756
- propagateUpdatedAtoms(familiesInScope, scope, undefined, undefined, false, timestamp, true);
713
+ propagateInScope(familiesInScope, scope);
757
714
  }
758
715
  }
759
716
  };
760
- var propagateUpdatedAtoms = (atoms, data, subscriptions, families, isRecursive = false, timestamp = performance.now(), selectorsOnly = false, isInitOnly = false) => {
761
- if (atoms.length === 1 && !isRecursive && !selectorsOnly) {
717
+ var propagateAtomUpdate = (atoms, data, isInitOnly = false) => {
718
+ if (atoms.length === 1) {
762
719
  const atom = atoms[0];
763
720
  if (!isFamilyAtom(atom) && !isAtomFamily(atom)) {
764
721
  const dependents = data.stateDependents.get(atom);
@@ -771,24 +728,21 @@ var propagateUpdatedAtoms = (atoms, data, subscriptions, families, isRecursive =
771
728
  }
772
729
  }
773
730
  }
774
- if (!subscriptions)
775
- subscriptions = new Set;
776
- if (!families)
777
- families = new Map;
731
+ const subscriptions = new Set;
732
+ const families = new Map;
778
733
  const selectors = new Set;
779
734
  for (const atom of atoms) {
780
735
  addSetToSet(data.stateDependents.get(atom), selectors);
781
- if (!selectorsOnly) {
782
- addSetToSet(data.subscriptions.get(atom), subscriptions);
783
- if (isFamilyAtom(atom)) {
784
- if (!families.has(atom.family)) {
785
- families.set(atom.family, new Set);
786
- }
787
- families.get(atom.family).add(atom);
736
+ addSetToSet(data.subscriptions.get(atom), subscriptions);
737
+ if (isFamilyAtom(atom)) {
738
+ if (!families.has(atom.family)) {
739
+ families.set(atom.family, new Set);
788
740
  }
741
+ families.get(atom.family).add(atom);
789
742
  }
790
743
  }
791
744
  if (families.size > 0) {
745
+ const timestamp = performance.now();
792
746
  for (const [family, familyAtoms] of families) {
793
747
  addSetToSet(data.stateDependents.get(family), selectors);
794
748
  addSetToSet(data.subscriptions.get(family), subscriptions);
@@ -797,55 +751,68 @@ var propagateUpdatedAtoms = (atoms, data, subscriptions, families, isRecursive =
797
751
  addFamilyAtomsToSet(family, familyAtoms, data, timestamp);
798
752
  }
799
753
  }
800
- if (!isRecursive) {
801
- propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly);
802
- if (data.scopes && data.scopes.size > 0) {
803
- if (atoms.length === 1) {
804
- const atom = atoms[0];
805
- const shadowingScopes = isAtomFamily(atom) ? undefined : data.scopeValueIndex.get(atom);
806
- for (const [, scope] of data.scopes) {
807
- if (!shadowingScopes || !shadowingScopes.has(scope)) {
808
- propagateUpdatedAtoms(atoms, scope, undefined, undefined, false, timestamp, true, isInitOnly);
809
- }
810
- }
754
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly);
755
+ if (data.scopes && data.scopes.size > 0) {
756
+ propagateToScopes(atoms, data, isInitOnly);
757
+ }
758
+ };
759
+ var propagateInScope = (atoms, data, isInitOnly = false) => {
760
+ const subscriptions = new Set;
761
+ const families = new Map;
762
+ const selectors = new Set;
763
+ for (const atom of atoms) {
764
+ addSetToSet(data.stateDependents.get(atom), selectors);
765
+ }
766
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly);
767
+ if (data.scopes && data.scopes.size > 0) {
768
+ propagateToScopes(atoms, data, isInitOnly);
769
+ }
770
+ };
771
+ var propagateToScopes = (atoms, data, isInitOnly) => {
772
+ if (atoms.length === 1) {
773
+ const atom = atoms[0];
774
+ const shadowingScopes = isAtomFamily(atom) ? undefined : data.scopeValueIndex.get(atom);
775
+ for (const [, scope] of data.scopes) {
776
+ if (!shadowingScopes || !shadowingScopes.has(scope)) {
777
+ propagateInScope(atoms, scope, isInitOnly);
778
+ }
779
+ }
780
+ return;
781
+ }
782
+ let anyShadowed = false;
783
+ let atomShadows;
784
+ for (const atom of atoms) {
785
+ if (!isAtomFamily(atom)) {
786
+ const s = data.scopeValueIndex.get(atom);
787
+ if (s && s.size > 0) {
788
+ if (!atomShadows)
789
+ atomShadows = new Map;
790
+ atomShadows.set(atom, s);
791
+ anyShadowed = true;
792
+ }
793
+ }
794
+ }
795
+ if (!anyShadowed) {
796
+ for (const [, scope] of data.scopes) {
797
+ propagateInScope(atoms, scope, isInitOnly);
798
+ }
799
+ return;
800
+ }
801
+ for (const [, scope] of data.scopes) {
802
+ const atomsToUpdateInScope = [];
803
+ for (const atom of atoms) {
804
+ if (isAtomFamily(atom)) {
805
+ atomsToUpdateInScope.push(atom);
811
806
  } else {
812
- let anyShadowed = false;
813
- let atomShadows;
814
- for (const atom of atoms) {
815
- if (!isAtomFamily(atom)) {
816
- const s = data.scopeValueIndex.get(atom);
817
- if (s && s.size > 0) {
818
- if (!atomShadows)
819
- atomShadows = new Map;
820
- atomShadows.set(atom, s);
821
- anyShadowed = true;
822
- }
823
- }
824
- }
825
- if (!anyShadowed) {
826
- for (const [, scope] of data.scopes) {
827
- propagateUpdatedAtoms(atoms, scope, undefined, undefined, false, timestamp, true, isInitOnly);
828
- }
829
- } else {
830
- for (const [, scope] of data.scopes) {
831
- const atomsToUpdateInScope = [];
832
- for (const atom of atoms) {
833
- if (isAtomFamily(atom)) {
834
- atomsToUpdateInScope.push(atom);
835
- } else {
836
- const s = atomShadows.get(atom);
837
- if (!s || !s.has(scope)) {
838
- atomsToUpdateInScope.push(atom);
839
- }
840
- }
841
- }
842
- if (atomsToUpdateInScope.length > 0) {
843
- propagateUpdatedAtoms(atomsToUpdateInScope, scope, undefined, undefined, false, timestamp, true, isInitOnly);
844
- }
845
- }
807
+ const s = atomShadows.get(atom);
808
+ if (!s || !s.has(scope)) {
809
+ atomsToUpdateInScope.push(atom);
846
810
  }
847
811
  }
848
812
  }
813
+ if (atomsToUpdateInScope.length > 0) {
814
+ propagateInScope(atomsToUpdateInScope, scope, isInitOnly);
815
+ }
849
816
  }
850
817
  };
851
818
  var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false) => {
@@ -857,36 +824,147 @@ var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, fam
857
824
  callSubscribers(subscriptions, families);
858
825
  }
859
826
  };
860
- var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false) => {
861
- let currentSelectors = selectors;
862
- while (currentSelectors.size > 0) {
863
- const selectorsForNextPass = new Set;
864
- for (const selector of currentSelectors) {
865
- const currentValue = data.values.get(selector);
866
- if (isPromiseLike(currentValue) && isInitOnly) {
827
+ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly) => {
828
+ const closure = new Set(seeds);
829
+ {
830
+ const stack = [...seeds];
831
+ while (stack.length > 0) {
832
+ const s = stack.pop();
833
+ const downstream = data.stateDependents.get(s);
834
+ if (downstream) {
835
+ for (const d of downstream) {
836
+ if (!closure.has(d)) {
837
+ closure.add(d);
838
+ stack.push(d);
839
+ }
840
+ }
841
+ }
842
+ }
843
+ }
844
+ const pending = new Map;
845
+ const ready = [];
846
+ for (const s of closure) {
847
+ const deps = data.stateDependencies.get(s);
848
+ let count = 0;
849
+ if (deps) {
850
+ for (const d of deps) {
851
+ if (closure.has(d))
852
+ count++;
853
+ }
854
+ }
855
+ pending.set(s, count);
856
+ if (count === 0)
857
+ ready.push(s);
858
+ }
859
+ const needsEval = new Set(seeds);
860
+ const advance = (selector, propagateChange) => {
861
+ const downstream = data.stateDependents.get(selector);
862
+ if (!downstream)
863
+ return;
864
+ for (const d of downstream) {
865
+ if (!closure.has(d))
867
866
  continue;
867
+ const c = (pending.get(d) ?? 0) - 1;
868
+ pending.set(d, c);
869
+ if (propagateChange)
870
+ needsEval.add(d);
871
+ if (c <= 0)
872
+ ready.push(d);
873
+ }
874
+ };
875
+ const depsChange = {};
876
+ let head = 0;
877
+ while (head < ready.length) {
878
+ const selector = ready[head++];
879
+ const currentValue = data.values.get(selector);
880
+ if (isPromiseLike(currentValue) && isInitOnly) {
881
+ advance(selector, false);
882
+ continue;
883
+ }
884
+ if (!needsEval.has(selector)) {
885
+ advance(selector, false);
886
+ continue;
887
+ }
888
+ const dependents = data.stateDependents.get(selector);
889
+ const subscribers = data.subscriptions.get(selector);
890
+ if (!isPromiseLike(currentValue) && (!dependents || dependents.size === 0) && (!subscribers || subscribers.size === 0)) {
891
+ data.values.delete(selector);
892
+ advance(selector, false);
893
+ continue;
894
+ }
895
+ depsChange.added = undefined;
896
+ depsChange.removed = undefined;
897
+ const wasValueUpdated = reEvaluateSelector(selector, data, updatedInitializedAtoms, depsChange, currentValue);
898
+ const added = depsChange.added;
899
+ const removed = depsChange.removed;
900
+ if ((added || removed) && isLive(selector, data)) {
901
+ if (added) {
902
+ for (const dep of added) {
903
+ onLiveDependencyAdded(dep, data);
904
+ mountTransitiveDeps(dep, data);
905
+ }
868
906
  }
869
- const dependents = data.stateDependents.get(selector);
870
- const subscribers = data.subscriptions.get(selector);
871
- if (!isPromiseLike(currentValue) && (!dependents || dependents.size === 0) && (!subscribers || subscribers.size === 0)) {
872
- data.values.delete(selector);
873
- } else {
874
- const [wasValueUpdated, didEvalCrash, error, addedDeps, removedDeps] = reEvaluteSelector(selector, data, updatedInitializedAtoms);
875
- if ((addedDeps.size > 0 || removedDeps.size > 0) && isTransitivelySubscribed(selector, data)) {
876
- for (const dep of addedDeps) {
877
- mountTransitiveDeps(dep, data);
878
- }
879
- for (const dep of removedDeps) {
880
- unmountOrphanedDeps(dep, data);
881
- }
907
+ if (removed) {
908
+ for (const dep of removed) {
909
+ onLiveDependencyRemoved(dep, data);
910
+ unmountOrphanedDeps(dep, data);
911
+ }
912
+ }
913
+ }
914
+ advance(selector, wasValueUpdated);
915
+ if (wasValueUpdated && subscribers) {
916
+ addSetToSet(subscribers, collectedSubscribers);
917
+ }
918
+ }
919
+ };
920
+ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false) => {
921
+ if (selectors.size === 0)
922
+ return;
923
+ let downstreamSeeds;
924
+ const depsChange = {};
925
+ for (const selector of selectors) {
926
+ const currentValue = data.values.get(selector);
927
+ if (isPromiseLike(currentValue) && isInitOnly)
928
+ continue;
929
+ const dependents = data.stateDependents.get(selector);
930
+ const subscribers = data.subscriptions.get(selector);
931
+ if (!isPromiseLike(currentValue) && (!dependents || dependents.size === 0) && (!subscribers || subscribers.size === 0)) {
932
+ data.values.delete(selector);
933
+ continue;
934
+ }
935
+ depsChange.added = undefined;
936
+ depsChange.removed = undefined;
937
+ const wasValueUpdated = reEvaluateSelector(selector, data, updatedInitializedAtoms, depsChange, currentValue);
938
+ const added = depsChange.added;
939
+ const removed = depsChange.removed;
940
+ if ((added || removed) && isLive(selector, data)) {
941
+ if (added) {
942
+ for (const dep of added) {
943
+ onLiveDependencyAdded(dep, data);
944
+ mountTransitiveDeps(dep, data);
945
+ }
946
+ }
947
+ if (removed) {
948
+ for (const dep of removed) {
949
+ onLiveDependencyRemoved(dep, data);
950
+ unmountOrphanedDeps(dep, data);
882
951
  }
883
- if (!wasValueUpdated)
884
- continue;
885
- addSetToSet(data.stateDependents.get(selector), selectorsForNextPass);
886
- addSetToSet(subscribers, collectedSubscribers);
887
952
  }
888
953
  }
889
- currentSelectors = selectorsForNextPass;
954
+ if (!wasValueUpdated)
955
+ continue;
956
+ if (subscribers)
957
+ addSetToSet(subscribers, collectedSubscribers);
958
+ const downstream = data.stateDependents.get(selector);
959
+ if (downstream && downstream.size > 0) {
960
+ if (!downstreamSeeds)
961
+ downstreamSeeds = new Set;
962
+ for (const d of downstream)
963
+ downstreamSeeds.add(d);
964
+ }
965
+ }
966
+ if (downstreamSeeds && downstreamSeeds.size > 0) {
967
+ propagateDownstreamTopo(downstreamSeeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly);
890
968
  }
891
969
  };
892
970
 
@@ -894,11 +972,19 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
894
972
  var isFunction = (value) => typeof value === "function";
895
973
 
896
974
  // src/lib/setAtom.ts
897
- var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
898
- const emptyAtomPromise = currentValue?.__isEmptyAtomPromise__ ? currentValue : currentValue?.__emptyAtomPromiseOrigin__ ?? null;
899
- if (emptyAtomPromise) {
900
- promise.__emptyAtomPromiseOrigin__ = emptyAtomPromise;
975
+ var resolvePendingDefault = (atom, data, value) => {
976
+ let cur = data;
977
+ while (cur) {
978
+ const entry = cur.pendingDefaults.get(atom);
979
+ if (entry) {
980
+ entry.resolve(value);
981
+ cur.pendingDefaults.delete(atom);
982
+ return;
983
+ }
984
+ cur = "parent" in cur ? cur.parent : undefined;
901
985
  }
986
+ };
987
+ var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
902
988
  setValueInData(atom, promise, data);
903
989
  promise.then((resolvedValue) => {
904
990
  if (data.values.get(atom) !== promise)
@@ -906,15 +992,13 @@ var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
906
992
  setValueInData(atom, resolvedValue, data);
907
993
  if (atom.onSet && !skipOnSet)
908
994
  atom.onSet(resolvedValue, data);
909
- if (emptyAtomPromise) {
910
- emptyAtomPromise.__resolveEmptyAtomPromise__(resolvedValue);
911
- }
912
- propagateUpdatedAtoms([atom], data);
995
+ resolvePendingDefault(atom, data, resolvedValue);
996
+ propagateAtomUpdate([atom], data);
913
997
  }).catch(() => {
914
998
  if (data.values.get(atom) !== promise)
915
999
  return;
916
1000
  setValueInData(atom, currentValue, data);
917
- propagateUpdatedAtoms([atom], data);
1001
+ propagateAtomUpdate([atom], data);
918
1002
  });
919
1003
  };
920
1004
  var setAtom = (atom, newValue, data, skipOnSet = false) => {
@@ -936,9 +1020,9 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
936
1020
  handlePromise(atom, promise, currentValue, data, skipOnSet);
937
1021
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
938
1022
  initializedAtomsSet.add(atom);
939
- propagateUpdatedAtoms([...initializedAtomsSet], data);
1023
+ propagateAtomUpdate([...initializedAtomsSet], data);
940
1024
  } else {
941
- propagateUpdatedAtoms([atom], data);
1025
+ propagateAtomUpdate([atom], data);
942
1026
  }
943
1027
  return promise;
944
1028
  }
@@ -949,14 +1033,12 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
949
1033
  syncValue = setValueInData(atom, syncValue, data);
950
1034
  if (atom.onSet && !skipOnSet)
951
1035
  atom.onSet(syncValue, data);
952
- if (currentValue?.__isEmptyAtomPromise__) {
953
- currentValue.__resolveEmptyAtomPromise__(syncValue);
954
- }
1036
+ resolvePendingDefault(atom, data, syncValue);
955
1037
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
956
1038
  initializedAtomsSet.add(atom);
957
- propagateUpdatedAtoms([...initializedAtomsSet], data);
1039
+ propagateAtomUpdate([...initializedAtomsSet], data);
958
1040
  } else {
959
- propagateUpdatedAtoms([atom], data);
1041
+ propagateAtomUpdate([atom], data);
960
1042
  }
961
1043
  return syncValue;
962
1044
  };
@@ -964,12 +1046,11 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
964
1046
  // src/lib/initAtom.ts
965
1047
  var getAtomInitValue = (atom, data, initializedAtomsSet) => {
966
1048
  if (atom.defaultValue === undefined) {
967
- let promiseResolve;
968
- const promise = new Promise((resolve) => {
969
- promiseResolve = resolve;
1049
+ let resolve;
1050
+ const promise = new Promise((r) => {
1051
+ resolve = r;
970
1052
  });
971
- promise.__isEmptyAtomPromise__ = true;
972
- promise.__resolveEmptyAtomPromise__ = promiseResolve;
1053
+ data.pendingDefaults.set(atom, { promise, resolve });
973
1054
  return promise;
974
1055
  } else if (typeof atom.defaultValue === "function") {
975
1056
  const value = atom.defaultValue();
@@ -978,7 +1059,7 @@ var getAtomInitValue = (atom, data, initializedAtomsSet) => {
978
1059
  if (data.values.get(atom) !== value)
979
1060
  return;
980
1061
  setValueInData(atom, resolvedValue, data);
981
- propagateUpdatedAtoms([atom], data);
1062
+ propagateAtomUpdate([atom], data);
982
1063
  }, () => {
983
1064
  if (data.values.get(atom) === value) {
984
1065
  data.values.delete(atom);
@@ -1007,7 +1088,7 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
1007
1088
  if (data.values.has(state))
1008
1089
  return data.values.get(state);
1009
1090
  if (isAtom(state)) {
1010
- if ("parent" in data)
1091
+ if (data.parent)
1011
1092
  return getState(state, data.parent, initializedAtomsSet, circularDependencySet);
1012
1093
  if (isFamilyAtom(state)) {
1013
1094
  const familyValue = data.values.get(state.family);
@@ -1022,14 +1103,11 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
1022
1103
  return data.values.get(state);
1023
1104
  }
1024
1105
  if (isSelector(state)) {
1025
- if (_evalDepth >= MAX_EVAL_DEPTH) {
1026
- throw new NeedsInitError(state);
1027
- }
1028
1106
  initSelector(state, data, initializedAtomsSet, circularDependencySet);
1029
1107
  return data.values.get(state);
1030
1108
  }
1031
1109
  if (isAtomFamily(state)) {
1032
- if ("parent" in data) {
1110
+ if (data.parent) {
1033
1111
  const closestData = findClosestStoreWithAtomInitialized(state, data);
1034
1112
  return getState(state, closestData, initializedAtomsSet, circularDependencySet);
1035
1113
  }
@@ -1047,7 +1125,7 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
1047
1125
  throw new Error("Invalid object passed to get");
1048
1126
  }
1049
1127
  var findClosestStoreWithAtomInitialized = (atom, data) => {
1050
- if ("parent" in data === false)
1128
+ if (!data.parent)
1051
1129
  return data;
1052
1130
  if (data.values.has(atom))
1053
1131
  return data;
@@ -1077,16 +1155,16 @@ var resolveReactive = (value, data) => {
1077
1155
  // src/lib/createStoreData.ts
1078
1156
  var nextId = 0;
1079
1157
  var generateId = () => "__valdres_store_" + nextId++;
1080
- function makeLazyGetter(key) {
1158
+ function makeLazyGetter(key, factory = () => new WeakMap) {
1081
1159
  return {
1082
1160
  get() {
1083
- const map = new WeakMap;
1161
+ const value = factory();
1084
1162
  Object.defineProperty(this, key, {
1085
- value: map,
1163
+ value,
1086
1164
  writable: true,
1087
1165
  configurable: true
1088
1166
  });
1089
- return map;
1167
+ return value;
1090
1168
  },
1091
1169
  configurable: true
1092
1170
  };
@@ -1098,8 +1176,11 @@ Object.defineProperties(lazyProto, {
1098
1176
  stateDependents: makeLazyGetter("stateDependents"),
1099
1177
  stateDependencies: makeLazyGetter("stateDependencies"),
1100
1178
  mounts: makeLazyGetter("mounts"),
1179
+ liveDependentCount: makeLazyGetter("liveDependentCount"),
1101
1180
  abortControllers: makeLazyGetter("abortControllers"),
1102
- lastValueWriteAt: makeLazyGetter("lastValueWriteAt")
1181
+ lastValueWriteAt: makeLazyGetter("lastValueWriteAt"),
1182
+ circularDepSet: makeLazyGetter("circularDepSet", () => new WeakSet),
1183
+ latestEvalContext: makeLazyGetter("latestEvalContext")
1103
1184
  });
1104
1185
  function createStoreData(id, parent, options) {
1105
1186
  const data = Object.create(lazyProto);
@@ -1107,6 +1188,7 @@ function createStoreData(id, parent, options) {
1107
1188
  data.values = new WeakMap;
1108
1189
  data.scopes = new Map;
1109
1190
  data.scopeValueIndex = new WeakMap;
1191
+ data.pendingDefaults = new WeakMap;
1110
1192
  if (options?.batchUpdates) {
1111
1193
  data.batchUpdates = true;
1112
1194
  }
@@ -1133,10 +1215,10 @@ var resetAtom = (atom, data) => {
1133
1215
  let value = getAtomInitValue(atom, data, initializedAtomsSet);
1134
1216
  setValueInData(atom, value, data);
1135
1217
  if (!isPromiseLike(value)) {
1136
- propagateUpdatedAtoms([atom], data);
1218
+ propagateAtomUpdate([atom], data);
1137
1219
  }
1138
1220
  if (initializedAtomsSet.size > 0) {
1139
- throw new Error("Todo - propagateUpdatedAtoms on reset");
1221
+ throw new Error("Todo - propagateAtomUpdate on reset");
1140
1222
  }
1141
1223
  return value;
1142
1224
  };
@@ -1185,6 +1267,7 @@ var unsubscribe = (state, subscription, data) => {
1185
1267
  deleteMaxAgeCleanup(data, state);
1186
1268
  }
1187
1269
  data.subscriptions.delete(state);
1270
+ onLastDirectSubscriber(state, data);
1188
1271
  unmountOrphanedDeps(state, data);
1189
1272
  cleanupOrphanedDeps(state, data);
1190
1273
  }
@@ -1194,7 +1277,7 @@ var cleanupOrphanedDeps = (state, data, visited = new Set) => {
1194
1277
  if (visited.has(state))
1195
1278
  return;
1196
1279
  visited.add(state);
1197
- if (isTransitivelySubscribed(state, data))
1280
+ if (isLive(state, data))
1198
1281
  return;
1199
1282
  const deps = data.stateDependencies.get(state);
1200
1283
  if (deps) {
@@ -1223,7 +1306,7 @@ var initSubscribers = (state, data) => {
1223
1306
  return set;
1224
1307
  };
1225
1308
  var installMaxAgeTimer = (state, data) => {
1226
- if (!state.maxAge)
1309
+ if (state.maxAge === undefined)
1227
1310
  return;
1228
1311
  const globalState = isGlobalAtom(state) ? state : undefined;
1229
1312
  const existing = globalState?.maxAgeInterval;
@@ -1233,7 +1316,7 @@ var installMaxAgeTimer = (state, data) => {
1233
1316
  for (const s of globalState.stores) {
1234
1317
  if (s !== data && s.values.has(metaAtom2)) {
1235
1318
  setValueInData(metaAtom2, s.values.get(metaAtom2), data);
1236
- propagateUpdatedAtoms([metaAtom2], data);
1319
+ propagateAtomUpdate([metaAtom2], data);
1237
1320
  break;
1238
1321
  }
1239
1322
  }
@@ -1272,11 +1355,11 @@ var installMaxAgeTimer = (state, data) => {
1272
1355
  if (globalState) {
1273
1356
  for (const store of globalState.stores) {
1274
1357
  setValueInData(metaAtom, meta, store);
1275
- propagateUpdatedAtoms([metaAtom], store);
1358
+ propagateAtomUpdate([metaAtom], store);
1276
1359
  }
1277
1360
  } else {
1278
1361
  setValueInData(metaAtom, meta, data);
1279
- propagateUpdatedAtoms([metaAtom], data);
1362
+ propagateAtomUpdate([metaAtom], data);
1280
1363
  }
1281
1364
  };
1282
1365
  const isPastStaleIfErrorWindow = () => {
@@ -1287,11 +1370,11 @@ var installMaxAgeTimer = (state, data) => {
1287
1370
  if (globalState) {
1288
1371
  for (const store of globalState.stores) {
1289
1372
  setValueInData(atom, val, store);
1290
- propagateUpdatedAtoms([atom], store);
1373
+ propagateAtomUpdate([atom], store);
1291
1374
  }
1292
1375
  } else {
1293
1376
  setValueInData(atom, val, data);
1294
- propagateUpdatedAtoms([atom], data);
1377
+ propagateAtomUpdate([atom], data);
1295
1378
  }
1296
1379
  };
1297
1380
  const getValueStore = () => {
@@ -1421,7 +1504,7 @@ var installMaxAgeTimer = (state, data) => {
1421
1504
  };
1422
1505
  var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) => {
1423
1506
  let parentUnsubscribe;
1424
- if ("parent" in data && (!data.values.has(state) && isAtom(state) || isAtomFamily(state))) {
1507
+ if (data.parent && (!data.values.has(state) && isAtom(state) || isAtomFamily(state))) {
1425
1508
  const originalCallback = callback;
1426
1509
  parentUnsubscribe = subscribe(state, originalCallback, requireDeepEqualCheckBeforeCallback, data.parent);
1427
1510
  callback = (arg) => {
@@ -1460,10 +1543,11 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
1460
1543
  }
1461
1544
  subscribers.add(subscription);
1462
1545
  if (subscribers.size === 1) {
1463
- if (isAtom(state) && state.maxAge) {
1546
+ if (isAtom(state) && state.maxAge !== undefined && !data.parent) {
1464
1547
  installMaxAgeTimer(state, data);
1465
1548
  }
1466
1549
  if (!isFamily(state)) {
1550
+ onFirstDirectSubscriber(state, data);
1467
1551
  mountTransitiveDeps(state, data);
1468
1552
  }
1469
1553
  }
@@ -1479,7 +1563,7 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
1479
1563
  };
1480
1564
 
1481
1565
  // src/lib/setAtoms.ts
1482
- var setAtoms = (pairs, data, initializedAtomsSet) => {
1566
+ var setAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false) => {
1483
1567
  const updatedAtoms = [];
1484
1568
  for (let [atom, value] of pairs) {
1485
1569
  const currentValue = getState(atom, data, initializedAtomsSet);
@@ -1487,7 +1571,7 @@ var setAtoms = (pairs, data, initializedAtomsSet) => {
1487
1571
  if (!areEqual) {
1488
1572
  updatedAtoms.push(atom);
1489
1573
  value = setValueInData(atom, value, data);
1490
- if (atom.onSet)
1574
+ if (atom.onSet && !skipOnSet)
1491
1575
  atom.onSet(value, data);
1492
1576
  } else {
1493
1577
  setValueInData(atom, value, data);
@@ -1499,7 +1583,7 @@ var setAtoms = (pairs, data, initializedAtomsSet) => {
1499
1583
  }
1500
1584
  }
1501
1585
  if (updatedAtoms.length > 0) {
1502
- propagateUpdatedAtoms(updatedAtoms, data);
1586
+ propagateAtomUpdate(updatedAtoms, data);
1503
1587
  }
1504
1588
  };
1505
1589
 
@@ -1651,6 +1735,9 @@ class Transaction {
1651
1735
  };
1652
1736
  parentScope = (callback) => {
1653
1737
  if (!this.parentTransaction) {
1738
+ if (!this.data.parent) {
1739
+ throw new Error("Cannot access parentScope on root store");
1740
+ }
1654
1741
  this.parentTransaction = new Transaction(this.data.parent, undefined, this);
1655
1742
  }
1656
1743
  return this.parentTransaction.execute(callback, false);
@@ -1759,6 +1846,8 @@ var isCachedValueStale = (state, data) => {
1759
1846
  const maxAge = atom.maxAge;
1760
1847
  if (maxAge === undefined)
1761
1848
  return false;
1849
+ if (data.parent)
1850
+ return false;
1762
1851
  if (isGlobalAtom(atom)) {
1763
1852
  if (atom.maxAgeInterval !== undefined)
1764
1853
  return false;
@@ -1813,7 +1902,7 @@ function storeFromStoreData(data, detach) {
1813
1902
  if (_initSet.size) {
1814
1903
  const atoms = [..._initSet];
1815
1904
  _initSet.clear();
1816
- propagateUpdatedAtoms(atoms, data, undefined, undefined, false, undefined, false, true);
1905
+ propagateAtomUpdate(atoms, data, true);
1817
1906
  }
1818
1907
  }
1819
1908
  return res;
@@ -1874,11 +1963,13 @@ function storeFromStoreData(data, detach) {
1874
1963
  scopedStoreData = createStoreData(scopeId, data, data.batchUpdates ? { batchUpdates: true } : undefined);
1875
1964
  data.scopes.set(scopeId, scopedStoreData);
1876
1965
  }
1966
+ const consumers = scopedStoreData.scopeConsumers;
1967
+ const indexKeys = scopedStoreData.scopeIndexKeys;
1877
1968
  const detach2 = (expectedToDestroy = false) => {
1878
- scopedStoreData.scopeConsumers.delete(detach2);
1879
- if (scopedStoreData.scopeConsumers.size === 0) {
1969
+ consumers.delete(detach2);
1970
+ if (consumers.size === 0) {
1880
1971
  data.scopes.delete(scopeId);
1881
- for (const key of scopedStoreData.scopeIndexKeys) {
1972
+ for (const key of indexKeys) {
1882
1973
  const set2 = data.scopeValueIndex.get(key);
1883
1974
  if (set2) {
1884
1975
  set2.delete(scopedStoreData);
@@ -1886,15 +1977,15 @@ function storeFromStoreData(data, detach) {
1886
1977
  data.scopeValueIndex.delete(key);
1887
1978
  }
1888
1979
  }
1889
- scopedStoreData.scopeIndexKeys.clear();
1980
+ indexKeys.clear();
1890
1981
  return true;
1891
1982
  }
1892
1983
  if (expectedToDestroy) {
1893
- console.warn(`Scope ${scopeId} still has ${scopedStoreData.scopeConsumers.size} consumers, will not detach`);
1984
+ console.warn(`Scope ${scopeId} still has ${consumers.size} consumers, will not detach`);
1894
1985
  }
1895
1986
  return false;
1896
1987
  };
1897
- scopedStoreData.scopeConsumers.add(detach2);
1988
+ consumers.add(detach2);
1898
1989
  const newStore = storeFromStoreData(data.scopes.get(scopeId), detach2);
1899
1990
  return newStore;
1900
1991
  }
@@ -1926,25 +2017,83 @@ function storeFromStoreData(data, detach) {
1926
2017
  }
1927
2018
 
1928
2019
  // src/lib/mountAtom.ts
1929
- var isTransitivelySubscribed = (state, data, visited = new Set) => {
1930
- const stack = [state];
2020
+ var hasDirectSubscribers = (state, data) => {
2021
+ const subs = data.subscriptions.get(state);
2022
+ return !!subs && subs.size > 0;
2023
+ };
2024
+ var isLive = (state, data) => {
2025
+ if (hasDirectSubscribers(state, data))
2026
+ return true;
2027
+ const count = data.liveDependentCount.get(state);
2028
+ return !!count && count > 0;
2029
+ };
2030
+ var propagateLive = (root, data) => {
2031
+ const stack = [root];
1931
2032
  while (stack.length > 0) {
1932
2033
  const current = stack.pop();
1933
- if (visited.has(current))
2034
+ const deps = data.stateDependencies.get(current);
2035
+ if (!deps)
1934
2036
  continue;
1935
- visited.add(current);
1936
- const subs = data.subscriptions.get(current);
1937
- if (subs && subs.size > 0)
1938
- return true;
1939
- const dependents = data.stateDependents.get(current);
1940
- if (dependents) {
1941
- for (const dep of dependents) {
1942
- if (!visited.has(dep))
1943
- stack.push(dep);
2037
+ for (const dep of deps) {
2038
+ const prev = data.liveDependentCount.get(dep) ?? 0;
2039
+ data.liveDependentCount.set(dep, prev + 1);
2040
+ if (prev === 0 && !hasDirectSubscribers(dep, data)) {
2041
+ stack.push(dep);
1944
2042
  }
1945
2043
  }
1946
2044
  }
1947
- return false;
2045
+ };
2046
+ var propagateNotLive = (root, data) => {
2047
+ const stack = [root];
2048
+ while (stack.length > 0) {
2049
+ const current = stack.pop();
2050
+ const deps = data.stateDependencies.get(current);
2051
+ if (!deps)
2052
+ continue;
2053
+ for (const dep of deps) {
2054
+ const prev = data.liveDependentCount.get(dep) ?? 0;
2055
+ const next = prev - 1;
2056
+ if (next <= 0) {
2057
+ data.liveDependentCount.delete(dep);
2058
+ } else {
2059
+ data.liveDependentCount.set(dep, next);
2060
+ }
2061
+ if (prev === 1 && !hasDirectSubscribers(dep, data)) {
2062
+ stack.push(dep);
2063
+ }
2064
+ }
2065
+ }
2066
+ };
2067
+ var onFirstDirectSubscriber = (state, data) => {
2068
+ const liveDepCount = data.liveDependentCount.get(state) ?? 0;
2069
+ if (liveDepCount === 0) {
2070
+ propagateLive(state, data);
2071
+ }
2072
+ };
2073
+ var onLastDirectSubscriber = (state, data) => {
2074
+ const liveDepCount = data.liveDependentCount.get(state) ?? 0;
2075
+ if (liveDepCount === 0) {
2076
+ propagateNotLive(state, data);
2077
+ }
2078
+ };
2079
+ var onLiveDependencyAdded = (dep, data) => {
2080
+ const prev = data.liveDependentCount.get(dep) ?? 0;
2081
+ data.liveDependentCount.set(dep, prev + 1);
2082
+ if (prev === 0 && !hasDirectSubscribers(dep, data)) {
2083
+ propagateLive(dep, data);
2084
+ }
2085
+ };
2086
+ var onLiveDependencyRemoved = (dep, data) => {
2087
+ const prev = data.liveDependentCount.get(dep) ?? 0;
2088
+ const next = prev - 1;
2089
+ if (next <= 0) {
2090
+ data.liveDependentCount.delete(dep);
2091
+ } else {
2092
+ data.liveDependentCount.set(dep, next);
2093
+ }
2094
+ if (prev === 1 && !hasDirectSubscribers(dep, data)) {
2095
+ propagateNotLive(dep, data);
2096
+ }
1948
2097
  };
1949
2098
  var mountAtom = (state, data) => {
1950
2099
  const onMountFn = state.__valdresOnMount ?? state.onMount;
@@ -1954,7 +2103,9 @@ var mountAtom = (state, data) => {
1954
2103
  data.mounts.set(state, mountEntry);
1955
2104
  const store = data.storeRef ?? storeFromStoreData(data);
1956
2105
  try {
1957
- mountEntry.cleanup = onMountFn(store, state);
2106
+ const result = onMountFn(store, state);
2107
+ if (typeof result === "function")
2108
+ mountEntry.cleanup = result;
1958
2109
  } catch (error) {
1959
2110
  data.mounts.delete(state);
1960
2111
  throw error;
@@ -1969,14 +2120,20 @@ var unmountAtom = (state, data) => {
1969
2120
  mount.cleanup();
1970
2121
  }
1971
2122
  };
1972
- var mountTransitiveDeps = (state, data, visited = new Set) => {
2123
+ var mountTransitiveDeps = (state, data, visited) => {
2124
+ if (!state.__valdresOnMount && !state.onMount) {
2125
+ const deps = data.stateDependencies.get(state);
2126
+ if (!deps || deps.size === 0)
2127
+ return;
2128
+ }
2129
+ const seen = visited ?? new Set;
1973
2130
  let firstError = null;
1974
2131
  const stack = [state];
1975
2132
  while (stack.length > 0) {
1976
2133
  const current = stack.pop();
1977
- if (visited.has(current))
2134
+ if (seen.has(current))
1978
2135
  continue;
1979
- visited.add(current);
2136
+ seen.add(current);
1980
2137
  if (current.__valdresOnMount || current.onMount) {
1981
2138
  try {
1982
2139
  mountAtom(current, data);
@@ -1988,7 +2145,7 @@ var mountTransitiveDeps = (state, data, visited = new Set) => {
1988
2145
  const deps = data.stateDependencies.get(current);
1989
2146
  if (deps) {
1990
2147
  for (const dep of deps) {
1991
- if (!visited.has(dep))
2148
+ if (!seen.has(dep))
1992
2149
  stack.push(dep);
1993
2150
  }
1994
2151
  }
@@ -1997,16 +2154,22 @@ var mountTransitiveDeps = (state, data, visited = new Set) => {
1997
2154
  throw firstError.value;
1998
2155
  }
1999
2156
  };
2000
- var unmountOrphanedDeps = (state, data, visited = new Set) => {
2157
+ var unmountOrphanedDeps = (state, data, visited) => {
2158
+ if (!state.__valdresOnMount && !state.onMount) {
2159
+ const deps = data.stateDependencies.get(state);
2160
+ if (!deps || deps.size === 0)
2161
+ return;
2162
+ }
2163
+ const seen = visited ?? new Set;
2001
2164
  let firstError = null;
2002
2165
  const stack = [state];
2003
2166
  while (stack.length > 0) {
2004
2167
  const current = stack.pop();
2005
- if (visited.has(current))
2168
+ if (seen.has(current))
2006
2169
  continue;
2007
- visited.add(current);
2170
+ seen.add(current);
2008
2171
  if ((current.__valdresOnMount || current.onMount) && data.mounts.has(current)) {
2009
- if (!isTransitivelySubscribed(current, data)) {
2172
+ if (!isLive(current, data)) {
2010
2173
  try {
2011
2174
  unmountAtom(current, data);
2012
2175
  } catch (error) {
@@ -2018,7 +2181,7 @@ var unmountOrphanedDeps = (state, data, visited = new Set) => {
2018
2181
  const deps = data.stateDependencies.get(current);
2019
2182
  if (deps) {
2020
2183
  for (const dep of deps) {
2021
- if (!visited.has(dep))
2184
+ if (!seen.has(dep))
2022
2185
  stack.push(dep);
2023
2186
  }
2024
2187
  }
@@ -2045,8 +2208,7 @@ var globalStore = Object.assign(store("valdres-global-store"), {
2045
2208
  // src/lib/globalAtom.ts
2046
2209
  var globalAtom = (defaultValue, options) => {
2047
2210
  const stores = new Set;
2048
- if (options.onSet)
2049
- throw new Error("onSet on globalAtom is currently not supported");
2211
+ const userOnSet = options.onSet;
2050
2212
  const onInit = (setSelf2, data) => {
2051
2213
  setSelf2(globalStore.get(atom));
2052
2214
  stores.add(data);
@@ -2059,6 +2221,7 @@ var globalAtom = (defaultValue, options) => {
2059
2221
  }
2060
2222
  }
2061
2223
  }
2224
+ userOnSet?.(newValue, currentStore);
2062
2225
  };
2063
2226
  let mountCount = 0;
2064
2227
  let userCleanup;
@@ -2091,7 +2254,7 @@ var globalAtom = (defaultValue, options) => {
2091
2254
  const snapshot = [...stores];
2092
2255
  const subscribedStores = [];
2093
2256
  for (const s of snapshot) {
2094
- if (isTransitivelySubscribed(atom, s)) {
2257
+ if (isLive(atom, s)) {
2095
2258
  subscribedStores.push(s);
2096
2259
  }
2097
2260
  }
@@ -2116,14 +2279,14 @@ var globalAtom = (defaultValue, options) => {
2116
2279
  stores.delete(store2);
2117
2280
  store2.values.delete(atom);
2118
2281
  try {
2119
- propagateUpdatedAtoms([atom], store2);
2282
+ propagateAtomUpdate([atom], store2);
2120
2283
  } catch (e) {
2121
2284
  recordError(e);
2122
2285
  }
2123
2286
  }
2124
2287
  for (const s of subscribedStores) {
2125
2288
  stores.add(s);
2126
- if (atom.maxAge && (s.subscriptions.get(atom)?.size ?? 0) > 0) {
2289
+ if (atom.maxAge !== undefined && (s.subscriptions.get(atom)?.size ?? 0) > 0) {
2127
2290
  installMaxAgeTimer(atom, s);
2128
2291
  }
2129
2292
  try {
@@ -2149,9 +2312,7 @@ var globalAtom = (defaultValue, options) => {
2149
2312
  getSelf,
2150
2313
  resetSelf,
2151
2314
  detach,
2152
- get stores() {
2153
- return stores;
2154
- },
2315
+ stores,
2155
2316
  maxAgeInterval: undefined
2156
2317
  };
2157
2318
  return atom;
@@ -2247,11 +2408,7 @@ var createAtomFamily = (defaultValue, options) => {
2247
2408
  const isFunctionDefault = !isSelectorFamilyDefault && typeof defaultValue === "function";
2248
2409
  const hasName = !!options?.name;
2249
2410
  const isGlobal = !!options?.global;
2250
- const atomFamily = (...args) => {
2251
- const key = familyKey(args);
2252
- const cached = map.get(key);
2253
- if (cached !== undefined)
2254
- return cached;
2411
+ const build = (args, key) => {
2255
2412
  let dv;
2256
2413
  if (isSelectorFamilyDefault) {
2257
2414
  dv = defaultValue(...args);
@@ -2281,14 +2438,34 @@ var createAtomFamily = (defaultValue, options) => {
2281
2438
  map.set(key, familyAtom);
2282
2439
  return familyAtom;
2283
2440
  };
2441
+ function atomFamily(a0) {
2442
+ if (arguments.length === 1) {
2443
+ const t = typeof a0;
2444
+ if (t === "string" || t === "number" || t === "boolean") {
2445
+ const cached2 = map.get(a0);
2446
+ if (cached2 !== undefined)
2447
+ return cached2;
2448
+ return build([a0], a0);
2449
+ }
2450
+ }
2451
+ const args = Array.prototype.slice.call(arguments);
2452
+ const key = familyKey(args);
2453
+ const cached = map.get(key);
2454
+ if (cached !== undefined)
2455
+ return cached;
2456
+ return build(args, key);
2457
+ }
2284
2458
  if (hasName)
2285
2459
  Object.defineProperty(atomFamily, "name", {
2286
2460
  value: options.name,
2287
2461
  writable: false
2288
2462
  });
2289
- return Object.assign(atomFamily, {
2463
+ const callable = atomFamily;
2464
+ return Object.assign(callable, {
2290
2465
  __valdresAtomFamilyMap: map,
2291
- release: (...args) => map.delete(familyKey(args)),
2466
+ release: (...args) => {
2467
+ map.delete(familyKey(args));
2468
+ },
2292
2469
  equal
2293
2470
  });
2294
2471
  };
@@ -2389,11 +2566,10 @@ var selectorFamily = (callback, options) => {
2389
2566
  const cached = map.get(key);
2390
2567
  if (cached !== undefined)
2391
2568
  return cached;
2392
- const get = (selectorArgs) => callback(...args)(selectorArgs);
2393
2569
  const newSelector = {
2394
2570
  equal,
2395
2571
  ...options,
2396
- get,
2572
+ get: callback(...args),
2397
2573
  family: selectorFamily2,
2398
2574
  familyArgs: args,
2399
2575
  familyArgsStringified: key,
@@ -2416,9 +2592,9 @@ var isFamilySelector = (state) => isFamilyState(state) && isSelector(state);
2416
2592
 
2417
2593
  // src/index.ts
2418
2594
  if (globalThis.__valdres__) {
2419
- throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.3"}`);
2595
+ throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.4"}`);
2420
2596
  } else {
2421
- globalThis.__valdres__ = "1.0.0-beta.3";
2597
+ globalThis.__valdres__ = "1.0.0-beta.4";
2422
2598
  }
2423
2599
  export {
2424
2600
  store,