jotai-state-tree 1.3.4 → 1.3.6

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 */
@@ -126,11 +134,11 @@ var StateTreeNode = class {
126
134
  return;
127
135
  }
128
136
  globalStore.set(this.valueAtom, value);
137
+ this.notifySnapshotChange();
129
138
  this.notifyPatch(
130
139
  { op: "replace", path: this.$path, value },
131
140
  { op: "replace", path: this.$path, value: oldValue, oldValue }
132
141
  );
133
- this.notifySnapshotChange();
134
142
  }
135
143
  /** Add a child node */
136
144
  addChild(key, child) {
@@ -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;
@@ -232,11 +240,11 @@ var StateTreeNode = class {
232
240
  /** Notify about a property change (for use by model proxy) */
233
241
  notifyPropertyChange(propName, newValue, oldValue) {
234
242
  const path = this.$path ? `${this.$path}/${propName}` : `/${propName}`;
243
+ this.notifySnapshotChange();
235
244
  this.notifyPatch(
236
245
  { op: "replace", path, value: newValue },
237
246
  { op: "replace", path, value: oldValue, oldValue }
238
247
  );
239
- this.notifySnapshotChange();
240
248
  }
241
249
  /** Notify about a volatile state change (triggers snapshot listeners without patches) */
242
250
  notifyVolatileChange() {
@@ -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,
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-S8buyIlj.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-S8buyIlj.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-C2ADWEka.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-C2ADWEka.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-S8buyIlj.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-S8buyIlj.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-C2ADWEka.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-C2ADWEka.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 */
@@ -557,11 +565,11 @@ var StateTreeNode = class {
557
565
  return;
558
566
  }
559
567
  globalStore.set(this.valueAtom, value);
568
+ this.notifySnapshotChange();
560
569
  this.notifyPatch(
561
570
  { op: "replace", path: this.$path, value },
562
571
  { op: "replace", path: this.$path, value: oldValue, oldValue }
563
572
  );
564
- this.notifySnapshotChange();
565
573
  }
566
574
  /** Add a child node */
567
575
  addChild(key, child) {
@@ -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;
@@ -663,11 +671,11 @@ var StateTreeNode = class {
663
671
  /** Notify about a property change (for use by model proxy) */
664
672
  notifyPropertyChange(propName, newValue, oldValue) {
665
673
  const path = this.$path ? `${this.$path}/${propName}` : `/${propName}`;
674
+ this.notifySnapshotChange();
666
675
  this.notifyPatch(
667
676
  { op: "replace", path, value: newValue },
668
677
  { op: "replace", path, value: oldValue, oldValue }
669
678
  );
670
- this.notifySnapshotChange();
671
679
  }
672
680
  /** Notify about a volatile state change (triggers snapshot listeners without patches) */
673
681
  notifyVolatileChange() {
@@ -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
  }
@@ -2420,6 +2477,11 @@ var MSTArray = class _MSTArray extends Array {
2420
2477
  syncToNode() {
2421
2478
  const oldArray = this.node.getValue() || [];
2422
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
+ }
2423
2485
  const existingChildNodes = /* @__PURE__ */ new Set();
2424
2486
  for (const [, child] of this.node.getChildren()) {
2425
2487
  existingChildNodes.add(child);
@@ -2489,8 +2551,7 @@ var MSTArray = class _MSTArray extends Array {
2489
2551
  }
2490
2552
  } else if (newArray.length < oldArray.length && newArray.every((val, idx) => val === oldArray[idx])) {
2491
2553
  for (let i = oldArray.length - 1; i >= newArray.length; i--) {
2492
- const childNode = this.node.getChild(String(i));
2493
- const oldValSnap = childNode ? getSnapshotFromNode(childNode) : oldArray[i];
2554
+ const oldValSnap = oldSnapshots.get(i);
2494
2555
  patches.push({
2495
2556
  op: "remove",
2496
2557
  path: `${this.node.$path}/${i}`
@@ -2502,10 +2563,7 @@ var MSTArray = class _MSTArray extends Array {
2502
2563
  });
2503
2564
  }
2504
2565
  } else {
2505
- const oldSnap = oldArray.map((_, idx) => {
2506
- const childNode = this.node.getChild(String(idx));
2507
- return childNode ? getSnapshotFromNode(childNode) : oldArray[idx];
2508
- });
2566
+ const oldSnap = oldArray.map((_, idx) => oldSnapshots.get(idx));
2509
2567
  const newSnap = newArray.map((_, idx) => {
2510
2568
  const childNode = this.node.getChild(String(idx));
2511
2569
  return childNode ? getSnapshotFromNode(childNode) : newArray[idx];
@@ -2523,7 +2581,7 @@ var MSTArray = class _MSTArray extends Array {
2523
2581
  }
2524
2582
  const store = getGlobalStore();
2525
2583
  store.set(this.node.valueAtom, newArray);
2526
- this.node.invalidateSnapshot();
2584
+ this.node.notifySnapshotChange();
2527
2585
  patches.forEach((patch, idx) => {
2528
2586
  this.node.notifyPatch(patch, reversePatches[idx]);
2529
2587
  });
@@ -3615,9 +3673,11 @@ var UndoManager = class {
3615
3673
  this.isRedoing = false;
3616
3674
  this.skipRecording = false;
3617
3675
  this.grouping = false;
3676
+ this.actionGrouping = false;
3618
3677
  this.currentGroup = [];
3619
3678
  this.currentGroupInverse = [];
3620
3679
  this.disposer = null;
3680
+ this.actionDisposer = null;
3621
3681
  this.lastChangeTime = 0;
3622
3682
  this.target = target;
3623
3683
  this.options = {
@@ -3628,6 +3688,14 @@ var UndoManager = class {
3628
3688
  this.disposer = onPatch(target, (patch, reversePatch) => {
3629
3689
  this.recordPatch(patch, reversePatch);
3630
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
+ });
3631
3699
  }
3632
3700
  get canUndo() {
3633
3701
  return this.currentIndex >= 0;
@@ -3651,16 +3719,31 @@ var UndoManager = class {
3651
3719
  if (this.isUndoing || this.isRedoing || this.skipRecording) {
3652
3720
  return;
3653
3721
  }
3722
+ const node = getStateTreeNode(this.target);
3723
+ if (node.getRoot().$isApplyingHistory) {
3724
+ return;
3725
+ }
3654
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
+ }
3655
3738
  if (this.grouping) {
3656
3739
  this.currentGroup.push(reversePatch);
3657
- this.currentGroupInverse.unshift({ ...patch });
3740
+ this.currentGroupInverse.push({ ...patch });
3658
3741
  return;
3659
3742
  }
3660
3743
  if (this.options.groupByTime && this.historyEntries.length > 0 && now - this.lastChangeTime < this.options.groupingWindow && this.currentIndex === this.historyEntries.length - 1) {
3661
3744
  const lastEntry = this.historyEntries[this.currentIndex];
3662
3745
  lastEntry.patches.push(reversePatch);
3663
- lastEntry.inversePatches.unshift({ ...patch });
3746
+ lastEntry.inversePatches.push({ ...patch });
3664
3747
  lastEntry.timestamp = now;
3665
3748
  } else {
3666
3749
  if (this.currentIndex < this.historyEntries.length - 1) {
@@ -3684,6 +3767,10 @@ var UndoManager = class {
3684
3767
  if (!this.canUndo) {
3685
3768
  return;
3686
3769
  }
3770
+ const node = getStateTreeNode(this.target);
3771
+ const rootNode = node.getRoot();
3772
+ const wasApplying = rootNode.$isApplyingHistory;
3773
+ rootNode.$isApplyingHistory = true;
3687
3774
  this.isUndoing = true;
3688
3775
  try {
3689
3776
  const entry = this.historyEntries[this.currentIndex];
@@ -3693,12 +3780,17 @@ var UndoManager = class {
3693
3780
  this.currentIndex--;
3694
3781
  } finally {
3695
3782
  this.isUndoing = false;
3783
+ rootNode.$isApplyingHistory = wasApplying;
3696
3784
  }
3697
3785
  }
3698
3786
  redo() {
3699
3787
  if (!this.canRedo) {
3700
3788
  return;
3701
3789
  }
3790
+ const node = getStateTreeNode(this.target);
3791
+ const rootNode = node.getRoot();
3792
+ const wasApplying = rootNode.$isApplyingHistory;
3793
+ rootNode.$isApplyingHistory = true;
3702
3794
  this.isRedoing = true;
3703
3795
  try {
3704
3796
  this.currentIndex++;
@@ -3708,6 +3800,7 @@ var UndoManager = class {
3708
3800
  }
3709
3801
  } finally {
3710
3802
  this.isRedoing = false;
3803
+ rootNode.$isApplyingHistory = wasApplying;
3711
3804
  }
3712
3805
  }
3713
3806
  clear() {
@@ -3716,9 +3809,11 @@ var UndoManager = class {
3716
3809
  this.currentGroup = [];
3717
3810
  this.currentGroupInverse = [];
3718
3811
  this.grouping = false;
3812
+ this.actionGrouping = false;
3719
3813
  }
3720
3814
  startGroup() {
3721
3815
  this.grouping = true;
3816
+ this.actionGrouping = false;
3722
3817
  this.currentGroup = [];
3723
3818
  this.currentGroupInverse = [];
3724
3819
  }
@@ -3727,6 +3822,7 @@ var UndoManager = class {
3727
3822
  return;
3728
3823
  }
3729
3824
  this.grouping = false;
3825
+ this.actionGrouping = false;
3730
3826
  if (this.currentGroup.length > 0) {
3731
3827
  if (this.currentIndex < this.historyEntries.length - 1) {
3732
3828
  this.historyEntries.splice(this.currentIndex + 1);
@@ -3759,6 +3855,10 @@ var UndoManager = class {
3759
3855
  this.disposer();
3760
3856
  this.disposer = null;
3761
3857
  }
3858
+ if (this.actionDisposer) {
3859
+ this.actionDisposer();
3860
+ this.actionDisposer = null;
3861
+ }
3762
3862
  this.clear();
3763
3863
  }
3764
3864
  };
@@ -3777,9 +3877,12 @@ var TimeTravelManager = class {
3777
3877
  this.record();
3778
3878
  if (this.autoRecord) {
3779
3879
  this.disposer = onPatch(target, () => {
3780
- if (!this.isApplying) {
3781
- this.record();
3880
+ if (this.isApplying) return;
3881
+ const node = getStateTreeNode(this.target);
3882
+ if (node.getRoot().$isApplyingHistory) {
3883
+ return;
3782
3884
  }
3885
+ this.record();
3783
3886
  });
3784
3887
  }
3785
3888
  }
@@ -3817,12 +3920,17 @@ var TimeTravelManager = class {
3817
3920
  }
3818
3921
  goTo(index) {
3819
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;
3820
3927
  this.isApplying = true;
3821
3928
  try {
3822
3929
  this.index = index;
3823
3930
  applySnapshot(this.target, this.snapshots[index]);
3824
3931
  } finally {
3825
3932
  this.isApplying = false;
3933
+ rootNode.$isApplyingHistory = wasApplying;
3826
3934
  }
3827
3935
  }
3828
3936
  getSnapshot(index) {