mutate-cow 5.0.0 → 6.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.
@@ -5,12 +5,12 @@
5
5
  * in the file named "LICENSE" at the root directory of this distribution.
6
6
  */
7
7
 
8
- import clone from './clone.mjs';
8
+ import clone from './clone.js';
9
9
  import {
10
10
  STATUS_CHANGED,
11
11
  STATUS_NONE,
12
12
  STATUS_REVOKED,
13
- } from './constants.mjs';
13
+ } from './constants.js';
14
14
 
15
15
  const STALE_VALUE = Object.freeze(Object.create(null));
16
16
 
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)
@@ -5,12 +5,12 @@
5
5
  * in the file named "LICENSE" at the root directory of this distribution.
6
6
  */
7
7
 
8
- import canClone from './canClone.mjs';
8
+ import canClone from './canClone.js';
9
9
  import {
10
10
  NON_CONFIGURABLE,
11
11
  NON_CONFIGURABLE_AND_WRITABLE,
12
12
  NON_WRITABLE,
13
- } from './constants.mjs';
13
+ } from './constants.js';
14
14
 
15
15
  function isPrimitive(value) {
16
16
  switch (typeof value) {
@@ -5,8 +5,8 @@
5
5
  * in the file named "LICENSE" at the root directory of this distribution.
6
6
  */
7
7
 
8
- import canClone from './canClone.mjs';
9
- import CowContext from './CowContext.mjs';
8
+ import canClone from './canClone.js';
9
+ import CowContext from './CowContext.js';
10
10
 
11
11
  export default function mutate(source) {
12
12
  canClone(source);
@@ -101,32 +101,32 @@ declare export class CowContext<
101
101
  read(): T;
102
102
  write(): ShallowReadWrite<T>;
103
103
  get(): this;
104
- get<K1: KeyType<T>>(prop1: K1): $Get1<T, K1, R, this>;
105
- get<K1: KeyType<T>, K2: KeyType2<T, K1>>(prop1: K1, prop2: K2): $Get2<T, K1, K2, R, this>;
106
- get<K1: KeyType<T>, K2: KeyType2<T, K1>, K3: KeyType3<T, K1, K2>>(prop1: K1, prop2: K2, prop3: K3): $Get3<T, K1, K2, K3, R, this>;
107
- get<K1: KeyType<T>, K2: KeyType2<T, K1>, K3: KeyType3<T, K1, K2>, K4: KeyType4<T, K1, K2, K3>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4): $Get4<T, K1, K2, K3, K4, R, this>;
108
- get<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>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5): $Get5<T, K1, K2, K3, K4, K5, R, this>;
109
- get<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>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6): $Get6<T, K1, K2, K3, K4, K5, K6, R, this>;
110
- get<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>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, prop7: K7): $Get7<T, K1, K2, K3, K4, K5, K6, K7, R, this>;
111
- get<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>>(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, R, this>;
104
+ get<const K1: KeyType<T>>(prop1: K1): $Get1<T, K1, R, this>;
105
+ get<const K1: KeyType<T>, const K2: KeyType2<T, K1>>(prop1: K1, prop2: K2): $Get2<T, K1, K2, R, this>;
106
+ 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, R, this>;
107
+ 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, R, this>;
108
+ 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, R, this>;
109
+ 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, R, this>;
110
+ 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, R, this>;
111
+ 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, R, this>;
112
112
  set(newValue: T): this;
113
- set<K1: KeyType<T>>(prop1: K1, newValue: PropType<T, K1>): this;
114
- set<K1: KeyType<T>, K2: KeyType2<T, K1>>(prop1: K1, prop2: K2, newValue: PropType2<T, K1, K2>): this;
115
- set<K1: KeyType<T>, K2: KeyType2<T, K1>, K3: KeyType3<T, K1, K2>>(prop1: K1, prop2: K2, prop3: K3, newValue: PropType3<T, K1, K2, K3>): this;
116
- set<K1: KeyType<T>, K2: KeyType2<T, K1>, K3: KeyType3<T, K1, K2>, K4: KeyType4<T, K1, K2, K3>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, newValue: PropType4<T, K1, K2, K3, K4>): this;
117
- set<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>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, newValue: PropType5<T, K1, K2, K3, K4, K5>): this;
118
- set<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>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, newValue: PropType6<T, K1, K2, K3, K4, K5, K6>): this;
119
- set<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>>(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;
120
- set<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>>(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;
113
+ set<const K1: KeyType<T>>(prop1: K1, newValue: PropType<T, K1>): this;
114
+ set<const K1: KeyType<T>, const K2: KeyType2<T, K1>>(prop1: K1, prop2: K2, newValue: PropType2<T, K1, K2>): this;
115
+ 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;
116
+ 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;
117
+ 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;
118
+ 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;
119
+ 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;
120
+ 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;
121
121
  update(updater: (this) => mixed): this;
122
- update<K1: KeyType<T>>(prop1: K1, updater: ($Get1<T, K1, R, this>) => mixed): this;
123
- update<K1: KeyType<T>, K2: KeyType2<T, K1>>(prop1: K1, prop2: K2, updater: ($Get2<T, K1, K2, R, this>) => mixed): this;
124
- update<K1: KeyType<T>, K2: KeyType2<T, K1>, K3: KeyType3<T, K1, K2>>(prop1: K1, prop2: K2, prop3: K3, updater: ($Get3<T, K1, K2, K3, R, this>) => mixed): this;
125
- update<K1: KeyType<T>, K2: KeyType2<T, K1>, K3: KeyType3<T, K1, K2>, K4: KeyType4<T, K1, K2, K3>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, updater: ($Get4<T, K1, K2, K3, K4, R, this>) => mixed): this;
126
- update<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>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, updater: ($Get5<T, K1, K2, K3, K4, K5, R, this>) => mixed): this;
127
- update<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>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, updater: ($Get6<T, K1, K2, K3, K4, K5, K6, R, this>) => mixed): this;
128
- update<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>>(prop1: K1, prop2: K2, prop3: K3, prop4: K4, prop5: K5, prop6: K6, prop7: K7, updater: ($Get7<T, K1, K2, K3, K4, K5, K6, K7, R, this>) => mixed): this;
129
- update<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>>(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, R, this>) => mixed): this;
122
+ update<const K1: KeyType<T>>(prop1: K1, updater: ($Get1<T, K1, R, this>) => mixed): this;
123
+ update<const K1: KeyType<T>, const K2: KeyType2<T, K1>>(prop1: K1, prop2: K2, updater: ($Get2<T, K1, K2, R, this>) => mixed): this;
124
+ 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, R, this>) => mixed): this;
125
+ 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, R, this>) => mixed): this;
126
+ 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, R, this>) => mixed): this;
127
+ 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, R, this>) => mixed): this;
128
+ 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, R, this>) => mixed): this;
129
+ 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, R, this>) => mixed): this;
130
130
  parent(): ParentContext;
131
131
  root(): CowRootContext<R>;
132
132
  revoke(): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mutate-cow",
3
- "version": "5.0.0",
3
+ "version": "6.0.0",
4
4
  "description": "Update immutable objects as if they were mutable with copy-on-write",
5
5
  "keywords": [
6
6
  "immutable",
@@ -8,7 +8,7 @@
8
8
  "copy on write",
9
9
  "state management"
10
10
  ],
11
- "main": "index.mjs",
11
+ "main": "index.js",
12
12
  "types": "index.d.ts",
13
13
  "license": "MIT",
14
14
  "author": "Michael Wiencek <mwtuea@gmail.com>",
@@ -18,11 +18,13 @@
18
18
  },
19
19
  "devDependencies": {
20
20
  "benchmark": "2.1.4",
21
- "flow-bin": "0.222.0",
21
+ "flow-bin": "0.273.1",
22
22
  "tsd": "0.29.0"
23
23
  },
24
24
  "sideEffects": false,
25
+ "type": "module",
25
26
  "scripts": {
26
27
  "test": "node --test --experimental-test-coverage"
27
- }
28
+ },
29
+ "packageManager": "yarn@4.9.2+sha512.1fc009bc09d13cfd0e19efa44cbfc2b9cf6ca61482725eb35bbc5e257e093ebf4130db6dfe15d604ff4b79efd8e1e8e99b25fa7d0a6197c9f9826358d4d65c3c"
28
30
  }
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/index.test-d.ts DELETED
@@ -1,60 +0,0 @@
1
- import {
2
- expectType,
3
- expectNotType,
4
- expectError,
5
- } from 'tsd';
6
-
7
- import mutate, {
8
- type CowContext,
9
- type CowRootContext,
10
- } from './index';
11
-
12
- interface RoRoot {
13
- readonly obj: RoValueContainer;
14
- readonly arr: ReadonlyArray<RoValueContainer>;
15
- }
16
-
17
- interface RwRoot {
18
- obj: RoValueContainer;
19
- arr: ReadonlyArray<RoValueContainer>;
20
- }
21
-
22
- interface RoValueContainer {
23
- readonly value: string;
24
- readonly nested: RoValueContainer;
25
- }
26
-
27
- const _root: any = {
28
- obj: {
29
- value: '',
30
- nested: null,
31
- },
32
- arr: [],
33
- };
34
- _root.obj.nested = _root.obj;
35
- const root: RoRoot = _root;
36
-
37
- const ctx = mutate(root);
38
-
39
- expectType<RoRoot>(ctx.read());
40
- expectNotType<RwRoot>(ctx.read());
41
-
42
- expectType<RwRoot>(ctx.write());
43
- expectNotType<RoRoot>(ctx.write());
44
-
45
- type ObjContext = CowContext<RoValueContainer, RoRoot, CowRootContext<RoRoot>>;
46
-
47
- expectType<CowRootContext<RoRoot>>(ctx.get());
48
- expectType<ObjContext>(ctx.get('obj'));
49
- expectType<never>(ctx.get('does', 'not', 'exist'));
50
-
51
- expectType<CowRootContext<RoRoot>>(ctx.set('obj', 'value', ''));
52
- expectType<CowRootContext<RoRoot>>(ctx.set('obj', 'nested', 'value', ''));
53
- expectError(ctx.set('obj', 'value', 0));
54
- expectError(ctx.set('obj', 'nested', 'value', 0));
55
- expectError(ctx.set('does', 'not', 'exist'));
56
-
57
- expectType<CowRootContext<RoRoot>>(ctx.update((rootCtx: CowRootContext<RoRoot>) => undefined));
58
- expectType<CowRootContext<RoRoot>>(ctx.update('obj', (childCtx: ObjContext) => undefined));
59
- expectError(ctx.update());
60
- expectError(ctx.update('no', 'updater'));
package/test.mjs DELETED
@@ -1,715 +0,0 @@
1
- /*
2
- * @flow strict
3
- * Copyright (c) 2023 Michael Wiencek
4
- *
5
- * This source code is licensed under the MIT license. A copy can be found
6
- * in the file named "LICENSE" at the root directory of this distribution.
7
- */
8
-
9
- // $FlowIssue[cannot-resolve-module]
10
- import assert from 'node:assert';
11
- // $FlowIssue[cannot-resolve-module]
12
- import test from 'node:test';
13
-
14
- import mutate from './index.mjs';
15
-
16
- /*:: import * as types from './index.mjs'; */
17
-
18
- const ERROR_REVOKED =
19
- /^Error: This context has been revoked and can no longer be used\.$/;
20
-
21
- const ERROR_CLONE =
22
- /^Error: Only plain objects, arrays, and class instances can be cloned\./;
23
-
24
- /*::
25
- type DatePeriod = {
26
- year: number,
27
- };
28
-
29
- type ReadOnlyDatePeriod = {
30
- +year: number,
31
- +month?: number,
32
- +day?: number,
33
- };
34
-
35
- type Person = {
36
- name: string,
37
- birth_date: DatePeriod,
38
- death_date: DatePeriod,
39
- };
40
-
41
- type ReadOnlyPerson = {
42
- +name: string,
43
- +birth_date: ReadOnlyDatePeriod,
44
- +death_date: ReadOnlyDatePeriod,
45
- };
46
-
47
- type People = Array<Person>;
48
- type ReadOnlyPeople = $ReadOnlyArray<ReadOnlyPerson>;
49
- */
50
-
51
- const aliceBirthDate = Object.freeze({year: 2100});
52
- const aliceDeathDate = Object.freeze({year: 2330});
53
-
54
- const alice /*: ReadOnlyPerson */ = Object.freeze({
55
- name: 'Alice',
56
- birth_date: aliceBirthDate,
57
- death_date: aliceDeathDate,
58
- });
59
-
60
- const people/*: ReadOnlyPeople */ = Object.freeze([alice, alice]);
61
-
62
- const unsupportedProperties/*: {
63
- _value: string,
64
- +getValue: string,
65
- -setValue: string,
66
- +func: () => string,
67
- +numberObject: Number,
68
- +stringObject: String,
69
- } */ = {
70
- _value: '',
71
- get getValue()/*: string */ {
72
- return unsupportedProperties._value;
73
- },
74
- set setValue(x/*: string */) {
75
- unsupportedProperties._value = x;
76
- },
77
- func: () => '',
78
- numberObject: new Number(1),
79
- stringObject: new String(''),
80
- };
81
-
82
- test('mutate', async (t) => {
83
- await t.test('throws if you pass null', async (t) => {
84
- assert.throws(() => {
85
- // $FlowIgnore[incompatible-call]
86
- mutate(null);
87
- }, ERROR_CLONE);
88
- });
89
-
90
- await t.test('throws if you pass a derived built-in', async (t) => {
91
- class FunDate extends Date {}
92
- assert.throws(() => {
93
- mutate(new FunDate());
94
- }, ERROR_CLONE);
95
- });
96
-
97
- await t.test('throws if you pass a generator', async (t) => {
98
- function* makeGenerator() {
99
- yield 1;
100
- }
101
- assert.throws(() => {
102
- mutate(makeGenerator());
103
- }, ERROR_CLONE);
104
- });
105
- });
106
-
107
- test('read', async (t) => {
108
- await t.test('returns the original value if no changes were made', (t) => {
109
- assert.strictEqual(mutate(people).read()[0].birth_date.year, 2100);
110
- });
111
-
112
- await t.test('returns the current changes', (t) => {
113
- const ctx = mutate(people);
114
- ctx.set(0, 'birth_date', 'year', 1988);
115
- assert.strictEqual(ctx.read()[0].birth_date.year, 1988);
116
- });
117
-
118
- await t.test('throws if the context is revoked', (t) => {
119
- const ctx = mutate(alice);
120
- ctx.revoke();
121
- assert.throws(() => {
122
- ctx.read();
123
- }, ERROR_REVOKED);
124
- });
125
- });
126
-
127
- test('write', async (t) => {
128
- await t.test('forces a copy even if no changes are made', (t) => {
129
- const copy = mutate(alice).update(ctx => ctx.write()).final();
130
- assert.notStrictEqual(copy, alice);
131
- });
132
-
133
- await t.test('returns the current changes', (t) => {
134
- const ctx = mutate(people);
135
- ctx.set(0, 'birth_date', 'year', 1988);
136
- assert.strictEqual(ctx.write()[0].birth_date.year, 1988);
137
- });
138
-
139
- await t.test('allows pushing array values', (t) => {
140
- const copy = mutate(people)
141
- .update((ctx) => ctx.write().push(alice))
142
- .final();
143
- assert.strictEqual(copy[2], alice);
144
- });
145
-
146
- await t.test('can be called on child primitives', (t) => {
147
- const ctx = mutate(alice);
148
- ctx.get('birth_date', 'year').write();
149
- // $FlowIgnore[cannot-write]
150
- ctx.read().birth_date.year = 1988;
151
- const copy = ctx.final();
152
- assert.strictEqual(copy.birth_date.year, 1988);
153
- });
154
-
155
- await t.test('throws if the context is revoked', (t) => {
156
- const ctx = mutate(people);
157
- ctx.revoke();
158
- assert.throws(() => {
159
- ctx.write();
160
- }, ERROR_REVOKED);
161
- });
162
- });
163
-
164
- test('get', async (t) => {
165
- await t.test('returns `this` if no arguments are passed', (t) => {
166
- const ctx = mutate(alice);
167
- assert.strictEqual(ctx.get(), ctx);
168
- });
169
-
170
- await t.test('works with one prop', (t) => {
171
- assert.strictEqual(mutate(alice).get('birth_date').read(), alice.birth_date);
172
- });
173
-
174
- await t.test('works with two props', (t) => {
175
- assert.strictEqual(mutate(alice).get('birth_date', 'year').read(), alice.birth_date.year);
176
- });
177
-
178
- await t.test('chains with other get calls', (t) => {
179
- assert.strictEqual(mutate(alice).get('birth_date').get('year').read(), alice.birth_date.year);
180
- });
181
-
182
- await t.test('throws if the context is revoked', (t) => {
183
- const ctx = mutate(alice);
184
- ctx.revoke();
185
- assert.throws(() => {
186
- ctx.get('birth_date');
187
- }, ERROR_REVOKED);
188
- });
189
-
190
- await t.test('throws on getters and setters', (t) => {
191
- const ctx = mutate(unsupportedProperties);
192
-
193
- assert.throws(() => {
194
- ctx.get('getValue');
195
- }, /^Error: Getters are unsupported\.$/);
196
-
197
- assert.throws(() => {
198
- ctx.get('setValue');
199
- }, /^Error: Setters are unsupported\.$/);
200
- });
201
- });
202
-
203
- test('set', async (t) => {
204
- await t.test('works directly on the root (one argument)', (t) => {
205
- let copy = mutate(alice)
206
- .set({...alice, birth_date: {...alice.birth_date, year: 1988}})
207
- .final();
208
- assert.strictEqual(copy.birth_date.year, 1988);
209
- assert.strictEqual(copy.death_date, aliceDeathDate);
210
- copy = mutate(alice)
211
- .set(alice)
212
- .set('birth_date', 'year', 1999)
213
- .final();
214
- assert.strictEqual(copy.birth_date.year, 1999);
215
- assert.strictEqual(copy.death_date, aliceDeathDate);
216
- });
217
-
218
- await t.test('works directly on a child (one argument)', (t) => {
219
- let copy = mutate(alice)
220
- .get('birth_date')
221
- .set({...alice.birth_date, year: 1988})
222
- .finalRoot();
223
- assert.strictEqual(copy.birth_date.year, 1988);
224
- assert.strictEqual(copy.death_date, aliceDeathDate);
225
- copy = mutate(alice)
226
- .get('birth_date', 'year')
227
- .set(1999)
228
- .finalRoot();
229
- assert.strictEqual(copy.birth_date.year, 1999);
230
- assert.strictEqual(copy.death_date, aliceDeathDate);
231
- });
232
-
233
- await t.test('works with one prop', (t) => {
234
- const copy = mutate(alice)
235
- .get('birth_date')
236
- .set('year', 1988)
237
- .finalRoot();
238
- assert.strictEqual(copy.birth_date.year, 1988);
239
- assert.strictEqual(copy.death_date, aliceDeathDate);
240
- });
241
-
242
- await t.test('works with two props', (t) => {
243
- const copy = mutate(alice)
244
- .set('birth_date', 'year', 1988)
245
- .final();
246
- assert.strictEqual(copy.birth_date.year, 1988);
247
- assert.strictEqual(copy.death_date, aliceDeathDate);
248
- });
249
-
250
- await t.test('chains with other set calls', (t) => {
251
- let copy = mutate(alice)
252
- .set('birth_date', 'year', 1988)
253
- .set('death_date', 'year', 2088)
254
- .final();
255
- assert.strictEqual(copy.birth_date.year, 1988);
256
- assert.strictEqual(copy.death_date.year, 2088);
257
- copy = mutate(alice)
258
- .get('birth_date')
259
- .set('year', 1990)
260
- .set('month', 10)
261
- .set('day', 21)
262
- .parent()
263
- .get('death_date')
264
- .set('year', 2090)
265
- .set('month', 11)
266
- .set('day', 30)
267
- .parent()
268
- .final();
269
- assert.deepStrictEqual(
270
- copy.birth_date,
271
- {year: 1990, month: 10, day: 21},
272
- );
273
- assert.deepStrictEqual(
274
- copy.death_date,
275
- {year: 2090, month: 11, day: 30},
276
- );
277
- });
278
-
279
- await t.test('returns the same object if no changes are made', (t) => {
280
- const copy1 = mutate(alice)
281
- .set('name', alice.name)
282
- .set('birth_date', alice.birth_date)
283
- .set('death_date', alice.death_date)
284
- .set('birth_date', 'year', alice.birth_date.year)
285
- .set('death_date', 'year', alice.death_date.year)
286
- .final();
287
- assert.strictEqual(alice, copy1);
288
- const copy2 = mutate(people)
289
- .set(0, people[0])
290
- .set(0, 'name', people[0].name)
291
- .set(0, 'birth_date', people[0].birth_date)
292
- .set(0, 'death_date', people[0].death_date)
293
- .set(0, 'birth_date', 'year', people[0].birth_date.year)
294
- .set(0, 'death_date', 'year', people[0].death_date.year)
295
- .final();
296
- assert.strictEqual(people, copy2);
297
- });
298
-
299
- await t.test('clones the object only once', async (t) => {
300
- const ctx = mutate(alice);
301
- ctx.get('birth_date').set('year', 1987);
302
- const newBirthDate1 = ctx.read().birth_date;
303
- ctx.get('birth_date').set('year', 1988);
304
- const newBirthDate2 = ctx.read().birth_date;
305
- assert.strictEqual(newBirthDate1, newBirthDate2);
306
- });
307
-
308
- await t.test('throws if the context is revoked', (t) => {
309
- const ctx = mutate(alice);
310
- ctx.revoke();
311
- assert.throws(() => {
312
- ctx.set(alice);
313
- }, ERROR_REVOKED);
314
- });
315
-
316
- await t.test('throws if called on a function', async (t) => {
317
- const ctx = mutate(unsupportedProperties);
318
-
319
- assert.throws(() => {
320
- // $FlowIgnore[incompatible-call]
321
- ctx.get('func').set('error', null);
322
- }, ERROR_CLONE);
323
- });
324
-
325
- await t.test('throws if called on a number object', async (t) => {
326
- const ctx = mutate(unsupportedProperties);
327
- assert.throws(() => {
328
- // $FlowIgnore[incompatible-call]
329
- ctx.get('numberObject').set('error', null);
330
- }, ERROR_CLONE);
331
- });
332
-
333
- await t.test('can set number objects directly', async (t) => {
334
- const copy = mutate(unsupportedProperties)
335
- .set('numberObject', new Number(2))
336
- .final();
337
- assert.strictEqual(+copy.numberObject, 2);
338
- });
339
-
340
- await t.test('throws if called on a string object', async (t) => {
341
- const ctx = mutate(unsupportedProperties);
342
-
343
- assert.throws(() => {
344
- // $FlowIgnore[incompatible-call]
345
- ctx.get('stringObject').set('error', null);
346
- }, ERROR_CLONE);
347
- });
348
-
349
- await t.test('can set string objects directly', async (t) => {
350
- const copy = mutate(unsupportedProperties)
351
- .set('stringObject', new String('huh'))
352
- .final();
353
- assert.strictEqual(String(copy.stringObject), 'huh');
354
- });
355
-
356
- await t.test('works on class instances', async (t) => {
357
- class NiceBase {}
358
- class NiceClass extends NiceBase {
359
- /*::
360
- value: string;
361
- */
362
- constructor() {
363
- super();
364
- this.value = 'nice';
365
- }
366
- }
367
- const orig = new NiceClass();
368
- const copy = mutate(orig)
369
- .set('value', 'naughty')
370
- .final();
371
- assert.ok(copy instanceof NiceClass);
372
- assert.ok(copy instanceof NiceBase);
373
- assert.strictEqual(copy.value, 'naughty');
374
- assert.strictEqual(orig.value, 'nice');
375
- });
376
-
377
- await t.test('can add custom properties to arrays', (t) => {
378
- const copy = mutate(people)
379
- // $FlowIgnore[incompatible-call]
380
- .set('customProp', 10)
381
- .final();
382
- // $FlowIgnore[prop-missing]
383
- assert.strictEqual(copy.customProp, 10);
384
- });
385
-
386
- await t.test('works on cyclic references', (t) => {
387
- /*::
388
- type Cyclic = {x: Cyclic | null}
389
- */
390
- const orig/*: Cyclic */ = {x: null};
391
- orig.x = orig;
392
- Object.freeze(orig);
393
- const copy1 = mutate(orig)
394
- .get('x')
395
- .set('x', null)
396
- .parent()
397
- .final();
398
- assert.strictEqual((copy1.x?.x), null);
399
- const copy2 = mutate(orig)
400
- // $FlowIgnore[incompatible-call]
401
- .set('x', 'x', 'x', null)
402
- .final();
403
- assert.strictEqual((copy2.x?.x?.x), null);
404
- });
405
-
406
- await t.test('propagates changes to parent contexts', (t) => {
407
- const ctx = mutate/*:: <ReadOnlyPerson> */(alice);
408
- const birthDateContext1 = ctx.get('birth_date');
409
- const yearContext1 = birthDateContext1.get('year');
410
-
411
- birthDateContext1.set('year', 1988);
412
- // Should propagate to birthDateContext1 and yearContext1
413
- ctx.set('birth_date', {year: 2008});
414
-
415
- assert.strictEqual(ctx.get('birth_date').read().year, 2008);
416
- assert.strictEqual(birthDateContext1.read().year, 2008);
417
- assert.strictEqual(yearContext1.read(), 2008);
418
- });
419
-
420
- await t.test('works on shared references', (t) => {
421
- const shared/*: {+foo: string} */ = Object.freeze({foo: ''});
422
- const object/*: {
423
- +prop1: typeof shared,
424
- +prop2: typeof shared,
425
- } */ = Object.freeze({
426
- prop1: shared,
427
- prop2: shared,
428
- });
429
- const copy = mutate(object)
430
- .set('prop1', 'foo', 'abc')
431
- .set('prop2', 'foo', '123')
432
- .final();
433
- assert.strictEqual(copy.prop1.foo, 'abc');
434
- assert.strictEqual(copy.prop2.foo, '123');
435
- });
436
-
437
- await t.test('works on externally-frozen objects', (t) => {
438
- const source/*: {+ref: {+name: string} | null} */ =
439
- Object.freeze({ref: null});
440
- const frozenRef = Object.freeze({name: ''});
441
- const copy = mutate(source)
442
- .set('ref', frozenRef)
443
- // $FlowIgnore[incompatible-call]
444
- .set('ref', 'name', 'hi')
445
- .final();
446
- assert.strictEqual(copy.ref?.name, 'hi');
447
- });
448
- });
449
-
450
- test('update', async (t) => {
451
- await t.test('works directly on the root (one argument)', (t) => {
452
- const ctx = mutate(alice);
453
- ctx.update((ctx2) => {
454
- assert.strictEqual(ctx, ctx2);
455
- ctx2.set('birth_date', 'year', 1988);
456
- });
457
- const copy = ctx.final();
458
- assert.strictEqual(copy.birth_date.year, 1988);
459
- assert.strictEqual(copy.death_date, aliceDeathDate);
460
- });
461
-
462
- await t.test('works directly on a child (one argument)', (t) => {
463
- const ctx = mutate(alice).get('birth_date');
464
- ctx.update((ctx2) => {
465
- assert.strictEqual(ctx, ctx2);
466
- ctx2.set('year', 1988);
467
- });
468
- const copy = ctx.finalRoot();
469
- assert.strictEqual(copy.birth_date.year, 1988);
470
- assert.strictEqual(copy.death_date, aliceDeathDate);
471
- });
472
-
473
- await t.test('works with one prop', (t) => {
474
- const ctx = mutate(alice);
475
- ctx.update('birth_date', (ctx2) => {
476
- assert.strictEqual(ctx.get('birth_date'), ctx2);
477
- ctx2.set('year', 1988);
478
- });
479
- const copy = ctx.finalRoot();
480
- assert.strictEqual(copy.birth_date.year, 1988);
481
- assert.strictEqual(copy.death_date, aliceDeathDate);
482
- });
483
-
484
- await t.test('works with two props', (t) => {
485
- const ctx = mutate(alice);
486
- ctx.update('birth_date', 'year', (ctx2) => {
487
- assert.strictEqual(ctx.get('birth_date', 'year'), ctx2);
488
- ctx2.set(1988);
489
- });
490
- const copy = ctx.finalRoot();
491
- assert.strictEqual(copy.birth_date.year, 1988);
492
- assert.strictEqual(copy.death_date, aliceDeathDate);
493
- });
494
-
495
- await t.test('chains with other update calls', (t) => {
496
- let copy = mutate(alice)
497
- .update('birth_date', 'year', (ctx) => { ctx.set(1988); })
498
- .update('death_date', 'year', (ctx) => { ctx.set(2088); })
499
- .final();
500
- });
501
- });
502
-
503
- test('parent', async (t) => {
504
- const ctx = mutate(alice);
505
-
506
- await t.test('returns null on the root', (t) => {
507
- assert.strictEqual(ctx.parent(), null);
508
- });
509
-
510
- await t.test('returns the parent of a child context', (t) => {
511
- assert.strictEqual(ctx.get('birth_date').parent(), ctx);
512
- });
513
-
514
- await t.test('returns the parent of a grandchild context', (t) => {
515
- assert.strictEqual(ctx.get('birth_date', 'year').parent(), ctx.get('birth_date'));
516
- });
517
-
518
- ctx.revoke();
519
- });
520
-
521
- test('root', async (t) => {
522
- const ctx = mutate(alice);
523
-
524
- await t.test('returns the root on the root', (t) => {
525
- assert.strictEqual(ctx.root(), ctx);
526
- });
527
-
528
- await t.test('returns the root of a child context', (t) => {
529
- assert.strictEqual(ctx.get('birth_date').root(), ctx);
530
- });
531
-
532
- await t.test('returns the root of a grandchild context', (t) => {
533
- assert.strictEqual(ctx.get('birth_date', 'year').root(), ctx);
534
- });
535
-
536
- ctx.revoke();
537
- });
538
-
539
- test('revoke', async (t) => {
540
- await t.test('can revoke the root context', async (t) => {
541
- const ctx = mutate/*:: <ReadOnlyPerson> */(alice);
542
- const birthDateContext = ctx.get('birth_date');
543
- const birthDateYearContext = birthDateContext.get('year');
544
- const deathDateContext = ctx.get('death_date');
545
- const deathDateYearContext = deathDateContext.get('year');
546
-
547
- ctx.revoke();
548
- // Can call revoke twice.
549
- ctx.revoke();
550
-
551
- assert.ok(ctx.isRevoked());
552
-
553
- await t.test('revoking the root context revokes all child contexts', (t) => {
554
- assert.ok(birthDateContext.isRevoked());
555
- assert.ok(birthDateYearContext.isRevoked());
556
- assert.ok(deathDateContext.isRevoked());
557
- assert.ok(deathDateYearContext.isRevoked());
558
- });
559
-
560
- await t.test('attempting to use a revoked context throws', (t) => {
561
- assert.throws(() => {
562
- ctx.set('birth_date', alice.birth_date);
563
- }, ERROR_REVOKED);
564
- });
565
- });
566
- });
567
-
568
- test('final', async (t) => {
569
- await t.test('can be called on child contexts without affecting parent', (t) => {
570
- const root = Object.freeze({
571
- foo: Object.freeze({bar: ''}),
572
- });
573
- const rootCtx = mutate(root);
574
- const fooCtx = rootCtx.get('foo');
575
- const fooCopy = fooCtx.set('bar', 'a').final();
576
-
577
- assert.ok(Object.isFrozen(fooCopy));
578
- assert.strictEqual(fooCopy.bar, 'a');
579
-
580
- const rootCopy = rootCtx
581
- .set('foo', 'bar', 'b')
582
- .final();
583
-
584
- assert.ok(Object.isFrozen(rootCopy));
585
- assert.strictEqual(rootCopy.foo.bar, 'b');
586
- });
587
-
588
- await t.test('preserves frozenness of objects', (t) => {
589
- const copy = mutate(alice).final();
590
- assert.ok(Object.isFrozen(copy));
591
- assert.ok(Object.isFrozen(copy.birth_date));
592
- assert.ok(Object.isFrozen(copy.death_date));
593
- });
594
-
595
- await t.test('preserves frozenness of arrays', (t) => {
596
- const copy = mutate(people)
597
- .set(0, 'birth_date', 'year', 1988)
598
- .update((ctx) => {
599
- ctx.write().push(alice);
600
- })
601
- .final();
602
- assert.strictEqual(copy[0].birth_date.year, 1988);
603
- assert.strictEqual(copy[1], alice);
604
- assert.ok(Object.isFrozen(copy));
605
- assert.ok(Object.isFrozen(copy[0]));
606
- assert.ok(Object.isFrozen(copy[0].birth_date));
607
- assert.ok(Object.isFrozen(copy[0].death_date));
608
- });
609
-
610
- await t.test('preserves sealedness of objects', (t) => {
611
- const orig/*: {+name: string, +address?: string} */ =
612
- Object.seal({name: ''});
613
- const copy = mutate(orig)
614
- .set('address', 'abc')
615
- .final();
616
- assert.ok(Object.isSealed(copy));
617
- assert.ok(!Object.isFrozen(copy));
618
- });
619
-
620
- await t.test('preserves extensibility of objects', (t) => {
621
- const orig/*: {+name: string, +address?: string} */ =
622
- Object.preventExtensions({name: ''});
623
- const copy = mutate(orig)
624
- .set('address', 'abc')
625
- .final();
626
- assert.ok(!Object.isExtensible(copy));
627
- assert.ok(!Object.isSealed(copy));
628
- assert.ok(!Object.isFrozen(copy));
629
- });
630
-
631
- await t.test('preserves descriptors for individual properties', (t) => {
632
- const orig = {};
633
-
634
- const origDescriptors = {
635
- a: {configurable: false, enumerable: false, writable: false, value: undefined},
636
- b: {configurable: false, enumerable: false, writable: true, value: undefined},
637
- c: {configurable: false, enumerable: true, writable: false, value: undefined},
638
- d: {configurable: false, enumerable: true, writable: true, value: undefined},
639
- e: {configurable: true, enumerable: false, writable: false, value: undefined},
640
- f: {configurable: true, enumerable: false, writable: true, value: undefined},
641
- g: {configurable: true, enumerable: true, writable: false, value: undefined},
642
- h: {configurable: true, enumerable: true, writable: true, value: undefined},
643
- };
644
-
645
- // $FlowIgnore[prop-missing]
646
- Object.defineProperties(orig, origDescriptors);
647
- // $FlowIgnore[incompatible-call]
648
- const copy = mutate(orig).set('a', '1').final();
649
- const copyDescriptors = Object.getOwnPropertyDescriptors(copy);
650
- assert.deepStrictEqual(copyDescriptors.a, {...origDescriptors.a, value: '1'});
651
- assert.deepStrictEqual(copyDescriptors.b, origDescriptors.b);
652
- assert.deepStrictEqual(copyDescriptors.c, origDescriptors.c);
653
- assert.deepStrictEqual(copyDescriptors.d, origDescriptors.d);
654
- assert.deepStrictEqual(copyDescriptors.e, origDescriptors.e);
655
- assert.deepStrictEqual(copyDescriptors.f, origDescriptors.f);
656
- assert.deepStrictEqual(copyDescriptors.g, origDescriptors.g);
657
- assert.deepStrictEqual(copyDescriptors.h, origDescriptors.h);
658
- });
659
-
660
- await t.test('does not restore descriptors onto stale copies', (t) => {
661
- const root = Object.freeze({
662
- foo: Object.freeze({bar: ''}),
663
- });
664
-
665
- const rootCtx = mutate(root);
666
- rootCtx.get('foo').set('bar', 'a');
667
- const tmpFoo = rootCtx.get('foo').write();
668
-
669
- assert.ok(!Object.isFrozen(tmpFoo));
670
-
671
- rootCtx.set('foo', Object.freeze({bar: 'b'}));
672
- rootCtx.get('foo').set('bar', 'c');
673
-
674
- const rootCopy = rootCtx.finalRoot();
675
- assert.ok(Object.isFrozen(rootCopy));
676
- assert.ok(Object.isFrozen(rootCopy.foo));
677
- assert.strictEqual(rootCopy.foo.bar, 'c');
678
-
679
- // `tmpFoo` is stale
680
- assert.ok(!Object.isFrozen(tmpFoo));
681
- assert.strictEqual(tmpFoo.bar, 'a');
682
- });
683
-
684
- await t.test('preserves null prototypes', (t) => {
685
- const orig/*: {
686
- __proto__: null,
687
- +value: {__proto__: null, +number: number},
688
- } */ = Object.create(null, {
689
- value: {
690
- configurable: true,
691
- enumerable: true,
692
- value: Object.create(null, {
693
- number: {
694
- configurable: true,
695
- enumerable: true,
696
- value: 1,
697
- writable: false,
698
- },
699
- }),
700
- writable: false,
701
- },
702
- });
703
-
704
- const copy = mutate(orig)
705
- .get('value')
706
- .set('number', 2)
707
- .parent()
708
- .final();
709
-
710
- assert.strictEqual(orig.value.number, 1);
711
- assert.strictEqual(copy.value.number, 2);
712
- assert.strictEqual(Object.getPrototypeOf(copy), null);
713
- assert.strictEqual(Object.getPrototypeOf(copy.value), null);
714
- });
715
- });
File without changes
File without changes