jotai-state-tree 1.3.3 → 1.3.5

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.
@@ -4,6 +4,13 @@ var lifecycleHookHandlers = {};
4
4
  function setLifecycleHookHandlers(handlers) {
5
5
  lifecycleHookHandlers = handlers;
6
6
  }
7
+ var isApplyingSnapshotOrPatch = false;
8
+ function getIsApplyingSnapshotOrPatch() {
9
+ return isApplyingSnapshotOrPatch;
10
+ }
11
+ function setIsApplyingSnapshotOrPatch(value) {
12
+ isApplyingSnapshotOrPatch = value;
13
+ }
7
14
  var globalStore = createStore();
8
15
  function getGlobalStore() {
9
16
  return globalStore;
@@ -69,6 +76,7 @@ var StateTreeNode = class {
69
76
  this.$parent = null;
70
77
  this.$path = "";
71
78
  this.$isAlive = true;
79
+ this.$isApplyingHistory = false;
72
80
  /** Child nodes - uses Map but children are explicitly destroyed */
73
81
  this.children = /* @__PURE__ */ new Map();
74
82
  /** Snapshot listeners */
@@ -221,9 +229,9 @@ var StateTreeNode = class {
221
229
  }
222
230
  /** Notify snapshot listeners */
223
231
  notifySnapshotChange() {
224
- this.invalidateSnapshot();
225
232
  let current = this;
226
233
  while (current) {
234
+ current.invalidateSnapshot();
227
235
  const snapshot = getSnapshotFromNode(current);
228
236
  current.snapshotListeners.forEach((listener) => listener(snapshot));
229
237
  current = current.$parent;
@@ -359,9 +367,19 @@ function applySnapshotToNode(node, snapshot) {
359
367
  }
360
368
  }
361
369
  } else if (type._kind === "array" && Array.isArray(snapshot)) {
362
- node.setValue(snapshot);
370
+ const mstArray = node.getInstance();
371
+ if (mstArray) {
372
+ mstArray.replace(snapshot);
373
+ } else {
374
+ node.setValue(snapshot);
375
+ }
363
376
  } else if (type._kind === "map" && typeof snapshot === "object" && snapshot !== null) {
364
- node.setValue(snapshot);
377
+ const mstMap = node.getInstance();
378
+ if (mstMap) {
379
+ mstMap.replace(snapshot);
380
+ } else {
381
+ node.setValue(snapshot);
382
+ }
365
383
  } else {
366
384
  node.setValue(snapshot);
367
385
  }
@@ -522,7 +540,13 @@ function getSnapshot(target) {
522
540
  }
523
541
  function applySnapshot(target, snapshot) {
524
542
  const node = getStateTreeNode(target);
525
- applySnapshotToNode(node, snapshot);
543
+ const wasApplying = getIsApplyingSnapshotOrPatch();
544
+ setIsApplyingSnapshotOrPatch(true);
545
+ try {
546
+ applySnapshotToNode(node, snapshot);
547
+ } finally {
548
+ setIsApplyingSnapshotOrPatch(wasApplying);
549
+ }
526
550
  }
527
551
  function onSnapshot(target, listener) {
528
552
  const node = getStateTreeNode(target);
@@ -535,8 +559,14 @@ function onPatch(target, listener) {
535
559
  function applyPatch(target, patch) {
536
560
  const patches = Array.isArray(patch) ? patch : [patch];
537
561
  const rootNode = getStateTreeNode(target).getRoot();
538
- for (const p of patches) {
539
- applyPatchToNode(rootNode, p);
562
+ const wasApplying = getIsApplyingSnapshotOrPatch();
563
+ setIsApplyingSnapshotOrPatch(true);
564
+ try {
565
+ for (const p of patches) {
566
+ applyPatchToNode(rootNode, p);
567
+ }
568
+ } finally {
569
+ setIsApplyingSnapshotOrPatch(wasApplying);
540
570
  }
541
571
  }
542
572
  function applyPatchToNode(rootNode, patch) {
@@ -552,37 +582,61 @@ function applyPatchToNode(rootNode, patch) {
552
582
  const key = pathParts[pathParts.length - 1];
553
583
  switch (patch.op) {
554
584
  case "replace": {
555
- const childNode = node.getChild(key);
556
- if (childNode) {
557
- applySnapshotToNode(childNode, patch.value);
585
+ const instance = node.getInstance();
586
+ if (instance && Array.isArray(instance)) {
587
+ const index = parseInt(key, 10);
588
+ instance[index] = patch.value;
589
+ } else if (instance && instance instanceof Map) {
590
+ instance.set(key, patch.value);
558
591
  } else {
559
- const currentValue = node.getValue();
560
- currentValue[key] = patch.value;
561
- node.setValue(currentValue);
592
+ const childNode = node.getChild(key);
593
+ if (childNode) {
594
+ applySnapshotToNode(childNode, patch.value);
595
+ } else {
596
+ const currentValue = node.getValue();
597
+ currentValue[key] = patch.value;
598
+ node.setValue(currentValue);
599
+ }
562
600
  }
563
601
  break;
564
602
  }
565
603
  case "add": {
566
- const currentValue = node.getValue();
567
- if (Array.isArray(currentValue)) {
568
- const index = key === "-" ? currentValue.length : parseInt(key, 10);
569
- currentValue.splice(index, 0, patch.value);
570
- node.setValue([...currentValue]);
571
- } else if (typeof currentValue === "object" && currentValue !== null) {
572
- currentValue[key] = patch.value;
573
- node.setValue({ ...currentValue });
604
+ const instance = node.getInstance();
605
+ if (instance && Array.isArray(instance)) {
606
+ const index = key === "-" ? instance.length : parseInt(key, 10);
607
+ instance.splice(index, 0, patch.value);
608
+ } else if (instance && instance instanceof Map) {
609
+ instance.set(key, patch.value);
610
+ } else {
611
+ const currentValue = node.getValue();
612
+ if (Array.isArray(currentValue)) {
613
+ const index = key === "-" ? currentValue.length : parseInt(key, 10);
614
+ currentValue.splice(index, 0, patch.value);
615
+ node.setValue([...currentValue]);
616
+ } else if (typeof currentValue === "object" && currentValue !== null) {
617
+ currentValue[key] = patch.value;
618
+ node.setValue({ ...currentValue });
619
+ }
574
620
  }
575
621
  break;
576
622
  }
577
623
  case "remove": {
578
- const currentValue = node.getValue();
579
- if (Array.isArray(currentValue)) {
624
+ const instance = node.getInstance();
625
+ if (instance && Array.isArray(instance)) {
580
626
  const index = parseInt(key, 10);
581
- currentValue.splice(index, 1);
582
- node.setValue([...currentValue]);
583
- } else if (typeof currentValue === "object" && currentValue !== null) {
584
- delete currentValue[key];
585
- node.setValue({ ...currentValue });
627
+ instance.splice(index, 1);
628
+ } else if (instance && instance instanceof Map) {
629
+ instance.delete(key);
630
+ } else {
631
+ const currentValue = node.getValue();
632
+ if (Array.isArray(currentValue)) {
633
+ const index = parseInt(key, 10);
634
+ currentValue.splice(index, 1);
635
+ node.setValue([...currentValue]);
636
+ } else if (typeof currentValue === "object" && currentValue !== null) {
637
+ delete currentValue[key];
638
+ node.setValue({ ...currentValue });
639
+ }
586
640
  }
587
641
  break;
588
642
  }
@@ -636,7 +690,7 @@ function isActionRunning() {
636
690
  }
637
691
  function trackAction(node, name, args, fn) {
638
692
  const previousAction = currentAction;
639
- currentAction = { name, args, tree: node };
693
+ currentAction = { name, args, tree: node, parent: previousAction };
640
694
  try {
641
695
  const result = fn();
642
696
  const call = {
@@ -847,6 +901,7 @@ function unfreeze(target) {
847
901
 
848
902
  export {
849
903
  setLifecycleHookHandlers,
904
+ getIsApplyingSnapshotOrPatch,
850
905
  getGlobalStore,
851
906
  setGlobalStore,
852
907
  resetGlobalStore,
@@ -858,6 +913,7 @@ export {
858
913
  $treenode,
859
914
  getStateTreeNode,
860
915
  hasStateTreeNode,
916
+ getSnapshotFromNode,
861
917
  applySnapshotToNode,
862
918
  resolveIdentifier,
863
919
  getRegistryStats,
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance, x as IReversibleJsonPatch } from './tree-BV2K9utF.mjs';
2
- export { L as CustomTypeOptions, D as IAnyComplexType, E as IAnyMixin, G as IJsonPatch, z as IMSTArray, A as IMSTMap, y as IStateTreeNode, H as IValidationContext, K as IValidationError, J as IValidationResult, B as ModelInstance, F as ModelSelf, C as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './tree-BV2K9utF.mjs';
1
+ import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance, x as IReversibleJsonPatch } from './tree-BMaFqD3f.mjs';
2
+ export { L as CustomTypeOptions, D as IAnyComplexType, E as IAnyMixin, G as IJsonPatch, z as IMSTArray, A as IMSTMap, y as IStateTreeNode, H as IValidationContext, K as IValidationError, J as IValidationResult, B as ModelInstance, F as ModelSelf, C as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './tree-BMaFqD3f.mjs';
3
3
  import 'jotai/vanilla/internals';
4
4
  import 'jotai';
5
5
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance, x as IReversibleJsonPatch } from './tree-BV2K9utF.js';
2
- export { L as CustomTypeOptions, D as IAnyComplexType, E as IAnyMixin, G as IJsonPatch, z as IMSTArray, A as IMSTMap, y as IStateTreeNode, H as IValidationContext, K as IValidationError, J as IValidationResult, B as ModelInstance, F as ModelSelf, C as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './tree-BV2K9utF.js';
1
+ import { I as ISimpleType, a as IType, b as IIdentifierType, c as IIdentifierNumberType, d as ILiteralType, e as IEnumerationType, f as IFrozenType, M as ModelProperties, g as IModelType, h as MixinConfig, i as IMixin, j as IAnyType, k as IArrayType, l as IMapType, m as IOptionalType, n as IMaybeType, o as IMaybeNullType, p as IUnionType, U as UnionOptions, q as ILateType, r as IAnyModelType, R as ReferenceOptions, s as IReferenceType, t as ISafeReferenceType, u as IRefinementType, v as IDisposer, S as SnapshotIn, w as Instance, x as IReversibleJsonPatch } from './tree-BMaFqD3f.js';
2
+ export { L as CustomTypeOptions, D as IAnyComplexType, E as IAnyMixin, G as IJsonPatch, z as IMSTArray, A as IMSTMap, y as IStateTreeNode, H as IValidationContext, K as IValidationError, J as IValidationResult, B as ModelInstance, F as ModelSelf, C as SnapshotOut, T as applyPatch, O as applySnapshot, aw as cleanupStaleEntries, ax as clearAllRegistries, aa as clone, aq as cloneDeep, a8 as destroy, a9 as detach, am as findAll, an as findFirst, as as freeze, a2 as getEnv, ag as getGlobalStore, a4 as getIdentifier, af as getMembers, ar as getOrCreatePath, Y as getParent, $ as getParentOfType, a0 as getPath, a1 as getPathParts, av as getRegistryStats, aj as getRelativePath, X as getRoot, N as getSnapshot, ap as getTreeStats, a3 as getType, _ as hasParent, al as haveSameRoot, a5 as isAlive, ak as isAncestor, at as isFrozen, a6 as isRoot, a7 as isStateTreeNode, ao as isValidReference, W as onAction, ay as onLifecycleChange, Q as onPatch, P as onSnapshot, V as recordPatches, ai as resetGlobalStore, ae as resolveIdentifier, ac as resolvePath, ah as setGlobalStore, Z as tryGetParent, ad as tryResolve, au as unfreeze, ab as walk } from './tree-BMaFqD3f.js';
3
3
  import 'jotai/vanilla/internals';
4
4
  import 'jotai';
5
5
 
package/dist/index.js CHANGED
@@ -441,6 +441,13 @@ var lifecycleHookHandlers = {};
441
441
  function setLifecycleHookHandlers(handlers) {
442
442
  lifecycleHookHandlers = handlers;
443
443
  }
444
+ var isApplyingSnapshotOrPatch = false;
445
+ function getIsApplyingSnapshotOrPatch() {
446
+ return isApplyingSnapshotOrPatch;
447
+ }
448
+ function setIsApplyingSnapshotOrPatch(value) {
449
+ isApplyingSnapshotOrPatch = value;
450
+ }
444
451
  var globalStore = (0, import_jotai.createStore)();
445
452
  function getGlobalStore() {
446
453
  return globalStore;
@@ -500,6 +507,7 @@ var StateTreeNode = class {
500
507
  this.$parent = null;
501
508
  this.$path = "";
502
509
  this.$isAlive = true;
510
+ this.$isApplyingHistory = false;
503
511
  /** Child nodes - uses Map but children are explicitly destroyed */
504
512
  this.children = /* @__PURE__ */ new Map();
505
513
  /** Snapshot listeners */
@@ -652,9 +660,9 @@ var StateTreeNode = class {
652
660
  }
653
661
  /** Notify snapshot listeners */
654
662
  notifySnapshotChange() {
655
- this.invalidateSnapshot();
656
663
  let current = this;
657
664
  while (current) {
665
+ current.invalidateSnapshot();
658
666
  const snapshot = getSnapshotFromNode(current);
659
667
  current.snapshotListeners.forEach((listener) => listener(snapshot));
660
668
  current = current.$parent;
@@ -790,9 +798,19 @@ function applySnapshotToNode(node, snapshot) {
790
798
  }
791
799
  }
792
800
  } else if (type._kind === "array" && Array.isArray(snapshot)) {
793
- node.setValue(snapshot);
801
+ const mstArray = node.getInstance();
802
+ if (mstArray) {
803
+ mstArray.replace(snapshot);
804
+ } else {
805
+ node.setValue(snapshot);
806
+ }
794
807
  } else if (type._kind === "map" && typeof snapshot === "object" && snapshot !== null) {
795
- node.setValue(snapshot);
808
+ const mstMap = node.getInstance();
809
+ if (mstMap) {
810
+ mstMap.replace(snapshot);
811
+ } else {
812
+ node.setValue(snapshot);
813
+ }
796
814
  } else {
797
815
  node.setValue(snapshot);
798
816
  }
@@ -953,7 +971,13 @@ function getSnapshot(target) {
953
971
  }
954
972
  function applySnapshot(target, snapshot) {
955
973
  const node = getStateTreeNode(target);
956
- applySnapshotToNode(node, snapshot);
974
+ const wasApplying = getIsApplyingSnapshotOrPatch();
975
+ setIsApplyingSnapshotOrPatch(true);
976
+ try {
977
+ applySnapshotToNode(node, snapshot);
978
+ } finally {
979
+ setIsApplyingSnapshotOrPatch(wasApplying);
980
+ }
957
981
  }
958
982
  function onSnapshot(target, listener) {
959
983
  const node = getStateTreeNode(target);
@@ -966,8 +990,14 @@ function onPatch(target, listener) {
966
990
  function applyPatch(target, patch) {
967
991
  const patches = Array.isArray(patch) ? patch : [patch];
968
992
  const rootNode = getStateTreeNode(target).getRoot();
969
- for (const p of patches) {
970
- applyPatchToNode(rootNode, p);
993
+ const wasApplying = getIsApplyingSnapshotOrPatch();
994
+ setIsApplyingSnapshotOrPatch(true);
995
+ try {
996
+ for (const p of patches) {
997
+ applyPatchToNode(rootNode, p);
998
+ }
999
+ } finally {
1000
+ setIsApplyingSnapshotOrPatch(wasApplying);
971
1001
  }
972
1002
  }
973
1003
  function applyPatchToNode(rootNode, patch) {
@@ -983,37 +1013,61 @@ function applyPatchToNode(rootNode, patch) {
983
1013
  const key = pathParts[pathParts.length - 1];
984
1014
  switch (patch.op) {
985
1015
  case "replace": {
986
- const childNode = node.getChild(key);
987
- if (childNode) {
988
- applySnapshotToNode(childNode, patch.value);
1016
+ const instance = node.getInstance();
1017
+ if (instance && Array.isArray(instance)) {
1018
+ const index = parseInt(key, 10);
1019
+ instance[index] = patch.value;
1020
+ } else if (instance && instance instanceof Map) {
1021
+ instance.set(key, patch.value);
989
1022
  } else {
990
- const currentValue = node.getValue();
991
- currentValue[key] = patch.value;
992
- node.setValue(currentValue);
1023
+ const childNode = node.getChild(key);
1024
+ if (childNode) {
1025
+ applySnapshotToNode(childNode, patch.value);
1026
+ } else {
1027
+ const currentValue = node.getValue();
1028
+ currentValue[key] = patch.value;
1029
+ node.setValue(currentValue);
1030
+ }
993
1031
  }
994
1032
  break;
995
1033
  }
996
1034
  case "add": {
997
- const currentValue = node.getValue();
998
- if (Array.isArray(currentValue)) {
999
- const index = key === "-" ? currentValue.length : parseInt(key, 10);
1000
- currentValue.splice(index, 0, patch.value);
1001
- node.setValue([...currentValue]);
1002
- } else if (typeof currentValue === "object" && currentValue !== null) {
1003
- currentValue[key] = patch.value;
1004
- node.setValue({ ...currentValue });
1035
+ const instance = node.getInstance();
1036
+ if (instance && Array.isArray(instance)) {
1037
+ const index = key === "-" ? instance.length : parseInt(key, 10);
1038
+ instance.splice(index, 0, patch.value);
1039
+ } else if (instance && instance instanceof Map) {
1040
+ instance.set(key, patch.value);
1041
+ } else {
1042
+ const currentValue = node.getValue();
1043
+ if (Array.isArray(currentValue)) {
1044
+ const index = key === "-" ? currentValue.length : parseInt(key, 10);
1045
+ currentValue.splice(index, 0, patch.value);
1046
+ node.setValue([...currentValue]);
1047
+ } else if (typeof currentValue === "object" && currentValue !== null) {
1048
+ currentValue[key] = patch.value;
1049
+ node.setValue({ ...currentValue });
1050
+ }
1005
1051
  }
1006
1052
  break;
1007
1053
  }
1008
1054
  case "remove": {
1009
- const currentValue = node.getValue();
1010
- if (Array.isArray(currentValue)) {
1055
+ const instance = node.getInstance();
1056
+ if (instance && Array.isArray(instance)) {
1011
1057
  const index = parseInt(key, 10);
1012
- currentValue.splice(index, 1);
1013
- node.setValue([...currentValue]);
1014
- } else if (typeof currentValue === "object" && currentValue !== null) {
1015
- delete currentValue[key];
1016
- node.setValue({ ...currentValue });
1058
+ instance.splice(index, 1);
1059
+ } else if (instance && instance instanceof Map) {
1060
+ instance.delete(key);
1061
+ } else {
1062
+ const currentValue = node.getValue();
1063
+ if (Array.isArray(currentValue)) {
1064
+ const index = parseInt(key, 10);
1065
+ currentValue.splice(index, 1);
1066
+ node.setValue([...currentValue]);
1067
+ } else if (typeof currentValue === "object" && currentValue !== null) {
1068
+ delete currentValue[key];
1069
+ node.setValue({ ...currentValue });
1070
+ }
1017
1071
  }
1018
1072
  break;
1019
1073
  }
@@ -1067,7 +1121,7 @@ function isActionRunning() {
1067
1121
  }
1068
1122
  function trackAction(node, name, args, fn) {
1069
1123
  const previousAction = currentAction;
1070
- currentAction = { name, args, tree: node };
1124
+ currentAction = { name, args, tree: node, parent: previousAction };
1071
1125
  try {
1072
1126
  const result = fn();
1073
1127
  const call = {
@@ -1623,6 +1677,9 @@ function canWrite(node) {
1623
1677
  if (!node.$isAlive) {
1624
1678
  return false;
1625
1679
  }
1680
+ if (getIsApplyingSnapshotOrPatch()) {
1681
+ return true;
1682
+ }
1626
1683
  if (typeof process !== "undefined" && process.env && process.env.NODE_ENV === "production") {
1627
1684
  return true;
1628
1685
  }
@@ -2418,6 +2475,13 @@ var MSTArray = class _MSTArray extends Array {
2418
2475
  return [...this];
2419
2476
  }
2420
2477
  syncToNode() {
2478
+ const oldArray = this.node.getValue() || [];
2479
+ const newArray = [...this];
2480
+ const oldSnapshots = /* @__PURE__ */ new Map();
2481
+ for (let i = 0; i < oldArray.length; i++) {
2482
+ const childNode = this.node.getChild(String(i));
2483
+ oldSnapshots.set(i, childNode ? getSnapshotFromNode(childNode) : oldArray[i]);
2484
+ }
2421
2485
  const existingChildNodes = /* @__PURE__ */ new Set();
2422
2486
  for (const [, child] of this.node.getChildren()) {
2423
2487
  existingChildNodes.add(child);
@@ -2469,7 +2533,59 @@ var MSTArray = class _MSTArray extends Array {
2469
2533
  for (const [key, childNode] of newChildren) {
2470
2534
  this.node.addChild(key, childNode);
2471
2535
  }
2472
- this.node.setValue([...this]);
2536
+ const patches = [];
2537
+ const reversePatches = [];
2538
+ if (newArray.length > oldArray.length && oldArray.every((val, idx) => val === newArray[idx])) {
2539
+ for (let i = oldArray.length; i < newArray.length; i++) {
2540
+ const childNode = this.node.getChild(String(i));
2541
+ const valSnap = childNode ? getSnapshotFromNode(childNode) : newArray[i];
2542
+ patches.push({
2543
+ op: "add",
2544
+ path: `${this.node.$path}/${i}`,
2545
+ value: valSnap
2546
+ });
2547
+ reversePatches.push({
2548
+ op: "remove",
2549
+ path: `${this.node.$path}/${i}`
2550
+ });
2551
+ }
2552
+ } else if (newArray.length < oldArray.length && newArray.every((val, idx) => val === oldArray[idx])) {
2553
+ for (let i = oldArray.length - 1; i >= newArray.length; i--) {
2554
+ const oldValSnap = oldSnapshots.get(i);
2555
+ patches.push({
2556
+ op: "remove",
2557
+ path: `${this.node.$path}/${i}`
2558
+ });
2559
+ reversePatches.push({
2560
+ op: "add",
2561
+ path: `${this.node.$path}/${i}`,
2562
+ value: oldValSnap
2563
+ });
2564
+ }
2565
+ } else {
2566
+ const oldSnap = oldArray.map((_, idx) => oldSnapshots.get(idx));
2567
+ const newSnap = newArray.map((_, idx) => {
2568
+ const childNode = this.node.getChild(String(idx));
2569
+ return childNode ? getSnapshotFromNode(childNode) : newArray[idx];
2570
+ });
2571
+ patches.push({
2572
+ op: "replace",
2573
+ path: this.node.$path,
2574
+ value: newSnap
2575
+ });
2576
+ reversePatches.push({
2577
+ op: "replace",
2578
+ path: this.node.$path,
2579
+ value: oldSnap
2580
+ });
2581
+ }
2582
+ const store = getGlobalStore();
2583
+ store.set(this.node.valueAtom, newArray);
2584
+ this.node.invalidateSnapshot();
2585
+ patches.forEach((patch, idx) => {
2586
+ this.node.notifyPatch(patch, reversePatches[idx]);
2587
+ });
2588
+ this.node.notifyVolatileChange();
2473
2589
  }
2474
2590
  };
2475
2591
  var ArrayType = class {
@@ -3557,9 +3673,11 @@ var UndoManager = class {
3557
3673
  this.isRedoing = false;
3558
3674
  this.skipRecording = false;
3559
3675
  this.grouping = false;
3676
+ this.actionGrouping = false;
3560
3677
  this.currentGroup = [];
3561
3678
  this.currentGroupInverse = [];
3562
3679
  this.disposer = null;
3680
+ this.actionDisposer = null;
3563
3681
  this.lastChangeTime = 0;
3564
3682
  this.target = target;
3565
3683
  this.options = {
@@ -3570,6 +3688,14 @@ var UndoManager = class {
3570
3688
  this.disposer = onPatch(target, (patch, reversePatch) => {
3571
3689
  this.recordPatch(patch, reversePatch);
3572
3690
  });
3691
+ this.actionDisposer = onAction(target, () => {
3692
+ const current = getCurrentAction();
3693
+ if (current && !current.parent) {
3694
+ if (this.actionGrouping) {
3695
+ this.endGroup();
3696
+ }
3697
+ }
3698
+ });
3573
3699
  }
3574
3700
  get canUndo() {
3575
3701
  return this.currentIndex >= 0;
@@ -3593,16 +3719,31 @@ var UndoManager = class {
3593
3719
  if (this.isUndoing || this.isRedoing || this.skipRecording) {
3594
3720
  return;
3595
3721
  }
3722
+ const node = getStateTreeNode(this.target);
3723
+ if (node.getRoot().$isApplyingHistory) {
3724
+ return;
3725
+ }
3596
3726
  const now = Date.now();
3727
+ if (isActionRunning() && !this.grouping) {
3728
+ this.grouping = true;
3729
+ this.actionGrouping = true;
3730
+ this.currentGroup = [];
3731
+ this.currentGroupInverse = [];
3732
+ Promise.resolve().then(() => {
3733
+ if (this.actionGrouping) {
3734
+ this.endGroup();
3735
+ }
3736
+ });
3737
+ }
3597
3738
  if (this.grouping) {
3598
3739
  this.currentGroup.push(reversePatch);
3599
- this.currentGroupInverse.unshift({ ...patch });
3740
+ this.currentGroupInverse.push({ ...patch });
3600
3741
  return;
3601
3742
  }
3602
3743
  if (this.options.groupByTime && this.historyEntries.length > 0 && now - this.lastChangeTime < this.options.groupingWindow && this.currentIndex === this.historyEntries.length - 1) {
3603
3744
  const lastEntry = this.historyEntries[this.currentIndex];
3604
3745
  lastEntry.patches.push(reversePatch);
3605
- lastEntry.inversePatches.unshift({ ...patch });
3746
+ lastEntry.inversePatches.push({ ...patch });
3606
3747
  lastEntry.timestamp = now;
3607
3748
  } else {
3608
3749
  if (this.currentIndex < this.historyEntries.length - 1) {
@@ -3626,6 +3767,10 @@ var UndoManager = class {
3626
3767
  if (!this.canUndo) {
3627
3768
  return;
3628
3769
  }
3770
+ const node = getStateTreeNode(this.target);
3771
+ const rootNode = node.getRoot();
3772
+ const wasApplying = rootNode.$isApplyingHistory;
3773
+ rootNode.$isApplyingHistory = true;
3629
3774
  this.isUndoing = true;
3630
3775
  try {
3631
3776
  const entry = this.historyEntries[this.currentIndex];
@@ -3635,12 +3780,17 @@ var UndoManager = class {
3635
3780
  this.currentIndex--;
3636
3781
  } finally {
3637
3782
  this.isUndoing = false;
3783
+ rootNode.$isApplyingHistory = wasApplying;
3638
3784
  }
3639
3785
  }
3640
3786
  redo() {
3641
3787
  if (!this.canRedo) {
3642
3788
  return;
3643
3789
  }
3790
+ const node = getStateTreeNode(this.target);
3791
+ const rootNode = node.getRoot();
3792
+ const wasApplying = rootNode.$isApplyingHistory;
3793
+ rootNode.$isApplyingHistory = true;
3644
3794
  this.isRedoing = true;
3645
3795
  try {
3646
3796
  this.currentIndex++;
@@ -3650,6 +3800,7 @@ var UndoManager = class {
3650
3800
  }
3651
3801
  } finally {
3652
3802
  this.isRedoing = false;
3803
+ rootNode.$isApplyingHistory = wasApplying;
3653
3804
  }
3654
3805
  }
3655
3806
  clear() {
@@ -3658,9 +3809,11 @@ var UndoManager = class {
3658
3809
  this.currentGroup = [];
3659
3810
  this.currentGroupInverse = [];
3660
3811
  this.grouping = false;
3812
+ this.actionGrouping = false;
3661
3813
  }
3662
3814
  startGroup() {
3663
3815
  this.grouping = true;
3816
+ this.actionGrouping = false;
3664
3817
  this.currentGroup = [];
3665
3818
  this.currentGroupInverse = [];
3666
3819
  }
@@ -3669,6 +3822,7 @@ var UndoManager = class {
3669
3822
  return;
3670
3823
  }
3671
3824
  this.grouping = false;
3825
+ this.actionGrouping = false;
3672
3826
  if (this.currentGroup.length > 0) {
3673
3827
  if (this.currentIndex < this.historyEntries.length - 1) {
3674
3828
  this.historyEntries.splice(this.currentIndex + 1);
@@ -3701,6 +3855,10 @@ var UndoManager = class {
3701
3855
  this.disposer();
3702
3856
  this.disposer = null;
3703
3857
  }
3858
+ if (this.actionDisposer) {
3859
+ this.actionDisposer();
3860
+ this.actionDisposer = null;
3861
+ }
3704
3862
  this.clear();
3705
3863
  }
3706
3864
  };
@@ -3719,9 +3877,12 @@ var TimeTravelManager = class {
3719
3877
  this.record();
3720
3878
  if (this.autoRecord) {
3721
3879
  this.disposer = onPatch(target, () => {
3722
- if (!this.isApplying) {
3723
- this.record();
3880
+ if (this.isApplying) return;
3881
+ const node = getStateTreeNode(this.target);
3882
+ if (node.getRoot().$isApplyingHistory) {
3883
+ return;
3724
3884
  }
3885
+ this.record();
3725
3886
  });
3726
3887
  }
3727
3888
  }
@@ -3759,12 +3920,17 @@ var TimeTravelManager = class {
3759
3920
  }
3760
3921
  goTo(index) {
3761
3922
  if (index < 0 || index >= this.snapshots.length) return;
3923
+ const node = getStateTreeNode(this.target);
3924
+ const rootNode = node.getRoot();
3925
+ const wasApplying = rootNode.$isApplyingHistory;
3926
+ rootNode.$isApplyingHistory = true;
3762
3927
  this.isApplying = true;
3763
3928
  try {
3764
3929
  this.index = index;
3765
3930
  applySnapshot(this.target, this.snapshots[index]);
3766
3931
  } finally {
3767
3932
  this.isApplying = false;
3933
+ rootNode.$isApplyingHistory = wasApplying;
3768
3934
  }
3769
3935
  }
3770
3936
  getSnapshot(index) {