maraj 2.5.0 → 2.6.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 CHANGED
@@ -1,16 +1,18 @@
1
1
 
2
2
  # Maraj
3
3
 
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).
4
+ A fast lightweight utility for creating **immutable updates**. Fully typed. Only 600 bytes gziped. Includes usefull utilities for manipulating updates (like remove, select).
5
5
 
6
6
  ### Features
7
7
 
8
+ - updateImmutably and modifyImmutably.
8
9
  - Full typescript support.
9
10
  - Correct type inference.
10
- - Multiple updates at same time mantaining the order of execution.
11
+ - Multiple and concurrent updates.
11
12
  - Functional updates.
13
+ - Includes usefull utilities like "remove", "select".
12
14
 
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)
15
+ **<u>_For Typescript:_</u>** in tsconfig.json set **"strictNullChecks"** and **"noUncheckedIndexedAccess"** to **true** to be able to infer correctly the type of optional properties and properties with posible undefined value. ({key?: ''} !== {key: '' | undefined})
14
16
 
15
17
  ```ts
16
18
  const originalValue = { name: 'one', lastname: 'two' modified: false, files: [{ filename: 'one' }] }
@@ -20,22 +22,33 @@ const updatedValue = updateImmutably(orignalValue, {
20
22
  'modified': (value, fullObject) => !value,
21
23
  'files.0.filename': 'zero',
22
24
  })
23
-
24
25
  // updatedValue = { name: 'updated', lastname: 'two' modified: true, files: [{ filename: 'zero' }] }
25
-
26
26
  originalValue.files[1] === updatedValue.files[1] // true
27
+
28
+ const newValue = modifyImmutably(orignalValue, {
29
+ 'newProp': 'new',
30
+ 'data.0.name': 'dataName'
31
+ })
32
+ // updatedValue = { ... , newProp: 'new', data: [{ name: 'dataName' }] }
33
+ originalValue.files[1] === newValue.files[1] // true
27
34
  ```
28
35
 
29
36
  ### Limitations
30
37
 
31
- - If "addNonExistentPropsAndIndexes" is enabled (3rd parameter in the updateImmutably function), **numbers** will **always** create an **array** if the array/object doesnt exist beforehand.
38
+ - In "modifyImmutably", **numbers** inside path will **always** create an **array** if the array/object doesnt exist beforehand. If you want an object instead of an array, add it beforehand.
32
39
 
33
40
  ```ts
34
- const updatedValue = updateImmutably({}, {
41
+ const updatedValue = modifyImmutably({}, {
35
42
  'files.1.name': 'new name',
36
- }, true)
37
- // updatedValue = { files: [<empty>, { name: 'new name' }] }
38
- // the "files" prop is an array.
43
+ })
44
+ // The "files" prop is an array. updatedValue = { files: [<empty>, { name: 'new name' }] }
45
+
46
+ // To get an object instead of an array create an object beforehand
47
+ const updatedValue2 = modifyImmutably({}, {
48
+ 'files': {}, // add an object first, so then i can add props as numbers after it
49
+ 'files.1.name': 'new name',
50
+ })
51
+ // updatedValue2 = { files: { '1': { name: 'new name' } } }
39
52
  ```
40
53
 
41
54
  - The type "UpdateDotPathObject" (the type used as second parameter in the updateImmutably function) if used to create new update objects using computed properties { [key]: value } 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)
@@ -53,24 +66,23 @@ const updateFieldStore = <TPath extends DotPaths<Store>>(
53
66
  }
54
67
  ```
55
68
 
56
- - 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.
69
+ - Use the "set" utility if typescript doesn't infer properly _"an optional field"_ vs a _"field which can have 'undefined' as type"_. Usually happens in optional nested props inside arrays.
57
70
 
58
71
  ```ts
59
72
  const updatedValue = updateImmutably(data, {
60
- 'files.0.optionalProperty': 'new name',
61
- 'files.1.optionalProperty': undefined, //no ts error
62
- 'files.0.optionalProperty': set('new name'),
63
- 'files.1.optionalProperty': set(undefined), //throws ts error, undefined cant be a value.
73
+ 'files.0.nonOptionalProperty': 'new name',
74
+ 'files.1.nonOptionalProperty': undefined, //no ts error
75
+ 'files.0.nonOptionalProperty': set('new name'),
76
+ 'files.1.nonOptionalProperty': set(undefined), //throws ts error, undefined cant be a value.
64
77
  })
65
78
  ```
66
79
 
67
- ## Creating an Immutable Update
80
+ ## Creating a Strict Immutable Update
68
81
 
69
82
  Arguments:
70
83
 
71
84
  - Original Object
72
85
  - Update Object
73
- - If it should add non-existent properties/indexes
74
86
 
75
87
  ## Example
76
88
 
@@ -87,7 +99,6 @@ const data = {
87
99
  }
88
100
 
89
101
  const newUpdatedObject = updateImmutably(data, {
90
- "profile.age": 28, //add new properties if "addNonExistentPropsAndIndexes" is enabled
91
102
  "profile.name": "Lucas", //update values
92
103
  "works.1": "gs", //nested array update
93
104
  "history.1.available": (currentValue) => (!currentValue), //functional update
@@ -95,6 +106,34 @@ const newUpdatedObject = updateImmutably(data, {
95
106
  })
96
107
  ```
97
108
 
109
+ ## Creating an Immutable Update allowing new props to be added
110
+
111
+ Arguments:
112
+
113
+ - Original Object
114
+ - Update Object
115
+
116
+ ## Example
117
+
118
+ ```js
119
+ import { modifyImmutably } from "maraj";
120
+
121
+ const data = {
122
+ profile: { name: "Lukasz" },
123
+ works: ["soum", "fi"],
124
+ history: [
125
+ { title: "soum", size: 90, available: true, info: ["info1"] },
126
+ { title: "fi", size: 30, available: false, info: ["infoA"] },
127
+ ]
128
+ }
129
+
130
+ const newUpdatedObject = modifyImmutably(data, {
131
+ "profile.age": 28, //add new property
132
+ "profile.name": "Lucas", //update values
133
+ "works.10": "gs", //increases the array (filling it up with empty/undefined) and sets the value in the index of 10 to "gs"
134
+ })
135
+ ```
136
+
98
137
  ## Remove Utility
99
138
 
100
139
  Removes the **key of an object** or the **index of an array** in the key provided in the **updateImmutably.**
@@ -104,8 +143,9 @@ import {remove} from 'maraj'
104
143
 
105
144
  const newUpdatedObject = updateImmutably(person,{
106
145
  'profile': (value) => remove(value, 'name'), // returns new value without the "name" prop
146
+ 'profile': (value) => remove(value, ['name', 'lastname']), // returns new value without the "name" and "lastname" prop
107
147
  'works': (value) => remove(value, 1), // returns new array without the provided index (1)
108
- 'works': (value) => remove(value, [1, "0"]), // returns new array without the provided index (0,1)
148
+ 'works': (value) => remove(value, [1, "0"]), // returns new array without the provided index "0" and "1"
109
149
  })
110
150
  ```
111
151
 
@@ -135,8 +175,8 @@ import { splitPathAtLastKey } from 'maraj'
135
175
 
136
176
  const stringPath = 'works.1.size'
137
177
 
138
- const [remainingPath, last] = splitPathAtLastKey(stringPath)
139
- // last = 'size'
178
+ const [remainingPath, lastKey] = splitPathAtLastKey(stringPath)
179
+ // lastKey = 'size'
140
180
  // remainingPath = 'works.1'
141
181
  ```
142
182
 
package/dist/index.d.mts CHANGED
@@ -1,24 +1,47 @@
1
+ /** Returns only the optional keys of an object. */
2
+ type OptionalKeys<T> = {
3
+ [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
4
+ }[keyof T];
5
+ /** Removes properties or indexes, order doesn't matter. */
6
+ type Remove = <T extends object>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : OptionalKeys<T> | (OptionalKeys<T>)[]) => T;
7
+ /**
8
+ * Removes properties or indexes, order doesn't matter.
9
+ * @param value Object/array from where to remove keys/indexes shallowly.
10
+ * @param keys Keys to remove.
11
+ * @returns New object/array without the provided keys/indexes.
12
+ */
13
+ declare const remove: Remove;
14
+
1
15
  /** Index literal type to improve DX at managing updates. */
2
16
  declare const LITERAL_INDEX = "${number}";
3
17
  type LiteralIndex = typeof LITERAL_INDEX;
4
- type UpdateImmutably = <T extends object>(state: T, updates: DotPathUpdateObject<T>) => T;
18
+ type ObjectOrArray = Record<string | number | symbol, any>;
19
+ type OptionValues = 'error' | 'skip' | 'add';
20
+ type Options = {
21
+ onNewProps?: OptionValues;
22
+ onNewIndexes?: OptionValues;
23
+ };
24
+ type ImmutableUpdate = <TObject extends ObjectOrArray>(state: TObject, dotPathUpdateObject: DotPathUpdateObject<TObject>, options?: Options) => TObject;
5
25
  /**
6
- * Creates a new object applying strict immutable updates using dot-path syntax. Can't add new props or indexes.
26
+ * Creates a new object applying immutable updates using dot-path syntax.
27
+ * Can add or skip new props or indexes depending on options.
28
+ * TYPESCRIPT: Dot-Path Update Object type must STRICTLY match the type of state.
7
29
  * @param state Original State Object.
8
- * @param updates Dot Path Update Object.
9
- * @param addNonExistentPropsAndIndexes If it should or not create non-existent fields.
30
+ * @param dotPathUpdateObject Dot Path Update Object.
31
+ * @param options Options to change JS behaviour on non-existent fields.
10
32
  * @returns New Updated Object.
11
33
  */
12
- declare const updateImmutably: UpdateImmutably;
13
- type ModifyImmutably = <T extends object>(state: T, updates: DotPathUpdateObject<T> | Record<string, any>) => T;
34
+ declare const update: ImmutableUpdate;
35
+ type ExtendableImmutableUpdate = <TObject extends ObjectOrArray, TUpdateObject extends DotPathUpdateObject<TObject>>(state: TObject, dotPathUpdateObject: TUpdateObject) => ExtendedUpdate<TObject, TUpdateObject>;
14
36
  /**
15
- * Creates a new object applying immutable modifications using dot-path syntax. It will add non-existent props or indexes if provided.
37
+ * Creates a new object applying immutable updates using dot-path syntax.
38
+ * It will always add non-existent props or indexes if provided.
39
+ * TYPESCRIPT: Dot-Path Update Object type extends from the type of state and you can add new non-existent properties.
16
40
  * @param state Original State Object.
17
- * @param updates Dot Path Update Object.
18
- * @param addNonExistentPropsAndIndexes If it should or not create non-existent fields.
41
+ * @param dotPathUpdateObject Dot Path Update Object.
19
42
  * @returns New Updated Object.
20
43
  */
21
- declare const modifyImmutably: ModifyImmutably;
44
+ declare const extendableUpdate: ExtendableImmutableUpdate;
22
45
 
23
46
  /**
24
47
  * --------------------------------------------
@@ -37,7 +60,7 @@ type IsTuple<T extends ReadonlyArray<any>> = number extends T['length'] ? false
37
60
  * --------------------------------------------
38
61
  */
39
62
  /** Dot-paths of an array. */
40
- type ArrayPaths<TInnerType, TAllowedTypes> = TInnerType extends TAllowedTypes ? TInnerType extends PrimitivesAndNativeObjects ? `${ArrayIndexes}` : `${ArrayIndexes}` | `${ArrayIndexes}.${DotPaths<TInnerType, TAllowedTypes>}` : `${ArrayIndexes}.${DotPaths<TInnerType, TAllowedTypes>}`;
63
+ type ArrayPaths<TInnerType, TAllowedTypes> = TInnerType extends TAllowedTypes ? TInnerType extends PrimitivesAndNativeObjects ? number | `${ArrayIndexes}` : number | `${ArrayIndexes}` | `${ArrayIndexes}.${DotPaths<TInnerType, TAllowedTypes>}` : `${ArrayIndexes}.${DotPaths<TInnerType, TAllowedTypes>}`;
41
64
  /** Dot-paths of an object. */
42
65
  type ObjectPaths<T, TAllowedTypes> = {
43
66
  [Key in keyof T & string]: T[Key] extends TAllowedTypes ? T[Key] extends PrimitivesAndNativeObjects ? `${Key}` : `${Key}` | `${Key}.${DotPaths<T[Key], TAllowedTypes>}` : `${Key}.${DotPaths<T[Key], TAllowedTypes>}`;
@@ -54,7 +77,7 @@ type DotPaths<T, TAllowedTypes = any> = T extends ReadonlyArray<infer U> ? IsTup
54
77
  /** Nested type implementation. */
55
78
  type NestedType<TObject, TPath> = TPath extends '' ? TObject : TPath extends keyof TObject ? TObject[TPath] : TPath extends LiteralIndex ? never : TPath extends `${number}` ? TObject extends ReadonlyArray<infer U> ? U : never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? NestedType<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? TObject extends ReadonlyArray<infer U> ? NestedType<U, R> : never : never : never;
56
79
  /** Value of a nested property of a given dot-path. */
57
- type ValueInDotPath<TObject, TPath extends DotPaths<TObject> | ''> = NestedType<TObject, TPath>;
80
+ type ExactValueInDotPath<TObject, TPath extends DotPaths<TObject> | ''> = NestedType<TObject, TPath>;
58
81
  /**
59
82
  * --------------------------------------------
60
83
  * RETURNED VALUE OF A NESTED PROPERTY PROVIDING A DOT PATH
@@ -70,33 +93,51 @@ type ReturnedValueInDotPath<TObject, TPath extends DotPaths<TObject> | ''> = Ret
70
93
  * --------------------------------------------
71
94
  */
72
95
  /** Value of a dot-path update object, can be the value or a function. */
73
- type DotPathUpdateValue<TObject, TPath extends DotPaths<TObject>> = ValueInDotPath<TObject, TPath> | ((fieldValue: ValueInDotPath<TObject, TPath>, fullObject: TObject) => ValueInDotPath<TObject, TPath>);
96
+ type DotPathUpdateValue<TObject, TPath extends DotPaths<TObject>> = ExactValueInDotPath<TObject, TPath> | ((fieldValue: ExactValueInDotPath<TObject, TPath>) => ExactValueInDotPath<TObject, TPath>);
74
97
  /** Dot-path update object, used to give updates to the updateImmutably function. */
75
98
  type DotPathUpdateObject<TObject> = {
76
- [TPath in DotPaths<TObject>]?: ValueInDotPath<TObject, TPath> | ((fieldValue: ValueInDotPath<TObject, TPath>, fullObject: TObject) => ValueInDotPath<TObject, TPath>);
99
+ [TPath in DotPaths<TObject>]?: ExactValueInDotPath<TObject, TPath> | ((fieldValue: ExactValueInDotPath<TObject, TPath>) => ExactValueInDotPath<TObject, TPath>);
77
100
  };
78
101
  /**
79
102
  * --------------------------------------------
80
- * REQUIRED OR OPTIONAL KEYS
103
+ * EXTRACT TYPE FROM DOT-PATH UPDATE OBJECT
81
104
  * --------------------------------------------
82
105
  */
83
- /** Returns only the optional keys of an object. */
84
- type OptionalKeys<T> = {
85
- [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
106
+ /** Merging two objects */
107
+ /** Keys which arent dot-path keys */
108
+ type NonDotPathKeys<T> = {
109
+ [Key in keyof T]: Key extends `${infer K}.${string}` ? K : Key;
86
110
  }[keyof T];
87
- /** Returns only the required keys of an object. */
88
- type RequiredKeys<T> = {
89
- [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
111
+ /** Keys which are an index */
112
+ type KeysRepresentingAnIndex<T> = {
113
+ [Key in keyof T]: Key extends `${number}.${string}` ? never : Key;
90
114
  }[keyof T];
91
-
92
- type Remove = <T extends object>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : OptionalKeys<T> | (OptionalKeys<T>)[]) => T;
93
- /**
94
- * Removes properties or indexes, order doesn't matter.
95
- * @param value Object/array from where to remove keys/indexes shallowly.
96
- * @param keys Keys to remove.
97
- * @returns New object/array without the provided keys/indexes.
98
- */
99
- declare const remove: Remove;
115
+ /** Keys which start with an index */
116
+ type KeyPathsStartingWithAnIndex<T> = {
117
+ [Key in keyof T]: Key extends `${number}.${string}` ? Key : never;
118
+ }[keyof T];
119
+ /** Keys which are paths and its first key-path is removed */
120
+ type NestedPathKeys<T, K extends string | number> = {
121
+ [Key in keyof T]: Key extends `${K}.${infer U}` ? U : never;
122
+ }[keyof T];
123
+ /** Extract value if its a function */
124
+ type Value<T> = T extends (...args: any) => any ? ReturnType<T> : T;
125
+ /** To check if an empty object is provided */
126
+ type EmptyObject = Record<string | number | symbol, never>;
127
+ /** Used to extract the type from a dot-path update object. */
128
+ type ExtractTypeFromDotPathUpdateObject<T extends Record<string, any>> = T extends EmptyObject ? {} : keyof T extends LiteralIndex | number | `${number}` | `${number}.${infer U}` ? Value<T[KeyPathsStartingWithAnIndex<T>]> extends never ? Array<Value<T[KeysRepresentingAnIndex<T>]>> : Array<Value<T[KeysRepresentingAnIndex<T>]> | {
129
+ [Key2 in U]: Value<T[KeyPathsStartingWithAnIndex<T>]>;
130
+ }> : {
131
+ [Key1 in NonDotPathKeys<T> & string]: Key1 extends keyof T ? Value<T[Key1]> extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<{
132
+ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
133
+ }> extends Array<infer W> ? Array<U | W> : Array<U> & ExtractTypeFromDotPathUpdateObject<{
134
+ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
135
+ }> : Value<T[Key1]> : ExtractTypeFromDotPathUpdateObject<{
136
+ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
137
+ }>;
138
+ } & unknown;
139
+ /** New expanded value after aplying updates including new properties */
140
+ type ExtendedUpdate<TObject extends Record<string | number | symbol, any>, TDotPathUpdateObject extends Record<string | number | symbol, any>> = TObject extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject> extends Array<infer W> ? Array<U | W> : TObject & ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject> : ExtractTypeFromDotPathUpdateObject<TObject & TDotPathUpdateObject>;
100
141
 
101
142
  type Select = <TObject, TPath extends DotPaths<TObject> | ''>(state: TObject, path: TPath) => ReturnedValueInDotPath<TObject, TPath>;
102
143
  /**
@@ -131,4 +172,4 @@ declare const isObjectLiteral: (valueToTest: any) => boolean;
131
172
  declare const parseStringIndex: (v: string) => number | null;
132
173
  declare const isStringIndex: (v: string) => boolean;
133
174
 
134
- export { type DotPathUpdateObject, type DotPathUpdateValue, type DotPaths, type OptionalKeys, type PrimitivesAndNativeObjects, type RequiredKeys, type ReturnedValueInDotPath, type ValueInDotPath, isObjectLiteral, isStringIndex, modifyImmutably, parseStringIndex, remove, select, set, splitPathAtLastKey, updateImmutably };
175
+ export { type DotPathUpdateObject, type DotPathUpdateValue, type DotPaths, type ExactValueInDotPath, type LiteralIndex, type ReturnedValueInDotPath, extendableUpdate, isObjectLiteral, isStringIndex, parseStringIndex, remove, select, set, splitPathAtLastKey, update };
package/dist/index.d.ts CHANGED
@@ -1,24 +1,47 @@
1
+ /** Returns only the optional keys of an object. */
2
+ type OptionalKeys<T> = {
3
+ [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
4
+ }[keyof T];
5
+ /** Removes properties or indexes, order doesn't matter. */
6
+ type Remove = <T extends object>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : OptionalKeys<T> | (OptionalKeys<T>)[]) => T;
7
+ /**
8
+ * Removes properties or indexes, order doesn't matter.
9
+ * @param value Object/array from where to remove keys/indexes shallowly.
10
+ * @param keys Keys to remove.
11
+ * @returns New object/array without the provided keys/indexes.
12
+ */
13
+ declare const remove: Remove;
14
+
1
15
  /** Index literal type to improve DX at managing updates. */
2
16
  declare const LITERAL_INDEX = "${number}";
3
17
  type LiteralIndex = typeof LITERAL_INDEX;
4
- type UpdateImmutably = <T extends object>(state: T, updates: DotPathUpdateObject<T>) => T;
18
+ type ObjectOrArray = Record<string | number | symbol, any>;
19
+ type OptionValues = 'error' | 'skip' | 'add';
20
+ type Options = {
21
+ onNewProps?: OptionValues;
22
+ onNewIndexes?: OptionValues;
23
+ };
24
+ type ImmutableUpdate = <TObject extends ObjectOrArray>(state: TObject, dotPathUpdateObject: DotPathUpdateObject<TObject>, options?: Options) => TObject;
5
25
  /**
6
- * Creates a new object applying strict immutable updates using dot-path syntax. Can't add new props or indexes.
26
+ * Creates a new object applying immutable updates using dot-path syntax.
27
+ * Can add or skip new props or indexes depending on options.
28
+ * TYPESCRIPT: Dot-Path Update Object type must STRICTLY match the type of state.
7
29
  * @param state Original State Object.
8
- * @param updates Dot Path Update Object.
9
- * @param addNonExistentPropsAndIndexes If it should or not create non-existent fields.
30
+ * @param dotPathUpdateObject Dot Path Update Object.
31
+ * @param options Options to change JS behaviour on non-existent fields.
10
32
  * @returns New Updated Object.
11
33
  */
12
- declare const updateImmutably: UpdateImmutably;
13
- type ModifyImmutably = <T extends object>(state: T, updates: DotPathUpdateObject<T> | Record<string, any>) => T;
34
+ declare const update: ImmutableUpdate;
35
+ type ExtendableImmutableUpdate = <TObject extends ObjectOrArray, TUpdateObject extends DotPathUpdateObject<TObject>>(state: TObject, dotPathUpdateObject: TUpdateObject) => ExtendedUpdate<TObject, TUpdateObject>;
14
36
  /**
15
- * Creates a new object applying immutable modifications using dot-path syntax. It will add non-existent props or indexes if provided.
37
+ * Creates a new object applying immutable updates using dot-path syntax.
38
+ * It will always add non-existent props or indexes if provided.
39
+ * TYPESCRIPT: Dot-Path Update Object type extends from the type of state and you can add new non-existent properties.
16
40
  * @param state Original State Object.
17
- * @param updates Dot Path Update Object.
18
- * @param addNonExistentPropsAndIndexes If it should or not create non-existent fields.
41
+ * @param dotPathUpdateObject Dot Path Update Object.
19
42
  * @returns New Updated Object.
20
43
  */
21
- declare const modifyImmutably: ModifyImmutably;
44
+ declare const extendableUpdate: ExtendableImmutableUpdate;
22
45
 
23
46
  /**
24
47
  * --------------------------------------------
@@ -37,7 +60,7 @@ type IsTuple<T extends ReadonlyArray<any>> = number extends T['length'] ? false
37
60
  * --------------------------------------------
38
61
  */
39
62
  /** Dot-paths of an array. */
40
- type ArrayPaths<TInnerType, TAllowedTypes> = TInnerType extends TAllowedTypes ? TInnerType extends PrimitivesAndNativeObjects ? `${ArrayIndexes}` : `${ArrayIndexes}` | `${ArrayIndexes}.${DotPaths<TInnerType, TAllowedTypes>}` : `${ArrayIndexes}.${DotPaths<TInnerType, TAllowedTypes>}`;
63
+ type ArrayPaths<TInnerType, TAllowedTypes> = TInnerType extends TAllowedTypes ? TInnerType extends PrimitivesAndNativeObjects ? number | `${ArrayIndexes}` : number | `${ArrayIndexes}` | `${ArrayIndexes}.${DotPaths<TInnerType, TAllowedTypes>}` : `${ArrayIndexes}.${DotPaths<TInnerType, TAllowedTypes>}`;
41
64
  /** Dot-paths of an object. */
42
65
  type ObjectPaths<T, TAllowedTypes> = {
43
66
  [Key in keyof T & string]: T[Key] extends TAllowedTypes ? T[Key] extends PrimitivesAndNativeObjects ? `${Key}` : `${Key}` | `${Key}.${DotPaths<T[Key], TAllowedTypes>}` : `${Key}.${DotPaths<T[Key], TAllowedTypes>}`;
@@ -54,7 +77,7 @@ type DotPaths<T, TAllowedTypes = any> = T extends ReadonlyArray<infer U> ? IsTup
54
77
  /** Nested type implementation. */
55
78
  type NestedType<TObject, TPath> = TPath extends '' ? TObject : TPath extends keyof TObject ? TObject[TPath] : TPath extends LiteralIndex ? never : TPath extends `${number}` ? TObject extends ReadonlyArray<infer U> ? U : never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? NestedType<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? TObject extends ReadonlyArray<infer U> ? NestedType<U, R> : never : never : never;
56
79
  /** Value of a nested property of a given dot-path. */
57
- type ValueInDotPath<TObject, TPath extends DotPaths<TObject> | ''> = NestedType<TObject, TPath>;
80
+ type ExactValueInDotPath<TObject, TPath extends DotPaths<TObject> | ''> = NestedType<TObject, TPath>;
58
81
  /**
59
82
  * --------------------------------------------
60
83
  * RETURNED VALUE OF A NESTED PROPERTY PROVIDING A DOT PATH
@@ -70,33 +93,51 @@ type ReturnedValueInDotPath<TObject, TPath extends DotPaths<TObject> | ''> = Ret
70
93
  * --------------------------------------------
71
94
  */
72
95
  /** Value of a dot-path update object, can be the value or a function. */
73
- type DotPathUpdateValue<TObject, TPath extends DotPaths<TObject>> = ValueInDotPath<TObject, TPath> | ((fieldValue: ValueInDotPath<TObject, TPath>, fullObject: TObject) => ValueInDotPath<TObject, TPath>);
96
+ type DotPathUpdateValue<TObject, TPath extends DotPaths<TObject>> = ExactValueInDotPath<TObject, TPath> | ((fieldValue: ExactValueInDotPath<TObject, TPath>) => ExactValueInDotPath<TObject, TPath>);
74
97
  /** Dot-path update object, used to give updates to the updateImmutably function. */
75
98
  type DotPathUpdateObject<TObject> = {
76
- [TPath in DotPaths<TObject>]?: ValueInDotPath<TObject, TPath> | ((fieldValue: ValueInDotPath<TObject, TPath>, fullObject: TObject) => ValueInDotPath<TObject, TPath>);
99
+ [TPath in DotPaths<TObject>]?: ExactValueInDotPath<TObject, TPath> | ((fieldValue: ExactValueInDotPath<TObject, TPath>) => ExactValueInDotPath<TObject, TPath>);
77
100
  };
78
101
  /**
79
102
  * --------------------------------------------
80
- * REQUIRED OR OPTIONAL KEYS
103
+ * EXTRACT TYPE FROM DOT-PATH UPDATE OBJECT
81
104
  * --------------------------------------------
82
105
  */
83
- /** Returns only the optional keys of an object. */
84
- type OptionalKeys<T> = {
85
- [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
106
+ /** Merging two objects */
107
+ /** Keys which arent dot-path keys */
108
+ type NonDotPathKeys<T> = {
109
+ [Key in keyof T]: Key extends `${infer K}.${string}` ? K : Key;
86
110
  }[keyof T];
87
- /** Returns only the required keys of an object. */
88
- type RequiredKeys<T> = {
89
- [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
111
+ /** Keys which are an index */
112
+ type KeysRepresentingAnIndex<T> = {
113
+ [Key in keyof T]: Key extends `${number}.${string}` ? never : Key;
90
114
  }[keyof T];
91
-
92
- type Remove = <T extends object>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : OptionalKeys<T> | (OptionalKeys<T>)[]) => T;
93
- /**
94
- * Removes properties or indexes, order doesn't matter.
95
- * @param value Object/array from where to remove keys/indexes shallowly.
96
- * @param keys Keys to remove.
97
- * @returns New object/array without the provided keys/indexes.
98
- */
99
- declare const remove: Remove;
115
+ /** Keys which start with an index */
116
+ type KeyPathsStartingWithAnIndex<T> = {
117
+ [Key in keyof T]: Key extends `${number}.${string}` ? Key : never;
118
+ }[keyof T];
119
+ /** Keys which are paths and its first key-path is removed */
120
+ type NestedPathKeys<T, K extends string | number> = {
121
+ [Key in keyof T]: Key extends `${K}.${infer U}` ? U : never;
122
+ }[keyof T];
123
+ /** Extract value if its a function */
124
+ type Value<T> = T extends (...args: any) => any ? ReturnType<T> : T;
125
+ /** To check if an empty object is provided */
126
+ type EmptyObject = Record<string | number | symbol, never>;
127
+ /** Used to extract the type from a dot-path update object. */
128
+ type ExtractTypeFromDotPathUpdateObject<T extends Record<string, any>> = T extends EmptyObject ? {} : keyof T extends LiteralIndex | number | `${number}` | `${number}.${infer U}` ? Value<T[KeyPathsStartingWithAnIndex<T>]> extends never ? Array<Value<T[KeysRepresentingAnIndex<T>]>> : Array<Value<T[KeysRepresentingAnIndex<T>]> | {
129
+ [Key2 in U]: Value<T[KeyPathsStartingWithAnIndex<T>]>;
130
+ }> : {
131
+ [Key1 in NonDotPathKeys<T> & string]: Key1 extends keyof T ? Value<T[Key1]> extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<{
132
+ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
133
+ }> extends Array<infer W> ? Array<U | W> : Array<U> & ExtractTypeFromDotPathUpdateObject<{
134
+ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
135
+ }> : Value<T[Key1]> : ExtractTypeFromDotPathUpdateObject<{
136
+ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
137
+ }>;
138
+ } & unknown;
139
+ /** New expanded value after aplying updates including new properties */
140
+ type ExtendedUpdate<TObject extends Record<string | number | symbol, any>, TDotPathUpdateObject extends Record<string | number | symbol, any>> = TObject extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject> extends Array<infer W> ? Array<U | W> : TObject & ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject> : ExtractTypeFromDotPathUpdateObject<TObject & TDotPathUpdateObject>;
100
141
 
101
142
  type Select = <TObject, TPath extends DotPaths<TObject> | ''>(state: TObject, path: TPath) => ReturnedValueInDotPath<TObject, TPath>;
102
143
  /**
@@ -131,4 +172,4 @@ declare const isObjectLiteral: (valueToTest: any) => boolean;
131
172
  declare const parseStringIndex: (v: string) => number | null;
132
173
  declare const isStringIndex: (v: string) => boolean;
133
174
 
134
- export { type DotPathUpdateObject, type DotPathUpdateValue, type DotPaths, type OptionalKeys, type PrimitivesAndNativeObjects, type RequiredKeys, type ReturnedValueInDotPath, type ValueInDotPath, isObjectLiteral, isStringIndex, modifyImmutably, parseStringIndex, remove, select, set, splitPathAtLastKey, updateImmutably };
175
+ export { type DotPathUpdateObject, type DotPathUpdateValue, type DotPaths, type ExactValueInDotPath, type LiteralIndex, type ReturnedValueInDotPath, extendableUpdate, isObjectLiteral, isStringIndex, parseStringIndex, remove, select, set, splitPathAtLastKey, update };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var b=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var R=(t,e)=>{for(var r in e)b(t,r,{get:e[r],enumerable:!0})},$=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of w(e))!E.call(t,n)&&n!==r&&b(t,n,{get:()=>e[n],enumerable:!(o=S(e,n))||o.enumerable});return t};var U=t=>$(b({},"__esModule",{value:!0}),t);var V={};R(V,{isObjectLiteral:()=>d,isStringIndex:()=>h,modifyImmutably:()=>D,parseStringIndex:()=>g,remove:()=>I,select:()=>T,set:()=>O,splitPathAtLastKey:()=>P,updateImmutably:()=>K});module.exports=U(V);var I=(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 n=Symbol(),p=[...t];return r.forEach(a=>{p[a]=n}),p.filter(a=>a!==n)}let o={...t};return r.forEach(n=>delete o[n]),o};var T=(t,e)=>!e||typeof e!="string"?t:e.split(".").reduce((r,o)=>{if(r?.hasOwnProperty(o))return r[o]},t);var P=t=>{if(typeof t!="string")throw new Error(`Wrong dotPath type: ${t}`);let e=t.lastIndexOf(".")+1,r=e?t.slice(0,e-1):"",o=t.slice(e);return[r,o]};var O=t=>()=>t;var d=t=>!!t&&Object.getPrototypeOf(t)===Object.prototype;var g=t=>{let e=parseInt(t);return e.toString()===t?e:null},h=t=>parseInt(t).toString()===t;var A="${number}",j=(t,e,r)=>{if(!d(t)&&!Array.isArray(t))throw new Error(`"${t}" is not an literalObject/array`);if(!e)return t;let o=Array.isArray(t)?[...t]:{...t},n={};for(let[p,m]of Object.entries(e)){if(!p)continue;let a=p.split(".");if(a.includes(A))throw new Error(`"${A}" inside "${p}"`);let i=a.pop(),l=n,y=o;for(let u=0;u<a.length;u++){let s=a[u],c=y[s],L=d(c)||Array.isArray(c),x=r&&c===void 0;if(!L&&!x)throw new Error(`"${s}" doesnt exist in "${a.slice(0,u+1).join(".")}"`);l[s]||(x?y[s]=h(a[u+1]??i)?[]:{}:y[s]=Array.isArray(c)?[...c]:{...c},l[s]={}),y=y[s],l=l[s]}let f=y;if(!r&&!f.hasOwnProperty(i))throw new Error(`Property/Index "${i}" doesnt exist in the path "${a.join(".")+"."+i}". Its value is: "${f}"`);f[i]=typeof m=="function"?m(f[i],o):m,delete l[i]}return o},K=(t,e)=>j(t,e,!1),D=(t,e)=>j(t,e,!0);0&&(module.exports={isObjectLiteral,isStringIndex,modifyImmutably,parseStringIndex,remove,select,set,splitPathAtLastKey,updateImmutably});
1
+ "use strict";var m=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var v=(e,t)=>{for(var r in t)m(e,r,{get:t[r],enumerable:!0})},C=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of R(t))!k.call(e,a)&&a!==r&&m(e,a,{get:()=>t[a],enumerable:!(n=$(t,a))||n.enumerable});return e};var W=e=>C(m({},"__esModule",{value:!0}),e);var q={};v(q,{extendableUpdate:()=>L,isObjectLiteral:()=>d,isStringIndex:()=>y,parseStringIndex:()=>K,remove:()=>j,select:()=>P,set:()=>w,splitPathAtLastKey:()=>A,update:()=>D});module.exports=W(q);var j=(e,t)=>{if(!Array.isArray(e)&&e?.constructor!==Object)throw'Wrong type provided in "remove" function';let r=Array.isArray(t)?t:[t];if(Array.isArray(e)){let a=Symbol(),l=[...e];return r.forEach(s=>{l[s]=a}),l.filter(s=>s!==a)}let n={...e};return r.forEach(a=>delete n[a]),n};var P=(e,t)=>!t||typeof t!="string"?e:t.split(".").reduce((r,n)=>{if(r?.hasOwnProperty(n))return r[n]},e);var A=e=>{if(typeof e!="string")throw new Error(`Wrong dotPath type: ${e}`);let t=e.lastIndexOf(".")+1,r=t?e.slice(0,t-1):"",n=e.slice(t);return[r,n]};var w=e=>()=>e;var d=e=>!!e&&Object.getPrototypeOf(e)===Object.prototype;var K=e=>{let t=parseInt(e);return t.toString()===e?t:null},y=e=>parseInt(e).toString()===e;var U=e=>JSON.stringify(e),O=(e,t)=>e.slice(0,t).join("."),g=e=>Array.isArray(e),E=(e,t,r={})=>{if(r={onNewProps:"add",onNewIndexes:"error",...r},!d(e)&&!Array.isArray(e))throw new Error(`"${U(e)}" is not an literalObject/array`);if(!d(t))throw new Error('wrong "updates" type provided');let n=Array.isArray(e)?[...e]:{...e},a={};for(let[l,f]of Object.entries(t)){if(!l)continue;let s=l.split("."),p=s.pop(),u=a,i=n;for(let c=0;c<s.length;c++){let o=s[c],x=i[o],S=d(x),h=g(x),T=x===void 0;if(!S&&!h&&!T)throw new Error(`Property "${o}" in "${O(s,c)}" is a primitive`);if(!u[o]){if(T){let V=g(i)&&y(o),N=y(s[c+1]??p),I=V?r.onNewIndexes:r.onNewProps;if(I==="error")throw new Error(`${o} doesn't exist in "${O(s,c)}"`);if(I==="skip")continue;i[o]=N?[]:{}}else i[o]=h?[...x]:{...x};u[o]={}}i=i[o],u=u[o]}let b=i;if(!b.hasOwnProperty(p)){let o=Array.isArray(b)&&y(p)?r.onNewIndexes:r.onNewProps;if(o==="error")throw new Error(`Property/Index "${p}" doesnt exist in the path "${O([...s,p])}". Its value is: "${U(b)}"`);if(o==="skip")continue}b[p]=typeof f=="function"?f(b[p]):f,delete u[p]}return n},D=(e,t,r)=>E(e,t,r),L=(e,t)=>E(e,t);0&&(module.exports={extendableUpdate,isObjectLiteral,isStringIndex,parseStringIndex,remove,select,set,splitPathAtLastKey,update});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var P=(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 i=Symbol(),p=[...t];return r.forEach(n=>{p[n]=i}),p.filter(n=>n!==i)}let o={...t};return r.forEach(i=>delete o[i]),o};var O=(t,e)=>!e||typeof e!="string"?t:e.split(".").reduce((r,o)=>{if(r?.hasOwnProperty(o))return r[o]},t);var g=t=>{if(typeof t!="string")throw new Error(`Wrong dotPath type: ${t}`);let e=t.lastIndexOf(".")+1,r=e?t.slice(0,e-1):"",o=t.slice(e);return[r,o]};var A=t=>()=>t;var f=t=>!!t&&Object.getPrototypeOf(t)===Object.prototype;var j=t=>{let e=parseInt(t);return e.toString()===t?e:null},h=t=>parseInt(t).toString()===t;var x="${number}",I=(t,e,r)=>{if(!f(t)&&!Array.isArray(t))throw new Error(`"${t}" is not an literalObject/array`);if(!e)return t;let o=Array.isArray(t)?[...t]:{...t},i={};for(let[p,d]of Object.entries(e)){if(!p)continue;let n=p.split(".");if(n.includes(x))throw new Error(`"${x}" inside "${p}"`);let s=n.pop(),l=i,y=o;for(let u=0;u<n.length;u++){let a=n[u],c=y[a],T=f(c)||Array.isArray(c),b=r&&c===void 0;if(!T&&!b)throw new Error(`"${a}" doesnt exist in "${n.slice(0,u+1).join(".")}"`);l[a]||(b?y[a]=h(n[u+1]??s)?[]:{}:y[a]=Array.isArray(c)?[...c]:{...c},l[a]={}),y=y[a],l=l[a]}let m=y;if(!r&&!m.hasOwnProperty(s))throw new Error(`Property/Index "${s}" doesnt exist in the path "${n.join(".")+"."+s}". Its value is: "${m}"`);m[s]=typeof d=="function"?d(m[s],o):d,delete l[s]}return o},K=(t,e)=>I(t,e,!1),D=(t,e)=>I(t,e,!0);export{f as isObjectLiteral,h as isStringIndex,D as modifyImmutably,j as parseStringIndex,P as remove,O as select,A as set,g as splitPathAtLastKey,K as updateImmutably};
1
+ var U=(e,t)=>{if(!Array.isArray(e)&&e?.constructor!==Object)throw'Wrong type provided in "remove" function';let r=Array.isArray(t)?t:[t];if(Array.isArray(e)){let c=Symbol(),d=[...e];return r.forEach(o=>{d[o]=c}),d.filter(o=>o!==c)}let a={...e};return r.forEach(c=>delete a[c]),a};var g=(e,t)=>!t||typeof t!="string"?e:t.split(".").reduce((r,a)=>{if(r?.hasOwnProperty(a))return r[a]},e);var E=e=>{if(typeof e!="string")throw new Error(`Wrong dotPath type: ${e}`);let t=e.lastIndexOf(".")+1,r=t?e.slice(0,t-1):"",a=e.slice(t);return[r,a]};var D=e=>()=>e;var b=e=>!!e&&Object.getPrototypeOf(e)===Object.prototype;var L=e=>{let t=parseInt(e);return t.toString()===e?t:null},x=e=>parseInt(e).toString()===e;var I=e=>JSON.stringify(e),m=(e,t)=>e.slice(0,t).join("."),j=e=>Array.isArray(e),P=(e,t,r={})=>{if(r={onNewProps:"add",onNewIndexes:"error",...r},!b(e)&&!Array.isArray(e))throw new Error(`"${I(e)}" is not an literalObject/array`);if(!b(t))throw new Error('wrong "updates" type provided');let a=Array.isArray(e)?[...e]:{...e},c={};for(let[d,f]of Object.entries(t)){if(!d)continue;let o=d.split("."),s=o.pop(),y=c,p=a;for(let i=0;i<o.length;i++){let n=o[i],u=p[n],A=b(u),O=j(u),h=u===void 0;if(!A&&!O&&!h)throw new Error(`Property "${n}" in "${m(o,i)}" is a primitive`);if(!y[n]){if(h){let w=j(p)&&x(n),K=x(o[i+1]??s),T=w?r.onNewIndexes:r.onNewProps;if(T==="error")throw new Error(`${n} doesn't exist in "${m(o,i)}"`);if(T==="skip")continue;p[n]=K?[]:{}}else p[n]=O?[...u]:{...u};y[n]={}}p=p[n],y=y[n]}let l=p;if(!l.hasOwnProperty(s)){let n=Array.isArray(l)&&x(s)?r.onNewIndexes:r.onNewProps;if(n==="error")throw new Error(`Property/Index "${s}" doesnt exist in the path "${m([...o,s])}". Its value is: "${I(l)}"`);if(n==="skip")continue}l[s]=typeof f=="function"?f(l[s]):f,delete y[s]}return a},S=(e,t,r)=>P(e,t,r),V=(e,t)=>P(e,t);export{V as extendableUpdate,b as isObjectLiteral,x as isStringIndex,L as parseStringIndex,U as remove,g as select,D as set,E as splitPathAtLastKey,S as update};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "maraj",
3
- "version": "2.5.0",
4
- "description": "A lightweight 500 bytes gzipped alternative for immutable updates while dealing with objects and arrays",
3
+ "version": "2.6.0",
4
+ "description": "A lightweight 600 bytes gzipped alternative for immutable updates while dealing with objects and arrays",
5
5
  "keywords": [
6
6
  "shallow",
7
7
  "shallow copy",