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