valdres 1.0.0-beta.5 → 1.0.0-beta.7

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
@@ -97,10 +97,10 @@ var equal = (a, b, updatedAtomsSet) => {
97
97
  };
98
98
 
99
99
  // src/utils/isAtom.ts
100
- var isAtom = (state) => Object.hasOwn(state, "defaultValue");
100
+ var isAtom = (state) => state && Object.hasOwn(state, "defaultValue");
101
101
 
102
102
  // src/utils/isGlobalAtom.ts
103
- var isGlobalAtom = (state) => Object.hasOwn(state, "stores");
103
+ var isGlobalAtom = (state) => state && Object.hasOwn(state, "stores");
104
104
 
105
105
  // src/utils/isSelector.ts
106
106
  var isSelector = (state) => state && Object.hasOwn(state, "get");
@@ -108,6 +108,11 @@ var isSelector = (state) => state && Object.hasOwn(state, "get");
108
108
  // src/utils/isAtomFamily.ts
109
109
  var isAtomFamily = (state) => state && Object.hasOwn(state, "__valdresAtomFamilyMap");
110
110
 
111
+ // src/utils/isPromiseLike.ts
112
+ var isPromiseLike = (object) => {
113
+ return object && object.then && typeof object.then === "function";
114
+ };
115
+
111
116
  // src/utils/isFamilyState.ts
112
117
  var isFamilyState = (state) => state && Object.hasOwn(state, "family");
113
118
 
@@ -117,11 +122,6 @@ var isFamilyAtom = (state) => isFamilyState(state) && isAtom(state);
117
122
  // src/utils/isSelectorFamily.ts
118
123
  var isSelectorFamily = (state) => state && Object.hasOwn(state, "__valdresSelectorFamilyMap");
119
124
 
120
- // src/utils/isPromiseLike.ts
121
- var isPromiseLike = (object) => {
122
- return object && object.then && typeof object.then === "function";
123
- };
124
-
125
125
  // src/utils/deepFreeze.ts
126
126
  var deepFreeze = (obj, seen) => {
127
127
  if (obj === null || obj === undefined)
@@ -403,6 +403,213 @@ var cleanUpRejectedPromise = (selector, data, promise) => {
403
403
  data.values.delete(selector);
404
404
  };
405
405
 
406
+ // src/lib/notifyChangeListeners.ts
407
+ var changeListenerRegistry = { count: 0, selectorCount: 0 };
408
+ var hasChangeListener = (data) => {
409
+ if (changeListenerRegistry.count === 0)
410
+ return false;
411
+ let store = data;
412
+ while (store) {
413
+ const listeners = store.changeListeners;
414
+ if (listeners !== undefined && listeners.size > 0)
415
+ return true;
416
+ store = store.parent;
417
+ }
418
+ return false;
419
+ };
420
+ var hasSelectorChangeListener = (data) => {
421
+ if (changeListenerRegistry.selectorCount === 0)
422
+ return false;
423
+ let store = data;
424
+ while (store) {
425
+ const listeners = store.changeListeners;
426
+ if (listeners !== undefined) {
427
+ for (const flags of listeners.values()) {
428
+ if (flags.selectors)
429
+ return true;
430
+ }
431
+ }
432
+ store = store.parent;
433
+ }
434
+ return false;
435
+ };
436
+ var scopePath = (data) => {
437
+ const path = [];
438
+ let store = data;
439
+ while (store && store.parent) {
440
+ path.push(store.id);
441
+ store = store.parent;
442
+ }
443
+ path.reverse();
444
+ return path;
445
+ };
446
+ var notifyChangeListeners = (groups, meta) => {
447
+ let buckets;
448
+ for (const group of groups) {
449
+ if (group.changes.length === 0)
450
+ continue;
451
+ let store = group.data;
452
+ while (store) {
453
+ const listeners = store.changeListeners;
454
+ if (listeners !== undefined && listeners.size > 0) {
455
+ if (!buckets)
456
+ buckets = new Map;
457
+ let bucket = buckets.get(store);
458
+ if (!bucket) {
459
+ bucket = [];
460
+ buckets.set(store, bucket);
461
+ }
462
+ for (const change of group.changes)
463
+ bucket.push(change);
464
+ }
465
+ store = store.parent;
466
+ }
467
+ }
468
+ if (!buckets)
469
+ return;
470
+ for (const [store, changes] of buckets) {
471
+ const listeners = store.changeListeners;
472
+ if (listeners === undefined || listeners.size === 0)
473
+ continue;
474
+ let hasSelector;
475
+ let hasAtom;
476
+ let atomOnly;
477
+ let selectorOnly;
478
+ let firstError;
479
+ let hasError = false;
480
+ for (const [listener, flags] of [...listeners]) {
481
+ let payload;
482
+ if (flags.atoms && flags.selectors) {
483
+ payload = changes;
484
+ } else if (flags.atoms) {
485
+ if (atomOnly === undefined) {
486
+ hasSelector ??= changes.some((c) => c.type === "selector");
487
+ atomOnly = hasSelector ? changes.filter((c) => c.type === "atom") : changes;
488
+ }
489
+ payload = atomOnly;
490
+ } else if (flags.selectors) {
491
+ if (selectorOnly === undefined) {
492
+ hasAtom ??= changes.some((c) => c.type === "atom");
493
+ selectorOnly = hasAtom ? changes.filter((c) => c.type === "selector") : changes;
494
+ }
495
+ payload = selectorOnly;
496
+ } else {
497
+ continue;
498
+ }
499
+ if (payload.length === 0)
500
+ continue;
501
+ try {
502
+ listener(payload, meta);
503
+ } catch (error) {
504
+ if (!hasError) {
505
+ firstError = error;
506
+ hasError = true;
507
+ }
508
+ }
509
+ }
510
+ if (hasError)
511
+ throw firstError;
512
+ }
513
+ };
514
+ var buildChangeGroup = (data, changedAtoms, deletedAtoms, changedSelectors) => {
515
+ const scope = scopePath(data);
516
+ const changes = [];
517
+ let seen;
518
+ if (changedAtoms) {
519
+ for (const atom of changedAtoms) {
520
+ if (atom.__valdresInternal || isAtomFamily(atom))
521
+ continue;
522
+ if (seen?.has(atom))
523
+ continue;
524
+ (seen ??= new Set).add(atom);
525
+ changes.push({
526
+ type: "atom",
527
+ kind: "set",
528
+ state: atom,
529
+ value: data.values.get(atom),
530
+ scope
531
+ });
532
+ }
533
+ }
534
+ if (deletedAtoms) {
535
+ for (const atom of deletedAtoms) {
536
+ if (atom.__valdresInternal || isAtomFamily(atom))
537
+ continue;
538
+ changes.push({ type: "atom", kind: "delete", state: atom, scope });
539
+ }
540
+ }
541
+ if (changedSelectors) {
542
+ for (const selector of changedSelectors) {
543
+ if (selector.__valdresInternal || isSelectorFamily(selector))
544
+ continue;
545
+ if (!data.values.has(selector))
546
+ continue;
547
+ changes.push({
548
+ type: "selector",
549
+ state: selector,
550
+ value: data.values.get(selector),
551
+ scope
552
+ });
553
+ }
554
+ }
555
+ return { data, changes };
556
+ };
557
+ var emitGroup = (group, report) => {
558
+ if (group.changes.length === 0)
559
+ return;
560
+ if (typeof report === "string") {
561
+ notifyChangeListeners([group], { source: report });
562
+ } else {
563
+ report.groups.push(group);
564
+ }
565
+ };
566
+ var reportAtomChanges = (atoms, data, report, changedSelectors) => {
567
+ if (!hasChangeListener(data))
568
+ return;
569
+ const selectors = changedSelectors && changedSelectors.size > 0 && hasSelectorChangeListener(data) ? changedSelectors : undefined;
570
+ emitGroup(buildChangeGroup(data, atoms, undefined, selectors), report);
571
+ };
572
+ var reportUnsetAtom = (atom, data, revertedValue, report) => {
573
+ if (!hasChangeListener(data))
574
+ return;
575
+ const group = {
576
+ data,
577
+ changes: [
578
+ {
579
+ type: "atom",
580
+ kind: "unset",
581
+ state: atom,
582
+ value: revertedValue,
583
+ scope: scopePath(data)
584
+ }
585
+ ]
586
+ };
587
+ emitGroup(group, report);
588
+ };
589
+ var reportDeletedAtoms = (atoms, data, report, changedSelectors) => {
590
+ if (!hasChangeListener(data))
591
+ return;
592
+ const selectors = changedSelectors && changedSelectors.size > 0 && hasSelectorChangeListener(data) ? changedSelectors : undefined;
593
+ emitGroup(buildChangeGroup(data, undefined, atoms, selectors), report);
594
+ };
595
+ var reportSelectorChanges = (changedSelectors, data, report) => {
596
+ if (changedSelectors.size === 0)
597
+ return;
598
+ if (!hasSelectorChangeListener(data))
599
+ return;
600
+ emitGroup(buildChangeGroup(data, undefined, undefined, changedSelectors), report);
601
+ };
602
+ var createChangeSink = (name, source = "transaction") => ({
603
+ source,
604
+ name,
605
+ groups: []
606
+ });
607
+ var flushChangeSink = (sink) => {
608
+ if (sink.groups.length > 0) {
609
+ notifyChangeListeners(sink.groups, { source: sink.source, name: sink.name });
610
+ }
611
+ };
612
+
406
613
  // src/lib/initSelector.ts
407
614
  var neverAbortedSignal = new AbortController().signal;
408
615
  var syncOptionsCache = new WeakMap;
@@ -549,7 +756,7 @@ var handleSelectorResult = (value, selector, data) => {
549
756
  const initializedAtomsSet = new Set;
550
757
  const res = initSelector(selector, data, initializedAtomsSet);
551
758
  if (initializedAtomsSet.size > 0) {
552
- propagateAtomUpdate([...initializedAtomsSet], data);
759
+ propagateAtomUpdate([...initializedAtomsSet], data, false, undefined, "async-set");
553
760
  }
554
761
  return res;
555
762
  }).catch(() => {
@@ -590,7 +797,12 @@ var handleSelectorResult = (value, selector, data) => {
590
797
  const dependents = data.stateDependents.get(selector);
591
798
  const subs = data.subscriptions.get(selector);
592
799
  if (subs && subs.size > 0 || dependents && dependents.size > 0) {
593
- propagateDirtySelectors([], new Set(dependents), data, new Set(subs), new Map);
800
+ const changedSelectors = changeListenerRegistry.selectorCount !== 0 && hasSelectorChangeListener(data) ? new Set : undefined;
801
+ propagateDirtySelectors([], new Set(dependents), data, new Set(subs), new Map, false, undefined, changedSelectors);
802
+ if (changedSelectors) {
803
+ changedSelectors.add(selector);
804
+ reportSelectorChanges(changedSelectors, data, "async-set");
805
+ }
594
806
  }
595
807
  }).catch(() => {
596
808
  pendingAsyncDeps.delete(value);
@@ -628,6 +840,14 @@ var evaluate = (selector, data, initializedAtomsSet, circularDependencySet) => {
628
840
  };
629
841
 
630
842
  // src/lib/propagateUpdatedAtoms.ts
843
+ var notifyEntryFor = (notify, data) => {
844
+ let entry = notify.get(data);
845
+ if (entry === undefined) {
846
+ entry = { subscriptions: new Set, families: new Map };
847
+ notify.set(data, entry);
848
+ }
849
+ return entry;
850
+ };
631
851
  var reEvaluateSelector = (selector, data, updatedAtoms, depsChange, existingValue) => {
632
852
  try {
633
853
  const rawValue = evaluateSelector(selector, data, updatedAtoms, undefined, depsChange);
@@ -674,6 +894,35 @@ var callSubscribers = (subscriptions, families) => {
674
894
  if (hasError)
675
895
  throw firstError;
676
896
  };
897
+ var notifyDeferred = (notify) => {
898
+ let firstError;
899
+ let hasError = false;
900
+ for (const entry of notify.values()) {
901
+ if (entry.subscriptions.size > 0) {
902
+ try {
903
+ callSubscribers(entry.subscriptions, entry.families);
904
+ } catch (error) {
905
+ if (!hasError) {
906
+ firstError = error;
907
+ hasError = true;
908
+ }
909
+ }
910
+ }
911
+ }
912
+ if (hasError)
913
+ throw firstError;
914
+ };
915
+ var collectFamilyAtomsForNotify = (entry, changedByFamily) => {
916
+ for (const [family, atoms] of changedByFamily) {
917
+ let target = entry.families.get(family);
918
+ if (target === undefined) {
919
+ target = new Set;
920
+ entry.families.set(family, target);
921
+ }
922
+ for (const atom of atoms)
923
+ target.add(atom);
924
+ }
925
+ };
677
926
  var addSetToSet = (fromSet, toSet) => {
678
927
  if (fromSet && fromSet.size > 0) {
679
928
  for (const item of fromSet) {
@@ -681,20 +930,24 @@ var addSetToSet = (fromSet, toSet) => {
681
930
  }
682
931
  }
683
932
  };
684
- var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, families = new Map, timestamp = performance.now(), evaluatedSelectors) => {
933
+ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, deletedFamilyAtoms = new Map, timestamp = performance.now(), notify, report) => {
934
+ const notifyEntry = notify ? notifyEntryFor(notify, data) : undefined;
935
+ if (notifyEntry) {
936
+ subscriptions = notifyEntry.subscriptions;
937
+ }
685
938
  const selectors = new Set;
686
939
  for (const atom of atoms) {
687
940
  addSetToSet(data.stateDependents.get(atom), selectors);
688
941
  addSetToSet(data.subscriptions.get(atom), subscriptions);
689
942
  if (isFamilyAtom(atom)) {
690
- if (!families.has(atom.family)) {
691
- families.set(atom.family, new Set);
943
+ if (!deletedFamilyAtoms.has(atom.family)) {
944
+ deletedFamilyAtoms.set(atom.family, new Set);
692
945
  }
693
- families.get(atom.family).add(atom);
946
+ deletedFamilyAtoms.get(atom.family).add(atom);
694
947
  }
695
948
  }
696
- if (families.size > 0) {
697
- for (const [family, familyAtoms] of families) {
949
+ if (deletedFamilyAtoms.size > 0) {
950
+ for (const [family, familyAtoms] of deletedFamilyAtoms) {
698
951
  addSetToSet(data.stateDependents.get(family), selectors);
699
952
  addSetToSet(data.subscriptions.get(family), subscriptions);
700
953
  if (familyAtoms.size === 0)
@@ -702,28 +955,35 @@ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, families = ne
702
955
  deleteFamilyAtomsFromSet(family, familyAtoms, data, timestamp);
703
956
  }
704
957
  }
705
- propagateDirtySelectors(atoms, selectors, data, subscriptions, families, false, evaluatedSelectors);
706
- if (families.size > 0 && data.scopes && data.scopes.size > 0) {
707
- const scopeFamilies = new Map;
708
- for (const family of families.keys()) {
709
- const scopesWithFamily = data.scopeValueIndex.get(family);
710
- if (scopesWithFamily) {
711
- for (const scope of scopesWithFamily) {
712
- let list = scopeFamilies.get(scope);
713
- if (!list) {
714
- list = [];
715
- scopeFamilies.set(scope, list);
716
- }
717
- list.push(family);
718
- }
719
- }
720
- }
721
- for (const [scope, familiesInScope] of scopeFamilies) {
722
- propagateInScope(familiesInScope, scope, false, evaluatedSelectors);
723
- }
724
- }
725
- };
726
- var propagateAtomUpdate = (atoms, data, isInitOnly = false, evaluatedSelectors) => {
958
+ const selectorActive = report !== undefined && changeListenerRegistry.selectorCount !== 0 && hasSelectorChangeListener(data);
959
+ const changedSelectors = selectorActive ? new Set : undefined;
960
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, deletedFamilyAtoms, false, notify, changedSelectors);
961
+ if (notifyEntry)
962
+ collectFamilyAtomsForNotify(notifyEntry, deletedFamilyAtoms);
963
+ const hasScopeCascade = !!data.scopes && data.scopes.size > 0;
964
+ const watching = report !== undefined && changeListenerRegistry.count !== 0;
965
+ let localSink;
966
+ let effectiveReport = report;
967
+ if (selectorActive && hasScopeCascade && typeof report === "string") {
968
+ localSink = createChangeSink(undefined, report);
969
+ effectiveReport = localSink;
970
+ }
971
+ const reportIsSink = effectiveReport !== undefined && typeof effectiveReport !== "string";
972
+ if (watching && reportIsSink)
973
+ reportDeletedAtoms(atoms, data, effectiveReport, changedSelectors);
974
+ if (hasScopeCascade) {
975
+ const scopeAtoms = atoms.slice();
976
+ for (const family of deletedFamilyAtoms.keys()) {
977
+ scopeAtoms.push(family);
978
+ }
979
+ propagateToScopes(scopeAtoms, data, false, notify, effectiveReport);
980
+ }
981
+ if (watching && !reportIsSink)
982
+ reportDeletedAtoms(atoms, data, effectiveReport, changedSelectors);
983
+ if (localSink)
984
+ flushChangeSink(localSink);
985
+ };
986
+ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify, report, skipFamilyIndexUpdate = false, reportAtoms = true) => {
727
987
  if (atoms.length === 1) {
728
988
  const atom = atoms[0];
729
989
  if (!isFamilyAtom(atom) && !isAtomFamily(atom)) {
@@ -731,28 +991,35 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, evaluatedSelectors)
731
991
  if ((!dependents || dependents.size === 0) && (!data.scopes || data.scopes.size === 0)) {
732
992
  const subs = data.subscriptions.get(atom);
733
993
  if (subs && subs.size > 0) {
734
- callSubscribers(subs);
994
+ if (notify)
995
+ addSetToSet(subs, notifyEntryFor(notify, data).subscriptions);
996
+ else
997
+ callSubscribers(subs);
998
+ }
999
+ if (reportAtoms && report !== undefined && changeListenerRegistry.count !== 0 && !isInitOnly) {
1000
+ reportAtomChanges(atoms, data, report);
735
1001
  }
736
1002
  return;
737
1003
  }
738
1004
  }
739
1005
  }
740
- const subscriptions = new Set;
741
- const families = new Map;
1006
+ const notifyEntry = notify ? notifyEntryFor(notify, data) : undefined;
1007
+ const subscriptions = notifyEntry ? notifyEntry.subscriptions : new Set;
1008
+ const updatedFamilyAtoms = new Map;
742
1009
  const selectors = new Set;
743
1010
  for (const atom of atoms) {
744
1011
  addSetToSet(data.stateDependents.get(atom), selectors);
745
1012
  addSetToSet(data.subscriptions.get(atom), subscriptions);
746
- if (isFamilyAtom(atom)) {
747
- if (!families.has(atom.family)) {
748
- families.set(atom.family, new Set);
1013
+ if (isFamilyAtom(atom) && !skipFamilyIndexUpdate) {
1014
+ if (!updatedFamilyAtoms.has(atom.family)) {
1015
+ updatedFamilyAtoms.set(atom.family, new Set);
749
1016
  }
750
- families.get(atom.family).add(atom);
1017
+ updatedFamilyAtoms.get(atom.family).add(atom);
751
1018
  }
752
1019
  }
753
- if (families.size > 0) {
1020
+ if (updatedFamilyAtoms.size > 0) {
754
1021
  const timestamp = performance.now();
755
- for (const [family, familyAtoms] of families) {
1022
+ for (const [family, familyAtoms] of updatedFamilyAtoms) {
756
1023
  addSetToSet(data.stateDependents.get(family), selectors);
757
1024
  addSetToSet(data.subscriptions.get(family), subscriptions);
758
1025
  if (familyAtoms.size === 0)
@@ -762,35 +1029,65 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, evaluatedSelectors)
762
1029
  }
763
1030
  if (data.scopes && data.scopes.size > 0) {
764
1031
  for (const atom of atoms) {
765
- if (isAtomFamily(atom) && !families.has(atom)) {
1032
+ if (isAtomFamily(atom) && !updatedFamilyAtoms.has(atom)) {
766
1033
  recursivelyUpdateIndexes(data, atom);
767
1034
  }
768
1035
  }
769
1036
  }
770
- propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly, evaluatedSelectors);
771
- if (data.scopes && data.scopes.size > 0) {
772
- propagateToScopes(atoms, data, isInitOnly, evaluatedSelectors);
1037
+ const selectorActive = report !== undefined && !isInitOnly && changeListenerRegistry.selectorCount !== 0 && hasSelectorChangeListener(data);
1038
+ const changedSelectors = selectorActive ? new Set : undefined;
1039
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, updatedFamilyAtoms, isInitOnly, notify, changedSelectors);
1040
+ if (notifyEntry)
1041
+ collectFamilyAtomsForNotify(notifyEntry, updatedFamilyAtoms);
1042
+ const hasScopes = !!data.scopes && data.scopes.size > 0;
1043
+ const watching = report !== undefined && changeListenerRegistry.count !== 0 && !isInitOnly;
1044
+ let localSink;
1045
+ let effectiveReport = report;
1046
+ if (selectorActive && hasScopes && typeof report === "string") {
1047
+ localSink = createChangeSink(undefined, report);
1048
+ effectiveReport = localSink;
773
1049
  }
774
- };
775
- var propagateInScope = (atoms, data, isInitOnly = false, evaluatedSelectors) => {
776
- const subscriptions = new Set;
1050
+ const reportIsSink = effectiveReport !== undefined && typeof effectiveReport !== "string";
1051
+ const emitOrigin = (rpt) => {
1052
+ if (reportAtoms) {
1053
+ reportAtomChanges(atoms, data, rpt, changedSelectors);
1054
+ } else if (changedSelectors && changedSelectors.size > 0) {
1055
+ reportSelectorChanges(changedSelectors, data, rpt);
1056
+ }
1057
+ };
1058
+ if (watching && reportIsSink)
1059
+ emitOrigin(effectiveReport);
1060
+ if (hasScopes) {
1061
+ propagateToScopes(atoms, data, isInitOnly, notify, effectiveReport);
1062
+ }
1063
+ if (watching && !reportIsSink)
1064
+ emitOrigin(effectiveReport);
1065
+ if (localSink)
1066
+ flushChangeSink(localSink);
1067
+ };
1068
+ var propagateInScope = (atoms, data, isInitOnly = false, notify, report) => {
1069
+ const subscriptions = notify ? notifyEntryFor(notify, data).subscriptions : new Set;
777
1070
  const families = new Map;
778
1071
  const selectors = new Set;
779
1072
  for (const atom of atoms) {
780
1073
  addSetToSet(data.stateDependents.get(atom), selectors);
781
1074
  }
782
- propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly, evaluatedSelectors);
1075
+ const changedSelectors = report !== undefined && !isInitOnly && changeListenerRegistry.selectorCount !== 0 && hasSelectorChangeListener(data) ? new Set : undefined;
1076
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly, notify, changedSelectors);
1077
+ if (changedSelectors && changedSelectors.size > 0) {
1078
+ reportSelectorChanges(changedSelectors, data, report);
1079
+ }
783
1080
  if (data.scopes && data.scopes.size > 0) {
784
- propagateToScopes(atoms, data, isInitOnly, evaluatedSelectors);
1081
+ propagateToScopes(atoms, data, isInitOnly, notify, report);
785
1082
  }
786
1083
  };
787
- var propagateToScopes = (atoms, data, isInitOnly, evaluatedSelectors) => {
1084
+ var propagateToScopes = (atoms, data, isInitOnly, notify, report) => {
788
1085
  if (atoms.length === 1) {
789
1086
  const atom = atoms[0];
790
1087
  const shadowingScopes = isAtomFamily(atom) ? undefined : data.scopeValueIndex.get(atom);
791
1088
  for (const [, scope] of data.scopes) {
792
1089
  if (!shadowingScopes || !shadowingScopes.has(scope)) {
793
- propagateInScope(atoms, scope, isInitOnly, evaluatedSelectors);
1090
+ propagateInScope(atoms, scope, isInitOnly, notify, report);
794
1091
  }
795
1092
  }
796
1093
  return;
@@ -810,7 +1107,7 @@ var propagateToScopes = (atoms, data, isInitOnly, evaluatedSelectors) => {
810
1107
  }
811
1108
  if (!anyShadowed) {
812
1109
  for (const [, scope] of data.scopes) {
813
- propagateInScope(atoms, scope, isInitOnly, evaluatedSelectors);
1110
+ propagateInScope(atoms, scope, isInitOnly, notify, report);
814
1111
  }
815
1112
  return;
816
1113
  }
@@ -827,20 +1124,20 @@ var propagateToScopes = (atoms, data, isInitOnly, evaluatedSelectors) => {
827
1124
  }
828
1125
  }
829
1126
  if (atomsToUpdateInScope.length > 0) {
830
- propagateInScope(atomsToUpdateInScope, scope, isInitOnly, evaluatedSelectors);
1127
+ propagateInScope(atomsToUpdateInScope, scope, isInitOnly, notify, report);
831
1128
  }
832
1129
  }
833
1130
  };
834
- var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false, evaluatedSelectors) => {
1131
+ var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false, notify, changedSelectors) => {
835
1132
  const updatedInitializedAtoms = new Set(updatedAtoms);
836
1133
  if (selectors.size > 0) {
837
- propagateSelectorUpdates(selectors, data, subscriptions, updatedInitializedAtoms, isInitOnly, evaluatedSelectors);
1134
+ propagateSelectorUpdates(selectors, data, subscriptions, updatedInitializedAtoms, isInitOnly, changedSelectors);
838
1135
  }
839
- if (subscriptions.size > 0) {
1136
+ if (!notify && subscriptions.size > 0) {
840
1137
  callSubscribers(subscriptions, families);
841
1138
  }
842
1139
  };
843
- var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly, evaluatedSelectors) => {
1140
+ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly, changedSelectors) => {
844
1141
  const closure = new Set(seeds);
845
1142
  {
846
1143
  const stack = [...seeds];
@@ -901,10 +1198,6 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
901
1198
  let head = 0;
902
1199
  while (head < ready.length) {
903
1200
  const selector = ready[head++];
904
- if (evaluatedSelectors !== undefined && evaluatedSelectors.has(selector)) {
905
- advance(selector, false);
906
- continue;
907
- }
908
1201
  const currentValue = data.values.get(selector);
909
1202
  if (isPromiseLike(currentValue) && isInitOnly) {
910
1203
  advance(selector, false);
@@ -924,8 +1217,6 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
924
1217
  depsChange.added = undefined;
925
1218
  depsChange.removed = undefined;
926
1219
  const wasValueUpdated = reEvaluateSelector(selector, data, updatedInitializedAtoms, depsChange, currentValue);
927
- if (evaluatedSelectors !== undefined)
928
- evaluatedSelectors.add(selector);
929
1220
  const added = depsChange.added;
930
1221
  const removed = depsChange.removed;
931
1222
  if (added || removed) {
@@ -946,8 +1237,11 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
946
1237
  }
947
1238
  }
948
1239
  advance(selector, wasValueUpdated);
949
- if (wasValueUpdated && subscribers) {
950
- addSetToSet(subscribers, collectedSubscribers);
1240
+ if (wasValueUpdated) {
1241
+ if (changedSelectors)
1242
+ changedSelectors.add(selector);
1243
+ if (subscribers)
1244
+ addSetToSet(subscribers, collectedSubscribers);
951
1245
  }
952
1246
  }
953
1247
  if (!graphMutated)
@@ -978,8 +1272,6 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
978
1272
  depsChange.added = undefined;
979
1273
  depsChange.removed = undefined;
980
1274
  const wasValueUpdated = reEvaluateSelector(selector, data, updatedInitializedAtoms, depsChange, currentValue);
981
- if (evaluatedSelectors !== undefined)
982
- evaluatedSelectors.add(selector);
983
1275
  const added = depsChange.added;
984
1276
  const removed = depsChange.removed;
985
1277
  if ((added || removed) && isLive(selector, data)) {
@@ -997,6 +1289,8 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
997
1289
  }
998
1290
  }
999
1291
  if (wasValueUpdated) {
1292
+ if (changedSelectors)
1293
+ changedSelectors.add(selector);
1000
1294
  if (subscribers)
1001
1295
  addSetToSet(subscribers, collectedSubscribers);
1002
1296
  const downstream = data.stateDependents.get(selector);
@@ -1009,15 +1303,12 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
1009
1303
  work = next;
1010
1304
  }
1011
1305
  };
1012
- var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false, evaluatedSelectors) => {
1306
+ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false, changedSelectors) => {
1013
1307
  if (selectors.size === 0)
1014
1308
  return;
1015
1309
  let downstreamSeeds;
1016
1310
  const depsChange = {};
1017
1311
  for (const selector of selectors) {
1018
- if (evaluatedSelectors !== undefined && evaluatedSelectors.has(selector)) {
1019
- continue;
1020
- }
1021
1312
  const currentValue = data.values.get(selector);
1022
1313
  if (isPromiseLike(currentValue) && isInitOnly)
1023
1314
  continue;
@@ -1030,8 +1321,6 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
1030
1321
  depsChange.added = undefined;
1031
1322
  depsChange.removed = undefined;
1032
1323
  const wasValueUpdated = reEvaluateSelector(selector, data, updatedInitializedAtoms, depsChange, currentValue);
1033
- if (evaluatedSelectors !== undefined)
1034
- evaluatedSelectors.add(selector);
1035
1324
  const added = depsChange.added;
1036
1325
  const removed = depsChange.removed;
1037
1326
  if ((added || removed) && isLive(selector, data)) {
@@ -1050,6 +1339,8 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
1050
1339
  }
1051
1340
  if (!wasValueUpdated)
1052
1341
  continue;
1342
+ if (changedSelectors)
1343
+ changedSelectors.add(selector);
1053
1344
  if (subscribers)
1054
1345
  addSetToSet(subscribers, collectedSubscribers);
1055
1346
  const downstream = data.stateDependents.get(selector);
@@ -1061,14 +1352,14 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
1061
1352
  }
1062
1353
  }
1063
1354
  if (downstreamSeeds && downstreamSeeds.size > 0) {
1064
- propagateDownstreamTopo(downstreamSeeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly, evaluatedSelectors);
1355
+ propagateDownstreamTopo(downstreamSeeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly, changedSelectors);
1065
1356
  }
1066
1357
  };
1067
1358
 
1068
1359
  // src/lib/isFunction.ts
1069
1360
  var isFunction = (value) => typeof value === "function";
1070
1361
 
1071
- // src/lib/setAtom.ts
1362
+ // src/lib/resolvePendingDefault.ts
1072
1363
  var resolvePendingDefault = (atom, data, value) => {
1073
1364
  let cur = data;
1074
1365
  while (cur) {
@@ -1081,6 +1372,8 @@ var resolvePendingDefault = (atom, data, value) => {
1081
1372
  cur = "parent" in cur ? cur.parent : undefined;
1082
1373
  }
1083
1374
  };
1375
+
1376
+ // src/lib/setAtom.ts
1084
1377
  var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
1085
1378
  setValueInData(atom, promise, data);
1086
1379
  promise.then((resolvedValue) => {
@@ -1090,12 +1383,12 @@ var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
1090
1383
  if (atom.onSet && !skipOnSet)
1091
1384
  atom.onSet(resolvedValue, data);
1092
1385
  resolvePendingDefault(atom, data, resolvedValue);
1093
- propagateAtomUpdate([atom], data);
1386
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1094
1387
  }).catch(() => {
1095
1388
  if (data.values.get(atom) !== promise)
1096
1389
  return;
1097
1390
  setValueInData(atom, currentValue, data);
1098
- propagateAtomUpdate([atom], data);
1391
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1099
1392
  });
1100
1393
  };
1101
1394
  var setAtom = (atom, newValue, data, skipOnSet = false) => {
@@ -1117,9 +1410,9 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
1117
1410
  handlePromise(atom, promise, currentValue, data, skipOnSet);
1118
1411
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
1119
1412
  initializedAtomsSet.add(atom);
1120
- propagateAtomUpdate([...initializedAtomsSet], data);
1413
+ propagateAtomUpdate([...initializedAtomsSet], data, false, undefined, "set");
1121
1414
  } else {
1122
- propagateAtomUpdate([atom], data);
1415
+ propagateAtomUpdate([atom], data, false, undefined, "set");
1123
1416
  }
1124
1417
  return promise;
1125
1418
  }
@@ -1133,9 +1426,9 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
1133
1426
  resolvePendingDefault(atom, data, syncValue);
1134
1427
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
1135
1428
  initializedAtomsSet.add(atom);
1136
- propagateAtomUpdate([...initializedAtomsSet], data);
1429
+ propagateAtomUpdate([...initializedAtomsSet], data, false, undefined, "set");
1137
1430
  } else {
1138
- propagateAtomUpdate([atom], data);
1431
+ propagateAtomUpdate([atom], data, false, undefined, "set");
1139
1432
  }
1140
1433
  return syncValue;
1141
1434
  };
@@ -1156,7 +1449,7 @@ var getAtomInitValue = (atom, data, initializedAtomsSet) => {
1156
1449
  if (data.values.get(atom) !== value)
1157
1450
  return;
1158
1451
  setValueInData(atom, resolvedValue, data);
1159
- propagateAtomUpdate([atom], data);
1452
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1160
1453
  }, () => {
1161
1454
  if (data.values.get(atom) === value) {
1162
1455
  data.values.delete(atom);
@@ -1180,6 +1473,23 @@ var initAtom = (atom, data, initializedAtomsSet) => {
1180
1473
  }, data);
1181
1474
  };
1182
1475
 
1476
+ // src/lib/resolveAtomDefaultValue.ts
1477
+ var resolveAtomDefaultValue = (atom, data, initializedAtomsSet) => {
1478
+ if (atom.defaultValue === undefined) {
1479
+ let resolve;
1480
+ const promise = new Promise((r) => {
1481
+ resolve = r;
1482
+ });
1483
+ data.pendingDefaults.set(atom, { promise, resolve });
1484
+ return promise;
1485
+ } else if (typeof atom.defaultValue === "function") {
1486
+ return atom.defaultValue();
1487
+ } else if (isSelector(atom.defaultValue)) {
1488
+ return getState(atom.defaultValue, data, initializedAtomsSet);
1489
+ }
1490
+ return atom.defaultValue;
1491
+ };
1492
+
1183
1493
  // src/lib/getState.ts
1184
1494
  function getState(state, data, initializedAtomsSet, circularDependencySet) {
1185
1495
  if (data.values.has(state))
@@ -1191,7 +1501,21 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
1191
1501
  const familyValue = data.values.get(state.family);
1192
1502
  if (familyValue?.__index) {
1193
1503
  if (isAtomDeletedInFamilyIndex(state, familyValue.__index)) {
1194
- return state.defaultValue;
1504
+ const value = resolveAtomDefaultValue(state, data, initializedAtomsSet);
1505
+ const cached = setValueInData(state, value, data);
1506
+ if (isPromiseLike(cached)) {
1507
+ cached.then((resolvedValue) => {
1508
+ if (data.values.get(state) !== cached)
1509
+ return;
1510
+ setValueInData(state, resolvedValue, data);
1511
+ propagateAtomUpdate([state], data, false, undefined, undefined, true);
1512
+ }, () => {
1513
+ if (data.values.get(state) === cached) {
1514
+ data.values.delete(state);
1515
+ }
1516
+ });
1517
+ }
1518
+ return cached;
1195
1519
  }
1196
1520
  }
1197
1521
  }
@@ -1249,6 +1573,63 @@ var resolveReactive = (value, data) => {
1249
1573
  return value;
1250
1574
  };
1251
1575
 
1576
+ // src/lib/unsetValue.ts
1577
+ var InvalidStateError = "unset() expects an atom.";
1578
+ var detachOwnValue = (atom, data) => {
1579
+ if (!data.values.has(atom))
1580
+ return false;
1581
+ data.values.delete(atom);
1582
+ if (atom.maxAge !== undefined)
1583
+ data.lastValueWriteAt.delete(atom);
1584
+ const parent = data.parent;
1585
+ if (parent) {
1586
+ const scopes = parent.scopeValueIndex.get(atom);
1587
+ if (scopes) {
1588
+ scopes.delete(data);
1589
+ if (scopes.size === 0)
1590
+ parent.scopeValueIndex.delete(atom);
1591
+ }
1592
+ data.scopeIndexKeys.delete(atom);
1593
+ }
1594
+ return true;
1595
+ };
1596
+ var reDelegateScopeSubscriptions = (atom, data) => {
1597
+ const subs = data.subscriptions.get(atom);
1598
+ if (subs) {
1599
+ for (const sub of subs)
1600
+ sub.reDelegate?.();
1601
+ }
1602
+ };
1603
+ var effectiveValueAfterUnset = (atom, data) => {
1604
+ const parent = data.parent;
1605
+ if (parent) {
1606
+ const initSet = new Set;
1607
+ const value = getState(atom, parent, initSet);
1608
+ if (initSet.size > 0)
1609
+ propagateAtomUpdate([...initSet], parent, true);
1610
+ return value;
1611
+ }
1612
+ if (data.values.has(atom))
1613
+ return data.values.get(atom);
1614
+ return getAtomInitValue(atom, data, new Set);
1615
+ };
1616
+ var unsetValue = (atom, data) => {
1617
+ if (!isAtom(atom))
1618
+ throw new Error(InvalidStateError);
1619
+ if (!detachOwnValue(atom, data))
1620
+ return;
1621
+ if (hasChangeListener(data)) {
1622
+ const sink = createChangeSink(undefined, "unset");
1623
+ reportUnsetAtom(atom, data, effectiveValueAfterUnset(atom, data), sink);
1624
+ propagateAtomUpdate([atom], data, false, undefined, sink, false, false);
1625
+ reDelegateScopeSubscriptions(atom, data);
1626
+ flushChangeSink(sink);
1627
+ } else {
1628
+ propagateAtomUpdate([atom], data, false);
1629
+ reDelegateScopeSubscriptions(atom, data);
1630
+ }
1631
+ };
1632
+
1252
1633
  // src/lib/createStoreData.ts
1253
1634
  var nextId = 0;
1254
1635
  var generateId = () => "__valdres_store_" + nextId++;
@@ -1282,7 +1663,10 @@ Object.defineProperties(lazyProto, {
1282
1663
  function createStoreData(id, parent, options) {
1283
1664
  const data = Object.create(lazyProto);
1284
1665
  data.id = id ?? generateId();
1285
- data.values = new WeakMap;
1666
+ const enumerable = options?.enumerable ?? parent?.enumerable ?? false;
1667
+ if (enumerable)
1668
+ data.enumerable = true;
1669
+ data.values = enumerable ? new Map : new WeakMap;
1286
1670
  data.scopes = new Map;
1287
1671
  data.scopeValueIndex = new WeakMap;
1288
1672
  data.pendingDefaults = new WeakMap;
@@ -1303,7 +1687,42 @@ var deleteFamilyAtom = (atom, data) => {
1303
1687
  if (atom.family) {
1304
1688
  atom.family.release(...atom.familyArgs);
1305
1689
  }
1306
- propagateDeletedAtoms([atom], data);
1690
+ propagateDeletedAtoms([atom], data, undefined, undefined, undefined, undefined, "delete");
1691
+ };
1692
+
1693
+ // src/lib/onStoreChange.ts
1694
+ var onStoreChange = (callback, data, options) => {
1695
+ const atoms = options?.atoms ?? true;
1696
+ const selectors = options?.selectors ?? false;
1697
+ if (!atoms && !selectors)
1698
+ return () => {};
1699
+ let listeners = data.changeListeners;
1700
+ if (!listeners) {
1701
+ listeners = new Map;
1702
+ data.changeListeners = listeners;
1703
+ }
1704
+ const prev = listeners.get(callback);
1705
+ listeners.set(callback, { atoms, selectors });
1706
+ if (!prev)
1707
+ changeListenerRegistry.count++;
1708
+ const prevSelectors = prev?.selectors ?? false;
1709
+ if (selectors && !prevSelectors)
1710
+ changeListenerRegistry.selectorCount++;
1711
+ else if (!selectors && prevSelectors)
1712
+ changeListenerRegistry.selectorCount--;
1713
+ return () => {
1714
+ const current = data.changeListeners;
1715
+ if (!current)
1716
+ return;
1717
+ const flags = current.get(callback);
1718
+ if (flags && current.delete(callback)) {
1719
+ changeListenerRegistry.count--;
1720
+ if (flags.selectors)
1721
+ changeListenerRegistry.selectorCount--;
1722
+ if (current.size === 0)
1723
+ data.changeListeners = undefined;
1724
+ }
1725
+ };
1307
1726
  };
1308
1727
 
1309
1728
  // src/lib/resetAtom.ts
@@ -1312,7 +1731,7 @@ var resetAtom = (atom, data) => {
1312
1731
  let value = getAtomInitValue(atom, data, initializedAtomsSet);
1313
1732
  setValueInData(atom, value, data);
1314
1733
  if (!isPromiseLike(value)) {
1315
- propagateAtomUpdate([atom], data);
1734
+ propagateAtomUpdate([atom], data, false, undefined, "reset");
1316
1735
  }
1317
1736
  if (initializedAtomsSet.size > 0) {
1318
1737
  throw new Error("Todo - propagateAtomUpdate on reset");
@@ -1320,6 +1739,38 @@ var resetAtom = (atom, data) => {
1320
1739
  return value;
1321
1740
  };
1322
1741
 
1742
+ // src/lib/snapshot.ts
1743
+ var warnedStores = new WeakSet;
1744
+ var collect = (data, scope, out) => {
1745
+ for (const [state, value] of data.values) {
1746
+ if (state.__valdresInternal)
1747
+ continue;
1748
+ if (isAtomFamily(state) || isSelectorFamily(state))
1749
+ continue;
1750
+ out.push({
1751
+ type: isSelector(state) ? "selector" : "atom",
1752
+ state,
1753
+ value,
1754
+ scope
1755
+ });
1756
+ }
1757
+ for (const [id, scopeData] of data.scopes) {
1758
+ collect(scopeData, [...scope, id], out);
1759
+ }
1760
+ };
1761
+ var snapshot = (data) => {
1762
+ if (!data.enumerable) {
1763
+ if (!warnedStores.has(data)) {
1764
+ warnedStores.add(data);
1765
+ console.warn("store.snapshot() requires an enumerable store. Create it with " + "`store({ enumerable: true })` or `store(id, { enumerable: true })` " + "to retain state enumerably. Returning [].");
1766
+ }
1767
+ return [];
1768
+ }
1769
+ const out = [];
1770
+ collect(data, [], out);
1771
+ return out;
1772
+ };
1773
+
1323
1774
  // src/utils/isFamily.ts
1324
1775
  var isFamily = (state) => isAtomFamily(state) || isSelectorFamily(state);
1325
1776
 
@@ -1409,11 +1860,11 @@ var installMaxAgeTimer = (state, data) => {
1409
1860
  const existing = globalState?.maxAgeInterval;
1410
1861
  if (existing) {
1411
1862
  existing.refCount++;
1412
- const metaAtom2 = state.__cacheMeta ??= { equal, defaultValue: null };
1863
+ const metaAtom2 = state.__cacheMeta ??= { equal, defaultValue: null, __valdresInternal: true };
1413
1864
  for (const s of globalState.stores) {
1414
1865
  if (s !== data && s.values.has(metaAtom2)) {
1415
1866
  setValueInData(metaAtom2, s.values.get(metaAtom2), data);
1416
- propagateAtomUpdate([metaAtom2], data);
1867
+ propagateAtomUpdate([metaAtom2], data, false, undefined, "revalidate");
1417
1868
  break;
1418
1869
  }
1419
1870
  }
@@ -1440,7 +1891,7 @@ var installMaxAgeTimer = (state, data) => {
1440
1891
  const getMaxAge = () => resolveReactive(state.maxAge, data);
1441
1892
  const getSWR = () => state.staleWhileRevalidate !== undefined ? resolveReactive(state.staleWhileRevalidate, data) : Infinity;
1442
1893
  const getStaleIfError = () => state.staleIfError !== undefined ? resolveReactive(state.staleIfError, data) : Infinity;
1443
- const metaAtom = state.__cacheMeta ??= { equal, defaultValue: null };
1894
+ const metaAtom = state.__cacheMeta ??= { equal, defaultValue: null, __valdresInternal: true };
1444
1895
  const updateMeta = () => {
1445
1896
  const meta = {
1446
1897
  isRevalidating: revalidating,
@@ -1452,11 +1903,11 @@ var installMaxAgeTimer = (state, data) => {
1452
1903
  if (globalState) {
1453
1904
  for (const store of globalState.stores) {
1454
1905
  setValueInData(metaAtom, meta, store);
1455
- propagateAtomUpdate([metaAtom], store);
1906
+ propagateAtomUpdate([metaAtom], store, false, undefined, "revalidate");
1456
1907
  }
1457
1908
  } else {
1458
1909
  setValueInData(metaAtom, meta, data);
1459
- propagateAtomUpdate([metaAtom], data);
1910
+ propagateAtomUpdate([metaAtom], data, false, undefined, "revalidate");
1460
1911
  }
1461
1912
  };
1462
1913
  const isPastStaleIfErrorWindow = () => {
@@ -1467,11 +1918,11 @@ var installMaxAgeTimer = (state, data) => {
1467
1918
  if (globalState) {
1468
1919
  for (const store of globalState.stores) {
1469
1920
  setValueInData(atom, val, store);
1470
- propagateAtomUpdate([atom], store);
1921
+ propagateAtomUpdate([atom], store, false, undefined, "revalidate");
1471
1922
  }
1472
1923
  } else {
1473
1924
  setValueInData(atom, val, data);
1474
- propagateAtomUpdate([atom], data);
1925
+ propagateAtomUpdate([atom], data, false, undefined, "revalidate");
1475
1926
  }
1476
1927
  };
1477
1928
  const getValueStore = () => {
@@ -1602,15 +2053,24 @@ var installMaxAgeTimer = (state, data) => {
1602
2053
  var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) => {
1603
2054
  let parentUnsubscribe;
1604
2055
  let dropDelegate;
1605
- if (data.parent && (!data.values.has(state) && isAtom(state) || isAtomFamily(state))) {
2056
+ let reDelegate;
2057
+ if (data.parent && (isAtom(state) || isAtomFamily(state))) {
1606
2058
  const originalCallback = callback;
1607
- parentUnsubscribe = subscribe(state, originalCallback, requireDeepEqualCheckBeforeCallback, data.parent);
2059
+ const delegateToParent = () => subscribe(state, originalCallback, requireDeepEqualCheckBeforeCallback, data.parent);
2060
+ if (isAtomFamily(state) || !data.values.has(state)) {
2061
+ parentUnsubscribe = delegateToParent();
2062
+ }
1608
2063
  dropDelegate = () => {
1609
2064
  if (parentUnsubscribe) {
1610
2065
  parentUnsubscribe();
1611
2066
  parentUnsubscribe = undefined;
1612
2067
  }
1613
2068
  };
2069
+ reDelegate = () => {
2070
+ if (!parentUnsubscribe) {
2071
+ parentUnsubscribe = delegateToParent();
2072
+ }
2073
+ };
1614
2074
  callback = (arg) => {
1615
2075
  dropDelegate();
1616
2076
  originalCallback(arg);
@@ -1635,13 +2095,15 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
1635
2095
  callback,
1636
2096
  state,
1637
2097
  requireDeepEqualCheckBeforeCallback,
1638
- reRoot: dropDelegate
2098
+ reRoot: dropDelegate,
2099
+ reDelegate
1639
2100
  };
1640
2101
  } else {
1641
2102
  subscription = {
1642
2103
  callback,
1643
2104
  requireDeepEqualCheckBeforeCallback,
1644
- reRoot: dropDelegate
2105
+ reRoot: dropDelegate,
2106
+ reDelegate
1645
2107
  };
1646
2108
  }
1647
2109
  subscribers.add(subscription);
@@ -1670,7 +2132,8 @@ var writeAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false, onSetQueu
1670
2132
  const updatedAtoms = [];
1671
2133
  for (let [atom, value] of pairs) {
1672
2134
  const currentValue = getState(atom, data, initializedAtomsSet);
1673
- const areEqual = isPromiseLike(currentValue) || isPromiseLike(value) ? currentValue === value : atom.equal(currentValue, value);
2135
+ const currentIsPromise = isPromiseLike(currentValue);
2136
+ const areEqual = currentIsPromise || isPromiseLike(value) ? currentValue === value : atom.equal(currentValue, value);
1674
2137
  if (!areEqual) {
1675
2138
  updatedAtoms.push(atom);
1676
2139
  value = setValueInData(atom, value, data);
@@ -1680,6 +2143,9 @@ var writeAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false, onSetQueu
1680
2143
  else
1681
2144
  atom.onSet(value, data);
1682
2145
  }
2146
+ if (currentIsPromise && !isPromiseLike(value)) {
2147
+ resolvePendingDefault(atom, data, value);
2148
+ }
1683
2149
  } else {
1684
2150
  setValueInData(atom, value, data);
1685
2151
  }
@@ -1693,10 +2159,10 @@ var writeAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false, onSetQueu
1693
2159
  };
1694
2160
 
1695
2161
  // src/lib/setAtoms.ts
1696
- var setAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false) => {
2162
+ var setAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false, report) => {
1697
2163
  const updatedAtoms = writeAtoms(pairs, data, initializedAtomsSet, skipOnSet);
1698
2164
  if (updatedAtoms.length > 0) {
1699
- propagateAtomUpdate(updatedAtoms, data);
2165
+ propagateAtomUpdate(updatedAtoms, data, false, undefined, report);
1700
2166
  }
1701
2167
  };
1702
2168
 
@@ -1711,9 +2177,11 @@ class Transaction {
1711
2177
  data;
1712
2178
  parentTransaction;
1713
2179
  dirty;
2180
+ name;
1714
2181
  _scopedTransactions;
1715
2182
  _initializedAtomsSet;
1716
2183
  _deleteSet;
2184
+ _unsetSet;
1717
2185
  _selectorDependencies;
1718
2186
  _selectorCache;
1719
2187
  _atomMap;
@@ -1721,6 +2189,7 @@ class Transaction {
1721
2189
  this.data = data;
1722
2190
  this.parentTransaction = parentTransaction;
1723
2191
  this.dirty = false;
2192
+ this.name = undefined;
1724
2193
  this._atomMap = new Map;
1725
2194
  if (childTransaction) {
1726
2195
  this._scopedTransactions = new Map([
@@ -1731,6 +2200,9 @@ class Transaction {
1731
2200
  hasTxnOrData = (state) => {
1732
2201
  if (this._atomMap.has(state))
1733
2202
  return true;
2203
+ if (this._unsetSet?.has(state)) {
2204
+ return this.parentTransaction ? this.parentTransaction.hasTxnOrData(state) : false;
2205
+ }
1734
2206
  if (this.data.values.has(state))
1735
2207
  return true;
1736
2208
  if (this.parentTransaction)
@@ -1741,6 +2213,9 @@ class Transaction {
1741
2213
  if (this._atomMap.has(state)) {
1742
2214
  return this._atomMap.get(state);
1743
2215
  }
2216
+ if (this._unsetSet?.has(state)) {
2217
+ return this.parentTransaction ? this.parentTransaction.valueFromTxnOrData(state) : undefined;
2218
+ }
1744
2219
  if (this.data.values.has(state)) {
1745
2220
  return this.data.values.get(state);
1746
2221
  }
@@ -1753,6 +2228,9 @@ class Transaction {
1753
2228
  if (this.hasTxnOrData(state)) {
1754
2229
  return this.valueFromTxnOrData(state);
1755
2230
  }
2231
+ if (this._unsetSet?.has(state)) {
2232
+ return this.data.parent ? getState(state, this.data.parent, this.initializedAtomsSet) : getAtomInitValue(state, this.data, this.initializedAtomsSet);
2233
+ }
1756
2234
  return getState(state, this.data, this.initializedAtomsSet);
1757
2235
  } else if (isSelector(state)) {
1758
2236
  if (this.dirty) {
@@ -1788,6 +2266,7 @@ class Transaction {
1788
2266
  resolved = deepFreeze(resolved);
1789
2267
  }
1790
2268
  this._atomMap.set(atom, resolved);
2269
+ this._unsetSet?.delete(atom);
1791
2270
  if (!this.dirty)
1792
2271
  this.dirty = true;
1793
2272
  if (isFamilyAtom(atom)) {
@@ -1816,6 +2295,7 @@ class Transaction {
1816
2295
  if (index.deleted.has(atom))
1817
2296
  index.deleted.delete(atom);
1818
2297
  this._atomMap.set(atom, value);
2298
+ this._unsetSet?.delete(atom);
1819
2299
  }
1820
2300
  index.rendered = null;
1821
2301
  index.renderedArray = null;
@@ -1839,6 +2319,22 @@ class Transaction {
1839
2319
  this._atomMap.delete(atom);
1840
2320
  }
1841
2321
  };
2322
+ unset = (atom) => {
2323
+ if (!isAtom(atom))
2324
+ throw new Error("unset() expects an atom.");
2325
+ this._atomMap.delete(atom);
2326
+ if (!this._unsetSet)
2327
+ this._unsetSet = new Set;
2328
+ this._unsetSet.add(atom);
2329
+ };
2330
+ applyUnsets = (unsetSet, data) => {
2331
+ const unsetAtoms = [];
2332
+ for (const atom of unsetSet) {
2333
+ if (detachOwnValue(atom, data))
2334
+ unsetAtoms.push(atom);
2335
+ }
2336
+ return unsetAtoms;
2337
+ };
1842
2338
  scope = (scopeId, callback) => {
1843
2339
  if (this.data.scopes.has(scopeId)) {
1844
2340
  return this.scopedTransaction(scopeId).execute(callback, false);
@@ -1858,6 +2354,7 @@ class Transaction {
1858
2354
  reset = (atom) => {
1859
2355
  const value = getAtomInitValue(atom, this.data, this.initializedAtomsSet);
1860
2356
  this._atomMap.set(atom, value);
2357
+ this._unsetSet?.delete(atom);
1861
2358
  return value;
1862
2359
  };
1863
2360
  execute = (callback, autoCommit = true) => {
@@ -1874,18 +2371,58 @@ class Transaction {
1874
2371
  }
1875
2372
  };
1876
2373
  commit = () => {
2374
+ if (changeListenerRegistry.count === 0) {
2375
+ this.commitWork(undefined);
2376
+ return;
2377
+ }
2378
+ const sink = createChangeSink(this.name);
2379
+ try {
2380
+ this.commitWork(sink);
2381
+ } catch (error) {
2382
+ try {
2383
+ flushChangeSink(sink);
2384
+ } catch {}
2385
+ throw error;
2386
+ }
2387
+ flushChangeSink(sink);
2388
+ };
2389
+ commitWork = (sink) => {
1877
2390
  if (!this._scopedTransactions) {
1878
2391
  const initializedAtomsSet = new Set;
1879
- if (this._deleteSet?.size) {
1880
- const evaluatedSelectors2 = new Set;
2392
+ if (this._unsetSet?.size) {
2393
+ const notify2 = new Map;
1881
2394
  const updatedAtoms = writeAtoms(this._atomMap, this.data, initializedAtomsSet);
1882
2395
  if (updatedAtoms.length > 0) {
1883
- propagateAtomUpdate(updatedAtoms, this.data, false, evaluatedSelectors2);
2396
+ propagateAtomUpdate(updatedAtoms, this.data, false, notify2, sink);
2397
+ }
2398
+ if (this._deleteSet?.size) {
2399
+ deleteAtomFamilyAtoms(this._deleteSet, this.data);
2400
+ propagateDeletedAtoms([...this._deleteSet], this.data, undefined, undefined, undefined, notify2, sink);
2401
+ }
2402
+ const unsetAtoms = this.applyUnsets(this._unsetSet, this.data);
2403
+ if (unsetAtoms.length > 0) {
2404
+ if (sink) {
2405
+ for (const atom of unsetAtoms) {
2406
+ reportUnsetAtom(atom, this.data, effectiveValueAfterUnset(atom, this.data), sink);
2407
+ }
2408
+ }
2409
+ propagateAtomUpdate(unsetAtoms, this.data, false, notify2, sink, false, false);
2410
+ }
2411
+ notifyDeferred(notify2);
2412
+ for (const atom of unsetAtoms) {
2413
+ reDelegateScopeSubscriptions(atom, this.data);
2414
+ }
2415
+ } else if (this._deleteSet?.size) {
2416
+ const notify2 = new Map;
2417
+ const updatedAtoms = writeAtoms(this._atomMap, this.data, initializedAtomsSet);
2418
+ if (updatedAtoms.length > 0) {
2419
+ propagateAtomUpdate(updatedAtoms, this.data, false, notify2, sink);
1884
2420
  }
1885
2421
  deleteAtomFamilyAtoms(this._deleteSet, this.data);
1886
- propagateDeletedAtoms([...this._deleteSet], this.data, undefined, undefined, undefined, evaluatedSelectors2);
2422
+ propagateDeletedAtoms([...this._deleteSet], this.data, undefined, undefined, undefined, notify2, sink);
2423
+ notifyDeferred(notify2);
1887
2424
  } else {
1888
- setAtoms(this._atomMap, this.data, initializedAtomsSet);
2425
+ setAtoms(this._atomMap, this.data, initializedAtomsSet, false, sink);
1889
2426
  }
1890
2427
  return;
1891
2428
  }
@@ -1899,19 +2436,38 @@ class Transaction {
1899
2436
  deleteAtomFamilyAtoms(txn._deleteSet, entry.data);
1900
2437
  entry.deleted = [...txn._deleteSet];
1901
2438
  }
2439
+ if (txn._unsetSet?.size) {
2440
+ entry.unsetAtoms = txn.applyUnsets(txn._unsetSet, entry.data);
2441
+ }
1902
2442
  }
1903
2443
  for (const entry of plan) {
1904
2444
  for (const [atom, value, data] of entry.onSets) {
1905
2445
  atom.onSet(value, data);
1906
2446
  }
1907
2447
  }
1908
- const evaluatedSelectors = new Set;
1909
- for (const { data, updatedAtoms, deleted } of plan) {
2448
+ const notify = new Map;
2449
+ for (const { data, updatedAtoms, deleted, unsetAtoms } of plan) {
1910
2450
  if (updatedAtoms.length > 0) {
1911
- propagateAtomUpdate(updatedAtoms, data, false, evaluatedSelectors);
2451
+ propagateAtomUpdate(updatedAtoms, data, false, notify, sink);
1912
2452
  }
1913
2453
  if (deleted) {
1914
- propagateDeletedAtoms(deleted, data, undefined, undefined, undefined, evaluatedSelectors);
2454
+ propagateDeletedAtoms(deleted, data, undefined, undefined, undefined, notify, sink);
2455
+ }
2456
+ if (unsetAtoms && unsetAtoms.length > 0) {
2457
+ if (sink) {
2458
+ for (const atom of unsetAtoms) {
2459
+ reportUnsetAtom(atom, data, effectiveValueAfterUnset(atom, data), sink);
2460
+ }
2461
+ }
2462
+ propagateAtomUpdate(unsetAtoms, data, false, notify, sink, false, false);
2463
+ }
2464
+ }
2465
+ notifyDeferred(notify);
2466
+ for (const { data, unsetAtoms } of plan) {
2467
+ if (unsetAtoms) {
2468
+ for (const atom of unsetAtoms) {
2469
+ reDelegateScopeSubscriptions(atom, data);
2470
+ }
1915
2471
  }
1916
2472
  }
1917
2473
  };
@@ -1921,6 +2477,7 @@ class Transaction {
1921
2477
  data: this.data,
1922
2478
  updatedAtoms: [],
1923
2479
  deleted: undefined,
2480
+ unsetAtoms: undefined,
1924
2481
  onSets: []
1925
2482
  });
1926
2483
  if (this._scopedTransactions) {
@@ -1984,8 +2541,9 @@ class Transaction {
1984
2541
  }
1985
2542
  }
1986
2543
  }
1987
- var transaction = (callback, data) => {
2544
+ var transaction = (callback, data, name) => {
1988
2545
  const txn = new Transaction(data);
2546
+ txn.name = name;
1989
2547
  return txn.execute(callback);
1990
2548
  };
1991
2549
 
@@ -2096,12 +2654,19 @@ function storeFromStoreData(data, detach) {
2096
2654
  flushPendingTxn();
2097
2655
  return deleteFamilyAtom(atom, data);
2098
2656
  };
2657
+ const unset = (atom) => {
2658
+ if (data.batchUpdates)
2659
+ flushPendingTxn();
2660
+ return unsetValue(atom, data);
2661
+ };
2099
2662
  const sub = (state, callback, deepEqualCheckBeforeCallback = true) => subscribe(state, callback, deepEqualCheckBeforeCallback, data);
2100
- const txn = (callback) => {
2663
+ const txn = (callback, name) => {
2101
2664
  if (data.batchUpdates)
2102
2665
  flushPendingTxn();
2103
- return transaction(callback, data);
2666
+ return transaction(callback, data, name);
2104
2667
  };
2668
+ const onChange = (callback, options) => onStoreChange(callback, data, options);
2669
+ const storeSnapshot = () => snapshot(data);
2105
2670
  const scope = (scopeId, callback) => {
2106
2671
  if (callback) {
2107
2672
  if (!data.scopes.has(scopeId)) {
@@ -2154,8 +2719,11 @@ function storeFromStoreData(data, detach) {
2154
2719
  txn,
2155
2720
  reset,
2156
2721
  del,
2722
+ unset,
2157
2723
  data,
2158
2724
  scope,
2725
+ onChange,
2726
+ snapshot: storeSnapshot,
2159
2727
  detach
2160
2728
  };
2161
2729
  } else {
@@ -2166,8 +2734,11 @@ function storeFromStoreData(data, detach) {
2166
2734
  txn,
2167
2735
  reset,
2168
2736
  del,
2737
+ unset,
2169
2738
  data,
2170
- scope
2739
+ scope,
2740
+ onChange,
2741
+ snapshot: storeSnapshot
2171
2742
  };
2172
2743
  }
2173
2744
  }
@@ -2348,12 +2919,13 @@ var unmountOrphanedDeps = (state, data, visited) => {
2348
2919
  };
2349
2920
 
2350
2921
  // src/store.ts
2351
- var store = (idOrOptions) => {
2352
- const id = typeof idOrOptions === "string" ? idOrOptions : idOrOptions?.id;
2353
- const options = typeof idOrOptions === "object" ? idOrOptions : undefined;
2922
+ function store(idOrOptions, maybeOptions) {
2923
+ const optionsObject = typeof idOrOptions === "object" && idOrOptions !== null ? idOrOptions : undefined;
2924
+ const id = typeof idOrOptions === "string" ? idOrOptions : optionsObject?.id;
2925
+ const options = optionsObject ?? maybeOptions;
2354
2926
  const data = createStoreData(id, undefined, options);
2355
2927
  return storeFromStoreData(data);
2356
- };
2928
+ }
2357
2929
 
2358
2930
  // src/globalStore.ts
2359
2931
  var globalStore = Object.assign(store("valdres-global-store"), {
@@ -2407,9 +2979,9 @@ var globalAtom = (defaultValue, options) => {
2407
2979
  const getSelf = () => globalStore.get(atom);
2408
2980
  const setSelf = (newValue) => globalStore.set(atom, newValue);
2409
2981
  const resetSelf = () => {
2410
- const snapshot = [...stores];
2982
+ const snapshot2 = [...stores];
2411
2983
  const subscribedStores = [];
2412
- for (const s of snapshot) {
2984
+ for (const s of snapshot2) {
2413
2985
  if (isLive(atom, s)) {
2414
2986
  subscribedStores.push(s);
2415
2987
  }
@@ -2419,7 +2991,7 @@ var globalAtom = (defaultValue, options) => {
2419
2991
  if (!firstError)
2420
2992
  firstError = e;
2421
2993
  };
2422
- for (const s of snapshot) {
2994
+ for (const s of snapshot2) {
2423
2995
  try {
2424
2996
  unmountAtom(atom, s);
2425
2997
  } catch (e) {
@@ -2431,11 +3003,11 @@ var globalAtom = (defaultValue, options) => {
2431
3003
  atom.maxAgeInterval.refCount = 0;
2432
3004
  atom.maxAgeInterval = undefined;
2433
3005
  }
2434
- for (const store2 of snapshot) {
3006
+ for (const store2 of snapshot2) {
2435
3007
  stores.delete(store2);
2436
3008
  store2.values.delete(atom);
2437
3009
  try {
2438
- propagateAtomUpdate([atom], store2);
3010
+ propagateAtomUpdate([atom], store2, false, undefined, "reset");
2439
3011
  } catch (e) {
2440
3012
  recordError(e);
2441
3013
  }
@@ -2659,9 +3231,10 @@ var cacheMeta = (sourceAtom) => {
2659
3231
  if (sourceAtom.__cacheMetaSelector)
2660
3232
  return sourceAtom.__cacheMetaSelector;
2661
3233
  if (!sourceAtom.__cacheMeta) {
2662
- sourceAtom.__cacheMeta = { equal, defaultValue: null };
3234
+ sourceAtom.__cacheMeta = { equal, defaultValue: null, __valdresInternal: true };
2663
3235
  }
2664
3236
  sourceAtom.__cacheMetaSelector = selector((get) => get(sourceAtom.__cacheMeta));
3237
+ sourceAtom.__cacheMetaSelector.__valdresInternal = true;
2665
3238
  return sourceAtom.__cacheMetaSelector;
2666
3239
  };
2667
3240
  // src/indexConstructor.ts
@@ -2669,38 +3242,27 @@ var index = (family, callback, options) => {
2669
3242
  const map = new Map;
2670
3243
  const index2 = (term) => {
2671
3244
  const termKey = stableStringify(term);
2672
- if (map.has(termKey))
2673
- return map.get(termKey);
2674
- const termIndexSelectorSet = new Set;
2675
- const termIndexSelectorMap = new Map;
2676
- const termIndexSelector = selector((get) => {
2677
- const allFamilyAtoms = new Set(get(family));
2678
- const deletedAtoms = termIndexSelectorSet.symmetricDifference(allFamilyAtoms);
2679
- const addedAtoms = allFamilyAtoms.difference(termIndexSelectorSet);
2680
- if (deletedAtoms.size || addedAtoms.size) {
2681
- deletedAtoms.forEach((atom2) => {
2682
- termIndexSelectorSet.delete(atom2);
2683
- termIndexSelectorMap.delete(atom2);
2684
- });
2685
- addedAtoms.forEach((atom2) => {
2686
- termIndexSelectorSet.add(atom2);
2687
- termIndexSelectorMap.set(atom2, selector((get2) => callback(get2(atom2), term), {
2688
- name: `index:callback:selector:${atom2.name}`
2689
- }));
3245
+ const existing = map.get(termKey);
3246
+ if (existing)
3247
+ return existing;
3248
+ const predicateSelectors = new WeakMap;
3249
+ const predicateFor = (atom2) => {
3250
+ let sel = predicateSelectors.get(atom2);
3251
+ if (!sel) {
3252
+ sel = selector((get) => callback(get(atom2), term), {
3253
+ name: `index:callback:selector:${atom2.name}`
2690
3254
  });
2691
- return new Set(termIndexSelectorSet);
2692
- } else {
2693
- return termIndexSelectorSet;
3255
+ predicateSelectors.set(atom2, sel);
2694
3256
  }
2695
- }, { name: `index:${options?.name}(${termKey})` });
3257
+ return sel;
3258
+ };
2696
3259
  const filteredSelector = selector((get) => {
2697
- const set = get(termIndexSelector);
2698
3260
  const res = [];
2699
- set.forEach((atom2) => {
2700
- if (get(termIndexSelectorMap.get(atom2))) {
3261
+ const members = get(family);
3262
+ for (const atom2 of members) {
3263
+ if (get(predicateFor(atom2)))
2701
3264
  res.push(atom2);
2702
- }
2703
- });
3265
+ }
2704
3266
  return res;
2705
3267
  }, {
2706
3268
  name: `index:${options?.name}:${termKey}:termSelector`
@@ -2748,9 +3310,9 @@ var isFamilySelector = (state) => isFamilyState(state) && isSelector(state);
2748
3310
 
2749
3311
  // src/index.ts
2750
3312
  if (globalThis.__valdres__) {
2751
- throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.5"}`);
3313
+ throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.7"}`);
2752
3314
  } else {
2753
- globalThis.__valdres__ = "1.0.0-beta.5";
3315
+ globalThis.__valdres__ = "1.0.0-beta.7";
2754
3316
  }
2755
3317
  export {
2756
3318
  store,