atom.io 0.39.0 β†’ 0.40.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 (59) hide show
  1. package/dist/data/index.js.map +1 -1
  2. package/dist/eslint-plugin/index.js +2 -1
  3. package/dist/eslint-plugin/index.js.map +1 -1
  4. package/dist/internal/index.d.ts +14 -8
  5. package/dist/internal/index.d.ts.map +1 -1
  6. package/dist/internal/index.js +129 -87
  7. package/dist/internal/index.js.map +1 -1
  8. package/dist/introspection/index.d.ts.map +1 -1
  9. package/dist/introspection/index.js.map +1 -1
  10. package/dist/json/index.d.ts.map +1 -1
  11. package/dist/json/index.js.map +1 -1
  12. package/dist/main/index.d.ts +8 -7
  13. package/dist/main/index.d.ts.map +1 -1
  14. package/dist/main/index.js.map +1 -1
  15. package/dist/react/index.d.ts.map +1 -1
  16. package/dist/react/index.js +43 -2
  17. package/dist/react/index.js.map +1 -1
  18. package/dist/react-devtools/index.d.ts.map +1 -1
  19. package/dist/react-devtools/index.js +12 -10
  20. package/dist/react-devtools/index.js.map +1 -1
  21. package/dist/realtime/index.d.ts.map +1 -1
  22. package/dist/realtime/index.js.map +1 -1
  23. package/dist/realtime-client/index.js.map +1 -1
  24. package/dist/realtime-react/index.js.map +1 -1
  25. package/dist/realtime-server/index.d.ts.map +1 -1
  26. package/dist/realtime-server/index.js.map +1 -1
  27. package/dist/realtime-testing/index.d.ts.map +1 -1
  28. package/dist/realtime-testing/index.js.map +1 -1
  29. package/dist/transceivers/set-rtx/index.d.ts.map +1 -1
  30. package/dist/transceivers/set-rtx/index.js.map +1 -1
  31. package/dist/web/index.js.map +1 -1
  32. package/package.json +8 -8
  33. package/src/internal/events/ingest-selector-update.ts +13 -4
  34. package/src/internal/families/create-regular-atom-family.ts +3 -2
  35. package/src/internal/get-state/read-or-compute-value.ts +2 -1
  36. package/src/internal/get-state/reduce-reference.ts +15 -2
  37. package/src/internal/is-fn.ts +9 -0
  38. package/src/internal/operation.ts +3 -1
  39. package/src/internal/set-state/become.ts +11 -6
  40. package/src/internal/set-state/dispatch-state-update.ts +15 -12
  41. package/src/internal/set-state/operate-on-store.ts +3 -1
  42. package/src/internal/set-state/reset-atom-or-selector.ts +7 -7
  43. package/src/internal/set-state/set-atom-or-selector.ts +3 -2
  44. package/src/internal/set-state/set-atom.ts +4 -3
  45. package/src/internal/set-state/set-selector.ts +8 -7
  46. package/src/internal/timeline/create-timeline.ts +133 -98
  47. package/src/internal/timeline/time-travel.ts +42 -31
  48. package/src/internal/transaction/apply-transaction.ts +2 -2
  49. package/src/internal/transaction/build-transaction.ts +1 -1
  50. package/src/main/events.ts +8 -2
  51. package/src/main/logger.ts +1 -1
  52. package/src/main/timeline.ts +1 -7
  53. package/src/react-devtools/Updates.tsx +14 -9
  54. package/src/react-devtools/json-editor/editors-by-type/array-editor.tsx +1 -1
  55. package/src/react-devtools/json-editor/editors-by-type/object-editor.tsx +2 -3
  56. package/src/react-devtools/json-editor/editors-by-type/utilities/array-elements.ts +1 -1
  57. package/src/react-devtools/json-editor/editors-by-type/utilities/object-properties.ts +1 -1
  58. package/dist/use-o-DXPncKmZ.js +0 -47
  59. package/dist/use-o-DXPncKmZ.js.map +0 -1
@@ -663,7 +663,8 @@ function openOperation(store, token) {
663
663
  done: /* @__PURE__ */ new Set(),
664
664
  prev: /* @__PURE__ */ new Map(),
665
665
  timestamp: Date.now(),
666
- token
666
+ token,
667
+ subEvents: []
667
668
  };
668
669
  store.logger.info(`β­•`, token.type, token.key, `operation start in store "${store.config.name}"${isChildStore(store) ? ` ${store.transactionMeta.phase} "${store.transactionMeta.update.token.key}"` : ``}`);
669
670
  return store;
@@ -798,6 +799,14 @@ function evictCachedValue(target, key) {
798
799
  target.logger.info(`πŸ—‘`, `state`, key, `evicted`);
799
800
  }
800
801
 
802
+ //#endregion
803
+ //#region src/internal/is-fn.ts
804
+ const NON_CTOR_FN_REGEX = /^\[object (?:Async|Generator|AsyncGenerator)?Function\]$/;
805
+ function isFn(input) {
806
+ const protoString = Object.prototype.toString.call(input);
807
+ return NON_CTOR_FN_REGEX.test(protoString);
808
+ }
809
+
801
810
  //#endregion
802
811
  //#region src/internal/get-state/read-or-compute-value.ts
803
812
  function readOrComputeValue(target, state, mut) {
@@ -813,7 +822,7 @@ function readOrComputeValue(target, state, mut) {
813
822
  return state.getFrom(target);
814
823
  case `atom`: {
815
824
  let def;
816
- if (state.default instanceof Function) {
825
+ if (isFn(state.default)) {
817
826
  def = state.default();
818
827
  target.logger.info(`✨`, state.type, key, `computed default`, def);
819
828
  } else {
@@ -893,18 +902,24 @@ function actUponStore(store, token, id) {
893
902
 
894
903
  //#endregion
895
904
  //#region src/internal/set-state/become.ts
896
- const become = (nextVersionOfThing) => (originalThing) => nextVersionOfThing instanceof Function ? nextVersionOfThing(originalThing) : nextVersionOfThing;
905
+ function become(nextVersionOfThing, originalThing) {
906
+ if (isFn(nextVersionOfThing)) return nextVersionOfThing(originalThing);
907
+ return nextVersionOfThing;
908
+ }
897
909
 
898
910
  //#endregion
899
911
  //#region src/internal/set-state/set-atom.ts
900
912
  const setAtom = (target, atom, next) => {
901
913
  const oldValue = readOrComputeValue(target, atom, `mut`);
902
- let newValue = become(next)(oldValue);
914
+ let newValue = become(next, oldValue);
903
915
  target.logger.info(`⭐`, `atom`, atom.key, `setting value`, newValue);
904
916
  newValue = writeToCache(target, atom, newValue);
905
917
  markDone(target, atom.key);
906
918
  evictDownstreamFromAtom(target, atom);
907
- return [oldValue, newValue];
919
+ return {
920
+ oldValue,
921
+ newValue
922
+ };
908
923
  };
909
924
 
910
925
  //#endregion
@@ -914,7 +929,7 @@ function resetAtom(target, atom) {
914
929
  case `mutable_atom`: return setAtom(target, atom, new atom.class());
915
930
  case `atom`: {
916
931
  let def = atom.default;
917
- if (def instanceof Function) def = def();
932
+ if (isFn(def)) def = def();
918
933
  return setAtom(target, atom, def);
919
934
  }
920
935
  }
@@ -936,7 +951,10 @@ function resetAtomOrSelector(target, state) {
936
951
  dispatchOrDeferStateUpdate(target, state, rootProtoUpdate, false);
937
952
  }
938
953
  const newValue = state.getFrom(target);
939
- protoUpdate = [oldValue, newValue];
954
+ protoUpdate = {
955
+ oldValue,
956
+ newValue
957
+ };
940
958
  }
941
959
  break;
942
960
  }
@@ -975,20 +993,23 @@ function setSelector(target, selector, next) {
975
993
  const { type, key } = selector;
976
994
  switch (selector.type) {
977
995
  case `writable_pure_selector`:
978
- oldValue = selector.getFrom(target);
979
- newValue = become(next)(oldValue);
980
- writeToCache(target, selector, newValue);
996
+ oldValue = readOrComputeValue(target, selector, `mut`);
997
+ newValue = become(next, oldValue);
998
+ newValue = writeToCache(target, selector, newValue);
981
999
  break;
982
1000
  case `writable_held_selector`:
983
1001
  constant = selector.const;
984
- become(next)(constant);
1002
+ become(next, constant);
985
1003
  oldValue = constant;
986
1004
  newValue = constant;
987
1005
  }
988
1006
  target.logger.info(`⭐`, type, key, `setting to`, newValue);
989
1007
  markDone(target, key);
990
1008
  selector.setSelf(newValue);
991
- return [oldValue, newValue];
1009
+ return {
1010
+ oldValue,
1011
+ newValue
1012
+ };
992
1013
  }
993
1014
 
994
1015
  //#endregion
@@ -1247,9 +1268,10 @@ function ingestMoleculeTransferEvent(store, event, applying) {
1247
1268
  //#region src/internal/events/ingest-selector-update.ts
1248
1269
  function ingestSelectorUpdateEvent(store, selectorUpdate, applying) {
1249
1270
  let updates;
1250
- if (applying === `newValue`) updates = selectorUpdate.atomUpdates;
1251
- else updates = selectorUpdate.atomUpdates.toReversed();
1252
- for (const atomUpdate of updates) ingestAtomUpdateEvent(store, atomUpdate, applying);
1271
+ if (applying === `newValue`) updates = selectorUpdate.subEvents;
1272
+ else updates = selectorUpdate.subEvents.toReversed();
1273
+ for (const atomUpdate of updates) if (atomUpdate.type === `state_creation`) ingestCreationEvent(store, atomUpdate, applying);
1274
+ else ingestAtomUpdateEvent(store, atomUpdate, applying);
1253
1275
  }
1254
1276
 
1255
1277
  //#endregion
@@ -1326,7 +1348,7 @@ function applyTransaction(store, output) {
1326
1348
  parent.child = null;
1327
1349
  parent.on.transactionApplying.next(child.transactionMeta);
1328
1350
  const { subEvents: updates } = child.transactionMeta.update;
1329
- store.logger.info(`πŸ›„`, `transaction`, child.transactionMeta.update.token.key, `Applying transaction with ${updates.length} updates:`, updates);
1351
+ store.logger.info(`πŸ›„`, `transaction`, child.transactionMeta.update.token.key, `applying ${updates.length} subEvents:`, updates);
1330
1352
  ingestTransactionOutcomeEvent(parent, child.transactionMeta.update, `newValue`);
1331
1353
  if (isRootStore(parent)) {
1332
1354
  setEpochNumberOfAction(parent, child.transactionMeta.update.token.key, child.transactionMeta.update.epoch);
@@ -1335,7 +1357,7 @@ function applyTransaction(store, output) {
1335
1357
  type: `transaction`
1336
1358
  });
1337
1359
  myTransaction?.subject.next(child.transactionMeta.update);
1338
- store.logger.info(`πŸ›¬`, `transaction`, child.transactionMeta.update.token.key, `Finished applying transaction.`);
1360
+ store.logger.info(`πŸ›¬`, `transaction`, child.transactionMeta.update.token.key, `applied`);
1339
1361
  } else if (isChildStore(parent)) parent.transactionMeta.update.subEvents.push(child.transactionMeta.update);
1340
1362
  parent.on.transactionApplying.next(null);
1341
1363
  }
@@ -1421,7 +1443,7 @@ const buildTransaction = (store, token, params, id) => {
1421
1443
  };
1422
1444
  const child = Object.assign(childBase, { transactionMeta });
1423
1445
  parent.child = child;
1424
- store.logger.info(`πŸ›«`, `transaction`, token.key, `Building transaction with params:`, params);
1446
+ store.logger.info(`πŸ›«`, `transaction`, token.key, `building with params:`, params);
1425
1447
  return child;
1426
1448
  };
1427
1449
 
@@ -1468,17 +1490,19 @@ const TRANSACTION_PHASES = [
1468
1490
 
1469
1491
  //#endregion
1470
1492
  //#region src/internal/set-state/dispatch-state-update.ts
1471
- function dispatchOrDeferStateUpdate(target, state, [oldValue, newValue], stateIsNewlyCreated, family) {
1493
+ function dispatchOrDeferStateUpdate(target, state, { oldValue, newValue }, stateIsNewlyCreated, family) {
1472
1494
  const token = deposit(state);
1473
1495
  if (stateIsNewlyCreated && family) {
1474
1496
  state.subject.next({ newValue });
1475
1497
  const stateCreationEvent = {
1498
+ write: true,
1476
1499
  type: `state_creation`,
1477
1500
  subType: `writable`,
1478
1501
  token,
1479
1502
  timestamp: Date.now(),
1480
1503
  value: newValue
1481
1504
  };
1505
+ target.operation.subEvents.push(stateCreationEvent);
1482
1506
  const familySubject = family.subject;
1483
1507
  familySubject.next(stateCreationEvent);
1484
1508
  const innerTarget = newest(target);
@@ -1505,11 +1529,11 @@ function dispatchOrDeferStateUpdate(target, state, [oldValue, newValue], stateIs
1505
1529
  if (isRootStore(target)) {
1506
1530
  switch (type) {
1507
1531
  case `mutable_atom`:
1508
- target.logger.info(`πŸ“’`, type, key, `is now (`, newValue, `) subscribers:`, subject.subscribers);
1532
+ target.logger.info(`πŸ“’`, type, key, `is now (`, newValue, `) subscribers:`, subject.subscribers.keys());
1509
1533
  break;
1510
1534
  case `atom`:
1511
1535
  case `writable_pure_selector`:
1512
- case `writable_held_selector`: target.logger.info(`πŸ“’`, type, key, `went (`, oldValue, `->`, newValue, `) subscribers:`, subject.subscribers);
1536
+ case `writable_held_selector`: target.logger.info(`πŸ“’`, type, key, `went (`, oldValue, `->`, newValue, `) subscribers:`, subject.subscribers.keys());
1513
1537
  }
1514
1538
  subject.next(update);
1515
1539
  }
@@ -2037,7 +2061,7 @@ function createRegularAtomFamily(store, options, internalRoles) {
2037
2061
  const def = options.default;
2038
2062
  const individualOptions = {
2039
2063
  key: fullKey,
2040
- default: def instanceof Function ? () => def(key) : def
2064
+ default: isFn(def) ? () => def(key) : def
2041
2065
  };
2042
2066
  if (options.effects) individualOptions.effects = options.effects(key);
2043
2067
  const token = createRegularAtom(target, individualOptions, family);
@@ -2050,7 +2074,7 @@ function createRegularAtomFamily(store, options, internalRoles) {
2050
2074
  internalRoles
2051
2075
  });
2052
2076
  store.families.set(options.key, atomFamily$1);
2053
- if (options.default instanceof Function === false) store.defaults.set(options.key, options.default);
2077
+ if (isFn(options.default) === false) store.defaults.set(options.key, options.default);
2054
2078
  return familyToken;
2055
2079
  }
2056
2080
 
@@ -2288,9 +2312,22 @@ function reduceReference(store, ...params) {
2288
2312
  const isCounterfeit = `counterfeit` in token;
2289
2313
  const isNewlyCreated = Boolean(brandNewToken) && isCounterfeit === false;
2290
2314
  if (isNewlyCreated && family) {
2315
+ let subType;
2316
+ switch (token.type) {
2317
+ case `readonly_pure_selector`:
2318
+ case `readonly_held_selector`:
2319
+ subType = `readable`;
2320
+ break;
2321
+ case `atom`:
2322
+ case `mutable_atom`:
2323
+ case `writable_pure_selector`:
2324
+ case `writable_held_selector`:
2325
+ subType = `writable`;
2326
+ break;
2327
+ }
2291
2328
  const stateCreationEvent = {
2292
2329
  type: `state_creation`,
2293
- subType: `readable`,
2330
+ subType,
2294
2331
  token,
2295
2332
  timestamp: Date.now()
2296
2333
  };
@@ -3501,7 +3538,6 @@ function createTimeline(store, options, data) {
3501
3538
  subject: new Subject(),
3502
3539
  subscriptions: /* @__PURE__ */ new Map()
3503
3540
  };
3504
- if (options.shouldCapture) tl.shouldCapture = options.shouldCapture;
3505
3541
  const timelineKey = options.key;
3506
3542
  const target = newest(store);
3507
3543
  for (const initialTopic of options.scope) switch (initialTopic.type) {
@@ -3567,60 +3603,19 @@ function addAtomToTimeline(store, atomToken, tl) {
3567
3603
  const txUpdateInProgress = target.on.transactionApplying.state?.update;
3568
3604
  store.logger.info(`⏳`, `timeline`, tl.key, `atom`, atomToken.key, `went`, update.oldValue, `->`, update.newValue, txUpdateInProgress ? `in transaction "${txUpdateInProgress.token.key}"` : currentSelectorToken ? `in selector "${currentSelectorToken.key}"` : ``);
3569
3605
  if (tl.timeTraveling === null) if (txUpdateInProgress) joinTransaction(store, tl, txUpdateInProgress);
3570
- else if (currentSelectorToken && currentSelectorTime) {
3571
- let latestUpdate = tl.history.at(-1);
3572
- if (currentSelectorTime !== tl.selectorTime) {
3573
- latestUpdate = {
3574
- type: `selector_update`,
3575
- timestamp: currentSelectorTime,
3576
- token: currentSelectorToken,
3577
- atomUpdates: []
3578
- };
3579
- latestUpdate.atomUpdates.push({
3580
- type: `atom_update`,
3581
- token: atomToken,
3582
- update,
3583
- timestamp: Date.now()
3584
- });
3585
- if (tl.at !== tl.history.length) tl.history.splice(tl.at);
3586
- tl.history.push(latestUpdate);
3587
- store.logger.info(`βŒ›`, `timeline`, tl.key, `got a selector_update "${currentSelectorToken.key}" with`, latestUpdate.atomUpdates.map((atomUpdate) => atomUpdate.token.key));
3588
- tl.at = tl.history.length;
3589
- tl.selectorTime = currentSelectorTime;
3590
- } else if (latestUpdate?.type === `selector_update`) {
3591
- latestUpdate.atomUpdates.push({
3592
- type: `atom_update`,
3593
- token: atomToken,
3594
- update,
3595
- timestamp: Date.now()
3596
- });
3597
- store.logger.info(`βŒ›`, `timeline`, tl.key, `set selector_update "${currentSelectorToken.key}" to`, latestUpdate?.atomUpdates.map((atomUpdate) => atomUpdate.token.key));
3598
- }
3599
- if (latestUpdate) {
3600
- const willCaptureSelectorUpdate = tl.shouldCapture?.(latestUpdate, tl) ?? true;
3601
- if (willCaptureSelectorUpdate) tl.subject.next(latestUpdate);
3602
- else {
3603
- tl.history.pop();
3604
- tl.at = tl.history.length;
3605
- }
3606
- }
3607
- } else {
3606
+ else if (currentSelectorToken && currentSelectorTime) buildSelectorUpdate(store, tl, atomToken, update, currentSelectorToken, currentSelectorTime);
3607
+ else {
3608
3608
  const timestamp = Date.now();
3609
3609
  tl.selectorTime = null;
3610
- if (tl.at !== tl.history.length) tl.history.splice(tl.at);
3611
3610
  const atomUpdate = {
3611
+ write: true,
3612
3612
  type: `atom_update`,
3613
3613
  token: deposit(atom),
3614
3614
  update,
3615
3615
  timestamp
3616
3616
  };
3617
- const willCapture = tl.shouldCapture?.(atomUpdate, tl) ?? true;
3618
3617
  store.logger.info(`βŒ›`, `timeline`, tl.key, `got an atom_update to "${atom.key}"`);
3619
- if (willCapture) {
3620
- tl.history.push(atomUpdate);
3621
- tl.at = tl.history.length;
3622
- tl.subject.next(atomUpdate);
3623
- }
3618
+ addToHistory(tl, atomUpdate);
3624
3619
  }
3625
3620
  }));
3626
3621
  }
@@ -3649,23 +3644,55 @@ function joinTransaction(store, tl, txUpdateInProgress) {
3649
3644
  unsubscribe();
3650
3645
  tl.transactionKey = null;
3651
3646
  if (tl.timeTraveling === null && currentTxInstanceId) {
3652
- if (tl.at !== tl.history.length) tl.history.splice(tl.at);
3653
3647
  const timelineTopics = store.timelineTopics.getRelatedKeys(tl.key);
3654
3648
  const subEventsFiltered = filterTransactionSubEvents(transactionUpdate.subEvents, timelineTopics);
3655
3649
  const timelineTransactionUpdate = {
3650
+ write: true,
3656
3651
  ...transactionUpdate,
3657
3652
  subEvents: subEventsFiltered
3658
3653
  };
3659
- const willCapture = tl.shouldCapture?.(timelineTransactionUpdate, tl) ?? true;
3660
- if (willCapture) {
3661
- tl.history.push(timelineTransactionUpdate);
3662
- tl.at = tl.history.length;
3663
- tl.subject.next(timelineTransactionUpdate);
3664
- }
3654
+ addToHistory(tl, timelineTransactionUpdate);
3665
3655
  }
3666
3656
  });
3667
3657
  }
3668
3658
  }
3659
+ function buildSelectorUpdate(store, tl, atomToken, eventOrUpdate, currentSelectorToken, currentSelectorTime) {
3660
+ let latestUpdate = tl.history.at(-1);
3661
+ if (currentSelectorTime !== tl.selectorTime) {
3662
+ const selectorUpdate = latestUpdate = {
3663
+ write: true,
3664
+ type: `selector_update`,
3665
+ timestamp: currentSelectorTime,
3666
+ token: currentSelectorToken,
3667
+ subEvents: []
3668
+ };
3669
+ if (`type` in eventOrUpdate) latestUpdate.subEvents.push(eventOrUpdate);
3670
+ else latestUpdate.subEvents.push({
3671
+ type: `atom_update`,
3672
+ token: atomToken,
3673
+ update: eventOrUpdate,
3674
+ timestamp: Date.now()
3675
+ });
3676
+ addToHistory(tl, latestUpdate);
3677
+ tl.selectorTime = currentSelectorTime;
3678
+ store.logger.info(`βŒ›`, `timeline`, tl.key, `got a selector_update "${currentSelectorToken.key}" with`, latestUpdate.subEvents.map((event) => event.token.key));
3679
+ const operation = store.operation;
3680
+ const unsub = store.on.operationClose.subscribe(`timeline:${tl.key} (needs to gather nested selector creations)`, () => {
3681
+ unsub();
3682
+ if (operation.open) selectorUpdate.subEvents = [...operation.subEvents, ...selectorUpdate.subEvents];
3683
+ });
3684
+ } else if (latestUpdate?.type === `selector_update`) {
3685
+ if (`type` in eventOrUpdate) latestUpdate.subEvents.push(eventOrUpdate);
3686
+ else latestUpdate.subEvents.push({
3687
+ type: `atom_update`,
3688
+ token: atomToken,
3689
+ update: eventOrUpdate,
3690
+ timestamp: Date.now()
3691
+ });
3692
+ store.logger.info(`βŒ›`, `timeline`, tl.key, `set selector_update "${currentSelectorToken.key}" to`, latestUpdate?.subEvents.map((event) => event.token.key));
3693
+ }
3694
+ if (latestUpdate) tl.subject.next(latestUpdate);
3695
+ }
3669
3696
  function filterTransactionSubEvents(updates, timelineTopics) {
3670
3697
  return updates.filter((updateFromTx) => {
3671
3698
  if (updateFromTx.type === `transaction_outcome`) return true;
@@ -3694,16 +3721,15 @@ function filterTransactionSubEvents(updates, timelineTopics) {
3694
3721
  });
3695
3722
  }
3696
3723
  function handleStateLifecycleEvent(store, event, tl) {
3724
+ const currentSelectorToken = store.operation.open && store.operation.token.type === `writable_pure_selector` ? store.operation.token : null;
3725
+ const currentSelectorTime = store.operation.open && store.operation.token.type === `writable_pure_selector` ? store.operation.timestamp : null;
3697
3726
  if (!tl.timeTraveling) {
3698
3727
  const target = newest(store);
3699
3728
  if (isChildStore(target)) {} else {
3700
3729
  const txUpdateInProgress = target.on.transactionApplying.state;
3701
3730
  if (txUpdateInProgress) joinTransaction(store, tl, txUpdateInProgress.update);
3702
- else {
3703
- tl.history.push(event);
3704
- tl.at = tl.history.length;
3705
- tl.subject.next(event);
3706
- }
3731
+ else if (currentSelectorToken && currentSelectorTime && event.type === `state_creation`) buildSelectorUpdate(store, tl, event.token, event, currentSelectorToken, currentSelectorTime);
3732
+ else addToHistory(tl, event);
3707
3733
  }
3708
3734
  }
3709
3735
  switch (event.type) {
@@ -3716,6 +3742,12 @@ function handleStateLifecycleEvent(store, event, tl) {
3716
3742
  break;
3717
3743
  }
3718
3744
  }
3745
+ function addToHistory(tl, event) {
3746
+ if (tl.at !== tl.history.length) tl.history.splice(tl.at);
3747
+ tl.history.push(event);
3748
+ tl.at = tl.history.length;
3749
+ tl.subject.next(event);
3750
+ }
3719
3751
 
3720
3752
  //#endregion
3721
3753
  //#region src/internal/timeline/time-travel.ts
@@ -3731,10 +3763,21 @@ const timeTravel = (store, action, token) => {
3731
3763
  return;
3732
3764
  }
3733
3765
  timelineData.timeTraveling = action === `redo` ? `into_future` : `into_past`;
3734
- if (action === `undo`) --timelineData.at;
3735
- const event = timelineData.history[timelineData.at];
3766
+ let nextIndex = timelineData.at;
3767
+ let events;
3768
+ switch (action) {
3769
+ case `undo`:
3770
+ --nextIndex;
3771
+ while (nextIndex !== 0 && timelineData.history[nextIndex].write !== true) --nextIndex;
3772
+ events = timelineData.history.slice(nextIndex, timelineData.at).reverse();
3773
+ break;
3774
+ case `redo`:
3775
+ ++nextIndex;
3776
+ events = timelineData.history.slice(timelineData.at, nextIndex);
3777
+ }
3778
+ timelineData.at = nextIndex;
3736
3779
  const applying = action === `redo` ? `newValue` : `oldValue`;
3737
- switch (event.type) {
3780
+ for (const event of events) switch (event.type) {
3738
3781
  case `atom_update`:
3739
3782
  ingestAtomUpdateEvent(store, event, applying);
3740
3783
  break;
@@ -3753,10 +3796,9 @@ const timeTravel = (store, action, token) => {
3753
3796
  case `molecule_creation`:
3754
3797
  case `molecule_disposal`:
3755
3798
  }
3756
- if (action === `redo`) ++timelineData.at;
3757
3799
  timelineData.subject.next(action);
3758
3800
  timelineData.timeTraveling = null;
3759
- store.logger.info(`⏹️`, `timeline`, token.key, `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`);
3801
+ store.logger.info(`⏸️`, `timeline`, token.key, `"${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`);
3760
3802
  };
3761
3803
 
3762
3804
  //#endregion