@rimbu/deep 0.11.2 → 0.12.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/dist/main/deep.js +211 -0
- package/dist/main/deep.js.map +1 -0
- package/dist/main/index.js +7 -9
- package/dist/main/index.js.map +1 -1
- package/dist/main/internal.js +6 -4
- package/dist/main/internal.js.map +1 -1
- package/dist/main/match.js +160 -199
- package/dist/main/match.js.map +1 -1
- package/dist/main/patch.js +101 -125
- package/dist/main/patch.js.map +1 -1
- package/dist/main/path.js +109 -62
- package/dist/main/path.js.map +1 -1
- package/dist/main/protected.js +0 -19
- package/dist/main/protected.js.map +1 -1
- package/dist/main/selector.js +40 -0
- package/dist/main/selector.js.map +1 -0
- package/dist/main/tuple.js.map +1 -1
- package/dist/module/deep.js +192 -0
- package/dist/module/deep.js.map +1 -0
- package/dist/module/index.js +9 -1
- package/dist/module/index.js.map +1 -1
- package/dist/module/internal.js +4 -4
- package/dist/module/internal.js.map +1 -1
- package/dist/module/match.js +159 -179
- package/dist/module/match.js.map +1 -1
- package/dist/module/patch.js +91 -112
- package/dist/module/patch.js.map +1 -1
- package/dist/module/path.js +99 -44
- package/dist/module/path.js.map +1 -1
- package/dist/module/protected.js +1 -17
- package/dist/module/protected.js.map +1 -1
- package/dist/module/selector.js +36 -0
- package/dist/module/selector.js.map +1 -0
- package/dist/module/tuple.js.map +1 -1
- package/dist/types/deep.d.ts +284 -0
- package/dist/types/index.d.ts +10 -1
- package/dist/types/internal.d.ts +7 -4
- package/dist/types/match.d.ts +74 -80
- package/dist/types/patch.d.ts +57 -50
- package/dist/types/path.d.ts +177 -34
- package/dist/types/protected.d.ts +1 -16
- package/dist/types/selector.d.ts +47 -0
- package/dist/types/tuple.d.ts +10 -0
- package/package.json +4 -4
- package/src/deep.ts +362 -0
- package/src/index.ts +14 -10
- package/src/internal.ts +7 -4
- package/src/match.ts +396 -212
- package/src/patch.ts +173 -176
- package/src/path.ts +400 -74
- package/src/protected.ts +14 -25
- package/src/selector.ts +90 -0
- package/src/tuple.ts +12 -0
package/src/patch.ts
CHANGED
|
@@ -1,257 +1,254 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RimbuError,
|
|
3
|
-
type AnyFunc,
|
|
4
|
-
isPlainObj,
|
|
5
|
-
type PlainObj,
|
|
6
|
-
} from '@rimbu/base';
|
|
1
|
+
import { IsAnyFunc, IsArray, isPlainObj, IsPlainObj } from '@rimbu/base';
|
|
7
2
|
|
|
8
3
|
import type { Protected } from './internal';
|
|
4
|
+
import type { Tuple } from './tuple';
|
|
9
5
|
|
|
10
6
|
/**
|
|
11
7
|
* A type to determine the allowed input type for the `patch` function.
|
|
12
8
|
* @typeparam T - the input type to be patched
|
|
13
9
|
*/
|
|
14
|
-
export type Patch<T> = Patch.Entry<T, T>;
|
|
10
|
+
export type Patch<T, C = T> = Patch.Entry<T, C, T, T>;
|
|
15
11
|
|
|
16
12
|
export namespace Patch {
|
|
17
13
|
/**
|
|
18
14
|
* The entry type for a (nested) patch. Can be either a patch object or a function accepting the nested patch function and returning a patch object.
|
|
19
15
|
* @typeparam T - the input value type
|
|
16
|
+
* @typeparam C - a utility type
|
|
17
|
+
* @typeparam P - the parent type
|
|
20
18
|
* @typeparam R - the root object type
|
|
21
19
|
*/
|
|
22
|
-
export type Entry<T, R> =
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
export type Entry<T, C, P, R> = IsAnyFunc<T> extends true
|
|
21
|
+
? T
|
|
22
|
+
: IsPlainObj<T> extends true
|
|
23
|
+
? Patch.WithResult<T, P, R, Patch.Obj<T, C, R>>
|
|
24
|
+
: Tuple.IsTuple<T> extends true
|
|
25
|
+
? Patch.WithResult<T, P, R, T | Patch.Tup<T, C, R>>
|
|
26
|
+
: IsArray<T> extends true
|
|
27
|
+
? Patch.WithResult<T, P, R, T>
|
|
28
|
+
: Patch.WithResult<T, P, R, T>;
|
|
25
29
|
|
|
26
30
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* @typeparam
|
|
30
|
-
* @typeparam R - the root
|
|
31
|
+
* Either result type S, or a patch function with the value type, the parent type, and the root type.
|
|
32
|
+
* @typeparam T - the value type
|
|
33
|
+
* @typeparam P - the parent type
|
|
34
|
+
* @typeparam R - the root type
|
|
35
|
+
* @typeparam S - the result type
|
|
31
36
|
*/
|
|
32
|
-
export type
|
|
33
|
-
[K in keyof T]?:
|
|
34
|
-
| (T[K] extends AnyFunc ? never : ObjItem<T[K], R>)
|
|
35
|
-
| ((
|
|
36
|
-
cur: Protected<T[K]>,
|
|
37
|
-
parent: Protected<T>,
|
|
38
|
-
root: Protected<R>
|
|
39
|
-
) => ObjItem<T[K], R>);
|
|
40
|
-
};
|
|
37
|
+
export type WithResult<T, P, R, S> = S | Patch.Func<T, P, R, S>;
|
|
41
38
|
|
|
42
39
|
/**
|
|
43
|
-
* A patch
|
|
44
|
-
*
|
|
45
|
-
* @typeparam
|
|
40
|
+
* A function patch type that is a function taking the current value, the parent and root values,
|
|
41
|
+
* and returns a return value.
|
|
42
|
+
* @typeparam T - the value type
|
|
43
|
+
* @typeparam P - the parent type
|
|
44
|
+
* @typeparam R - the root type
|
|
45
|
+
* @typeparam S - the result type
|
|
46
46
|
*/
|
|
47
|
-
export type
|
|
47
|
+
export type Func<T, P, R, S> = (
|
|
48
|
+
current: Protected<T>,
|
|
49
|
+
parent: Protected<P>,
|
|
50
|
+
root: Protected<R>
|
|
51
|
+
) => Protected<S>;
|
|
48
52
|
|
|
49
53
|
/**
|
|
50
|
-
*
|
|
54
|
+
* A type defining the allowed patch values for tuples.
|
|
55
|
+
* @typeparam T - the input tuple type
|
|
56
|
+
* @typeparam C - a utility type
|
|
57
|
+
* @typeparam R - the root type
|
|
51
58
|
*/
|
|
52
|
-
export type
|
|
59
|
+
export type Tup<T, C, R> = {
|
|
60
|
+
[K in Tuple.KeysOf<T>]?: Patch.Entry<T[K & keyof T], C[K & keyof C], T, R>;
|
|
61
|
+
} & NotIterable;
|
|
53
62
|
|
|
54
63
|
/**
|
|
55
|
-
*
|
|
56
|
-
* @typeparam T - the patch value type
|
|
57
|
-
* @typeparam Q - the input value type
|
|
58
|
-
* @param patchItems - a number of `Patch` objects that patch a given value of type T.
|
|
59
|
-
* @example
|
|
60
|
-
* ```ts
|
|
61
|
-
* const items = [{ a: 1, b: 'a' }, { a: 2, b: 'b' }]
|
|
62
|
-
* items.map(Patch.create({ a: v => v + 1 }))
|
|
63
|
-
* // => [{ a: 2, b: 'a' }, { a: 3, b: 'b' }]
|
|
64
|
-
* ```
|
|
64
|
+
* Utility type to exclude Iterable types.
|
|
65
65
|
*/
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
return (value) => patch<Q>(value, ...patchItems);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
66
|
+
export type NotIterable = {
|
|
67
|
+
[Symbol.iterator]?: never;
|
|
68
|
+
};
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
/**
|
|
71
|
+
* A type defining the allowed patch values for objects.
|
|
72
|
+
* @typeparam T - the input value type
|
|
73
|
+
* @typeparam C - a utility type
|
|
74
|
+
* @typeparam R - the root object type
|
|
75
|
+
*/
|
|
76
|
+
export type Obj<T, C, R> = T | Patch.ObjProps<T, C, R>[];
|
|
76
77
|
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
* patch({ a: 1, b: { c: true, d: 'a' } }, { b: patchNested({ d: 'b' }) })
|
|
87
|
-
* // => { a: 1, b: { c: true, d: 'b' } }
|
|
88
|
-
* ```
|
|
89
|
-
*/
|
|
90
|
-
export function patchNested<T, R, Q extends T = T>(
|
|
91
|
-
...patchDataItems: Patch.Obj<Q, R>[]
|
|
92
|
-
): NestedObj<T, R, Q> {
|
|
93
|
-
return new NestedObj(patchDataItems);
|
|
78
|
+
/**
|
|
79
|
+
* A type defining the allowed patch values for object properties.
|
|
80
|
+
* @typeparam T - the input value type
|
|
81
|
+
* @typeparam C - a utility type
|
|
82
|
+
* @typeparam R - the root object type
|
|
83
|
+
*/
|
|
84
|
+
export type ObjProps<T, C, R> = {
|
|
85
|
+
[K in keyof C]?: K extends keyof T ? Patch.Entry<T[K], C[K], T, R> : never;
|
|
86
|
+
};
|
|
94
87
|
}
|
|
95
88
|
|
|
96
89
|
/**
|
|
97
90
|
* Returns an immutably updated version of the given `value` where the given `patchItems` have been
|
|
98
91
|
* applied to the result.
|
|
92
|
+
* The Rimbu patch notation is as follows:
|
|
93
|
+
* - if the target is a simple value or array, the patch can be the same type or a function returning the same type
|
|
94
|
+
* - if the target is a tuple (array of fixed length), the patch be the same type or an object containing numeric keys with patches indicating the tuple index to patch
|
|
95
|
+
* - if the target is an object, the patch can be the same type, or an array containing partial keys with their patches for the object
|
|
96
|
+
* @typeparam T - the type of the value to patch
|
|
97
|
+
* @typeparam TE - a utility type
|
|
98
|
+
* @typeparam TT - a utility type
|
|
99
99
|
* @param value - the input value to patch
|
|
100
|
-
* @param
|
|
100
|
+
* @param patchItem - the `Patch` value to apply to the input value
|
|
101
101
|
* @example
|
|
102
102
|
* ```ts
|
|
103
103
|
* const input = { a: 1, b: { c: true, d: 'a' } }
|
|
104
|
-
* patch(input, { a: 2 }) // => { a: 2, b: { c: true, d: 'a' } }
|
|
105
|
-
* patch(input
|
|
104
|
+
* patch(input, [{ a: 2 }]) // => { a: 2, b: { c: true, d: 'a' } }
|
|
105
|
+
* patch(input, [{ b: [{ c: (v) => !v }] }] )
|
|
106
106
|
* // => { a: 1, b: { c: false, d: 'a' } }
|
|
107
|
-
* patch(input:
|
|
107
|
+
* patch(input: [{ a: (v) => v + 1, b: [{ d: 'q' }] }] )
|
|
108
108
|
* // => { a: 2, b: { c: true, d: 'q' } }
|
|
109
109
|
* ```
|
|
110
110
|
*/
|
|
111
|
-
export function patch<T
|
|
112
|
-
const newValue = isPlainObj(value) ? { ...value } : value;
|
|
113
|
-
const changedRef = { changed: false };
|
|
114
|
-
|
|
115
|
-
const result = processPatch(newValue, newValue, patchItems, changedRef);
|
|
116
|
-
|
|
117
|
-
if (changedRef.changed) return result;
|
|
118
|
-
|
|
119
|
-
return value;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Interface providing a shared reference to a `changed` boolean.
|
|
124
|
-
*/
|
|
125
|
-
interface ChangedRef {
|
|
126
|
-
changed: boolean;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function processPatch<T, R>(
|
|
111
|
+
export function patch<T, TE extends T = T, TT = T>(
|
|
130
112
|
value: T,
|
|
131
|
-
|
|
132
|
-
patchDataItems: Patch.Entry<T, R>[],
|
|
133
|
-
changedRef: ChangedRef
|
|
113
|
+
patchItem: Patch<TE, TT>
|
|
134
114
|
): T {
|
|
135
|
-
|
|
136
|
-
const len = patchDataItems.length;
|
|
137
|
-
|
|
138
|
-
while (++i < len) {
|
|
139
|
-
const patchItem = patchDataItems[i];
|
|
140
|
-
if (patchItem instanceof Function) {
|
|
141
|
-
const item = patchItem(patchNested);
|
|
142
|
-
if (item instanceof NestedObj) {
|
|
143
|
-
processPatch(value, root, item.patchDataItems, changedRef);
|
|
144
|
-
} else {
|
|
145
|
-
processPatchObj(value, root, item, changedRef);
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
processPatchObj(value, root, patchItem, changedRef);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return value;
|
|
115
|
+
return patchEntry(value, value, value, patchItem as Patch<T>);
|
|
153
116
|
}
|
|
154
117
|
|
|
155
|
-
function
|
|
118
|
+
function patchEntry<T, C, P, R>(
|
|
156
119
|
value: T,
|
|
120
|
+
parent: P,
|
|
157
121
|
root: R,
|
|
158
|
-
|
|
159
|
-
changedRef: ChangedRef
|
|
122
|
+
patchItem: Patch.Entry<T, C, P, R>
|
|
160
123
|
): T {
|
|
161
|
-
if (
|
|
124
|
+
if (Object.is(value, patchItem)) {
|
|
125
|
+
// patching a value with itself never changes the value
|
|
162
126
|
return value;
|
|
163
127
|
}
|
|
164
128
|
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
);
|
|
129
|
+
if (typeof value === 'function') {
|
|
130
|
+
// function input, directly return patch
|
|
131
|
+
return patchItem as T;
|
|
169
132
|
}
|
|
170
133
|
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
134
|
+
if (typeof patchItem === 'function') {
|
|
135
|
+
// function patch always needs to be resolved first
|
|
136
|
+
const item = patchItem(value, parent, root);
|
|
137
|
+
|
|
138
|
+
return patchEntry(value, parent, root, item);
|
|
175
139
|
}
|
|
176
140
|
|
|
177
|
-
|
|
178
|
-
|
|
141
|
+
if (isPlainObj(value)) {
|
|
142
|
+
// value is plain object
|
|
143
|
+
return patchPlainObj(value, root, patchItem as any);
|
|
144
|
+
}
|
|
179
145
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
) {
|
|
185
|
-
RimbuError.throwInvalidUsageError(
|
|
186
|
-
`patch: received patch object key '${key}' which is not allowed to prevent prototype pollution`
|
|
187
|
-
);
|
|
188
|
-
}
|
|
146
|
+
if (Array.isArray(value)) {
|
|
147
|
+
// value is tuple or array
|
|
148
|
+
return patchArr(value, root, patchItem as any);
|
|
149
|
+
}
|
|
189
150
|
|
|
190
|
-
|
|
151
|
+
// value is primitive type or complex object
|
|
191
152
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
`patch: received update function object key ${key} but the key was not present in the source object. Either explicitely set the value in the source to undefined or use a direct value.`
|
|
195
|
-
);
|
|
196
|
-
}
|
|
153
|
+
return patchItem as T;
|
|
154
|
+
}
|
|
197
155
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
156
|
+
function patchPlainObj<T, C, R>(
|
|
157
|
+
value: T,
|
|
158
|
+
root: R,
|
|
159
|
+
patchItem: T | Patch.Obj<T, C, R>
|
|
160
|
+
): T {
|
|
161
|
+
if (!Array.isArray(patchItem)) {
|
|
162
|
+
// the patch is a complete replacement of the current value
|
|
203
163
|
|
|
204
|
-
|
|
164
|
+
return patchItem as T;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// patch is an array of partial updates
|
|
168
|
+
|
|
169
|
+
// copy the input value
|
|
170
|
+
const result = { ...value };
|
|
171
|
+
|
|
172
|
+
let anyChange = false;
|
|
173
|
+
|
|
174
|
+
// loop over patches in array
|
|
175
|
+
for (const entry of patchItem) {
|
|
176
|
+
// update root if needed
|
|
177
|
+
const currentRoot = (value as any) === root ? { ...result } : root;
|
|
205
178
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
179
|
+
// loop over all the patch keys
|
|
180
|
+
for (const key in entry as T) {
|
|
181
|
+
// patch the value at the given key with the patch at that key
|
|
182
|
+
const currentValue = result[key];
|
|
183
|
+
const newValue = patchEntry(
|
|
184
|
+
currentValue,
|
|
185
|
+
value,
|
|
186
|
+
currentRoot,
|
|
187
|
+
(entry as any)[key]
|
|
212
188
|
);
|
|
213
|
-
} else {
|
|
214
|
-
newValue = processPatchObjItem(
|
|
215
|
-
target,
|
|
216
|
-
root,
|
|
217
|
-
update as any,
|
|
218
|
-
changedRef
|
|
219
|
-
) as any;
|
|
220
|
-
}
|
|
221
189
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
190
|
+
if (!Object.is(currentValue, newValue)) {
|
|
191
|
+
// if value changed, set it in result and mark change
|
|
192
|
+
anyChange = true;
|
|
193
|
+
result[key] = newValue;
|
|
194
|
+
}
|
|
225
195
|
}
|
|
226
196
|
}
|
|
227
197
|
|
|
198
|
+
if (anyChange) {
|
|
199
|
+
// something changed, return new value
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// nothing changed, return old value
|
|
228
204
|
return value;
|
|
229
205
|
}
|
|
230
206
|
|
|
231
|
-
function
|
|
207
|
+
function patchArr<T extends any[], C, R>(
|
|
232
208
|
value: T,
|
|
233
209
|
root: R,
|
|
234
|
-
|
|
235
|
-
superChangedRef: ChangedRef
|
|
210
|
+
patchItem: T | Patch.Tup<T, C, R>
|
|
236
211
|
): T {
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
|
|
212
|
+
if (Array.isArray(patchItem)) {
|
|
213
|
+
// value is a normal array
|
|
214
|
+
// patch is a complete replacement of current array
|
|
240
215
|
|
|
241
|
-
|
|
242
|
-
|
|
216
|
+
return patchItem;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// value is a tuple
|
|
220
|
+
// patch is an object containing numeric keys with function values
|
|
221
|
+
// that update the tuple at the given indices
|
|
222
|
+
|
|
223
|
+
// copy the tuple
|
|
224
|
+
const result = [...value] as T;
|
|
225
|
+
let anyChange = false;
|
|
226
|
+
|
|
227
|
+
// loop over all index keys in object
|
|
228
|
+
for (const index in patchItem) {
|
|
229
|
+
const numIndex = index as any as number;
|
|
230
|
+
|
|
231
|
+
// patch the tuple at the given index
|
|
232
|
+
const currentValue = result[numIndex];
|
|
233
|
+
const newValue = patchEntry(
|
|
234
|
+
currentValue,
|
|
235
|
+
value,
|
|
243
236
|
root,
|
|
244
|
-
|
|
245
|
-
changedRef
|
|
237
|
+
(patchItem as any)[index]
|
|
246
238
|
);
|
|
247
239
|
|
|
248
|
-
if (
|
|
249
|
-
|
|
250
|
-
|
|
240
|
+
if (!Object.is(newValue, currentValue)) {
|
|
241
|
+
// if value changed, set it in result and mark change
|
|
242
|
+
anyChange = true;
|
|
243
|
+
result[numIndex] = newValue;
|
|
251
244
|
}
|
|
245
|
+
}
|
|
252
246
|
|
|
253
|
-
|
|
247
|
+
if (anyChange) {
|
|
248
|
+
// something changed, return new value
|
|
249
|
+
return result;
|
|
254
250
|
}
|
|
255
251
|
|
|
256
|
-
return
|
|
252
|
+
// nothing changed, return old value
|
|
253
|
+
return value;
|
|
257
254
|
}
|