mutate-cow 5.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -7
- package/package.json +10 -6
- package/{CowContext.mjs → src/index.js} +149 -26
- package/src/index.js.flow +114 -0
- package/{index.d.ts → types/index.d.ts} +23 -13
- package/bench.mjs +0 -74
- package/canClone.mjs +0 -40
- package/clone.mjs +0 -99
- package/constants.mjs +0 -23
- package/index.mjs +0 -14
- package/index.mjs.flow +0 -140
- package/index.test-d.ts +0 -60
- package/test.mjs +0 -715
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ Passing zero arguments returns `ctx`.
|
|
|
67
67
|
```js
|
|
68
68
|
ctx.get() === ctx;
|
|
69
69
|
ctx.get('bar').read() === foo.bar;
|
|
70
|
-
ctx.get('bar', 'baz').read() ===
|
|
70
|
+
ctx.get('bar', 'baz').read().length === 0;
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
### ctx.set(...path: [prop1, ...], value)
|
|
@@ -77,13 +77,14 @@ Sets the given `path` to `value` on the current working copy. Returns `ctx`.
|
|
|
77
77
|
Passing zero property names (i.e., only a value) sets the current context's value.
|
|
78
78
|
|
|
79
79
|
```js
|
|
80
|
+
const qux = ['qux'];
|
|
80
81
|
// these all do the same thing
|
|
81
|
-
ctx.set({bar: {baz:
|
|
82
|
-
ctx.set('bar', {baz:
|
|
83
|
-
ctx.set('bar', 'baz',
|
|
84
|
-
ctx.get('bar').set({baz:
|
|
85
|
-
ctx.get('bar').set('baz',
|
|
86
|
-
ctx.get('bar', 'baz').set(
|
|
82
|
+
ctx.set({bar: {baz: qux}});
|
|
83
|
+
ctx.set('bar', {baz: qux});
|
|
84
|
+
ctx.set('bar', 'baz', qux);
|
|
85
|
+
ctx.get('bar').set({baz: qux});
|
|
86
|
+
ctx.get('bar').set('baz', qux);
|
|
87
|
+
ctx.get('bar', 'baz').set(qux);
|
|
87
88
|
```
|
|
88
89
|
|
|
89
90
|
### ctx.update(...path: [prop1, ...], updater)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mutate-cow",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "Update immutable objects as if they were mutable with copy-on-write",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"immutable",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"copy on write",
|
|
9
9
|
"state management"
|
|
10
10
|
],
|
|
11
|
-
"main": "index.
|
|
12
|
-
"types": "index.d.ts",
|
|
11
|
+
"main": "src/index.js",
|
|
12
|
+
"types": "types/index.d.ts",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"author": "Michael Wiencek <mwtuea@gmail.com>",
|
|
15
15
|
"repository": {
|
|
@@ -18,11 +18,15 @@
|
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"benchmark": "2.1.4",
|
|
21
|
-
"flow-bin": "0.
|
|
21
|
+
"flow-bin": "0.275.0",
|
|
22
|
+
"immer": "10.1.1",
|
|
23
|
+
"mutative": "1.2.0",
|
|
22
24
|
"tsd": "0.29.0"
|
|
23
25
|
},
|
|
24
26
|
"sideEffects": false,
|
|
27
|
+
"type": "module",
|
|
25
28
|
"scripts": {
|
|
26
|
-
"test": "node --test --experimental-test-coverage"
|
|
27
|
-
}
|
|
29
|
+
"test": "node --test --experimental-test-coverage test/index.js"
|
|
30
|
+
},
|
|
31
|
+
"packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c"
|
|
28
32
|
}
|
|
@@ -5,20 +5,130 @@
|
|
|
5
5
|
* in the file named "LICENSE" at the root directory of this distribution.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
STATUS_CHANGED,
|
|
11
|
-
STATUS_NONE,
|
|
12
|
-
STATUS_REVOKED,
|
|
13
|
-
} from './constants.mjs';
|
|
8
|
+
const NATIVE_CODE_REGEXP = /^function \w*\(\) \{\s*\[native code\]\s*\}$/m;
|
|
14
9
|
|
|
15
|
-
const
|
|
10
|
+
const STATUS_NONE = 1;
|
|
11
|
+
const STATUS_CHANGED = 2;
|
|
12
|
+
const STATUS_REVOKED = 3;
|
|
13
|
+
const STATUS_STALE = 4;
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
function isPrimitive(value) {
|
|
16
|
+
if (value === null) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
const type = typeof value;
|
|
20
|
+
return (type !== 'function' && type !== 'object');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getCloneableType(value) {
|
|
24
|
+
if (isPrimitive(value)) {
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
if (typeof value !== 'object') {
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
let proto = Object.getPrototypeOf(value);
|
|
31
|
+
while (proto) {
|
|
32
|
+
let ctor = proto.constructor;
|
|
33
|
+
// A Generator object's constructor is an object.
|
|
34
|
+
if (ctor && typeof ctor === 'object') {
|
|
35
|
+
ctor = ctor.constructor;
|
|
36
|
+
}
|
|
37
|
+
if (
|
|
38
|
+
typeof ctor === 'function' &&
|
|
39
|
+
ctor.name !== 'Array' &&
|
|
40
|
+
ctor.name !== 'Object' &&
|
|
41
|
+
NATIVE_CODE_REGEXP.test(Function.prototype.toString.call(ctor))
|
|
42
|
+
) {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
proto = Object.getPrototypeOf(proto);
|
|
46
|
+
}
|
|
47
|
+
return 2;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function throwIfTypeNotCloneable(cloneableType) {
|
|
51
|
+
if (cloneableType === 0) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
'Only plain objects, arrays, and class instances ' +
|
|
54
|
+
'can be cloned. Primitives, functions, and built-ins ' +
|
|
55
|
+
'are unsupported.',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function restoreDescriptors(copy, changedDescriptors) {
|
|
61
|
+
for (let i = 0; i < changedDescriptors.length; i++) {
|
|
62
|
+
const [name, origDesc] = changedDescriptors[i];
|
|
63
|
+
const descriptor = Object.getOwnPropertyDescriptor(copy, name);
|
|
64
|
+
if (descriptor) {
|
|
65
|
+
Object.assign(descriptor, origDesc);
|
|
66
|
+
Reflect.defineProperty(copy, name, descriptor);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function clone(source, callbacks) {
|
|
72
|
+
const cloneableType = getCloneableType(source);
|
|
73
|
+
throwIfTypeNotCloneable(cloneableType);
|
|
74
|
+
if (cloneableType === 1) {
|
|
75
|
+
return source;
|
|
76
|
+
}
|
|
77
|
+
const proto = Object.getPrototypeOf(source);
|
|
78
|
+
let changedDescriptors = [];
|
|
79
|
+
let copy;
|
|
80
|
+
if (Array.isArray(source)) {
|
|
81
|
+
copy = Reflect.construct(Array, source, proto.constructor);
|
|
82
|
+
} else {
|
|
83
|
+
copy = Object.create(proto);
|
|
84
|
+
}
|
|
85
|
+
const ownKeys = Reflect.ownKeys(source);
|
|
86
|
+
for (let i = 0; i < ownKeys.length; i++) {
|
|
87
|
+
const key = ownKeys[i];
|
|
88
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
89
|
+
let origDesc;
|
|
90
|
+
if (descriptor.configurable === false) {
|
|
91
|
+
descriptor.configurable = true;
|
|
92
|
+
origDesc = {configurable: false};
|
|
93
|
+
}
|
|
94
|
+
if (descriptor.writable === false) {
|
|
95
|
+
descriptor.writable = true;
|
|
96
|
+
origDesc = {...origDesc, writable: false};
|
|
97
|
+
}
|
|
98
|
+
if (origDesc) {
|
|
99
|
+
changedDescriptors.push([key, origDesc]);
|
|
100
|
+
}
|
|
101
|
+
Reflect.defineProperty(copy, key, descriptor);
|
|
102
|
+
}
|
|
103
|
+
if (changedDescriptors.length) {
|
|
104
|
+
callbacks.push({
|
|
105
|
+
func: restoreDescriptors,
|
|
106
|
+
args: [copy, changedDescriptors],
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (Object.isFrozen(source)) {
|
|
110
|
+
callbacks.push({
|
|
111
|
+
func: Object.freeze,
|
|
112
|
+
args: [copy],
|
|
113
|
+
});
|
|
114
|
+
} else if (Object.isSealed(source)) {
|
|
115
|
+
callbacks.push({
|
|
116
|
+
func: Object.seal,
|
|
117
|
+
args: [copy],
|
|
118
|
+
});
|
|
119
|
+
} else if (!Object.isExtensible(source)) {
|
|
120
|
+
callbacks.push({
|
|
121
|
+
func: Object.preventExtensions,
|
|
122
|
+
args: [copy],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return copy;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export class CowContext {
|
|
129
|
+
constructor(source, prop, parent) {
|
|
19
130
|
this._source = source;
|
|
20
131
|
this._prop = prop;
|
|
21
|
-
this._root = root || this;
|
|
22
132
|
this._parent = parent;
|
|
23
133
|
this._callbacks = [];
|
|
24
134
|
this._result = null;
|
|
@@ -52,8 +162,7 @@ export default class CowContext {
|
|
|
52
162
|
}
|
|
53
163
|
}
|
|
54
164
|
|
|
55
|
-
|
|
56
|
-
const target = this.read();
|
|
165
|
+
_getPropDescriptor(target, prop) {
|
|
57
166
|
const descriptor = Reflect.getOwnPropertyDescriptor(target, prop);
|
|
58
167
|
if (descriptor) {
|
|
59
168
|
if (descriptor.get) {
|
|
@@ -62,22 +171,29 @@ export default class CowContext {
|
|
|
62
171
|
if (descriptor.set) {
|
|
63
172
|
throw new Error('Setters are unsupported.');
|
|
64
173
|
}
|
|
174
|
+
return descriptor;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_getPropValue(prop) {
|
|
179
|
+
const target = this.read();
|
|
180
|
+
const descriptor = this._getPropDescriptor(target, prop);
|
|
181
|
+
if (descriptor) {
|
|
65
182
|
return descriptor.value;
|
|
66
183
|
}
|
|
67
184
|
return Reflect.get(target, prop);
|
|
68
185
|
}
|
|
69
186
|
|
|
70
187
|
_getSource() {
|
|
71
|
-
|
|
72
|
-
if (source === STALE_VALUE) {
|
|
188
|
+
if (this._status === STATUS_STALE) {
|
|
73
189
|
/*
|
|
74
190
|
* `this._parent` should always be defined here, because we only
|
|
75
|
-
* ever
|
|
191
|
+
* ever set `STATUS_STALE` onto child contexts.
|
|
76
192
|
*/
|
|
77
|
-
|
|
78
|
-
this.
|
|
193
|
+
this._source = this._parent._getPropValue(this._prop);
|
|
194
|
+
this._status = STATUS_NONE;
|
|
79
195
|
}
|
|
80
|
-
return
|
|
196
|
+
return this._source;
|
|
81
197
|
}
|
|
82
198
|
|
|
83
199
|
_throwIfRevoked() {
|
|
@@ -113,7 +229,7 @@ export default class CowContext {
|
|
|
113
229
|
return child;
|
|
114
230
|
}
|
|
115
231
|
|
|
116
|
-
child = new CowContext(value, prop, this
|
|
232
|
+
child = new CowContext(value, prop, this);
|
|
117
233
|
children.set(prop, child);
|
|
118
234
|
return child;
|
|
119
235
|
}
|
|
@@ -136,9 +252,8 @@ export default class CowContext {
|
|
|
136
252
|
}
|
|
137
253
|
|
|
138
254
|
_set(prop, newValue) {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
if (!Object.is(origValue, newValue)) {
|
|
255
|
+
const descriptor = this._getPropDescriptor(this.read(), prop);
|
|
256
|
+
if (descriptor === undefined || !Object.is(descriptor.value, newValue)) {
|
|
142
257
|
this._copyForWrite();
|
|
143
258
|
this._result[prop] = newValue;
|
|
144
259
|
|
|
@@ -148,10 +263,10 @@ export default class CowContext {
|
|
|
148
263
|
if (children) {
|
|
149
264
|
const child = children.get(prop);
|
|
150
265
|
if (child) {
|
|
151
|
-
child._source =
|
|
266
|
+
child._source = null;
|
|
152
267
|
child._callbacks = [];
|
|
153
268
|
child._result = null;
|
|
154
|
-
child._status =
|
|
269
|
+
child._status = STATUS_STALE;
|
|
155
270
|
}
|
|
156
271
|
}
|
|
157
272
|
}
|
|
@@ -186,7 +301,11 @@ export default class CowContext {
|
|
|
186
301
|
|
|
187
302
|
root() {
|
|
188
303
|
this._throwIfRevoked();
|
|
189
|
-
|
|
304
|
+
let root = this;
|
|
305
|
+
while (root._parent !== null) {
|
|
306
|
+
root = root._parent;
|
|
307
|
+
}
|
|
308
|
+
return root;
|
|
190
309
|
}
|
|
191
310
|
|
|
192
311
|
revoke() {
|
|
@@ -204,7 +323,6 @@ export default class CowContext {
|
|
|
204
323
|
}
|
|
205
324
|
this._source = null;
|
|
206
325
|
this._prop = null;
|
|
207
|
-
this._root = null;
|
|
208
326
|
this._parent = null;
|
|
209
327
|
this._callbacks = null;
|
|
210
328
|
this._result = null;
|
|
@@ -236,3 +354,8 @@ export default class CowContext {
|
|
|
236
354
|
return this.root().final();
|
|
237
355
|
}
|
|
238
356
|
}
|
|
357
|
+
|
|
358
|
+
export default function mutate(source) {
|
|
359
|
+
throwIfTypeNotCloneable(getCloneableType(source));
|
|
360
|
+
return new CowContext(source, null, null);
|
|
361
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
export type KeyType<+T> =
|
|
4
|
+
T extends $ReadOnlyArray<mixed> ? number :
|
|
5
|
+
T extends ({__proto__: null, ...} | interface {}) ? $Keys<T> :
|
|
6
|
+
empty;
|
|
7
|
+
export type KeyType2<+T, +K1: KeyType<T>> = KeyType<PropType<T, K1>>;
|
|
8
|
+
export type KeyType3<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>> = KeyType<PropType2<T, K1, K2>>;
|
|
9
|
+
export type KeyType4<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>> = KeyType<PropType3<T, K1, K2, K3>>;
|
|
10
|
+
export type KeyType5<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>> = KeyType<PropType4<T, K1, K2, K3, K4>>;
|
|
11
|
+
export type KeyType6<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>, +K5: KeyType5<T, K1, K2, K3, K4>> = KeyType<PropType5<T, K1, K2, K3, K4, K5>>;
|
|
12
|
+
export type KeyType7<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>, +K5: KeyType5<T, K1, K2, K3, K4>, +K6: KeyType6<T, K1, K2, K3, K4, K5>> = KeyType<PropType6<T, K1, K2, K3, K4, K5, K6>>;
|
|
13
|
+
export type KeyType8<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>, +K5: KeyType5<T, K1, K2, K3, K4>, +K6: KeyType6<T, K1, K2, K3, K4, K5>, +K7: KeyType7<T, K1, K2, K3, K4, K5, K6>> = KeyType<PropType7<T, K1, K2, K3, K4, K5, K6, K7>>;
|
|
14
|
+
|
|
15
|
+
export type PropType<+T, +K: KeyType<T>> =
|
|
16
|
+
T extends $ReadOnlyArray<infer V> ? V :
|
|
17
|
+
T extends ({__proto__: null, ...} | interface {}) ? T[K] :
|
|
18
|
+
empty;
|
|
19
|
+
export type PropType2<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>> =
|
|
20
|
+
PropType<PropType<T, K1>, K2>;
|
|
21
|
+
export type PropType3<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>> =
|
|
22
|
+
PropType<PropType2<T, K1, K2>, K3>;
|
|
23
|
+
export type PropType4<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>> =
|
|
24
|
+
PropType<PropType3<T, K1, K2, K3>, K4>;
|
|
25
|
+
export type PropType5<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>, +K5: KeyType5<T, K1, K2, K3, K4>> =
|
|
26
|
+
PropType<PropType4<T, K1, K2, K3, K4>, K5>;
|
|
27
|
+
export type PropType6<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>, +K5: KeyType5<T, K1, K2, K3, K4>, +K6: KeyType6<T, K1, K2, K3, K4, K5>> =
|
|
28
|
+
PropType<PropType5<T, K1, K2, K3, K4, K5>, K6>;
|
|
29
|
+
export type PropType7<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>, +K5: KeyType5<T, K1, K2, K3, K4>, +K6: KeyType6<T, K1, K2, K3, K4, K5>, +K7: KeyType7<T, K1, K2, K3, K4, K5, K6>> =
|
|
30
|
+
PropType<PropType6<T, K1, K2, K3, K4, K5, K6>, K7>;
|
|
31
|
+
export type PropType8<+T, +K1: KeyType<T>, +K2: KeyType2<T, K1>, +K3: KeyType3<T, K1, K2>, +K4: KeyType4<T, K1, K2, K3>, +K5: KeyType5<T, K1, K2, K3, K4>, +K6: KeyType6<T, K1, K2, K3, K4, K5>, +K7: KeyType7<T, K1, K2, K3, K4, K5, K6>, +K8: KeyType8<T, K1, K2, K3, K4, K5, K6, K7>> =
|
|
32
|
+
PropType<PropType7<T, K1, K2, K3, K4, K5, K6, K7>, K8>;
|
|
33
|
+
|
|
34
|
+
export type ShallowReadWrite<+T> =
|
|
35
|
+
T extends $ReadOnlyArray<infer V> ? Array<V> :
|
|
36
|
+
T extends {__proto__: null, ...} ? {__proto__: null, ...T} :
|
|
37
|
+
T extends {...} ? {...T} :
|
|
38
|
+
T;
|
|
39
|
+
|
|
40
|
+
export type CowRootContext<+R> = CowContext<R, null>;
|
|
41
|
+
|
|
42
|
+
export type CowAnyContext = CowContext<mixed, CowAnyContext | null>;
|
|
43
|
+
|
|
44
|
+
type $Get1<+T, +K1, +ThisContext> =
|
|
45
|
+
CowContext<PropType<T, K1>, ThisContext>;
|
|
46
|
+
type $Get2<+T, +K1, +K2, +ThisContext> =
|
|
47
|
+
CowContext<PropType2<T, K1, K2>, $Get1<T, K1, ThisContext>>;
|
|
48
|
+
type $Get3<+T, +K1, +K2, +K3, +ThisContext> =
|
|
49
|
+
CowContext<PropType3<T, K1, K2, K3>, $Get2<T, K1, K2, ThisContext>>;
|
|
50
|
+
type $Get4<+T, +K1, +K2, +K3, +K4, +ThisContext> =
|
|
51
|
+
CowContext<PropType4<T, K1, K2, K3, K4>, $Get3<T, K1, K2, K3, ThisContext>>;
|
|
52
|
+
type $Get5<+T, +K1, +K2, +K3, +K4, +K5, +ThisContext> =
|
|
53
|
+
CowContext<PropType5<T, K1, K2, K3, K4, K5>, $Get4<T, K1, K2, K3, K4, ThisContext>>;
|
|
54
|
+
type $Get6<+T, +K1, +K2, +K3, +K4, +K5, +K6, +ThisContext> =
|
|
55
|
+
CowContext<PropType6<T, K1, K2, K3, K4, K5, K6>, $Get5<T, K1, K2, K3, K4, K5, ThisContext>>;
|
|
56
|
+
type $Get7<+T, +K1, +K2, +K3, +K4, +K5, +K6, +K7, +ThisContext> =
|
|
57
|
+
CowContext<PropType7<T, K1, K2, K3, K4, K5, K6, K7>, $Get6<T, K1, K2, K3, K4, K5, K6, ThisContext>>;
|
|
58
|
+
type $Get8<+T, +K1, +K2, +K3, +K4, +K5, +K6, +K7, +K8, +ThisContext> =
|
|
59
|
+
CowContext<PropType8<T, K1, K2, K3, K4, K5, K6, K7, K8>, $Get7<T, K1, K2, K3, K4, K5, K6, K7, ThisContext>>;
|
|
60
|
+
|
|
61
|
+
export type GetCowContextSource<+C> =
|
|
62
|
+
C extends CowContext<infer T, mixed>
|
|
63
|
+
? T
|
|
64
|
+
: empty;
|
|
65
|
+
|
|
66
|
+
export type GetCowContextRoot<+C> =
|
|
67
|
+
C extends CowContext<infer T, infer P>
|
|
68
|
+
? (P extends null ? C : GetCowContextRoot<P>)
|
|
69
|
+
: empty;
|
|
70
|
+
|
|
71
|
+
declare class CowContext<
|
|
72
|
+
+T,
|
|
73
|
+
+ParentContext: CowAnyContext | null = CowAnyContext | null,
|
|
74
|
+
>{
|
|
75
|
+
read(): T;
|
|
76
|
+
write(): ShallowReadWrite<T>;
|
|
77
|
+
get(): this;
|
|
78
|
+
get<const K1: KeyType<T>>(prop1: K1): $Get1<T, K1, this>;
|
|
79
|
+
get<const K1: KeyType<T>, const K2: KeyType2<T, K1>>(prop1: K1, prop2: K2): $Get2<T, K1, K2, this>;
|
|
80
|
+
get<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>>(prop1: K1, prop2: K2, prop3: K3): $Get3<T, K1, K2, K3, this>;
|
|
81
|
+
get<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4): $Get4<T, K1, K2, K3, K4, this>;
|
|
82
|
+
get<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5): $Get5<T, K1, K2, K3, K4, K5, this>;
|
|
83
|
+
get<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6): $Get6<T, K1, K2, K3, K4, K5, K6, this>;
|
|
84
|
+
get<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>, const K7: KeyType7<T, K1, K2, K3, K4, K5, K6>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, prop7: K7): $Get7<T, K1, K2, K3, K4, K5, K6, K7, this>;
|
|
85
|
+
get<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>, const K7: KeyType7<T, K1, K2, K3, K4, K5, K6>, const K8: KeyType8<T, K1, K2, K3, K4, K5, K6, K7>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, prop7: K7, prop8: K8): $Get8<T, K1, K2, K3, K4, K5, K6, K7, K8, this>;
|
|
86
|
+
set(newValue: T): this;
|
|
87
|
+
set<const K1: KeyType<T>>(prop1: K1, newValue: PropType<T, K1>): this;
|
|
88
|
+
set<const K1: KeyType<T>, const K2: KeyType2<T, K1>>(prop1: K1, prop2: K2, newValue: PropType2<T, K1, K2>): this;
|
|
89
|
+
set<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>>(prop1: K1, prop2: K2, prop3: K3, newValue: PropType3<T, K1, K2, K3>): this;
|
|
90
|
+
set<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, newValue: PropType4<T, K1, K2, K3, K4>): this;
|
|
91
|
+
set<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, newValue: PropType5<T, K1, K2, K3, K4, K5>): this;
|
|
92
|
+
set<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, newValue: PropType6<T, K1, K2, K3, K4, K5, K6>): this;
|
|
93
|
+
set<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>, const K7: KeyType7<T, K1, K2, K3, K4, K5, K6>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, prop7: K7, newValue: PropType7<T, K1, K2, K3, K4, K5, K6, K7>): this;
|
|
94
|
+
set<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>, const K7: KeyType7<T, K1, K2, K3, K4, K5, K6>, const K8: KeyType8<T, K1, K2, K3, K4, K5, K6, K7>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, prop7: K7, prop8: K8, newValue: PropType8<T, K1, K2, K3, K4, K5, K6, K7, K8>): this;
|
|
95
|
+
update(updater: (this) => mixed): this;
|
|
96
|
+
update<const K1: KeyType<T>>(prop1: K1, updater: ($Get1<T, K1, this>) => mixed): this;
|
|
97
|
+
update<const K1: KeyType<T>, const K2: KeyType2<T, K1>>(prop1: K1, prop2: K2, updater: ($Get2<T, K1, K2, this>) => mixed): this;
|
|
98
|
+
update<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>>(prop1: K1, prop2: K2, prop3: K3, updater: ($Get3<T, K1, K2, K3, this>) => mixed): this;
|
|
99
|
+
update<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, updater: ($Get4<T, K1, K2, K3, K4, this>) => mixed): this;
|
|
100
|
+
update<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, updater: ($Get5<T, K1, K2, K3, K4, K5, this>) => mixed): this;
|
|
101
|
+
update<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, updater: ($Get6<T, K1, K2, K3, K4, K5, K6, this>) => mixed): this;
|
|
102
|
+
update<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>, const K7: KeyType7<T, K1, K2, K3, K4, K5, K6>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, prop7: K7, updater: ($Get7<T, K1, K2, K3, K4, K5, K6, K7, this>) => mixed): this;
|
|
103
|
+
update<const K1: KeyType<T>, const K2: KeyType2<T, K1>, const K3: KeyType3<T, K1, K2>, const K4: KeyType4<T, K1, K2, K3>, const K5: KeyType5<T, K1, K2, K3, K4>, const K6: KeyType6<T, K1, K2, K3, K4, K5>, const K7: KeyType7<T, K1, K2, K3, K4, K5, K6>, const K8: KeyType8<T, K1, K2, K3, K4, K5, K6, K7>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, prop7: K7, prop8: K8, updater: ($Get8<T, K1, K2, K3, K4, K5, K6, K7, K8, this>) => mixed): this;
|
|
104
|
+
parent(): ParentContext;
|
|
105
|
+
root(): GetCowContextRoot<this>;
|
|
106
|
+
revoke(): void;
|
|
107
|
+
isRevoked(): boolean;
|
|
108
|
+
final(): T;
|
|
109
|
+
finalRoot(): GetCowContextSource<GetCowContextRoot<this>>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
declare export default function mutate<T>(
|
|
113
|
+
source: T,
|
|
114
|
+
): CowRootContext<T>;
|
|
@@ -7,43 +7,53 @@ type NestedProp<T, Path extends ReadonlyArray<PropertyKey>> =
|
|
|
7
7
|
)
|
|
8
8
|
: T;
|
|
9
9
|
|
|
10
|
-
type NestedContext<T,
|
|
10
|
+
type NestedContext<T, ParentContext extends CowAnyContext | null, Path extends ReadonlyArray<PropertyKey>> =
|
|
11
11
|
Path extends [infer First, ...infer Rest]
|
|
12
12
|
? (
|
|
13
13
|
First extends keyof T
|
|
14
|
-
? NestedContext<T[First],
|
|
14
|
+
? NestedContext<T[First], CowContext<T, ParentContext>, Extract<Rest, ReadonlyArray<PropertyKey>>>
|
|
15
15
|
: never
|
|
16
16
|
)
|
|
17
|
-
: CowContext<T,
|
|
17
|
+
: CowContext<T, ParentContext>;
|
|
18
18
|
|
|
19
19
|
type ShallowReadWrite<T> =
|
|
20
20
|
T extends ReadonlyArray<infer V> ? Array<V> :
|
|
21
21
|
T extends object ? {-readonly [K in keyof T]: T[K]} : never;
|
|
22
22
|
|
|
23
|
-
type CowRootContext<R
|
|
23
|
+
type CowRootContext<R> = CowContext<R, null>;
|
|
24
24
|
|
|
25
|
-
type CowAnyContext
|
|
26
|
-
CowContext<unknown,
|
|
25
|
+
type CowAnyContext =
|
|
26
|
+
CowContext<unknown, CowAnyContext | null>;
|
|
27
|
+
|
|
28
|
+
type GetCowContextSource<C> =
|
|
29
|
+
C extends CowContext<infer T, CowAnyContext | null>
|
|
30
|
+
? T
|
|
31
|
+
: never;
|
|
32
|
+
|
|
33
|
+
type GetCowContextRoot<C> =
|
|
34
|
+
C extends CowContext<infer T, infer P>
|
|
35
|
+
? (P extends null ? C : GetCowContextRoot<P>)
|
|
36
|
+
: never;
|
|
27
37
|
|
|
28
38
|
declare class CowContext<
|
|
29
39
|
out T,
|
|
30
|
-
|
|
31
|
-
out ParentContext extends CowAnyContext
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
out ParentContext extends CowAnyContext | null = CowAnyContext | null,
|
|
32
42
|
> {
|
|
33
43
|
read(): T;
|
|
34
44
|
write(): ShallowReadWrite<T>;
|
|
35
|
-
get<Path extends ReadonlyArray<PropertyKey>>(...path: Path): NestedContext<T,
|
|
45
|
+
get<Path extends ReadonlyArray<PropertyKey>>(...path: Path): NestedContext<T, ParentContext, Path>;
|
|
36
46
|
set<Path extends ReadonlyArray<PropertyKey>>(...args: [...Path, NestedProp<T, Path>]): this;
|
|
37
|
-
update<Path extends ReadonlyArray<PropertyKey>>(...args: [...Path, (childContext: NestedContext<T,
|
|
47
|
+
update<Path extends ReadonlyArray<PropertyKey>>(...args: [...Path, (childContext: NestedContext<T, ParentContext, Path>) => unknown]): this;
|
|
38
48
|
parent(): ParentContext;
|
|
39
|
-
root():
|
|
49
|
+
root(): GetCowContextRoot<this>;
|
|
40
50
|
revoke(): void;
|
|
41
51
|
isRevoked(): boolean;
|
|
42
52
|
final(): T;
|
|
43
|
-
finalRoot():
|
|
53
|
+
finalRoot(): GetCowContextSource<GetCowContextRoot<this>>;
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
declare function mutate<T
|
|
56
|
+
declare function mutate<T>(
|
|
47
57
|
source: T,
|
|
48
58
|
): CowRootContext<T>;
|
|
49
59
|
|
package/bench.mjs
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2023 Michael Wiencek
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license. A copy can be found
|
|
5
|
-
* in the file named "LICENSE" at the root directory of this distribution.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import Benchmark from 'benchmark';
|
|
9
|
-
import mutate from './index.mjs';
|
|
10
|
-
|
|
11
|
-
const root = {};
|
|
12
|
-
for (let i = 65, next = root; i <= 90; i++) {
|
|
13
|
-
next = next[String.fromCharCode(i)] = {
|
|
14
|
-
prop1: 1,
|
|
15
|
-
prop2: 2,
|
|
16
|
-
prop3: 3,
|
|
17
|
-
prop4: 4,
|
|
18
|
-
prop5: 5,
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const updater1 = () => {
|
|
23
|
-
mutate(root).set('root', true).final();
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const updater2 = () => {
|
|
27
|
-
mutate(root)
|
|
28
|
-
.set(
|
|
29
|
-
'A',
|
|
30
|
-
'B',
|
|
31
|
-
'C',
|
|
32
|
-
'D',
|
|
33
|
-
'E',
|
|
34
|
-
'F',
|
|
35
|
-
'G',
|
|
36
|
-
'H',
|
|
37
|
-
'I',
|
|
38
|
-
'J',
|
|
39
|
-
'K',
|
|
40
|
-
'L',
|
|
41
|
-
'M',
|
|
42
|
-
'N',
|
|
43
|
-
'O',
|
|
44
|
-
'P',
|
|
45
|
-
'Q',
|
|
46
|
-
'R',
|
|
47
|
-
'S',
|
|
48
|
-
'T',
|
|
49
|
-
'U',
|
|
50
|
-
'V',
|
|
51
|
-
'W',
|
|
52
|
-
'X',
|
|
53
|
-
'Y',
|
|
54
|
-
'Z',
|
|
55
|
-
'leaf',
|
|
56
|
-
true,
|
|
57
|
-
)
|
|
58
|
-
.final();
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
function test() {
|
|
62
|
-
updater1(); // shallow update
|
|
63
|
-
updater2(); // deep update
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
new Benchmark.Suite()
|
|
67
|
-
.add('test', test)
|
|
68
|
-
.on('cycle', function (event) {
|
|
69
|
-
console.log(String(event.target));
|
|
70
|
-
})
|
|
71
|
-
.on('complete', function () {
|
|
72
|
-
console.log('Fastest is ' + this.filter('fastest').map('name'));
|
|
73
|
-
})
|
|
74
|
-
.run({async: true});
|
package/canClone.mjs
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright (c) 2023 Michael Wiencek
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license. A copy can be found
|
|
5
|
-
* in the file named "LICENSE" at the root directory of this distribution.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const funcToString = Function.prototype.toString;
|
|
9
|
-
|
|
10
|
-
const nativeCodeRegExp = /^function \w*\(\) \{\s*\[native code\]\s*\}$/m;
|
|
11
|
-
|
|
12
|
-
export const CANNOT_CLONE_ERROR =
|
|
13
|
-
'Only plain objects, arrays, and class instances ' +
|
|
14
|
-
'can be cloned. Primitives, functions, and built-ins ' +
|
|
15
|
-
'are unsupported.';
|
|
16
|
-
|
|
17
|
-
export default function canClone(object) {
|
|
18
|
-
if (!object || typeof object !== 'object') {
|
|
19
|
-
throw new Error(CANNOT_CLONE_ERROR);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
let proto = Reflect.getPrototypeOf(object);
|
|
23
|
-
|
|
24
|
-
while (proto) {
|
|
25
|
-
let ctor = proto.constructor;
|
|
26
|
-
// A Generator object's constructor is an object.
|
|
27
|
-
if (ctor && typeof ctor === 'object') {
|
|
28
|
-
ctor = ctor.constructor;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (typeof ctor === 'function' &&
|
|
32
|
-
ctor.name !== 'Array' &&
|
|
33
|
-
ctor.name !== 'Object' &&
|
|
34
|
-
nativeCodeRegExp.test(funcToString.call(ctor))) {
|
|
35
|
-
throw new Error(CANNOT_CLONE_ERROR);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
proto = Reflect.getPrototypeOf(proto);
|
|
39
|
-
}
|
|
40
|
-
}
|