atom.io 0.12.1 → 0.13.0

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.
Files changed (81) hide show
  1. package/dist/index.cjs.map +1 -1
  2. package/dist/index.d.cts +6 -3
  3. package/dist/index.d.ts +6 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/metafile-cjs.json +1 -1
  6. package/dist/metafile-esm.json +1 -1
  7. package/internal/dist/index.cjs +1561 -1512
  8. package/internal/dist/index.cjs.map +1 -1
  9. package/internal/dist/index.d.cts +24 -14
  10. package/internal/dist/index.d.ts +24 -14
  11. package/internal/dist/index.js +1560 -1513
  12. package/internal/dist/index.js.map +1 -1
  13. package/internal/dist/metafile-cjs.json +1 -1
  14. package/internal/dist/metafile-esm.json +1 -1
  15. package/internal/src/atom/create-atom.ts +4 -4
  16. package/internal/src/atom/delete-atom.ts +11 -11
  17. package/internal/src/atom/is-default.ts +5 -5
  18. package/internal/src/caching.ts +12 -10
  19. package/internal/src/families/create-atom-family.ts +3 -3
  20. package/internal/src/families/create-readonly-selector-family.ts +3 -3
  21. package/internal/src/families/create-selector-family.ts +4 -4
  22. package/internal/src/index.ts +1 -0
  23. package/internal/src/keys.ts +4 -4
  24. package/internal/src/lazy-map.ts +6 -2
  25. package/internal/src/lineage.ts +18 -0
  26. package/internal/src/mutable/create-mutable-atom.ts +5 -4
  27. package/internal/src/mutable/get-json-family.ts +3 -3
  28. package/internal/src/mutable/tracker.ts +13 -10
  29. package/internal/src/operation.ts +19 -19
  30. package/internal/src/selector/create-read-write-selector.ts +5 -4
  31. package/internal/src/selector/create-readonly-selector.ts +4 -3
  32. package/internal/src/selector/create-selector.ts +6 -6
  33. package/internal/src/selector/delete-selector.ts +10 -10
  34. package/internal/src/selector/get-selector-dependency-keys.ts +2 -2
  35. package/internal/src/selector/register-selector.ts +4 -4
  36. package/internal/src/selector/update-selector-atoms.ts +2 -2
  37. package/internal/src/set-state/copy-mutable-if-needed.ts +4 -3
  38. package/internal/src/set-state/copy-mutable-in-transaction.ts +10 -16
  39. package/internal/src/set-state/copy-mutable-into-new-store.ts +1 -1
  40. package/internal/src/set-state/evict-downstream.ts +5 -5
  41. package/internal/src/set-state/set-atom.ts +6 -1
  42. package/internal/src/set-state/stow-update.ts +7 -2
  43. package/internal/src/store/store.ts +31 -24
  44. package/internal/src/store/withdraw-new-family-member.ts +3 -4
  45. package/internal/src/store/withdraw.ts +58 -59
  46. package/internal/src/subject.ts +14 -0
  47. package/internal/src/subscribe/recall-state.ts +5 -5
  48. package/internal/src/timeline/add-atom-to-timeline.ts +37 -27
  49. package/internal/src/timeline/create-timeline.ts +6 -8
  50. package/internal/src/timeline/time-travel.ts +22 -4
  51. package/internal/src/transaction/abort-transaction.ts +5 -3
  52. package/internal/src/transaction/apply-transaction.ts +80 -58
  53. package/internal/src/transaction/build-transaction.ts +33 -23
  54. package/internal/src/transaction/create-transaction.ts +6 -9
  55. package/internal/src/transaction/index.ts +2 -10
  56. package/internal/src/transaction/redo-transaction.ts +15 -10
  57. package/internal/src/transaction/undo-transaction.ts +15 -16
  58. package/introspection/dist/index.cjs +2 -2
  59. package/introspection/dist/index.cjs.map +1 -1
  60. package/introspection/dist/index.js +3 -3
  61. package/introspection/dist/index.js.map +1 -1
  62. package/introspection/src/attach-atom-index.ts +2 -2
  63. package/introspection/src/attach-selector-index.ts +2 -2
  64. package/package.json +3 -3
  65. package/react-devtools/dist/index.cjs +22 -8
  66. package/react-devtools/dist/index.cjs.map +1 -1
  67. package/react-devtools/dist/index.d.cts +17 -9
  68. package/react-devtools/dist/index.d.ts +17 -9
  69. package/react-devtools/dist/index.js +22 -8
  70. package/react-devtools/dist/index.js.map +1 -1
  71. package/react-devtools/dist/metafile-cjs.json +1 -1
  72. package/react-devtools/dist/metafile-esm.json +1 -1
  73. package/react-devtools/src/Updates.tsx +22 -10
  74. package/src/transaction.ts +7 -2
  75. package/transceivers/set-rtx/dist/index.cjs.map +1 -1
  76. package/transceivers/set-rtx/dist/index.d.cts +2 -2
  77. package/transceivers/set-rtx/dist/index.d.ts +2 -2
  78. package/transceivers/set-rtx/dist/index.js.map +1 -1
  79. package/transceivers/set-rtx/dist/metafile-cjs.json +1 -1
  80. package/transceivers/set-rtx/dist/metafile-esm.json +1 -1
  81. package/transceivers/set-rtx/src/set-rtx.ts +3 -3
@@ -1,4 +1,4 @@
1
- import { AtomIOLogger, setState, getState, subscribe, subscribeToTimeline } from 'atom.io';
1
+ import { getState, setState, subscribe, subscribeToTimeline, AtomIOLogger, runTransaction } from 'atom.io';
2
2
  import { selectJson, stringifyJson, parseJson, selectJsonFamily } from 'atom.io/json';
3
3
 
4
4
  var __defProp = Object.defineProperty;
@@ -40,25 +40,209 @@ var Future = class extends Promise {
40
40
  }
41
41
  };
42
42
 
43
- // src/transaction/abort-transaction.ts
44
- var abortTransaction = (store) => {
45
- if (store.transactionStatus.phase === `idle`) {
46
- store.logger.warn(
47
- `\u{1F41E}`,
48
- `transaction`,
49
- `???`,
50
- `abortTransaction called outside of a transaction. This is probably a bug in AtomIO.`
43
+ // src/lineage.ts
44
+ function newest(scion) {
45
+ while (scion.child !== null) {
46
+ scion = scion.child;
47
+ }
48
+ return scion;
49
+ }
50
+ function eldest(scion) {
51
+ while (scion.parent !== null) {
52
+ scion = scion.parent;
53
+ }
54
+ return scion;
55
+ }
56
+
57
+ // src/caching.ts
58
+ function cacheValue(key, value, subject, store) {
59
+ const target = newest(store);
60
+ const currentValue = target.valueMap.get(key);
61
+ if (currentValue instanceof Future) {
62
+ currentValue.cancel();
63
+ }
64
+ if (value instanceof Promise) {
65
+ const future = new Future(value);
66
+ newest(store).valueMap.set(key, future);
67
+ future.then((resolved) => {
68
+ if (future.isCanceled) {
69
+ return;
70
+ }
71
+ cacheValue(key, resolved, subject, store);
72
+ subject.next({ newValue: resolved, oldValue: future });
73
+ }).catch((thrown) => {
74
+ if (thrown !== `canceled`) {
75
+ store.logger.error(`\u{1F4A5}`, `state`, key, `rejected:`, thrown);
76
+ }
77
+ });
78
+ return future;
79
+ }
80
+ target.valueMap.set(key, value);
81
+ return value;
82
+ }
83
+ var readCachedValue = (key, store) => {
84
+ return newest(store).valueMap.get(key);
85
+ };
86
+ var isValueCached = (key, store) => {
87
+ return newest(store).valueMap.has(key);
88
+ };
89
+ var evictCachedValue = (key, store) => {
90
+ const core = newest(store);
91
+ const currentValue = core.valueMap.get(key);
92
+ if (currentValue instanceof Future) {
93
+ currentValue.cancel();
94
+ }
95
+ if (core.operation.open) {
96
+ core.operation.prev.set(key, currentValue);
97
+ }
98
+ core.valueMap.delete(key);
99
+ store.logger.info(`\u{1F5D1}`, `state`, key, `evicted`);
100
+ };
101
+ var Tracker = class {
102
+ constructor(mutableState, store) {
103
+ this.unsubscribeFromInnerValue = null;
104
+ this.mutableState = mutableState;
105
+ const target = newest(store);
106
+ this.latestUpdateState = this.initializeState(mutableState, target);
107
+ this.observeCore(mutableState, this.latestUpdateState, target);
108
+ this.updateCore(mutableState, this.latestUpdateState, target);
109
+ target.trackers.set(mutableState.key, this);
110
+ }
111
+ initializeState(mutableState, store) {
112
+ const latestUpdateStateKey = `*${mutableState.key}`;
113
+ deleteAtom({ type: `atom`, key: latestUpdateStateKey }, store);
114
+ const familyMetaData = mutableState.family ? {
115
+ key: `*${mutableState.family.key}`,
116
+ subKey: mutableState.family.subKey
117
+ } : void 0;
118
+ const latestUpdateState = createAtom(
119
+ {
120
+ key: latestUpdateStateKey,
121
+ default: null
122
+ },
123
+ familyMetaData,
124
+ store
125
+ );
126
+ return latestUpdateState;
127
+ }
128
+ observeCore(mutableState, latestUpdateState, store) {
129
+ const originalInnerValue = getState(mutableState, store);
130
+ const target = newest(store);
131
+ this.unsubscribeFromInnerValue = originalInnerValue.subscribe(
132
+ `tracker:${store.config.name}:${target.transactionMeta === null ? `main` : target.transactionMeta.update.key}`,
133
+ (update) => {
134
+ const unsubscribe = store.subject.operationStatus.subscribe(
135
+ mutableState.key,
136
+ () => {
137
+ unsubscribe();
138
+ setState(latestUpdateState, update, store);
139
+ }
140
+ );
141
+ }
142
+ );
143
+ subscribe(
144
+ mutableState,
145
+ (update) => {
146
+ var _a;
147
+ if (update.newValue !== update.oldValue) {
148
+ (_a = this.unsubscribeFromInnerValue) == null ? void 0 : _a.call(this);
149
+ const target2 = newest(store);
150
+ this.unsubscribeFromInnerValue = update.newValue.subscribe(
151
+ `tracker:${store.config.name}:${target2.transactionMeta === null ? `main` : target2.transactionMeta.update.key}`,
152
+ (update2) => {
153
+ const unsubscribe = store.subject.operationStatus.subscribe(
154
+ mutableState.key,
155
+ () => {
156
+ unsubscribe();
157
+ setState(latestUpdateState, update2, store);
158
+ }
159
+ );
160
+ }
161
+ );
162
+ }
163
+ },
164
+ `${store.config.name}: tracker observing inner value`,
165
+ store
166
+ );
167
+ }
168
+ updateCore(mutableState, latestUpdateState, store) {
169
+ subscribe(
170
+ latestUpdateState,
171
+ ({ newValue, oldValue }) => {
172
+ const timelineId = store.timelineAtoms.getRelatedKey(
173
+ latestUpdateState.key
174
+ );
175
+ if (timelineId) {
176
+ const timelineData = store.timelines.get(timelineId);
177
+ if (timelineData == null ? void 0 : timelineData.timeTraveling) {
178
+ const unsubscribe2 = subscribeToTimeline(
179
+ { key: timelineId, type: `timeline` },
180
+ (update) => {
181
+ unsubscribe2();
182
+ setState(
183
+ mutableState,
184
+ (transceiver) => {
185
+ if (update === `redo` && newValue) {
186
+ transceiver.do(newValue);
187
+ } else if (update === `undo` && oldValue) {
188
+ transceiver.undo(oldValue);
189
+ }
190
+ return transceiver;
191
+ },
192
+ store
193
+ );
194
+ }
195
+ );
196
+ return;
197
+ }
198
+ }
199
+ const unsubscribe = store.subject.operationStatus.subscribe(
200
+ latestUpdateState.key,
201
+ () => {
202
+ unsubscribe();
203
+ const mutable = getState(mutableState, store);
204
+ const updateNumber = mutable.getUpdateNumber(newValue);
205
+ const eventOffset = updateNumber - mutable.cacheUpdateNumber;
206
+ if (newValue && eventOffset === 1) {
207
+ setState(
208
+ mutableState,
209
+ (transceiver) => (transceiver.do(newValue), transceiver),
210
+ store
211
+ );
212
+ }
213
+ }
214
+ );
215
+ },
216
+ `${store.config.name}: tracker observing latest update`,
217
+ store
51
218
  );
52
- return;
53
219
  }
220
+ };
221
+
222
+ // src/mutable/create-mutable-atom.ts
223
+ function createMutableAtom(options, store) {
54
224
  store.logger.info(
55
- `\u{1FA82}`,
56
- `transaction`,
57
- store.transactionStatus.key,
58
- `Aborting transaction`
225
+ `\u{1F527}`,
226
+ `atom`,
227
+ options.key,
228
+ `creating in store "${store.config.name}"`
59
229
  );
60
- store.transactionStatus = { phase: `idle` };
61
- };
230
+ const coreState = createAtom(options, void 0, store);
231
+ new Tracker(coreState, store);
232
+ const jsonState = selectJson(coreState, options, store);
233
+ const target = newest(store);
234
+ subscribe(
235
+ jsonState,
236
+ () => {
237
+ const trackerHasBeenInitialized = newest(store).trackers.has(coreState.key);
238
+ if (!trackerHasBeenInitialized) {
239
+ new Tracker(coreState, store);
240
+ }
241
+ },
242
+ `tracker-initializer:${store == null ? void 0 : store.config.name}:${target.transactionMeta === null ? `main` : `${target.transactionMeta.update.key}`}`
243
+ );
244
+ return coreState;
245
+ }
62
246
 
63
247
  // src/store/deposit.ts
64
248
  function deposit(state) {
@@ -349,10 +533,22 @@ var Subject = class {
349
533
  }
350
534
  }
351
535
  };
536
+ var StatefulSubject = class extends Subject {
537
+ constructor(initialState) {
538
+ super();
539
+ this.state = initialState;
540
+ }
541
+ next(value) {
542
+ this.state = value;
543
+ super.next(value);
544
+ }
545
+ };
352
546
 
353
547
  // src/store/store.ts
354
548
  var Store = class {
355
549
  constructor(name, store = null) {
550
+ this.parent = null;
551
+ this.child = null;
356
552
  this.valueMap = /* @__PURE__ */ new Map();
357
553
  this.atoms = /* @__PURE__ */ new Map();
358
554
  this.selectors = /* @__PURE__ */ new Map();
@@ -384,10 +580,11 @@ var Store = class {
384
580
  selectorCreation: new Subject(),
385
581
  transactionCreation: new Subject(),
386
582
  timelineCreation: new Subject(),
583
+ transactionApplying: new StatefulSubject(null),
387
584
  operationStatus: new Subject()
388
585
  };
389
586
  this.operation = { open: false };
390
- this.transactionStatus = { phase: `idle` };
587
+ this.transactionMeta = null;
391
588
  this.config = {
392
589
  name: `IMPLICIT_STORE`
393
590
  };
@@ -411,7 +608,7 @@ var Store = class {
411
608
  if (store !== null) {
412
609
  this.valueMap = new Map(store == null ? void 0 : store.valueMap);
413
610
  this.operation = __spreadValues({}, store == null ? void 0 : store.operation);
414
- this.transactionStatus = __spreadValues({}, store == null ? void 0 : store.transactionStatus);
611
+ this.transactionMeta = (store == null ? void 0 : store.transactionMeta) ? __spreadValues({}, store == null ? void 0 : store.transactionMeta) : null;
415
612
  this.config = __spreadProps(__spreadValues({}, store == null ? void 0 : store.config), {
416
613
  name
417
614
  });
@@ -446,1681 +643,1531 @@ var clearStore = (store) => {
446
643
  store.config = config;
447
644
  };
448
645
 
449
- // src/timeline/add-atom-to-timeline.ts
450
- var addAtomToTimeline = (atomToken, tl, store) => {
451
- const atom = withdraw(atomToken, store);
452
- if (atom === void 0) {
453
- throw new Error(
454
- `Cannot subscribe to atom "${atomToken.key}" because it has not been initialized in store "${store.config.name}"`
455
- );
646
+ // src/store/withdraw.ts
647
+ function withdraw(token, store) {
648
+ var _a, _b, _c, _d;
649
+ const target = newest(store);
650
+ const state = (_d = (_c = (_b = (_a = target.atoms.get(token.key)) != null ? _a : target.selectors.get(token.key)) != null ? _b : target.readonlySelectors.get(token.key)) != null ? _c : target.transactions.get(token.key)) != null ? _d : target.timelines.get(token.key);
651
+ if (state) {
652
+ return state;
456
653
  }
457
- atom.subject.subscribe(`timeline`, (update) => {
458
- var _a, _b, _c, _d;
459
- const currentSelectorKey = store.operation.open && store.operation.token.type === `selector` ? store.operation.token.key : null;
460
- const currentSelectorTime = store.operation.open && store.operation.token.type === `selector` ? store.operation.time : null;
461
- const currentTransactionKey = store.transactionStatus.phase === `applying` ? store.transactionStatus.key : null;
462
- const currentTransactionTime = store.transactionStatus.phase === `applying` ? store.transactionStatus.time : null;
654
+ return void 0;
655
+ }
656
+
657
+ // src/store/withdraw-new-family-member.ts
658
+ function withdrawNewFamilyMember(token, store) {
659
+ if (token.family) {
463
660
  store.logger.info(
464
- `\u23F3`,
465
- `timeline`,
466
- tl.key,
467
- `atom`,
468
- atomToken.key,
469
- `went`,
470
- update.oldValue,
471
- `->`,
472
- update.newValue,
473
- currentTransactionKey ? `in transaction "${currentTransactionKey}"` : currentSelectorKey ? `in selector "${currentSelectorKey}"` : ``
661
+ `\u{1F46A}`,
662
+ token.type,
663
+ token.key,
664
+ `creating new family member in store "${store.config.name}"`
474
665
  );
475
- if (tl.timeTraveling === null) {
476
- if (tl.selectorTime && tl.selectorTime !== currentSelectorTime) {
477
- const mostRecentUpdate = tl.history.at(-1);
478
- if (mostRecentUpdate === void 0) {
479
- throw new Error(
480
- `Timeline "${tl.key}" has a selectorTime, but no history. This is most likely a bug in AtomIO.`
481
- );
482
- }
483
- }
484
- if (currentTransactionKey && store.transactionStatus.phase === `applying`) {
485
- const currentTransaction = withdraw(
486
- { key: currentTransactionKey, type: `transaction` },
487
- store
488
- );
489
- if (currentTransaction === void 0) {
490
- throw new Error(
491
- `Transaction "${currentTransactionKey}" not found in store "${store.config.name}". This is surprising, because we are in the application phase of "${currentTransactionKey}".`
492
- );
493
- }
494
- if (tl.transactionKey !== currentTransactionKey) {
495
- if (tl.transactionKey) {
496
- store.logger.error(
497
- `\u{1F41E}`,
498
- `timeline`,
499
- tl.key,
500
- `unable to resolve transaction "${tl.transactionKey}. This is probably a bug in AtomIO.`
501
- );
502
- }
503
- tl.transactionKey = currentTransactionKey;
504
- const unsubscribe = currentTransaction.subject.subscribe(
505
- `timeline:${tl.key}`,
506
- (update2) => {
507
- var _a2, _b2;
508
- unsubscribe();
509
- if (tl.timeTraveling === null && currentTransactionTime) {
510
- if (tl.at !== tl.history.length) {
511
- tl.history.splice(tl.at);
512
- }
513
- const atomUpdates = update2.atomUpdates.filter((atomUpdate) => {
514
- const core = target(store);
515
- const atomOrFamilyKeys = core.timelineAtoms.getRelatedKeys(
516
- tl.key
517
- );
518
- return atomOrFamilyKeys ? [...atomOrFamilyKeys].some(
519
- (key) => {
520
- var _a3;
521
- return key === atomUpdate.key || key === ((_a3 = atomUpdate.family) == null ? void 0 : _a3.key);
522
- }
523
- ) : false;
524
- });
525
- const timelineTransactionUpdate = __spreadProps(__spreadValues({
526
- type: `transaction_update`,
527
- timestamp: currentTransactionTime
528
- }, update2), {
529
- atomUpdates
530
- });
531
- const willCapture = (_b2 = (_a2 = tl.shouldCapture) == null ? void 0 : _a2.call(tl, timelineTransactionUpdate, tl)) != null ? _b2 : true;
532
- if (willCapture) {
533
- tl.history.push(timelineTransactionUpdate);
534
- tl.at = tl.history.length;
535
- tl.subject.next(timelineTransactionUpdate);
536
- }
537
- }
538
- tl.transactionKey = null;
539
- store.logger.info(
540
- `\u231B`,
541
- `timeline`,
542
- tl.key,
543
- `got a transaction_update "${update2.key}"`
544
- );
545
- }
546
- );
547
- }
548
- } else if (currentSelectorKey && currentSelectorTime) {
549
- let latestUpdate = tl.history.at(-1);
550
- if (currentSelectorTime !== tl.selectorTime) {
551
- latestUpdate = {
552
- type: `selector_update`,
553
- timestamp: currentSelectorTime,
554
- key: currentSelectorKey,
555
- atomUpdates: []
556
- };
557
- latestUpdate.atomUpdates.push(__spreadValues({
558
- key: atom.key,
559
- type: `atom_update`
560
- }, update));
561
- if (tl.at !== tl.history.length) {
562
- tl.history.splice(tl.at);
563
- }
564
- tl.history.push(latestUpdate);
565
- store.logger.info(
566
- `\u231B`,
567
- `timeline`,
568
- tl.key,
569
- `got a selector_update "${currentSelectorKey}" with`,
570
- latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
571
- );
572
- tl.at = tl.history.length;
573
- tl.selectorTime = currentSelectorTime;
574
- } else {
575
- if ((latestUpdate == null ? void 0 : latestUpdate.type) === `selector_update`) {
576
- latestUpdate.atomUpdates.push(__spreadValues({
577
- key: atom.key,
578
- type: `atom_update`
579
- }, update));
580
- store.logger.info(
581
- `\u231B`,
582
- `timeline`,
583
- tl.key,
584
- `set selector_update "${currentSelectorKey}" to`,
585
- latestUpdate == null ? void 0 : latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
586
- );
587
- }
588
- }
589
- if (latestUpdate) {
590
- const willCaptureSelectorUpdate = (_b = (_a = tl.shouldCapture) == null ? void 0 : _a.call(tl, latestUpdate, tl)) != null ? _b : true;
591
- if (willCaptureSelectorUpdate) {
592
- tl.subject.next(latestUpdate);
593
- } else {
594
- tl.history.pop();
595
- tl.at = tl.history.length;
596
- }
597
- }
598
- } else {
599
- const timestamp = Date.now();
600
- tl.selectorTime = null;
601
- if (tl.at !== tl.history.length) {
602
- tl.history.splice(tl.at);
603
- }
604
- const atomUpdate = {
605
- type: `atom_update`,
606
- timestamp,
607
- key: atom.key,
608
- oldValue: update.oldValue,
609
- newValue: update.newValue
610
- };
611
- if (atom.family) {
612
- atomUpdate.family = atom.family;
613
- }
614
- const willCapture = (_d = (_c = tl.shouldCapture) == null ? void 0 : _c.call(tl, atomUpdate, tl)) != null ? _d : true;
615
- store.logger.info(
616
- `\u231B`,
617
- `timeline`,
618
- tl.key,
619
- `got an atom_update to "${atom.key}"`
620
- );
621
- if (willCapture) {
622
- tl.history.push(atomUpdate);
623
- tl.at = tl.history.length;
624
- tl.subject.next(atomUpdate);
666
+ const target = newest(store);
667
+ const family = target.families.get(token.family.key);
668
+ if (family) {
669
+ const jsonSubKey = JSON.parse(token.family.subKey);
670
+ family(jsonSubKey);
671
+ const state = withdraw(token, store);
672
+ return state;
673
+ }
674
+ }
675
+ return void 0;
676
+ }
677
+
678
+ // src/families/create-atom-family.ts
679
+ function createAtomFamily(options, store) {
680
+ const subject = new Subject();
681
+ const atomFamily = Object.assign(
682
+ (key) => {
683
+ const subKey = stringifyJson(key);
684
+ const family = { key: options.key, subKey };
685
+ const fullKey = `${options.key}(${subKey})`;
686
+ const existing = withdraw({ key: fullKey, type: `atom` }, store);
687
+ let token;
688
+ if (existing) {
689
+ token = deposit(existing);
690
+ } else {
691
+ const individualOptions = {
692
+ key: fullKey,
693
+ default: options.default instanceof Function ? options.default(key) : options.default
694
+ };
695
+ if (options.effects) {
696
+ individualOptions.effects = options.effects(key);
625
697
  }
698
+ token = createAtom(individualOptions, family, store);
699
+ subject.next(token);
626
700
  }
701
+ return token;
702
+ },
703
+ {
704
+ key: options.key,
705
+ type: `atom_family`,
706
+ subject
627
707
  }
628
- });
629
- };
708
+ );
709
+ const target = newest(store);
710
+ target.families.set(options.key, atomFamily);
711
+ return atomFamily;
712
+ }
630
713
 
631
- // src/timeline/create-timeline.ts
632
- function createTimeline(options, store, data) {
633
- var _a, _b;
634
- const tl = __spreadProps(__spreadValues({
635
- type: `timeline`,
636
- key: options.key,
637
- at: 0,
638
- timeTraveling: null,
639
- selectorTime: null,
640
- transactionKey: null
641
- }, data), {
642
- history: (_a = data == null ? void 0 : data.history.map((update) => __spreadValues({}, update))) != null ? _a : [],
643
- install: (store2) => createTimeline(options, store2, tl),
644
- subject: new Subject()
645
- });
646
- if (options.shouldCapture) {
647
- tl.shouldCapture = options.shouldCapture;
648
- }
649
- const core = target(store);
650
- for (const tokenOrFamily of options.atoms) {
651
- const timelineKey = core.timelineAtoms.getRelatedKey(tokenOrFamily.key);
652
- if (timelineKey) {
653
- store.logger.error(
654
- `\u274C`,
655
- `timeline`,
656
- options.key,
657
- `Failed to add atom "${tokenOrFamily.key}" because it already belongs to timeline "${timelineKey}"`
658
- );
659
- continue;
660
- }
661
- if (tokenOrFamily.type === `atom_family`) {
662
- const family = tokenOrFamily;
663
- family.subject.subscribe(`timeline:${options.key}`, (token2) => {
664
- addAtomToTimeline(token2, tl, store);
665
- });
666
- for (const atom of core.atoms.values()) {
667
- if (((_b = atom.family) == null ? void 0 : _b.key) === family.key) {
668
- addAtomToTimeline(atom, tl, store);
669
- }
670
- }
671
- } else {
672
- const token2 = tokenOrFamily;
673
- if (`family` in token2 && token2.family) {
674
- const familyTimelineKey = core.timelineAtoms.getRelatedKey(
675
- token2.family.key
676
- );
677
- if (familyTimelineKey) {
678
- store.logger.error(
679
- `\u274C`,
680
- `timeline`,
681
- options.key,
682
- `Failed to add atom "${token2.key}" because its family "${token2.family.key}" already belongs to timeline "${familyTimelineKey}"`
683
- );
684
- continue;
685
- }
686
- }
687
- addAtomToTimeline(token2, tl, store);
688
- }
689
- core.timelineAtoms = core.timelineAtoms.set({
690
- atomKey: tokenOrFamily.key,
691
- timelineKey: options.key
692
- });
714
+ // src/operation.ts
715
+ var openOperation = (token, store) => {
716
+ const target = newest(store);
717
+ if (target.operation.open) {
718
+ store.logger.error(
719
+ `\u274C`,
720
+ token.type,
721
+ token.key,
722
+ `failed to setState during a setState for "${target.operation.token.key}"`
723
+ );
724
+ return `rejection`;
693
725
  }
694
- store.timelines.set(options.key, tl);
695
- const token = {
696
- key: options.key,
697
- type: `timeline`
726
+ target.operation = {
727
+ open: true,
728
+ done: /* @__PURE__ */ new Set(),
729
+ prev: /* @__PURE__ */ new Map(),
730
+ time: Date.now(),
731
+ token
698
732
  };
699
- store.subject.timelineCreation.next(token);
700
- return token;
701
- }
702
- var timeTravel = (direction, token, store) => {
703
- const action = direction === `forward` ? `redo` : `undo`;
704
733
  store.logger.info(
705
- direction === `forward` ? `\u23E9` : `\u23EA`,
706
- `timeline`,
734
+ `\u2B55`,
735
+ token.type,
707
736
  token.key,
708
- action
737
+ `operation start in store "${store.config.name}"${target.transactionMeta === null ? `` : ` ${target.transactionMeta.phase} "${target.transactionMeta.update.key}"`}`
709
738
  );
710
- const timelineData = store.timelines.get(token.key);
711
- if (!timelineData) {
712
- store.logger.error(
713
- `\u{1F41E}`,
714
- `timeline`,
715
- token.key,
716
- `Failed to ${action}. This timeline has not been initialized.`
739
+ };
740
+ var closeOperation = (store) => {
741
+ const target = newest(store);
742
+ if (target.operation.open) {
743
+ store.logger.info(
744
+ `\u{1F534}`,
745
+ target.operation.token.type,
746
+ target.operation.token.key,
747
+ `operation done in store "${store.config.name}"`
717
748
  );
718
- return;
719
749
  }
720
- if (direction === `forward` && timelineData.at === timelineData.history.length || direction === `backward` && timelineData.at === 0) {
750
+ target.operation = { open: false };
751
+ store.subject.operationStatus.next(target.operation);
752
+ };
753
+ var isDone = (key, store) => {
754
+ const target = newest(store);
755
+ if (!target.operation.open) {
721
756
  store.logger.warn(
722
- `\u{1F481}`,
723
- `timeline`,
724
- token.key,
725
- `Failed to ${action} at the ${direction === `forward` ? `end` : `beginning`} of timeline "${token.key}". There is nothing to ${action}.`
757
+ `\u{1F41E}`,
758
+ `unknown`,
759
+ key,
760
+ `isDone called outside of an operation. This is probably a bug.`
726
761
  );
727
- return;
762
+ return true;
728
763
  }
729
- timelineData.timeTraveling = direction === `forward` ? `into_future` : `into_past`;
730
- if (direction === `backward`) {
731
- --timelineData.at;
764
+ return target.operation.done.has(key);
765
+ };
766
+ var markDone = (key, store) => {
767
+ const target = newest(store);
768
+ if (!target.operation.open) {
769
+ store.logger.warn(
770
+ `\u{1F41E}`,
771
+ `unknown`,
772
+ key,
773
+ `markDone called outside of an operation. This is probably a bug.`
774
+ );
775
+ return;
732
776
  }
733
- const update = timelineData.history[timelineData.at];
734
- const updateValues = (atomUpdate) => {
735
- const { key, newValue, oldValue } = atomUpdate;
736
- const value = direction === `forward` ? newValue : oldValue;
737
- setState({ key, type: `atom` }, value, store);
738
- };
739
- switch (update.type) {
740
- case `atom_update`: {
741
- updateValues(update);
742
- break;
777
+ target.operation.done.add(key);
778
+ };
779
+
780
+ // src/set-state/become.ts
781
+ var become = (nextVersionOfThing) => (originalThing) => nextVersionOfThing instanceof Function ? nextVersionOfThing(
782
+ originalThing instanceof Function ? originalThing() : originalThing
783
+ ) : nextVersionOfThing;
784
+
785
+ // src/read-or-compute-value.ts
786
+ var readOrComputeValue = (state, store) => {
787
+ if (isValueCached(state.key, store)) {
788
+ store.logger.info(`\u{1F4D6}`, state.type, state.key, `reading cached value`);
789
+ return readCachedValue(state.key, store);
790
+ }
791
+ if (state.type !== `atom`) {
792
+ store.logger.info(`\u{1F9EE}`, state.type, state.key, `computing value`);
793
+ return state.get();
794
+ }
795
+ const fallback = state.default instanceof Function ? state.default() : state.default;
796
+ store.logger.info(
797
+ `\u{1F481}`,
798
+ `atom`,
799
+ state.key,
800
+ `could not find cached value; using default`,
801
+ fallback
802
+ );
803
+ return state.default instanceof Function ? state.default() : state.default;
804
+ };
805
+
806
+ // src/set-state/copy-mutable-if-needed.ts
807
+ function copyMutableIfNeeded(atom, transform, origin, target) {
808
+ const originValue = origin.valueMap.get(atom.key);
809
+ const targetValue = target.valueMap.get(atom.key);
810
+ if (originValue === targetValue) {
811
+ origin.logger.info(`\u{1F4C3}`, `atom`, `${atom.key}`, `copying`);
812
+ const jsonValue = transform.toJson(originValue);
813
+ const copiedValue = transform.fromJson(jsonValue);
814
+ target.valueMap.set(atom.key, copiedValue);
815
+ new Tracker(atom, origin);
816
+ return copiedValue;
817
+ }
818
+ return targetValue;
819
+ }
820
+
821
+ // src/set-state/copy-mutable-in-transaction.ts
822
+ function copyMutableIfWithinTransaction(oldValue, atom, store) {
823
+ const target = newest(store);
824
+ const parent = target.parent;
825
+ if (parent !== null) {
826
+ if (`toJson` in atom && `fromJson` in atom) {
827
+ const copiedValue = copyMutableIfNeeded(atom, atom, parent, target);
828
+ return copiedValue;
743
829
  }
744
- case `selector_update`:
745
- case `transaction_update`: {
746
- const updates = direction === `forward` ? update.atomUpdates : [...update.atomUpdates].reverse();
747
- for (const atomUpdate of updates) {
748
- updateValues(atomUpdate);
830
+ if (`family` in atom) {
831
+ const family = parent.families.get(atom.family.key);
832
+ if (family && family.type === `atom_family`) {
833
+ const result = copyMutableFamilyMemberWithinTransaction(
834
+ atom,
835
+ family,
836
+ parent,
837
+ target
838
+ );
839
+ if (result) {
840
+ return result;
841
+ }
749
842
  }
750
- break;
751
843
  }
752
844
  }
753
- if (direction === `forward`) {
754
- ++timelineData.at;
845
+ return oldValue;
846
+ }
847
+ function copyMutableFamilyMemberWithinTransaction(atom, family, origin, target) {
848
+ if (`toJson` in family && `fromJson` in family) {
849
+ const copyCreated = copyMutableIfNeeded(atom, family, origin, target);
850
+ return copyCreated;
755
851
  }
756
- timelineData.subject.next(action);
757
- timelineData.timeTraveling = null;
852
+ return null;
853
+ }
854
+
855
+ // src/set-state/emit-update.ts
856
+ var emitUpdate = (state, update, store) => {
758
857
  store.logger.info(
759
- `\u23F9\uFE0F`,
760
- `timeline`,
761
- token.key,
762
- `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`
858
+ `\u{1F4E2}`,
859
+ state.type,
860
+ state.key,
861
+ `went (`,
862
+ update.oldValue,
863
+ `->`,
864
+ update.newValue,
865
+ `) subscribers:`,
866
+ state.subject.subscribers
763
867
  );
868
+ state.subject.next(update);
764
869
  };
765
870
 
766
- // src/store/withdraw.ts
767
- function withdraw(token, store) {
768
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
769
- let core = target(store);
770
- let state = (_d = (_c = (_b = (_a = core.atoms.get(token.key)) != null ? _a : core.selectors.get(token.key)) != null ? _b : core.readonlySelectors.get(token.key)) != null ? _c : core.transactions.get(token.key)) != null ? _d : core.timelines.get(token.key);
771
- if (state) {
772
- return state;
773
- }
774
- if (store.transactionStatus.phase === `applying`) {
775
- core = store.transactionStatus.core;
776
- state = (_h = (_g = (_f = (_e = core.atoms.get(token.key)) != null ? _e : core.selectors.get(token.key)) != null ? _f : core.readonlySelectors.get(token.key)) != null ? _g : core.transactions.get(token.key)) != null ? _h : core.timelines.get(token.key);
777
- if (state) {
871
+ // src/set-state/evict-downstream.ts
872
+ var evictDownStream = (atom, store) => {
873
+ const target = newest(store);
874
+ const downstreamKeys = target.selectorAtoms.getRelatedKeys(atom.key);
875
+ store.logger.info(
876
+ `\u{1F9F9}`,
877
+ atom.type,
878
+ atom.key,
879
+ downstreamKeys ? `evicting ${downstreamKeys.size} states downstream:` : `no downstream states`,
880
+ downstreamKeys != null ? downstreamKeys : `to evict`
881
+ );
882
+ if (downstreamKeys) {
883
+ if (target.operation.open) {
778
884
  store.logger.info(
779
- `\u{1F6E0}\uFE0F`,
780
- token.type,
781
- token.key,
782
- `add ${token.type} "${token.key}"`
885
+ `\u{1F9F9}`,
886
+ atom.type,
887
+ atom.key,
888
+ `[ ${[...target.operation.done].join(`, `)} ] already done`
783
889
  );
784
- switch (state.type) {
785
- case `atom`: {
786
- store.atoms.set(token.key, state);
787
- store.valueMap.set(token.key, state.default);
788
- const stateKey = state.key;
789
- const familyKey = (_i = state.family) == null ? void 0 : _i.key;
790
- let timelineKey = core.timelineAtoms.getRelatedKey(stateKey);
791
- if (timelineKey === void 0 && typeof familyKey === `string`) {
792
- timelineKey = core.timelineAtoms.getRelatedKey(familyKey);
793
- }
794
- const timeline = typeof timelineKey === `string` ? store.timelines.get(timelineKey) : void 0;
795
- if (timeline) {
796
- addAtomToTimeline(state, timeline, store);
797
- }
798
- break;
799
- }
800
- case `selector`:
801
- core.selectors.set(token.key, state);
802
- break;
803
- case `readonly_selector`:
804
- core.readonlySelectors.set(token.key, state);
805
- break;
806
- case `transaction`:
807
- core.transactions.set(token.key, state);
808
- break;
809
- case `timeline`:
810
- core.timelines.set(token.key, state);
811
- break;
890
+ }
891
+ for (const key of downstreamKeys) {
892
+ if (isDone(key, store)) {
893
+ continue;
812
894
  }
813
- return state;
895
+ evictCachedValue(key, store);
896
+ markDone(key, store);
814
897
  }
815
898
  }
816
- return void 0;
817
- }
899
+ };
818
900
 
819
- // src/store/withdraw-new-family-member.ts
820
- function withdrawNewFamilyMember(token, store) {
821
- if (token.family) {
822
- store.logger.info(
823
- `\u{1F46A}`,
824
- token.type,
825
- token.key,
826
- `creating new family member in store "${store.config.name}"`
827
- );
828
- const core = target(store);
829
- const family = core.families.get(token.family.key);
830
- if (family) {
831
- const jsonSubKey = JSON.parse(token.family.subKey);
832
- family(jsonSubKey);
833
- const state = withdraw(token, store);
834
- return state;
835
- }
901
+ // src/set-state/stow-update.ts
902
+ function shouldUpdateBeStowed(key, update) {
903
+ if (isTransceiver(update.newValue)) {
904
+ return false;
836
905
  }
837
- return void 0;
906
+ if (key.includes(`\u{1F441}\u200D\u{1F5E8}`)) {
907
+ return false;
908
+ }
909
+ return true;
838
910
  }
839
-
840
- // src/transaction/apply-transaction.ts
841
- var applyTransaction = (output, store) => {
842
- if (store.transactionStatus.phase !== `building`) {
843
- store.logger.warn(
911
+ var stowUpdate = (state, update, store) => {
912
+ const { key } = state;
913
+ const target = newest(store);
914
+ if (target.transactionMeta === null || target.transactionMeta.phase !== `building`) {
915
+ store.logger.error(
844
916
  `\u{1F41E}`,
845
- `transaction`,
846
- `???`,
847
- `applyTransaction called outside of a transaction. This is probably a bug in AtomIO.`
917
+ `atom`,
918
+ key,
919
+ `stowUpdate called outside of a transaction. This is probably a bug.`
848
920
  );
849
921
  return;
850
922
  }
851
- store.transactionStatus.phase = `applying`;
852
- store.transactionStatus.output = output;
853
- const { atomUpdates } = store.transactionStatus;
854
- store.logger.info(
855
- `\u{1F6C4}`,
856
- `transaction`,
857
- store.transactionStatus.key,
858
- `Applying transaction with ${atomUpdates.length} updates:`,
859
- atomUpdates
860
- );
861
- for (const { key, newValue } of atomUpdates) {
862
- const token = { key, type: `atom` };
863
- if (!store.valueMap.has(token.key)) {
864
- if (token.family) {
865
- const family = store.families.get(token.family.key);
866
- if (family) {
867
- family(token.family.subKey);
868
- }
869
- } else {
870
- const newAtom = store.transactionStatus.core.atoms.get(token.key);
871
- if (!newAtom) {
872
- throw new Error(
873
- `Absurd Error: Atom "${token.key}" not found while copying updates from transaction "${store.transactionStatus.key}" to store "${store.config.name}"`
874
- );
875
- }
876
- store.atoms.set(newAtom.key, newAtom);
877
- store.valueMap.set(newAtom.key, newAtom.default);
878
- store.logger.info(
879
- `\u{1F528}`,
880
- `transaction`,
881
- store.transactionStatus.key,
882
- `Adding atom "${newAtom.key}"`
883
- );
884
- }
885
- }
886
- setState(token, newValue, store);
923
+ const shouldStow = shouldUpdateBeStowed(key, update);
924
+ if (!shouldStow) {
925
+ return;
887
926
  }
888
- const myTransaction = withdraw(
889
- { key: store.transactionStatus.key, type: `transaction` },
890
- store
891
- );
892
- if (myTransaction === void 0) {
893
- throw new Error(
894
- `Transaction "${store.transactionStatus.key}" not found. Absurd. How is this running?`
895
- );
927
+ const atomUpdate = __spreadValues({ key }, update);
928
+ if (state.family) {
929
+ atomUpdate.family = state.family;
896
930
  }
897
- myTransaction.subject.next({
898
- key: store.transactionStatus.key,
899
- atomUpdates,
900
- output,
901
- params: store.transactionStatus.params
902
- });
931
+ target.transactionMeta.update.updates.push(atomUpdate);
903
932
  store.logger.info(
904
- `\u{1F6EC}`,
905
- `transaction`,
906
- store.transactionStatus.key,
907
- `Finished applying transaction.`
933
+ `\u{1F4C1}`,
934
+ `atom`,
935
+ key,
936
+ `stowed (`,
937
+ update.oldValue,
938
+ `->`,
939
+ update.newValue,
940
+ `)`
908
941
  );
909
- store.transactionStatus = { phase: `idle` };
910
942
  };
911
943
 
912
- // src/lazy-map.ts
913
- var LazyMap = class extends Map {
914
- constructor(source) {
915
- super();
916
- this.source = source;
917
- this.deleted = /* @__PURE__ */ new Set();
918
- }
919
- get(key) {
920
- const has = super.has(key);
921
- if (has) {
922
- return super.get(key);
923
- }
924
- if (!this.deleted.has(key) && this.source.has(key)) {
925
- const value = this.source.get(key);
926
- super.set(key, value);
927
- return value;
928
- }
929
- return void 0;
930
- }
931
- _has(key) {
932
- return super.has(key);
944
+ // src/set-state/set-atom.ts
945
+ var setAtom = (atom, next, store) => {
946
+ const target = newest(store);
947
+ const oldValue = readOrComputeValue(atom, store);
948
+ let newValue = copyMutableIfWithinTransaction(oldValue, atom, store);
949
+ newValue = become(next)(newValue);
950
+ store.logger.info(`\u{1F4DD}`, `atom`, atom.key, `set to`, newValue);
951
+ newValue = cacheValue(atom.key, newValue, atom.subject, store);
952
+ if (isAtomDefault(atom.key, store)) {
953
+ markAtomAsNotDefault(atom.key, store);
933
954
  }
934
- has(key) {
935
- return !this.deleted.has(key) && (super.has(key) || this.source.has(key));
955
+ markDone(atom.key, store);
956
+ evictDownStream(atom, store);
957
+ const update = { oldValue, newValue };
958
+ if (target.transactionMeta === null || target.transactionMeta.phase === `applying`) {
959
+ emitUpdate(atom, update, store);
960
+ } else {
961
+ stowUpdate(atom, update, store);
936
962
  }
937
- delete(key) {
938
- this.deleted.add(key);
939
- return super.delete(key);
963
+ };
964
+
965
+ // src/set-state/set-atom-or-selector.ts
966
+ var setAtomOrSelector = (state, value, store) => {
967
+ if (state.type === `selector`) {
968
+ state.set(value);
969
+ } else {
970
+ setAtom(state, value, store);
940
971
  }
941
972
  };
942
973
 
943
- // src/transaction/build-transaction.ts
944
- var buildTransaction = (key, params, store) => {
945
- store.transactionStatus = {
946
- key,
947
- phase: `building`,
948
- time: Date.now(),
949
- core: {
950
- atoms: new LazyMap(store.atoms),
951
- atomsThatAreDefault: new Set(store.atomsThatAreDefault),
952
- families: new LazyMap(store.families),
953
- operation: { open: false },
954
- readonlySelectors: new LazyMap(store.readonlySelectors),
955
- timelines: new LazyMap(store.timelines),
956
- timelineAtoms: new Junction(store.timelineAtoms.toJSON()),
957
- trackers: /* @__PURE__ */ new Map(),
958
- transactions: new LazyMap(store.transactions),
959
- selectorAtoms: new Junction(store.selectorAtoms.toJSON()),
960
- selectorGraph: new Junction(store.selectorGraph.toJSON(), {
961
- makeContentKey: (...keys) => keys.sort().join(`:`)
962
- }),
963
- selectors: new LazyMap(store.selectors),
964
- valueMap: new LazyMap(store.valueMap)
965
- },
966
- atomUpdates: [],
967
- params,
968
- output: void 0
969
- };
970
- store.logger.info(
971
- `\u{1F6EB}`,
972
- `transaction`,
973
- key,
974
- `Building transaction with params:`,
975
- params
976
- );
974
+ // src/keys.ts
975
+ var isAtomKey = (key, store) => newest(store).atoms.has(key);
976
+ var isSelectorKey = (key, store) => newest(store).selectors.has(key);
977
+ var isReadonlySelectorKey = (key, store) => newest(store).readonlySelectors.has(key);
978
+ var isStateKey = (key, store) => isAtomKey(key, store) || isSelectorKey(key, store) || isReadonlySelectorKey(key, store);
979
+
980
+ // src/selector/get-selector-dependency-keys.ts
981
+ var getSelectorDependencyKeys = (key, store) => {
982
+ const sources = newest(store).selectorGraph.getRelationEntries({ downstreamSelectorKey: key }).filter(([_, { source }]) => source !== key).map(([_, { source }]) => source).filter((source) => isStateKey(source, store));
983
+ return sources;
977
984
  };
978
- function createTransaction(options, store) {
979
- const newTransaction = {
980
- key: options.key,
981
- type: `transaction`,
982
- run: (...params) => {
983
- buildTransaction(options.key, params, store);
984
- try {
985
- const output = options.do(
986
- {
987
- get: (token2) => getState(token2, store),
988
- set: (token2, value) => setState(token2, value, store)
989
- },
990
- ...params
991
- );
992
- applyTransaction(output, store);
993
- return output;
994
- } catch (thrown) {
995
- abortTransaction(store);
996
- store.logger.warn(`\u{1F4A5}`, `transaction`, options.key, `caught:`, thrown);
997
- throw thrown;
998
- }
999
- },
1000
- install: (store2) => createTransaction(options, store2),
1001
- subject: new Subject()
1002
- };
1003
- const core = target(store);
1004
- core.transactions.set(newTransaction.key, newTransaction);
1005
- const token = deposit(newTransaction);
1006
- store.subject.transactionCreation.next(token);
1007
- return token;
1008
- }
1009
- var target = (store) => store.transactionStatus.phase === `building` ? store.transactionStatus.core : store;
1010
- var redoTransactionUpdate = (update, store) => {
1011
- store.logger.info(`\u23ED\uFE0F`, `transaction`, update.key, `Redo`);
1012
- for (const { key, newValue } of update.atomUpdates) {
1013
- const token = { key, type: `atom` };
1014
- const state = withdraw(token, store);
1015
- if (state === void 0) {
985
+
986
+ // src/selector/trace-selector-atoms.ts
987
+ var traceSelectorAtoms = (selectorKey, directDependencyKey, store) => {
988
+ const rootKeys = [];
989
+ const indirectDependencyKeys = getSelectorDependencyKeys(
990
+ directDependencyKey,
991
+ store
992
+ );
993
+ let depth = 0;
994
+ while (indirectDependencyKeys.length > 0) {
995
+ const indirectDependencyKey = indirectDependencyKeys.shift();
996
+ ++depth;
997
+ if (depth > 99999) {
1016
998
  throw new Error(
1017
- `State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
999
+ `Maximum selector dependency depth exceeded (> 99999) in selector "${selectorKey}". This is likely due to a circular dependency.`
1000
+ );
1001
+ }
1002
+ if (!isAtomKey(indirectDependencyKey, store)) {
1003
+ indirectDependencyKeys.push(
1004
+ ...getSelectorDependencyKeys(indirectDependencyKey, store)
1018
1005
  );
1006
+ } else if (!rootKeys.includes(indirectDependencyKey)) {
1007
+ rootKeys.push(indirectDependencyKey);
1019
1008
  }
1020
- setState(state, newValue, store);
1021
1009
  }
1010
+ return rootKeys;
1022
1011
  };
1023
- var undoTransactionUpdate = (update, store) => {
1024
- store.logger.info(
1025
- `\u23EE\uFE0F`,
1026
- `transaction`,
1027
- update.key,
1028
- `Undoing transaction update`,
1029
- update
1012
+ var traceAllSelectorAtoms = (selectorKey, store) => {
1013
+ const directDependencyKeys = getSelectorDependencyKeys(selectorKey, store);
1014
+ return directDependencyKeys.flatMap(
1015
+ (depKey) => isAtomKey(depKey, store) ? depKey : traceSelectorAtoms(selectorKey, depKey, store)
1030
1016
  );
1031
- for (const { key, oldValue } of update.atomUpdates) {
1032
- const token = { key, type: `atom` };
1033
- const state = withdraw(token, store);
1017
+ };
1018
+
1019
+ // src/selector/update-selector-atoms.ts
1020
+ var updateSelectorAtoms = (selectorKey, dependency, store) => {
1021
+ const core = newest(store);
1022
+ if (dependency.type === `atom`) {
1023
+ core.selectorAtoms = core.selectorAtoms.set({
1024
+ selectorKey,
1025
+ atomKey: dependency.key
1026
+ });
1027
+ store.logger.info(
1028
+ `\u{1F50D}`,
1029
+ `selector`,
1030
+ selectorKey,
1031
+ `discovers root atom "${dependency.key}"`
1032
+ );
1033
+ } else {
1034
+ const rootKeys = traceSelectorAtoms(selectorKey, dependency.key, store);
1035
+ store.logger.info(
1036
+ `\u{1F50D}`,
1037
+ `selector`,
1038
+ selectorKey,
1039
+ `discovers root atoms: [ ${rootKeys.map((key) => `"${key}"`).join(`, `)} ]`
1040
+ );
1041
+ for (const atomKey of rootKeys) {
1042
+ core.selectorAtoms = core.selectorAtoms.set({
1043
+ selectorKey,
1044
+ atomKey
1045
+ });
1046
+ }
1047
+ }
1048
+ };
1049
+
1050
+ // src/selector/register-selector.ts
1051
+ var registerSelector = (selectorKey, store) => ({
1052
+ get: (dependency) => {
1053
+ const target = newest(store);
1054
+ const alreadyRegistered = target.selectorGraph.getRelationEntries({ downstreamSelectorKey: selectorKey }).some(([_, { source }]) => source === dependency.key);
1055
+ const dependencyState = withdraw(dependency, store);
1056
+ if (dependencyState === void 0) {
1057
+ throw new Error(
1058
+ `State "${dependency.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
1059
+ );
1060
+ }
1061
+ const dependencyValue = readOrComputeValue(dependencyState, store);
1062
+ store.logger.info(
1063
+ `\u{1F50C}`,
1064
+ `selector`,
1065
+ selectorKey,
1066
+ `registers dependency ( "${dependency.key}" =`,
1067
+ dependencyValue,
1068
+ `)`
1069
+ );
1070
+ if (!alreadyRegistered) {
1071
+ target.selectorGraph = target.selectorGraph.set(
1072
+ {
1073
+ upstreamSelectorKey: dependency.key,
1074
+ downstreamSelectorKey: selectorKey
1075
+ },
1076
+ {
1077
+ source: dependency.key
1078
+ }
1079
+ );
1080
+ }
1081
+ updateSelectorAtoms(selectorKey, dependency, store);
1082
+ return dependencyValue;
1083
+ },
1084
+ set: (stateToken, newValue) => {
1085
+ const state = withdraw(stateToken, store);
1034
1086
  if (state === void 0) {
1035
1087
  throw new Error(
1036
- `State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
1088
+ `State "${stateToken.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
1037
1089
  );
1038
1090
  }
1039
- setState(state, oldValue, store);
1091
+ setAtomOrSelector(state, newValue, store);
1092
+ }
1093
+ });
1094
+
1095
+ // src/selector/create-read-write-selector.ts
1096
+ var createReadWriteSelector = (options, family, store) => {
1097
+ const target = newest(store);
1098
+ const subject = new Subject();
1099
+ const { get, set } = registerSelector(options.key, store);
1100
+ const getSelf = () => {
1101
+ const value = options.get({ get });
1102
+ cacheValue(options.key, value, subject, store);
1103
+ return value;
1104
+ };
1105
+ const setSelf = (next) => {
1106
+ const oldValue = getSelf();
1107
+ const newValue = become(next)(oldValue);
1108
+ store.logger.info(
1109
+ `\u{1F4DD}`,
1110
+ `selector`,
1111
+ options.key,
1112
+ `set (`,
1113
+ oldValue,
1114
+ `->`,
1115
+ newValue,
1116
+ `)`
1117
+ );
1118
+ cacheValue(options.key, newValue, subject, store);
1119
+ markDone(options.key, store);
1120
+ if (target.transactionMeta === null) {
1121
+ subject.next({ newValue, oldValue });
1122
+ }
1123
+ options.set({ get, set }, newValue);
1124
+ };
1125
+ const mySelector = __spreadValues(__spreadProps(__spreadValues({}, options), {
1126
+ subject,
1127
+ install: (s) => createSelector(options, family, s),
1128
+ get: getSelf,
1129
+ set: setSelf,
1130
+ type: `selector`
1131
+ }), family && { family });
1132
+ target.selectors.set(options.key, mySelector);
1133
+ const initialValue = getSelf();
1134
+ store.logger.info(`\u2728`, mySelector.type, mySelector.key, `=`, initialValue);
1135
+ const token = {
1136
+ key: options.key,
1137
+ type: `selector`
1138
+ };
1139
+ if (family) {
1140
+ token.family = family;
1040
1141
  }
1142
+ store.subject.selectorCreation.next(token);
1143
+ return token;
1041
1144
  };
1042
1145
 
1043
- // src/transaction/index.ts
1044
- var TRANSACTION_PHASES = [`idle`, `building`, `applying`];
1146
+ // src/selector/create-readonly-selector.ts
1147
+ var createReadonlySelector = (options, family, store) => {
1148
+ const target = newest(store);
1149
+ const subject = new Subject();
1150
+ const { get } = registerSelector(options.key, store);
1151
+ const getSelf = () => {
1152
+ const value = options.get({ get });
1153
+ cacheValue(options.key, value, subject, store);
1154
+ return value;
1155
+ };
1156
+ const readonlySelector = __spreadValues(__spreadProps(__spreadValues({}, options), {
1157
+ subject,
1158
+ install: (s) => createSelector(options, family, s),
1159
+ get: getSelf,
1160
+ type: `readonly_selector`
1161
+ }), family && { family });
1162
+ target.readonlySelectors.set(options.key, readonlySelector);
1163
+ const initialValue = getSelf();
1164
+ store.logger.info(
1165
+ `\u2728`,
1166
+ readonlySelector.type,
1167
+ readonlySelector.key,
1168
+ `=`,
1169
+ initialValue
1170
+ );
1171
+ const token = {
1172
+ key: options.key,
1173
+ type: `readonly_selector`
1174
+ };
1175
+ if (family) {
1176
+ token.family = family;
1177
+ }
1178
+ store.subject.selectorCreation.next(token);
1179
+ return token;
1180
+ };
1045
1181
 
1046
- // src/caching.ts
1047
- function cacheValue(key, value, subject, store) {
1048
- const currentValue = target(store).valueMap.get(key);
1049
- if (currentValue instanceof Future) {
1050
- currentValue.cancel();
1182
+ // src/selector/create-selector.ts
1183
+ function createSelector(options, family, store) {
1184
+ const target = newest(store);
1185
+ const existingWritable = target.selectors.get(options.key);
1186
+ const existingReadonly = target.readonlySelectors.get(options.key);
1187
+ if (existingWritable || existingReadonly) {
1188
+ store.logger.error(
1189
+ `\u274C`,
1190
+ existingReadonly ? `readonly_selector` : `selector`,
1191
+ options.key,
1192
+ `Tried to create selector, but it already exists in the store. (Ignore if you are in development using hot module replacement.)`
1193
+ );
1051
1194
  }
1052
- if (value instanceof Promise) {
1053
- const future = new Future(value);
1054
- target(store).valueMap.set(key, future);
1055
- future.then((resolved) => {
1056
- if (future.isCanceled) {
1057
- return;
1058
- }
1059
- cacheValue(key, resolved, subject, store);
1060
- subject.next({ newValue: resolved, oldValue: future });
1061
- }).catch((thrown) => {
1062
- if (thrown !== `canceled`) {
1063
- store.logger.error(`\u{1F4A5}`, `state`, key, `rejected:`, thrown);
1064
- }
1065
- });
1066
- return future;
1195
+ if (`set` in options) {
1196
+ return createReadWriteSelector(options, family, store);
1067
1197
  }
1068
- target(store).valueMap.set(key, value);
1069
- return value;
1198
+ return createReadonlySelector(options, family, store);
1070
1199
  }
1071
- var readCachedValue = (key, store) => target(store).valueMap.get(key);
1072
- var isValueCached = (key, store) => target(store).valueMap.has(key);
1073
- var evictCachedValue = (key, store) => {
1074
- const core = target(store);
1075
- const currentValue = core.valueMap.get(key);
1076
- if (currentValue instanceof Future) {
1077
- currentValue.cancel();
1078
- }
1079
- if (core.operation.open) {
1080
- core.operation.prev.set(key, currentValue);
1081
- }
1082
- core.valueMap.delete(key);
1083
- store.logger.info(`\u{1F5D1}`, `state`, key, `evicted`);
1084
- };
1085
- var Tracker = class {
1086
- constructor(mutableState, store) {
1087
- this.unsubscribeFromInnerValue = null;
1088
- this.mutableState = mutableState;
1089
- this.latestUpdateState = this.initializeState(mutableState, store);
1090
- this.observeCore(mutableState, this.latestUpdateState, store);
1091
- this.updateCore(mutableState, this.latestUpdateState, store);
1092
- const core = target(store);
1093
- core.trackers.set(mutableState.key, this);
1094
- }
1095
- initializeState(mutableState, store) {
1096
- const latestUpdateStateKey = `*${mutableState.key}`;
1097
- deleteAtom({ type: `atom`, key: latestUpdateStateKey }, store);
1098
- const familyMetaData = mutableState.family ? {
1099
- key: `*${mutableState.family.key}`,
1100
- subKey: mutableState.family.subKey
1101
- } : void 0;
1102
- const latestUpdateState = createAtom(
1103
- {
1104
- key: latestUpdateStateKey,
1105
- default: null
1106
- },
1107
- familyMetaData,
1108
- store
1109
- );
1110
- return latestUpdateState;
1111
- }
1112
- observeCore(mutableState, latestUpdateState, store) {
1113
- const originalInnerValue = getState(mutableState, store);
1114
- this.unsubscribeFromInnerValue = originalInnerValue.subscribe(
1115
- `tracker:${store.config.name}:${store.transactionStatus.phase === `idle` ? `main` : store.transactionStatus.key}`,
1116
- (update) => {
1117
- const unsubscribe = store.subject.operationStatus.subscribe(
1118
- mutableState.key,
1119
- () => {
1120
- unsubscribe();
1121
- setState(latestUpdateState, update, store);
1122
- }
1123
- );
1124
- }
1125
- );
1126
- subscribe(
1127
- mutableState,
1128
- (update) => {
1129
- var _a;
1130
- if (update.newValue !== update.oldValue) {
1131
- (_a = this.unsubscribeFromInnerValue) == null ? void 0 : _a.call(this);
1132
- this.unsubscribeFromInnerValue = update.newValue.subscribe(
1133
- `tracker:${store.config.name}:${store.transactionStatus.phase === `idle` ? `main` : store.transactionStatus.key}`,
1134
- (update2) => {
1135
- const unsubscribe = store.subject.operationStatus.subscribe(
1136
- mutableState.key,
1137
- () => {
1138
- unsubscribe();
1139
- setState(latestUpdateState, update2, store);
1140
- }
1141
- );
1142
- }
1143
- );
1144
- }
1145
- },
1146
- `${store.config.name}: tracker observing inner value`,
1147
- store
1148
- );
1200
+
1201
+ // src/selector/delete-selector.ts
1202
+ function deleteSelector(selectorToken, store) {
1203
+ const target = newest(store);
1204
+ const { key } = selectorToken;
1205
+ switch (selectorToken.type) {
1206
+ case `selector`:
1207
+ target.selectors.delete(key);
1208
+ break;
1209
+ case `readonly_selector`:
1210
+ target.readonlySelectors.delete(key);
1211
+ break;
1149
1212
  }
1150
- updateCore(mutableState, latestUpdateState, store) {
1151
- subscribe(
1152
- latestUpdateState,
1153
- ({ newValue, oldValue }) => {
1154
- const timelineId = store.timelineAtoms.getRelatedKey(
1155
- latestUpdateState.key
1156
- );
1157
- if (timelineId) {
1158
- const timelineData = store.timelines.get(timelineId);
1159
- if (timelineData == null ? void 0 : timelineData.timeTraveling) {
1160
- const unsubscribe2 = subscribeToTimeline(
1161
- { key: timelineId, type: `timeline` },
1162
- (update) => {
1163
- unsubscribe2();
1164
- setState(
1165
- mutableState,
1166
- (transceiver) => {
1167
- if (update === `redo` && newValue) {
1168
- transceiver.do(newValue);
1169
- } else if (update === `undo` && oldValue) {
1170
- transceiver.undo(oldValue);
1171
- }
1172
- return transceiver;
1173
- },
1174
- store
1175
- );
1176
- }
1177
- );
1178
- return;
1179
- }
1180
- }
1181
- const unsubscribe = store.subject.operationStatus.subscribe(
1182
- latestUpdateState.key,
1183
- () => {
1184
- unsubscribe();
1185
- const mutable = getState(mutableState, store);
1186
- const updateNumber = mutable.getUpdateNumber(newValue);
1187
- const eventOffset = updateNumber - mutable.cacheUpdateNumber;
1188
- if (newValue && eventOffset === 1) {
1189
- setState(
1190
- mutableState,
1191
- (transceiver) => (transceiver.do(newValue), transceiver),
1192
- store
1193
- );
1194
- }
1195
- }
1196
- );
1197
- },
1198
- `${store.config.name}: tracker observing latest update`,
1199
- store
1200
- );
1213
+ target.valueMap.delete(key);
1214
+ target.selectorAtoms.delete(key);
1215
+ const downstreamTokens = target.selectorGraph.getRelationEntries({ upstreamSelectorKey: key }).filter(([_, { source }]) => source === key).map(
1216
+ ([downstreamSelectorKey]) => {
1217
+ var _a;
1218
+ return (_a = target.selectors.get(downstreamSelectorKey)) != null ? _a : target.readonlySelectors.get(downstreamSelectorKey);
1219
+ }
1220
+ );
1221
+ for (const downstreamToken of downstreamTokens) {
1222
+ if (downstreamToken) {
1223
+ deleteSelector(downstreamToken, store);
1224
+ }
1201
1225
  }
1202
- };
1226
+ target.selectorGraph.delete(key);
1227
+ store.logger.info(`\u{1F525}`, selectorToken.type, `${key}`, `deleted`);
1228
+ }
1203
1229
 
1204
- // src/mutable/create-mutable-atom.ts
1205
- function createMutableAtom(options, store) {
1206
- store.logger.info(
1207
- `\u{1F527}`,
1208
- `atom`,
1209
- options.key,
1210
- `creating in store "${store.config.name}"`
1211
- );
1212
- const coreState = createAtom(options, void 0, store);
1213
- new Tracker(coreState, store);
1214
- const jsonState = selectJson(coreState, options, store);
1215
- subscribe(
1216
- jsonState,
1217
- () => {
1218
- const trackerHasBeenInitialized = target(store).trackers.has(coreState.key);
1219
- if (!trackerHasBeenInitialized) {
1220
- new Tracker(coreState, store);
1230
+ // src/families/create-readonly-selector-family.ts
1231
+ function createReadonlySelectorFamily(options, store) {
1232
+ const subject = new Subject();
1233
+ return Object.assign(
1234
+ (key) => {
1235
+ const target = newest(store);
1236
+ const subKey = stringifyJson(key);
1237
+ const family = { key: options.key, subKey };
1238
+ const fullKey = `${options.key}(${subKey})`;
1239
+ const existing = target.readonlySelectors.get(fullKey);
1240
+ if (existing) {
1241
+ return deposit(existing);
1221
1242
  }
1243
+ return createSelector(
1244
+ {
1245
+ key: fullKey,
1246
+ get: options.get(key)
1247
+ },
1248
+ family,
1249
+ store
1250
+ );
1222
1251
  },
1223
- `tracker-initializer:${store == null ? void 0 : store.config.name}:${store.transactionStatus.phase === `idle` ? `main` : store.transactionStatus.key}`
1252
+ {
1253
+ key: options.key,
1254
+ type: `readonly_selector_family`,
1255
+ subject
1256
+ }
1224
1257
  );
1225
- return coreState;
1226
1258
  }
1227
- function createAtomFamily(options, store) {
1259
+ function createSelectorFamily(options, store) {
1260
+ const isReadonly = !(`set` in options);
1261
+ if (isReadonly) {
1262
+ return createReadonlySelectorFamily(options, store);
1263
+ }
1264
+ const target = newest(store);
1228
1265
  const subject = new Subject();
1229
- const atomFamily = Object.assign(
1266
+ const selectorFamily = Object.assign(
1230
1267
  (key) => {
1231
1268
  const subKey = stringifyJson(key);
1232
1269
  const family = { key: options.key, subKey };
1233
1270
  const fullKey = `${options.key}(${subKey})`;
1234
- const existing = withdraw({ key: fullKey, type: `atom` }, store);
1235
- let token;
1271
+ const existing = target.selectors.get(fullKey);
1236
1272
  if (existing) {
1237
- token = deposit(existing);
1238
- } else {
1239
- const individualOptions = {
1240
- key: fullKey,
1241
- default: options.default instanceof Function ? options.default(key) : options.default
1242
- };
1243
- if (options.effects) {
1244
- individualOptions.effects = options.effects(key);
1245
- }
1246
- token = createAtom(individualOptions, family, store);
1247
- subject.next(token);
1273
+ return deposit(existing);
1248
1274
  }
1275
+ const token = createSelector(
1276
+ {
1277
+ key: fullKey,
1278
+ get: options.get(key),
1279
+ set: options.set(key)
1280
+ },
1281
+ family,
1282
+ store
1283
+ );
1284
+ subject.next(token);
1249
1285
  return token;
1250
1286
  },
1251
1287
  {
1252
1288
  key: options.key,
1253
- type: `atom_family`,
1254
- subject
1289
+ type: `selector_family`
1255
1290
  }
1256
1291
  );
1257
- const core = target(store);
1258
- core.families.set(options.key, atomFamily);
1259
- return atomFamily;
1292
+ target.families.set(options.key, selectorFamily);
1293
+ return selectorFamily;
1260
1294
  }
1261
1295
 
1262
- // src/operation.ts
1263
- var openOperation = (token, store) => {
1264
- const core = target(store);
1265
- if (core.operation.open) {
1266
- store.logger.error(
1267
- `\u274C`,
1268
- token.type,
1269
- token.key,
1270
- `failed to setState during a setState for "${core.operation.token.key}"`
1296
+ // src/mutable/tracker-family.ts
1297
+ var FamilyTracker = class {
1298
+ constructor(findMutableState, store) {
1299
+ this.findLatestUpdateState = createAtomFamily(
1300
+ {
1301
+ key: `*${findMutableState.key}`,
1302
+ default: null
1303
+ },
1304
+ store
1271
1305
  );
1272
- return `rejection`;
1273
- }
1274
- core.operation = {
1275
- open: true,
1276
- done: /* @__PURE__ */ new Set(),
1277
- prev: /* @__PURE__ */ new Map(),
1278
- time: Date.now(),
1279
- token
1280
- };
1281
- store.logger.info(
1282
- `\u2B55`,
1283
- token.type,
1284
- token.key,
1285
- `operation start in store "${store.config.name}"${store.transactionStatus.phase === `idle` ? `` : ` ${store.transactionStatus.phase} "${store.transactionStatus.key}"`}`
1286
- );
1287
- };
1288
- var closeOperation = (store) => {
1289
- const core = target(store);
1290
- if (core.operation.open) {
1291
- store.logger.info(
1292
- `\u{1F534}`,
1293
- core.operation.token.type,
1294
- core.operation.token.key,
1295
- `operation done in store "${store.config.name}"`
1306
+ this.findMutableState = findMutableState;
1307
+ this.findMutableState.subject.subscribe(
1308
+ `store=${store.config.name}::tracker-atom-family`,
1309
+ (atomToken) => {
1310
+ if (atomToken.family) {
1311
+ const key = parseJson(atomToken.family.subKey);
1312
+ this.findLatestUpdateState(key);
1313
+ new Tracker(atomToken, store);
1314
+ }
1315
+ }
1296
1316
  );
1297
- }
1298
- core.operation = { open: false };
1299
- store.subject.operationStatus.next(core.operation);
1300
- };
1301
- var isDone = (key, store) => {
1302
- const core = target(store);
1303
- if (!core.operation.open) {
1304
- store.logger.warn(
1305
- `\u{1F41E}`,
1306
- `unknown`,
1307
- key,
1308
- `isDone called outside of an operation. This is probably a bug.`
1309
- );
1310
- return true;
1311
- }
1312
- return core.operation.done.has(key);
1313
- };
1314
- var markDone = (key, store) => {
1315
- const core = target(store);
1316
- if (!core.operation.open) {
1317
- store.logger.warn(
1318
- `\u{1F41E}`,
1319
- `unknown`,
1320
- key,
1321
- `markDone called outside of an operation. This is probably a bug.`
1317
+ this.findLatestUpdateState.subject.subscribe(
1318
+ `store=${store.config.name}::tracker-atom-family`,
1319
+ (atomToken) => {
1320
+ if (atomToken.family) {
1321
+ const key = parseJson(atomToken.family.subKey);
1322
+ const mutableAtomToken = this.findMutableState(key);
1323
+ new Tracker(mutableAtomToken, store);
1324
+ }
1325
+ }
1322
1326
  );
1323
- return;
1324
1327
  }
1325
- core.operation.done.add(key);
1326
1328
  };
1327
1329
 
1328
- // src/set-state/become.ts
1329
- var become = (nextVersionOfThing) => (originalThing) => nextVersionOfThing instanceof Function ? nextVersionOfThing(
1330
- originalThing instanceof Function ? originalThing() : originalThing
1331
- ) : nextVersionOfThing;
1330
+ // src/mutable/create-mutable-atom-family.ts
1331
+ function createMutableAtomFamily(options, store) {
1332
+ const coreFamily = Object.assign(
1333
+ createAtomFamily(options, store),
1334
+ options
1335
+ );
1336
+ selectJsonFamily(coreFamily, options);
1337
+ new FamilyTracker(coreFamily, store);
1338
+ return coreFamily;
1339
+ }
1332
1340
 
1333
- // src/read-or-compute-value.ts
1334
- var readOrComputeValue = (state, store) => {
1335
- if (isValueCached(state.key, store)) {
1336
- store.logger.info(`\u{1F4D6}`, state.type, state.key, `reading cached value`);
1337
- return readCachedValue(state.key, store);
1338
- }
1339
- if (state.type !== `atom`) {
1340
- store.logger.info(`\u{1F9EE}`, state.type, state.key, `computing value`);
1341
- return state.get();
1342
- }
1343
- const fallback = state.default instanceof Function ? state.default() : state.default;
1344
- store.logger.info(
1345
- `\u{1F481}`,
1346
- `atom`,
1347
- state.key,
1348
- `could not find cached value; using default`,
1349
- fallback
1341
+ // src/mutable/get-json-family.ts
1342
+ var getJsonFamily = (mutableAtomFamily, store) => {
1343
+ const target = newest(store);
1344
+ const key = `${mutableAtomFamily.key}:JSON`;
1345
+ const jsonFamily = target.families.get(
1346
+ key
1350
1347
  );
1351
- return state.default instanceof Function ? state.default() : state.default;
1348
+ return jsonFamily;
1352
1349
  };
1353
1350
 
1354
- // src/set-state/copy-mutable-if-needed.ts
1355
- function copyMutableIfNeeded(atom, transform, origin, target2) {
1356
- const originValue = origin.valueMap.get(atom.key);
1357
- const targetValue = target2.valueMap.get(atom.key);
1358
- if (originValue === targetValue) {
1359
- origin.logger.info(`\u{1F4C3}`, `atom`, `${atom.key}`, `copying`);
1360
- const copiedValue = transform.fromJson(transform.toJson(originValue));
1361
- target2.valueMap.set(atom.key, copiedValue);
1362
- new Tracker(atom, origin);
1363
- return copiedValue;
1351
+ // src/mutable/get-json-token.ts
1352
+ var getJsonToken = (mutableAtomToken) => {
1353
+ const key = mutableAtomToken.family ? `${mutableAtomToken.family.key}:JSON(${mutableAtomToken.family.subKey})` : `${mutableAtomToken.key}:JSON`;
1354
+ const jsonToken = { type: `selector`, key };
1355
+ if (mutableAtomToken.family) {
1356
+ jsonToken.family = {
1357
+ key: `${mutableAtomToken.family.key}:JSON`,
1358
+ subKey: mutableAtomToken.family.subKey
1359
+ };
1364
1360
  }
1365
- return targetValue;
1366
- }
1361
+ return jsonToken;
1362
+ };
1367
1363
 
1368
- // src/set-state/copy-mutable-in-transaction.ts
1369
- function copyMutableIfWithinTransaction(oldValue, atom, store) {
1370
- if (store.transactionStatus.phase === `building` || store.transactionStatus.phase === `applying`) {
1371
- if (`toJson` in atom && `fromJson` in atom) {
1372
- const copiedValue = copyMutableIfNeeded(
1373
- atom,
1374
- atom,
1375
- store,
1376
- store.transactionStatus.core
1377
- );
1378
- return copiedValue;
1379
- }
1380
- if (`family` in atom) {
1381
- const family = store.transactionStatus.core.families.get(atom.family.key);
1382
- if (family && family.type === `atom_family`) {
1383
- const result = copyMutableFamilyMemberWithinTransaction(
1384
- atom,
1385
- family,
1386
- store,
1387
- store.transactionStatus.core
1388
- );
1389
- if (result) {
1390
- return result;
1391
- }
1392
- }
1393
- }
1364
+ // src/mutable/get-update-token.ts
1365
+ var getUpdateToken = (mutableAtomToken) => {
1366
+ const key = `*${mutableAtomToken.key}`;
1367
+ const updateToken = { type: `atom`, key };
1368
+ if (mutableAtomToken.family) {
1369
+ updateToken.family = {
1370
+ key: `*${mutableAtomToken.family.key}`,
1371
+ subKey: mutableAtomToken.family.subKey
1372
+ };
1394
1373
  }
1395
- return oldValue;
1374
+ return updateToken;
1375
+ };
1376
+
1377
+ // src/mutable/is-atom-token-mutable.ts
1378
+ function isAtomTokenMutable(token) {
1379
+ return token.key.endsWith(`::mutable`);
1396
1380
  }
1397
- function copyMutableFamilyMemberWithinTransaction(atom, family, origin, target2) {
1398
- if (`toJson` in family && `fromJson` in family) {
1399
- const copyCreated = copyMutableIfNeeded(atom, family, origin, target2);
1400
- return copyCreated;
1401
- }
1402
- return null;
1381
+
1382
+ // src/mutable/transceiver.ts
1383
+ function isTransceiver(value) {
1384
+ return typeof value === `object` && value !== null && `do` in value && `undo` in value && `subscribe` in value;
1403
1385
  }
1404
1386
 
1405
- // src/set-state/emit-update.ts
1406
- var emitUpdate = (state, update, store) => {
1407
- store.logger.info(
1408
- `\u{1F4E2}`,
1409
- state.type,
1410
- state.key,
1411
- `went (`,
1412
- update.oldValue,
1413
- `->`,
1414
- update.newValue,
1415
- `) subscribers:`,
1416
- state.subject.subscribers
1417
- );
1418
- state.subject.next(update);
1387
+ // src/mutable/index.ts
1388
+ var isAtomMutable = (atom) => `isMutable` in atom;
1389
+
1390
+ // src/atom/is-default.ts
1391
+ var isAtomDefault = (key, store) => {
1392
+ const core = newest(store);
1393
+ return core.atomsThatAreDefault.has(key);
1394
+ };
1395
+ var markAtomAsDefault = (key, store) => {
1396
+ const core = newest(store);
1397
+ core.atomsThatAreDefault = new Set(core.atomsThatAreDefault).add(key);
1398
+ };
1399
+ var markAtomAsNotDefault = (key, store) => {
1400
+ const core = newest(store);
1401
+ core.atomsThatAreDefault = new Set(newest(store).atomsThatAreDefault);
1402
+ core.atomsThatAreDefault.delete(key);
1403
+ };
1404
+ var isSelectorDefault = (key, store) => {
1405
+ const rootKeys = traceAllSelectorAtoms(key, store);
1406
+ return rootKeys.every((rootKey) => isAtomDefault(rootKey, store));
1419
1407
  };
1420
1408
 
1421
- // src/set-state/evict-downstream.ts
1422
- var evictDownStream = (atom, store) => {
1423
- const core = target(store);
1424
- const downstreamKeys = core.selectorAtoms.getRelatedKeys(atom.key);
1409
+ // src/atom/create-atom.ts
1410
+ function createAtom(options, family, store) {
1425
1411
  store.logger.info(
1426
- `\u{1F9F9}`,
1427
- atom.type,
1428
- atom.key,
1429
- downstreamKeys ? `evicting ${downstreamKeys.size} states downstream:` : `no downstream states`,
1430
- downstreamKeys != null ? downstreamKeys : `to evict`
1412
+ `\u{1F528}`,
1413
+ `atom`,
1414
+ options.key,
1415
+ `creating in store "${store.config.name}"`
1431
1416
  );
1432
- if (downstreamKeys) {
1433
- if (core.operation.open) {
1434
- store.logger.info(
1435
- `\u{1F9F9}`,
1436
- atom.type,
1437
- atom.key,
1438
- `[ ${[...core.operation.done].join(`, `)} ] already done`
1417
+ const target = newest(store);
1418
+ const existing = target.atoms.get(options.key);
1419
+ if (existing) {
1420
+ store.logger.error(
1421
+ `\u274C`,
1422
+ `atom`,
1423
+ options.key,
1424
+ `Tried to create atom, but it already exists in the store.`,
1425
+ `(Ignore if you are in development using hot module replacement.)`
1426
+ );
1427
+ return deposit(existing);
1428
+ }
1429
+ const subject = new Subject();
1430
+ const newAtom = __spreadValues(__spreadProps(__spreadValues({}, options), {
1431
+ type: `atom`,
1432
+ install: (store2) => {
1433
+ store2.logger.info(
1434
+ `\u{1F6E0}\uFE0F`,
1435
+ `atom`,
1436
+ options.key,
1437
+ `installing in store "${store2.config.name}"`
1439
1438
  );
1440
- }
1441
- for (const key of downstreamKeys) {
1442
- if (isDone(key, store)) {
1443
- continue;
1439
+ return `mutable` in options ? createMutableAtom(options, store2) : createAtom(options, void 0, store2);
1440
+ },
1441
+ subject
1442
+ }), family && { family });
1443
+ let initialValue = options.default;
1444
+ if (options.default instanceof Function) {
1445
+ initialValue = options.default();
1446
+ }
1447
+ target.atoms.set(newAtom.key, newAtom);
1448
+ markAtomAsDefault(options.key, store);
1449
+ cacheValue(options.key, initialValue, subject, store);
1450
+ const token = deposit(newAtom);
1451
+ if (options.effects) {
1452
+ let effectIndex = 0;
1453
+ const cleanupFunctions = [];
1454
+ for (const effect of options.effects) {
1455
+ const cleanup = effect({
1456
+ setSelf: (next) => setState(token, next, store),
1457
+ onSet: (handle) => subscribe(token, handle, `effect[${effectIndex}]`, store)
1458
+ });
1459
+ if (cleanup) {
1460
+ cleanupFunctions.push(cleanup);
1444
1461
  }
1445
- evictCachedValue(key, store);
1446
- markDone(key, store);
1462
+ ++effectIndex;
1447
1463
  }
1464
+ newAtom.cleanup = () => {
1465
+ for (const cleanup of cleanupFunctions) {
1466
+ cleanup();
1467
+ }
1468
+ };
1448
1469
  }
1449
- };
1450
-
1451
- // src/set-state/stow-update.ts
1452
- function shouldUpdateBeStowed(key, update) {
1453
- if (isTransceiver(update.newValue)) {
1454
- return false;
1455
- }
1456
- if (key.includes(`\u{1F441}\u200D\u{1F5E8}`)) {
1457
- return false;
1458
- }
1459
- return true;
1470
+ store.subject.atomCreation.next(token);
1471
+ return token;
1460
1472
  }
1461
- var stowUpdate = (state, update, store) => {
1462
- const { key } = state;
1463
- if (store.transactionStatus.phase !== `building`) {
1473
+
1474
+ // src/atom/delete-atom.ts
1475
+ function deleteAtom(atomToken, store) {
1476
+ var _a, _b;
1477
+ const target = newest(store);
1478
+ const { key } = atomToken;
1479
+ const atom = target.atoms.get(key);
1480
+ if (!atom) {
1464
1481
  store.logger.error(
1465
- `\u{1F41E}`,
1482
+ `\u274C`,
1466
1483
  `atom`,
1467
- key,
1468
- `stowUpdate called outside of a transaction. This is probably a bug.`
1484
+ `${key}`,
1485
+ `Tried to delete atom, but it does not exist in the store.`
1469
1486
  );
1470
- return;
1471
1487
  }
1472
- const shouldStow = shouldUpdateBeStowed(key, update);
1473
- if (!shouldStow) {
1474
- return;
1475
- }
1476
- const atomUpdate = __spreadValues({ key }, update);
1477
- if (state.family) {
1478
- atomUpdate.family = state.family;
1488
+ (_a = atom == null ? void 0 : atom.cleanup) == null ? void 0 : _a.call(atom);
1489
+ target.atoms.delete(key);
1490
+ target.valueMap.delete(key);
1491
+ const selectorKeys = target.selectorAtoms.getRelatedKeys(key);
1492
+ if (selectorKeys) {
1493
+ for (const selectorKey of selectorKeys) {
1494
+ const token = (_b = target.selectors.get(selectorKey)) != null ? _b : target.readonlySelectors.get(selectorKey);
1495
+ if (token) {
1496
+ deleteSelector(token, store);
1497
+ }
1498
+ }
1479
1499
  }
1480
- store.transactionStatus.atomUpdates.push(atomUpdate);
1481
- store.logger.info(
1482
- `\u{1F4C1}`,
1483
- `atom`,
1484
- key,
1485
- `stowed (`,
1486
- update.oldValue,
1487
- `->`,
1488
- update.newValue,
1489
- `)`
1490
- );
1491
- };
1500
+ target.selectorAtoms.delete(key);
1501
+ target.atomsThatAreDefault.delete(key);
1502
+ target.timelineAtoms.delete(key);
1503
+ store.logger.info(`\u{1F525}`, `atom`, `${key}`, `deleted`);
1504
+ }
1492
1505
 
1493
- // src/set-state/set-atom.ts
1494
- var setAtom = (atom, next, store) => {
1495
- const oldValue = readOrComputeValue(atom, store);
1496
- let newValue = copyMutableIfWithinTransaction(oldValue, atom, store);
1497
- newValue = become(next)(newValue);
1498
- store.logger.info(`\u{1F4DD}`, `atom`, atom.key, `set to`, newValue);
1499
- newValue = cacheValue(atom.key, newValue, atom.subject, store);
1500
- if (isAtomDefault(atom.key, store)) {
1501
- markAtomAsNotDefault(atom.key, store);
1506
+ // src/lazy-map.ts
1507
+ var LazyMap = class extends Map {
1508
+ constructor(source) {
1509
+ super();
1510
+ this.source = source;
1511
+ this.deleted = /* @__PURE__ */ new Set();
1502
1512
  }
1503
- markDone(atom.key, store);
1504
- evictDownStream(atom, store);
1505
- const update = { oldValue, newValue };
1506
- if (store.transactionStatus.phase !== `building`) {
1507
- emitUpdate(atom, update, store);
1508
- } else {
1509
- stowUpdate(atom, update, store);
1513
+ get(key) {
1514
+ const has = super.has(key);
1515
+ if (has) {
1516
+ return super.get(key);
1517
+ }
1518
+ if (!this.deleted.has(key) && this.source.has(key)) {
1519
+ const value = this.source.get(key);
1520
+ return value;
1521
+ }
1522
+ return void 0;
1510
1523
  }
1511
- };
1512
-
1513
- // src/set-state/set-atom-or-selector.ts
1514
- var setAtomOrSelector = (state, value, store) => {
1515
- if (state.type === `selector`) {
1516
- state.set(value);
1517
- } else {
1518
- setAtom(state, value, store);
1524
+ set(key, value) {
1525
+ this.deleted.delete(key);
1526
+ return super.set(key, value);
1527
+ }
1528
+ hasOwn(key) {
1529
+ return super.has(key);
1530
+ }
1531
+ has(key) {
1532
+ return !this.deleted.has(key) && (super.has(key) || this.source.has(key));
1533
+ }
1534
+ delete(key) {
1535
+ this.deleted.add(key);
1536
+ return super.delete(key);
1519
1537
  }
1520
1538
  };
1521
1539
 
1522
- // src/keys.ts
1523
- var isAtomKey = (key, store) => target(store).atoms.has(key);
1524
- var isSelectorKey = (key, store) => target(store).selectors.has(key);
1525
- var isReadonlySelectorKey = (key, store) => target(store).readonlySelectors.has(key);
1526
- var isStateKey = (key, store) => isAtomKey(key, store) || isSelectorKey(key, store) || isReadonlySelectorKey(key, store);
1527
-
1528
- // src/selector/get-selector-dependency-keys.ts
1529
- var getSelectorDependencyKeys = (key, store) => {
1530
- const sources = target(store).selectorGraph.getRelationEntries({ downstreamSelectorKey: key }).filter(([_, { source }]) => source !== key).map(([_, { source }]) => source).filter((source) => isStateKey(source, store));
1531
- return sources;
1532
- };
1533
-
1534
- // src/selector/trace-selector-atoms.ts
1535
- var traceSelectorAtoms = (selectorKey, directDependencyKey, store) => {
1536
- const rootKeys = [];
1537
- const indirectDependencyKeys = getSelectorDependencyKeys(
1538
- directDependencyKey,
1539
- store
1540
- );
1541
- let depth = 0;
1542
- while (indirectDependencyKeys.length > 0) {
1543
- const indirectDependencyKey = indirectDependencyKeys.shift();
1544
- ++depth;
1545
- if (depth > 99999) {
1546
- throw new Error(
1547
- `Maximum selector dependency depth exceeded (> 99999) in selector "${selectorKey}". This is likely due to a circular dependency.`
1548
- );
1549
- }
1550
- if (!isAtomKey(indirectDependencyKey, store)) {
1551
- indirectDependencyKeys.push(
1552
- ...getSelectorDependencyKeys(indirectDependencyKey, store)
1553
- );
1554
- } else if (!rootKeys.includes(indirectDependencyKey)) {
1555
- rootKeys.push(indirectDependencyKey);
1556
- }
1540
+ // src/not-found-error.ts
1541
+ var capitalize = (str) => str[0].toUpperCase() + str.slice(1);
1542
+ function prettyPrintTokenType(token) {
1543
+ if (token.type === `readonly_selector`) {
1544
+ return `Readonly Selector`;
1545
+ }
1546
+ return capitalize(token.type);
1547
+ }
1548
+ var NotFoundError = class extends Error {
1549
+ constructor(token, store) {
1550
+ super(
1551
+ `${prettyPrintTokenType(token)} "${token.key}" not found in store "${store.config.name}".`
1552
+ );
1557
1553
  }
1558
- return rootKeys;
1559
- };
1560
- var traceAllSelectorAtoms = (selectorKey, store) => {
1561
- const directDependencyKeys = getSelectorDependencyKeys(selectorKey, store);
1562
- return directDependencyKeys.flatMap(
1563
- (depKey) => isAtomKey(depKey, store) ? depKey : traceSelectorAtoms(selectorKey, depKey, store)
1564
- );
1565
1554
  };
1566
1555
 
1567
- // src/selector/update-selector-atoms.ts
1568
- var updateSelectorAtoms = (selectorKey, dependency, store) => {
1569
- const core = target(store);
1570
- if (dependency.type === `atom`) {
1571
- core.selectorAtoms = core.selectorAtoms.set({
1572
- selectorKey,
1573
- atomKey: dependency.key
1574
- });
1575
- store.logger.info(
1576
- `\u{1F50D}`,
1577
- `selector`,
1578
- selectorKey,
1579
- `discovers root atom "${dependency.key}"`
1580
- );
1581
- } else {
1582
- const rootKeys = traceSelectorAtoms(selectorKey, dependency.key, store);
1583
- store.logger.info(
1584
- `\u{1F50D}`,
1585
- `selector`,
1586
- selectorKey,
1587
- `discovers root atoms: [ ${rootKeys.map((key) => `"${key}"`).join(`, `)} ]`
1556
+ // src/subscribe/recall-state.ts
1557
+ var recallState = (state, store) => {
1558
+ const target = newest(store);
1559
+ if (!target.operation.open) {
1560
+ store.logger.warn(
1561
+ `\u{1F41E}`,
1562
+ state.type,
1563
+ state.key,
1564
+ `recall called outside of an operation. This is probably a bug.`
1588
1565
  );
1589
- for (const atomKey of rootKeys) {
1590
- core.selectorAtoms = core.selectorAtoms.set({
1591
- selectorKey,
1592
- atomKey
1593
- });
1594
- }
1566
+ return target.valueMap.get(state.key);
1595
1567
  }
1568
+ return target.operation.prev.get(state.key);
1596
1569
  };
1597
1570
 
1598
- // src/selector/register-selector.ts
1599
- var registerSelector = (selectorKey, store) => ({
1600
- get: (dependency) => {
1601
- const core = target(store);
1602
- const alreadyRegistered = core.selectorGraph.getRelationEntries({ downstreamSelectorKey: selectorKey }).some(([_, { source }]) => source === dependency.key);
1603
- const dependencyState = withdraw(dependency, store);
1604
- if (dependencyState === void 0) {
1605
- throw new Error(
1606
- `State "${dependency.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
1607
- );
1608
- }
1609
- const dependencyValue = readOrComputeValue(dependencyState, store);
1610
- store.logger.info(
1611
- `\u{1F50C}`,
1612
- `selector`,
1613
- selectorKey,
1614
- `registers dependency ( "${dependency.key}" =`,
1615
- dependencyValue,
1616
- `)`
1617
- );
1618
- if (!alreadyRegistered) {
1619
- core.selectorGraph = core.selectorGraph.set(
1620
- {
1621
- upstreamSelectorKey: dependency.key,
1622
- downstreamSelectorKey: selectorKey
1623
- },
1624
- {
1625
- source: dependency.key
1626
- }
1627
- );
1628
- }
1629
- updateSelectorAtoms(selectorKey, dependency, store);
1630
- return dependencyValue;
1631
- },
1632
- set: (stateToken, newValue) => {
1633
- const state = withdraw(stateToken, store);
1634
- if (state === void 0) {
1571
+ // src/subscribe/subscribe-to-root-atoms.ts
1572
+ var subscribeToRootAtoms = (state, store) => {
1573
+ const dependencySubscriptions = `default` in state ? null : traceAllSelectorAtoms(state.key, store).map((atomKey) => {
1574
+ const atom = store.atoms.get(atomKey);
1575
+ if (atom === void 0) {
1635
1576
  throw new Error(
1636
- `State "${stateToken.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
1577
+ `Atom "${atomKey}", a dependency of selector "${state.key}", not found in store "${store.config.name}".`
1637
1578
  );
1638
1579
  }
1639
- setAtomOrSelector(state, newValue, store);
1640
- }
1641
- });
1642
-
1643
- // src/selector/create-read-write-selector.ts
1644
- var createReadWriteSelector = (options, family, store, core) => {
1645
- const subject = new Subject();
1646
- const { get, set } = registerSelector(options.key, store);
1647
- const getSelf = () => {
1648
- const value = options.get({ get });
1649
- cacheValue(options.key, value, subject, store);
1650
- return value;
1651
- };
1652
- const setSelf = (next) => {
1653
- const oldValue = getSelf();
1654
- const newValue = become(next)(oldValue);
1655
- store.logger.info(
1656
- `\u{1F4DD}`,
1657
- `selector`,
1658
- options.key,
1659
- `set (`,
1660
- oldValue,
1661
- `->`,
1662
- newValue,
1663
- `)`
1580
+ return atom.subject.subscribe(
1581
+ `${state.type}:${state.key}`,
1582
+ (atomChange) => {
1583
+ store.logger.info(
1584
+ `\u{1F4E2}`,
1585
+ state.type,
1586
+ state.key,
1587
+ `root`,
1588
+ atomKey,
1589
+ `went`,
1590
+ atomChange.oldValue,
1591
+ `->`,
1592
+ atomChange.newValue
1593
+ );
1594
+ const oldValue = recallState(state, store);
1595
+ const newValue = readOrComputeValue(state, store);
1596
+ store.logger.info(
1597
+ `\u2728`,
1598
+ state.type,
1599
+ state.key,
1600
+ `went`,
1601
+ oldValue,
1602
+ `->`,
1603
+ newValue
1604
+ );
1605
+ state.subject.next({ newValue, oldValue });
1606
+ }
1664
1607
  );
1665
- cacheValue(options.key, newValue, subject, store);
1666
- markDone(options.key, store);
1667
- if (store.transactionStatus.phase === `idle`) {
1668
- subject.next({ newValue, oldValue });
1669
- }
1670
- options.set({ get, set }, newValue);
1671
- };
1672
- const mySelector = __spreadValues(__spreadProps(__spreadValues({}, options), {
1673
- subject,
1674
- install: (s) => createSelector(options, family, s),
1675
- get: getSelf,
1676
- set: setSelf,
1677
- type: `selector`
1678
- }), family && { family });
1679
- core.selectors.set(options.key, mySelector);
1680
- const initialValue = getSelf();
1681
- store.logger.info(`\u2728`, mySelector.type, mySelector.key, `=`, initialValue);
1682
- const token = {
1683
- key: options.key,
1684
- type: `selector`
1685
- };
1686
- if (family) {
1687
- token.family = family;
1688
- }
1689
- store.subject.selectorCreation.next(token);
1690
- return token;
1691
- };
1692
-
1693
- // src/selector/create-readonly-selector.ts
1694
- var createReadonlySelector = (options, family, store, core) => {
1695
- const subject = new Subject();
1696
- const { get } = registerSelector(options.key, store);
1697
- const getSelf = () => {
1698
- const value = options.get({ get });
1699
- cacheValue(options.key, value, subject, store);
1700
- return value;
1701
- };
1702
- const readonlySelector = __spreadValues(__spreadProps(__spreadValues({}, options), {
1703
- subject,
1704
- install: (s) => createSelector(options, family, s),
1705
- get: getSelf,
1706
- type: `readonly_selector`
1707
- }), family && { family });
1708
- core.readonlySelectors.set(options.key, readonlySelector);
1709
- const initialValue = getSelf();
1710
- store.logger.info(
1711
- `\u2728`,
1712
- readonlySelector.type,
1713
- readonlySelector.key,
1714
- `=`,
1715
- initialValue
1716
- );
1717
- const token = {
1718
- key: options.key,
1719
- type: `readonly_selector`
1720
- };
1721
- if (family) {
1722
- token.family = family;
1723
- }
1724
- store.subject.selectorCreation.next(token);
1725
- return token;
1608
+ });
1609
+ return dependencySubscriptions;
1726
1610
  };
1727
1611
 
1728
- // src/selector/create-selector.ts
1729
- function createSelector(options, family, store) {
1730
- const core = target(store);
1731
- const existingWritable = core.selectors.get(options.key);
1732
- const existingReadonly = core.readonlySelectors.get(options.key);
1733
- if (existingWritable || existingReadonly) {
1734
- store.logger.error(
1735
- `\u274C`,
1736
- existingReadonly ? `readonly_selector` : `selector`,
1737
- options.key,
1738
- `Tried to create selector, but it already exists in the store. (Ignore if you are in development using hot module replacement.)`
1612
+ // src/timeline/add-atom-to-timeline.ts
1613
+ var addAtomToTimeline = (atomToken, tl, store) => {
1614
+ const atom = withdraw(atomToken, store);
1615
+ if (atom === void 0) {
1616
+ throw new Error(
1617
+ `Cannot subscribe to atom "${atomToken.key}" because it has not been initialized in store "${store.config.name}"`
1739
1618
  );
1740
1619
  }
1741
- if (`set` in options) {
1742
- return createReadWriteSelector(options, family, store, core);
1743
- }
1744
- return createReadonlySelector(options, family, store, core);
1745
- }
1746
-
1747
- // src/selector/delete-selector.ts
1748
- function deleteSelector(selectorToken, store) {
1749
- const core = target(store);
1750
- const { key } = selectorToken;
1751
- switch (selectorToken.type) {
1752
- case `selector`:
1753
- core.selectors.delete(key);
1754
- break;
1755
- case `readonly_selector`:
1756
- core.readonlySelectors.delete(key);
1757
- break;
1758
- }
1759
- core.valueMap.delete(key);
1760
- core.selectorAtoms.delete(key);
1761
- const downstreamTokens = core.selectorGraph.getRelationEntries({ upstreamSelectorKey: key }).filter(([_, { source }]) => source === key).map(
1762
- ([downstreamSelectorKey]) => {
1763
- var _a;
1764
- return (_a = core.selectors.get(downstreamSelectorKey)) != null ? _a : core.readonlySelectors.get(downstreamSelectorKey);
1765
- }
1766
- );
1767
- for (const downstreamToken of downstreamTokens) {
1768
- if (downstreamToken) {
1769
- deleteSelector(downstreamToken, store);
1770
- }
1771
- }
1772
- core.selectorGraph.delete(key);
1773
- store.logger.info(`\u{1F525}`, selectorToken.type, `${key}`, `deleted`);
1774
- }
1775
-
1776
- // src/families/create-readonly-selector-family.ts
1777
- function createReadonlySelectorFamily(options, store) {
1778
- const core = target(store);
1779
- const subject = new Subject();
1780
- return Object.assign(
1781
- (key) => {
1782
- const subKey = stringifyJson(key);
1783
- const family = { key: options.key, subKey };
1784
- const fullKey = `${options.key}(${subKey})`;
1785
- const existing = core.readonlySelectors.get(fullKey);
1786
- if (existing) {
1787
- return deposit(existing);
1620
+ atom.subject.subscribe(`timeline`, (update) => {
1621
+ var _a, _b, _c, _d, _e, _f;
1622
+ const target = newest(store);
1623
+ const currentSelectorKey = store.operation.open && store.operation.token.type === `selector` ? store.operation.token.key : null;
1624
+ const currentSelectorTime = store.operation.open && store.operation.token.type === `selector` ? store.operation.time : null;
1625
+ const currentTransactionKey = (_a = target.subject.transactionApplying.state) == null ? void 0 : _a.update.key;
1626
+ const currentTransactionTime = (_b = target.subject.transactionApplying.state) == null ? void 0 : _b.time;
1627
+ store.logger.info(
1628
+ `\u23F3`,
1629
+ `timeline`,
1630
+ tl.key,
1631
+ `atom`,
1632
+ atomToken.key,
1633
+ `went`,
1634
+ update.oldValue,
1635
+ `->`,
1636
+ update.newValue,
1637
+ currentTransactionKey ? `in transaction "${currentTransactionKey}"` : currentSelectorKey ? `in selector "${currentSelectorKey}"` : ``
1638
+ );
1639
+ if (tl.timeTraveling === null) {
1640
+ if (tl.selectorTime && tl.selectorTime !== currentSelectorTime) {
1641
+ const mostRecentUpdate = tl.history.at(-1);
1642
+ if (mostRecentUpdate === void 0) {
1643
+ throw new Error(
1644
+ `Timeline "${tl.key}" has a selectorTime, but no history. This is most likely a bug in AtomIO.`
1645
+ );
1646
+ }
1647
+ }
1648
+ if (currentTransactionKey) {
1649
+ const currentTransaction = withdraw(
1650
+ { key: currentTransactionKey, type: `transaction` },
1651
+ store
1652
+ );
1653
+ if (currentTransaction === void 0) {
1654
+ throw new Error(
1655
+ `Transaction "${currentTransactionKey}" not found in store "${store.config.name}". This is surprising, because we are in the application phase of "${currentTransactionKey}".`
1656
+ );
1657
+ }
1658
+ if (tl.transactionKey !== currentTransactionKey) {
1659
+ if (tl.transactionKey) {
1660
+ store.logger.error(
1661
+ `\u{1F41E}`,
1662
+ `timeline`,
1663
+ tl.key,
1664
+ `unable to resolve transaction "${tl.transactionKey}. This is probably a bug in AtomIO.`
1665
+ );
1666
+ }
1667
+ tl.transactionKey = currentTransactionKey;
1668
+ const unsubscribe = currentTransaction.subject.subscribe(
1669
+ `timeline:${tl.key}`,
1670
+ (update2) => {
1671
+ var _a2, _b2;
1672
+ unsubscribe();
1673
+ if (tl.timeTraveling === null && currentTransactionTime) {
1674
+ if (tl.at !== tl.history.length) {
1675
+ tl.history.splice(tl.at);
1676
+ }
1677
+ const filterUpdates = (updates2) => updates2.filter((updateFromTx) => {
1678
+ const target2 = newest(store);
1679
+ if (`updates` in updateFromTx) {
1680
+ return true;
1681
+ }
1682
+ const atomOrFamilyKeys = target2.timelineAtoms.getRelatedKeys(tl.key);
1683
+ return atomOrFamilyKeys ? [...atomOrFamilyKeys].some(
1684
+ (key) => {
1685
+ var _a3;
1686
+ return key === updateFromTx.key || key === ((_a3 = updateFromTx.family) == null ? void 0 : _a3.key);
1687
+ }
1688
+ ) : false;
1689
+ }).map((updateFromTx) => {
1690
+ if (`updates` in updateFromTx) {
1691
+ return __spreadProps(__spreadValues({}, updateFromTx), {
1692
+ updates: filterUpdates(updateFromTx.updates)
1693
+ });
1694
+ }
1695
+ return updateFromTx;
1696
+ });
1697
+ const updates = filterUpdates(update2.updates);
1698
+ const timelineTransactionUpdate = __spreadProps(__spreadValues({
1699
+ type: `transaction_update`,
1700
+ timestamp: currentTransactionTime
1701
+ }, update2), {
1702
+ updates
1703
+ });
1704
+ const willCapture = (_b2 = (_a2 = tl.shouldCapture) == null ? void 0 : _a2.call(tl, timelineTransactionUpdate, tl)) != null ? _b2 : true;
1705
+ if (willCapture) {
1706
+ tl.history.push(timelineTransactionUpdate);
1707
+ tl.at = tl.history.length;
1708
+ tl.subject.next(timelineTransactionUpdate);
1709
+ }
1710
+ }
1711
+ tl.transactionKey = null;
1712
+ store.logger.info(
1713
+ `\u231B`,
1714
+ `timeline`,
1715
+ tl.key,
1716
+ `got a transaction_update "${update2.key}"`
1717
+ );
1718
+ }
1719
+ );
1720
+ }
1721
+ } else if (currentSelectorKey && currentSelectorTime) {
1722
+ let latestUpdate = tl.history.at(-1);
1723
+ if (currentSelectorTime !== tl.selectorTime) {
1724
+ latestUpdate = {
1725
+ type: `selector_update`,
1726
+ timestamp: currentSelectorTime,
1727
+ key: currentSelectorKey,
1728
+ atomUpdates: []
1729
+ };
1730
+ latestUpdate.atomUpdates.push(__spreadValues({
1731
+ key: atom.key,
1732
+ type: `atom_update`
1733
+ }, update));
1734
+ if (tl.at !== tl.history.length) {
1735
+ tl.history.splice(tl.at);
1736
+ }
1737
+ tl.history.push(latestUpdate);
1738
+ store.logger.info(
1739
+ `\u231B`,
1740
+ `timeline`,
1741
+ tl.key,
1742
+ `got a selector_update "${currentSelectorKey}" with`,
1743
+ latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
1744
+ );
1745
+ tl.at = tl.history.length;
1746
+ tl.selectorTime = currentSelectorTime;
1747
+ } else {
1748
+ if ((latestUpdate == null ? void 0 : latestUpdate.type) === `selector_update`) {
1749
+ latestUpdate.atomUpdates.push(__spreadValues({
1750
+ key: atom.key,
1751
+ type: `atom_update`
1752
+ }, update));
1753
+ store.logger.info(
1754
+ `\u231B`,
1755
+ `timeline`,
1756
+ tl.key,
1757
+ `set selector_update "${currentSelectorKey}" to`,
1758
+ latestUpdate == null ? void 0 : latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.key)
1759
+ );
1760
+ }
1761
+ }
1762
+ if (latestUpdate) {
1763
+ const willCaptureSelectorUpdate = (_d = (_c = tl.shouldCapture) == null ? void 0 : _c.call(tl, latestUpdate, tl)) != null ? _d : true;
1764
+ if (willCaptureSelectorUpdate) {
1765
+ tl.subject.next(latestUpdate);
1766
+ } else {
1767
+ tl.history.pop();
1768
+ tl.at = tl.history.length;
1769
+ }
1770
+ }
1771
+ } else {
1772
+ const timestamp = Date.now();
1773
+ tl.selectorTime = null;
1774
+ if (tl.at !== tl.history.length) {
1775
+ tl.history.splice(tl.at);
1776
+ }
1777
+ const atomUpdate = {
1778
+ type: `atom_update`,
1779
+ timestamp,
1780
+ key: atom.key,
1781
+ oldValue: update.oldValue,
1782
+ newValue: update.newValue
1783
+ };
1784
+ if (atom.family) {
1785
+ atomUpdate.family = atom.family;
1786
+ }
1787
+ const willCapture = (_f = (_e = tl.shouldCapture) == null ? void 0 : _e.call(tl, atomUpdate, tl)) != null ? _f : true;
1788
+ store.logger.info(
1789
+ `\u231B`,
1790
+ `timeline`,
1791
+ tl.key,
1792
+ `got an atom_update to "${atom.key}"`
1793
+ );
1794
+ if (willCapture) {
1795
+ tl.history.push(atomUpdate);
1796
+ tl.at = tl.history.length;
1797
+ tl.subject.next(atomUpdate);
1798
+ }
1788
1799
  }
1789
- return createSelector(
1790
- {
1791
- key: fullKey,
1792
- get: options.get(key)
1793
- },
1794
- family,
1795
- store
1796
- );
1797
- },
1798
- {
1799
- key: options.key,
1800
- type: `readonly_selector_family`,
1801
- subject
1802
1800
  }
1803
- );
1804
- }
1805
- function createSelectorFamily(options, store) {
1806
- const isReadonly = !(`set` in options);
1807
- if (isReadonly) {
1808
- return createReadonlySelectorFamily(options, store);
1801
+ });
1802
+ };
1803
+
1804
+ // src/timeline/create-timeline.ts
1805
+ function createTimeline(options, store, data) {
1806
+ var _a, _b;
1807
+ const tl = __spreadProps(__spreadValues({
1808
+ type: `timeline`,
1809
+ key: options.key,
1810
+ at: 0,
1811
+ timeTraveling: null,
1812
+ selectorTime: null,
1813
+ transactionKey: null
1814
+ }, data), {
1815
+ history: (_a = data == null ? void 0 : data.history.map((update) => __spreadValues({}, update))) != null ? _a : [],
1816
+ install: (store2) => createTimeline(options, store2, tl),
1817
+ subject: new Subject()
1818
+ });
1819
+ if (options.shouldCapture) {
1820
+ tl.shouldCapture = options.shouldCapture;
1809
1821
  }
1810
- const core = target(store);
1811
- const subject = new Subject();
1812
- const selectorFamily = Object.assign(
1813
- (key) => {
1814
- const subKey = stringifyJson(key);
1815
- const family = { key: options.key, subKey };
1816
- const fullKey = `${options.key}(${subKey})`;
1817
- const existing = core.selectors.get(fullKey);
1818
- if (existing) {
1819
- return deposit(existing);
1820
- }
1821
- const token = createSelector(
1822
- {
1823
- key: fullKey,
1824
- get: options.get(key),
1825
- set: options.set(key)
1826
- },
1827
- family,
1828
- store
1822
+ const target = newest(store);
1823
+ for (const tokenOrFamily of options.atoms) {
1824
+ const timelineKey = target.timelineAtoms.getRelatedKey(tokenOrFamily.key);
1825
+ if (timelineKey) {
1826
+ store.logger.error(
1827
+ `\u274C`,
1828
+ `timeline`,
1829
+ options.key,
1830
+ `Failed to add atom "${tokenOrFamily.key}" because it already belongs to timeline "${timelineKey}"`
1829
1831
  );
1830
- subject.next(token);
1831
- return token;
1832
- },
1833
- {
1834
- key: options.key,
1835
- type: `selector_family`
1832
+ continue;
1836
1833
  }
1837
- );
1838
- core.families.set(options.key, selectorFamily);
1839
- return selectorFamily;
1840
- }
1841
-
1842
- // src/mutable/tracker-family.ts
1843
- var FamilyTracker = class {
1844
- constructor(findMutableState, store) {
1845
- this.findLatestUpdateState = createAtomFamily(
1846
- {
1847
- key: `*${findMutableState.key}`,
1848
- default: null
1849
- },
1850
- store
1851
- );
1852
- this.findMutableState = findMutableState;
1853
- this.findMutableState.subject.subscribe(
1854
- `store=${store.config.name}::tracker-atom-family`,
1855
- (atomToken) => {
1856
- if (atomToken.family) {
1857
- const key = parseJson(atomToken.family.subKey);
1858
- this.findLatestUpdateState(key);
1859
- new Tracker(atomToken, store);
1834
+ if (tokenOrFamily.type === `atom_family`) {
1835
+ const family = tokenOrFamily;
1836
+ family.subject.subscribe(`timeline:${options.key}`, (token2) => {
1837
+ addAtomToTimeline(token2, tl, store);
1838
+ });
1839
+ for (const atom of target.atoms.values()) {
1840
+ if (((_b = atom.family) == null ? void 0 : _b.key) === family.key) {
1841
+ addAtomToTimeline(atom, tl, store);
1860
1842
  }
1861
1843
  }
1862
- );
1863
- this.findLatestUpdateState.subject.subscribe(
1864
- `store=${store.config.name}::tracker-atom-family`,
1865
- (atomToken) => {
1866
- if (atomToken.family) {
1867
- const key = parseJson(atomToken.family.subKey);
1868
- const mutableAtomToken = this.findMutableState(key);
1869
- new Tracker(mutableAtomToken, store);
1844
+ } else {
1845
+ const token2 = tokenOrFamily;
1846
+ if (`family` in token2 && token2.family) {
1847
+ const familyTimelineKey = target.timelineAtoms.getRelatedKey(
1848
+ token2.family.key
1849
+ );
1850
+ if (familyTimelineKey) {
1851
+ store.logger.error(
1852
+ `\u274C`,
1853
+ `timeline`,
1854
+ options.key,
1855
+ `Failed to add atom "${token2.key}" because its family "${token2.family.key}" already belongs to timeline "${familyTimelineKey}"`
1856
+ );
1857
+ continue;
1870
1858
  }
1871
1859
  }
1872
- );
1873
- }
1874
- };
1875
-
1876
- // src/mutable/create-mutable-atom-family.ts
1877
- function createMutableAtomFamily(options, store) {
1878
- const coreFamily = Object.assign(
1879
- createAtomFamily(options, store),
1880
- options
1881
- );
1882
- selectJsonFamily(coreFamily, options);
1883
- new FamilyTracker(coreFamily, store);
1884
- return coreFamily;
1885
- }
1886
-
1887
- // src/mutable/get-json-family.ts
1888
- var getJsonFamily = (mutableAtomFamily, store) => {
1889
- const core = target(store);
1890
- const key = `${mutableAtomFamily.key}:JSON`;
1891
- const jsonFamily = core.families.get(
1892
- key
1893
- );
1894
- return jsonFamily;
1895
- };
1896
-
1897
- // src/mutable/get-json-token.ts
1898
- var getJsonToken = (mutableAtomToken) => {
1899
- const key = mutableAtomToken.family ? `${mutableAtomToken.family.key}:JSON(${mutableAtomToken.family.subKey})` : `${mutableAtomToken.key}:JSON`;
1900
- const jsonToken = { type: `selector`, key };
1901
- if (mutableAtomToken.family) {
1902
- jsonToken.family = {
1903
- key: `${mutableAtomToken.family.key}:JSON`,
1904
- subKey: mutableAtomToken.family.subKey
1905
- };
1906
- }
1907
- return jsonToken;
1908
- };
1909
-
1910
- // src/mutable/get-update-token.ts
1911
- var getUpdateToken = (mutableAtomToken) => {
1912
- const key = `*${mutableAtomToken.key}`;
1913
- const updateToken = { type: `atom`, key };
1914
- if (mutableAtomToken.family) {
1915
- updateToken.family = {
1916
- key: `*${mutableAtomToken.family.key}`,
1917
- subKey: mutableAtomToken.family.subKey
1918
- };
1860
+ addAtomToTimeline(token2, tl, store);
1861
+ }
1862
+ target.timelineAtoms = target.timelineAtoms.set({
1863
+ atomKey: tokenOrFamily.key,
1864
+ timelineKey: options.key
1865
+ });
1919
1866
  }
1920
- return updateToken;
1921
- };
1922
-
1923
- // src/mutable/is-atom-token-mutable.ts
1924
- function isAtomTokenMutable(token) {
1925
- return token.key.endsWith(`::mutable`);
1926
- }
1927
-
1928
- // src/mutable/transceiver.ts
1929
- function isTransceiver(value) {
1930
- return typeof value === `object` && value !== null && `do` in value && `undo` in value && `subscribe` in value;
1867
+ store.timelines.set(options.key, tl);
1868
+ const token = {
1869
+ key: options.key,
1870
+ type: `timeline`
1871
+ };
1872
+ store.subject.timelineCreation.next(token);
1873
+ return token;
1931
1874
  }
1932
-
1933
- // src/mutable/index.ts
1934
- var isAtomMutable = (atom) => `isMutable` in atom;
1935
-
1936
- // src/atom/is-default.ts
1937
- var isAtomDefault = (key, store) => {
1938
- const core = target(store);
1939
- return core.atomsThatAreDefault.has(key);
1940
- };
1941
- var markAtomAsDefault = (key, store) => {
1942
- const core = target(store);
1943
- core.atomsThatAreDefault = new Set(core.atomsThatAreDefault).add(key);
1944
- };
1945
- var markAtomAsNotDefault = (key, store) => {
1946
- const core = target(store);
1947
- core.atomsThatAreDefault = new Set(target(store).atomsThatAreDefault);
1948
- core.atomsThatAreDefault.delete(key);
1949
- };
1950
- var isSelectorDefault = (key, store) => {
1951
- const rootKeys = traceAllSelectorAtoms(key, store);
1952
- return rootKeys.every((rootKey) => isAtomDefault(rootKey, store));
1953
- };
1954
-
1955
- // src/atom/create-atom.ts
1956
- function createAtom(options, family, store) {
1875
+ var timeTravel = (direction, token, store) => {
1876
+ const action = direction === `forward` ? `redo` : `undo`;
1957
1877
  store.logger.info(
1958
- `\u{1F528}`,
1959
- `atom`,
1960
- options.key,
1961
- `creating in store "${store.config.name}"`
1878
+ direction === `forward` ? `\u23E9` : `\u23EA`,
1879
+ `timeline`,
1880
+ token.key,
1881
+ action
1962
1882
  );
1963
- const core = target(store);
1964
- const existing = core.atoms.get(options.key);
1965
- if (existing) {
1966
- store.logger.error(
1967
- `\u274C`,
1968
- `atom`,
1969
- options.key,
1970
- `Tried to create atom, but it already exists in the store.`,
1971
- `(Ignore if you are in development using hot module replacement.)`
1883
+ const timelineData = store.timelines.get(token.key);
1884
+ if (!timelineData) {
1885
+ store.logger.error(
1886
+ `\u{1F41E}`,
1887
+ `timeline`,
1888
+ token.key,
1889
+ `Failed to ${action}. This timeline has not been initialized.`
1972
1890
  );
1973
- return deposit(existing);
1891
+ return;
1974
1892
  }
1975
- const subject = new Subject();
1976
- const newAtom = __spreadValues(__spreadProps(__spreadValues({}, options), {
1977
- type: `atom`,
1978
- install: (store2) => {
1979
- store2.logger.info(
1980
- `\u{1F6E0}\uFE0F`,
1981
- `atom`,
1982
- options.key,
1983
- `installing in store "${store2.config.name}"`
1984
- );
1985
- return `mutable` in options ? createMutableAtom(options, store2) : createAtom(options, void 0, store2);
1986
- },
1987
- subject
1988
- }), family && { family });
1989
- let initialValue = options.default;
1990
- if (options.default instanceof Function) {
1991
- initialValue = options.default();
1893
+ if (direction === `forward` && timelineData.at === timelineData.history.length || direction === `backward` && timelineData.at === 0) {
1894
+ store.logger.warn(
1895
+ `\u{1F481}`,
1896
+ `timeline`,
1897
+ token.key,
1898
+ `Failed to ${action} at the ${direction === `forward` ? `end` : `beginning`} of timeline "${token.key}". There is nothing to ${action}.`
1899
+ );
1900
+ return;
1992
1901
  }
1993
- core.atoms.set(newAtom.key, newAtom);
1994
- markAtomAsDefault(options.key, store);
1995
- cacheValue(options.key, initialValue, subject, store);
1996
- const token = deposit(newAtom);
1997
- if (options.effects) {
1998
- let effectIndex = 0;
1999
- const cleanupFunctions = [];
2000
- for (const effect of options.effects) {
2001
- const cleanup = effect({
2002
- setSelf: (next) => setState(token, next, store),
2003
- onSet: (handle) => subscribe(token, handle, `effect[${effectIndex}]`, store)
2004
- });
2005
- if (cleanup) {
2006
- cleanupFunctions.push(cleanup);
1902
+ timelineData.timeTraveling = direction === `forward` ? `into_future` : `into_past`;
1903
+ if (direction === `backward`) {
1904
+ --timelineData.at;
1905
+ }
1906
+ const update = timelineData.history[timelineData.at];
1907
+ const updateValues = (atomUpdate) => {
1908
+ const { key, newValue, oldValue } = atomUpdate;
1909
+ const value = direction === `forward` ? newValue : oldValue;
1910
+ setState({ key, type: `atom` }, value, store);
1911
+ };
1912
+ const updateValuesFromTransactionUpdate = (transactionUpdate) => {
1913
+ const updates = direction === `forward` ? transactionUpdate.updates : [...transactionUpdate.updates].reverse();
1914
+ for (const updateFromTransaction of updates) {
1915
+ if (`newValue` in updateFromTransaction) {
1916
+ updateValues(updateFromTransaction);
1917
+ } else {
1918
+ updateValuesFromTransactionUpdate(updateFromTransaction);
2007
1919
  }
2008
- ++effectIndex;
2009
1920
  }
2010
- newAtom.cleanup = () => {
2011
- for (const cleanup of cleanupFunctions) {
2012
- cleanup();
1921
+ };
1922
+ switch (update.type) {
1923
+ case `atom_update`: {
1924
+ updateValues(update);
1925
+ break;
1926
+ }
1927
+ case `selector_update`: {
1928
+ const updates = direction === `forward` ? update.atomUpdates : [...update.atomUpdates].reverse();
1929
+ for (const atomUpdate of updates) {
1930
+ updateValues(atomUpdate);
2013
1931
  }
2014
- };
1932
+ break;
1933
+ }
1934
+ case `transaction_update`: {
1935
+ updateValuesFromTransactionUpdate(update);
1936
+ break;
1937
+ }
2015
1938
  }
2016
- store.subject.atomCreation.next(token);
2017
- return token;
2018
- }
1939
+ if (direction === `forward`) {
1940
+ ++timelineData.at;
1941
+ }
1942
+ timelineData.subject.next(action);
1943
+ timelineData.timeTraveling = null;
1944
+ store.logger.info(
1945
+ `\u23F9\uFE0F`,
1946
+ `timeline`,
1947
+ token.key,
1948
+ `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`
1949
+ );
1950
+ };
2019
1951
 
2020
- // src/atom/delete-atom.ts
2021
- function deleteAtom(atomToken, store) {
2022
- var _a, _b;
2023
- const core = target(store);
2024
- const { key } = atomToken;
2025
- const atom = core.atoms.get(key);
2026
- if (!atom) {
2027
- store.logger.error(
2028
- `\u274C`,
2029
- `atom`,
2030
- `${key}`,
2031
- `Tried to delete atom, but it does not exist in the store.`
1952
+ // src/transaction/abort-transaction.ts
1953
+ var abortTransaction = (store) => {
1954
+ const target = newest(store);
1955
+ if (target.transactionMeta === null || target.parent === null) {
1956
+ store.logger.warn(
1957
+ `\u{1F41E}`,
1958
+ `transaction`,
1959
+ `???`,
1960
+ `abortTransaction called outside of a transaction. This is probably a bug in AtomIO.`
2032
1961
  );
1962
+ return;
2033
1963
  }
2034
- (_a = atom == null ? void 0 : atom.cleanup) == null ? void 0 : _a.call(atom);
2035
- core.atoms.delete(key);
2036
- core.valueMap.delete(key);
2037
- const selectorKeys = core.selectorAtoms.getRelatedKeys(key);
2038
- if (selectorKeys) {
2039
- for (const selectorKey of selectorKeys) {
2040
- const token = (_b = core.selectors.get(selectorKey)) != null ? _b : core.readonlySelectors.get(selectorKey);
2041
- if (token) {
2042
- deleteSelector(token, store);
1964
+ store.logger.info(
1965
+ `\u{1FA82}`,
1966
+ `transaction`,
1967
+ target.transactionMeta.update.key,
1968
+ `Aborting transaction`
1969
+ );
1970
+ target.parent.child = null;
1971
+ };
1972
+ function ingestAtomUpdate(update, parent, child) {
1973
+ var _a, _b, _c;
1974
+ const { key, newValue } = update;
1975
+ const token = { key, type: `atom` };
1976
+ if (!parent.valueMap.has(token.key)) {
1977
+ if (token.family) {
1978
+ const family = parent.families.get(token.family.key);
1979
+ if (family) {
1980
+ family(token.family.subKey);
1981
+ }
1982
+ } else {
1983
+ const newAtom = child.atoms.get(token.key);
1984
+ if (!newAtom) {
1985
+ throw new Error(
1986
+ `Absurd Error: Atom "${token.key}" not found while copying updates from transaction "${(_a = child.transactionMeta) == null ? void 0 : _a.update.key}" to store "${parent.config.name}"`
1987
+ );
2043
1988
  }
1989
+ parent.atoms.set(newAtom.key, newAtom);
1990
+ parent.valueMap.set(newAtom.key, newAtom.default);
1991
+ parent.logger.info(
1992
+ `\u{1F528}`,
1993
+ `transaction`,
1994
+ (_c = (_b = child.transactionMeta) == null ? void 0 : _b.update.key) != null ? _c : `???`,
1995
+ `Adding atom "${newAtom.key}"`
1996
+ );
2044
1997
  }
2045
1998
  }
2046
- core.selectorAtoms.delete(key);
2047
- core.atomsThatAreDefault.delete(key);
2048
- core.timelineAtoms.delete(key);
2049
- store.logger.info(`\u{1F525}`, `atom`, `${key}`, `deleted`);
1999
+ setState(token, newValue, parent);
2050
2000
  }
2051
-
2052
- // src/not-found-error.ts
2053
- var capitalize = (str) => str[0].toUpperCase() + str.slice(1);
2054
- function prettyPrintTokenType(token) {
2055
- if (token.type === `readonly_selector`) {
2056
- return `Readonly Selector`;
2001
+ function ingestTransactionUpdate(transactionUpdate, parent, child) {
2002
+ for (const update of transactionUpdate.updates) {
2003
+ if (`newValue` in update) {
2004
+ ingestAtomUpdate(update, parent, child);
2005
+ } else {
2006
+ ingestTransactionUpdate(update, parent, child);
2007
+ }
2057
2008
  }
2058
- return capitalize(token.type);
2059
2009
  }
2060
- var NotFoundError = class extends Error {
2061
- constructor(token, store) {
2062
- super(
2063
- `${prettyPrintTokenType(token)} "${token.key}" not found in store "${store.config.name}".`
2064
- );
2065
- }
2066
- };
2067
-
2068
- // src/subscribe/recall-state.ts
2069
- var recallState = (state, store) => {
2070
- const core = target(store);
2071
- if (!core.operation.open) {
2010
+ var applyTransaction = (output, store) => {
2011
+ var _a;
2012
+ const child = newest(store);
2013
+ const { parent } = child;
2014
+ if (parent === null || child.transactionMeta === null || ((_a = child.transactionMeta) == null ? void 0 : _a.phase) !== `building`) {
2072
2015
  store.logger.warn(
2073
2016
  `\u{1F41E}`,
2074
- state.type,
2075
- state.key,
2076
- `recall called outside of an operation. This is probably a bug.`
2017
+ `transaction`,
2018
+ `???`,
2019
+ `applyTransaction called outside of a transaction. This is probably a bug in AtomIO.`
2020
+ );
2021
+ return;
2022
+ }
2023
+ child.transactionMeta.phase = `applying`;
2024
+ child.transactionMeta.update.output = output;
2025
+ parent.child = null;
2026
+ parent.subject.transactionApplying.next(child.transactionMeta);
2027
+ const { updates } = child.transactionMeta.update;
2028
+ store.logger.info(
2029
+ `\u{1F6C4}`,
2030
+ `transaction`,
2031
+ child.transactionMeta.update.key,
2032
+ `Applying transaction with ${updates.length} updates:`,
2033
+ updates
2034
+ );
2035
+ if (parent.transactionMeta === null) {
2036
+ ingestTransactionUpdate(child.transactionMeta.update, parent, child);
2037
+ const myTransaction = withdraw(
2038
+ { key: child.transactionMeta.update.key, type: `transaction` },
2039
+ store
2040
+ );
2041
+ myTransaction == null ? void 0 : myTransaction.subject.next(child.transactionMeta.update);
2042
+ store.logger.info(
2043
+ `\u{1F6EC}`,
2044
+ `transaction`,
2045
+ child.transactionMeta.update.key,
2046
+ `Finished applying transaction.`
2077
2047
  );
2078
- return core.valueMap.get(state.key);
2048
+ } else {
2049
+ ingestTransactionUpdate(child.transactionMeta.update, parent, child);
2050
+ parent.transactionMeta.update.updates.push(child.transactionMeta.update);
2079
2051
  }
2080
- return core.operation.prev.get(state.key);
2052
+ parent.subject.transactionApplying.next(null);
2081
2053
  };
2082
2054
 
2083
- // src/subscribe/subscribe-to-root-atoms.ts
2084
- var subscribeToRootAtoms = (state, store) => {
2085
- const dependencySubscriptions = `default` in state ? null : traceAllSelectorAtoms(state.key, store).map((atomKey) => {
2086
- const atom = store.atoms.get(atomKey);
2087
- if (atom === void 0) {
2088
- throw new Error(
2089
- `Atom "${atomKey}", a dependency of selector "${state.key}", not found in store "${store.config.name}".`
2090
- );
2091
- }
2092
- return atom.subject.subscribe(
2093
- `${state.type}:${state.key}`,
2094
- (atomChange) => {
2095
- store.logger.info(
2096
- `\u{1F4E2}`,
2097
- state.type,
2098
- state.key,
2099
- `root`,
2100
- atomKey,
2101
- `went`,
2102
- atomChange.oldValue,
2103
- `->`,
2104
- atomChange.newValue
2055
+ // src/transaction/build-transaction.ts
2056
+ var buildTransaction = (key, params, store) => {
2057
+ const parent = newest(store);
2058
+ parent.child = {
2059
+ parent,
2060
+ child: null,
2061
+ subject: parent.subject,
2062
+ loggers: parent.loggers,
2063
+ logger: parent.logger,
2064
+ config: parent.config,
2065
+ transactionMeta: {
2066
+ phase: `building`,
2067
+ time: Date.now(),
2068
+ update: {
2069
+ key,
2070
+ updates: [],
2071
+ params,
2072
+ output: void 0
2073
+ }
2074
+ },
2075
+ atoms: new LazyMap(parent.atoms),
2076
+ atomsThatAreDefault: new Set(parent.atomsThatAreDefault),
2077
+ families: new LazyMap(parent.families),
2078
+ operation: { open: false },
2079
+ readonlySelectors: new LazyMap(parent.readonlySelectors),
2080
+ timelines: new LazyMap(parent.timelines),
2081
+ timelineAtoms: new Junction(parent.timelineAtoms.toJSON()),
2082
+ trackers: /* @__PURE__ */ new Map(),
2083
+ transactions: new LazyMap(parent.transactions),
2084
+ selectorAtoms: new Junction(parent.selectorAtoms.toJSON()),
2085
+ selectorGraph: new Junction(parent.selectorGraph.toJSON(), {
2086
+ makeContentKey: (...keys) => keys.sort().join(`:`)
2087
+ }),
2088
+ selectors: new LazyMap(parent.selectors),
2089
+ valueMap: new LazyMap(parent.valueMap)
2090
+ };
2091
+ store.logger.info(
2092
+ `\u{1F6EB}`,
2093
+ `transaction`,
2094
+ key,
2095
+ `Building transaction with params:`,
2096
+ params
2097
+ );
2098
+ };
2099
+ function createTransaction(options, store) {
2100
+ const newTransaction = {
2101
+ key: options.key,
2102
+ type: `transaction`,
2103
+ run: (...params) => {
2104
+ buildTransaction(options.key, params, store);
2105
+ try {
2106
+ const output = options.do(
2107
+ {
2108
+ get: (token2) => getState(token2, store),
2109
+ set: (token2, value) => setState(token2, value, store),
2110
+ run: (token2) => runTransaction(token2, store)
2111
+ },
2112
+ ...params
2105
2113
  );
2106
- const oldValue = recallState(state, store);
2107
- const newValue = readOrComputeValue(state, store);
2108
- store.logger.info(
2109
- `\u2728`,
2110
- state.type,
2111
- state.key,
2112
- `went`,
2113
- oldValue,
2114
- `->`,
2115
- newValue
2114
+ applyTransaction(output, store);
2115
+ return output;
2116
+ } catch (thrown) {
2117
+ abortTransaction(store);
2118
+ store.logger.warn(`\u{1F4A5}`, `transaction`, options.key, `caught:`, thrown);
2119
+ throw thrown;
2120
+ }
2121
+ },
2122
+ install: (store2) => createTransaction(options, store2),
2123
+ subject: new Subject()
2124
+ };
2125
+ const target = newest(store);
2126
+ target.transactions.set(newTransaction.key, newTransaction);
2127
+ const token = deposit(newTransaction);
2128
+ store.subject.transactionCreation.next(token);
2129
+ return token;
2130
+ }
2131
+ var redoTransactionUpdate = (transactionUpdate, store) => {
2132
+ store.logger.info(`\u23ED\uFE0F`, `transaction`, transactionUpdate.key, `Redo`);
2133
+ for (const update of transactionUpdate.updates) {
2134
+ if (`newValue` in update) {
2135
+ const { key, newValue } = update;
2136
+ const token = { key, type: `atom` };
2137
+ const state = withdraw(token, store);
2138
+ if (state === void 0) {
2139
+ throw new Error(
2140
+ `State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
2116
2141
  );
2117
- state.subject.next({ newValue, oldValue });
2118
2142
  }
2119
- );
2120
- });
2121
- return dependencySubscriptions;
2143
+ setState(state, newValue, store);
2144
+ } else {
2145
+ redoTransactionUpdate(update, store);
2146
+ }
2147
+ }
2148
+ };
2149
+ var undoTransactionUpdate = (transactionUpdate, store) => {
2150
+ store.logger.info(`\u23EE\uFE0F`, `transaction`, transactionUpdate.key, `Undo`);
2151
+ for (const update of transactionUpdate.updates.reverse()) {
2152
+ if (`newValue` in update) {
2153
+ const { key, newValue } = update;
2154
+ const token = { key, type: `atom` };
2155
+ const state = withdraw(token, store);
2156
+ if (state === void 0) {
2157
+ throw new Error(
2158
+ `State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
2159
+ );
2160
+ }
2161
+ setState(state, newValue, store);
2162
+ } else {
2163
+ undoTransactionUpdate(update, store);
2164
+ }
2165
+ }
2122
2166
  };
2123
2167
 
2124
- export { FamilyTracker, Future, IMPLICIT, LazyMap, NotFoundError, Store, Subject, TRANSACTION_PHASES, Tracker, abortTransaction, addAtomToTimeline, applyTransaction, become, buildTransaction, cacheValue, clearStore, closeOperation, createAtom, createAtomFamily, createMutableAtom, createMutableAtomFamily, createReadonlySelectorFamily, createSelector, createSelectorFamily, createTimeline, createTransaction, deleteAtom, deleteSelector, deposit, evictCachedValue, getJsonFamily, getJsonToken, getSelectorDependencyKeys, getUpdateToken, isAtomDefault, isAtomKey, isAtomMutable, isAtomTokenMutable, isDone, isReadonlySelectorKey, isSelectorDefault, isSelectorKey, isStateKey, isTransceiver, isValueCached, markAtomAsDefault, markAtomAsNotDefault, markDone, openOperation, readCachedValue, readOrComputeValue, redoTransactionUpdate, registerSelector, setAtomOrSelector, subscribeToRootAtoms, target, timeTravel, traceAllSelectorAtoms, traceSelectorAtoms, undoTransactionUpdate, updateSelectorAtoms, withdraw, withdrawNewFamilyMember };
2168
+ // src/transaction/index.ts
2169
+ var TRANSACTION_PHASES = [`idle`, `building`, `applying`];
2170
+
2171
+ export { FamilyTracker, Future, IMPLICIT, LazyMap, NotFoundError, StatefulSubject, Store, Subject, TRANSACTION_PHASES, Tracker, abortTransaction, addAtomToTimeline, applyTransaction, become, buildTransaction, cacheValue, clearStore, closeOperation, createAtom, createAtomFamily, createMutableAtom, createMutableAtomFamily, createReadonlySelectorFamily, createSelector, createSelectorFamily, createTimeline, createTransaction, deleteAtom, deleteSelector, deposit, eldest, evictCachedValue, getJsonFamily, getJsonToken, getSelectorDependencyKeys, getUpdateToken, isAtomDefault, isAtomKey, isAtomMutable, isAtomTokenMutable, isDone, isReadonlySelectorKey, isSelectorDefault, isSelectorKey, isStateKey, isTransceiver, isValueCached, markAtomAsDefault, markAtomAsNotDefault, markDone, newest, openOperation, readCachedValue, readOrComputeValue, redoTransactionUpdate, registerSelector, setAtomOrSelector, subscribeToRootAtoms, timeTravel, traceAllSelectorAtoms, traceSelectorAtoms, undoTransactionUpdate, updateSelectorAtoms, withdraw, withdrawNewFamilyMember };
2125
2172
  //# sourceMappingURL=out.js.map
2126
2173
  //# sourceMappingURL=index.js.map