atom.io 0.12.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/index.cjs.map +1 -1
  2. package/dist/index.d.cts +6 -3
  3. package/dist/index.d.ts +6 -3
  4. package/dist/index.js.map +1 -1
  5. package/dist/metafile-cjs.json +1 -1
  6. package/dist/metafile-esm.json +1 -1
  7. package/internal/dist/index.cjs +1561 -1512
  8. package/internal/dist/index.cjs.map +1 -1
  9. package/internal/dist/index.d.cts +24 -14
  10. package/internal/dist/index.d.ts +24 -14
  11. package/internal/dist/index.js +1560 -1513
  12. package/internal/dist/index.js.map +1 -1
  13. package/internal/dist/metafile-cjs.json +1 -1
  14. package/internal/dist/metafile-esm.json +1 -1
  15. package/internal/src/atom/create-atom.ts +4 -4
  16. package/internal/src/atom/delete-atom.ts +11 -11
  17. package/internal/src/atom/is-default.ts +5 -5
  18. package/internal/src/caching.ts +12 -10
  19. package/internal/src/families/create-atom-family.ts +3 -3
  20. package/internal/src/families/create-readonly-selector-family.ts +3 -3
  21. package/internal/src/families/create-selector-family.ts +4 -4
  22. package/internal/src/index.ts +1 -0
  23. package/internal/src/keys.ts +4 -4
  24. package/internal/src/lazy-map.ts +6 -2
  25. package/internal/src/lineage.ts +18 -0
  26. package/internal/src/mutable/create-mutable-atom.ts +5 -4
  27. package/internal/src/mutable/get-json-family.ts +3 -3
  28. package/internal/src/mutable/tracker.ts +13 -10
  29. package/internal/src/operation.ts +19 -19
  30. package/internal/src/selector/create-read-write-selector.ts +5 -4
  31. package/internal/src/selector/create-readonly-selector.ts +4 -3
  32. package/internal/src/selector/create-selector.ts +6 -6
  33. package/internal/src/selector/delete-selector.ts +10 -10
  34. package/internal/src/selector/get-selector-dependency-keys.ts +2 -2
  35. package/internal/src/selector/register-selector.ts +4 -4
  36. package/internal/src/selector/update-selector-atoms.ts +2 -2
  37. package/internal/src/set-state/copy-mutable-if-needed.ts +4 -3
  38. package/internal/src/set-state/copy-mutable-in-transaction.ts +10 -16
  39. package/internal/src/set-state/copy-mutable-into-new-store.ts +1 -1
  40. package/internal/src/set-state/evict-downstream.ts +5 -5
  41. package/internal/src/set-state/set-atom.ts +6 -1
  42. package/internal/src/set-state/stow-update.ts +7 -2
  43. package/internal/src/store/store.ts +31 -24
  44. package/internal/src/store/withdraw-new-family-member.ts +3 -4
  45. package/internal/src/store/withdraw.ts +58 -59
  46. package/internal/src/subject.ts +14 -0
  47. package/internal/src/subscribe/recall-state.ts +5 -5
  48. package/internal/src/timeline/add-atom-to-timeline.ts +37 -27
  49. package/internal/src/timeline/create-timeline.ts +6 -8
  50. package/internal/src/timeline/time-travel.ts +22 -4
  51. package/internal/src/transaction/abort-transaction.ts +5 -3
  52. package/internal/src/transaction/apply-transaction.ts +80 -58
  53. package/internal/src/transaction/build-transaction.ts +33 -23
  54. package/internal/src/transaction/create-transaction.ts +6 -9
  55. package/internal/src/transaction/index.ts +2 -10
  56. package/internal/src/transaction/redo-transaction.ts +15 -10
  57. package/internal/src/transaction/undo-transaction.ts +15 -16
  58. package/introspection/dist/index.cjs +2 -2
  59. package/introspection/dist/index.cjs.map +1 -1
  60. package/introspection/dist/index.js +3 -3
  61. package/introspection/dist/index.js.map +1 -1
  62. package/introspection/src/attach-atom-index.ts +2 -2
  63. package/introspection/src/attach-selector-index.ts +2 -2
  64. package/package.json +3 -3
  65. package/react-devtools/dist/index.cjs +22 -8
  66. package/react-devtools/dist/index.cjs.map +1 -1
  67. package/react-devtools/dist/index.d.cts +17 -9
  68. package/react-devtools/dist/index.d.ts +17 -9
  69. package/react-devtools/dist/index.js +22 -8
  70. package/react-devtools/dist/index.js.map +1 -1
  71. package/react-devtools/dist/metafile-cjs.json +1 -1
  72. package/react-devtools/dist/metafile-esm.json +1 -1
  73. package/react-devtools/src/Updates.tsx +22 -10
  74. package/src/transaction.ts +7 -2
  75. package/transceivers/set-rtx/dist/index.cjs.map +1 -1
  76. package/transceivers/set-rtx/dist/index.d.cts +2 -2
  77. package/transceivers/set-rtx/dist/index.d.ts +2 -2
  78. package/transceivers/set-rtx/dist/index.js.map +1 -1
  79. package/transceivers/set-rtx/dist/metafile-cjs.json +1 -1
  80. package/transceivers/set-rtx/dist/metafile-esm.json +1 -1
  81. package/transceivers/set-rtx/src/set-rtx.ts +3 -3
@@ -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,1686 +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(
715
- `\u{1F41E}`,
716
- `timeline`,
717
- token.key,
718
- `Failed to ${action}. This timeline has not been initialized.`
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}"`
719
750
  );
720
- return;
721
751
  }
722
- if (direction === `forward` && timelineData.at === timelineData.history.length || direction === `backward` && timelineData.at === 0) {
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) {
723
758
  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}.`
759
+ `\u{1F41E}`,
760
+ `unknown`,
761
+ key,
762
+ `isDone called outside of an operation. This is probably a bug.`
728
763
  );
729
- return;
764
+ return true;
730
765
  }
731
- timelineData.timeTraveling = direction === `forward` ? `into_future` : `into_past`;
732
- if (direction === `backward`) {
733
- --timelineData.at;
766
+ return target.operation.done.has(key);
767
+ };
768
+ var markDone = (key, store) => {
769
+ const target = newest(store);
770
+ if (!target.operation.open) {
771
+ store.logger.warn(
772
+ `\u{1F41E}`,
773
+ `unknown`,
774
+ key,
775
+ `markDone called outside of an operation. This is probably a bug.`
776
+ );
777
+ return;
734
778
  }
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;
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);
792
+ }
793
+ if (state.type !== `atom`) {
794
+ store.logger.info(`\u{1F9EE}`, state.type, state.key, `computing value`);
795
+ return state.get();
796
+ }
797
+ const fallback = state.default instanceof Function ? state.default() : state.default;
798
+ store.logger.info(
799
+ `\u{1F481}`,
800
+ `atom`,
801
+ state.key,
802
+ `could not find cached value; using default`,
803
+ fallback
804
+ );
805
+ return state.default instanceof Function ? state.default() : state.default;
806
+ };
807
+
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;
819
+ }
820
+ return targetValue;
821
+ }
822
+
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;
745
831
  }
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);
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
840
+ );
841
+ if (result) {
842
+ return result;
843
+ }
751
844
  }
752
- break;
753
845
  }
754
846
  }
755
- if (direction === `forward`) {
756
- ++timelineData.at;
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;
757
853
  }
758
- timelineData.subject.next(action);
759
- timelineData.timeTraveling = null;
854
+ return null;
855
+ }
856
+
857
+ // src/set-state/emit-update.ts
858
+ var emitUpdate = (state, update, store) => {
760
859
  store.logger.info(
761
- `\u23F9\uFE0F`,
762
- `timeline`,
763
- token.key,
764
- `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`
860
+ `\u{1F4E2}`,
861
+ state.type,
862
+ state.key,
863
+ `went (`,
864
+ update.oldValue,
865
+ `->`,
866
+ update.newValue,
867
+ `) subscribers:`,
868
+ state.subject.subscribers
765
869
  );
870
+ state.subject.next(update);
766
871
  };
767
872
 
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) {
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);
877
+ store.logger.info(
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`
883
+ );
884
+ if (downstreamKeys) {
885
+ if (target.operation.open) {
780
886
  store.logger.info(
781
- `\u{1F6E0}\uFE0F`,
782
- token.type,
783
- token.key,
784
- `add ${token.type} "${token.key}"`
887
+ `\u{1F9F9}`,
888
+ atom.type,
889
+ atom.key,
890
+ `[ ${[...target.operation.done].join(`, `)} ] already done`
785
891
  );
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;
892
+ }
893
+ for (const key of downstreamKeys) {
894
+ if (isDone(key, store)) {
895
+ continue;
814
896
  }
815
- return state;
897
+ evictCachedValue(key, store);
898
+ markDone(key, store);
816
899
  }
817
900
  }
818
- return void 0;
819
- }
901
+ };
820
902
 
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;
837
- }
903
+ // src/set-state/stow-update.ts
904
+ function shouldUpdateBeStowed(key, update) {
905
+ if (isTransceiver(update.newValue)) {
906
+ return false;
838
907
  }
839
- return void 0;
908
+ if (key.includes(`\u{1F441}\u200D\u{1F5E8}`)) {
909
+ return false;
910
+ }
911
+ return true;
840
912
  }
841
-
842
- // src/transaction/apply-transaction.ts
843
- var applyTransaction = (output, store) => {
844
- if (store.transactionStatus.phase !== `building`) {
845
- store.logger.warn(
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(
846
918
  `\u{1F41E}`,
847
- `transaction`,
848
- `???`,
849
- `applyTransaction called outside of a transaction. This is probably a bug in AtomIO.`
919
+ `atom`,
920
+ key,
921
+ `stowUpdate called outside of a transaction. This is probably a bug.`
850
922
  );
851
923
  return;
852
924
  }
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}"`
885
- );
886
- }
887
- }
888
- atom_io.setState(token, newValue, store);
925
+ const shouldStow = shouldUpdateBeStowed(key, update);
926
+ if (!shouldStow) {
927
+ return;
889
928
  }
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
- );
929
+ const atomUpdate = __spreadValues({ key }, update);
930
+ if (state.family) {
931
+ atomUpdate.family = state.family;
898
932
  }
899
- myTransaction.subject.next({
900
- key: store.transactionStatus.key,
901
- atomUpdates,
902
- output,
903
- params: store.transactionStatus.params
904
- });
933
+ target.transactionMeta.update.updates.push(atomUpdate);
905
934
  store.logger.info(
906
- `\u{1F6EC}`,
907
- `transaction`,
908
- store.transactionStatus.key,
909
- `Finished applying transaction.`
935
+ `\u{1F4C1}`,
936
+ `atom`,
937
+ key,
938
+ `stowed (`,
939
+ update.oldValue,
940
+ `->`,
941
+ update.newValue,
942
+ `)`
910
943
  );
911
- store.transactionStatus = { phase: `idle` };
912
944
  };
913
945
 
914
- // src/lazy-map.ts
915
- var LazyMap = class extends Map {
916
- constructor(source) {
917
- super();
918
- this.source = source;
919
- this.deleted = /* @__PURE__ */ new Set();
920
- }
921
- get(key) {
922
- const has = super.has(key);
923
- if (has) {
924
- return super.get(key);
925
- }
926
- if (!this.deleted.has(key) && this.source.has(key)) {
927
- const value = this.source.get(key);
928
- super.set(key, value);
929
- return value;
930
- }
931
- return void 0;
932
- }
933
- _has(key) {
934
- return super.has(key);
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);
935
956
  }
936
- has(key) {
937
- return !this.deleted.has(key) && (super.has(key) || this.source.has(key));
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);
938
964
  }
939
- delete(key) {
940
- this.deleted.add(key);
941
- return super.delete(key);
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);
942
973
  }
943
974
  };
944
975
 
945
- // src/transaction/build-transaction.ts
946
- var buildTransaction = (key, params, store) => {
947
- store.transactionStatus = {
948
- key,
949
- phase: `building`,
950
- time: Date.now(),
951
- core: {
952
- atoms: new LazyMap(store.atoms),
953
- atomsThatAreDefault: new Set(store.atomsThatAreDefault),
954
- families: new LazyMap(store.families),
955
- operation: { open: false },
956
- readonlySelectors: new LazyMap(store.readonlySelectors),
957
- timelines: new LazyMap(store.timelines),
958
- timelineAtoms: new Junction(store.timelineAtoms.toJSON()),
959
- trackers: /* @__PURE__ */ new Map(),
960
- transactions: new LazyMap(store.transactions),
961
- selectorAtoms: new Junction(store.selectorAtoms.toJSON()),
962
- selectorGraph: new Junction(store.selectorGraph.toJSON(), {
963
- makeContentKey: (...keys) => keys.sort().join(`:`)
964
- }),
965
- selectors: new LazyMap(store.selectors),
966
- valueMap: new LazyMap(store.valueMap)
967
- },
968
- atomUpdates: [],
969
- params,
970
- output: void 0
971
- };
972
- store.logger.info(
973
- `\u{1F6EB}`,
974
- `transaction`,
975
- key,
976
- `Building transaction with params:`,
977
- params
978
- );
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;
979
986
  };
980
- function createTransaction(options, store) {
981
- const newTransaction = {
982
- key: options.key,
983
- type: `transaction`,
984
- run: (...params) => {
985
- buildTransaction(options.key, params, store);
986
- try {
987
- const output = options.do(
988
- {
989
- get: (token2) => atom_io.getState(token2, store),
990
- set: (token2, value) => atom_io.setState(token2, value, store)
991
- },
992
- ...params
993
- );
994
- applyTransaction(output, store);
995
- return output;
996
- } catch (thrown) {
997
- abortTransaction(store);
998
- store.logger.warn(`\u{1F4A5}`, `transaction`, options.key, `caught:`, thrown);
999
- throw thrown;
1000
- }
1001
- },
1002
- install: (store2) => createTransaction(options, store2),
1003
- subject: new Subject()
1004
- };
1005
- const core = target(store);
1006
- core.transactions.set(newTransaction.key, newTransaction);
1007
- const token = deposit(newTransaction);
1008
- store.subject.transactionCreation.next(token);
1009
- return token;
1010
- }
1011
- var target = (store) => store.transactionStatus.phase === `building` ? store.transactionStatus.core : store;
1012
- var redoTransactionUpdate = (update, store) => {
1013
- store.logger.info(`\u23ED\uFE0F`, `transaction`, update.key, `Redo`);
1014
- for (const { key, newValue } of update.atomUpdates) {
1015
- const token = { key, type: `atom` };
1016
- const state = withdraw(token, store);
1017
- if (state === void 0) {
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) {
1018
1000
  throw new Error(
1019
- `State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
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)
1020
1007
  );
1008
+ } else if (!rootKeys.includes(indirectDependencyKey)) {
1009
+ rootKeys.push(indirectDependencyKey);
1021
1010
  }
1022
- atom_io.setState(state, newValue, store);
1023
1011
  }
1012
+ return rootKeys;
1024
1013
  };
1025
- var undoTransactionUpdate = (update, store) => {
1026
- store.logger.info(
1027
- `\u23EE\uFE0F`,
1028
- `transaction`,
1029
- update.key,
1030
- `Undoing transaction update`,
1031
- update
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)
1032
1018
  );
1033
- for (const { key, oldValue } of update.atomUpdates) {
1034
- const token = { key, type: `atom` };
1035
- const state = withdraw(token, store);
1019
+ };
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
+ });
1029
+ store.logger.info(
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(`, `)} ]`
1042
+ );
1043
+ for (const atomKey of rootKeys) {
1044
+ core.selectorAtoms = core.selectorAtoms.set({
1045
+ selectorKey,
1046
+ atomKey
1047
+ });
1048
+ }
1049
+ }
1050
+ };
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
+ `)`
1071
+ );
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);
1036
1088
  if (state === void 0) {
1037
1089
  throw new Error(
1038
- `State "${token.key}" not found in this store. This is surprising, because we are navigating the history of the store.`
1090
+ `State "${stateToken.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
1039
1091
  );
1040
1092
  }
1041
- atom_io.setState(state, oldValue, store);
1093
+ setAtomOrSelector(state, newValue, store);
1094
+ }
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
+ `)`
1119
+ );
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;
1042
1143
  }
1144
+ store.subject.selectorCreation.next(token);
1145
+ return token;
1043
1146
  };
1044
1147
 
1045
- // src/transaction/index.ts
1046
- var TRANSACTION_PHASES = [`idle`, `building`, `applying`];
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();
1166
+ store.logger.info(
1167
+ `\u2728`,
1168
+ readonlySelector.type,
1169
+ readonlySelector.key,
1170
+ `=`,
1171
+ initialValue
1172
+ );
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;
1182
+ };
1047
1183
 
1048
- // src/caching.ts
1049
- function cacheValue(key, value, subject, store) {
1050
- const currentValue = target(store).valueMap.get(key);
1051
- if (currentValue instanceof Future) {
1052
- currentValue.cancel();
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
+ );
1053
1196
  }
1054
- if (value instanceof Promise) {
1055
- const future = new Future(value);
1056
- target(store).valueMap.set(key, future);
1057
- future.then((resolved) => {
1058
- if (future.isCanceled) {
1059
- return;
1060
- }
1061
- cacheValue(key, resolved, subject, store);
1062
- subject.next({ newValue: resolved, oldValue: future });
1063
- }).catch((thrown) => {
1064
- if (thrown !== `canceled`) {
1065
- store.logger.error(`\u{1F4A5}`, `state`, key, `rejected:`, thrown);
1066
- }
1067
- });
1068
- return future;
1197
+ if (`set` in options) {
1198
+ return createReadWriteSelector(options, family, store);
1069
1199
  }
1070
- target(store).valueMap.set(key, value);
1071
- return value;
1200
+ return createReadonlySelector(options, family, store);
1072
1201
  }
1073
- var readCachedValue = (key, store) => target(store).valueMap.get(key);
1074
- var isValueCached = (key, store) => target(store).valueMap.has(key);
1075
- var evictCachedValue = (key, store) => {
1076
- const core = target(store);
1077
- const currentValue = core.valueMap.get(key);
1078
- if (currentValue instanceof Future) {
1079
- currentValue.cancel();
1080
- }
1081
- if (core.operation.open) {
1082
- core.operation.prev.set(key, currentValue);
1083
- }
1084
- core.valueMap.delete(key);
1085
- store.logger.info(`\u{1F5D1}`, `state`, key, `evicted`);
1086
- };
1087
- var Tracker = class {
1088
- constructor(mutableState, store) {
1089
- this.unsubscribeFromInnerValue = null;
1090
- this.mutableState = mutableState;
1091
- this.latestUpdateState = this.initializeState(mutableState, store);
1092
- this.observeCore(mutableState, this.latestUpdateState, store);
1093
- this.updateCore(mutableState, this.latestUpdateState, store);
1094
- const core = target(store);
1095
- core.trackers.set(mutableState.key, this);
1096
- }
1097
- initializeState(mutableState, store) {
1098
- const latestUpdateStateKey = `*${mutableState.key}`;
1099
- deleteAtom({ type: `atom`, key: latestUpdateStateKey }, store);
1100
- const familyMetaData = mutableState.family ? {
1101
- key: `*${mutableState.family.key}`,
1102
- subKey: mutableState.family.subKey
1103
- } : void 0;
1104
- const latestUpdateState = createAtom(
1105
- {
1106
- key: latestUpdateStateKey,
1107
- default: null
1108
- },
1109
- familyMetaData,
1110
- store
1111
- );
1112
- return latestUpdateState;
1113
- }
1114
- observeCore(mutableState, latestUpdateState, store) {
1115
- const originalInnerValue = atom_io.getState(mutableState, store);
1116
- this.unsubscribeFromInnerValue = originalInnerValue.subscribe(
1117
- `tracker:${store.config.name}:${store.transactionStatus.phase === `idle` ? `main` : store.transactionStatus.key}`,
1118
- (update) => {
1119
- const unsubscribe = store.subject.operationStatus.subscribe(
1120
- mutableState.key,
1121
- () => {
1122
- unsubscribe();
1123
- atom_io.setState(latestUpdateState, update, store);
1124
- }
1125
- );
1126
- }
1127
- );
1128
- atom_io.subscribe(
1129
- mutableState,
1130
- (update) => {
1131
- var _a;
1132
- if (update.newValue !== update.oldValue) {
1133
- (_a = this.unsubscribeFromInnerValue) == null ? void 0 : _a.call(this);
1134
- this.unsubscribeFromInnerValue = update.newValue.subscribe(
1135
- `tracker:${store.config.name}:${store.transactionStatus.phase === `idle` ? `main` : store.transactionStatus.key}`,
1136
- (update2) => {
1137
- const unsubscribe = store.subject.operationStatus.subscribe(
1138
- mutableState.key,
1139
- () => {
1140
- unsubscribe();
1141
- atom_io.setState(latestUpdateState, update2, store);
1142
- }
1143
- );
1144
- }
1145
- );
1146
- }
1147
- },
1148
- `${store.config.name}: tracker observing inner value`,
1149
- store
1150
- );
1202
+
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;
1151
1214
  }
1152
- updateCore(mutableState, latestUpdateState, store) {
1153
- atom_io.subscribe(
1154
- latestUpdateState,
1155
- ({ newValue, oldValue }) => {
1156
- const timelineId = store.timelineAtoms.getRelatedKey(
1157
- latestUpdateState.key
1158
- );
1159
- if (timelineId) {
1160
- const timelineData = store.timelines.get(timelineId);
1161
- if (timelineData == null ? void 0 : timelineData.timeTraveling) {
1162
- const unsubscribe2 = atom_io.subscribeToTimeline(
1163
- { key: timelineId, type: `timeline` },
1164
- (update) => {
1165
- unsubscribe2();
1166
- atom_io.setState(
1167
- mutableState,
1168
- (transceiver) => {
1169
- if (update === `redo` && newValue) {
1170
- transceiver.do(newValue);
1171
- } else if (update === `undo` && oldValue) {
1172
- transceiver.undo(oldValue);
1173
- }
1174
- return transceiver;
1175
- },
1176
- store
1177
- );
1178
- }
1179
- );
1180
- return;
1181
- }
1182
- }
1183
- const unsubscribe = store.subject.operationStatus.subscribe(
1184
- latestUpdateState.key,
1185
- () => {
1186
- unsubscribe();
1187
- const mutable = atom_io.getState(mutableState, store);
1188
- const updateNumber = mutable.getUpdateNumber(newValue);
1189
- const eventOffset = updateNumber - mutable.cacheUpdateNumber;
1190
- if (newValue && eventOffset === 1) {
1191
- atom_io.setState(
1192
- mutableState,
1193
- (transceiver) => (transceiver.do(newValue), transceiver),
1194
- store
1195
- );
1196
- }
1197
- }
1198
- );
1199
- },
1200
- `${store.config.name}: tracker observing latest update`,
1201
- store
1202
- );
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);
1221
+ }
1222
+ );
1223
+ for (const downstreamToken of downstreamTokens) {
1224
+ if (downstreamToken) {
1225
+ deleteSelector(downstreamToken, store);
1226
+ }
1203
1227
  }
1204
- };
1228
+ target.selectorGraph.delete(key);
1229
+ store.logger.info(`\u{1F525}`, selectorToken.type, `${key}`, `deleted`);
1230
+ }
1205
1231
 
1206
- // src/mutable/create-mutable-atom.ts
1207
- function createMutableAtom(options, store) {
1208
- store.logger.info(
1209
- `\u{1F527}`,
1210
- `atom`,
1211
- options.key,
1212
- `creating in store "${store.config.name}"`
1213
- );
1214
- const coreState = createAtom(options, void 0, store);
1215
- new Tracker(coreState, store);
1216
- const jsonState = json.selectJson(coreState, options, store);
1217
- atom_io.subscribe(
1218
- jsonState,
1219
- () => {
1220
- const trackerHasBeenInitialized = target(store).trackers.has(coreState.key);
1221
- if (!trackerHasBeenInitialized) {
1222
- new Tracker(coreState, store);
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);
1223
1244
  }
1245
+ return createSelector(
1246
+ {
1247
+ key: fullKey,
1248
+ get: options.get(key)
1249
+ },
1250
+ family,
1251
+ store
1252
+ );
1224
1253
  },
1225
- `tracker-initializer:${store == null ? void 0 : store.config.name}:${store.transactionStatus.phase === `idle` ? `main` : store.transactionStatus.key}`
1254
+ {
1255
+ key: options.key,
1256
+ type: `readonly_selector_family`,
1257
+ subject
1258
+ }
1226
1259
  );
1227
- return coreState;
1228
1260
  }
1229
- function createAtomFamily(options, store) {
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);
1230
1267
  const subject = new Subject();
1231
- const atomFamily = Object.assign(
1268
+ const selectorFamily = Object.assign(
1232
1269
  (key) => {
1233
1270
  const subKey = json.stringifyJson(key);
1234
1271
  const family = { key: options.key, subKey };
1235
1272
  const fullKey = `${options.key}(${subKey})`;
1236
- const existing = withdraw({ key: fullKey, type: `atom` }, store);
1237
- let token;
1273
+ const existing = target.selectors.get(fullKey);
1238
1274
  if (existing) {
1239
- token = deposit(existing);
1240
- } else {
1241
- const individualOptions = {
1242
- key: fullKey,
1243
- default: options.default instanceof Function ? options.default(key) : options.default
1244
- };
1245
- if (options.effects) {
1246
- individualOptions.effects = options.effects(key);
1247
- }
1248
- token = createAtom(individualOptions, family, store);
1249
- subject.next(token);
1275
+ return deposit(existing);
1250
1276
  }
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);
1251
1287
  return token;
1252
1288
  },
1253
1289
  {
1254
1290
  key: options.key,
1255
- type: `atom_family`,
1256
- subject
1291
+ type: `selector_family`
1257
1292
  }
1258
1293
  );
1259
- const core = target(store);
1260
- core.families.set(options.key, atomFamily);
1261
- return atomFamily;
1294
+ target.families.set(options.key, selectorFamily);
1295
+ return selectorFamily;
1262
1296
  }
1263
1297
 
1264
- // src/operation.ts
1265
- var openOperation = (token, store) => {
1266
- const core = target(store);
1267
- if (core.operation.open) {
1268
- store.logger.error(
1269
- `\u274C`,
1270
- token.type,
1271
- token.key,
1272
- `failed to setState during a setState for "${core.operation.token.key}"`
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
1273
1307
  );
1274
- return `rejection`;
1275
- }
1276
- core.operation = {
1277
- open: true,
1278
- done: /* @__PURE__ */ new Set(),
1279
- prev: /* @__PURE__ */ new Map(),
1280
- time: Date.now(),
1281
- token
1282
- };
1283
- store.logger.info(
1284
- `\u2B55`,
1285
- token.type,
1286
- token.key,
1287
- `operation start in store "${store.config.name}"${store.transactionStatus.phase === `idle` ? `` : ` ${store.transactionStatus.phase} "${store.transactionStatus.key}"`}`
1288
- );
1289
- };
1290
- var closeOperation = (store) => {
1291
- const core = target(store);
1292
- if (core.operation.open) {
1293
- store.logger.info(
1294
- `\u{1F534}`,
1295
- core.operation.token.type,
1296
- core.operation.token.key,
1297
- `operation done in store "${store.config.name}"`
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
+ }
1298
1318
  );
1299
- }
1300
- core.operation = { open: false };
1301
- store.subject.operationStatus.next(core.operation);
1302
- };
1303
- var isDone = (key, store) => {
1304
- const core = target(store);
1305
- if (!core.operation.open) {
1306
- store.logger.warn(
1307
- `\u{1F41E}`,
1308
- `unknown`,
1309
- key,
1310
- `isDone called outside of an operation. This is probably a bug.`
1311
- );
1312
- return true;
1313
- }
1314
- return core.operation.done.has(key);
1315
- };
1316
- var markDone = (key, store) => {
1317
- const core = target(store);
1318
- if (!core.operation.open) {
1319
- store.logger.warn(
1320
- `\u{1F41E}`,
1321
- `unknown`,
1322
- key,
1323
- `markDone called outside of an operation. This is probably a bug.`
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
+ }
1324
1328
  );
1325
- return;
1326
1329
  }
1327
- core.operation.done.add(key);
1328
1330
  };
1329
1331
 
1330
- // src/set-state/become.ts
1331
- var become = (nextVersionOfThing) => (originalThing) => nextVersionOfThing instanceof Function ? nextVersionOfThing(
1332
- originalThing instanceof Function ? originalThing() : originalThing
1333
- ) : nextVersionOfThing;
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;
1341
+ }
1334
1342
 
1335
- // src/read-or-compute-value.ts
1336
- var readOrComputeValue = (state, store) => {
1337
- if (isValueCached(state.key, store)) {
1338
- store.logger.info(`\u{1F4D6}`, state.type, state.key, `reading cached value`);
1339
- return readCachedValue(state.key, store);
1340
- }
1341
- if (state.type !== `atom`) {
1342
- store.logger.info(`\u{1F9EE}`, state.type, state.key, `computing value`);
1343
- return state.get();
1344
- }
1345
- const fallback = state.default instanceof Function ? state.default() : state.default;
1346
- store.logger.info(
1347
- `\u{1F481}`,
1348
- `atom`,
1349
- state.key,
1350
- `could not find cached value; using default`,
1351
- fallback
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
1352
1349
  );
1353
- return state.default instanceof Function ? state.default() : state.default;
1350
+ return jsonFamily;
1354
1351
  };
1355
1352
 
1356
- // src/set-state/copy-mutable-if-needed.ts
1357
- function copyMutableIfNeeded(atom, transform, origin, target2) {
1358
- const originValue = origin.valueMap.get(atom.key);
1359
- const targetValue = target2.valueMap.get(atom.key);
1360
- if (originValue === targetValue) {
1361
- origin.logger.info(`\u{1F4C3}`, `atom`, `${atom.key}`, `copying`);
1362
- const copiedValue = transform.fromJson(transform.toJson(originValue));
1363
- target2.valueMap.set(atom.key, copiedValue);
1364
- new Tracker(atom, origin);
1365
- return copiedValue;
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
+ };
1366
1362
  }
1367
- return targetValue;
1368
- }
1363
+ return jsonToken;
1364
+ };
1369
1365
 
1370
- // src/set-state/copy-mutable-in-transaction.ts
1371
- function copyMutableIfWithinTransaction(oldValue, atom, store) {
1372
- if (store.transactionStatus.phase === `building` || store.transactionStatus.phase === `applying`) {
1373
- if (`toJson` in atom && `fromJson` in atom) {
1374
- const copiedValue = copyMutableIfNeeded(
1375
- atom,
1376
- atom,
1377
- store,
1378
- store.transactionStatus.core
1379
- );
1380
- return copiedValue;
1381
- }
1382
- if (`family` in atom) {
1383
- const family = store.transactionStatus.core.families.get(atom.family.key);
1384
- if (family && family.type === `atom_family`) {
1385
- const result = copyMutableFamilyMemberWithinTransaction(
1386
- atom,
1387
- family,
1388
- store,
1389
- store.transactionStatus.core
1390
- );
1391
- if (result) {
1392
- return result;
1393
- }
1394
- }
1395
- }
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
+ };
1396
1375
  }
1397
- return oldValue;
1376
+ return updateToken;
1377
+ };
1378
+
1379
+ // src/mutable/is-atom-token-mutable.ts
1380
+ function isAtomTokenMutable(token) {
1381
+ return token.key.endsWith(`::mutable`);
1398
1382
  }
1399
- function copyMutableFamilyMemberWithinTransaction(atom, family, origin, target2) {
1400
- if (`toJson` in family && `fromJson` in family) {
1401
- const copyCreated = copyMutableIfNeeded(atom, family, origin, target2);
1402
- return copyCreated;
1403
- }
1404
- return null;
1383
+
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;
1405
1387
  }
1406
1388
 
1407
- // src/set-state/emit-update.ts
1408
- var emitUpdate = (state, update, store) => {
1409
- store.logger.info(
1410
- `\u{1F4E2}`,
1411
- state.type,
1412
- state.key,
1413
- `went (`,
1414
- update.oldValue,
1415
- `->`,
1416
- update.newValue,
1417
- `) subscribers:`,
1418
- state.subject.subscribers
1419
- );
1420
- state.subject.next(update);
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);
1396
+ };
1397
+ var markAtomAsDefault = (key, store) => {
1398
+ const core = newest(store);
1399
+ core.atomsThatAreDefault = new Set(core.atomsThatAreDefault).add(key);
1400
+ };
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));
1421
1409
  };
1422
1410
 
1423
- // src/set-state/evict-downstream.ts
1424
- var evictDownStream = (atom, store) => {
1425
- const core = target(store);
1426
- const downstreamKeys = core.selectorAtoms.getRelatedKeys(atom.key);
1411
+ // src/atom/create-atom.ts
1412
+ function createAtom(options, family, store) {
1427
1413
  store.logger.info(
1428
- `\u{1F9F9}`,
1429
- atom.type,
1430
- atom.key,
1431
- downstreamKeys ? `evicting ${downstreamKeys.size} states downstream:` : `no downstream states`,
1432
- downstreamKeys != null ? downstreamKeys : `to evict`
1414
+ `\u{1F528}`,
1415
+ `atom`,
1416
+ options.key,
1417
+ `creating in store "${store.config.name}"`
1433
1418
  );
1434
- if (downstreamKeys) {
1435
- if (core.operation.open) {
1436
- store.logger.info(
1437
- `\u{1F9F9}`,
1438
- atom.type,
1439
- atom.key,
1440
- `[ ${[...core.operation.done].join(`, `)} ] already done`
1419
+ const target = newest(store);
1420
+ const existing = target.atoms.get(options.key);
1421
+ if (existing) {
1422
+ store.logger.error(
1423
+ `\u274C`,
1424
+ `atom`,
1425
+ options.key,
1426
+ `Tried to create atom, but it already exists in the store.`,
1427
+ `(Ignore if you are in development using hot module replacement.)`
1428
+ );
1429
+ return deposit(existing);
1430
+ }
1431
+ const subject = new Subject();
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}"`
1441
1440
  );
1442
- }
1443
- for (const key of downstreamKeys) {
1444
- if (isDone(key, store)) {
1445
- continue;
1441
+ return `mutable` in options ? createMutableAtom(options, store2) : createAtom(options, void 0, store2);
1442
+ },
1443
+ subject
1444
+ }), family && { family });
1445
+ let initialValue = options.default;
1446
+ if (options.default instanceof Function) {
1447
+ initialValue = options.default();
1448
+ }
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);
1446
1463
  }
1447
- evictCachedValue(key, store);
1448
- markDone(key, store);
1464
+ ++effectIndex;
1449
1465
  }
1466
+ newAtom.cleanup = () => {
1467
+ for (const cleanup of cleanupFunctions) {
1468
+ cleanup();
1469
+ }
1470
+ };
1450
1471
  }
1451
- };
1452
-
1453
- // src/set-state/stow-update.ts
1454
- function shouldUpdateBeStowed(key, update) {
1455
- if (isTransceiver(update.newValue)) {
1456
- return false;
1457
- }
1458
- if (key.includes(`\u{1F441}\u200D\u{1F5E8}`)) {
1459
- return false;
1460
- }
1461
- return true;
1472
+ store.subject.atomCreation.next(token);
1473
+ return token;
1462
1474
  }
1463
- var stowUpdate = (state, update, store) => {
1464
- const { key } = state;
1465
- if (store.transactionStatus.phase !== `building`) {
1475
+
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) {
1466
1483
  store.logger.error(
1467
- `\u{1F41E}`,
1484
+ `\u274C`,
1468
1485
  `atom`,
1469
- key,
1470
- `stowUpdate called outside of a transaction. This is probably a bug.`
1486
+ `${key}`,
1487
+ `Tried to delete atom, but it does not exist in the store.`
1471
1488
  );
1472
- return;
1473
1489
  }
1474
- const shouldStow = shouldUpdateBeStowed(key, update);
1475
- if (!shouldStow) {
1476
- return;
1477
- }
1478
- const atomUpdate = __spreadValues({ key }, update);
1479
- if (state.family) {
1480
- atomUpdate.family = state.family;
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
+ }
1481
1501
  }
1482
- store.transactionStatus.atomUpdates.push(atomUpdate);
1483
- store.logger.info(
1484
- `\u{1F4C1}`,
1485
- `atom`,
1486
- key,
1487
- `stowed (`,
1488
- update.oldValue,
1489
- `->`,
1490
- update.newValue,
1491
- `)`
1492
- );
1493
- };
1502
+ target.selectorAtoms.delete(key);
1503
+ target.atomsThatAreDefault.delete(key);
1504
+ target.timelineAtoms.delete(key);
1505
+ store.logger.info(`\u{1F525}`, `atom`, `${key}`, `deleted`);
1506
+ }
1494
1507
 
1495
- // src/set-state/set-atom.ts
1496
- var setAtom = (atom, next, store) => {
1497
- const oldValue = readOrComputeValue(atom, store);
1498
- let newValue = copyMutableIfWithinTransaction(oldValue, atom, store);
1499
- newValue = become(next)(newValue);
1500
- store.logger.info(`\u{1F4DD}`, `atom`, atom.key, `set to`, newValue);
1501
- newValue = cacheValue(atom.key, newValue, atom.subject, store);
1502
- if (isAtomDefault(atom.key, store)) {
1503
- markAtomAsNotDefault(atom.key, store);
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();
1504
1514
  }
1505
- markDone(atom.key, store);
1506
- evictDownStream(atom, store);
1507
- const update = { oldValue, newValue };
1508
- if (store.transactionStatus.phase !== `building`) {
1509
- emitUpdate(atom, update, store);
1510
- } else {
1511
- stowUpdate(atom, update, store);
1515
+ get(key) {
1516
+ const has = super.has(key);
1517
+ if (has) {
1518
+ return super.get(key);
1519
+ }
1520
+ if (!this.deleted.has(key) && this.source.has(key)) {
1521
+ const value = this.source.get(key);
1522
+ return value;
1523
+ }
1524
+ return void 0;
1512
1525
  }
1513
- };
1514
-
1515
- // src/set-state/set-atom-or-selector.ts
1516
- var setAtomOrSelector = (state, value, store) => {
1517
- if (state.type === `selector`) {
1518
- state.set(value);
1519
- } else {
1520
- setAtom(state, value, store);
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);
1521
1539
  }
1522
1540
  };
1523
1541
 
1524
- // src/keys.ts
1525
- var isAtomKey = (key, store) => target(store).atoms.has(key);
1526
- var isSelectorKey = (key, store) => target(store).selectors.has(key);
1527
- var isReadonlySelectorKey = (key, store) => target(store).readonlySelectors.has(key);
1528
- var isStateKey = (key, store) => isAtomKey(key, store) || isSelectorKey(key, store) || isReadonlySelectorKey(key, store);
1529
-
1530
- // src/selector/get-selector-dependency-keys.ts
1531
- var getSelectorDependencyKeys = (key, store) => {
1532
- const sources = target(store).selectorGraph.getRelationEntries({ downstreamSelectorKey: key }).filter(([_, { source }]) => source !== key).map(([_, { source }]) => source).filter((source) => isStateKey(source, store));
1533
- return sources;
1534
- };
1535
-
1536
- // src/selector/trace-selector-atoms.ts
1537
- var traceSelectorAtoms = (selectorKey, directDependencyKey, store) => {
1538
- const rootKeys = [];
1539
- const indirectDependencyKeys = getSelectorDependencyKeys(
1540
- directDependencyKey,
1541
- store
1542
- );
1543
- let depth = 0;
1544
- while (indirectDependencyKeys.length > 0) {
1545
- const indirectDependencyKey = indirectDependencyKeys.shift();
1546
- ++depth;
1547
- if (depth > 99999) {
1548
- throw new Error(
1549
- `Maximum selector dependency depth exceeded (> 99999) in selector "${selectorKey}". This is likely due to a circular dependency.`
1550
- );
1551
- }
1552
- if (!isAtomKey(indirectDependencyKey, store)) {
1553
- indirectDependencyKeys.push(
1554
- ...getSelectorDependencyKeys(indirectDependencyKey, store)
1555
- );
1556
- } else if (!rootKeys.includes(indirectDependencyKey)) {
1557
- rootKeys.push(indirectDependencyKey);
1558
- }
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);
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
+ );
1559
1555
  }
1560
- return rootKeys;
1561
- };
1562
- var traceAllSelectorAtoms = (selectorKey, store) => {
1563
- const directDependencyKeys = getSelectorDependencyKeys(selectorKey, store);
1564
- return directDependencyKeys.flatMap(
1565
- (depKey) => isAtomKey(depKey, store) ? depKey : traceSelectorAtoms(selectorKey, depKey, store)
1566
- );
1567
1556
  };
1568
1557
 
1569
- // src/selector/update-selector-atoms.ts
1570
- var updateSelectorAtoms = (selectorKey, dependency, store) => {
1571
- const core = target(store);
1572
- if (dependency.type === `atom`) {
1573
- core.selectorAtoms = core.selectorAtoms.set({
1574
- selectorKey,
1575
- atomKey: dependency.key
1576
- });
1577
- store.logger.info(
1578
- `\u{1F50D}`,
1579
- `selector`,
1580
- selectorKey,
1581
- `discovers root atom "${dependency.key}"`
1582
- );
1583
- } else {
1584
- const rootKeys = traceSelectorAtoms(selectorKey, dependency.key, store);
1585
- store.logger.info(
1586
- `\u{1F50D}`,
1587
- `selector`,
1588
- selectorKey,
1589
- `discovers root atoms: [ ${rootKeys.map((key) => `"${key}"`).join(`, `)} ]`
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.`
1590
1567
  );
1591
- for (const atomKey of rootKeys) {
1592
- core.selectorAtoms = core.selectorAtoms.set({
1593
- selectorKey,
1594
- atomKey
1595
- });
1596
- }
1568
+ return target.valueMap.get(state.key);
1597
1569
  }
1570
+ return target.operation.prev.get(state.key);
1598
1571
  };
1599
1572
 
1600
- // src/selector/register-selector.ts
1601
- var registerSelector = (selectorKey, store) => ({
1602
- get: (dependency) => {
1603
- const core = target(store);
1604
- const alreadyRegistered = core.selectorGraph.getRelationEntries({ downstreamSelectorKey: selectorKey }).some(([_, { source }]) => source === dependency.key);
1605
- const dependencyState = withdraw(dependency, store);
1606
- if (dependencyState === void 0) {
1607
- throw new Error(
1608
- `State "${dependency.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
1609
- );
1610
- }
1611
- const dependencyValue = readOrComputeValue(dependencyState, store);
1612
- store.logger.info(
1613
- `\u{1F50C}`,
1614
- `selector`,
1615
- selectorKey,
1616
- `registers dependency ( "${dependency.key}" =`,
1617
- dependencyValue,
1618
- `)`
1619
- );
1620
- if (!alreadyRegistered) {
1621
- core.selectorGraph = core.selectorGraph.set(
1622
- {
1623
- upstreamSelectorKey: dependency.key,
1624
- downstreamSelectorKey: selectorKey
1625
- },
1626
- {
1627
- source: dependency.key
1628
- }
1629
- );
1630
- }
1631
- updateSelectorAtoms(selectorKey, dependency, store);
1632
- return dependencyValue;
1633
- },
1634
- set: (stateToken, newValue) => {
1635
- const state = withdraw(stateToken, store);
1636
- if (state === void 0) {
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) {
1637
1578
  throw new Error(
1638
- `State "${stateToken.key}" not found in this store. Did you forget to initialize with the "atom" or "selector" function?`
1579
+ `Atom "${atomKey}", a dependency of selector "${state.key}", not found in store "${store.config.name}".`
1639
1580
  );
1640
1581
  }
1641
- setAtomOrSelector(state, newValue, store);
1642
- }
1643
- });
1644
-
1645
- // src/selector/create-read-write-selector.ts
1646
- var createReadWriteSelector = (options, family, store, core) => {
1647
- const subject = new Subject();
1648
- const { get, set } = registerSelector(options.key, store);
1649
- const getSelf = () => {
1650
- const value = options.get({ get });
1651
- cacheValue(options.key, value, subject, store);
1652
- return value;
1653
- };
1654
- const setSelf = (next) => {
1655
- const oldValue = getSelf();
1656
- const newValue = become(next)(oldValue);
1657
- store.logger.info(
1658
- `\u{1F4DD}`,
1659
- `selector`,
1660
- options.key,
1661
- `set (`,
1662
- oldValue,
1663
- `->`,
1664
- newValue,
1665
- `)`
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
+ }
1666
1609
  );
1667
- cacheValue(options.key, newValue, subject, store);
1668
- markDone(options.key, store);
1669
- if (store.transactionStatus.phase === `idle`) {
1670
- subject.next({ newValue, oldValue });
1671
- }
1672
- options.set({ get, set }, newValue);
1673
- };
1674
- const mySelector = __spreadValues(__spreadProps(__spreadValues({}, options), {
1675
- subject,
1676
- install: (s) => createSelector(options, family, s),
1677
- get: getSelf,
1678
- set: setSelf,
1679
- type: `selector`
1680
- }), family && { family });
1681
- core.selectors.set(options.key, mySelector);
1682
- const initialValue = getSelf();
1683
- store.logger.info(`\u2728`, mySelector.type, mySelector.key, `=`, initialValue);
1684
- const token = {
1685
- key: options.key,
1686
- type: `selector`
1687
- };
1688
- if (family) {
1689
- token.family = family;
1690
- }
1691
- store.subject.selectorCreation.next(token);
1692
- return token;
1693
- };
1694
-
1695
- // src/selector/create-readonly-selector.ts
1696
- var createReadonlySelector = (options, family, store, core) => {
1697
- const subject = new Subject();
1698
- const { get } = registerSelector(options.key, store);
1699
- const getSelf = () => {
1700
- const value = options.get({ get });
1701
- cacheValue(options.key, value, subject, store);
1702
- return value;
1703
- };
1704
- const readonlySelector = __spreadValues(__spreadProps(__spreadValues({}, options), {
1705
- subject,
1706
- install: (s) => createSelector(options, family, s),
1707
- get: getSelf,
1708
- type: `readonly_selector`
1709
- }), family && { family });
1710
- core.readonlySelectors.set(options.key, readonlySelector);
1711
- const initialValue = getSelf();
1712
- store.logger.info(
1713
- `\u2728`,
1714
- readonlySelector.type,
1715
- readonlySelector.key,
1716
- `=`,
1717
- initialValue
1718
- );
1719
- const token = {
1720
- key: options.key,
1721
- type: `readonly_selector`
1722
- };
1723
- if (family) {
1724
- token.family = family;
1725
- }
1726
- store.subject.selectorCreation.next(token);
1727
- return token;
1610
+ });
1611
+ return dependencySubscriptions;
1728
1612
  };
1729
1613
 
1730
- // src/selector/create-selector.ts
1731
- function createSelector(options, family, store) {
1732
- const core = target(store);
1733
- const existingWritable = core.selectors.get(options.key);
1734
- const existingReadonly = core.readonlySelectors.get(options.key);
1735
- if (existingWritable || existingReadonly) {
1736
- store.logger.error(
1737
- `\u274C`,
1738
- existingReadonly ? `readonly_selector` : `selector`,
1739
- options.key,
1740
- `Tried to create selector, but it already exists in the store. (Ignore if you are in development using hot module replacement.)`
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}"`
1741
1620
  );
1742
1621
  }
1743
- if (`set` in options) {
1744
- return createReadWriteSelector(options, family, store, core);
1745
- }
1746
- return createReadonlySelector(options, family, store, core);
1747
- }
1748
-
1749
- // src/selector/delete-selector.ts
1750
- function deleteSelector(selectorToken, store) {
1751
- const core = target(store);
1752
- const { key } = selectorToken;
1753
- switch (selectorToken.type) {
1754
- case `selector`:
1755
- core.selectors.delete(key);
1756
- break;
1757
- case `readonly_selector`:
1758
- core.readonlySelectors.delete(key);
1759
- break;
1760
- }
1761
- core.valueMap.delete(key);
1762
- core.selectorAtoms.delete(key);
1763
- const downstreamTokens = core.selectorGraph.getRelationEntries({ upstreamSelectorKey: key }).filter(([_, { source }]) => source === key).map(
1764
- ([downstreamSelectorKey]) => {
1765
- var _a;
1766
- return (_a = core.selectors.get(downstreamSelectorKey)) != null ? _a : core.readonlySelectors.get(downstreamSelectorKey);
1767
- }
1768
- );
1769
- for (const downstreamToken of downstreamTokens) {
1770
- if (downstreamToken) {
1771
- deleteSelector(downstreamToken, store);
1772
- }
1773
- }
1774
- core.selectorGraph.delete(key);
1775
- store.logger.info(`\u{1F525}`, selectorToken.type, `${key}`, `deleted`);
1776
- }
1777
-
1778
- // src/families/create-readonly-selector-family.ts
1779
- function createReadonlySelectorFamily(options, store) {
1780
- const core = target(store);
1781
- const subject = new Subject();
1782
- return Object.assign(
1783
- (key) => {
1784
- const subKey = json.stringifyJson(key);
1785
- const family = { key: options.key, subKey };
1786
- const fullKey = `${options.key}(${subKey})`;
1787
- const existing = core.readonlySelectors.get(fullKey);
1788
- if (existing) {
1789
- return deposit(existing);
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
+ }
1790
1801
  }
1791
- return createSelector(
1792
- {
1793
- key: fullKey,
1794
- get: options.get(key)
1795
- },
1796
- family,
1797
- store
1798
- );
1799
- },
1800
- {
1801
- key: options.key,
1802
- type: `readonly_selector_family`,
1803
- subject
1804
1802
  }
1805
- );
1806
- }
1807
- function createSelectorFamily(options, store) {
1808
- const isReadonly = !(`set` in options);
1809
- if (isReadonly) {
1810
- 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;
1811
1823
  }
1812
- const core = target(store);
1813
- const subject = new Subject();
1814
- const selectorFamily = Object.assign(
1815
- (key) => {
1816
- const subKey = json.stringifyJson(key);
1817
- const family = { key: options.key, subKey };
1818
- const fullKey = `${options.key}(${subKey})`;
1819
- const existing = core.selectors.get(fullKey);
1820
- if (existing) {
1821
- return deposit(existing);
1822
- }
1823
- const token = createSelector(
1824
- {
1825
- key: fullKey,
1826
- get: options.get(key),
1827
- set: options.set(key)
1828
- },
1829
- family,
1830
- 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}"`
1831
1833
  );
1832
- subject.next(token);
1833
- return token;
1834
- },
1835
- {
1836
- key: options.key,
1837
- type: `selector_family`
1834
+ continue;
1838
1835
  }
1839
- );
1840
- core.families.set(options.key, selectorFamily);
1841
- return selectorFamily;
1842
- }
1843
-
1844
- // src/mutable/tracker-family.ts
1845
- var FamilyTracker = class {
1846
- constructor(findMutableState, store) {
1847
- this.findLatestUpdateState = createAtomFamily(
1848
- {
1849
- key: `*${findMutableState.key}`,
1850
- default: null
1851
- },
1852
- store
1853
- );
1854
- this.findMutableState = findMutableState;
1855
- this.findMutableState.subject.subscribe(
1856
- `store=${store.config.name}::tracker-atom-family`,
1857
- (atomToken) => {
1858
- if (atomToken.family) {
1859
- const key = json.parseJson(atomToken.family.subKey);
1860
- this.findLatestUpdateState(key);
1861
- 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);
1862
1844
  }
1863
1845
  }
1864
- );
1865
- this.findLatestUpdateState.subject.subscribe(
1866
- `store=${store.config.name}::tracker-atom-family`,
1867
- (atomToken) => {
1868
- if (atomToken.family) {
1869
- const key = json.parseJson(atomToken.family.subKey);
1870
- const mutableAtomToken = this.findMutableState(key);
1871
- 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;
1872
1860
  }
1873
1861
  }
1874
- );
1875
- }
1876
- };
1877
-
1878
- // src/mutable/create-mutable-atom-family.ts
1879
- function createMutableAtomFamily(options, store) {
1880
- const coreFamily = Object.assign(
1881
- createAtomFamily(options, store),
1882
- options
1883
- );
1884
- json.selectJsonFamily(coreFamily, options);
1885
- new FamilyTracker(coreFamily, store);
1886
- return coreFamily;
1887
- }
1888
-
1889
- // src/mutable/get-json-family.ts
1890
- var getJsonFamily = (mutableAtomFamily, store) => {
1891
- const core = target(store);
1892
- const key = `${mutableAtomFamily.key}:JSON`;
1893
- const jsonFamily = core.families.get(
1894
- key
1895
- );
1896
- return jsonFamily;
1897
- };
1898
-
1899
- // src/mutable/get-json-token.ts
1900
- var getJsonToken = (mutableAtomToken) => {
1901
- const key = mutableAtomToken.family ? `${mutableAtomToken.family.key}:JSON(${mutableAtomToken.family.subKey})` : `${mutableAtomToken.key}:JSON`;
1902
- const jsonToken = { type: `selector`, key };
1903
- if (mutableAtomToken.family) {
1904
- jsonToken.family = {
1905
- key: `${mutableAtomToken.family.key}:JSON`,
1906
- subKey: mutableAtomToken.family.subKey
1907
- };
1908
- }
1909
- return jsonToken;
1910
- };
1911
-
1912
- // src/mutable/get-update-token.ts
1913
- var getUpdateToken = (mutableAtomToken) => {
1914
- const key = `*${mutableAtomToken.key}`;
1915
- const updateToken = { type: `atom`, key };
1916
- if (mutableAtomToken.family) {
1917
- updateToken.family = {
1918
- key: `*${mutableAtomToken.family.key}`,
1919
- subKey: mutableAtomToken.family.subKey
1920
- };
1862
+ addAtomToTimeline(token2, tl, store);
1863
+ }
1864
+ target.timelineAtoms = target.timelineAtoms.set({
1865
+ atomKey: tokenOrFamily.key,
1866
+ timelineKey: options.key
1867
+ });
1921
1868
  }
1922
- return updateToken;
1923
- };
1924
-
1925
- // src/mutable/is-atom-token-mutable.ts
1926
- function isAtomTokenMutable(token) {
1927
- return token.key.endsWith(`::mutable`);
1928
- }
1929
-
1930
- // src/mutable/transceiver.ts
1931
- function isTransceiver(value) {
1932
- 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;
1933
1876
  }
1934
-
1935
- // src/mutable/index.ts
1936
- var isAtomMutable = (atom) => `isMutable` in atom;
1937
-
1938
- // src/atom/is-default.ts
1939
- var isAtomDefault = (key, store) => {
1940
- const core = target(store);
1941
- return core.atomsThatAreDefault.has(key);
1942
- };
1943
- var markAtomAsDefault = (key, store) => {
1944
- const core = target(store);
1945
- core.atomsThatAreDefault = new Set(core.atomsThatAreDefault).add(key);
1946
- };
1947
- var markAtomAsNotDefault = (key, store) => {
1948
- const core = target(store);
1949
- core.atomsThatAreDefault = new Set(target(store).atomsThatAreDefault);
1950
- core.atomsThatAreDefault.delete(key);
1951
- };
1952
- var isSelectorDefault = (key, store) => {
1953
- const rootKeys = traceAllSelectorAtoms(key, store);
1954
- return rootKeys.every((rootKey) => isAtomDefault(rootKey, store));
1955
- };
1956
-
1957
- // src/atom/create-atom.ts
1958
- function createAtom(options, family, store) {
1877
+ var timeTravel = (direction, token, store) => {
1878
+ const action = direction === `forward` ? `redo` : `undo`;
1959
1879
  store.logger.info(
1960
- `\u{1F528}`,
1961
- `atom`,
1962
- options.key,
1963
- `creating in store "${store.config.name}"`
1880
+ direction === `forward` ? `\u23E9` : `\u23EA`,
1881
+ `timeline`,
1882
+ token.key,
1883
+ action
1964
1884
  );
1965
- const core = target(store);
1966
- const existing = core.atoms.get(options.key);
1967
- if (existing) {
1968
- store.logger.error(
1969
- `\u274C`,
1970
- `atom`,
1971
- options.key,
1972
- `Tried to create atom, but it already exists in the store.`,
1973
- `(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.`
1974
1892
  );
1975
- return deposit(existing);
1893
+ return;
1976
1894
  }
1977
- const subject = new Subject();
1978
- const newAtom = __spreadValues(__spreadProps(__spreadValues({}, options), {
1979
- type: `atom`,
1980
- install: (store2) => {
1981
- store2.logger.info(
1982
- `\u{1F6E0}\uFE0F`,
1983
- `atom`,
1984
- options.key,
1985
- `installing in store "${store2.config.name}"`
1986
- );
1987
- return `mutable` in options ? createMutableAtom(options, store2) : createAtom(options, void 0, store2);
1988
- },
1989
- subject
1990
- }), family && { family });
1991
- let initialValue = options.default;
1992
- if (options.default instanceof Function) {
1993
- 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;
1994
1903
  }
1995
- core.atoms.set(newAtom.key, newAtom);
1996
- markAtomAsDefault(options.key, store);
1997
- cacheValue(options.key, initialValue, subject, store);
1998
- const token = deposit(newAtom);
1999
- if (options.effects) {
2000
- let effectIndex = 0;
2001
- const cleanupFunctions = [];
2002
- for (const effect of options.effects) {
2003
- const cleanup = effect({
2004
- setSelf: (next) => atom_io.setState(token, next, store),
2005
- onSet: (handle) => atom_io.subscribe(token, handle, `effect[${effectIndex}]`, store)
2006
- });
2007
- if (cleanup) {
2008
- 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);
2009
1921
  }
2010
- ++effectIndex;
2011
1922
  }
2012
- newAtom.cleanup = () => {
2013
- for (const cleanup of cleanupFunctions) {
2014
- 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);
2015
1933
  }
2016
- };
1934
+ break;
1935
+ }
1936
+ case `transaction_update`: {
1937
+ updateValuesFromTransactionUpdate(update);
1938
+ break;
1939
+ }
2017
1940
  }
2018
- store.subject.atomCreation.next(token);
2019
- return token;
2020
- }
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
+ };
2021
1953
 
2022
- // src/atom/delete-atom.ts
2023
- function deleteAtom(atomToken, store) {
2024
- var _a, _b;
2025
- const core = target(store);
2026
- const { key } = atomToken;
2027
- const atom = core.atoms.get(key);
2028
- if (!atom) {
2029
- store.logger.error(
2030
- `\u274C`,
2031
- `atom`,
2032
- `${key}`,
2033
- `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.`
2034
1963
  );
1964
+ return;
2035
1965
  }
2036
- (_a = atom == null ? void 0 : atom.cleanup) == null ? void 0 : _a.call(atom);
2037
- core.atoms.delete(key);
2038
- core.valueMap.delete(key);
2039
- const selectorKeys = core.selectorAtoms.getRelatedKeys(key);
2040
- if (selectorKeys) {
2041
- for (const selectorKey of selectorKeys) {
2042
- const token = (_b = core.selectors.get(selectorKey)) != null ? _b : core.readonlySelectors.get(selectorKey);
2043
- if (token) {
2044
- 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
+ );
2045
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
+ );
2046
1999
  }
2047
2000
  }
2048
- core.selectorAtoms.delete(key);
2049
- core.atomsThatAreDefault.delete(key);
2050
- core.timelineAtoms.delete(key);
2051
- store.logger.info(`\u{1F525}`, `atom`, `${key}`, `deleted`);
2001
+ atom_io.setState(token, newValue, parent);
2052
2002
  }
2053
-
2054
- // src/not-found-error.ts
2055
- var capitalize = (str) => str[0].toUpperCase() + str.slice(1);
2056
- function prettyPrintTokenType(token) {
2057
- if (token.type === `readonly_selector`) {
2058
- 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
+ }
2059
2010
  }
2060
- return capitalize(token.type);
2061
2011
  }
2062
- var NotFoundError = class extends Error {
2063
- constructor(token, store) {
2064
- super(
2065
- `${prettyPrintTokenType(token)} "${token.key}" not found in store "${store.config.name}".`
2066
- );
2067
- }
2068
- };
2069
-
2070
- // src/subscribe/recall-state.ts
2071
- var recallState = (state, store) => {
2072
- const core = target(store);
2073
- 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`) {
2074
2017
  store.logger.warn(
2075
2018
  `\u{1F41E}`,
2076
- state.type,
2077
- state.key,
2078
- `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.`
2079
2049
  );
2080
- return core.valueMap.get(state.key);
2050
+ } else {
2051
+ ingestTransactionUpdate(child.transactionMeta.update, parent, child);
2052
+ parent.transactionMeta.update.updates.push(child.transactionMeta.update);
2081
2053
  }
2082
- return core.operation.prev.get(state.key);
2054
+ parent.subject.transactionApplying.next(null);
2083
2055
  };
2084
2056
 
2085
- // src/subscribe/subscribe-to-root-atoms.ts
2086
- var subscribeToRootAtoms = (state, store) => {
2087
- const dependencySubscriptions = `default` in state ? null : traceAllSelectorAtoms(state.key, store).map((atomKey) => {
2088
- const atom = store.atoms.get(atomKey);
2089
- if (atom === void 0) {
2090
- throw new Error(
2091
- `Atom "${atomKey}", a dependency of selector "${state.key}", not found in store "${store.config.name}".`
2092
- );
2093
- }
2094
- return atom.subject.subscribe(
2095
- `${state.type}:${state.key}`,
2096
- (atomChange) => {
2097
- store.logger.info(
2098
- `\u{1F4E2}`,
2099
- state.type,
2100
- state.key,
2101
- `root`,
2102
- atomKey,
2103
- `went`,
2104
- atomChange.oldValue,
2105
- `->`,
2106
- 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
2107
2115
  );
2108
- const oldValue = recallState(state, store);
2109
- const newValue = readOrComputeValue(state, store);
2110
- store.logger.info(
2111
- `\u2728`,
2112
- state.type,
2113
- state.key,
2114
- `went`,
2115
- oldValue,
2116
- `->`,
2117
- 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.`
2118
2143
  );
2119
- state.subject.next({ newValue, oldValue });
2120
2144
  }
2121
- );
2122
- });
2123
- 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
+ }
2124
2168
  };
2125
2169
 
2170
+ // src/transaction/index.ts
2171
+ var TRANSACTION_PHASES = [`idle`, `building`, `applying`];
2172
+
2126
2173
  exports.FamilyTracker = FamilyTracker;
2127
2174
  exports.Future = Future;
2128
2175
  exports.IMPLICIT = IMPLICIT;
2129
2176
  exports.LazyMap = LazyMap;
2130
2177
  exports.NotFoundError = NotFoundError;
2178
+ exports.StatefulSubject = StatefulSubject;
2131
2179
  exports.Store = Store;
2132
2180
  exports.Subject = Subject;
2133
2181
  exports.TRANSACTION_PHASES = TRANSACTION_PHASES;
@@ -2152,6 +2200,7 @@ exports.createTransaction = createTransaction;
2152
2200
  exports.deleteAtom = deleteAtom;
2153
2201
  exports.deleteSelector = deleteSelector;
2154
2202
  exports.deposit = deposit;
2203
+ exports.eldest = eldest;
2155
2204
  exports.evictCachedValue = evictCachedValue;
2156
2205
  exports.getJsonFamily = getJsonFamily;
2157
2206
  exports.getJsonToken = getJsonToken;
@@ -2171,6 +2220,7 @@ exports.isValueCached = isValueCached;
2171
2220
  exports.markAtomAsDefault = markAtomAsDefault;
2172
2221
  exports.markAtomAsNotDefault = markAtomAsNotDefault;
2173
2222
  exports.markDone = markDone;
2223
+ exports.newest = newest;
2174
2224
  exports.openOperation = openOperation;
2175
2225
  exports.readCachedValue = readCachedValue;
2176
2226
  exports.readOrComputeValue = readOrComputeValue;
@@ -2178,7 +2228,6 @@ exports.redoTransactionUpdate = redoTransactionUpdate;
2178
2228
  exports.registerSelector = registerSelector;
2179
2229
  exports.setAtomOrSelector = setAtomOrSelector;
2180
2230
  exports.subscribeToRootAtoms = subscribeToRootAtoms;
2181
- exports.target = target;
2182
2231
  exports.timeTravel = timeTravel;
2183
2232
  exports.traceAllSelectorAtoms = traceAllSelectorAtoms;
2184
2233
  exports.traceSelectorAtoms = traceSelectorAtoms;