maraj 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -76
- package/dist/index.d.mts +33 -45
- package/dist/index.d.ts +33 -45
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,102 +1,100 @@
|
|
|
1
1
|
|
|
2
2
|
# Maraj
|
|
3
3
|
|
|
4
|
-
A lightweight
|
|
4
|
+
A fast lightweight utility for creating **immutable updates**. Full typescript support. Only 600 bytes gziped. Includes usefull utilities for manipulating updates (like remove, select).
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
### Features
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
- Full typescript support.
|
|
9
|
+
- Correct type inference.
|
|
10
|
+
- Multiple updates at same time mantaining the order of execution.
|
|
11
|
+
- Functional updates.
|
|
9
12
|
|
|
10
|
-
|
|
13
|
+
**<u>For Typescript:</u>** in tsconfig.json set **"strictNullChecks"** and **"noUncheckedIndexedAccess"** to true for correct type inference of optional properties (so it can correctly differentiate between optional props and props with possible undefined value)
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
```ts
|
|
16
|
+
const originalValue = { name: 'one', lastname: 'two' modified: false, files: [{ filename: 'one' }] }
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
const updatedValue = updateImmutably(orignalValue, {
|
|
19
|
+
'name': 'updated',
|
|
20
|
+
'modified': (value, fullObject) => !value,
|
|
21
|
+
'files.0.filename': 'zero',
|
|
22
|
+
})
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
// updatedValue = { name: 'updated', lastname: 'two' modified: true, files: [{ filename: 'zero' }] }
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
originalValue.files[1] === updatedValue.files[1] // true
|
|
27
|
+
```
|
|
21
28
|
|
|
22
|
-
|
|
29
|
+
### Limitations
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
2. An **Update object** (key-value pairs representing the updates we want to make)
|
|
31
|
+
- If "addNonExistentPropsAndIndexes" is enabled, numbers will always create an array if the array/object doesnt exist beforehand.
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
```ts
|
|
34
|
+
const updatedValue = updateImmutably({}, {
|
|
35
|
+
'files.1.name': 'new name',
|
|
36
|
+
}, true)
|
|
37
|
+
// updatedValue = { files: [<empty>, { name: 'new name' }] }
|
|
38
|
+
```
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
- The type "UpdateDotObject" using computed properties ( [key] ) will always throw a ts error even if the typing is correct. [Playground simple explanation](https://www.typescriptlang.org/play?ts=5.3.2#code/LAKALgngDgpgBAGQJYGcxwLxwOQENtwA+cABgCQDeAdgK4C2ARjAE4C+JooksiqYASjADGAe2YATTHApwA2gGk4SKrzQBdAPwAuOItacQoqmjhDmMXGBiDREqQB4AKnBgAPK1XEpVYAHwAKAGsYCB1HAEpMX2lQODijE3MhHWQ0GzFJLApYuNyAejy4AD0wbxZmMR0UZSF4ADcAFgA6AGYmgFYldAB3JAAbPrgwAAsK7rhcFXKxCfQhYeFA5QBzUxE6KBorSWCIb0nJOtw+mhhvGmqqVasNvst4PqQrZmOyupgVJAAzIYWh6BWSm8tiSYByuTkuzUOl24P0IFy5jANGYKiSoFYQA)
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const updateFieldStore = <TPath extends DotPaths<Store>>(
|
|
44
|
+
path: TPath,
|
|
45
|
+
newValue: UpdateDotPathValue<Store, TPath>
|
|
46
|
+
) => {
|
|
47
|
+
const update: UpdateDotPathObject<Store> = {
|
|
48
|
+
// ^throws ts error even if the typing is correct
|
|
49
|
+
[path]: newValue
|
|
50
|
+
} // fix: coherce it with " as UpdateDotPathObject<Store>"
|
|
51
|
+
return update
|
|
35
52
|
}
|
|
36
53
|
```
|
|
37
54
|
|
|
38
|
-
|
|
55
|
+
- Use the "set" utility if typescript doesn't distinguish properly _"an optional field"_ vs a _"field which can have 'undefined' as type"_. Usually happens in optional nested props inside arrays.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const updatedValue = updateImmutably(data, {
|
|
59
|
+
'files.0.optionalProperty': 'new name',
|
|
60
|
+
'files.1.optionalProperty': undefined, //no ts error
|
|
61
|
+
'files.0.optionalProperty': set('new name'),
|
|
62
|
+
'files.1.optionalProperty': set(undefined), //throws ts error, undefined cant be a value.
|
|
63
|
+
})
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Creating an Immutable Update
|
|
39
67
|
|
|
40
|
-
|
|
41
|
-
- For arrays just use its index value
|
|
42
|
-
- Can accept conditional statements. (If the key = false or undefined or null it will skip that field update.)
|
|
68
|
+
Arguments:
|
|
43
69
|
|
|
44
|
-
-
|
|
70
|
+
- Original Object
|
|
71
|
+
- Update Object
|
|
72
|
+
- If it should add non-existent properties/indexes
|
|
45
73
|
|
|
46
74
|
## Example
|
|
47
75
|
|
|
48
76
|
```js
|
|
49
|
-
import
|
|
77
|
+
import { updateImmutably } from "maraj";
|
|
50
78
|
|
|
51
79
|
const data = {
|
|
52
|
-
|
|
53
|
-
works: [
|
|
80
|
+
profile: { name: "Lukasz" },
|
|
81
|
+
works: ["soum", "fi"],
|
|
82
|
+
history: [
|
|
54
83
|
{ title: "soum", size: 90, available: true, info: ["info1"] },
|
|
55
84
|
{ title: "fi", size: 30, available: false, info: ["infoA"] },
|
|
56
85
|
]
|
|
57
86
|
}
|
|
58
87
|
|
|
59
88
|
const newUpdatedObject = updateImmutably(data, {
|
|
60
|
-
"age": 28, //add new
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
"person.works": (v) => spread(v, "portfolio") //common utilities
|
|
89
|
+
"profile.age": 28, //add new properties if "addNonExistentPropsAndIndexes" is enabled
|
|
90
|
+
"profile.name": "Lucas", //update values
|
|
91
|
+
"works.1": "gs", //nested array update
|
|
92
|
+
"history.1.available": (currentValue) => (!currentValue), //functional update
|
|
93
|
+
"works": (v) => [...v, 'portfolio'] //add new value to array
|
|
66
94
|
})
|
|
67
|
-
|
|
68
|
-
/** Does not make a deep copy and "falsy" fields are ignored */
|
|
69
|
-
newUpdatedObject.works[0] === data.works[0] //true
|
|
70
|
-
|
|
71
|
-
/** New Updated Object
|
|
72
|
-
{
|
|
73
|
-
person: { name: "Lucas", works: ["soum", "gs", "portfolio"], age: 28 },
|
|
74
|
-
works: [
|
|
75
|
-
{ title: "soum", size: 90, available: true, info: ["info1"] },
|
|
76
|
-
{ title: "fi", size: 30, available: true, info: ["infoA"] },
|
|
77
|
-
]
|
|
78
|
-
}
|
|
79
|
-
*/
|
|
80
95
|
```
|
|
81
96
|
|
|
82
|
-
##
|
|
83
|
-
|
|
84
|
-
### spread
|
|
85
|
-
|
|
86
|
-
**For Objects and Arrays: Spreads** its argument into the key provided in the updateImmutably.
|
|
87
|
-
**For the rest: Sets** its argument into the key provided in the updateImmutably.
|
|
88
|
-
|
|
89
|
-
```js
|
|
90
|
-
import {spread} from 'maraj'
|
|
91
|
-
|
|
92
|
-
const newUpdatedObject = updateImmutably(data,{
|
|
93
|
-
'person': (value) => spread(value, {info1: 'new Info'}), //same as (newValue) => (value) => ({...value, ...newValue})
|
|
94
|
-
'works': (value) => spread(value, {title: 'new project', size: 20}) //same as (newValueToAdd) => (value) => ([...value, ...newValueToAdd])
|
|
95
|
-
'person.works': (value) => spread(value, "portfolio") //same as (newValue) => (value) => (newValue)
|
|
96
|
-
})
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### remove
|
|
97
|
+
## Remove Utility
|
|
100
98
|
|
|
101
99
|
Removes the **key of an object** or the **index of an array** in the key provided in the **updateImmutably.**
|
|
102
100
|
|
|
@@ -104,36 +102,35 @@ Removes the **key of an object** or the **index of an array** in the key provide
|
|
|
104
102
|
import {remove} from 'maraj'
|
|
105
103
|
|
|
106
104
|
const newUpdatedObject = updateImmutably(person,{
|
|
107
|
-
'
|
|
108
|
-
'works': (value) => remove(value, 1) // returns new array
|
|
105
|
+
'profile': (value) => remove(value, 'name'), // returns new value without the "name" prop
|
|
106
|
+
'works': (value) => remove(value, 1), // returns new array without the provided index (1)
|
|
107
|
+
'works': (value) => remove(value, [1, "0"]), // returns new array without the provided index (0,1)
|
|
109
108
|
})
|
|
110
109
|
```
|
|
111
110
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
**Creates a store selector** from provided string path.
|
|
111
|
+
## Select Utility
|
|
115
112
|
|
|
116
|
-
|
|
113
|
+
Selects the value of an object/array from a string-dot-path. Usefull in state management libraries.
|
|
117
114
|
|
|
118
115
|
```js
|
|
119
116
|
import {select} from 'maraj'
|
|
120
117
|
|
|
121
118
|
const stringPath = 'works.1.size'
|
|
122
119
|
|
|
123
|
-
const
|
|
120
|
+
const work1SizeSelector = (state) => select(state, stringPath) // will behave as (store) => store[works][1][size]
|
|
124
121
|
```
|
|
125
122
|
|
|
126
|
-
|
|
123
|
+
## SplitPathAtLastKey Utility
|
|
127
124
|
|
|
128
|
-
Usefull when you want to
|
|
125
|
+
Usefull when you want to retrieve the last key (for example an id).
|
|
129
126
|
|
|
130
127
|
Splits string path (example:'projects.1.data.size' ) into two:
|
|
131
128
|
|
|
132
|
-
- id: the last key in the path (in this case 'size')
|
|
133
129
|
- remainingPath: the remaining path (in this case 'projects.1.data')
|
|
130
|
+
- lastKey: the last key in the path (in this case 'size')
|
|
134
131
|
|
|
135
132
|
```js
|
|
136
|
-
import {splitPathAtLastKey} from 'maraj'
|
|
133
|
+
import { splitPathAtLastKey } from 'maraj'
|
|
137
134
|
|
|
138
135
|
const stringPath = 'works.1.size'
|
|
139
136
|
|
|
@@ -141,3 +138,16 @@ const [remainingPath, last] = splitPathAtLastKey(stringPath)
|
|
|
141
138
|
// last = 'size'
|
|
142
139
|
// remainingPath = 'works.1'
|
|
143
140
|
```
|
|
141
|
+
|
|
142
|
+
## Set Utility
|
|
143
|
+
|
|
144
|
+
In some unusual cases (usually when updating an optional object prop inside an array) typescript infers incorrectly the optional key vs a key with undefined value. To fix this typing issue, use the "set" function so it correctly infers the type in the field.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
const updatedValue = updateImmutably(data, {
|
|
148
|
+
'files.0.optionalProperty': 'new name',
|
|
149
|
+
'files.1.optionalProperty': undefined, //no ts error
|
|
150
|
+
'files.0.optionalProperty': set('new name'),
|
|
151
|
+
'files.1.optionalProperty': set(undefined), //throws ts error, undefined cant be a value.
|
|
152
|
+
})
|
|
153
|
+
```
|
package/dist/index.d.mts
CHANGED
|
@@ -1,25 +1,17 @@
|
|
|
1
|
-
type Remove = <T extends object>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : (keyof T | (keyof T)[])) => T;
|
|
2
|
-
/**
|
|
3
|
-
* Removes properties or indexes, order doesn't matter.
|
|
4
|
-
*/
|
|
5
|
-
declare const remove: Remove;
|
|
6
|
-
|
|
7
1
|
/**
|
|
8
2
|
* Paths implementation
|
|
9
3
|
*/
|
|
10
4
|
type PrimitivesAndNativeObjects = null | undefined | string | number | boolean | symbol | bigint | Date | FileList | File;
|
|
11
5
|
type IsTuple<T extends ReadonlyArray<any>> = number extends T['length'] ? false : true;
|
|
12
6
|
type Index = '_INDEX_';
|
|
13
|
-
type
|
|
14
|
-
type
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
type ObjectPaths<T, TLength extends boolean> = {
|
|
18
|
-
[Key in keyof T & string]: `${Key}` | `${Key}.${RootFlatten<T[Key], TLength>}`;
|
|
7
|
+
type ArrayIndexPaths = `${number}` | Index;
|
|
8
|
+
type ArrayPaths<TInnerType> = `${ArrayIndexPaths}` | `${ArrayIndexPaths}.${Flatten<TInnerType>}`;
|
|
9
|
+
type ObjectPaths<T> = {
|
|
10
|
+
[Key in keyof T & string]: `${Key}` | `${Key}.${Flatten<T[Key]>}`;
|
|
19
11
|
}[keyof T & string];
|
|
20
|
-
type
|
|
21
|
-
type
|
|
22
|
-
type
|
|
12
|
+
type TuplePaths<T> = ObjectPaths<Omit<T, keyof any[]>>;
|
|
13
|
+
type Flatten<T> = T extends ReadonlyArray<infer U> ? IsTuple<T> extends true ? TuplePaths<T> : ArrayPaths<U> : T extends PrimitivesAndNativeObjects ? never : ObjectPaths<T>;
|
|
14
|
+
type DotPaths<T> = Flatten<T>;
|
|
23
15
|
/**
|
|
24
16
|
* Type to know when a path is indexing an array
|
|
25
17
|
*/
|
|
@@ -27,14 +19,31 @@ type PathIndexingArray<T extends string | number> = `${T}` | `${T}.${string}` |
|
|
|
27
19
|
/**
|
|
28
20
|
* Nested Property Type implementation
|
|
29
21
|
*/
|
|
30
|
-
type NestedProperty<TObject, TPath
|
|
22
|
+
type NestedProperty<TObject, TPath> = TPath extends '' ? TObject : TPath extends keyof TObject ? TObject[TPath] : TPath extends (`${number}` | Index) ? TObject extends Array<infer U> ? TPath extends `${number}.${infer K}` ? K extends keyof U ? NestedProperty<U, K> : never : U : never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? NestedProperty<Required<TObject>[K], R> : K extends `${number}` | Index ? TObject extends Array<infer U> ? NestedProperty<U, R> : never : never : never;
|
|
31
23
|
type ValueInDotPath<TObject, TPath extends DotPaths<TObject> | ''> = NestedProperty<TObject, TPath>;
|
|
24
|
+
type DotPathUpdateValue<TObject, TPath extends DotPaths<TObject>> = TPath extends PathIndexingArray<Index> ? [`PATH ERROR: replace _INDEX_ with a NUMBER`, ValueInDotPath<TObject, TPath>, ((fieldValue: ValueInDotPath<TObject, TPath>, fullObject: TObject) => ValueInDotPath<TObject, TPath>)] : ValueInDotPath<TObject, TPath> | ((fieldValue: ValueInDotPath<TObject, TPath>, fullObject: TObject) => ValueInDotPath<TObject, TPath>);
|
|
32
25
|
/**
|
|
33
26
|
* Update Object Type Implementation
|
|
34
27
|
*/
|
|
35
|
-
type DotPathUpdateObject<
|
|
36
|
-
[
|
|
37
|
-
}
|
|
28
|
+
type DotPathUpdateObject<TObject> = {
|
|
29
|
+
[TKey in DotPaths<TObject>]?: DotPathUpdateValue<TObject, TKey>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Optional and Required keys
|
|
33
|
+
*/
|
|
34
|
+
type NonPartialKeys = string | number | boolean | symbol | bigint | null | object;
|
|
35
|
+
type OptionalKeys<T> = {
|
|
36
|
+
[K in keyof T & string]: T[K] extends NonPartialKeys ? never : K;
|
|
37
|
+
}[keyof T & string];
|
|
38
|
+
type RequiredKeys<T> = {
|
|
39
|
+
[K in keyof T & string]: T[K] extends NonPartialKeys ? K : never;
|
|
40
|
+
}[keyof T & string];
|
|
41
|
+
|
|
42
|
+
type Remove = <T extends object>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : (OptionalKeys<T> | (OptionalKeys<T>)[])) => T;
|
|
43
|
+
/**
|
|
44
|
+
* Removes properties or indexes, order doesn't matter.
|
|
45
|
+
*/
|
|
46
|
+
declare const remove: Remove;
|
|
38
47
|
|
|
39
48
|
type Select = <T extends object, TPath extends (DotPaths<T> | '')>(state: T, path: TPath) => TPath extends PathIndexingArray<Index> ? never : TPath extends PathIndexingArray<number> ? ValueInDotPath<T, TPath> | undefined : ValueInDotPath<T, TPath>;
|
|
40
49
|
/**
|
|
@@ -43,47 +52,26 @@ type Select = <T extends object, TPath extends (DotPaths<T> | '')>(state: T, pat
|
|
|
43
52
|
*/
|
|
44
53
|
declare const select: Select;
|
|
45
54
|
|
|
46
|
-
type
|
|
55
|
+
type PathWithoutLastKey = string;
|
|
56
|
+
type LastKey = string;
|
|
57
|
+
type SplitDotPathAtLastKey = <T extends string>(dotPath: T) => [PathWithoutLastKey, LastKey];
|
|
47
58
|
/**
|
|
48
59
|
* Splits string dotPath into ID and RemainingPath
|
|
49
60
|
*/
|
|
50
61
|
declare const splitPathAtLastKey: SplitDotPathAtLastKey;
|
|
51
62
|
|
|
52
|
-
type Spread = <T extends object>(value: T, update: T extends Array<infer U> ? Array<U> : Partial<T>) => T;
|
|
53
|
-
/**
|
|
54
|
-
* Spreads if its an object or array to the target, if its not then returns same value
|
|
55
|
-
*/
|
|
56
|
-
declare const spread: Spread;
|
|
57
|
-
|
|
58
63
|
type Set = <T>(newValue: T) => (value: T) => T;
|
|
59
64
|
/**
|
|
60
65
|
* Allows to set strongly typed values (removing inacurate "undefined" inference) in nested values inside arrays
|
|
61
66
|
*/
|
|
62
67
|
declare const set: Set;
|
|
63
68
|
|
|
64
|
-
type
|
|
65
|
-
type NonStrictMode = 'implement-non-existent-paths';
|
|
66
|
-
type UpdateImmutably = {
|
|
67
|
-
<T extends object>(state: T, updates: DotPathUpdateObject<T>): T;
|
|
68
|
-
};
|
|
69
|
+
type UpdateImmutably = <T extends object>(state: T, updates: DotPathUpdateObject<T>, addNonExistentPropsAndIndexes?: true) => T;
|
|
69
70
|
/**
|
|
70
71
|
* Creates a new updated object/array applying the changes provided in the update object.
|
|
71
72
|
* It doesnt modify the original input.
|
|
72
73
|
* If any of the paths doesnt exists it returns
|
|
73
74
|
*/
|
|
74
75
|
declare const updateImmutably: UpdateImmutably;
|
|
75
|
-
type PosibleExpandedProps<T> = {
|
|
76
|
-
[K in keyof T]?: T[K] extends object ? PosibleExpandedProps<T[K]> & Record<any, any> : T[K];
|
|
77
|
-
};
|
|
78
|
-
type UpdateImmutablyNonStrictly = {
|
|
79
|
-
<T extends object>(state: T, updates: DotPathUpdateObject<T>, mode: SemiStrictMode): T;
|
|
80
|
-
<T extends object>(state: T, updates: DotPathUpdateObject<T>, mode: NonStrictMode): PosibleExpandedProps<T>;
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* Creates a new updated object/array applying the changes provided in the update object.
|
|
84
|
-
* It doesnt modify the original input.
|
|
85
|
-
* If any of the paths doesnt exists it adds it if possible.
|
|
86
|
-
*/
|
|
87
|
-
declare const updateImmutablyAllowingNewProps: UpdateImmutablyNonStrictly;
|
|
88
76
|
|
|
89
|
-
export { type DotPathUpdateObject, type DotPaths, type
|
|
77
|
+
export { type DotPathUpdateObject, type DotPathUpdateValue, type DotPaths, type OptionalKeys, type RequiredKeys, type ValueInDotPath, remove, select, set, splitPathAtLastKey, updateImmutably };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,25 +1,17 @@
|
|
|
1
|
-
type Remove = <T extends object>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : (keyof T | (keyof T)[])) => T;
|
|
2
|
-
/**
|
|
3
|
-
* Removes properties or indexes, order doesn't matter.
|
|
4
|
-
*/
|
|
5
|
-
declare const remove: Remove;
|
|
6
|
-
|
|
7
1
|
/**
|
|
8
2
|
* Paths implementation
|
|
9
3
|
*/
|
|
10
4
|
type PrimitivesAndNativeObjects = null | undefined | string | number | boolean | symbol | bigint | Date | FileList | File;
|
|
11
5
|
type IsTuple<T extends ReadonlyArray<any>> = number extends T['length'] ? false : true;
|
|
12
6
|
type Index = '_INDEX_';
|
|
13
|
-
type
|
|
14
|
-
type
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
type ObjectPaths<T, TLength extends boolean> = {
|
|
18
|
-
[Key in keyof T & string]: `${Key}` | `${Key}.${RootFlatten<T[Key], TLength>}`;
|
|
7
|
+
type ArrayIndexPaths = `${number}` | Index;
|
|
8
|
+
type ArrayPaths<TInnerType> = `${ArrayIndexPaths}` | `${ArrayIndexPaths}.${Flatten<TInnerType>}`;
|
|
9
|
+
type ObjectPaths<T> = {
|
|
10
|
+
[Key in keyof T & string]: `${Key}` | `${Key}.${Flatten<T[Key]>}`;
|
|
19
11
|
}[keyof T & string];
|
|
20
|
-
type
|
|
21
|
-
type
|
|
22
|
-
type
|
|
12
|
+
type TuplePaths<T> = ObjectPaths<Omit<T, keyof any[]>>;
|
|
13
|
+
type Flatten<T> = T extends ReadonlyArray<infer U> ? IsTuple<T> extends true ? TuplePaths<T> : ArrayPaths<U> : T extends PrimitivesAndNativeObjects ? never : ObjectPaths<T>;
|
|
14
|
+
type DotPaths<T> = Flatten<T>;
|
|
23
15
|
/**
|
|
24
16
|
* Type to know when a path is indexing an array
|
|
25
17
|
*/
|
|
@@ -27,14 +19,31 @@ type PathIndexingArray<T extends string | number> = `${T}` | `${T}.${string}` |
|
|
|
27
19
|
/**
|
|
28
20
|
* Nested Property Type implementation
|
|
29
21
|
*/
|
|
30
|
-
type NestedProperty<TObject, TPath
|
|
22
|
+
type NestedProperty<TObject, TPath> = TPath extends '' ? TObject : TPath extends keyof TObject ? TObject[TPath] : TPath extends (`${number}` | Index) ? TObject extends Array<infer U> ? TPath extends `${number}.${infer K}` ? K extends keyof U ? NestedProperty<U, K> : never : U : never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? NestedProperty<Required<TObject>[K], R> : K extends `${number}` | Index ? TObject extends Array<infer U> ? NestedProperty<U, R> : never : never : never;
|
|
31
23
|
type ValueInDotPath<TObject, TPath extends DotPaths<TObject> | ''> = NestedProperty<TObject, TPath>;
|
|
24
|
+
type DotPathUpdateValue<TObject, TPath extends DotPaths<TObject>> = TPath extends PathIndexingArray<Index> ? [`PATH ERROR: replace _INDEX_ with a NUMBER`, ValueInDotPath<TObject, TPath>, ((fieldValue: ValueInDotPath<TObject, TPath>, fullObject: TObject) => ValueInDotPath<TObject, TPath>)] : ValueInDotPath<TObject, TPath> | ((fieldValue: ValueInDotPath<TObject, TPath>, fullObject: TObject) => ValueInDotPath<TObject, TPath>);
|
|
32
25
|
/**
|
|
33
26
|
* Update Object Type Implementation
|
|
34
27
|
*/
|
|
35
|
-
type DotPathUpdateObject<
|
|
36
|
-
[
|
|
37
|
-
}
|
|
28
|
+
type DotPathUpdateObject<TObject> = {
|
|
29
|
+
[TKey in DotPaths<TObject>]?: DotPathUpdateValue<TObject, TKey>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Optional and Required keys
|
|
33
|
+
*/
|
|
34
|
+
type NonPartialKeys = string | number | boolean | symbol | bigint | null | object;
|
|
35
|
+
type OptionalKeys<T> = {
|
|
36
|
+
[K in keyof T & string]: T[K] extends NonPartialKeys ? never : K;
|
|
37
|
+
}[keyof T & string];
|
|
38
|
+
type RequiredKeys<T> = {
|
|
39
|
+
[K in keyof T & string]: T[K] extends NonPartialKeys ? K : never;
|
|
40
|
+
}[keyof T & string];
|
|
41
|
+
|
|
42
|
+
type Remove = <T extends object>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : (OptionalKeys<T> | (OptionalKeys<T>)[])) => T;
|
|
43
|
+
/**
|
|
44
|
+
* Removes properties or indexes, order doesn't matter.
|
|
45
|
+
*/
|
|
46
|
+
declare const remove: Remove;
|
|
38
47
|
|
|
39
48
|
type Select = <T extends object, TPath extends (DotPaths<T> | '')>(state: T, path: TPath) => TPath extends PathIndexingArray<Index> ? never : TPath extends PathIndexingArray<number> ? ValueInDotPath<T, TPath> | undefined : ValueInDotPath<T, TPath>;
|
|
40
49
|
/**
|
|
@@ -43,47 +52,26 @@ type Select = <T extends object, TPath extends (DotPaths<T> | '')>(state: T, pat
|
|
|
43
52
|
*/
|
|
44
53
|
declare const select: Select;
|
|
45
54
|
|
|
46
|
-
type
|
|
55
|
+
type PathWithoutLastKey = string;
|
|
56
|
+
type LastKey = string;
|
|
57
|
+
type SplitDotPathAtLastKey = <T extends string>(dotPath: T) => [PathWithoutLastKey, LastKey];
|
|
47
58
|
/**
|
|
48
59
|
* Splits string dotPath into ID and RemainingPath
|
|
49
60
|
*/
|
|
50
61
|
declare const splitPathAtLastKey: SplitDotPathAtLastKey;
|
|
51
62
|
|
|
52
|
-
type Spread = <T extends object>(value: T, update: T extends Array<infer U> ? Array<U> : Partial<T>) => T;
|
|
53
|
-
/**
|
|
54
|
-
* Spreads if its an object or array to the target, if its not then returns same value
|
|
55
|
-
*/
|
|
56
|
-
declare const spread: Spread;
|
|
57
|
-
|
|
58
63
|
type Set = <T>(newValue: T) => (value: T) => T;
|
|
59
64
|
/**
|
|
60
65
|
* Allows to set strongly typed values (removing inacurate "undefined" inference) in nested values inside arrays
|
|
61
66
|
*/
|
|
62
67
|
declare const set: Set;
|
|
63
68
|
|
|
64
|
-
type
|
|
65
|
-
type NonStrictMode = 'implement-non-existent-paths';
|
|
66
|
-
type UpdateImmutably = {
|
|
67
|
-
<T extends object>(state: T, updates: DotPathUpdateObject<T>): T;
|
|
68
|
-
};
|
|
69
|
+
type UpdateImmutably = <T extends object>(state: T, updates: DotPathUpdateObject<T>, addNonExistentPropsAndIndexes?: true) => T;
|
|
69
70
|
/**
|
|
70
71
|
* Creates a new updated object/array applying the changes provided in the update object.
|
|
71
72
|
* It doesnt modify the original input.
|
|
72
73
|
* If any of the paths doesnt exists it returns
|
|
73
74
|
*/
|
|
74
75
|
declare const updateImmutably: UpdateImmutably;
|
|
75
|
-
type PosibleExpandedProps<T> = {
|
|
76
|
-
[K in keyof T]?: T[K] extends object ? PosibleExpandedProps<T[K]> & Record<any, any> : T[K];
|
|
77
|
-
};
|
|
78
|
-
type UpdateImmutablyNonStrictly = {
|
|
79
|
-
<T extends object>(state: T, updates: DotPathUpdateObject<T>, mode: SemiStrictMode): T;
|
|
80
|
-
<T extends object>(state: T, updates: DotPathUpdateObject<T>, mode: NonStrictMode): PosibleExpandedProps<T>;
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* Creates a new updated object/array applying the changes provided in the update object.
|
|
84
|
-
* It doesnt modify the original input.
|
|
85
|
-
* If any of the paths doesnt exists it adds it if possible.
|
|
86
|
-
*/
|
|
87
|
-
declare const updateImmutablyAllowingNewProps: UpdateImmutablyNonStrictly;
|
|
88
76
|
|
|
89
|
-
export { type DotPathUpdateObject, type DotPaths, type
|
|
77
|
+
export { type DotPathUpdateObject, type DotPathUpdateValue, type DotPaths, type OptionalKeys, type RequiredKeys, type ValueInDotPath, remove, select, set, splitPathAtLastKey, updateImmutably };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var f=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var j=(t,e)=>{for(var r in e)f(t,r,{get:e[r],enumerable:!0})},O=(t,e,r,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of D(e))!w.call(t,o)&&o!==r&&f(t,o,{get:()=>e[o],enumerable:!(a=I(e,o))||a.enumerable});return t};var g=t=>O(f({},"__esModule",{value:!0}),t);var v={};j(v,{remove:()=>x,select:()=>T,set:()=>b,splitPathAtLastKey:()=>m,updateImmutably:()=>A});module.exports=g(v);var x=(t,e)=>{if(!Array.isArray(t)&&t?.constructor!==Object)throw'Wrong type provided in "remove" function';let r=Array.isArray(e)?e:[e];if(Array.isArray(t)){let o=Symbol(),y=[...t];return r.forEach(n=>{y[n]=o}),y.filter(n=>n!==o)}let a={...t};return r.forEach(o=>delete a[o]),a};var T=(t,e)=>!e||typeof e!="string"?t:e.split(".").reduce((r,a)=>{if(r?.hasOwnProperty(a))return r[a]},t);var m=t=>{if(typeof t!="string")throw new Error(`Wrong dotPath type: ${t}`);let e=t.lastIndexOf(".")+1,r=e?t.slice(0,e-1):"",a=t.slice(e);return[r,a]};var b=t=>()=>t;var A=(t,e,r)=>{if(t?.constructor!==Object&&!Array.isArray(t))throw new Error(`${t} is not an object/array`);if(!e)return t;let a=Array.isArray(t)?[...t]:{...t},o={};for(let[y,l]of Object.entries(e)){if(!y)continue;let n=y.split("."),s=n.pop(),c=o,d=a;for(let u=0;u<n.length;u++){let i=n[u],p=d[i],K=typeof p=="object"&&(p.constructor===Object||Array.isArray(p)),P=r&&p===void 0;if(!K&&!P)throw new Error(`Invalid path provided: "${i}" is not an object/array in "${y}"`);c[i]||(P?d[i]=isNaN(n[u+1]??s)?{}:[]:d[i]=Array.isArray(p)?[...p]:{...p},c[i]={}),d=d[i],c=c[i]}let h=d;if(!r){if(Array.isArray(h)&&!(s<h.length))throw new Error(`Index ${s} doesnt exist in the path ${n.join(".")} because its value is ${s}, use spread operator for adding new indexes`);if(!h.hasOwnProperty(s))throw new Error(`Property ${s} doesnt exist in the path ${n.join(".")} because its value is ${h}`)}h[s]=typeof l=="function"?l(h[s],a):l,delete c[s]}return a};0&&(module.exports={remove,select,set,splitPathAtLastKey,updateImmutably});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var x=(t,e)=>{if(!Array.isArray(t)&&t?.constructor!==Object)throw'Wrong type provided in "remove" function';let a=Array.isArray(e)?e:[e];if(Array.isArray(t)){let p=Symbol(),y=[...t];return a.forEach(o=>{y[o]=p}),y.filter(o=>o!==p)}let r={...t};return a.forEach(p=>delete r[p]),r};var T=(t,e)=>!e||typeof e!="string"?t:e.split(".").reduce((a,r)=>{if(a?.hasOwnProperty(r))return a[r]},t);var m=t=>{if(typeof t!="string")throw new Error(`Wrong dotPath type: ${t}`);let e=t.lastIndexOf(".")+1,a=e?t.slice(0,e-1):"",r=t.slice(e);return[a,r]};var b=t=>()=>t;var A=(t,e,a)=>{if(t?.constructor!==Object&&!Array.isArray(t))throw new Error(`${t} is not an object/array`);if(!e)return t;let r=Array.isArray(t)?[...t]:{...t},p={};for(let[y,l]of Object.entries(e)){if(!y)continue;let o=y.split("."),n=o.pop(),c=p,d=r;for(let u=0;u<o.length;u++){let s=o[u],i=d[s],P=typeof i=="object"&&(i.constructor===Object||Array.isArray(i)),f=a&&i===void 0;if(!P&&!f)throw new Error(`Invalid path provided: "${s}" is not an object/array in "${y}"`);c[s]||(f?d[s]=isNaN(o[u+1]??n)?{}:[]:d[s]=Array.isArray(i)?[...i]:{...i},c[s]={}),d=d[s],c=c[s]}let h=d;if(!a){if(Array.isArray(h)&&!(n<h.length))throw new Error(`Index ${n} doesnt exist in the path ${o.join(".")} because its value is ${n}, use spread operator for adding new indexes`);if(!h.hasOwnProperty(n))throw new Error(`Property ${n} doesnt exist in the path ${o.join(".")} because its value is ${h}`)}h[n]=typeof l=="function"?l(h[n],r):l,delete c[n]}return r};export{x as remove,T as select,b as set,m as splitPathAtLastKey,A as updateImmutably};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maraj",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A lightweight 500 bytes gzipped alternative for immutable updates while dealing with objects and arrays",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shallow",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@changesets/cli": "^2.27.1",
|
|
39
39
|
"@microsoft/api-extractor": "^7.38.5",
|
|
40
40
|
"tsup": "^8.0.1",
|
|
41
|
-
"typescript": "^
|
|
41
|
+
"typescript": "^5.3.3",
|
|
42
42
|
"vitest": "^0.28.4"
|
|
43
43
|
}
|
|
44
44
|
}
|