valdres 1.0.0-beta.6 → 1.0.0-beta.8

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,45 +122,7 @@ 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
- // src/utils/deepFreeze.ts
126
- var deepFreeze = (obj, seen) => {
127
- if (obj === null || obj === undefined)
128
- return obj;
129
- if (typeof obj !== "object" && typeof obj !== "function")
130
- return obj;
131
- if (Object.isFrozen(obj) || seen?.has(obj))
132
- return obj;
133
- if (Array.isArray(obj)) {
134
- for (const item of obj) {
135
- if (item && typeof item === "object") {
136
- seen ??= new WeakSet;
137
- seen.add(obj);
138
- deepFreeze(item, seen);
139
- }
140
- }
141
- } else {
142
- const propNames = Object.getOwnPropertyNames(obj);
143
- for (const name of propNames) {
144
- const value = obj[name];
145
- if (value && typeof value === "object") {
146
- seen ??= new WeakSet;
147
- seen.add(obj);
148
- deepFreeze(value, seen);
149
- }
150
- }
151
- }
152
- return Object.freeze(obj);
153
- };
154
-
155
- // src/lib/IS_PROD.ts
156
- var IS_PROD = typeof process !== "undefined" && process.env != null && process.env.NODE_ENV === "production";
157
-
158
- // src/lib/setValueInData.ts
125
+ // src/lib/trackScopeValue.ts
159
126
  var trackScopeValue = (key, data) => {
160
127
  const parent = data.parent;
161
128
  const indexKeys = data.scopeIndexKeys;
@@ -170,30 +137,6 @@ var trackScopeValue = (key, data) => {
170
137
  set.add(data);
171
138
  indexKeys.add(key);
172
139
  };
173
- var setValueInData = (atom, value, data) => {
174
- const isNewAtomInScope = data.parent && Object.hasOwn(atom, "defaultValue") && !data.values.has(atom);
175
- let written;
176
- if (atom.mutable || IS_PROD) {
177
- data.values.set(atom, value);
178
- written = value;
179
- } else {
180
- const frozenValue = value !== null && (typeof value === "object" || typeof value === "function") ? deepFreeze(value) : value;
181
- data.values.set(atom, frozenValue);
182
- written = frozenValue;
183
- }
184
- if (isNewAtomInScope) {
185
- trackScopeValue(atom, data);
186
- const subs = data.subscriptions.get(atom);
187
- if (subs) {
188
- for (const sub of subs)
189
- sub.reRoot?.();
190
- }
191
- }
192
- if (atom.maxAge !== undefined) {
193
- data.lastValueWriteAt.set(atom, Date.now());
194
- }
195
- return written;
196
- };
197
140
 
198
141
  // src/lib/atomFamilyIndex.ts
199
142
  var getAtomFamilyRenderedMap = (index) => {
@@ -292,13 +235,29 @@ var recursivelyUpdateIndexes = (data, family) => {
292
235
  recursivelyUpdateIndexes(scopedData, family);
293
236
  }
294
237
  };
238
+ var ensureFamilyAncestorChain = (family, data) => {
239
+ if (!data.parent)
240
+ return;
241
+ const parentIndex = initFamilyIndex(family, data.parent);
242
+ const own = data.values.get(family).__index;
243
+ if (own.parentIndex !== parentIndex) {
244
+ own.parentIndex = parentIndex;
245
+ own.rendered = null;
246
+ own.renderedArray = null;
247
+ data.values.set(family, renderAtomFamilyIndex(own));
248
+ recursivelyUpdateIndexes(data, family);
249
+ }
250
+ };
295
251
  var addFamilyAtomsToSet = (family, familyAtoms, data, timestamp) => {
296
252
  if (familyAtoms.size === 0)
297
- return;
253
+ return false;
298
254
  const index = findFamilyIndex(family, data);
299
255
  if (!index)
300
256
  throw new Error("index not found");
257
+ let membershipChanged = false;
301
258
  for (const atom of familyAtoms) {
259
+ if (!(index.created.has(atom) && !index.deleted.has(atom)))
260
+ membershipChanged = true;
302
261
  index.created.set(atom, timestamp);
303
262
  index.deleted.delete(atom);
304
263
  }
@@ -306,6 +265,7 @@ var addFamilyAtomsToSet = (family, familyAtoms, data, timestamp) => {
306
265
  index.renderedArray = null;
307
266
  data.values.set(family, renderAtomFamilyIndex(index));
308
267
  recursivelyUpdateIndexes(data, family);
268
+ return membershipChanged;
309
269
  };
310
270
 
311
271
  // src/errors/lib/generateSelectorTrace.ts
@@ -403,6 +363,276 @@ var cleanUpRejectedPromise = (selector, data, promise) => {
403
363
  data.values.delete(selector);
404
364
  };
405
365
 
366
+ // src/lib/notifyChangeListeners.ts
367
+ var changeListenerRegistry = { count: 0, selectorCount: 0 };
368
+ var hasChangeListener = (data) => {
369
+ if (changeListenerRegistry.count === 0)
370
+ return false;
371
+ let store = data;
372
+ while (store) {
373
+ const listeners = store.changeListeners;
374
+ if (listeners !== undefined && listeners.size > 0)
375
+ return true;
376
+ store = store.parent;
377
+ }
378
+ return false;
379
+ };
380
+ var hasSelectorChangeListener = (data) => {
381
+ if (changeListenerRegistry.selectorCount === 0)
382
+ return false;
383
+ let store = data;
384
+ while (store) {
385
+ const listeners = store.changeListeners;
386
+ if (listeners !== undefined) {
387
+ for (const flags of listeners.values()) {
388
+ if (flags.selectors)
389
+ return true;
390
+ }
391
+ }
392
+ store = store.parent;
393
+ }
394
+ return false;
395
+ };
396
+ var scopePath = (data) => {
397
+ const path = [];
398
+ let store = data;
399
+ while (store && store.parent) {
400
+ path.push(store.id);
401
+ store = store.parent;
402
+ }
403
+ path.reverse();
404
+ return path;
405
+ };
406
+ var notifyChangeListeners = (groups, meta) => {
407
+ let buckets;
408
+ for (const group of groups) {
409
+ if (group.changes.length === 0)
410
+ continue;
411
+ let store = group.data;
412
+ while (store) {
413
+ const listeners = store.changeListeners;
414
+ if (listeners !== undefined && listeners.size > 0) {
415
+ if (!buckets)
416
+ buckets = new Map;
417
+ let bucket = buckets.get(store);
418
+ if (!bucket) {
419
+ bucket = [];
420
+ buckets.set(store, bucket);
421
+ }
422
+ for (const change of group.changes)
423
+ bucket.push(change);
424
+ }
425
+ store = store.parent;
426
+ }
427
+ }
428
+ if (!buckets)
429
+ return;
430
+ for (const [store, changes] of buckets) {
431
+ const listeners = store.changeListeners;
432
+ if (listeners === undefined || listeners.size === 0)
433
+ continue;
434
+ let hasSelector;
435
+ let hasAtom;
436
+ let atomOnly;
437
+ let selectorOnly;
438
+ let firstError;
439
+ let hasError = false;
440
+ for (const [listener, flags] of [...listeners]) {
441
+ let payload;
442
+ if (flags.atoms && flags.selectors) {
443
+ payload = changes;
444
+ } else if (flags.atoms) {
445
+ if (atomOnly === undefined) {
446
+ hasSelector ??= changes.some((c) => c.type === "selector");
447
+ atomOnly = hasSelector ? changes.filter((c) => c.type === "atom") : changes;
448
+ }
449
+ payload = atomOnly;
450
+ } else if (flags.selectors) {
451
+ if (selectorOnly === undefined) {
452
+ hasAtom ??= changes.some((c) => c.type === "atom");
453
+ selectorOnly = hasAtom ? changes.filter((c) => c.type === "selector") : changes;
454
+ }
455
+ payload = selectorOnly;
456
+ } else {
457
+ continue;
458
+ }
459
+ if (payload.length === 0)
460
+ continue;
461
+ try {
462
+ listener(payload, meta);
463
+ } catch (error) {
464
+ if (!hasError) {
465
+ firstError = error;
466
+ hasError = true;
467
+ }
468
+ }
469
+ }
470
+ if (hasError)
471
+ throw firstError;
472
+ }
473
+ };
474
+ var buildChangeGroup = (data, changedAtoms, deletedAtoms, changedSelectors) => {
475
+ const scope = scopePath(data);
476
+ const changes = [];
477
+ let seen;
478
+ if (changedAtoms) {
479
+ for (const atom of changedAtoms) {
480
+ if (atom.__valdresInternal || isAtomFamily(atom))
481
+ continue;
482
+ if (seen?.has(atom))
483
+ continue;
484
+ (seen ??= new Set).add(atom);
485
+ changes.push({
486
+ type: "atom",
487
+ kind: "set",
488
+ state: atom,
489
+ value: data.values.get(atom),
490
+ scope
491
+ });
492
+ }
493
+ }
494
+ if (deletedAtoms) {
495
+ for (const atom of deletedAtoms) {
496
+ if (atom.__valdresInternal || isAtomFamily(atom))
497
+ continue;
498
+ changes.push({ type: "atom", kind: "delete", state: atom, scope });
499
+ }
500
+ }
501
+ if (changedSelectors) {
502
+ for (const selector of changedSelectors) {
503
+ if (selector.__valdresInternal || isSelectorFamily(selector))
504
+ continue;
505
+ if (!data.values.has(selector))
506
+ continue;
507
+ changes.push({
508
+ type: "selector",
509
+ state: selector,
510
+ value: data.values.get(selector),
511
+ scope
512
+ });
513
+ }
514
+ }
515
+ return { data, changes };
516
+ };
517
+ var emitGroup = (group, report) => {
518
+ if (group.changes.length === 0)
519
+ return;
520
+ if (typeof report === "string") {
521
+ notifyChangeListeners([group], { source: report });
522
+ } else {
523
+ report.groups.push(group);
524
+ }
525
+ };
526
+ var reportAtomChanges = (atoms, data, report, changedSelectors) => {
527
+ if (!hasChangeListener(data))
528
+ return;
529
+ const selectors = changedSelectors && changedSelectors.size > 0 && hasSelectorChangeListener(data) ? changedSelectors : undefined;
530
+ emitGroup(buildChangeGroup(data, atoms, undefined, selectors), report);
531
+ };
532
+ var reportUnsetAtom = (atom, data, revertedValue, report) => {
533
+ if (!hasChangeListener(data))
534
+ return;
535
+ const group = {
536
+ data,
537
+ changes: [
538
+ {
539
+ type: "atom",
540
+ kind: "unset",
541
+ state: atom,
542
+ value: revertedValue,
543
+ scope: scopePath(data)
544
+ }
545
+ ]
546
+ };
547
+ emitGroup(group, report);
548
+ };
549
+ var reportDeletedAtoms = (atoms, data, report, changedSelectors) => {
550
+ if (!hasChangeListener(data))
551
+ return;
552
+ const selectors = changedSelectors && changedSelectors.size > 0 && hasSelectorChangeListener(data) ? changedSelectors : undefined;
553
+ emitGroup(buildChangeGroup(data, undefined, atoms, selectors), report);
554
+ };
555
+ var reportSelectorChanges = (changedSelectors, data, report) => {
556
+ if (changedSelectors.size === 0)
557
+ return;
558
+ if (!hasSelectorChangeListener(data))
559
+ return;
560
+ emitGroup(buildChangeGroup(data, undefined, undefined, changedSelectors), report);
561
+ };
562
+ var createChangeSink = (name, source = "transaction") => ({
563
+ source,
564
+ name,
565
+ groups: []
566
+ });
567
+ var flushChangeSink = (sink) => {
568
+ if (sink.groups.length > 0) {
569
+ notifyChangeListeners(sink.groups, { source: sink.source, name: sink.name });
570
+ }
571
+ };
572
+
573
+ // src/utils/deepFreeze.ts
574
+ var deepFreeze = (obj, seen) => {
575
+ if (obj === null || obj === undefined)
576
+ return obj;
577
+ if (typeof obj !== "object" && typeof obj !== "function")
578
+ return obj;
579
+ if (Object.isFrozen(obj) || seen?.has(obj))
580
+ return obj;
581
+ if (Array.isArray(obj)) {
582
+ for (const item of obj) {
583
+ if (item && typeof item === "object") {
584
+ seen ??= new WeakSet;
585
+ seen.add(obj);
586
+ deepFreeze(item, seen);
587
+ }
588
+ }
589
+ } else {
590
+ const propNames = Object.getOwnPropertyNames(obj);
591
+ for (const name of propNames) {
592
+ const value = obj[name];
593
+ if (value && typeof value === "object") {
594
+ seen ??= new WeakSet;
595
+ seen.add(obj);
596
+ deepFreeze(value, seen);
597
+ }
598
+ }
599
+ }
600
+ return Object.freeze(obj);
601
+ };
602
+
603
+ // src/lib/IS_PROD.ts
604
+ var IS_PROD = typeof process !== "undefined" && process.env != null && process.env.NODE_ENV === "production";
605
+
606
+ // src/lib/setValueInData.ts
607
+ var setValueInData = (atom, value, data) => {
608
+ const isNewAtomInScope = data.parent && Object.hasOwn(atom, "defaultValue") && !data.values.has(atom);
609
+ const isNewFamilyInScope = !!data.parent && !data.values.has(atom) && isAtomFamily(atom);
610
+ let written;
611
+ if (atom.mutable || IS_PROD) {
612
+ data.values.set(atom, value);
613
+ written = value;
614
+ } else {
615
+ const frozenValue = value !== null && (typeof value === "object" || typeof value === "function") ? deepFreeze(value) : value;
616
+ data.values.set(atom, frozenValue);
617
+ written = frozenValue;
618
+ }
619
+ if (isNewAtomInScope) {
620
+ trackScopeValue(atom, data);
621
+ const subs = data.subscriptions.get(atom);
622
+ if (subs) {
623
+ for (const sub of subs)
624
+ sub.reRoot?.();
625
+ }
626
+ } else if (isNewFamilyInScope) {
627
+ trackScopeValue(atom, data);
628
+ ensureFamilyAncestorChain(atom, data);
629
+ }
630
+ if (atom.maxAge !== undefined) {
631
+ data.lastValueWriteAt.set(atom, Date.now());
632
+ }
633
+ return written;
634
+ };
635
+
406
636
  // src/lib/initSelector.ts
407
637
  var neverAbortedSignal = new AbortController().signal;
408
638
  var syncOptionsCache = new WeakMap;
@@ -549,7 +779,7 @@ var handleSelectorResult = (value, selector, data) => {
549
779
  const initializedAtomsSet = new Set;
550
780
  const res = initSelector(selector, data, initializedAtomsSet);
551
781
  if (initializedAtomsSet.size > 0) {
552
- propagateAtomUpdate([...initializedAtomsSet], data);
782
+ propagateAtomUpdate([...initializedAtomsSet], data, false, undefined, "async-set");
553
783
  }
554
784
  return res;
555
785
  }).catch(() => {
@@ -590,7 +820,12 @@ var handleSelectorResult = (value, selector, data) => {
590
820
  const dependents = data.stateDependents.get(selector);
591
821
  const subs = data.subscriptions.get(selector);
592
822
  if (subs && subs.size > 0 || dependents && dependents.size > 0) {
593
- propagateDirtySelectors([], new Set(dependents), data, new Set(subs), new Map);
823
+ const changedSelectors = changeListenerRegistry.selectorCount !== 0 && hasSelectorChangeListener(data) ? new Set : undefined;
824
+ propagateDirtySelectors([], new Set(dependents), data, new Set(subs), new Map, false, undefined, changedSelectors);
825
+ if (changedSelectors) {
826
+ changedSelectors.add(selector);
827
+ reportSelectorChanges(changedSelectors, data, "async-set");
828
+ }
594
829
  }
595
830
  }).catch(() => {
596
831
  pendingAsyncDeps.delete(value);
@@ -718,7 +953,7 @@ var addSetToSet = (fromSet, toSet) => {
718
953
  }
719
954
  }
720
955
  };
721
- var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, deletedFamilyAtoms = new Map, timestamp = performance.now(), notify) => {
956
+ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, deletedFamilyAtoms = new Map, timestamp = performance.now(), notify, report) => {
722
957
  const notifyEntry = notify ? notifyEntryFor(notify, data) : undefined;
723
958
  if (notifyEntry) {
724
959
  subscriptions = notifyEntry.subscriptions;
@@ -743,30 +978,35 @@ var propagateDeletedAtoms = (atoms, data, subscriptions = new Set, deletedFamily
743
978
  deleteFamilyAtomsFromSet(family, familyAtoms, data, timestamp);
744
979
  }
745
980
  }
746
- propagateDirtySelectors(atoms, selectors, data, subscriptions, deletedFamilyAtoms, false, notify);
981
+ const selectorActive = report !== undefined && changeListenerRegistry.selectorCount !== 0 && hasSelectorChangeListener(data);
982
+ const changedSelectors = selectorActive ? new Set : undefined;
983
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, deletedFamilyAtoms, false, notify, changedSelectors);
747
984
  if (notifyEntry)
748
985
  collectFamilyAtomsForNotify(notifyEntry, deletedFamilyAtoms);
749
- if (deletedFamilyAtoms.size > 0 && data.scopes && data.scopes.size > 0) {
750
- const scopeFamilies = new Map;
986
+ const hasScopeCascade = !!data.scopes && data.scopes.size > 0;
987
+ const watching = report !== undefined && changeListenerRegistry.count !== 0;
988
+ let localSink;
989
+ let effectiveReport = report;
990
+ if (selectorActive && hasScopeCascade && typeof report === "string") {
991
+ localSink = createChangeSink(undefined, report);
992
+ effectiveReport = localSink;
993
+ }
994
+ const reportIsSink = effectiveReport !== undefined && typeof effectiveReport !== "string";
995
+ if (watching && reportIsSink)
996
+ reportDeletedAtoms(atoms, data, effectiveReport, changedSelectors);
997
+ if (hasScopeCascade) {
998
+ const scopeAtoms = atoms.slice();
751
999
  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);
1000
+ scopeAtoms.push(family);
766
1001
  }
1002
+ propagateToScopes(scopeAtoms, data, false, notify, effectiveReport);
767
1003
  }
1004
+ if (watching && !reportIsSink)
1005
+ reportDeletedAtoms(atoms, data, effectiveReport, changedSelectors);
1006
+ if (localSink)
1007
+ flushChangeSink(localSink);
768
1008
  };
769
- var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
1009
+ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify, report, skipFamilyIndexUpdate = false, reportAtoms = true) => {
770
1010
  if (atoms.length === 1) {
771
1011
  const atom = atoms[0];
772
1012
  if (!isFamilyAtom(atom) && !isAtomFamily(atom)) {
@@ -779,6 +1019,9 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
779
1019
  else
780
1020
  callSubscribers(subs);
781
1021
  }
1022
+ if (reportAtoms && report !== undefined && changeListenerRegistry.count !== 0 && !isInitOnly) {
1023
+ reportAtomChanges(atoms, data, report);
1024
+ }
782
1025
  return;
783
1026
  }
784
1027
  }
@@ -790,13 +1033,14 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
790
1033
  for (const atom of atoms) {
791
1034
  addSetToSet(data.stateDependents.get(atom), selectors);
792
1035
  addSetToSet(data.subscriptions.get(atom), subscriptions);
793
- if (isFamilyAtom(atom)) {
1036
+ if (isFamilyAtom(atom) && !skipFamilyIndexUpdate) {
794
1037
  if (!updatedFamilyAtoms.has(atom.family)) {
795
1038
  updatedFamilyAtoms.set(atom.family, new Set);
796
1039
  }
797
1040
  updatedFamilyAtoms.get(atom.family).add(atom);
798
1041
  }
799
1042
  }
1043
+ let membershipChanged;
800
1044
  if (updatedFamilyAtoms.size > 0) {
801
1045
  const timestamp = performance.now();
802
1046
  for (const [family, familyAtoms] of updatedFamilyAtoms) {
@@ -804,7 +1048,11 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
804
1048
  addSetToSet(data.subscriptions.get(family), subscriptions);
805
1049
  if (familyAtoms.size === 0)
806
1050
  throw new Error("Should not be possible");
807
- addFamilyAtomsToSet(family, familyAtoms, data, timestamp);
1051
+ if (addFamilyAtomsToSet(family, familyAtoms, data, timestamp)) {
1052
+ if (!membershipChanged)
1053
+ membershipChanged = new Set;
1054
+ membershipChanged.add(family);
1055
+ }
808
1056
  }
809
1057
  }
810
1058
  if (data.scopes && data.scopes.size > 0) {
@@ -814,32 +1062,68 @@ var propagateAtomUpdate = (atoms, data, isInitOnly = false, notify) => {
814
1062
  }
815
1063
  }
816
1064
  }
817
- propagateDirtySelectors(atoms, selectors, data, subscriptions, updatedFamilyAtoms, isInitOnly, notify);
1065
+ const selectorActive = report !== undefined && !isInitOnly && changeListenerRegistry.selectorCount !== 0 && hasSelectorChangeListener(data);
1066
+ const changedSelectors = selectorActive ? new Set : undefined;
1067
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, updatedFamilyAtoms, isInitOnly, notify, changedSelectors);
818
1068
  if (notifyEntry)
819
1069
  collectFamilyAtomsForNotify(notifyEntry, updatedFamilyAtoms);
820
- if (data.scopes && data.scopes.size > 0) {
821
- propagateToScopes(atoms, data, isInitOnly, notify);
1070
+ const hasScopes = !!data.scopes && data.scopes.size > 0;
1071
+ const watching = report !== undefined && changeListenerRegistry.count !== 0 && !isInitOnly;
1072
+ let localSink;
1073
+ let effectiveReport = report;
1074
+ if (selectorActive && hasScopes && typeof report === "string") {
1075
+ localSink = createChangeSink(undefined, report);
1076
+ effectiveReport = localSink;
1077
+ }
1078
+ const reportIsSink = effectiveReport !== undefined && typeof effectiveReport !== "string";
1079
+ const emitOrigin = (rpt) => {
1080
+ if (reportAtoms) {
1081
+ reportAtomChanges(atoms, data, rpt, changedSelectors);
1082
+ } else if (changedSelectors && changedSelectors.size > 0) {
1083
+ reportSelectorChanges(changedSelectors, data, rpt);
1084
+ }
1085
+ };
1086
+ if (watching && reportIsSink)
1087
+ emitOrigin(effectiveReport);
1088
+ if (hasScopes) {
1089
+ let scopeAtoms = atoms;
1090
+ if (membershipChanged) {
1091
+ scopeAtoms = atoms.slice();
1092
+ for (const family of membershipChanged) {
1093
+ if (!scopeAtoms.includes(family))
1094
+ scopeAtoms.push(family);
1095
+ }
1096
+ }
1097
+ propagateToScopes(scopeAtoms, data, isInitOnly, notify, effectiveReport);
822
1098
  }
1099
+ if (watching && !reportIsSink)
1100
+ emitOrigin(effectiveReport);
1101
+ if (localSink)
1102
+ flushChangeSink(localSink);
823
1103
  };
824
- var propagateInScope = (atoms, data, isInitOnly = false, notify) => {
1104
+ var propagateInScope = (atoms, data, isInitOnly = false, notify, report) => {
825
1105
  const subscriptions = notify ? notifyEntryFor(notify, data).subscriptions : new Set;
826
1106
  const families = new Map;
827
1107
  const selectors = new Set;
828
1108
  for (const atom of atoms) {
829
1109
  addSetToSet(data.stateDependents.get(atom), selectors);
830
1110
  }
831
- propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly, notify);
1111
+ const changedSelectors = report !== undefined && !isInitOnly && changeListenerRegistry.selectorCount !== 0 && hasSelectorChangeListener(data) ? new Set : undefined;
1112
+ propagateDirtySelectors(atoms, selectors, data, subscriptions, families, isInitOnly, notify, changedSelectors);
1113
+ if (changedSelectors && changedSelectors.size > 0) {
1114
+ reportSelectorChanges(changedSelectors, data, report);
1115
+ }
832
1116
  if (data.scopes && data.scopes.size > 0) {
833
- propagateToScopes(atoms, data, isInitOnly, notify);
1117
+ propagateToScopes(atoms, data, isInitOnly, notify, report);
834
1118
  }
835
1119
  };
836
- var propagateToScopes = (atoms, data, isInitOnly, notify) => {
1120
+ var propagateToScopes = (atoms, data, isInitOnly, notify, report) => {
837
1121
  if (atoms.length === 1) {
838
1122
  const atom = atoms[0];
839
1123
  const shadowingScopes = isAtomFamily(atom) ? undefined : data.scopeValueIndex.get(atom);
840
1124
  for (const [, scope] of data.scopes) {
841
1125
  if (!shadowingScopes || !shadowingScopes.has(scope)) {
842
- propagateInScope(atoms, scope, isInitOnly, notify);
1126
+ propagateInScope(atoms, scope, isInitOnly, notify, report);
843
1127
  }
844
1128
  }
845
1129
  return;
@@ -859,7 +1143,7 @@ var propagateToScopes = (atoms, data, isInitOnly, notify) => {
859
1143
  }
860
1144
  if (!anyShadowed) {
861
1145
  for (const [, scope] of data.scopes) {
862
- propagateInScope(atoms, scope, isInitOnly, notify);
1146
+ propagateInScope(atoms, scope, isInitOnly, notify, report);
863
1147
  }
864
1148
  return;
865
1149
  }
@@ -876,20 +1160,20 @@ var propagateToScopes = (atoms, data, isInitOnly, notify) => {
876
1160
  }
877
1161
  }
878
1162
  if (atomsToUpdateInScope.length > 0) {
879
- propagateInScope(atomsToUpdateInScope, scope, isInitOnly, notify);
1163
+ propagateInScope(atomsToUpdateInScope, scope, isInitOnly, notify, report);
880
1164
  }
881
1165
  }
882
1166
  };
883
- var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false, notify) => {
1167
+ var propagateDirtySelectors = (updatedAtoms, selectors, data, subscriptions, families, isInitOnly = false, notify, changedSelectors) => {
884
1168
  const updatedInitializedAtoms = new Set(updatedAtoms);
885
1169
  if (selectors.size > 0) {
886
- propagateSelectorUpdates(selectors, data, subscriptions, updatedInitializedAtoms, isInitOnly);
1170
+ propagateSelectorUpdates(selectors, data, subscriptions, updatedInitializedAtoms, isInitOnly, changedSelectors);
887
1171
  }
888
1172
  if (!notify && subscriptions.size > 0) {
889
1173
  callSubscribers(subscriptions, families);
890
1174
  }
891
1175
  };
892
- var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly) => {
1176
+ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly, changedSelectors) => {
893
1177
  const closure = new Set(seeds);
894
1178
  {
895
1179
  const stack = [...seeds];
@@ -989,8 +1273,11 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
989
1273
  }
990
1274
  }
991
1275
  advance(selector, wasValueUpdated);
992
- if (wasValueUpdated && subscribers) {
993
- addSetToSet(subscribers, collectedSubscribers);
1276
+ if (wasValueUpdated) {
1277
+ if (changedSelectors)
1278
+ changedSelectors.add(selector);
1279
+ if (subscribers)
1280
+ addSetToSet(subscribers, collectedSubscribers);
994
1281
  }
995
1282
  }
996
1283
  if (!graphMutated)
@@ -1038,6 +1325,8 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
1038
1325
  }
1039
1326
  }
1040
1327
  if (wasValueUpdated) {
1328
+ if (changedSelectors)
1329
+ changedSelectors.add(selector);
1041
1330
  if (subscribers)
1042
1331
  addSetToSet(subscribers, collectedSubscribers);
1043
1332
  const downstream = data.stateDependents.get(selector);
@@ -1050,7 +1339,7 @@ var propagateDownstreamTopo = (seeds, data, collectedSubscribers, updatedInitial
1050
1339
  work = next;
1051
1340
  }
1052
1341
  };
1053
- var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false) => {
1342
+ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly = false, changedSelectors) => {
1054
1343
  if (selectors.size === 0)
1055
1344
  return;
1056
1345
  let downstreamSeeds;
@@ -1086,6 +1375,8 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
1086
1375
  }
1087
1376
  if (!wasValueUpdated)
1088
1377
  continue;
1378
+ if (changedSelectors)
1379
+ changedSelectors.add(selector);
1089
1380
  if (subscribers)
1090
1381
  addSetToSet(subscribers, collectedSubscribers);
1091
1382
  const downstream = data.stateDependents.get(selector);
@@ -1097,7 +1388,7 @@ var propagateSelectorUpdates = (selectors, data, collectedSubscribers, updatedIn
1097
1388
  }
1098
1389
  }
1099
1390
  if (downstreamSeeds && downstreamSeeds.size > 0) {
1100
- propagateDownstreamTopo(downstreamSeeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly);
1391
+ propagateDownstreamTopo(downstreamSeeds, data, collectedSubscribers, updatedInitializedAtoms, isInitOnly, changedSelectors);
1101
1392
  }
1102
1393
  };
1103
1394
 
@@ -1128,12 +1419,12 @@ var handlePromise = (atom, promise, currentValue, data, skipOnSet) => {
1128
1419
  if (atom.onSet && !skipOnSet)
1129
1420
  atom.onSet(resolvedValue, data);
1130
1421
  resolvePendingDefault(atom, data, resolvedValue);
1131
- propagateAtomUpdate([atom], data);
1422
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1132
1423
  }).catch(() => {
1133
1424
  if (data.values.get(atom) !== promise)
1134
1425
  return;
1135
1426
  setValueInData(atom, currentValue, data);
1136
- propagateAtomUpdate([atom], data);
1427
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1137
1428
  });
1138
1429
  };
1139
1430
  var setAtom = (atom, newValue, data, skipOnSet = false) => {
@@ -1155,9 +1446,9 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
1155
1446
  handlePromise(atom, promise, currentValue, data, skipOnSet);
1156
1447
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
1157
1448
  initializedAtomsSet.add(atom);
1158
- propagateAtomUpdate([...initializedAtomsSet], data);
1449
+ propagateAtomUpdate([...initializedAtomsSet], data, false, undefined, "set");
1159
1450
  } else {
1160
- propagateAtomUpdate([atom], data);
1451
+ propagateAtomUpdate([atom], data, false, undefined, "set");
1161
1452
  }
1162
1453
  return promise;
1163
1454
  }
@@ -1171,9 +1462,9 @@ var setAtom = (atom, newValue, data, skipOnSet = false) => {
1171
1462
  resolvePendingDefault(atom, data, syncValue);
1172
1463
  if (initializedAtomsSet && initializedAtomsSet.size > 0) {
1173
1464
  initializedAtomsSet.add(atom);
1174
- propagateAtomUpdate([...initializedAtomsSet], data);
1465
+ propagateAtomUpdate([...initializedAtomsSet], data, false, undefined, "set");
1175
1466
  } else {
1176
- propagateAtomUpdate([atom], data);
1467
+ propagateAtomUpdate([atom], data, false, undefined, "set");
1177
1468
  }
1178
1469
  return syncValue;
1179
1470
  };
@@ -1194,7 +1485,7 @@ var getAtomInitValue = (atom, data, initializedAtomsSet) => {
1194
1485
  if (data.values.get(atom) !== value)
1195
1486
  return;
1196
1487
  setValueInData(atom, resolvedValue, data);
1197
- propagateAtomUpdate([atom], data);
1488
+ propagateAtomUpdate([atom], data, false, undefined, "async-set");
1198
1489
  }, () => {
1199
1490
  if (data.values.get(atom) === value) {
1200
1491
  data.values.delete(atom);
@@ -1218,6 +1509,23 @@ var initAtom = (atom, data, initializedAtomsSet) => {
1218
1509
  }, data);
1219
1510
  };
1220
1511
 
1512
+ // src/lib/resolveAtomDefaultValue.ts
1513
+ var resolveAtomDefaultValue = (atom, data, initializedAtomsSet) => {
1514
+ if (atom.defaultValue === undefined) {
1515
+ let resolve;
1516
+ const promise = new Promise((r) => {
1517
+ resolve = r;
1518
+ });
1519
+ data.pendingDefaults.set(atom, { promise, resolve });
1520
+ return promise;
1521
+ } else if (typeof atom.defaultValue === "function") {
1522
+ return atom.defaultValue();
1523
+ } else if (isSelector(atom.defaultValue)) {
1524
+ return getState(atom.defaultValue, data, initializedAtomsSet);
1525
+ }
1526
+ return atom.defaultValue;
1527
+ };
1528
+
1221
1529
  // src/lib/getState.ts
1222
1530
  function getState(state, data, initializedAtomsSet, circularDependencySet) {
1223
1531
  if (data.values.has(state))
@@ -1229,7 +1537,21 @@ function getState(state, data, initializedAtomsSet, circularDependencySet) {
1229
1537
  const familyValue = data.values.get(state.family);
1230
1538
  if (familyValue?.__index) {
1231
1539
  if (isAtomDeletedInFamilyIndex(state, familyValue.__index)) {
1232
- return state.defaultValue;
1540
+ const value = resolveAtomDefaultValue(state, data, initializedAtomsSet);
1541
+ const cached = setValueInData(state, value, data);
1542
+ if (isPromiseLike(cached)) {
1543
+ cached.then((resolvedValue) => {
1544
+ if (data.values.get(state) !== cached)
1545
+ return;
1546
+ setValueInData(state, resolvedValue, data);
1547
+ propagateAtomUpdate([state], data, false, undefined, undefined, true);
1548
+ }, () => {
1549
+ if (data.values.get(state) === cached) {
1550
+ data.values.delete(state);
1551
+ }
1552
+ });
1553
+ }
1554
+ return cached;
1233
1555
  }
1234
1556
  }
1235
1557
  }
@@ -1287,6 +1609,63 @@ var resolveReactive = (value, data) => {
1287
1609
  return value;
1288
1610
  };
1289
1611
 
1612
+ // src/lib/unsetValue.ts
1613
+ var InvalidStateError = "unset() expects an atom.";
1614
+ var detachOwnValue = (atom, data) => {
1615
+ if (!data.values.has(atom))
1616
+ return false;
1617
+ data.values.delete(atom);
1618
+ if (atom.maxAge !== undefined)
1619
+ data.lastValueWriteAt.delete(atom);
1620
+ const parent = data.parent;
1621
+ if (parent) {
1622
+ const scopes = parent.scopeValueIndex.get(atom);
1623
+ if (scopes) {
1624
+ scopes.delete(data);
1625
+ if (scopes.size === 0)
1626
+ parent.scopeValueIndex.delete(atom);
1627
+ }
1628
+ data.scopeIndexKeys.delete(atom);
1629
+ }
1630
+ return true;
1631
+ };
1632
+ var reDelegateScopeSubscriptions = (atom, data) => {
1633
+ const subs = data.subscriptions.get(atom);
1634
+ if (subs) {
1635
+ for (const sub of subs)
1636
+ sub.reDelegate?.();
1637
+ }
1638
+ };
1639
+ var effectiveValueAfterUnset = (atom, data) => {
1640
+ const parent = data.parent;
1641
+ if (parent) {
1642
+ const initSet = new Set;
1643
+ const value = getState(atom, parent, initSet);
1644
+ if (initSet.size > 0)
1645
+ propagateAtomUpdate([...initSet], parent, true);
1646
+ return value;
1647
+ }
1648
+ if (data.values.has(atom))
1649
+ return data.values.get(atom);
1650
+ return getAtomInitValue(atom, data, new Set);
1651
+ };
1652
+ var unsetValue = (atom, data) => {
1653
+ if (!isAtom(atom))
1654
+ throw new Error(InvalidStateError);
1655
+ if (!detachOwnValue(atom, data))
1656
+ return;
1657
+ if (hasChangeListener(data)) {
1658
+ const sink = createChangeSink(undefined, "unset");
1659
+ reportUnsetAtom(atom, data, effectiveValueAfterUnset(atom, data), sink);
1660
+ propagateAtomUpdate([atom], data, false, undefined, sink, false, false);
1661
+ reDelegateScopeSubscriptions(atom, data);
1662
+ flushChangeSink(sink);
1663
+ } else {
1664
+ propagateAtomUpdate([atom], data, false);
1665
+ reDelegateScopeSubscriptions(atom, data);
1666
+ }
1667
+ };
1668
+
1290
1669
  // src/lib/createStoreData.ts
1291
1670
  var nextId = 0;
1292
1671
  var generateId = () => "__valdres_store_" + nextId++;
@@ -1320,7 +1699,10 @@ Object.defineProperties(lazyProto, {
1320
1699
  function createStoreData(id, parent, options) {
1321
1700
  const data = Object.create(lazyProto);
1322
1701
  data.id = id ?? generateId();
1323
- data.values = new WeakMap;
1702
+ const enumerable = options?.enumerable ?? parent?.enumerable ?? false;
1703
+ if (enumerable)
1704
+ data.enumerable = true;
1705
+ data.values = enumerable ? new Map : new WeakMap;
1324
1706
  data.scopes = new Map;
1325
1707
  data.scopeValueIndex = new WeakMap;
1326
1708
  data.pendingDefaults = new WeakMap;
@@ -1341,7 +1723,42 @@ var deleteFamilyAtom = (atom, data) => {
1341
1723
  if (atom.family) {
1342
1724
  atom.family.release(...atom.familyArgs);
1343
1725
  }
1344
- propagateDeletedAtoms([atom], data);
1726
+ propagateDeletedAtoms([atom], data, undefined, undefined, undefined, undefined, "delete");
1727
+ };
1728
+
1729
+ // src/lib/onStoreChange.ts
1730
+ var onStoreChange = (callback, data, options) => {
1731
+ const atoms = options?.atoms ?? true;
1732
+ const selectors = options?.selectors ?? false;
1733
+ if (!atoms && !selectors)
1734
+ return () => {};
1735
+ let listeners = data.changeListeners;
1736
+ if (!listeners) {
1737
+ listeners = new Map;
1738
+ data.changeListeners = listeners;
1739
+ }
1740
+ const prev = listeners.get(callback);
1741
+ listeners.set(callback, { atoms, selectors });
1742
+ if (!prev)
1743
+ changeListenerRegistry.count++;
1744
+ const prevSelectors = prev?.selectors ?? false;
1745
+ if (selectors && !prevSelectors)
1746
+ changeListenerRegistry.selectorCount++;
1747
+ else if (!selectors && prevSelectors)
1748
+ changeListenerRegistry.selectorCount--;
1749
+ return () => {
1750
+ const current = data.changeListeners;
1751
+ if (!current)
1752
+ return;
1753
+ const flags = current.get(callback);
1754
+ if (flags && current.delete(callback)) {
1755
+ changeListenerRegistry.count--;
1756
+ if (flags.selectors)
1757
+ changeListenerRegistry.selectorCount--;
1758
+ if (current.size === 0)
1759
+ data.changeListeners = undefined;
1760
+ }
1761
+ };
1345
1762
  };
1346
1763
 
1347
1764
  // src/lib/resetAtom.ts
@@ -1350,7 +1767,7 @@ var resetAtom = (atom, data) => {
1350
1767
  let value = getAtomInitValue(atom, data, initializedAtomsSet);
1351
1768
  setValueInData(atom, value, data);
1352
1769
  if (!isPromiseLike(value)) {
1353
- propagateAtomUpdate([atom], data);
1770
+ propagateAtomUpdate([atom], data, false, undefined, "reset");
1354
1771
  }
1355
1772
  if (initializedAtomsSet.size > 0) {
1356
1773
  throw new Error("Todo - propagateAtomUpdate on reset");
@@ -1358,6 +1775,38 @@ var resetAtom = (atom, data) => {
1358
1775
  return value;
1359
1776
  };
1360
1777
 
1778
+ // src/lib/snapshot.ts
1779
+ var warnedStores = new WeakSet;
1780
+ var collect = (data, scope, out) => {
1781
+ for (const [state, value] of data.values) {
1782
+ if (state.__valdresInternal)
1783
+ continue;
1784
+ if (isAtomFamily(state) || isSelectorFamily(state))
1785
+ continue;
1786
+ out.push({
1787
+ type: isSelector(state) ? "selector" : "atom",
1788
+ state,
1789
+ value,
1790
+ scope
1791
+ });
1792
+ }
1793
+ for (const [id, scopeData] of data.scopes) {
1794
+ collect(scopeData, [...scope, id], out);
1795
+ }
1796
+ };
1797
+ var snapshot = (data) => {
1798
+ if (!data.enumerable) {
1799
+ if (!warnedStores.has(data)) {
1800
+ warnedStores.add(data);
1801
+ console.warn("store.snapshot() requires an enumerable store. Create it with " + "`store({ enumerable: true })` or `store(id, { enumerable: true })` " + "to retain state enumerably. Returning [].");
1802
+ }
1803
+ return [];
1804
+ }
1805
+ const out = [];
1806
+ collect(data, [], out);
1807
+ return out;
1808
+ };
1809
+
1361
1810
  // src/utils/isFamily.ts
1362
1811
  var isFamily = (state) => isAtomFamily(state) || isSelectorFamily(state);
1363
1812
 
@@ -1447,11 +1896,11 @@ var installMaxAgeTimer = (state, data) => {
1447
1896
  const existing = globalState?.maxAgeInterval;
1448
1897
  if (existing) {
1449
1898
  existing.refCount++;
1450
- const metaAtom2 = state.__cacheMeta ??= { equal, defaultValue: null };
1899
+ const metaAtom2 = state.__cacheMeta ??= { equal, defaultValue: null, __valdresInternal: true };
1451
1900
  for (const s of globalState.stores) {
1452
1901
  if (s !== data && s.values.has(metaAtom2)) {
1453
1902
  setValueInData(metaAtom2, s.values.get(metaAtom2), data);
1454
- propagateAtomUpdate([metaAtom2], data);
1903
+ propagateAtomUpdate([metaAtom2], data, false, undefined, "revalidate");
1455
1904
  break;
1456
1905
  }
1457
1906
  }
@@ -1478,7 +1927,7 @@ var installMaxAgeTimer = (state, data) => {
1478
1927
  const getMaxAge = () => resolveReactive(state.maxAge, data);
1479
1928
  const getSWR = () => state.staleWhileRevalidate !== undefined ? resolveReactive(state.staleWhileRevalidate, data) : Infinity;
1480
1929
  const getStaleIfError = () => state.staleIfError !== undefined ? resolveReactive(state.staleIfError, data) : Infinity;
1481
- const metaAtom = state.__cacheMeta ??= { equal, defaultValue: null };
1930
+ const metaAtom = state.__cacheMeta ??= { equal, defaultValue: null, __valdresInternal: true };
1482
1931
  const updateMeta = () => {
1483
1932
  const meta = {
1484
1933
  isRevalidating: revalidating,
@@ -1490,11 +1939,11 @@ var installMaxAgeTimer = (state, data) => {
1490
1939
  if (globalState) {
1491
1940
  for (const store of globalState.stores) {
1492
1941
  setValueInData(metaAtom, meta, store);
1493
- propagateAtomUpdate([metaAtom], store);
1942
+ propagateAtomUpdate([metaAtom], store, false, undefined, "revalidate");
1494
1943
  }
1495
1944
  } else {
1496
1945
  setValueInData(metaAtom, meta, data);
1497
- propagateAtomUpdate([metaAtom], data);
1946
+ propagateAtomUpdate([metaAtom], data, false, undefined, "revalidate");
1498
1947
  }
1499
1948
  };
1500
1949
  const isPastStaleIfErrorWindow = () => {
@@ -1505,11 +1954,11 @@ var installMaxAgeTimer = (state, data) => {
1505
1954
  if (globalState) {
1506
1955
  for (const store of globalState.stores) {
1507
1956
  setValueInData(atom, val, store);
1508
- propagateAtomUpdate([atom], store);
1957
+ propagateAtomUpdate([atom], store, false, undefined, "revalidate");
1509
1958
  }
1510
1959
  } else {
1511
1960
  setValueInData(atom, val, data);
1512
- propagateAtomUpdate([atom], data);
1961
+ propagateAtomUpdate([atom], data, false, undefined, "revalidate");
1513
1962
  }
1514
1963
  };
1515
1964
  const getValueStore = () => {
@@ -1640,15 +2089,24 @@ var installMaxAgeTimer = (state, data) => {
1640
2089
  var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) => {
1641
2090
  let parentUnsubscribe;
1642
2091
  let dropDelegate;
1643
- if (data.parent && (!data.values.has(state) && isAtom(state) || isAtomFamily(state))) {
2092
+ let reDelegate;
2093
+ if (data.parent && (isAtom(state) || isAtomFamily(state))) {
1644
2094
  const originalCallback = callback;
1645
- parentUnsubscribe = subscribe(state, originalCallback, requireDeepEqualCheckBeforeCallback, data.parent);
2095
+ const delegateToParent = () => subscribe(state, originalCallback, requireDeepEqualCheckBeforeCallback, data.parent);
2096
+ if (isAtomFamily(state) || !data.values.has(state)) {
2097
+ parentUnsubscribe = delegateToParent();
2098
+ }
1646
2099
  dropDelegate = () => {
1647
2100
  if (parentUnsubscribe) {
1648
2101
  parentUnsubscribe();
1649
2102
  parentUnsubscribe = undefined;
1650
2103
  }
1651
2104
  };
2105
+ reDelegate = () => {
2106
+ if (!parentUnsubscribe) {
2107
+ parentUnsubscribe = delegateToParent();
2108
+ }
2109
+ };
1652
2110
  callback = (arg) => {
1653
2111
  dropDelegate();
1654
2112
  originalCallback(arg);
@@ -1673,13 +2131,15 @@ var subscribe = (state, callback, requireDeepEqualCheckBeforeCallback, data) =>
1673
2131
  callback,
1674
2132
  state,
1675
2133
  requireDeepEqualCheckBeforeCallback,
1676
- reRoot: dropDelegate
2134
+ reRoot: dropDelegate,
2135
+ reDelegate
1677
2136
  };
1678
2137
  } else {
1679
2138
  subscription = {
1680
2139
  callback,
1681
2140
  requireDeepEqualCheckBeforeCallback,
1682
- reRoot: dropDelegate
2141
+ reRoot: dropDelegate,
2142
+ reDelegate
1683
2143
  };
1684
2144
  }
1685
2145
  subscribers.add(subscription);
@@ -1735,10 +2195,10 @@ var writeAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false, onSetQueu
1735
2195
  };
1736
2196
 
1737
2197
  // src/lib/setAtoms.ts
1738
- var setAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false) => {
2198
+ var setAtoms = (pairs, data, initializedAtomsSet, skipOnSet = false, report) => {
1739
2199
  const updatedAtoms = writeAtoms(pairs, data, initializedAtomsSet, skipOnSet);
1740
2200
  if (updatedAtoms.length > 0) {
1741
- propagateAtomUpdate(updatedAtoms, data);
2201
+ propagateAtomUpdate(updatedAtoms, data, false, undefined, report);
1742
2202
  }
1743
2203
  };
1744
2204
 
@@ -1753,9 +2213,11 @@ class Transaction {
1753
2213
  data;
1754
2214
  parentTransaction;
1755
2215
  dirty;
2216
+ name;
1756
2217
  _scopedTransactions;
1757
2218
  _initializedAtomsSet;
1758
2219
  _deleteSet;
2220
+ _unsetSet;
1759
2221
  _selectorDependencies;
1760
2222
  _selectorCache;
1761
2223
  _atomMap;
@@ -1763,6 +2225,7 @@ class Transaction {
1763
2225
  this.data = data;
1764
2226
  this.parentTransaction = parentTransaction;
1765
2227
  this.dirty = false;
2228
+ this.name = undefined;
1766
2229
  this._atomMap = new Map;
1767
2230
  if (childTransaction) {
1768
2231
  this._scopedTransactions = new Map([
@@ -1773,6 +2236,9 @@ class Transaction {
1773
2236
  hasTxnOrData = (state) => {
1774
2237
  if (this._atomMap.has(state))
1775
2238
  return true;
2239
+ if (this._unsetSet?.has(state)) {
2240
+ return this.parentTransaction ? this.parentTransaction.hasTxnOrData(state) : false;
2241
+ }
1776
2242
  if (this.data.values.has(state))
1777
2243
  return true;
1778
2244
  if (this.parentTransaction)
@@ -1783,6 +2249,9 @@ class Transaction {
1783
2249
  if (this._atomMap.has(state)) {
1784
2250
  return this._atomMap.get(state);
1785
2251
  }
2252
+ if (this._unsetSet?.has(state)) {
2253
+ return this.parentTransaction ? this.parentTransaction.valueFromTxnOrData(state) : undefined;
2254
+ }
1786
2255
  if (this.data.values.has(state)) {
1787
2256
  return this.data.values.get(state);
1788
2257
  }
@@ -1795,6 +2264,9 @@ class Transaction {
1795
2264
  if (this.hasTxnOrData(state)) {
1796
2265
  return this.valueFromTxnOrData(state);
1797
2266
  }
2267
+ if (this._unsetSet?.has(state)) {
2268
+ return this.data.parent ? getState(state, this.data.parent, this.initializedAtomsSet) : getAtomInitValue(state, this.data, this.initializedAtomsSet);
2269
+ }
1798
2270
  return getState(state, this.data, this.initializedAtomsSet);
1799
2271
  } else if (isSelector(state)) {
1800
2272
  if (this.dirty) {
@@ -1830,6 +2302,7 @@ class Transaction {
1830
2302
  resolved = deepFreeze(resolved);
1831
2303
  }
1832
2304
  this._atomMap.set(atom, resolved);
2305
+ this._unsetSet?.delete(atom);
1833
2306
  if (!this.dirty)
1834
2307
  this.dirty = true;
1835
2308
  if (isFamilyAtom(atom)) {
@@ -1858,6 +2331,7 @@ class Transaction {
1858
2331
  if (index.deleted.has(atom))
1859
2332
  index.deleted.delete(atom);
1860
2333
  this._atomMap.set(atom, value);
2334
+ this._unsetSet?.delete(atom);
1861
2335
  }
1862
2336
  index.rendered = null;
1863
2337
  index.renderedArray = null;
@@ -1881,6 +2355,22 @@ class Transaction {
1881
2355
  this._atomMap.delete(atom);
1882
2356
  }
1883
2357
  };
2358
+ unset = (atom) => {
2359
+ if (!isAtom(atom))
2360
+ throw new Error("unset() expects an atom.");
2361
+ this._atomMap.delete(atom);
2362
+ if (!this._unsetSet)
2363
+ this._unsetSet = new Set;
2364
+ this._unsetSet.add(atom);
2365
+ };
2366
+ applyUnsets = (unsetSet, data) => {
2367
+ const unsetAtoms = [];
2368
+ for (const atom of unsetSet) {
2369
+ if (detachOwnValue(atom, data))
2370
+ unsetAtoms.push(atom);
2371
+ }
2372
+ return unsetAtoms;
2373
+ };
1884
2374
  scope = (scopeId, callback) => {
1885
2375
  if (this.data.scopes.has(scopeId)) {
1886
2376
  return this.scopedTransaction(scopeId).execute(callback, false);
@@ -1900,6 +2390,7 @@ class Transaction {
1900
2390
  reset = (atom) => {
1901
2391
  const value = getAtomInitValue(atom, this.data, this.initializedAtomsSet);
1902
2392
  this._atomMap.set(atom, value);
2393
+ this._unsetSet?.delete(atom);
1903
2394
  return value;
1904
2395
  };
1905
2396
  execute = (callback, autoCommit = true) => {
@@ -1916,19 +2407,58 @@ class Transaction {
1916
2407
  }
1917
2408
  };
1918
2409
  commit = () => {
2410
+ if (changeListenerRegistry.count === 0) {
2411
+ this.commitWork(undefined);
2412
+ return;
2413
+ }
2414
+ const sink = createChangeSink(this.name);
2415
+ try {
2416
+ this.commitWork(sink);
2417
+ } catch (error) {
2418
+ try {
2419
+ flushChangeSink(sink);
2420
+ } catch {}
2421
+ throw error;
2422
+ }
2423
+ flushChangeSink(sink);
2424
+ };
2425
+ commitWork = (sink) => {
1919
2426
  if (!this._scopedTransactions) {
1920
2427
  const initializedAtomsSet = new Set;
1921
- if (this._deleteSet?.size) {
2428
+ if (this._unsetSet?.size) {
1922
2429
  const notify2 = new Map;
1923
2430
  const updatedAtoms = writeAtoms(this._atomMap, this.data, initializedAtomsSet);
1924
2431
  if (updatedAtoms.length > 0) {
1925
- propagateAtomUpdate(updatedAtoms, this.data, false, notify2);
2432
+ propagateAtomUpdate(updatedAtoms, this.data, false, notify2, sink);
2433
+ }
2434
+ if (this._deleteSet?.size) {
2435
+ deleteAtomFamilyAtoms(this._deleteSet, this.data);
2436
+ propagateDeletedAtoms([...this._deleteSet], this.data, undefined, undefined, undefined, notify2, sink);
2437
+ }
2438
+ const unsetAtoms = this.applyUnsets(this._unsetSet, this.data);
2439
+ if (unsetAtoms.length > 0) {
2440
+ if (sink) {
2441
+ for (const atom of unsetAtoms) {
2442
+ reportUnsetAtom(atom, this.data, effectiveValueAfterUnset(atom, this.data), sink);
2443
+ }
2444
+ }
2445
+ propagateAtomUpdate(unsetAtoms, this.data, false, notify2, sink, false, false);
2446
+ }
2447
+ notifyDeferred(notify2);
2448
+ for (const atom of unsetAtoms) {
2449
+ reDelegateScopeSubscriptions(atom, this.data);
2450
+ }
2451
+ } else if (this._deleteSet?.size) {
2452
+ const notify2 = new Map;
2453
+ const updatedAtoms = writeAtoms(this._atomMap, this.data, initializedAtomsSet);
2454
+ if (updatedAtoms.length > 0) {
2455
+ propagateAtomUpdate(updatedAtoms, this.data, false, notify2, sink);
1926
2456
  }
1927
2457
  deleteAtomFamilyAtoms(this._deleteSet, this.data);
1928
- propagateDeletedAtoms([...this._deleteSet], this.data, undefined, undefined, undefined, notify2);
2458
+ propagateDeletedAtoms([...this._deleteSet], this.data, undefined, undefined, undefined, notify2, sink);
1929
2459
  notifyDeferred(notify2);
1930
2460
  } else {
1931
- setAtoms(this._atomMap, this.data, initializedAtomsSet);
2461
+ setAtoms(this._atomMap, this.data, initializedAtomsSet, false, sink);
1932
2462
  }
1933
2463
  return;
1934
2464
  }
@@ -1942,6 +2472,9 @@ class Transaction {
1942
2472
  deleteAtomFamilyAtoms(txn._deleteSet, entry.data);
1943
2473
  entry.deleted = [...txn._deleteSet];
1944
2474
  }
2475
+ if (txn._unsetSet?.size) {
2476
+ entry.unsetAtoms = txn.applyUnsets(txn._unsetSet, entry.data);
2477
+ }
1945
2478
  }
1946
2479
  for (const entry of plan) {
1947
2480
  for (const [atom, value, data] of entry.onSets) {
@@ -1949,15 +2482,30 @@ class Transaction {
1949
2482
  }
1950
2483
  }
1951
2484
  const notify = new Map;
1952
- for (const { data, updatedAtoms, deleted } of plan) {
2485
+ for (const { data, updatedAtoms, deleted, unsetAtoms } of plan) {
1953
2486
  if (updatedAtoms.length > 0) {
1954
- propagateAtomUpdate(updatedAtoms, data, false, notify);
2487
+ propagateAtomUpdate(updatedAtoms, data, false, notify, sink);
1955
2488
  }
1956
2489
  if (deleted) {
1957
- propagateDeletedAtoms(deleted, data, undefined, undefined, undefined, notify);
2490
+ propagateDeletedAtoms(deleted, data, undefined, undefined, undefined, notify, sink);
2491
+ }
2492
+ if (unsetAtoms && unsetAtoms.length > 0) {
2493
+ if (sink) {
2494
+ for (const atom of unsetAtoms) {
2495
+ reportUnsetAtom(atom, data, effectiveValueAfterUnset(atom, data), sink);
2496
+ }
2497
+ }
2498
+ propagateAtomUpdate(unsetAtoms, data, false, notify, sink, false, false);
1958
2499
  }
1959
2500
  }
1960
2501
  notifyDeferred(notify);
2502
+ for (const { data, unsetAtoms } of plan) {
2503
+ if (unsetAtoms) {
2504
+ for (const atom of unsetAtoms) {
2505
+ reDelegateScopeSubscriptions(atom, data);
2506
+ }
2507
+ }
2508
+ }
1961
2509
  };
1962
2510
  collectStores = (plan) => {
1963
2511
  plan.push({
@@ -1965,6 +2513,7 @@ class Transaction {
1965
2513
  data: this.data,
1966
2514
  updatedAtoms: [],
1967
2515
  deleted: undefined,
2516
+ unsetAtoms: undefined,
1968
2517
  onSets: []
1969
2518
  });
1970
2519
  if (this._scopedTransactions) {
@@ -2007,7 +2556,13 @@ class Transaction {
2007
2556
  if (moveUpIfParent && this.parentTransaction)
2008
2557
  return this.parentTransaction.cloneFamilyIntoTxn(family, parentIndex, moveUpIfParent);
2009
2558
  const currentFamilyList = this.get(family);
2010
- const clonedIndex = cloneAtomFamilyIndex(currentFamilyList.__index, parentIndex);
2559
+ const scopeFirstMaterialization = this.data.parent && !this._atomMap.has(family) && !this.data.values.has(family);
2560
+ let clonedIndex;
2561
+ if (scopeFirstMaterialization) {
2562
+ clonedIndex = createAtomFamilyIndex(parentIndex ?? currentFamilyList.__index);
2563
+ } else {
2564
+ clonedIndex = cloneAtomFamilyIndex(currentFamilyList.__index, parentIndex);
2565
+ }
2011
2566
  if (this._scopedTransactions?.size) {
2012
2567
  for (const [, scopedTxn] of this._scopedTransactions) {
2013
2568
  scopedTxn.cloneFamilyIntoTxn(family, clonedIndex, false);
@@ -2028,8 +2583,9 @@ class Transaction {
2028
2583
  }
2029
2584
  }
2030
2585
  }
2031
- var transaction = (callback, data) => {
2586
+ var transaction = (callback, data, name) => {
2032
2587
  const txn = new Transaction(data);
2588
+ txn.name = name;
2033
2589
  return txn.execute(callback);
2034
2590
  };
2035
2591
 
@@ -2140,12 +2696,19 @@ function storeFromStoreData(data, detach) {
2140
2696
  flushPendingTxn();
2141
2697
  return deleteFamilyAtom(atom, data);
2142
2698
  };
2699
+ const unset = (atom) => {
2700
+ if (data.batchUpdates)
2701
+ flushPendingTxn();
2702
+ return unsetValue(atom, data);
2703
+ };
2143
2704
  const sub = (state, callback, deepEqualCheckBeforeCallback = true) => subscribe(state, callback, deepEqualCheckBeforeCallback, data);
2144
- const txn = (callback) => {
2705
+ const txn = (callback, name) => {
2145
2706
  if (data.batchUpdates)
2146
2707
  flushPendingTxn();
2147
- return transaction(callback, data);
2708
+ return transaction(callback, data, name);
2148
2709
  };
2710
+ const onChange = (callback, options) => onStoreChange(callback, data, options);
2711
+ const storeSnapshot = () => snapshot(data);
2149
2712
  const scope = (scopeId, callback) => {
2150
2713
  if (callback) {
2151
2714
  if (!data.scopes.has(scopeId)) {
@@ -2198,8 +2761,11 @@ function storeFromStoreData(data, detach) {
2198
2761
  txn,
2199
2762
  reset,
2200
2763
  del,
2764
+ unset,
2201
2765
  data,
2202
2766
  scope,
2767
+ onChange,
2768
+ snapshot: storeSnapshot,
2203
2769
  detach
2204
2770
  };
2205
2771
  } else {
@@ -2210,8 +2776,11 @@ function storeFromStoreData(data, detach) {
2210
2776
  txn,
2211
2777
  reset,
2212
2778
  del,
2779
+ unset,
2213
2780
  data,
2214
- scope
2781
+ scope,
2782
+ onChange,
2783
+ snapshot: storeSnapshot
2215
2784
  };
2216
2785
  }
2217
2786
  }
@@ -2392,12 +2961,13 @@ var unmountOrphanedDeps = (state, data, visited) => {
2392
2961
  };
2393
2962
 
2394
2963
  // src/store.ts
2395
- var store = (idOrOptions) => {
2396
- const id = typeof idOrOptions === "string" ? idOrOptions : idOrOptions?.id;
2397
- const options = typeof idOrOptions === "object" ? idOrOptions : undefined;
2964
+ function store(idOrOptions, maybeOptions) {
2965
+ const optionsObject = typeof idOrOptions === "object" && idOrOptions !== null ? idOrOptions : undefined;
2966
+ const id = typeof idOrOptions === "string" ? idOrOptions : optionsObject?.id;
2967
+ const options = optionsObject ?? maybeOptions;
2398
2968
  const data = createStoreData(id, undefined, options);
2399
2969
  return storeFromStoreData(data);
2400
- };
2970
+ }
2401
2971
 
2402
2972
  // src/globalStore.ts
2403
2973
  var globalStore = Object.assign(store("valdres-global-store"), {
@@ -2451,9 +3021,9 @@ var globalAtom = (defaultValue, options) => {
2451
3021
  const getSelf = () => globalStore.get(atom);
2452
3022
  const setSelf = (newValue) => globalStore.set(atom, newValue);
2453
3023
  const resetSelf = () => {
2454
- const snapshot = [...stores];
3024
+ const snapshot2 = [...stores];
2455
3025
  const subscribedStores = [];
2456
- for (const s of snapshot) {
3026
+ for (const s of snapshot2) {
2457
3027
  if (isLive(atom, s)) {
2458
3028
  subscribedStores.push(s);
2459
3029
  }
@@ -2463,7 +3033,7 @@ var globalAtom = (defaultValue, options) => {
2463
3033
  if (!firstError)
2464
3034
  firstError = e;
2465
3035
  };
2466
- for (const s of snapshot) {
3036
+ for (const s of snapshot2) {
2467
3037
  try {
2468
3038
  unmountAtom(atom, s);
2469
3039
  } catch (e) {
@@ -2475,11 +3045,11 @@ var globalAtom = (defaultValue, options) => {
2475
3045
  atom.maxAgeInterval.refCount = 0;
2476
3046
  atom.maxAgeInterval = undefined;
2477
3047
  }
2478
- for (const store2 of snapshot) {
3048
+ for (const store2 of snapshot2) {
2479
3049
  stores.delete(store2);
2480
3050
  store2.values.delete(atom);
2481
3051
  try {
2482
- propagateAtomUpdate([atom], store2);
3052
+ propagateAtomUpdate([atom], store2, false, undefined, "reset");
2483
3053
  } catch (e) {
2484
3054
  recordError(e);
2485
3055
  }
@@ -2703,9 +3273,10 @@ var cacheMeta = (sourceAtom) => {
2703
3273
  if (sourceAtom.__cacheMetaSelector)
2704
3274
  return sourceAtom.__cacheMetaSelector;
2705
3275
  if (!sourceAtom.__cacheMeta) {
2706
- sourceAtom.__cacheMeta = { equal, defaultValue: null };
3276
+ sourceAtom.__cacheMeta = { equal, defaultValue: null, __valdresInternal: true };
2707
3277
  }
2708
3278
  sourceAtom.__cacheMetaSelector = selector((get) => get(sourceAtom.__cacheMeta));
3279
+ sourceAtom.__cacheMetaSelector.__valdresInternal = true;
2709
3280
  return sourceAtom.__cacheMetaSelector;
2710
3281
  };
2711
3282
  // src/indexConstructor.ts
@@ -2781,9 +3352,9 @@ var isFamilySelector = (state) => isFamilyState(state) && isSelector(state);
2781
3352
 
2782
3353
  // src/index.ts
2783
3354
  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"}`);
3355
+ throw new Error(`Error! An instance of valdres is already loaded. Loaded: ${globalThis.__valdres__}. Attempted to load: ${"1.0.0-beta.8"}`);
2785
3356
  } else {
2786
- globalThis.__valdres__ = "1.0.0-beta.6";
3357
+ globalThis.__valdres__ = "1.0.0-beta.8";
2787
3358
  }
2788
3359
  export {
2789
3360
  store,