jotai-state-tree 1.3.2 → 1.3.4
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.
- package/dist/{chunk-OAXK4KFM.mjs → chunk-NFJRKXCI.mjs} +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +59 -1
- package/dist/index.mjs +61 -2
- package/dist/react.d.mts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.mjs +1 -1
- package/dist/{tree-BV2K9utF.d.mts → tree-S8buyIlj.d.mts} +1 -1
- package/dist/{tree-BV2K9utF.d.ts → tree-S8buyIlj.d.ts} +1 -1
- package/package.json +1 -1
- package/src/__tests__/index.test.ts +45 -0
- package/src/array.ts +77 -2
- package/src/tree.ts +1 -1
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-
|
|
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-
|
|
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';
|
|
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-
|
|
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-
|
|
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';
|
|
3
3
|
import 'jotai/vanilla/internals';
|
|
4
4
|
import 'jotai';
|
|
5
5
|
|
package/dist/index.js
CHANGED
|
@@ -2418,6 +2418,8 @@ var MSTArray = class _MSTArray extends Array {
|
|
|
2418
2418
|
return [...this];
|
|
2419
2419
|
}
|
|
2420
2420
|
syncToNode() {
|
|
2421
|
+
const oldArray = this.node.getValue() || [];
|
|
2422
|
+
const newArray = [...this];
|
|
2421
2423
|
const existingChildNodes = /* @__PURE__ */ new Set();
|
|
2422
2424
|
for (const [, child] of this.node.getChildren()) {
|
|
2423
2425
|
existingChildNodes.add(child);
|
|
@@ -2469,7 +2471,63 @@ var MSTArray = class _MSTArray extends Array {
|
|
|
2469
2471
|
for (const [key, childNode] of newChildren) {
|
|
2470
2472
|
this.node.addChild(key, childNode);
|
|
2471
2473
|
}
|
|
2472
|
-
|
|
2474
|
+
const patches = [];
|
|
2475
|
+
const reversePatches = [];
|
|
2476
|
+
if (newArray.length > oldArray.length && oldArray.every((val, idx) => val === newArray[idx])) {
|
|
2477
|
+
for (let i = oldArray.length; i < newArray.length; i++) {
|
|
2478
|
+
const childNode = this.node.getChild(String(i));
|
|
2479
|
+
const valSnap = childNode ? getSnapshotFromNode(childNode) : newArray[i];
|
|
2480
|
+
patches.push({
|
|
2481
|
+
op: "add",
|
|
2482
|
+
path: `${this.node.$path}/${i}`,
|
|
2483
|
+
value: valSnap
|
|
2484
|
+
});
|
|
2485
|
+
reversePatches.push({
|
|
2486
|
+
op: "remove",
|
|
2487
|
+
path: `${this.node.$path}/${i}`
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
} else if (newArray.length < oldArray.length && newArray.every((val, idx) => val === oldArray[idx])) {
|
|
2491
|
+
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];
|
|
2494
|
+
patches.push({
|
|
2495
|
+
op: "remove",
|
|
2496
|
+
path: `${this.node.$path}/${i}`
|
|
2497
|
+
});
|
|
2498
|
+
reversePatches.push({
|
|
2499
|
+
op: "add",
|
|
2500
|
+
path: `${this.node.$path}/${i}`,
|
|
2501
|
+
value: oldValSnap
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
} else {
|
|
2505
|
+
const oldSnap = oldArray.map((_, idx) => {
|
|
2506
|
+
const childNode = this.node.getChild(String(idx));
|
|
2507
|
+
return childNode ? getSnapshotFromNode(childNode) : oldArray[idx];
|
|
2508
|
+
});
|
|
2509
|
+
const newSnap = newArray.map((_, idx) => {
|
|
2510
|
+
const childNode = this.node.getChild(String(idx));
|
|
2511
|
+
return childNode ? getSnapshotFromNode(childNode) : newArray[idx];
|
|
2512
|
+
});
|
|
2513
|
+
patches.push({
|
|
2514
|
+
op: "replace",
|
|
2515
|
+
path: this.node.$path,
|
|
2516
|
+
value: newSnap
|
|
2517
|
+
});
|
|
2518
|
+
reversePatches.push({
|
|
2519
|
+
op: "replace",
|
|
2520
|
+
path: this.node.$path,
|
|
2521
|
+
value: oldSnap
|
|
2522
|
+
});
|
|
2523
|
+
}
|
|
2524
|
+
const store = getGlobalStore();
|
|
2525
|
+
store.set(this.node.valueAtom, newArray);
|
|
2526
|
+
this.node.invalidateSnapshot();
|
|
2527
|
+
patches.forEach((patch, idx) => {
|
|
2528
|
+
this.node.notifyPatch(patch, reversePatches[idx]);
|
|
2529
|
+
});
|
|
2530
|
+
this.node.notifyVolatileChange();
|
|
2473
2531
|
}
|
|
2474
2532
|
};
|
|
2475
2533
|
var ArrayType = class {
|
package/dist/index.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
getRelativePath,
|
|
28
28
|
getRoot,
|
|
29
29
|
getSnapshot,
|
|
30
|
+
getSnapshotFromNode,
|
|
30
31
|
getStateTreeNode,
|
|
31
32
|
getTreeStats,
|
|
32
33
|
getType,
|
|
@@ -57,7 +58,7 @@ import {
|
|
|
57
58
|
tryResolve,
|
|
58
59
|
unfreeze,
|
|
59
60
|
walk
|
|
60
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-NFJRKXCI.mjs";
|
|
61
62
|
|
|
62
63
|
// src/primitives.ts
|
|
63
64
|
function createSimpleType(name, validator, defaultValue) {
|
|
@@ -1479,6 +1480,8 @@ var MSTArray = class _MSTArray extends Array {
|
|
|
1479
1480
|
return [...this];
|
|
1480
1481
|
}
|
|
1481
1482
|
syncToNode() {
|
|
1483
|
+
const oldArray = this.node.getValue() || [];
|
|
1484
|
+
const newArray = [...this];
|
|
1482
1485
|
const existingChildNodes = /* @__PURE__ */ new Set();
|
|
1483
1486
|
for (const [, child] of this.node.getChildren()) {
|
|
1484
1487
|
existingChildNodes.add(child);
|
|
@@ -1530,7 +1533,63 @@ var MSTArray = class _MSTArray extends Array {
|
|
|
1530
1533
|
for (const [key, childNode] of newChildren) {
|
|
1531
1534
|
this.node.addChild(key, childNode);
|
|
1532
1535
|
}
|
|
1533
|
-
|
|
1536
|
+
const patches = [];
|
|
1537
|
+
const reversePatches = [];
|
|
1538
|
+
if (newArray.length > oldArray.length && oldArray.every((val, idx) => val === newArray[idx])) {
|
|
1539
|
+
for (let i = oldArray.length; i < newArray.length; i++) {
|
|
1540
|
+
const childNode = this.node.getChild(String(i));
|
|
1541
|
+
const valSnap = childNode ? getSnapshotFromNode(childNode) : newArray[i];
|
|
1542
|
+
patches.push({
|
|
1543
|
+
op: "add",
|
|
1544
|
+
path: `${this.node.$path}/${i}`,
|
|
1545
|
+
value: valSnap
|
|
1546
|
+
});
|
|
1547
|
+
reversePatches.push({
|
|
1548
|
+
op: "remove",
|
|
1549
|
+
path: `${this.node.$path}/${i}`
|
|
1550
|
+
});
|
|
1551
|
+
}
|
|
1552
|
+
} else if (newArray.length < oldArray.length && newArray.every((val, idx) => val === oldArray[idx])) {
|
|
1553
|
+
for (let i = oldArray.length - 1; i >= newArray.length; i--) {
|
|
1554
|
+
const childNode = this.node.getChild(String(i));
|
|
1555
|
+
const oldValSnap = childNode ? getSnapshotFromNode(childNode) : oldArray[i];
|
|
1556
|
+
patches.push({
|
|
1557
|
+
op: "remove",
|
|
1558
|
+
path: `${this.node.$path}/${i}`
|
|
1559
|
+
});
|
|
1560
|
+
reversePatches.push({
|
|
1561
|
+
op: "add",
|
|
1562
|
+
path: `${this.node.$path}/${i}`,
|
|
1563
|
+
value: oldValSnap
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
} else {
|
|
1567
|
+
const oldSnap = oldArray.map((_, idx) => {
|
|
1568
|
+
const childNode = this.node.getChild(String(idx));
|
|
1569
|
+
return childNode ? getSnapshotFromNode(childNode) : oldArray[idx];
|
|
1570
|
+
});
|
|
1571
|
+
const newSnap = newArray.map((_, idx) => {
|
|
1572
|
+
const childNode = this.node.getChild(String(idx));
|
|
1573
|
+
return childNode ? getSnapshotFromNode(childNode) : newArray[idx];
|
|
1574
|
+
});
|
|
1575
|
+
patches.push({
|
|
1576
|
+
op: "replace",
|
|
1577
|
+
path: this.node.$path,
|
|
1578
|
+
value: newSnap
|
|
1579
|
+
});
|
|
1580
|
+
reversePatches.push({
|
|
1581
|
+
op: "replace",
|
|
1582
|
+
path: this.node.$path,
|
|
1583
|
+
value: oldSnap
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
const store = getGlobalStore();
|
|
1587
|
+
store.set(this.node.valueAtom, newArray);
|
|
1588
|
+
this.node.invalidateSnapshot();
|
|
1589
|
+
patches.forEach((patch, idx) => {
|
|
1590
|
+
this.node.notifyPatch(patch, reversePatches[idx]);
|
|
1591
|
+
});
|
|
1592
|
+
this.node.notifyVolatileChange();
|
|
1534
1593
|
}
|
|
1535
1594
|
};
|
|
1536
1595
|
var ArrayType = class {
|
package/dist/react.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { ComponentType, FC, ReactNode } from 'react';
|
|
2
|
-
import { ag as getGlobalStore } from './tree-
|
|
3
|
-
export { az as hasStateTreeNode } from './tree-
|
|
2
|
+
import { ag as getGlobalStore } from './tree-S8buyIlj.mjs';
|
|
3
|
+
export { az as hasStateTreeNode } from './tree-S8buyIlj.mjs';
|
|
4
4
|
import 'jotai/vanilla/internals';
|
|
5
5
|
import 'jotai';
|
|
6
6
|
|
package/dist/react.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { ComponentType, FC, ReactNode } from 'react';
|
|
2
|
-
import { ag as getGlobalStore } from './tree-
|
|
3
|
-
export { az as hasStateTreeNode } from './tree-
|
|
2
|
+
import { ag as getGlobalStore } from './tree-S8buyIlj.js';
|
|
3
|
+
export { az as hasStateTreeNode } from './tree-S8buyIlj.js';
|
|
4
4
|
import 'jotai/vanilla/internals';
|
|
5
5
|
import 'jotai';
|
|
6
6
|
|
package/dist/react.mjs
CHANGED
|
@@ -375,7 +375,7 @@ declare class StateTreeNode implements IStateTreeNode {
|
|
|
375
375
|
/** Subscribe to patches */
|
|
376
376
|
onPatch(listener: (patch: IJsonPatch, reversePatch: IReversibleJsonPatch) => void): IDisposer;
|
|
377
377
|
/** Notify patch listeners */
|
|
378
|
-
|
|
378
|
+
notifyPatch(patch: IJsonPatch, reversePatch: IReversibleJsonPatch): void;
|
|
379
379
|
/** Notify snapshot listeners */
|
|
380
380
|
private notifySnapshotChange;
|
|
381
381
|
/** Notify about a property change (for use by model proxy) */
|
|
@@ -375,7 +375,7 @@ declare class StateTreeNode implements IStateTreeNode {
|
|
|
375
375
|
/** Subscribe to patches */
|
|
376
376
|
onPatch(listener: (patch: IJsonPatch, reversePatch: IReversibleJsonPatch) => void): IDisposer;
|
|
377
377
|
/** Notify patch listeners */
|
|
378
|
-
|
|
378
|
+
notifyPatch(patch: IJsonPatch, reversePatch: IReversibleJsonPatch): void;
|
|
379
379
|
/** Notify snapshot listeners */
|
|
380
380
|
private notifySnapshotChange;
|
|
381
381
|
/** Notify about a property change (for use by model proxy) */
|
package/package.json
CHANGED
|
@@ -687,6 +687,51 @@ describe("Patches", () => {
|
|
|
687
687
|
disposer();
|
|
688
688
|
});
|
|
689
689
|
|
|
690
|
+
it("should listen to complex array patches", () => {
|
|
691
|
+
const Todo = types.model("Todo", {
|
|
692
|
+
id: types.identifier,
|
|
693
|
+
title: types.string,
|
|
694
|
+
done: types.optional(types.boolean, false),
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
const TodoStore = types
|
|
698
|
+
.model("TodoStore", {
|
|
699
|
+
todos: types.optional(types.array(Todo), []),
|
|
700
|
+
})
|
|
701
|
+
.actions((self) => ({
|
|
702
|
+
addTodo(title: string) {
|
|
703
|
+
self.todos.push({
|
|
704
|
+
id: "1",
|
|
705
|
+
title,
|
|
706
|
+
done: false,
|
|
707
|
+
});
|
|
708
|
+
},
|
|
709
|
+
}));
|
|
710
|
+
|
|
711
|
+
const store = TodoStore.create({
|
|
712
|
+
todos: [],
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
const patches: any[] = [];
|
|
716
|
+
onPatch(store, (patch) => {
|
|
717
|
+
patches.push(patch);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
store.addTodo("Learn jotai-state-tree");
|
|
721
|
+
|
|
722
|
+
expect(patches).toEqual([
|
|
723
|
+
{
|
|
724
|
+
op: "add",
|
|
725
|
+
path: "/todos/0",
|
|
726
|
+
value: {
|
|
727
|
+
id: "1",
|
|
728
|
+
title: "Learn jotai-state-tree",
|
|
729
|
+
done: false,
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
]);
|
|
733
|
+
});
|
|
734
|
+
|
|
690
735
|
it("should apply patches", () => {
|
|
691
736
|
const Model = types.model("Model", {
|
|
692
737
|
value: types.number,
|
package/src/array.ts
CHANGED
|
@@ -10,12 +10,15 @@ import type {
|
|
|
10
10
|
IValidationContext,
|
|
11
11
|
IValidationResult,
|
|
12
12
|
IAnyType,
|
|
13
|
+
IJsonPatch,
|
|
14
|
+
IReversibleJsonPatch,
|
|
13
15
|
} from "./types";
|
|
14
16
|
import {
|
|
15
17
|
StateTreeNode,
|
|
16
18
|
$treenode,
|
|
17
19
|
getStateTreeNode,
|
|
18
20
|
getGlobalStore,
|
|
21
|
+
getSnapshotFromNode,
|
|
19
22
|
} from "./tree";
|
|
20
23
|
import { canWrite } from "./lifecycle";
|
|
21
24
|
|
|
@@ -235,6 +238,9 @@ class MSTArray<T> extends Array<T> implements IMSTArray<T> {
|
|
|
235
238
|
}
|
|
236
239
|
|
|
237
240
|
private syncToNode(): void {
|
|
241
|
+
const oldArray = (this.node.getValue() as unknown[]) || [];
|
|
242
|
+
const newArray = [...this];
|
|
243
|
+
|
|
238
244
|
// Collect existing child nodes for cleanup comparison
|
|
239
245
|
const existingChildNodes = new Set<StateTreeNode>();
|
|
240
246
|
for (const [, child] of this.node.getChildren()) {
|
|
@@ -303,8 +309,77 @@ class MSTArray<T> extends Array<T> implements IMSTArray<T> {
|
|
|
303
309
|
this.node.addChild(key, childNode);
|
|
304
310
|
}
|
|
305
311
|
|
|
306
|
-
//
|
|
307
|
-
|
|
312
|
+
// Determine diff and generate granular patches
|
|
313
|
+
const patches: IJsonPatch[] = [];
|
|
314
|
+
const reversePatches: IReversibleJsonPatch[] = [];
|
|
315
|
+
|
|
316
|
+
// Case 1: Simple push (items added at the end)
|
|
317
|
+
if (newArray.length > oldArray.length && oldArray.every((val, idx) => val === newArray[idx])) {
|
|
318
|
+
for (let i = oldArray.length; i < newArray.length; i++) {
|
|
319
|
+
const childNode = this.node.getChild(String(i));
|
|
320
|
+
const valSnap = childNode ? getSnapshotFromNode(childNode) : newArray[i];
|
|
321
|
+
patches.push({
|
|
322
|
+
op: "add",
|
|
323
|
+
path: `${this.node.$path}/${i}`,
|
|
324
|
+
value: valSnap,
|
|
325
|
+
});
|
|
326
|
+
reversePatches.push({
|
|
327
|
+
op: "remove",
|
|
328
|
+
path: `${this.node.$path}/${i}`,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// Case 2: Simple pop (items removed from the end)
|
|
333
|
+
else if (newArray.length < oldArray.length && newArray.every((val, idx) => val === oldArray[idx])) {
|
|
334
|
+
for (let i = oldArray.length - 1; i >= newArray.length; i--) {
|
|
335
|
+
const childNode = this.node.getChild(String(i));
|
|
336
|
+
const oldValSnap = childNode ? getSnapshotFromNode(childNode) : oldArray[i];
|
|
337
|
+
patches.push({
|
|
338
|
+
op: "remove",
|
|
339
|
+
path: `${this.node.$path}/${i}`,
|
|
340
|
+
});
|
|
341
|
+
reversePatches.push({
|
|
342
|
+
op: "add",
|
|
343
|
+
path: `${this.node.$path}/${i}`,
|
|
344
|
+
value: oldValSnap,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Case 3: Other mutations (fallback to replace array)
|
|
349
|
+
else {
|
|
350
|
+
const oldSnap = oldArray.map((_, idx) => {
|
|
351
|
+
const childNode = this.node.getChild(String(idx));
|
|
352
|
+
return childNode ? getSnapshotFromNode(childNode) : oldArray[idx];
|
|
353
|
+
});
|
|
354
|
+
const newSnap = newArray.map((_, idx) => {
|
|
355
|
+
const childNode = this.node.getChild(String(idx));
|
|
356
|
+
return childNode ? getSnapshotFromNode(childNode) : newArray[idx];
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
patches.push({
|
|
360
|
+
op: "replace",
|
|
361
|
+
path: this.node.$path,
|
|
362
|
+
value: newSnap,
|
|
363
|
+
});
|
|
364
|
+
reversePatches.push({
|
|
365
|
+
op: "replace",
|
|
366
|
+
path: this.node.$path,
|
|
367
|
+
value: oldSnap,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Update the node's value silently
|
|
372
|
+
const store = getGlobalStore();
|
|
373
|
+
store.set(this.node.valueAtom, newArray);
|
|
374
|
+
this.node.invalidateSnapshot();
|
|
375
|
+
|
|
376
|
+
// Notify patch listeners
|
|
377
|
+
patches.forEach((patch, idx) => {
|
|
378
|
+
this.node.notifyPatch(patch, reversePatches[idx]);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Notify snapshot changes
|
|
382
|
+
this.node.notifyVolatileChange();
|
|
308
383
|
}
|
|
309
384
|
}
|
|
310
385
|
|
package/src/tree.ts
CHANGED
|
@@ -385,7 +385,7 @@ export class StateTreeNode implements IStateTreeNode {
|
|
|
385
385
|
}
|
|
386
386
|
|
|
387
387
|
/** Notify patch listeners */
|
|
388
|
-
|
|
388
|
+
notifyPatch(patch: IJsonPatch, reversePatch: IReversibleJsonPatch) {
|
|
389
389
|
this.patchListeners.forEach((listener) => listener(patch, reversePatch));
|
|
390
390
|
// Bubble up to parent
|
|
391
391
|
if (this.$parent) {
|