valdres 1.0.0-beta.6 → 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
@@ -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);
@@ -718,7 +930,7 @@ var addSetToSet = (fromSet, toSet) => {
718
930
  }
719
931
  }
720
932
  };
721
- var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, deletedFamilyAtoms = new Map, timestamp = performance.now(), notify) => {
933
+ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, deletedFamilyAtoms = new Map, timestamp = performance.now(), notify, report) => {
722
934
  const notifyEntry = notify ? notifyEntryFor(notify, data) : undefined;
723
935
  if (notifyEntry) {
724
936
  subscriptions = notifyEntry.subscriptions;
@@ -743,30 +955,35 @@ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, deletedFamily
743
955
  deleteFamilyAtomsFromSet(family, familyAtoms, data, timestamp);
744
956
  }
745
957
  }
746
- propagateDirtySelectors(atoms, selectors, data, subscriptions, deletedFamilyAtoms, false, notify);
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);
747
961
  if (notifyEntry)
748
962
  collectFamilyAtomsForNotify(notifyEntry, deletedFamilyAtoms);
749
- if (deletedFamilyAtoms.size > 0 && data.scopes && data.scopes.size > 0) {
750
- const scopeFamilies = new Map;
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();
751
976
  for (const family of deletedFamilyAtoms.keys()) {
752
- const scopesWithFamily = data.scopeValueIndex.get(family);
753
- if (scopesWithFamily) {
754
- for (const scope of scopesWithFamily) {
755
- let list = scopeFamilies.get(scope);
756
- if (!list) {
757
- list = [];
758
- scopeFamilies.set(scope, list);
759
- }
760
- list.push(family);
761
- }
762
- }
763
- }
764
- for (const [scope, familiesInScope] of scopeFamilies) {
765
- propagateInScope(familiesInScope, scope, false, notify);
977
+ scopeAtoms.push(family);
766
978
  }
979
+ propagateToScopes(scopeAtoms, data, false, notify, effectiveReport);
767
980
  }
981
+ if (watching && !reportIsSink)
982
+ reportDeletedAtoms(atoms, data, effectiveReport, changedSelectors);
983
+ if (localSink)
984
+ flushChangeSink(localSink);
768
985
  };
769
- var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
986
+ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify, report, skipFamilyIndexUpdate = false, reportAtoms = true) => {
770
987
  if (atoms.length === 1) {
771
988
  const atom = atoms[0];
772
989
  if (!isFamilyAtom(atom) && !isAtomFamily(atom)) {
@@ -779,6 +996,9 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
779
996
  else
780
997
  callSubscribers(subs);
781
998
  }
999
+ if (reportAtoms && report !== undefined && changeListenerRegistry.count !== 0 && !isInitOnly) {
1000
+ reportAtomChanges(atoms, data, report);
1001
+ }
782
1002
  return;
783
1003
  }
784
1004
  }
@@ -790,7 +1010,7 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
790
1010
  for (const atom of atoms) {
791
1011
  addSetToSet(data.stateDependents.get(atom), selectors);
792
1012
  addSetToSet(data.subscriptions.get(atom), subscriptions);
793
- if (isFamilyAtom(atom)) {
1013
+ if (isFamilyAtom(atom) && !skipFamilyIndexUpdate) {
794
1014
  if (!updatedFamilyAtoms.has(atom.family)) {
795
1015
  updatedFamilyAtoms.set(atom.family, new Set);
796
1016
  }
@@ -814,32 +1034,60 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
814
1034
  }
815
1035
  }
816
1036
  }
817
- propagateDirtySelectors(atoms, selectors, data, subscriptions, updatedFamilyAtoms, isInitOnly, notify);
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);
818
1040
  if (notifyEntry)
819
1041
  collectFamilyAtomsForNotify(notifyEntry, updatedFamilyAtoms);
820
- if (data.scopes && data.scopes.size > 0) {
821
- propagateToScopes(atoms, data, isInitOnly, notify);
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;
1049
+ }
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);
822
1062
  }
1063
+ if (watching && !reportIsSink)
1064
+ emitOrigin(effectiveReport);
1065
+ if (localSink)
1066
+ flushChangeSink(localSink);
823
1067
  };
824
- var propagateInScope = (atoms, data, isInitOnly = false, notify) => {
1068
+ var propagateInScope = (atoms, data, isInitOnly = false, notify, report) => {
825
1069
  const subscriptions = notify ? notifyEntryFor(notify, data).subscriptions : new Set;
826
1070
  const families = new Map;
827
1071
  const selectors = new Set;
828
1072
  for (const atom of atoms) {
829
1073
  addSetToSet(data.stateDependents.get(atom), selectors);
830
1074
  }
831
- propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly, notify);
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
+ }
832
1080
  if (data.scopes && data.scopes.size > 0) {
833
- propagateToScopes(atoms, data, isInitOnly, notify);
1081
+ propagateToScopes(atoms, data, isInitOnly, notify, report);
834
1082
  }
835
1083
  };
836
- var propagateToScopes = (atoms, data, isInitOnly, notify) => {
1084
+ var propagateToScopes = (atoms, data, isInitOnly, notify, report) => {
837
1085
  if (atoms.length === 1) {
838
1086
  const atom = atoms[0];
839
1087
  const shadowingScopes = isAtomFamily(atom) ? undefined : data.scopeValueIndex.get(atom);
840
1088
  for (const [, scope] of data.scopes) {
841
1089
  if (!shadowingScopes || !shadowingScopes.has(scope)) {
842
- propagateInScope(atoms, scope, isInitOnly, notify);
1090
+ propagateInScope(atoms, scope, isInitOnly, notify, report);
843
1091
  }
844
1092
  }
845
1093
  return;
@@ -859,7 +1107,7 @@ var propagateToScopes = (atoms, data, isInitOnly, notify) => {
859
1107
  }
860
1108
  if (!anyShadowed) {
861
1109
  for (const [, scope] of data.scopes) {
862
- propagateInScope(atoms, scope, isInitOnly, notify);
1110
+ propagateInScope(atoms, scope, isInitOnly, notify, report);
863
1111
  }
864
1112
  return;
865
1113
  }
@@ -876,20 +1124,20 @@ var propagateToScopes = (atoms, data, isInitOnly, notify) => {
876
1124
  }
877
1125
  }
878
1126
  if (atomsToUpdateInScope.length > 0) {
879
- propagateInScope(atomsToUpdateInScope, scope, isInitOnly, notify);
1127
+ propagateInScope(atomsToUpdateInScope, scope, isInitOnly, notify, report);
880
1128
  }
881
1129
  }
882
1130
  };
883
- var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false, notify) => {
1131
+ var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false, notify, changedSelectors) => {
884
1132
  const updatedInitializedAtoms = new Set(updatedAtoms);
885
1133
  if (selectors.size > 0) {
886
- propagateSelectorUpdates(selectors, data, subscriptions, updatedInitializedAtoms, isInitOnly);
1134
+ propagateSelectorUpdates(selectors, data, subscriptions, updatedInitializedAtoms, isInitOnly, changedSelectors);
887
1135
  }
888
1136
  if (!notify && subscriptions.size > 0) {
889
1137
  callSubscribers(subscriptions, families);
890
1138
  }
891
1139
  };
892
- var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly) => {
1140
+ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly, changedSelectors) => {
893
1141
  const closure = new Set(seeds);
894
1142
  {
895
1143
  const stack = [...seeds];
@@ -989,8 +1237,11 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
989
1237
  }
990
1238
  }
991
1239
  advance(selector, wasValueUpdated);
992
- if (wasValueUpdated && subscribers) {
993
- addSetToSet(subscribers, collectedSubscribers);
1240
+ if (wasValueUpdated) {
1241
+ if (changedSelectors)
1242
+ changedSelectors.add(selector);
1243
+ if (subscribers)
1244
+ addSetToSet(subscribers, collectedSubscribers);
994
1245
  }
995
1246
  }
996
1247
  if (!graphMutated)
@@ -1038,6 +1289,8 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
1038
1289
  }
1039
1290
  }
1040
1291
  if (wasValueUpdated) {
1292
+ if (changedSelectors)
1293
+ changedSelectors.add(selector);
1041
1294
  if (subscribers)
1042
1295
  addSetToSet(subscribers, collectedSubscribers);
1043
1296
  const downstream = data.stateDependents.get(selector);
@@ -1050,7 +1303,7 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
1050
1303
  work = next;
1051
1304
  }
1052
1305
  };
1053
- var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false) => {
1306
+ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false, changedSelectors) => {
1054
1307
  if (selectors.size === 0)
1055
1308
  return;
1056
1309
  let downstreamSeeds;
@@ -1086,6 +1339,8 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
1086
1339
  }
1087
1340
  if (!wasValueUpdated)
1088
1341
  continue;
1342
+ if (changedSelectors)
1343
+ changedSelectors.add(selector);
1089
1344
  if (subscribers)
1090
1345
  addSetToSet(subscribers, collectedSubscribers);
1091
1346
  const downstream = data.stateDependents.get(selector);
@@ -1097,7 +1352,7 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
1097
1352
  }
1098
1353
  }
1099
1354
  if (downstreamSeeds && downstreamSeeds.size > 0) {
1100
- propagateDownstreamTopo(downstreamSeeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly);
1355
+ propagateDownstreamTopo(downstreamSeeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly, changedSelectors);
1101
1356
  }
1102
1357
  };
1103
1358
 
@@ -1128,12 +1383,12 @@ var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
1128
1383
  if (atom.onSet && !skipOnSet)
1129
1384
  atom.onSet(resolvedValue, data);
1130
1385
  resolvePendingDefault(atom, data, resolvedValue);
1131
- propagateAtomUpdate([atom], data);
1386
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1132
1387
  }).catch(() => {
1133
1388
  if (data.values.get(atom) !== promise)
1134
1389
  return;
1135
1390
  setValueInData(atom, currentValue, data);
1136
- propagateAtomUpdate([atom], data);
1391
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1137
1392
  });
1138
1393
  };
1139
1394
  var setAtom = (atom, newValue, data, skipOnSet = false) => {
@@ -1155,9 +1410,9 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
1155
1410
  handlePromise(atom, promise, currentValue, data, skipOnSet);
1156
1411
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
1157
1412
  initializedAtomsSet.add(atom);
1158
- propagateAtomUpdate([...initializedAtomsSet], data);
1413
+ propagateAtomUpdate([...initializedAtomsSet], data, false, undefined, "set");
1159
1414
  } else {
1160
- propagateAtomUpdate([atom], data);
1415
+ propagateAtomUpdate([atom], data, false, undefined, "set");
1161
1416
  }
1162
1417
  return promise;
1163
1418
  }
@@ -1171,9 +1426,9 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
1171
1426
  resolvePendingDefault(atom, data, syncValue);
1172
1427
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
1173
1428
  initializedAtomsSet.add(atom);
1174
- propagateAtomUpdate([...initializedAtomsSet], data);
1429
+ propagateAtomUpdate([...initializedAtomsSet], data, false, undefined, "set");
1175
1430
  } else {
1176
- propagateAtomUpdate([atom], data);
1431
+ propagateAtomUpdate([atom], data, false, undefined, "set");
1177
1432
  }
1178
1433
  return syncValue;
1179
1434
  };
@@ -1194,7 +1449,7 @@ var getAtomInitValue = (atom, data, initializedAtomsSet) => {
1194
1449
  if (data.values.get(atom) !== value)
1195
1450
  return;
1196
1451
  setValueInData(atom, resolvedValue, data);
1197
- propagateAtomUpdate([atom], data);
1452
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1198
1453
  }, () => {
1199
1454
  if (data.values.get(atom) === value) {
1200
1455
  data.values.delete(atom);
@@ -1218,6 +1473,23 @@ var initAtom = (atom, data, initializedAtomsSet) => {
1218
1473
  }, data);
1219
1474
  };
1220
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
+
1221
1493
  // src/lib/getState.ts
1222
1494
  function getState(state, data, initializedAtomsSet, circularDependencySet) {
1223
1495
  if (data.values.has(state))
@@ -1229,7 +1501,21 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
1229
1501
  const familyValue = data.values.get(state.family);
1230
1502
  if (familyValue?.__index) {
1231
1503
  if (isAtomDeletedInFamilyIndex(state, familyValue.__index)) {
1232
- 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;
1233
1519
  }
1234
1520
  }
1235
1521
  }
@@ -1287,6 +1573,63 @@ var resolveReactive = (value, data) => {
1287
1573
  return value;
1288
1574
  };
1289
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
+
1290
1633
  // src/lib/createStoreData.ts
1291
1634
  var nextId = 0;
1292
1635
  var generateId = () => "__valdres_store_" + nextId++;
@@ -1320,7 +1663,10 @@ Object.defineProperties(lazyProto, {
1320
1663
  function createStoreData(id, parent, options) {
1321
1664
  const data = Object.create(lazyProto);
1322
1665
  data.id = id ?? generateId();
1323
- 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;
1324
1670
  data.scopes = new Map;
1325
1671
  data.scopeValueIndex = new WeakMap;
1326
1672
  data.pendingDefaults = new WeakMap;
@@ -1341,7 +1687,42 @@ var deleteFamilyAtom = (atom, data) => {
1341
1687
  if (atom.family) {
1342
1688
  atom.family.release(...atom.familyArgs);
1343
1689
  }
1344
- 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
+ };
1345
1726
  };
1346
1727
 
1347
1728
  // src/lib/resetAtom.ts
@@ -1350,7 +1731,7 @@ var resetAtom = (atom, data) => {
1350
1731
  let value = getAtomInitValue(atom, data, initializedAtomsSet);
1351
1732
  setValueInData(atom, value, data);
1352
1733
  if (!isPromiseLike(value)) {
1353
- propagateAtomUpdate([atom], data);
1734
+ propagateAtomUpdate([atom], data, false, undefined, "reset");
1354
1735
  }
1355
1736
  if (initializedAtomsSet.size > 0) {
1356
1737
  throw new Error("Todo - propagateAtomUpdate on reset");
@@ -1358,6 +1739,38 @@ var resetAtom = (atom, data) => {
1358
1739
  return value;
1359
1740
  };
1360
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
+
1361
1774
  // src/utils/isFamily.ts
1362
1775
  var isFamily = (state) => isAtomFamily(state) || isSelectorFamily(state);
1363
1776
 
@@ -1447,11 +1860,11 @@ var installMaxAgeTimer = (state, data) => {
1447
1860
  const existing = globalState?.maxAgeInterval;
1448
1861
  if (existing) {
1449
1862
  existing.refCount++;
1450
- const metaAtom2 = state.__cacheMeta ??= { equal, defaultValue: null };
1863
+ const metaAtom2 = state.__cacheMeta ??= { equal, defaultValue: null, __valdresInternal: true };
1451
1864
  for (const s of globalState.stores) {
1452
1865
  if (s !== data && s.values.has(metaAtom2)) {
1453
1866
  setValueInData(metaAtom2, s.values.get(metaAtom2), data);
1454
- propagateAtomUpdate([metaAtom2], data);
1867
+ propagateAtomUpdate([metaAtom2], data, false, undefined, "revalidate");
1455
1868
  break;
1456
1869
  }
1457
1870
  }
@@ -1478,7 +1891,7 @@ var installMaxAgeTimer = (state, data) => {
1478
1891
  const getMaxAge = () => resolveReactive(state.maxAge, data);
1479
1892
  const getSWR = () => state.staleWhileRevalidate !== undefined ? resolveReactive(state.staleWhileRevalidate, data) : Infinity;
1480
1893
  const getStaleIfError = () => state.staleIfError !== undefined ? resolveReactive(state.staleIfError, data) : Infinity;
1481
- const metaAtom = state.__cacheMeta ??= { equal, defaultValue: null };
1894
+ const metaAtom = state.__cacheMeta ??= { equal, defaultValue: null, __valdresInternal: true };
1482
1895
  const updateMeta = () => {
1483
1896
  const meta = {
1484
1897
  isRevalidating: revalidating,
@@ -1490,11 +1903,11 @@ var installMaxAgeTimer = (state, data) => {
1490
1903
  if (globalState) {
1491
1904
  for (const store of globalState.stores) {
1492
1905
  setValueInData(metaAtom, meta, store);
1493
- propagateAtomUpdate([metaAtom], store);
1906
+ propagateAtomUpdate([metaAtom], store, false, undefined, "revalidate");
1494
1907
  }
1495
1908
  } else {
1496
1909
  setValueInData(metaAtom, meta, data);
1497
- propagateAtomUpdate([metaAtom], data);
1910
+ propagateAtomUpdate([metaAtom], data, false, undefined, "revalidate");
1498
1911
  }
1499
1912
  };
1500
1913
  const isPastStaleIfErrorWindow = () => {
@@ -1505,11 +1918,11 @@ var installMaxAgeTimer = (state, data) => {
1505
1918
  if (globalState) {
1506
1919
  for (const store of globalState.stores) {
1507
1920
  setValueInData(atom, val, store);
1508
- propagateAtomUpdate([atom], store);
1921
+ propagateAtomUpdate([atom], store, false, undefined, "revalidate");
1509
1922
  }
1510
1923
  } else {
1511
1924
  setValueInData(atom, val, data);
1512
- propagateAtomUpdate([atom], data);
1925
+ propagateAtomUpdate([atom], data, false, undefined, "revalidate");
1513
1926
  }
1514
1927
  };
1515
1928
  const getValueStore = () => {
@@ -1640,15 +2053,24 @@ var installMaxAgeTimer = (state, data) => {
1640
2053
  var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) => {
1641
2054
  let parentUnsubscribe;
1642
2055
  let dropDelegate;
1643
- if (data.parent && (!data.values.has(state) && isAtom(state) || isAtomFamily(state))) {
2056
+ let reDelegate;
2057
+ if (data.parent && (isAtom(state) || isAtomFamily(state))) {
1644
2058
  const originalCallback = callback;
1645
- 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
+ }
1646
2063
  dropDelegate = () => {
1647
2064
  if (parentUnsubscribe) {
1648
2065
  parentUnsubscribe();
1649
2066
  parentUnsubscribe = undefined;
1650
2067
  }
1651
2068
  };
2069
+ reDelegate = () => {
2070
+ if (!parentUnsubscribe) {
2071
+ parentUnsubscribe = delegateToParent();
2072
+ }
2073
+ };
1652
2074
  callback = (arg) => {
1653
2075
  dropDelegate();
1654
2076
  originalCallback(arg);
@@ -1673,13 +2095,15 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
1673
2095
  callback,
1674
2096
  state,
1675
2097
  requireDeepEqualCheckBeforeCallback,
1676
- reRoot: dropDelegate
2098
+ reRoot: dropDelegate,
2099
+ reDelegate
1677
2100
  };
1678
2101
  } else {
1679
2102
  subscription = {
1680
2103
  callback,
1681
2104
  requireDeepEqualCheckBeforeCallback,
1682
- reRoot: dropDelegate
2105
+ reRoot: dropDelegate,
2106
+ reDelegate
1683
2107
  };
1684
2108
  }
1685
2109
  subscribers.add(subscription);
@@ -1735,10 +2159,10 @@ var writeAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false, onSetQueu
1735
2159
  };
1736
2160
 
1737
2161
  // src/lib/setAtoms.ts
1738
- var setAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false) => {
2162
+ var setAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false, report) => {
1739
2163
  const updatedAtoms = writeAtoms(pairs, data, initializedAtomsSet, skipOnSet);
1740
2164
  if (updatedAtoms.length > 0) {
1741
- propagateAtomUpdate(updatedAtoms, data);
2165
+ propagateAtomUpdate(updatedAtoms, data, false, undefined, report);
1742
2166
  }
1743
2167
  };
1744
2168
 
@@ -1753,9 +2177,11 @@ class Transaction {
1753
2177
  data;
1754
2178
  parentTransaction;
1755
2179
  dirty;
2180
+ name;
1756
2181
  _scopedTransactions;
1757
2182
  _initializedAtomsSet;
1758
2183
  _deleteSet;
2184
+ _unsetSet;
1759
2185
  _selectorDependencies;
1760
2186
  _selectorCache;
1761
2187
  _atomMap;
@@ -1763,6 +2189,7 @@ class Transaction {
1763
2189
  this.data = data;
1764
2190
  this.parentTransaction = parentTransaction;
1765
2191
  this.dirty = false;
2192
+ this.name = undefined;
1766
2193
  this._atomMap = new Map;
1767
2194
  if (childTransaction) {
1768
2195
  this._scopedTransactions = new Map([
@@ -1773,6 +2200,9 @@ class Transaction {
1773
2200
  hasTxnOrData = (state) => {
1774
2201
  if (this._atomMap.has(state))
1775
2202
  return true;
2203
+ if (this._unsetSet?.has(state)) {
2204
+ return this.parentTransaction ? this.parentTransaction.hasTxnOrData(state) : false;
2205
+ }
1776
2206
  if (this.data.values.has(state))
1777
2207
  return true;
1778
2208
  if (this.parentTransaction)
@@ -1783,6 +2213,9 @@ class Transaction {
1783
2213
  if (this._atomMap.has(state)) {
1784
2214
  return this._atomMap.get(state);
1785
2215
  }
2216
+ if (this._unsetSet?.has(state)) {
2217
+ return this.parentTransaction ? this.parentTransaction.valueFromTxnOrData(state) : undefined;
2218
+ }
1786
2219
  if (this.data.values.has(state)) {
1787
2220
  return this.data.values.get(state);
1788
2221
  }
@@ -1795,6 +2228,9 @@ class Transaction {
1795
2228
  if (this.hasTxnOrData(state)) {
1796
2229
  return this.valueFromTxnOrData(state);
1797
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
+ }
1798
2234
  return getState(state, this.data, this.initializedAtomsSet);
1799
2235
  } else if (isSelector(state)) {
1800
2236
  if (this.dirty) {
@@ -1830,6 +2266,7 @@ class Transaction {
1830
2266
  resolved = deepFreeze(resolved);
1831
2267
  }
1832
2268
  this._atomMap.set(atom, resolved);
2269
+ this._unsetSet?.delete(atom);
1833
2270
  if (!this.dirty)
1834
2271
  this.dirty = true;
1835
2272
  if (isFamilyAtom(atom)) {
@@ -1858,6 +2295,7 @@ class Transaction {
1858
2295
  if (index.deleted.has(atom))
1859
2296
  index.deleted.delete(atom);
1860
2297
  this._atomMap.set(atom, value);
2298
+ this._unsetSet?.delete(atom);
1861
2299
  }
1862
2300
  index.rendered = null;
1863
2301
  index.renderedArray = null;
@@ -1881,6 +2319,22 @@ class Transaction {
1881
2319
  this._atomMap.delete(atom);
1882
2320
  }
1883
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
+ };
1884
2338
  scope = (scopeId, callback) => {
1885
2339
  if (this.data.scopes.has(scopeId)) {
1886
2340
  return this.scopedTransaction(scopeId).execute(callback, false);
@@ -1900,6 +2354,7 @@ class Transaction {
1900
2354
  reset = (atom) => {
1901
2355
  const value = getAtomInitValue(atom, this.data, this.initializedAtomsSet);
1902
2356
  this._atomMap.set(atom, value);
2357
+ this._unsetSet?.delete(atom);
1903
2358
  return value;
1904
2359
  };
1905
2360
  execute = (callback, autoCommit = true) => {
@@ -1916,19 +2371,58 @@ class Transaction {
1916
2371
  }
1917
2372
  };
1918
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) => {
1919
2390
  if (!this._scopedTransactions) {
1920
2391
  const initializedAtomsSet = new Set;
1921
- if (this._deleteSet?.size) {
2392
+ if (this._unsetSet?.size) {
2393
+ const notify2 = new Map;
2394
+ const updatedAtoms = writeAtoms(this._atomMap, this.data, initializedAtomsSet);
2395
+ if (updatedAtoms.length > 0) {
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) {
1922
2416
  const notify2 = new Map;
1923
2417
  const updatedAtoms = writeAtoms(this._atomMap, this.data, initializedAtomsSet);
1924
2418
  if (updatedAtoms.length > 0) {
1925
- propagateAtomUpdate(updatedAtoms, this.data, false, notify2);
2419
+ propagateAtomUpdate(updatedAtoms, this.data, false, notify2, sink);
1926
2420
  }
1927
2421
  deleteAtomFamilyAtoms(this._deleteSet, this.data);
1928
- propagateDeletedAtoms([...this._deleteSet], this.data, undefined, undefined, undefined, notify2);
2422
+ propagateDeletedAtoms([...this._deleteSet], this.data, undefined, undefined, undefined, notify2, sink);
1929
2423
  notifyDeferred(notify2);
1930
2424
  } else {
1931
- setAtoms(this._atomMap, this.data, initializedAtomsSet);
2425
+ setAtoms(this._atomMap, this.data, initializedAtomsSet, false, sink);
1932
2426
  }
1933
2427
  return;
1934
2428
  }
@@ -1942,6 +2436,9 @@ class Transaction {
1942
2436
  deleteAtomFamilyAtoms(txn._deleteSet, entry.data);
1943
2437
  entry.deleted = [...txn._deleteSet];
1944
2438
  }
2439
+ if (txn._unsetSet?.size) {
2440
+ entry.unsetAtoms = txn.applyUnsets(txn._unsetSet, entry.data);
2441
+ }
1945
2442
  }
1946
2443
  for (const entry of plan) {
1947
2444
  for (const [atom, value, data] of entry.onSets) {
@@ -1949,15 +2446,30 @@ class Transaction {
1949
2446
  }
1950
2447
  }
1951
2448
  const notify = new Map;
1952
- for (const { data, updatedAtoms, deleted } of plan) {
2449
+ for (const { data, updatedAtoms, deleted, unsetAtoms } of plan) {
1953
2450
  if (updatedAtoms.length > 0) {
1954
- propagateAtomUpdate(updatedAtoms, data, false, notify);
2451
+ propagateAtomUpdate(updatedAtoms, data, false, notify, sink);
1955
2452
  }
1956
2453
  if (deleted) {
1957
- propagateDeletedAtoms(deleted, data, undefined, undefined, undefined, notify);
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);
1958
2463
  }
1959
2464
  }
1960
2465
  notifyDeferred(notify);
2466
+ for (const { data, unsetAtoms } of plan) {
2467
+ if (unsetAtoms) {
2468
+ for (const atom of unsetAtoms) {
2469
+ reDelegateScopeSubscriptions(atom, data);
2470
+ }
2471
+ }
2472
+ }
1961
2473
  };
1962
2474
  collectStores = (plan) => {
1963
2475
  plan.push({
@@ -1965,6 +2477,7 @@ class Transaction {
1965
2477
  data: this.data,
1966
2478
  updatedAtoms: [],
1967
2479
  deleted: undefined,
2480
+ unsetAtoms: undefined,
1968
2481
  onSets: []
1969
2482
  });
1970
2483
  if (this._scopedTransactions) {
@@ -2028,8 +2541,9 @@ class Transaction {
2028
2541
  }
2029
2542
  }
2030
2543
  }
2031
- var transaction = (callback, data) => {
2544
+ var transaction = (callback, data, name) => {
2032
2545
  const txn = new Transaction(data);
2546
+ txn.name = name;
2033
2547
  return txn.execute(callback);
2034
2548
  };
2035
2549
 
@@ -2140,12 +2654,19 @@ function storeFromStoreData(data, detach) {
2140
2654
  flushPendingTxn();
2141
2655
  return deleteFamilyAtom(atom, data);
2142
2656
  };
2657
+ const unset = (atom) => {
2658
+ if (data.batchUpdates)
2659
+ flushPendingTxn();
2660
+ return unsetValue(atom, data);
2661
+ };
2143
2662
  const sub = (state, callback, deepEqualCheckBeforeCallback = true) => subscribe(state, callback, deepEqualCheckBeforeCallback, data);
2144
- const txn = (callback) => {
2663
+ const txn = (callback, name) => {
2145
2664
  if (data.batchUpdates)
2146
2665
  flushPendingTxn();
2147
- return transaction(callback, data);
2666
+ return transaction(callback, data, name);
2148
2667
  };
2668
+ const onChange = (callback, options) => onStoreChange(callback, data, options);
2669
+ const storeSnapshot = () => snapshot(data);
2149
2670
  const scope = (scopeId, callback) => {
2150
2671
  if (callback) {
2151
2672
  if (!data.scopes.has(scopeId)) {
@@ -2198,8 +2719,11 @@ function storeFromStoreData(data, detach) {
2198
2719
  txn,
2199
2720
  reset,
2200
2721
  del,
2722
+ unset,
2201
2723
  data,
2202
2724
  scope,
2725
+ onChange,
2726
+ snapshot: storeSnapshot,
2203
2727
  detach
2204
2728
  };
2205
2729
  } else {
@@ -2210,8 +2734,11 @@ function storeFromStoreData(data, detach) {
2210
2734
  txn,
2211
2735
  reset,
2212
2736
  del,
2737
+ unset,
2213
2738
  data,
2214
- scope
2739
+ scope,
2740
+ onChange,
2741
+ snapshot: storeSnapshot
2215
2742
  };
2216
2743
  }
2217
2744
  }
@@ -2392,12 +2919,13 @@ var unmountOrphanedDeps = (state, data, visited) => {
2392
2919
  };
2393
2920
 
2394
2921
  // src/store.ts
2395
- var store = (idOrOptions) => {
2396
- const id = typeof idOrOptions === "string" ? idOrOptions : idOrOptions?.id;
2397
- 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;
2398
2926
  const data = createStoreData(id, undefined, options);
2399
2927
  return storeFromStoreData(data);
2400
- };
2928
+ }
2401
2929
 
2402
2930
  // src/globalStore.ts
2403
2931
  var globalStore = Object.assign(store("valdres-global-store"), {
@@ -2451,9 +2979,9 @@ var globalAtom = (defaultValue, options) => {
2451
2979
  const getSelf = () => globalStore.get(atom);
2452
2980
  const setSelf = (newValue) => globalStore.set(atom, newValue);
2453
2981
  const resetSelf = () => {
2454
- const snapshot = [...stores];
2982
+ const snapshot2 = [...stores];
2455
2983
  const subscribedStores = [];
2456
- for (const s of snapshot) {
2984
+ for (const s of snapshot2) {
2457
2985
  if (isLive(atom, s)) {
2458
2986
  subscribedStores.push(s);
2459
2987
  }
@@ -2463,7 +2991,7 @@ var globalAtom = (defaultValue, options) => {
2463
2991
  if (!firstError)
2464
2992
  firstError = e;
2465
2993
  };
2466
- for (const s of snapshot) {
2994
+ for (const s of snapshot2) {
2467
2995
  try {
2468
2996
  unmountAtom(atom, s);
2469
2997
  } catch (e) {
@@ -2475,11 +3003,11 @@ var globalAtom = (defaultValue, options) => {
2475
3003
  atom.maxAgeInterval.refCount = 0;
2476
3004
  atom.maxAgeInterval = undefined;
2477
3005
  }
2478
- for (const store2 of snapshot) {
3006
+ for (const store2 of snapshot2) {
2479
3007
  stores.delete(store2);
2480
3008
  store2.values.delete(atom);
2481
3009
  try {
2482
- propagateAtomUpdate([atom], store2);
3010
+ propagateAtomUpdate([atom], store2, false, undefined, "reset");
2483
3011
  } catch (e) {
2484
3012
  recordError(e);
2485
3013
  }
@@ -2703,9 +3231,10 @@ var cacheMeta = (sourceAtom) => {
2703
3231
  if (sourceAtom.__cacheMetaSelector)
2704
3232
  return sourceAtom.__cacheMetaSelector;
2705
3233
  if (!sourceAtom.__cacheMeta) {
2706
- sourceAtom.__cacheMeta = { equal, defaultValue: null };
3234
+ sourceAtom.__cacheMeta = { equal, defaultValue: null, __valdresInternal: true };
2707
3235
  }
2708
3236
  sourceAtom.__cacheMetaSelector = selector((get) => get(sourceAtom.__cacheMeta));
3237
+ sourceAtom.__cacheMetaSelector.__valdresInternal = true;
2709
3238
  return sourceAtom.__cacheMetaSelector;
2710
3239
  };
2711
3240
  // src/indexConstructor.ts
@@ -2781,9 +3310,9 @@ var isFamilySelector = (state) => isFamilyState(state) && isSelector(state);
2781
3310
 
2782
3311
  // src/index.ts
2783
3312
  if (globalThis.__valdres__) {
2784
- throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.6"}`);
3313
+ throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.7"}`);
2785
3314
  } else {
2786
- globalThis.__valdres__ = "1.0.0-beta.6";
3315
+ globalThis.__valdres__ = "1.0.0-beta.7";
2787
3316
  }
2788
3317
  export {
2789
3318
  store,