Skip to content

Commit 5396f53

Browse files
authored
fix(patch): fix patch generator issue (#60)
1 parent 7e95b49 commit 5396f53

File tree

8 files changed

+157
-49
lines changed

8 files changed

+157
-49
lines changed

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,23 +110,23 @@ Mutative is up to 6x faster than naive handcrafted reducer for updating immutabl
110110

111111
> Mutative passed all of Immer's test cases.
112112
113-
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.9 vs Immer v10.1.1]
113+
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.10 vs Immer v10.1.1]
114114

115115
![Benchmark](benchmark.jpg)
116116

117117
```
118-
Naive handcrafted reducer - No Freeze x 4,447 ops/sec ±0.85% (96 runs sampled)
119-
Mutative - No Freeze x 6,246 ops/sec ±1.29% (92 runs sampled)
120-
Immer - No Freeze x 5.26 ops/sec ±0.56% (18 runs sampled)
118+
Naive handcrafted reducer - No Freeze x 4,439 ops/sec ±0.65% (98 runs sampled)
119+
Mutative - No Freeze x 6,300 ops/sec ±1.19% (94 runs sampled)
120+
Immer - No Freeze x 5.26 ops/sec ±0.59% (18 runs sampled)
121121
122-
Mutative - Freeze x 950 ops/sec ±0.95% (96 runs sampled)
123-
Immer - Freeze x 377 ops/sec ±0.37% (93 runs sampled)
122+
Mutative - Freeze x 937 ops/sec ±1.25% (95 runs sampled)
123+
Immer - Freeze x 378 ops/sec ±0.66% (93 runs sampled)
124124
125-
Mutative - Patches and No Freeze x 978 ops/sec ±0.16% (97 runs sampled)
126-
Immer - Patches and No Freeze x 5.23 ops/sec ±0.25% (18 runs sampled)
125+
Mutative - Patches and No Freeze x 975 ops/sec ±0.17% (99 runs sampled)
126+
Immer - Patches and No Freeze x 5.29 ops/sec ±0.30% (18 runs sampled)
127127
128-
Mutative - Patches and Freeze x 504 ops/sec ±1.01% (94 runs sampled)
129-
Immer - Patches and Freeze x 272 ops/sec ±0.83% (89 runs sampled)
128+
Mutative - Patches and Freeze x 512 ops/sec ±0.85% (98 runs sampled)
129+
Immer - Patches and Freeze x 278 ops/sec ±0.57% (90 runs sampled)
130130
131131
The fastest method is Mutative - No Freeze
132132
```
@@ -137,7 +137,7 @@ Run `yarn benchmark` to measure performance.
137137
138138
Immer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.
139139

140-
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,246 ops/sec`) and Immer (`377 ops/sec`).
140+
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,300 ops/sec`) and Immer (`378 ops/sec`).
141141

142142
Overall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance). Run `yarn performance` to get all the performance results locally.
143143

benchmark.jpg

647 Bytes
Loading

src/apply.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,7 @@ export function apply<T extends object, F extends boolean = false>(
6767
);
6868
}
6969
// use `index` in Set draft
70-
base = get(
71-
getType(base) === DraftType.Set ? Array.from(base) : base,
72-
key
73-
);
70+
base = get(parentType === DraftType.Set ? Array.from(base) : base, key);
7471
if (typeof base !== 'object') {
7572
throw new Error(`Cannot apply patch at '${path.join('/')}'.`);
7673
}

src/utils/draft.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,15 @@ export function getPath(
5858
if (target.parent) {
5959
return getPath(target.parent, path);
6060
}
61-
return path.reverse();
61+
// `target` is root draft.
62+
path.reverse();
63+
try {
64+
// check if the path is valid
65+
resolvePath(target.copy, path);
66+
} catch (e) {
67+
return null;
68+
}
69+
return path;
6270
}
6371

6472
export function getType(target: any) {
@@ -124,3 +132,15 @@ export function unescapePath(path: string | (string | number)[]) {
124132
.map((_item) => _item.replace(/~1/g, '/').replace(/~0/g, '~'))
125133
.slice(1);
126134
}
135+
136+
export function resolvePath(base: any, path: (string | number)[]) {
137+
for (let index = 0; index < path.length - 1; index += 1) {
138+
const key = path[index];
139+
// use `index` in Set draft
140+
base = get(getType(base) === DraftType.Set ? Array.from(base) : base, key);
141+
if (typeof base !== 'object') {
142+
throw new Error(`Cannot resolve patch at '${path.join('/')}'.`);
143+
}
144+
}
145+
return base;
146+
}

test/index.test.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3992,3 +3992,94 @@ test('#57 - curried create returns object of wrong type', () => {
39923992
const nextState = finalize();
39933993
expect(nextState).toEqual({ a: { x: 'test' } });
39943994
});
3995+
3996+
test('#59 - Failure to apply inverse patchset', () => {
3997+
const parentId = 'parent';
3998+
const childId = 'child';
3999+
const myObj = {
4000+
[parentId]: { name: 'parent', children: [childId] },
4001+
[childId]: { name: 'child' },
4002+
};
4003+
const [newState, patchset, inverse] = create(
4004+
myObj,
4005+
(draft: any) => {
4006+
// delete children
4007+
while (draft[parentId].children.length) {
4008+
const id = draft[parentId].children[0];
4009+
draft[parentId].children.splice(0, 1);
4010+
delete draft[id]; // delete child object
4011+
}
4012+
4013+
// delete parent
4014+
// @ts-ignore
4015+
delete draft[parentId];
4016+
},
4017+
{ enablePatches: true }
4018+
);
4019+
const reverted = apply(newState, inverse);
4020+
expect(reverted).toEqual(myObj);
4021+
const reverted2 = apply(myObj, patchset);
4022+
expect(reverted2).toEqual(newState);
4023+
});
4024+
4025+
test('#59 - Failure to apply inverse patchset(Set)', () => {
4026+
const parentId = 'parent';
4027+
const childId = 'child';
4028+
4029+
const myObj = {
4030+
[parentId]: new Set([{ name: 'parent', children: [childId] }]),
4031+
[childId]: { name: 'child' },
4032+
};
4033+
4034+
const [newState, patchset, inverse] = create(
4035+
myObj,
4036+
(draft: any) => {
4037+
// delete children
4038+
const parent: any = Array.from(draft[parentId])[0];
4039+
while (parent.children.length) {
4040+
const id = parent.children[0];
4041+
parent.children.splice(0, 1);
4042+
delete draft[id]; // delete child object
4043+
}
4044+
4045+
// delete parent
4046+
draft[parentId].clear();
4047+
},
4048+
{ enablePatches: true }
4049+
);
4050+
const reverted = apply(newState, inverse);
4051+
expect(reverted).toEqual(myObj);
4052+
const reverted2 = apply(myObj, patchset);
4053+
expect(reverted2).toEqual(newState);
4054+
});
4055+
4056+
test('#59 - Failure to apply inverse patchset(Map)', () => {
4057+
const parentId = 'parent';
4058+
const childId = 'child';
4059+
4060+
const myObj = {
4061+
[parentId]: new Map([[0, { name: 'parent', children: [childId] }]]),
4062+
[childId]: { name: 'child' },
4063+
};
4064+
4065+
const [newState, patchset, inverse] = create(
4066+
myObj,
4067+
(draft: any) => {
4068+
// delete children
4069+
const parent: any = draft[parentId].get(0);
4070+
while (parent.children.length) {
4071+
const id = parent.children[0];
4072+
parent.children.splice(0, 1);
4073+
delete draft[id]; // delete child object
4074+
}
4075+
4076+
// delete parent
4077+
draft[parentId].clear();
4078+
},
4079+
{ enablePatches: true }
4080+
);
4081+
const reverted = apply(newState, inverse);
4082+
expect(reverted).toEqual(myObj);
4083+
const reverted2 = apply(myObj, patchset);
4084+
expect(reverted2).toEqual(newState);
4085+
});

website/blog/releases/1.0/index.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,23 @@ const state = create(baseState, (draft) => {
8181

8282
> Mutative passed all of Immer's test cases.
8383
84-
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.9 vs Immer v10.1.1]
84+
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.10 vs Immer v10.1.1]
8585

8686
![Benchmark](img/benchmark.jpg)
8787

8888
```
89-
Naive handcrafted reducer - No Freeze x 4,447 ops/sec ±0.85% (96 runs sampled)
90-
Mutative - No Freeze x 6,246 ops/sec ±1.29% (92 runs sampled)
91-
Immer - No Freeze x 5.26 ops/sec ±0.56% (18 runs sampled)
89+
Naive handcrafted reducer - No Freeze x 4,439 ops/sec ±0.65% (98 runs sampled)
90+
Mutative - No Freeze x 6,300 ops/sec ±1.19% (94 runs sampled)
91+
Immer - No Freeze x 5.26 ops/sec ±0.59% (18 runs sampled)
9292
93-
Mutative - Freeze x 950 ops/sec ±0.95% (96 runs sampled)
94-
Immer - Freeze x 377 ops/sec ±0.37% (93 runs sampled)
93+
Mutative - Freeze x 937 ops/sec ±1.25% (95 runs sampled)
94+
Immer - Freeze x 378 ops/sec ±0.66% (93 runs sampled)
9595
96-
Mutative - Patches and No Freeze x 978 ops/sec ±0.16% (97 runs sampled)
97-
Immer - Patches and No Freeze x 5.23 ops/sec ±0.25% (18 runs sampled)
96+
Mutative - Patches and No Freeze x 975 ops/sec ±0.17% (99 runs sampled)
97+
Immer - Patches and No Freeze x 5.29 ops/sec ±0.30% (18 runs sampled)
9898
99-
Mutative - Patches and Freeze x 504 ops/sec ±1.01% (94 runs sampled)
100-
Immer - Patches and Freeze x 272 ops/sec ±0.83% (89 runs sampled)
99+
Mutative - Patches and Freeze x 512 ops/sec ±0.85% (98 runs sampled)
100+
Immer - Patches and Freeze x 278 ops/sec ±0.57% (90 runs sampled)
101101
102102
The fastest method is Mutative - No Freeze
103103
```
@@ -108,7 +108,7 @@ Run `yarn benchmark` to measure performance.
108108
109109
Immer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.
110110

111-
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,246 ops/sec`) and Immer (`377 ops/sec`).
111+
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,300 ops/sec`) and Immer (`378 ops/sec`).
112112

113113
Overall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance).
114114

website/docs/extra-topics/comparison-with-immer.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,23 @@ Mutative has fewer bugs such as accidental draft escapes than Immer, [view detai
2525

2626
> Mutative passed all of Immer's test cases.
2727
28-
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.9 vs Immer v10.1.1]
28+
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.10 vs Immer v10.1.1]
2929

3030
![Benchmark](img/benchmark.jpg)
3131

3232
```
33-
Naive handcrafted reducer - No Freeze x 4,447 ops/sec ±0.85% (96 runs sampled)
34-
Mutative - No Freeze x 6,246 ops/sec ±1.29% (92 runs sampled)
35-
Immer - No Freeze x 5.26 ops/sec ±0.56% (18 runs sampled)
33+
Naive handcrafted reducer - No Freeze x 4,439 ops/sec ±0.65% (98 runs sampled)
34+
Mutative - No Freeze x 6,300 ops/sec ±1.19% (94 runs sampled)
35+
Immer - No Freeze x 5.26 ops/sec ±0.59% (18 runs sampled)
3636
37-
Mutative - Freeze x 950 ops/sec ±0.95% (96 runs sampled)
38-
Immer - Freeze x 377 ops/sec ±0.37% (93 runs sampled)
37+
Mutative - Freeze x 937 ops/sec ±1.25% (95 runs sampled)
38+
Immer - Freeze x 378 ops/sec ±0.66% (93 runs sampled)
3939
40-
Mutative - Patches and No Freeze x 978 ops/sec ±0.16% (97 runs sampled)
41-
Immer - Patches and No Freeze x 5.23 ops/sec ±0.25% (18 runs sampled)
40+
Mutative - Patches and No Freeze x 975 ops/sec ±0.17% (99 runs sampled)
41+
Immer - Patches and No Freeze x 5.29 ops/sec ±0.30% (18 runs sampled)
4242
43-
Mutative - Patches and Freeze x 504 ops/sec ±1.01% (94 runs sampled)
44-
Immer - Patches and Freeze x 272 ops/sec ±0.83% (89 runs sampled)
43+
Mutative - Patches and Freeze x 512 ops/sec ±0.85% (98 runs sampled)
44+
Immer - Patches and Freeze x 278 ops/sec ±0.57% (90 runs sampled)
4545
4646
The fastest method is Mutative - No Freeze
4747
```
@@ -52,7 +52,7 @@ Run `yarn benchmark` to measure performance.
5252
5353
Immer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.
5454

55-
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,246 ops/sec`) and Immer (`377 ops/sec`).
55+
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,300 ops/sec`) and Immer (`378 ops/sec`).
5656

5757
Overall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance). Run `yarn performance` to get all the performance results locally.
5858

website/docs/getting-started/performance.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,23 @@ const state = create(baseState, (draft) => {
6969

7070
> Mutative passed all of Immer's test cases.
7171
72-
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.9 vs Immer v10.1.1]
72+
Measure(ops/sec) to update 50K arrays and 1K objects, bigger is better([view source](https://github.com/unadlib/mutative/blob/main/test/performance/benchmark.ts)). [Mutative v1.0.10 vs Immer v10.1.1]
7373

7474
![Benchmark](img/benchmark.jpg)
7575

7676
```
77-
Naive handcrafted reducer - No Freeze x 4,447 ops/sec ±0.85% (96 runs sampled)
78-
Mutative - No Freeze x 6,246 ops/sec ±1.29% (92 runs sampled)
79-
Immer - No Freeze x 5.26 ops/sec ±0.56% (18 runs sampled)
77+
Naive handcrafted reducer - No Freeze x 4,439 ops/sec ±0.65% (98 runs sampled)
78+
Mutative - No Freeze x 6,300 ops/sec ±1.19% (94 runs sampled)
79+
Immer - No Freeze x 5.26 ops/sec ±0.59% (18 runs sampled)
8080
81-
Mutative - Freeze x 950 ops/sec ±0.95% (96 runs sampled)
82-
Immer - Freeze x 377 ops/sec ±0.37% (93 runs sampled)
81+
Mutative - Freeze x 937 ops/sec ±1.25% (95 runs sampled)
82+
Immer - Freeze x 378 ops/sec ±0.66% (93 runs sampled)
8383
84-
Mutative - Patches and No Freeze x 978 ops/sec ±0.16% (97 runs sampled)
85-
Immer - Patches and No Freeze x 5.23 ops/sec ±0.25% (18 runs sampled)
84+
Mutative - Patches and No Freeze x 975 ops/sec ±0.17% (99 runs sampled)
85+
Immer - Patches and No Freeze x 5.29 ops/sec ±0.30% (18 runs sampled)
8686
87-
Mutative - Patches and Freeze x 504 ops/sec ±1.01% (94 runs sampled)
88-
Immer - Patches and Freeze x 272 ops/sec ±0.83% (89 runs sampled)
87+
Mutative - Patches and Freeze x 512 ops/sec ±0.85% (98 runs sampled)
88+
Immer - Patches and Freeze x 278 ops/sec ±0.57% (90 runs sampled)
8989
9090
The fastest method is Mutative - No Freeze
9191
```
@@ -96,7 +96,7 @@ Run `yarn benchmark` to measure performance.
9696
9797
Immer relies on auto-freeze to be enabled, if auto-freeze is disabled, Immer will have a huge performance drop and Mutative will have a huge performance lead, especially with large data structures it will have a performance lead of more than 50x.
9898

99-
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,246 ops/sec`) and Immer (`377 ops/sec`).
99+
So if you are using Immer, you will have to enable auto-freeze for performance. Mutative is disabled auto-freeze by default. With the default configuration of both, we can see the 16x performance gap between Mutative (`6,300 ops/sec`) and Immer (`378 ops/sec`).
100100

101101
Overall, Mutative has a huge performance lead over Immer in [more performance testing scenarios](https://github.com/unadlib/mutative/tree/main/test/performance). Run `yarn performance` to get all the performance results locally.
102102

0 commit comments

Comments
 (0)