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 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: 2}});
82
- ctx.set('bar', {baz: 2});
83
- ctx.set('bar', 'baz', 2);
84
- ctx.get('bar').set({baz: 2});
85
- ctx.get('bar').set('baz', 2);
86
- ctx.get('bar', 'baz').set(2);
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": "5.0.0",
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.mjs",
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.222.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
- import clone from './clone.mjs';
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 STALE_VALUE = Object.freeze(Object.create(null));
10
+ const STATUS_NONE = 1;
11
+ const STATUS_CHANGED = 2;
12
+ const STATUS_REVOKED = 3;
13
+ const STATUS_STALE = 4;
16
14
 
17
- export default class CowContext {
18
- constructor(source, prop, root, parent) {
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
- _getPropValue(prop) {
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
- let source = this._source;
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 assign `STALE_VALUE` onto child contexts.
191
+ * ever set `STATUS_STALE` onto child contexts.
76
192
  */
77
- source = this._parent._getPropValue(this._prop);
78
- this._source = source;
193
+ this._source = this._parent._getPropValue(this._prop);
194
+ this._status = STATUS_NONE;
79
195
  }
80
- return source;
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._root, 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 origValue = this._getPropValue(prop);
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 = STALE_VALUE;
266
+ child._source = null;
152
267
  child._callbacks = [];
153
268
  child._result = null;
154
- child._status = STATUS_NONE;
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
- return this._root;
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, R extends object, ParentContext extends CowAnyContext<R> | null, Path extends ReadonlyArray<PropertyKey>> =
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], R, CowContext<T, R, ParentContext>, Extract<Rest, ReadonlyArray<PropertyKey>>>
14
+ ? NestedContext<T[First], CowContext<T, ParentContext>, Extract<Rest, ReadonlyArray<PropertyKey>>>
15
15
  : never
16
16
  )
17
- : CowContext<T, R, ParentContext>;
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 extends object> = CowContext<R, R, null>;
23
+ type CowRootContext<R> = CowContext<R, null>;
24
24
 
25
- type CowAnyContext<R extends object> =
26
- CowContext<unknown, R, CowAnyContext<R> | null>;
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
- out R extends object,
31
- out ParentContext extends CowAnyContext<R> | null = CowAnyContext<R> | null,
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, R, ParentContext, Path>;
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, R, ParentContext, Path>) => unknown]): this;
47
+ update<Path extends ReadonlyArray<PropertyKey>>(...args: [...Path, (childContext: NestedContext<T, ParentContext, Path>) => unknown]): this;
38
48
  parent(): ParentContext;
39
- root(): CowRootContext<R>;
49
+ root(): GetCowContextRoot<this>;
40
50
  revoke(): void;
41
51
  isRevoked(): boolean;
42
52
  final(): T;
43
- finalRoot(): R;
53
+ finalRoot(): GetCowContextSource<GetCowContextRoot<this>>;
44
54
  }
45
55
 
46
- declare function mutate<T extends object>(
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
- }