@rimbu/deep 2.0.3 → 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.
@@ -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
- }