valdres 1.0.0-beta.2 → 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,21 +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
- const abortController = new AbortController;
434
- data.abortControllers.set(selector, abortController);
435
- options = { signal: abortController.signal, storeId: data.id };
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
+ }
446
+ }
447
+ return controller.signal;
448
+ }
449
+ };
450
+ }
436
451
  }
437
452
  let allDepsThisEval;
438
453
  let result;
@@ -457,8 +472,6 @@ var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencyS
457
472
  return value;
458
473
  }, options);
459
474
  } catch (error) {
460
- if (error instanceof NeedsInitError)
461
- throw error;
462
475
  if (error instanceof SuspendAndWaitForResolveError) {
463
476
  result = error;
464
477
  } else if (error instanceof SelectorEvaluationError) {
@@ -479,22 +492,28 @@ var evaluateSelector = (selector, data, initializedAtomsSet, circularDependencyS
479
492
  updatedDependencies.add(dep);
480
493
  }
481
494
  }
482
- const prev2 = currentDependencies ?? new Set;
495
+ const prev = currentDependencies ?? new Set;
483
496
  for (const state of updatedDependencies) {
484
- if (!prev2.has(state)) {
497
+ if (!prev.has(state)) {
485
498
  const set = getOrInitDependentsSet(state, data);
486
499
  set.add(selector);
487
- if (addedDepsOut)
488
- addedDepsOut.add(state);
500
+ if (depsChangeOut) {
501
+ if (!depsChangeOut.added)
502
+ depsChangeOut.added = new Set;
503
+ depsChangeOut.added.add(state);
504
+ }
489
505
  }
490
506
  }
491
507
  if (!isAsyncResult) {
492
- for (const state of prev2) {
508
+ for (const state of prev) {
493
509
  if (!updatedDependencies.has(state)) {
494
510
  const set = getOrInitDependentsSet(state, data);
495
511
  set.delete(selector);
496
- if (removedDepsOut)
497
- removedDepsOut.add(state);
512
+ if (depsChangeOut) {
513
+ if (!depsChangeOut.removed)
514
+ depsChangeOut.removed = new Set;
515
+ depsChangeOut.removed.add(state);
516
+ }
498
517
  }
499
518
  }
500
519
  }
@@ -521,7 +540,7 @@ var handleSelectorResult = (value, selector, data) => {
521
540
  const initializedAtomsSet = new Set;
522
541
  const res = initSelector(selector, data, initializedAtomsSet);
523
542
  if (initializedAtomsSet.size > 0) {
524
- propagateUpdatedAtoms([...initializedAtomsSet], data);
543
+ propagateAtomUpdate([...initializedAtomsSet], data);
525
544
  }
526
545
  return res;
527
546
  }).catch(() => {
@@ -543,12 +562,16 @@ var handleSelectorResult = (value, selector, data) => {
543
562
  pendingAsyncDeps.delete(value);
544
563
  const currentDeps = data.stateDependencies.get(selector);
545
564
  if (currentDeps) {
565
+ const selectorIsLive = isLive(selector, data);
546
566
  for (const dep of currentDeps) {
547
567
  if (!evalDeps.has(dep)) {
548
568
  currentDeps.delete(dep);
549
569
  const dependents2 = data.stateDependents.get(dep);
550
570
  if (dependents2)
551
571
  dependents2.delete(selector);
572
+ if (selectorIsLive) {
573
+ onLiveDependencyRemoved(dep, data);
574
+ }
552
575
  unmountOrphanedDeps(dep, data);
553
576
  }
554
577
  }
@@ -566,11 +589,13 @@ var handleSelectorResult = (value, selector, data) => {
566
589
  });
567
590
  return value;
568
591
  } else {
569
- data.abortControllers.set(selector, false);
592
+ if (selector.get.length >= 2) {
593
+ data.abortControllers.set(selector, false);
594
+ }
570
595
  return value;
571
596
  }
572
597
  };
573
- var initSelectorDirect = (selector, data, initializedAtomsSet, circularDependencySet) => {
598
+ var initSelector = (selector, data, initializedAtomsSet, circularDependencySet = data.circularDepSet) => {
574
599
  const existingValue = data.values.get(selector);
575
600
  const updatedValue = evaluate(selector, data, initializedAtomsSet, circularDependencySet);
576
601
  const areEqual = isPromiseLike(existingValue) || isPromiseLike(updatedValue) ? existingValue === updatedValue : selector.equal(existingValue, updatedValue);
@@ -581,56 +606,6 @@ var initSelectorDirect = (selector, data, initializedAtomsSet, circularDependenc
581
606
  return true;
582
607
  }
583
608
  };
584
- var initSelectorTrampoline = (selector, data, initializedAtomsSet, circularDependencySet) => {
585
- const stack = [selector];
586
- const inStack = new Set([selector]);
587
- while (stack.length > 0) {
588
- const current = stack[stack.length - 1];
589
- if (data.values.has(current)) {
590
- stack.pop();
591
- inStack.delete(current);
592
- continue;
593
- }
594
- try {
595
- initSelectorDirect(current, data, initializedAtomsSet, circularDependencySet);
596
- stack.pop();
597
- inStack.delete(current);
598
- } catch (e) {
599
- if (e instanceof NeedsInitError) {
600
- if (inStack.has(e.selector)) {
601
- throw new SelectorCircularDependencyError;
602
- }
603
- stack.push(e.selector);
604
- inStack.add(e.selector);
605
- } else {
606
- throw e;
607
- }
608
- }
609
- }
610
- };
611
- var initSelector = (selector, data, initializedAtomsSet, circularDependencySet = sharedCircularDepSet) => {
612
- const isTopLevel = _evalDepth === 0 && !_inTrampoline;
613
- _evalDepth++;
614
- const existingValue = data.values.get(selector);
615
- try {
616
- return initSelectorDirect(selector, data, initializedAtomsSet, circularDependencySet);
617
- } catch (e) {
618
- if (e instanceof NeedsInitError && isTopLevel) {
619
- _inTrampoline = true;
620
- try {
621
- initSelectorTrampoline(selector, data, initializedAtomsSet, circularDependencySet);
622
- } finally {
623
- _inTrampoline = false;
624
- }
625
- const newValue = data.values.get(selector);
626
- const areEqual = isPromiseLike(existingValue) || isPromiseLike(newValue) ? existingValue === newValue : selector.equal(existingValue, newValue);
627
- return !areEqual;
628
- }
629
- throw e;
630
- } finally {
631
- _evalDepth--;
632
- }
633
- };
634
609
  var evaluate = (selector, data, initializedAtomsSet, circularDependencySet) => {
635
610
  let tmpValue;
636
611
  try {
@@ -644,23 +619,18 @@ var evaluate = (selector, data, initializedAtomsSet, circularDependencySet) => {
644
619
  };
645
620
 
646
621
  // src/lib/propagateUpdatedAtoms.ts
647
- var reEvaluteSelector = (selector, data, updatedAtoms) => {
648
- const existingValue = data.values.get(selector);
649
- const addedDeps = new Set;
650
- const removedDeps = new Set;
622
+ var reEvaluateSelector = (selector, data, updatedAtoms, depsChange, existingValue) => {
651
623
  try {
652
- const rawValue = evaluateSelector(selector, data, updatedAtoms, undefined, addedDeps, removedDeps);
653
- const udpatedValue = handleSelectorResult(rawValue, selector, data);
654
- const areEqual = isPromiseLike(existingValue) || isPromiseLike(udpatedValue) ? existingValue === udpatedValue : selector.equal(existingValue, udpatedValue, updatedAtoms);
655
- if (areEqual) {
656
- return [false, false, undefined, addedDeps, removedDeps];
657
- } else {
658
- setValueInData(selector, udpatedValue, data);
659
- return [true, false, undefined, addedDeps, removedDeps];
660
- }
661
- } 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 {
662
632
  data.values.delete(selector);
663
- return [true, true, error, addedDeps, removedDeps];
633
+ return true;
664
634
  }
665
635
  };
666
636
  var callSubscribers = (subscriptions, families) => {
@@ -740,12 +710,12 @@ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, families = ne
740
710
  }
741
711
  }
742
712
  for (const [scope, familiesInScope] of scopeFamilies) {
743
- propagateUpdatedAtoms(familiesInScope, scope, undefined, undefined, false, timestamp, true);
713
+ propagateInScope(familiesInScope, scope);
744
714
  }
745
715
  }
746
716
  };
747
- var propagateUpdatedAtoms = (atoms, data, subscriptions, families, isRecursive = false, timestamp = performance.now(), selectorsOnly = false, isInitOnly = false) => {
748
- if (atoms.length === 1 && !isRecursive && !selectorsOnly) {
717
+ var propagateAtomUpdate = (atoms, data, isInitOnly = false) => {
718
+ if (atoms.length === 1) {
749
719
  const atom = atoms[0];
750
720
  if (!isFamilyAtom(atom) && !isAtomFamily(atom)) {
751
721
  const dependents = data.stateDependents.get(atom);
@@ -758,24 +728,21 @@ var propagateUpdatedAtoms = (atoms, data, subscriptions, families, isRecursive =
758
728
  }
759
729
  }
760
730
  }
761
- if (!subscriptions)
762
- subscriptions = new Set;
763
- if (!families)
764
- families = new Map;
731
+ const subscriptions = new Set;
732
+ const families = new Map;
765
733
  const selectors = new Set;
766
734
  for (const atom of atoms) {
767
735
  addSetToSet(data.stateDependents.get(atom), selectors);
768
- if (!selectorsOnly) {
769
- addSetToSet(data.subscriptions.get(atom), subscriptions);
770
- if (isFamilyAtom(atom)) {
771
- if (!families.has(atom.family)) {
772
- families.set(atom.family, new Set);
773
- }
774
- 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);
775
740
  }
741
+ families.get(atom.family).add(atom);
776
742
  }
777
743
  }
778
744
  if (families.size > 0) {
745
+ const timestamp = performance.now();
779
746
  for (const [family, familyAtoms] of families) {
780
747
  addSetToSet(data.stateDependents.get(family), selectors);
781
748
  addSetToSet(data.subscriptions.get(family), subscriptions);
@@ -784,55 +751,68 @@ var propagateUpdatedAtoms = (atoms, data, subscriptions, families, isRecursive =
784
751
  addFamilyAtomsToSet(family, familyAtoms, data, timestamp);
785
752
  }
786
753
  }
787
- if (!isRecursive) {
788
- propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly);
789
- if (data.scopes && data.scopes.size > 0) {
790
- if (atoms.length === 1) {
791
- const atom = atoms[0];
792
- const shadowingScopes = isAtomFamily(atom) ? undefined : data.scopeValueIndex.get(atom);
793
- for (const [, scope] of data.scopes) {
794
- if (!shadowingScopes || !shadowingScopes.has(scope)) {
795
- propagateUpdatedAtoms(atoms, scope, undefined, undefined, false, timestamp, true, isInitOnly);
796
- }
797
- }
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);
798
806
  } else {
799
- let anyShadowed = false;
800
- let atomShadows;
801
- for (const atom of atoms) {
802
- if (!isAtomFamily(atom)) {
803
- const s = data.scopeValueIndex.get(atom);
804
- if (s && s.size > 0) {
805
- if (!atomShadows)
806
- atomShadows = new Map;
807
- atomShadows.set(atom, s);
808
- anyShadowed = true;
809
- }
810
- }
811
- }
812
- if (!anyShadowed) {
813
- for (const [, scope] of data.scopes) {
814
- propagateUpdatedAtoms(atoms, scope, undefined, undefined, false, timestamp, true, isInitOnly);
815
- }
816
- } else {
817
- for (const [, scope] of data.scopes) {
818
- const atomsToUpdateInScope = [];
819
- for (const atom of atoms) {
820
- if (isAtomFamily(atom)) {
821
- atomsToUpdateInScope.push(atom);
822
- } else {
823
- const s = atomShadows.get(atom);
824
- if (!s || !s.has(scope)) {
825
- atomsToUpdateInScope.push(atom);
826
- }
827
- }
828
- }
829
- if (atomsToUpdateInScope.length > 0) {
830
- propagateUpdatedAtoms(atomsToUpdateInScope, scope, undefined, undefined, false, timestamp, true, isInitOnly);
831
- }
832
- }
807
+ const s = atomShadows.get(atom);
808
+ if (!s || !s.has(scope)) {
809
+ atomsToUpdateInScope.push(atom);
833
810
  }
834
811
  }
835
812
  }
813
+ if (atomsToUpdateInScope.length > 0) {
814
+ propagateInScope(atomsToUpdateInScope, scope, isInitOnly);
815
+ }
836
816
  }
837
817
  };
838
818
  var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false) => {
@@ -844,36 +824,147 @@ var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, fam
844
824
  callSubscribers(subscriptions, families);
845
825
  }
846
826
  };
847
- var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false) => {
848
- let currentSelectors = selectors;
849
- while (currentSelectors.size > 0) {
850
- const selectorsForNextPass = new Set;
851
- for (const selector of currentSelectors) {
852
- const currentValue = data.values.get(selector);
853
- 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))
854
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
+ }
855
906
  }
856
- const dependents = data.stateDependents.get(selector);
857
- const subscribers = data.subscriptions.get(selector);
858
- if (!isPromiseLike(currentValue) && (!dependents || dependents.size === 0) && (!subscribers || subscribers.size === 0)) {
859
- data.values.delete(selector);
860
- } else {
861
- const [wasValueUpdated, didEvalCrash, error, addedDeps, removedDeps] = reEvaluteSelector(selector, data, updatedInitializedAtoms);
862
- if ((addedDeps.size > 0 || removedDeps.size > 0) && isTransitivelySubscribed(selector, data)) {
863
- for (const dep of addedDeps) {
864
- mountTransitiveDeps(dep, data);
865
- }
866
- for (const dep of removedDeps) {
867
- unmountOrphanedDeps(dep, data);
868
- }
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);
869
951
  }
870
- if (!wasValueUpdated)
871
- continue;
872
- addSetToSet(data.stateDependents.get(selector), selectorsForNextPass);
873
- addSetToSet(subscribers, collectedSubscribers);
874
952
  }
875
953
  }
876
- 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);
877
968
  }
878
969
  };
879
970
 
@@ -881,11 +972,19 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
881
972
  var isFunction = (value) => typeof value === "function";
882
973
 
883
974
  // src/lib/setAtom.ts
884
- var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
885
- const emptyAtomPromise = currentValue?.__isEmptyAtomPromise__ ? currentValue : currentValue?.__emptyAtomPromiseOrigin__ ?? null;
886
- if (emptyAtomPromise) {
887
- 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;
888
985
  }
986
+ };
987
+ var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
889
988
  setValueInData(atom, promise, data);
890
989
  promise.then((resolvedValue) => {
891
990
  if (data.values.get(atom) !== promise)
@@ -893,15 +992,13 @@ var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
893
992
  setValueInData(atom, resolvedValue, data);
894
993
  if (atom.onSet && !skipOnSet)
895
994
  atom.onSet(resolvedValue, data);
896
- if (emptyAtomPromise) {
897
- emptyAtomPromise.__resolveEmptyAtomPromise__(resolvedValue);
898
- }
899
- propagateUpdatedAtoms([atom], data);
995
+ resolvePendingDefault(atom, data, resolvedValue);
996
+ propagateAtomUpdate([atom], data);
900
997
  }).catch(() => {
901
998
  if (data.values.get(atom) !== promise)
902
999
  return;
903
1000
  setValueInData(atom, currentValue, data);
904
- propagateUpdatedAtoms([atom], data);
1001
+ propagateAtomUpdate([atom], data);
905
1002
  });
906
1003
  };
907
1004
  var setAtom = (atom, newValue, data, skipOnSet = false) => {
@@ -923,9 +1020,9 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
923
1020
  handlePromise(atom, promise, currentValue, data, skipOnSet);
924
1021
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
925
1022
  initializedAtomsSet.add(atom);
926
- propagateUpdatedAtoms([...initializedAtomsSet], data);
1023
+ propagateAtomUpdate([...initializedAtomsSet], data);
927
1024
  } else {
928
- propagateUpdatedAtoms([atom], data);
1025
+ propagateAtomUpdate([atom], data);
929
1026
  }
930
1027
  return promise;
931
1028
  }
@@ -936,14 +1033,12 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
936
1033
  syncValue = setValueInData(atom, syncValue, data);
937
1034
  if (atom.onSet && !skipOnSet)
938
1035
  atom.onSet(syncValue, data);
939
- if (currentValue?.__isEmptyAtomPromise__) {
940
- currentValue.__resolveEmptyAtomPromise__(syncValue);
941
- }
1036
+ resolvePendingDefault(atom, data, syncValue);
942
1037
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
943
1038
  initializedAtomsSet.add(atom);
944
- propagateUpdatedAtoms([...initializedAtomsSet], data);
1039
+ propagateAtomUpdate([...initializedAtomsSet], data);
945
1040
  } else {
946
- propagateUpdatedAtoms([atom], data);
1041
+ propagateAtomUpdate([atom], data);
947
1042
  }
948
1043
  return syncValue;
949
1044
  };
@@ -951,12 +1046,11 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
951
1046
  // src/lib/initAtom.ts
952
1047
  var getAtomInitValue = (atom, data, initializedAtomsSet) => {
953
1048
  if (atom.defaultValue === undefined) {
954
- let promiseResolve;
955
- const promise = new Promise((resolve) => {
956
- promiseResolve = resolve;
1049
+ let resolve;
1050
+ const promise = new Promise((r) => {
1051
+ resolve = r;
957
1052
  });
958
- promise.__isEmptyAtomPromise__ = true;
959
- promise.__resolveEmptyAtomPromise__ = promiseResolve;
1053
+ data.pendingDefaults.set(atom, { promise, resolve });
960
1054
  return promise;
961
1055
  } else if (typeof atom.defaultValue === "function") {
962
1056
  const value = atom.defaultValue();
@@ -965,7 +1059,7 @@ var getAtomInitValue = (atom, data, initializedAtomsSet) => {
965
1059
  if (data.values.get(atom) !== value)
966
1060
  return;
967
1061
  setValueInData(atom, resolvedValue, data);
968
- propagateUpdatedAtoms([atom], data);
1062
+ propagateAtomUpdate([atom], data);
969
1063
  }, () => {
970
1064
  if (data.values.get(atom) === value) {
971
1065
  data.values.delete(atom);
@@ -994,7 +1088,7 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
994
1088
  if (data.values.has(state))
995
1089
  return data.values.get(state);
996
1090
  if (isAtom(state)) {
997
- if ("parent" in data)
1091
+ if (data.parent)
998
1092
  return getState(state, data.parent, initializedAtomsSet, circularDependencySet);
999
1093
  if (isFamilyAtom(state)) {
1000
1094
  const familyValue = data.values.get(state.family);
@@ -1009,14 +1103,11 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
1009
1103
  return data.values.get(state);
1010
1104
  }
1011
1105
  if (isSelector(state)) {
1012
- if (_evalDepth >= MAX_EVAL_DEPTH) {
1013
- throw new NeedsInitError(state);
1014
- }
1015
1106
  initSelector(state, data, initializedAtomsSet, circularDependencySet);
1016
1107
  return data.values.get(state);
1017
1108
  }
1018
1109
  if (isAtomFamily(state)) {
1019
- if ("parent" in data) {
1110
+ if (data.parent) {
1020
1111
  const closestData = findClosestStoreWithAtomInitialized(state, data);
1021
1112
  return getState(state, closestData, initializedAtomsSet, circularDependencySet);
1022
1113
  }
@@ -1034,7 +1125,7 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
1034
1125
  throw new Error("Invalid object passed to get");
1035
1126
  }
1036
1127
  var findClosestStoreWithAtomInitialized = (atom, data) => {
1037
- if ("parent" in data === false)
1128
+ if (!data.parent)
1038
1129
  return data;
1039
1130
  if (data.values.has(atom))
1040
1131
  return data;
@@ -1064,16 +1155,16 @@ var resolveReactive = (value, data) => {
1064
1155
  // src/lib/createStoreData.ts
1065
1156
  var nextId = 0;
1066
1157
  var generateId = () => "__valdres_store_" + nextId++;
1067
- function makeLazyGetter(key) {
1158
+ function makeLazyGetter(key, factory = () => new WeakMap) {
1068
1159
  return {
1069
1160
  get() {
1070
- const map = new WeakMap;
1161
+ const value = factory();
1071
1162
  Object.defineProperty(this, key, {
1072
- value: map,
1163
+ value,
1073
1164
  writable: true,
1074
1165
  configurable: true
1075
1166
  });
1076
- return map;
1167
+ return value;
1077
1168
  },
1078
1169
  configurable: true
1079
1170
  };
@@ -1085,8 +1176,11 @@ Object.defineProperties(lazyProto, {
1085
1176
  stateDependents: makeLazyGetter("stateDependents"),
1086
1177
  stateDependencies: makeLazyGetter("stateDependencies"),
1087
1178
  mounts: makeLazyGetter("mounts"),
1179
+ liveDependentCount: makeLazyGetter("liveDependentCount"),
1088
1180
  abortControllers: makeLazyGetter("abortControllers"),
1089
- lastValueWriteAt: makeLazyGetter("lastValueWriteAt")
1181
+ lastValueWriteAt: makeLazyGetter("lastValueWriteAt"),
1182
+ circularDepSet: makeLazyGetter("circularDepSet", () => new WeakSet),
1183
+ latestEvalContext: makeLazyGetter("latestEvalContext")
1090
1184
  });
1091
1185
  function createStoreData(id, parent, options) {
1092
1186
  const data = Object.create(lazyProto);
@@ -1094,6 +1188,7 @@ function createStoreData(id, parent, options) {
1094
1188
  data.values = new WeakMap;
1095
1189
  data.scopes = new Map;
1096
1190
  data.scopeValueIndex = new WeakMap;
1191
+ data.pendingDefaults = new WeakMap;
1097
1192
  if (options?.batchUpdates) {
1098
1193
  data.batchUpdates = true;
1099
1194
  }
@@ -1120,10 +1215,10 @@ var resetAtom = (atom, data) => {
1120
1215
  let value = getAtomInitValue(atom, data, initializedAtomsSet);
1121
1216
  setValueInData(atom, value, data);
1122
1217
  if (!isPromiseLike(value)) {
1123
- propagateUpdatedAtoms([atom], data);
1218
+ propagateAtomUpdate([atom], data);
1124
1219
  }
1125
1220
  if (initializedAtomsSet.size > 0) {
1126
- throw new Error("Todo - propagateUpdatedAtoms on reset");
1221
+ throw new Error("Todo - propagateAtomUpdate on reset");
1127
1222
  }
1128
1223
  return value;
1129
1224
  };
@@ -1172,6 +1267,7 @@ var unsubscribe = (state, subscription, data) => {
1172
1267
  deleteMaxAgeCleanup(data, state);
1173
1268
  }
1174
1269
  data.subscriptions.delete(state);
1270
+ onLastDirectSubscriber(state, data);
1175
1271
  unmountOrphanedDeps(state, data);
1176
1272
  cleanupOrphanedDeps(state, data);
1177
1273
  }
@@ -1181,7 +1277,7 @@ var cleanupOrphanedDeps = (state, data, visited = new Set) => {
1181
1277
  if (visited.has(state))
1182
1278
  return;
1183
1279
  visited.add(state);
1184
- if (isTransitivelySubscribed(state, data))
1280
+ if (isLive(state, data))
1185
1281
  return;
1186
1282
  const deps = data.stateDependencies.get(state);
1187
1283
  if (deps) {
@@ -1210,7 +1306,7 @@ var initSubscribers = (state, data) => {
1210
1306
  return set;
1211
1307
  };
1212
1308
  var installMaxAgeTimer = (state, data) => {
1213
- if (!state.maxAge)
1309
+ if (state.maxAge === undefined)
1214
1310
  return;
1215
1311
  const globalState = isGlobalAtom(state) ? state : undefined;
1216
1312
  const existing = globalState?.maxAgeInterval;
@@ -1220,7 +1316,7 @@ var installMaxAgeTimer = (state, data) => {
1220
1316
  for (const s of globalState.stores) {
1221
1317
  if (s !== data && s.values.has(metaAtom2)) {
1222
1318
  setValueInData(metaAtom2, s.values.get(metaAtom2), data);
1223
- propagateUpdatedAtoms([metaAtom2], data);
1319
+ propagateAtomUpdate([metaAtom2], data);
1224
1320
  break;
1225
1321
  }
1226
1322
  }
@@ -1259,11 +1355,11 @@ var installMaxAgeTimer = (state, data) => {
1259
1355
  if (globalState) {
1260
1356
  for (const store of globalState.stores) {
1261
1357
  setValueInData(metaAtom, meta, store);
1262
- propagateUpdatedAtoms([metaAtom], store);
1358
+ propagateAtomUpdate([metaAtom], store);
1263
1359
  }
1264
1360
  } else {
1265
1361
  setValueInData(metaAtom, meta, data);
1266
- propagateUpdatedAtoms([metaAtom], data);
1362
+ propagateAtomUpdate([metaAtom], data);
1267
1363
  }
1268
1364
  };
1269
1365
  const isPastStaleIfErrorWindow = () => {
@@ -1274,11 +1370,11 @@ var installMaxAgeTimer = (state, data) => {
1274
1370
  if (globalState) {
1275
1371
  for (const store of globalState.stores) {
1276
1372
  setValueInData(atom, val, store);
1277
- propagateUpdatedAtoms([atom], store);
1373
+ propagateAtomUpdate([atom], store);
1278
1374
  }
1279
1375
  } else {
1280
1376
  setValueInData(atom, val, data);
1281
- propagateUpdatedAtoms([atom], data);
1377
+ propagateAtomUpdate([atom], data);
1282
1378
  }
1283
1379
  };
1284
1380
  const getValueStore = () => {
@@ -1408,7 +1504,7 @@ var installMaxAgeTimer = (state, data) => {
1408
1504
  };
1409
1505
  var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) => {
1410
1506
  let parentUnsubscribe;
1411
- if ("parent" in data && (!data.values.has(state) && isAtom(state) || isAtomFamily(state))) {
1507
+ if (data.parent && (!data.values.has(state) && isAtom(state) || isAtomFamily(state))) {
1412
1508
  const originalCallback = callback;
1413
1509
  parentUnsubscribe = subscribe(state, originalCallback, requireDeepEqualCheckBeforeCallback, data.parent);
1414
1510
  callback = (arg) => {
@@ -1447,10 +1543,11 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
1447
1543
  }
1448
1544
  subscribers.add(subscription);
1449
1545
  if (subscribers.size === 1) {
1450
- if (isAtom(state) && state.maxAge) {
1546
+ if (isAtom(state) && state.maxAge !== undefined && !data.parent) {
1451
1547
  installMaxAgeTimer(state, data);
1452
1548
  }
1453
1549
  if (!isFamily(state)) {
1550
+ onFirstDirectSubscriber(state, data);
1454
1551
  mountTransitiveDeps(state, data);
1455
1552
  }
1456
1553
  }
@@ -1466,7 +1563,7 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
1466
1563
  };
1467
1564
 
1468
1565
  // src/lib/setAtoms.ts
1469
- var setAtoms = (pairs, data, initializedAtomsSet) => {
1566
+ var setAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false) => {
1470
1567
  const updatedAtoms = [];
1471
1568
  for (let [atom, value] of pairs) {
1472
1569
  const currentValue = getState(atom, data, initializedAtomsSet);
@@ -1474,7 +1571,7 @@ var setAtoms = (pairs, data, initializedAtomsSet) => {
1474
1571
  if (!areEqual) {
1475
1572
  updatedAtoms.push(atom);
1476
1573
  value = setValueInData(atom, value, data);
1477
- if (atom.onSet)
1574
+ if (atom.onSet && !skipOnSet)
1478
1575
  atom.onSet(value, data);
1479
1576
  } else {
1480
1577
  setValueInData(atom, value, data);
@@ -1486,7 +1583,7 @@ var setAtoms = (pairs, data, initializedAtomsSet) => {
1486
1583
  }
1487
1584
  }
1488
1585
  if (updatedAtoms.length > 0) {
1489
- propagateUpdatedAtoms(updatedAtoms, data);
1586
+ propagateAtomUpdate(updatedAtoms, data);
1490
1587
  }
1491
1588
  };
1492
1589
 
@@ -1638,6 +1735,9 @@ class Transaction {
1638
1735
  };
1639
1736
  parentScope = (callback) => {
1640
1737
  if (!this.parentTransaction) {
1738
+ if (!this.data.parent) {
1739
+ throw new Error("Cannot access parentScope on root store");
1740
+ }
1641
1741
  this.parentTransaction = new Transaction(this.data.parent, undefined, this);
1642
1742
  }
1643
1743
  return this.parentTransaction.execute(callback, false);
@@ -1746,6 +1846,8 @@ var isCachedValueStale = (state, data) => {
1746
1846
  const maxAge = atom.maxAge;
1747
1847
  if (maxAge === undefined)
1748
1848
  return false;
1849
+ if (data.parent)
1850
+ return false;
1749
1851
  if (isGlobalAtom(atom)) {
1750
1852
  if (atom.maxAgeInterval !== undefined)
1751
1853
  return false;
@@ -1800,7 +1902,7 @@ function storeFromStoreData(data, detach) {
1800
1902
  if (_initSet.size) {
1801
1903
  const atoms = [..._initSet];
1802
1904
  _initSet.clear();
1803
- propagateUpdatedAtoms(atoms, data, undefined, undefined, false, undefined, false, true);
1905
+ propagateAtomUpdate(atoms, data, true);
1804
1906
  }
1805
1907
  }
1806
1908
  return res;
@@ -1861,11 +1963,13 @@ function storeFromStoreData(data, detach) {
1861
1963
  scopedStoreData = createStoreData(scopeId, data, data.batchUpdates ? { batchUpdates: true } : undefined);
1862
1964
  data.scopes.set(scopeId, scopedStoreData);
1863
1965
  }
1966
+ const consumers = scopedStoreData.scopeConsumers;
1967
+ const indexKeys = scopedStoreData.scopeIndexKeys;
1864
1968
  const detach2 = (expectedToDestroy = false) => {
1865
- scopedStoreData.scopeConsumers.delete(detach2);
1866
- if (scopedStoreData.scopeConsumers.size === 0) {
1969
+ consumers.delete(detach2);
1970
+ if (consumers.size === 0) {
1867
1971
  data.scopes.delete(scopeId);
1868
- for (const key of scopedStoreData.scopeIndexKeys) {
1972
+ for (const key of indexKeys) {
1869
1973
  const set2 = data.scopeValueIndex.get(key);
1870
1974
  if (set2) {
1871
1975
  set2.delete(scopedStoreData);
@@ -1873,15 +1977,15 @@ function storeFromStoreData(data, detach) {
1873
1977
  data.scopeValueIndex.delete(key);
1874
1978
  }
1875
1979
  }
1876
- scopedStoreData.scopeIndexKeys.clear();
1980
+ indexKeys.clear();
1877
1981
  return true;
1878
1982
  }
1879
1983
  if (expectedToDestroy) {
1880
- 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`);
1881
1985
  }
1882
1986
  return false;
1883
1987
  };
1884
- scopedStoreData.scopeConsumers.add(detach2);
1988
+ consumers.add(detach2);
1885
1989
  const newStore = storeFromStoreData(data.scopes.get(scopeId), detach2);
1886
1990
  return newStore;
1887
1991
  }
@@ -1913,25 +2017,83 @@ function storeFromStoreData(data, detach) {
1913
2017
  }
1914
2018
 
1915
2019
  // src/lib/mountAtom.ts
1916
- var isTransitivelySubscribed = (state, data, visited = new Set) => {
1917
- 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];
1918
2032
  while (stack.length > 0) {
1919
2033
  const current = stack.pop();
1920
- if (visited.has(current))
2034
+ const deps = data.stateDependencies.get(current);
2035
+ if (!deps)
1921
2036
  continue;
1922
- visited.add(current);
1923
- const subs = data.subscriptions.get(current);
1924
- if (subs && subs.size > 0)
1925
- return true;
1926
- const dependents = data.stateDependents.get(current);
1927
- if (dependents) {
1928
- for (const dep of dependents) {
1929
- if (!visited.has(dep))
1930
- 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);
1931
2042
  }
1932
2043
  }
1933
2044
  }
1934
- 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
+ }
1935
2097
  };
1936
2098
  var mountAtom = (state, data) => {
1937
2099
  const onMountFn = state.__valdresOnMount ?? state.onMount;
@@ -1941,7 +2103,9 @@ var mountAtom = (state, data) => {
1941
2103
  data.mounts.set(state, mountEntry);
1942
2104
  const store = data.storeRef ?? storeFromStoreData(data);
1943
2105
  try {
1944
- mountEntry.cleanup = onMountFn(store, state);
2106
+ const result = onMountFn(store, state);
2107
+ if (typeof result === "function")
2108
+ mountEntry.cleanup = result;
1945
2109
  } catch (error) {
1946
2110
  data.mounts.delete(state);
1947
2111
  throw error;
@@ -1956,14 +2120,20 @@ var unmountAtom = (state, data) => {
1956
2120
  mount.cleanup();
1957
2121
  }
1958
2122
  };
1959
- 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;
1960
2130
  let firstError = null;
1961
2131
  const stack = [state];
1962
2132
  while (stack.length > 0) {
1963
2133
  const current = stack.pop();
1964
- if (visited.has(current))
2134
+ if (seen.has(current))
1965
2135
  continue;
1966
- visited.add(current);
2136
+ seen.add(current);
1967
2137
  if (current.__valdresOnMount || current.onMount) {
1968
2138
  try {
1969
2139
  mountAtom(current, data);
@@ -1975,7 +2145,7 @@ var mountTransitiveDeps = (state, data, visited = new Set) => {
1975
2145
  const deps = data.stateDependencies.get(current);
1976
2146
  if (deps) {
1977
2147
  for (const dep of deps) {
1978
- if (!visited.has(dep))
2148
+ if (!seen.has(dep))
1979
2149
  stack.push(dep);
1980
2150
  }
1981
2151
  }
@@ -1984,16 +2154,22 @@ var mountTransitiveDeps = (state, data, visited = new Set) => {
1984
2154
  throw firstError.value;
1985
2155
  }
1986
2156
  };
1987
- 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;
1988
2164
  let firstError = null;
1989
2165
  const stack = [state];
1990
2166
  while (stack.length > 0) {
1991
2167
  const current = stack.pop();
1992
- if (visited.has(current))
2168
+ if (seen.has(current))
1993
2169
  continue;
1994
- visited.add(current);
2170
+ seen.add(current);
1995
2171
  if ((current.__valdresOnMount || current.onMount) && data.mounts.has(current)) {
1996
- if (!isTransitivelySubscribed(current, data)) {
2172
+ if (!isLive(current, data)) {
1997
2173
  try {
1998
2174
  unmountAtom(current, data);
1999
2175
  } catch (error) {
@@ -2005,7 +2181,7 @@ var unmountOrphanedDeps = (state, data, visited = new Set) => {
2005
2181
  const deps = data.stateDependencies.get(current);
2006
2182
  if (deps) {
2007
2183
  for (const dep of deps) {
2008
- if (!visited.has(dep))
2184
+ if (!seen.has(dep))
2009
2185
  stack.push(dep);
2010
2186
  }
2011
2187
  }
@@ -2032,8 +2208,7 @@ var globalStore = Object.assign(store("valdres-global-store"), {
2032
2208
  // src/lib/globalAtom.ts
2033
2209
  var globalAtom = (defaultValue, options) => {
2034
2210
  const stores = new Set;
2035
- if (options.onSet)
2036
- throw new Error("onSet on globalAtom is currently not supported");
2211
+ const userOnSet = options.onSet;
2037
2212
  const onInit = (setSelf2, data) => {
2038
2213
  setSelf2(globalStore.get(atom));
2039
2214
  stores.add(data);
@@ -2046,6 +2221,7 @@ var globalAtom = (defaultValue, options) => {
2046
2221
  }
2047
2222
  }
2048
2223
  }
2224
+ userOnSet?.(newValue, currentStore);
2049
2225
  };
2050
2226
  let mountCount = 0;
2051
2227
  let userCleanup;
@@ -2078,7 +2254,7 @@ var globalAtom = (defaultValue, options) => {
2078
2254
  const snapshot = [...stores];
2079
2255
  const subscribedStores = [];
2080
2256
  for (const s of snapshot) {
2081
- if (isTransitivelySubscribed(atom, s)) {
2257
+ if (isLive(atom, s)) {
2082
2258
  subscribedStores.push(s);
2083
2259
  }
2084
2260
  }
@@ -2103,14 +2279,14 @@ var globalAtom = (defaultValue, options) => {
2103
2279
  stores.delete(store2);
2104
2280
  store2.values.delete(atom);
2105
2281
  try {
2106
- propagateUpdatedAtoms([atom], store2);
2282
+ propagateAtomUpdate([atom], store2);
2107
2283
  } catch (e) {
2108
2284
  recordError(e);
2109
2285
  }
2110
2286
  }
2111
2287
  for (const s of subscribedStores) {
2112
2288
  stores.add(s);
2113
- if (atom.maxAge && (s.subscriptions.get(atom)?.size ?? 0) > 0) {
2289
+ if (atom.maxAge !== undefined && (s.subscriptions.get(atom)?.size ?? 0) > 0) {
2114
2290
  installMaxAgeTimer(atom, s);
2115
2291
  }
2116
2292
  try {
@@ -2136,9 +2312,7 @@ var globalAtom = (defaultValue, options) => {
2136
2312
  getSelf,
2137
2313
  resetSelf,
2138
2314
  detach,
2139
- get stores() {
2140
- return stores;
2141
- },
2315
+ stores,
2142
2316
  maxAgeInterval: undefined
2143
2317
  };
2144
2318
  return atom;
@@ -2234,11 +2408,7 @@ var createAtomFamily = (defaultValue, options) => {
2234
2408
  const isFunctionDefault = !isSelectorFamilyDefault && typeof defaultValue === "function";
2235
2409
  const hasName = !!options?.name;
2236
2410
  const isGlobal = !!options?.global;
2237
- const atomFamily = (...args) => {
2238
- const key = familyKey(args);
2239
- const cached = map.get(key);
2240
- if (cached !== undefined)
2241
- return cached;
2411
+ const build = (args, key) => {
2242
2412
  let dv;
2243
2413
  if (isSelectorFamilyDefault) {
2244
2414
  dv = defaultValue(...args);
@@ -2268,14 +2438,34 @@ var createAtomFamily = (defaultValue, options) => {
2268
2438
  map.set(key, familyAtom);
2269
2439
  return familyAtom;
2270
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
+ }
2271
2458
  if (hasName)
2272
2459
  Object.defineProperty(atomFamily, "name", {
2273
2460
  value: options.name,
2274
2461
  writable: false
2275
2462
  });
2276
- return Object.assign(atomFamily, {
2463
+ const callable = atomFamily;
2464
+ return Object.assign(callable, {
2277
2465
  __valdresAtomFamilyMap: map,
2278
- release: (...args) => map.delete(familyKey(args)),
2466
+ release: (...args) => {
2467
+ map.delete(familyKey(args));
2468
+ },
2279
2469
  equal
2280
2470
  });
2281
2471
  };
@@ -2376,11 +2566,10 @@ var selectorFamily = (callback, options) => {
2376
2566
  const cached = map.get(key);
2377
2567
  if (cached !== undefined)
2378
2568
  return cached;
2379
- const get = (selectorArgs) => callback(...args)(selectorArgs);
2380
2569
  const newSelector = {
2381
2570
  equal,
2382
2571
  ...options,
2383
- get,
2572
+ get: callback(...args),
2384
2573
  family: selectorFamily2,
2385
2574
  familyArgs: args,
2386
2575
  familyArgsStringified: key,
@@ -2403,9 +2592,9 @@ var isFamilySelector = (state) => isFamilyState(state) && isSelector(state);
2403
2592
 
2404
2593
  // src/index.ts
2405
2594
  if (globalThis.__valdres__) {
2406
- throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.2"}`);
2595
+ throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.4"}`);
2407
2596
  } else {
2408
- globalThis.__valdres__ = "1.0.0-beta.2";
2597
+ globalThis.__valdres__ = "1.0.0-beta.4";
2409
2598
  }
2410
2599
  export {
2411
2600
  store,