@rimbu/deep 2.0.4 → 2.0.5
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/package.json +5 -7
- package/dist/bun/deep.mts +0 -364
- package/dist/bun/index.mts +0 -23
- package/dist/bun/internal.mts +0 -8
- package/dist/bun/match.mts +0 -702
- package/dist/bun/patch.mts +0 -263
- package/dist/bun/path.mts +0 -428
- package/dist/bun/protected.mts +0 -31
- package/dist/bun/selector.mts +0 -93
- package/dist/bun/tuple.mts +0 -201
package/dist/bun/patch.mts
DELETED
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type IsAnyFunc,
|
|
3
|
-
type IsArray,
|
|
4
|
-
isPlainObj,
|
|
5
|
-
type IsPlainObj,
|
|
6
|
-
} from '@rimbu/base';
|
|
7
|
-
|
|
8
|
-
import type { Protected } from './internal.mts';
|
|
9
|
-
import type { Tuple } from './tuple.mts';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* A type to determine the allowed input type for the `patch` function.
|
|
13
|
-
* @typeparam T - the input type to be patched
|
|
14
|
-
*/
|
|
15
|
-
export type Patch<T, C = T> = Patch.Entry<T, C, T, T>;
|
|
16
|
-
|
|
17
|
-
export namespace Patch {
|
|
18
|
-
/**
|
|
19
|
-
* 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.
|
|
20
|
-
* @typeparam T - the input value type
|
|
21
|
-
* @typeparam C - a utility type
|
|
22
|
-
* @typeparam P - the parent type
|
|
23
|
-
* @typeparam R - the root object type
|
|
24
|
-
*/
|
|
25
|
-
export type Entry<T, C, P, R> =
|
|
26
|
-
IsAnyFunc<T> extends true
|
|
27
|
-
? T
|
|
28
|
-
: IsPlainObj<T> extends true
|
|
29
|
-
? Patch.WithResult<T, P, R, Patch.Obj<T, C, R>>
|
|
30
|
-
: Tuple.IsTuple<T> extends true
|
|
31
|
-
? Patch.WithResult<T, P, R, T | Patch.Tup<T, C, R>>
|
|
32
|
-
: IsArray<T> extends true
|
|
33
|
-
? Patch.WithResult<T, P, R, T>
|
|
34
|
-
: Patch.WithResult<T, P, R, T>;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Either result type S, or a patch function with the value type, the parent type, and the root type.
|
|
38
|
-
* @typeparam T - the value type
|
|
39
|
-
* @typeparam P - the parent type
|
|
40
|
-
* @typeparam R - the root type
|
|
41
|
-
* @typeparam S - the result type
|
|
42
|
-
*/
|
|
43
|
-
export type WithResult<T, P, R, S> = S | Patch.Func<T, P, R, S>;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* A function patch type that is a function taking the current value, the parent and root values,
|
|
47
|
-
* and returns a return value.
|
|
48
|
-
* @typeparam T - the value type
|
|
49
|
-
* @typeparam P - the parent type
|
|
50
|
-
* @typeparam R - the root type
|
|
51
|
-
* @typeparam S - the result type
|
|
52
|
-
*/
|
|
53
|
-
export type Func<T, P, R, S> = (
|
|
54
|
-
current: Protected<T>,
|
|
55
|
-
parent: Protected<P>,
|
|
56
|
-
root: Protected<R>
|
|
57
|
-
) => Protected<S>;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* A type defining the allowed patch values for tuples.
|
|
61
|
-
* @typeparam T - the input tuple type
|
|
62
|
-
* @typeparam C - a utility type
|
|
63
|
-
* @typeparam R - the root type
|
|
64
|
-
*/
|
|
65
|
-
export type Tup<T, C, R> = {
|
|
66
|
-
[K in Tuple.KeysOf<T>]?: Patch.Entry<T[K & keyof T], C[K & keyof C], T, R>;
|
|
67
|
-
} & NotIterable;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Utility type to exclude Iterable types.
|
|
71
|
-
*/
|
|
72
|
-
export type NotIterable = {
|
|
73
|
-
[Symbol.iterator]?: never;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* A type defining the allowed patch values for objects.
|
|
78
|
-
* @typeparam T - the input value type
|
|
79
|
-
* @typeparam C - a utility type
|
|
80
|
-
* @typeparam R - the root object type
|
|
81
|
-
*/
|
|
82
|
-
export type Obj<T, C, R> = T | Patch.ObjProps<T, C, R>[];
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* A type defining the allowed patch values for object properties.
|
|
86
|
-
* @typeparam T - the input value type
|
|
87
|
-
* @typeparam C - a utility type
|
|
88
|
-
* @typeparam R - the root object type
|
|
89
|
-
*/
|
|
90
|
-
export type ObjProps<T, C, R> = {
|
|
91
|
-
[K in keyof C]?: K extends keyof T ? Patch.Entry<T[K], C[K], T, R> : never;
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Returns an immutably updated version of the given `value` where the given `patchItems` have been
|
|
97
|
-
* applied to the result.
|
|
98
|
-
* The Rimbu patch notation is as follows:
|
|
99
|
-
* - if the target is a simple value or array, the patch can be the same type or a function returning the same type
|
|
100
|
-
* - 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
|
|
101
|
-
* - 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
|
|
102
|
-
* @typeparam T - the type of the value to patch
|
|
103
|
-
* @typeparam TE - a utility type
|
|
104
|
-
* @typeparam TT - a utility type
|
|
105
|
-
* @param value - the input value to patch
|
|
106
|
-
* @param patchItem - the `Patch` value to apply to the input value
|
|
107
|
-
* @example
|
|
108
|
-
* ```ts
|
|
109
|
-
* const input = { a: 1, b: { c: true, d: 'a' } }
|
|
110
|
-
* patch(input, [{ a: 2 }]) // => { a: 2, b: { c: true, d: 'a' } }
|
|
111
|
-
* patch(input, [{ b: [{ c: (v) => !v }] }] )
|
|
112
|
-
* // => { a: 1, b: { c: false, d: 'a' } }
|
|
113
|
-
* patch(input, [{ a: (v) => v + 1, b: [{ d: 'q' }] }] )
|
|
114
|
-
* // => { a: 2, b: { c: true, d: 'q' } }
|
|
115
|
-
* ```
|
|
116
|
-
*/
|
|
117
|
-
export function patch<T, TE extends T = T, TT = T>(
|
|
118
|
-
value: T,
|
|
119
|
-
patchItem: Patch<TE, T & TT>
|
|
120
|
-
): T {
|
|
121
|
-
return patchEntry(value, value, value, patchItem as Patch<T>);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function patchEntry<T, C, P, R>(
|
|
125
|
-
value: T,
|
|
126
|
-
parent: P,
|
|
127
|
-
root: R,
|
|
128
|
-
patchItem: Patch.Entry<T, C, P, R>
|
|
129
|
-
): T {
|
|
130
|
-
if (Object.is(value, patchItem)) {
|
|
131
|
-
// patching a value with itself never changes the value
|
|
132
|
-
return value;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (typeof value === 'function') {
|
|
136
|
-
// function input, directly return patch
|
|
137
|
-
return patchItem as T;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (typeof patchItem === 'function') {
|
|
141
|
-
// function patch always needs to be resolved first
|
|
142
|
-
const item = patchItem(value, parent, root);
|
|
143
|
-
|
|
144
|
-
return patchEntry(value, parent, root, item);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (isPlainObj(value)) {
|
|
148
|
-
// value is plain object
|
|
149
|
-
return patchPlainObj(value, root, patchItem as any);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (Array.isArray(value)) {
|
|
153
|
-
// value is tuple or array
|
|
154
|
-
return patchArr(value, root, patchItem as any);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// value is primitive type or complex object
|
|
158
|
-
|
|
159
|
-
return patchItem as T;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function patchPlainObj<T, C, R>(
|
|
163
|
-
value: T,
|
|
164
|
-
root: R,
|
|
165
|
-
patchItem: T | Patch.Obj<T, C, R>
|
|
166
|
-
): T {
|
|
167
|
-
if (!Array.isArray(patchItem)) {
|
|
168
|
-
// the patch is a complete replacement of the current value
|
|
169
|
-
|
|
170
|
-
return patchItem as T;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// patch is an array of partial updates
|
|
174
|
-
|
|
175
|
-
// copy the input value
|
|
176
|
-
const result = { ...value };
|
|
177
|
-
|
|
178
|
-
let anyChange = false;
|
|
179
|
-
|
|
180
|
-
// loop over patches in array
|
|
181
|
-
for (const entry of patchItem) {
|
|
182
|
-
// update root if needed
|
|
183
|
-
const currentRoot = (value as any) === root ? { ...result } : root;
|
|
184
|
-
|
|
185
|
-
// keep current updated result as parent
|
|
186
|
-
const parent = { ...result };
|
|
187
|
-
|
|
188
|
-
// loop over all the patch keys
|
|
189
|
-
for (const key in entry as T) {
|
|
190
|
-
// patch the value at the given key with the patch at that key
|
|
191
|
-
const currentValue = result[key];
|
|
192
|
-
const newValue = patchEntry(
|
|
193
|
-
currentValue,
|
|
194
|
-
parent,
|
|
195
|
-
currentRoot,
|
|
196
|
-
(entry as any)[key]
|
|
197
|
-
);
|
|
198
|
-
|
|
199
|
-
if (!Object.is(currentValue, newValue)) {
|
|
200
|
-
// if value changed, set it in result and mark change
|
|
201
|
-
anyChange = true;
|
|
202
|
-
result[key] = newValue;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (anyChange) {
|
|
208
|
-
// something changed, return new value
|
|
209
|
-
return result;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// nothing changed, return old value
|
|
213
|
-
return value;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function patchArr<T extends any[], C, R>(
|
|
217
|
-
value: T,
|
|
218
|
-
root: R,
|
|
219
|
-
patchItem: T | Patch.Tup<T, C, R>
|
|
220
|
-
): T {
|
|
221
|
-
if (Array.isArray(patchItem)) {
|
|
222
|
-
// value is a normal array
|
|
223
|
-
// patch is a complete replacement of current array
|
|
224
|
-
|
|
225
|
-
return patchItem;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// value is a tuple
|
|
229
|
-
// patch is an object containing numeric keys with function values
|
|
230
|
-
// that update the tuple at the given indices
|
|
231
|
-
|
|
232
|
-
// copy the tuple
|
|
233
|
-
const result = [...value] as T;
|
|
234
|
-
let anyChange = false;
|
|
235
|
-
|
|
236
|
-
// loop over all index keys in object
|
|
237
|
-
for (const index in patchItem) {
|
|
238
|
-
const numIndex = index as any as number;
|
|
239
|
-
|
|
240
|
-
// patch the tuple at the given index
|
|
241
|
-
const currentValue = result[numIndex];
|
|
242
|
-
const newValue = patchEntry(
|
|
243
|
-
currentValue,
|
|
244
|
-
value,
|
|
245
|
-
root,
|
|
246
|
-
(patchItem as any)[index]
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
if (!Object.is(newValue, currentValue)) {
|
|
250
|
-
// if value changed, set it in result and mark change
|
|
251
|
-
anyChange = true;
|
|
252
|
-
result[numIndex] = newValue;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (anyChange) {
|
|
257
|
-
// something changed, return new value
|
|
258
|
-
return result;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// nothing changed, return old value
|
|
262
|
-
return value;
|
|
263
|
-
}
|
package/dist/bun/path.mts
DELETED
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import type { IsAnyFunc, IsArray, IsPlainObj } from '@rimbu/base';
|
|
2
|
-
import { Deep, type Patch } from './internal.mts';
|
|
3
|
-
import type { Tuple } from './tuple.mts';
|
|
4
|
-
|
|
5
|
-
export namespace Path {
|
|
6
|
-
/**
|
|
7
|
-
* A string representing a path into an (nested) object of type T.
|
|
8
|
-
* @typeparam T - the object type to select in
|
|
9
|
-
* @example
|
|
10
|
-
* ```ts
|
|
11
|
-
* const p: Path.Get<{ a: { b: { c : 5 } } }> = 'a.b'
|
|
12
|
-
* ```
|
|
13
|
-
*/
|
|
14
|
-
export type Get<T> = Path.Internal.Generic<T, false, false, true>;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* A string representing a path into an (nested) object of type T.
|
|
18
|
-
* @typeparam T - the object type to select in
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* const p: Path.Set<{ a: { b: { c : 5 } } }> = 'a.b'
|
|
22
|
-
* ```
|
|
23
|
-
*/
|
|
24
|
-
export type Set<T> = Path.Internal.Generic<T, true, false, true>;
|
|
25
|
-
|
|
26
|
-
export namespace Internal {
|
|
27
|
-
/**
|
|
28
|
-
* Determines the allowed paths into a value of type `T`.
|
|
29
|
-
* @typeparam T - the source type
|
|
30
|
-
* @typeparam Write - if true the path should be writable (no optional chaining)
|
|
31
|
-
* @typeparam Maybe - if true the value at the current path is optional
|
|
32
|
-
* @typeparam First - if true this is the root call
|
|
33
|
-
* @note type is mapped as template literal to prevent non-string types to leak through
|
|
34
|
-
*/
|
|
35
|
-
export type Generic<
|
|
36
|
-
T,
|
|
37
|
-
Write extends boolean,
|
|
38
|
-
Maybe extends boolean,
|
|
39
|
-
First extends boolean = false,
|
|
40
|
-
> = `${IsAnyFunc<T> extends true
|
|
41
|
-
? // functions can not be further decomposed
|
|
42
|
-
''
|
|
43
|
-
: // empty string is always an option
|
|
44
|
-
'' | Path.Internal.NonEmpty<T, Write, Maybe, First>}`;
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Determines the allowed non-empty paths into a value of type `T`.
|
|
48
|
-
* @typeparam T - the source type
|
|
49
|
-
* @typeparam Write - if true the path should be writable (no optional chaining)
|
|
50
|
-
* @typeparam Maybe - if true the value at the current path is optional
|
|
51
|
-
* @typeparam First - if true this is the root call
|
|
52
|
-
*/
|
|
53
|
-
export type NonEmpty<
|
|
54
|
-
T,
|
|
55
|
-
Write extends boolean,
|
|
56
|
-
Maybe extends boolean,
|
|
57
|
-
First extends boolean,
|
|
58
|
-
> =
|
|
59
|
-
Path.Internal.IsOptional<T> extends true
|
|
60
|
-
? // the value T may be null or undefined, check whether further chaining is allowed
|
|
61
|
-
Write extends false
|
|
62
|
-
? // path is not used to write to, so optional chaining is allowed
|
|
63
|
-
Path.Internal.Generic<Exclude<T, undefined | null>, Write, true>
|
|
64
|
-
: // path can be written to, no optional chaining allowed
|
|
65
|
-
never
|
|
66
|
-
: // determine separator, and continue with non-optional value
|
|
67
|
-
`${Path.Internal.Separator<
|
|
68
|
-
First,
|
|
69
|
-
Maybe,
|
|
70
|
-
IsArray<T>
|
|
71
|
-
>}${Path.Internal.NonOptional<T, Write, Maybe>}`;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Determines the allowed paths into a non-optional value of type `T`.
|
|
75
|
-
* @typeparam T - the source type
|
|
76
|
-
* @typeparam Write - if true the path should be writable (no optional chaining)
|
|
77
|
-
* @typeparam Maybe - if true the value at the current path is optional
|
|
78
|
-
*/
|
|
79
|
-
export type NonOptional<T, Write extends boolean, Maybe extends boolean> =
|
|
80
|
-
Tuple.IsTuple<T> extends true
|
|
81
|
-
? // determine allowed paths for tuple
|
|
82
|
-
Path.Internal.Tup<T, Write, Maybe>
|
|
83
|
-
: T extends readonly any[]
|
|
84
|
-
? // determine allowed paths for array
|
|
85
|
-
Write extends false
|
|
86
|
-
? // path is not writable so arrays are allowed
|
|
87
|
-
Path.Internal.Arr<T>
|
|
88
|
-
: // path is writable, no arrays allowed
|
|
89
|
-
never
|
|
90
|
-
: IsPlainObj<T> extends true
|
|
91
|
-
? // determine allowed paths for object
|
|
92
|
-
Path.Internal.Obj<T, Write, Maybe>
|
|
93
|
-
: // no match
|
|
94
|
-
never;
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Determines the allowed paths for a tuple. Since tuples have fixed types, they do not
|
|
98
|
-
* need to be optional, in contrast to arrays.
|
|
99
|
-
* @typeparam T - the input tuple type
|
|
100
|
-
* @typeparam Write - if true the path should be writable (no optional chaining)
|
|
101
|
-
* @typeparam Maybe - if true the value at the current path is optional
|
|
102
|
-
*/
|
|
103
|
-
export type Tup<T, Write extends boolean, Maybe extends boolean> = {
|
|
104
|
-
[K in Tuple.KeysOf<T>]: `[${K}]${Path.Internal.Generic<T[K], Write, Maybe>}`;
|
|
105
|
-
}[Tuple.KeysOf<T>];
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Determines the allowed paths for an array.
|
|
109
|
-
* @typeparam T - the input array type
|
|
110
|
-
*/
|
|
111
|
-
export type Arr<T extends readonly any[]> =
|
|
112
|
-
// first `[index]` and then the rest of the path, which cannot be Write (since optional) and must be Maybe
|
|
113
|
-
`[${number}]${Path.Internal.Generic<T[number], false, true>}`;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Determines the allowed paths for an object.
|
|
117
|
-
* @typeparam T - the input object type
|
|
118
|
-
* @typeparam Write - if true the path should be writable (no optional chaining)
|
|
119
|
-
* @typeparam Maybe - if true the value at the current path is optional
|
|
120
|
-
*/
|
|
121
|
-
export type Obj<T, Write extends boolean, Maybe extends boolean> = {
|
|
122
|
-
[K in keyof T]: `${K & string}${Path.Internal.Generic<
|
|
123
|
-
T[K],
|
|
124
|
-
Write,
|
|
125
|
-
// If writable (not optional), Maybe is false. If value is optional, Maybe is true. Otherwise, forward current Maybe.
|
|
126
|
-
Write extends true ? false : Path.Internal.IsOptional<T[K], true, Maybe>
|
|
127
|
-
>}`;
|
|
128
|
-
}[keyof T];
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Determines the allowed path part separator based on the input types.
|
|
132
|
-
* @typeparam First - if true, this is the first call
|
|
133
|
-
* @typeparam Maybe - if true, the value is optional
|
|
134
|
-
* @typeparam IsArray - if true, the value is an array
|
|
135
|
-
*/
|
|
136
|
-
export type Separator<
|
|
137
|
-
First extends boolean,
|
|
138
|
-
Maybe extends boolean,
|
|
139
|
-
IsArray extends boolean,
|
|
140
|
-
> = Maybe extends true
|
|
141
|
-
? First extends true
|
|
142
|
-
? // first optional value cannot have separator
|
|
143
|
-
never
|
|
144
|
-
: // non-first optional value must have separator
|
|
145
|
-
'?.'
|
|
146
|
-
: First extends true
|
|
147
|
-
? // first non-optional value has empty separator
|
|
148
|
-
''
|
|
149
|
-
: IsArray extends true
|
|
150
|
-
? // array selectors do not have separator
|
|
151
|
-
''
|
|
152
|
-
: // normal separator
|
|
153
|
-
'.';
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Determines whether the given type `T` is optional, that is, whether it can be null or undefined.
|
|
157
|
-
* @typeparam T - the input type
|
|
158
|
-
* @typeparam True - the value to return if `T` is optional
|
|
159
|
-
* @typeparam False - the value to return if `T` is mandatory
|
|
160
|
-
*/
|
|
161
|
-
export type IsOptional<T, True = true, False = false> = undefined extends T
|
|
162
|
-
? // is optional
|
|
163
|
-
True
|
|
164
|
-
: null extends T
|
|
165
|
-
? // is optional
|
|
166
|
-
True
|
|
167
|
-
: // not optional
|
|
168
|
-
False;
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Returns type `T` if `Maybe` is false, `T | undefined` otherwise.
|
|
172
|
-
* @typeparam T - the input type
|
|
173
|
-
* @typeparam Maybe - if true, the return type value should be optional
|
|
174
|
-
*/
|
|
175
|
-
export type MaybeValue<T, Maybe extends boolean> = Maybe extends true
|
|
176
|
-
? T | undefined
|
|
177
|
-
: T;
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Utility type to only add non-empty string types to a string array.
|
|
181
|
-
* @typeparam A - the input string array
|
|
182
|
-
* @typeparam T - the string value to optionally add
|
|
183
|
-
*/
|
|
184
|
-
export type AppendIfNotEmpty<
|
|
185
|
-
A extends string[],
|
|
186
|
-
T extends string,
|
|
187
|
-
> = T extends ''
|
|
188
|
-
? // empty string, do not add
|
|
189
|
-
A
|
|
190
|
-
: // non-empty string, add to array
|
|
191
|
-
[...A, T];
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* The result type when selecting from object type T a path with type P.
|
|
196
|
-
* @typeparam T - the object type to select in
|
|
197
|
-
* @typeparam P - a Path in object type T
|
|
198
|
-
* @example
|
|
199
|
-
* ```ts
|
|
200
|
-
* let r!: Path.Result<{ a: { b: { c: number } } }, 'a.b'>;
|
|
201
|
-
* // => type of r: { c: number }
|
|
202
|
-
* ```
|
|
203
|
-
*/
|
|
204
|
-
export type Result<T, P extends string> = Path.Result.For<
|
|
205
|
-
T,
|
|
206
|
-
Path.Result.Tokenize<P>,
|
|
207
|
-
false
|
|
208
|
-
>;
|
|
209
|
-
|
|
210
|
-
export namespace Result {
|
|
211
|
-
/**
|
|
212
|
-
* Determines the result type for an array of tokens representing subpaths in type `T`.
|
|
213
|
-
* @typeparam T - the current source type
|
|
214
|
-
* @typeparam Tokens - an array of elements indicating a path into the source type
|
|
215
|
-
* @typeparam Maybe - if true indicates that the path may be undefined
|
|
216
|
-
*/
|
|
217
|
-
export type For<
|
|
218
|
-
T,
|
|
219
|
-
Tokens,
|
|
220
|
-
Maybe extends boolean = Path.Internal.IsOptional<T>,
|
|
221
|
-
> = Tokens extends []
|
|
222
|
-
? // no more token
|
|
223
|
-
Path.Internal.MaybeValue<T, Maybe>
|
|
224
|
-
: Path.Internal.IsOptional<T> extends true
|
|
225
|
-
? // T can be null or undefined, so continue with Maybe set to true
|
|
226
|
-
Path.Result.For<Exclude<T, undefined | null>, Tokens, Maybe>
|
|
227
|
-
: Tokens extends ['?.', infer Key, ...infer Rest]
|
|
228
|
-
? // optional chaining, process first part and set Maybe to true
|
|
229
|
-
Path.Result.For<Path.Result.Part<T, Key, Maybe>, Rest, true>
|
|
230
|
-
: Tokens extends ['.', infer Key, ...infer Rest]
|
|
231
|
-
? // normal chaining, process first part and continue
|
|
232
|
-
Path.Result.For<Path.Result.Part<T, Key, false>, Rest, Maybe>
|
|
233
|
-
: Tokens extends [infer Key, ...infer Rest]
|
|
234
|
-
? // process first part, and continue
|
|
235
|
-
Path.Result.For<Path.Result.Part<T, Key, false>, Rest, Maybe>
|
|
236
|
-
: never;
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Determines the result of getting the property/index `K` from type `T`, taking into
|
|
240
|
-
* account that the value may be optional.
|
|
241
|
-
* @typeparam T - the current source type
|
|
242
|
-
* @typeparam K - the key to get from the source type
|
|
243
|
-
* @typeparam Maybe - if true indicates that the path may be undefined
|
|
244
|
-
*/
|
|
245
|
-
export type Part<T, K, Maybe extends boolean> =
|
|
246
|
-
IsArray<T> extends true
|
|
247
|
-
? // for arrays, Maybe needs to be set to true to force optional chaining
|
|
248
|
-
// for tuples, Maybe should be false
|
|
249
|
-
Path.Internal.MaybeValue<
|
|
250
|
-
T[K & keyof T],
|
|
251
|
-
Tuple.IsTuple<T> extends true ? Maybe : true
|
|
252
|
-
>
|
|
253
|
-
: // Return the type at the given key, and take `Maybe` into account
|
|
254
|
-
Path.Internal.MaybeValue<T[K & keyof T], Maybe>;
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Converts a path string into separate tokens in a string array.
|
|
258
|
-
* @typeparam P - the literal string path type
|
|
259
|
-
* @typeparam Token - the token currently being produced
|
|
260
|
-
* @typeparam Res - the resulting literal string token array
|
|
261
|
-
*/
|
|
262
|
-
export type Tokenize<
|
|
263
|
-
P extends string,
|
|
264
|
-
Token extends string = '',
|
|
265
|
-
Res extends string[] = [],
|
|
266
|
-
> = P extends ''
|
|
267
|
-
? // no more input to process, return result
|
|
268
|
-
Path.Internal.AppendIfNotEmpty<Res, Token>
|
|
269
|
-
: P extends `[${infer Index}]${infer Rest}`
|
|
270
|
-
? // input is an array selector, append index to tokens. Continue with new token
|
|
271
|
-
Tokenize<
|
|
272
|
-
Rest,
|
|
273
|
-
'',
|
|
274
|
-
[...Path.Internal.AppendIfNotEmpty<Res, Token>, Index]
|
|
275
|
-
>
|
|
276
|
-
: P extends `?.${infer Rest}`
|
|
277
|
-
? // optional chaining, append to tokens. Continue with new token
|
|
278
|
-
Tokenize<
|
|
279
|
-
Rest,
|
|
280
|
-
'',
|
|
281
|
-
[...Path.Internal.AppendIfNotEmpty<Res, Token>, '?.']
|
|
282
|
-
>
|
|
283
|
-
: P extends `.${infer Rest}`
|
|
284
|
-
? // normal chaining, append to tokens. Continue with new token
|
|
285
|
-
Tokenize<
|
|
286
|
-
Rest,
|
|
287
|
-
'',
|
|
288
|
-
[...Path.Internal.AppendIfNotEmpty<Res, Token>, '.']
|
|
289
|
-
>
|
|
290
|
-
: P extends `${infer First}${infer Rest}`
|
|
291
|
-
? // process next character
|
|
292
|
-
Tokenize<Rest, `${Token}${First}`, Res>
|
|
293
|
-
: never;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
/**
|
|
297
|
-
* Regular expression used to split a path string into tokens.
|
|
298
|
-
*/
|
|
299
|
-
export const stringSplitRegex = /\?\.|\.|\[|\]/g;
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* The allowed values of a split path.
|
|
303
|
-
*/
|
|
304
|
-
export type StringSplit = (string | number | undefined)[];
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Return the given `path` string split into an array of subpaths.
|
|
308
|
-
* @param path - the input string path
|
|
309
|
-
*/
|
|
310
|
-
export function stringSplit(path: string): Path.StringSplit {
|
|
311
|
-
return path.split(Path.stringSplitRegex);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Returns the value resulting from selecting the given `path` in the given `source` object.
|
|
317
|
-
* It supports optional chaining for nullable values or values that may be undefined, and also
|
|
318
|
-
* for accessing objects inside an array.
|
|
319
|
-
* There is currently no support for forcing non-null (the `!` operator).
|
|
320
|
-
* @typeparam T - the object type to select in
|
|
321
|
-
* @typeparam P - a Path in object type T
|
|
322
|
-
* @param source - the object to select in
|
|
323
|
-
* @param path - the path into the object
|
|
324
|
-
* @example
|
|
325
|
-
* ```ts
|
|
326
|
-
* const value = { a: { b: { c: [{ d: 5 }, { d: 6 }] } } }
|
|
327
|
-
* Deep.getAt(value, 'a.b');
|
|
328
|
-
* // => { c: [{ d: 5 }, { d: 6 }] }
|
|
329
|
-
* Deep.getAt(value, 'a.b.c');
|
|
330
|
-
* // => [{ d: 5 }, { d: 6 }]
|
|
331
|
-
* Deep.getAt(value, 'a.b.c[1]');
|
|
332
|
-
* // => { d: 6 }
|
|
333
|
-
* Deep.getAt(value, 'a.b.c[1]?.d');
|
|
334
|
-
* // => 6
|
|
335
|
-
* ```
|
|
336
|
-
*/
|
|
337
|
-
export function getAt<T, P extends Path.Get<T>>(
|
|
338
|
-
source: T,
|
|
339
|
-
path: P
|
|
340
|
-
): Path.Result<T, P> {
|
|
341
|
-
if (path === '') {
|
|
342
|
-
// empty path always directly returns source value
|
|
343
|
-
return source as any;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const items = Path.stringSplit(path);
|
|
347
|
-
|
|
348
|
-
// start with `source` as result value
|
|
349
|
-
let result = source as any;
|
|
350
|
-
|
|
351
|
-
for (const item of items) {
|
|
352
|
-
if (undefined === item || item === '' || item === '[') {
|
|
353
|
-
// ignore irrelevant items
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (undefined === result || null === result) {
|
|
358
|
-
// optional chaining assumed and no value available, skip rest of path and return undefined
|
|
359
|
-
return undefined as any;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// set current result to subpath value
|
|
363
|
-
result = result[item];
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return result;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Patches the value at the given path in the source to the given value.
|
|
371
|
-
* Because the path to update must exist in the `source` object, optional
|
|
372
|
-
* chaining and array indexing is not allowed.
|
|
373
|
-
* @param source - the object to update
|
|
374
|
-
* @param path - the path in the object to update
|
|
375
|
-
* @param patchItem - the patch for the value at the given path
|
|
376
|
-
* @example
|
|
377
|
-
* ```ts
|
|
378
|
-
* const value = { a: { b: { c: 5 } } };
|
|
379
|
-
* Deep.patchAt(value, 'a.b.c', v => v + 5);
|
|
380
|
-
* // => { a: { b: { c: 6 } } }
|
|
381
|
-
* ```
|
|
382
|
-
*/
|
|
383
|
-
export function patchAt<T, P extends Path.Set<T>, C = Path.Result<T, P>>(
|
|
384
|
-
source: T,
|
|
385
|
-
path: P,
|
|
386
|
-
patchItem: Patch<Path.Result<T, P>, Path.Result<T, P> & C>
|
|
387
|
-
): T {
|
|
388
|
-
if (path === '') {
|
|
389
|
-
return Deep.patch(source, patchItem as any);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const items = Path.stringSplit(path);
|
|
393
|
-
|
|
394
|
-
// creates a patch object based on the current path
|
|
395
|
-
function createPatchPart(index: number, target: any): any {
|
|
396
|
-
if (index === items.length) {
|
|
397
|
-
// processed all items, return the input `patchItem`
|
|
398
|
-
return patchItem;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const item = items[index];
|
|
402
|
-
|
|
403
|
-
if (undefined === item || item === '') {
|
|
404
|
-
// empty items can be ignored
|
|
405
|
-
return createPatchPart(index + 1, target);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
if (item === '[') {
|
|
409
|
-
// next item is array index, set arrayMode to true
|
|
410
|
-
return createPatchPart(index + 1, target);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// create object with subPart as property key, and the restuls of processing next parts as value
|
|
414
|
-
const result = {
|
|
415
|
-
[item]: createPatchPart(index + 1, target[item]),
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
if (Array.isArray(target)) {
|
|
419
|
-
// target in source object is array/tuple, so the patch should be object
|
|
420
|
-
return result;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// target in source is not an array, so it patch should be an array
|
|
424
|
-
return [result];
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
return Deep.patch(source, createPatchPart(0, source));
|
|
428
|
-
}
|