maraj 2.10.0 → 3.0.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,211 +1,249 @@
1
-
2
1
  # Maraj
3
2
 
4
- A fast lightweight utility for creating **immutable updates**. Fully typed. Only 600 bytes gziped. Includes usefull utilities for manipulating updates (like remove, select).
3
+ A small utility for **immutable updates** of objects and arrays with dot paths. Fully typed, about 1.2 KB gzipped. Includes helpers to work with updates (`remove`, `select`, `set`, ...).
5
4
 
6
5
  ### Features
7
6
 
8
- - Update and ExtendableUpdate.
9
- - Usefull utilities like "remove", "select".
10
- - Full typescript support.
11
- - Correct type inference.
12
- - Multiple and concurrent updates.
13
- - Nested functional updates.
7
+ - `update` with typed dot paths (`'files.0.name'`), checked by TypeScript.
8
+ - Several updates in one call, sharing the copies of common parents.
9
+ - Function updates that get the current value.
10
+ - Only the objects and arrays along each path are copied; everything else keeps its reference.
11
+ - Options to add, skip or throw on missing props and indexes.
12
+ - Helpers: `remove`, `select`, `set`, `splitPathAtLastKey` and a few type helpers.
14
13
 
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?: ... } is not equal to { key: ... | undefined } )
14
+ ```sh
15
+ npm install maraj
16
+ ```
16
17
 
17
18
  ```ts
18
- const originalValue = { name: 'one', lastname: 'two' modified: false, files: [{ filename: 'one' }] }
19
+ import { update } from 'maraj'
19
20
 
20
- const updatedValue = update(orignalValue, {
21
+ const original = { name: 'one', lastname: 'two', modified: false, files: [{ filename: 'one' }, { filename: 'two' }] }
22
+
23
+ const updated = update(original, {
21
24
  'name': 'updated',
22
- 'modified': (value, fullObject) => !value,
25
+ 'modified': (value) => !value,
23
26
  'files.0.filename': 'zero',
24
27
  })
25
- // updatedValue = { name: 'updated', lastname: 'two' modified: true, files: [{ filename: 'zero' }] }
26
- originalValue.files[1] === updatedValue.files[1] // true
27
-
28
- const newValue = extendableUpdate(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
28
+ // updated = { name: 'updated', lastname: 'two', modified: true, files: [{ filename: 'zero' }, { filename: 'two' }] }
29
+ original.files[1] === updated.files[1] // true, not copied
30
+ original.files === updated.files // false, copied because 'files.0' changed
34
31
  ```
35
32
 
36
- ### Limitations
33
+ <u>**For TypeScript:**</u> set **"strictNullChecks"** and **"noUncheckedIndexedAccess"** to **true** in tsconfig.json, so optional props and props that can be `undefined` get the right type (`{ key?: string }` is not the same as `{ key: string | undefined }`). With **"exactOptionalPropertyTypes"** too, setting `undefined` on a prop that isn't optional is an error.
34
+
35
+ ## update
36
+
37
+ ```ts
38
+ update(state, updateObject, options?)
39
+ ```
37
40
 
38
- - Can work with keys containing dots ({ "this.is.dotted.key": 123 }), however those keys (keys containing dots) must exist in the object beforehand (hasOwnProperty) and also must have a non-literal-object/array value to behave properly; like primitives, sets, maps, and so on. For example the path "a.b.c.d" will work properly in the following scenarios:
39
- - { "a.b.c.d": ... }
40
- - { a: { "b.c.d": ... } }
41
- - { a: { b: {"c.d": ... } } }
42
- - { a: { b: {"c": { d: ... } } } }
41
+ - `state`: an object literal or array, anything else throws (`Date`, `Map`, class instances, `null`).
42
+ - `updateObject`: an object literal of `{ 'dot.path': newValue }`, applied in order.
43
+ - `options`: what to do with paths that don't exist yet (see [Options](#options)).
43
44
 
44
- It can still work with dot-paths keys containing an object or array, however the paths targeting nested properties in this key are forbiden. For example having the following object { a: { "b.c": { d: ... } } } you can use the path "a" and "a.b.c", but you cant use "a.b.c.d", this path will create a new property "b" inside "a", then "c" and lastly "d" resulting in the following object { a: { "b.c": { d: ... } }, b: { c: { d: ... } } }
45
+ Returns a new object with the updates; `state` is never changed.
45
46
 
46
- - In "extendableUpdate", **numbers** inside path will **always** create a mutable **array** if the array/object doesnt exist beforehand. If you want an object/tuple/readonlyArray instead of an array, add it beforehand as type.
47
+ - A **function** as value is called with the current value (`undefined` if the prop doesn't exist) and its result is set. To set a function as the value, use [`set`](#set).
48
+ - Missing parents are created: an array when the next key is an index (`'files.1.name'` on `{}` -> `{ files: [<empty>, { name }] }`), an object otherwise.
49
+ - A primitive or `null` in the middle of a path is replaced by a new object (`'a.b'` on `{ a: 1 }` -> `{ a: { b } }`).
50
+ - Only own props are read: `'toString.value'` creates a `toString` prop instead of reading `Object.prototype.toString`.
51
+ - A path with `'__proto__'` throws (it would change the prototype of the copy).
52
+ - An empty path (`''`) is skipped, and so are symbol keys.
47
53
 
48
54
  ```ts
49
- const updatedValue = extendableUpdate({}, {
50
- 'files.1.name': 'new name',
51
- })
52
- // The "files" prop is an array. updatedValue = { files: [<empty>, { name: 'new name' }] }
55
+ import { update } from 'maraj'
53
56
 
54
- // To get an object instead of an array create an object beforehand
55
- const updatedValue2 = extendableUpdate({}, {
56
- 'files': {}, // add an object first, so then i can add props as numbers after it
57
- 'files.1.name': 'new name',
57
+ const data = {
58
+ profile: { name: 'Lukasz' },
59
+ works: ['soum', 'fi'],
60
+ history: [
61
+ { title: 'soum', size: 90, available: true },
62
+ { title: 'fi', size: 30, available: false },
63
+ ],
64
+ }
65
+
66
+ const newData = update(data, {
67
+ 'profile.name': 'Lucas',
68
+ 'works.1': 'gs',
69
+ 'history.1.available': (available) => !available,
70
+ 'works': (works) => [...works, 'portfolio'], // runs after 'works.1', so it gets ['soum', 'gs']
58
71
  })
59
- // updatedValue2 = { files: { '1': { name: 'new name' } } }
60
72
  ```
61
73
 
62
- - The type "UpdateObject" (the type of the object provided containing the updates, used as second parameter in the update function) if used to create new update objects using computed properties in a inline syntax { [key]: value } will always throw a ts error even if the typing is correct, use the second syntax.
74
+ TypeScript only accepts paths that exist in the type of `state`, with values of the same type. Use [`experimental_extendableUpdate`](#experimental_extendableupdate) to add new props.
75
+
76
+ ### Options
77
+
78
+ Each option takes `'add'` (default), `'skip'` or `'error'`:
79
+
80
+ - `onNewProps`: a prop doesn't exist, in an object, or a string key in an array.
81
+ - `onNewIndexes`: an index doesn't exist in an array (`'works.5'` on a 2-item array). `'error'` makes sure no empty items are created.
82
+ - `onPathsIntersectedByPrimitiveValues`: a primitive or `null` is in the middle of the path (`'a.b'` on `{ a: 1 }`). `'add'` replaces it with an object.
83
+
84
+ `'skip'` skips only that path, at any depth, and still applies the rest. `'error'` throws an `Error` naming the key and the path.
63
85
 
64
86
  ```ts
65
- const updateFieldStore = <T, TPath extends DotPaths<T>>(
66
- state: T,
67
- path: TPath,
68
- newValue: UpdateValue<T, TPath>
69
- ) => {
70
- // ts error even if the typing is correct (typescript issue)
71
- const updateObject1: UpdateObject<T> = {
72
- [path]: newValue
73
- }
74
-
75
- // All good, no ts error
76
- const updateObject2: UpdateObject<T> = {}
77
- updateObject[path] = newValue
78
-
79
- }
87
+ update({ a: { d: 1 } }, { 'a.b.c': 1, 'a.d': 2 }, { onNewProps: 'skip' })
88
+ // { a: { d: 2 } }
89
+
90
+ update({ list: [1, 2] }, { 'list.5': 3 }, { onNewIndexes: 'error' })
91
+ // throws: Property/Index "5" doesnt exist in the path "list.5"...
80
92
  ```
81
93
 
82
- - 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 nested props inside arrays.
94
+ ## experimental_extendableUpdate
95
+
96
+ Same as `update`, but TypeScript also accepts new paths and the returned type includes them. It has no options: new props and indexes are always added.
83
97
 
84
98
  ```ts
85
- const updatedValue = update(data, {
86
- 'files.0.nonOptionalProperty': 'new name',
87
- 'files.1.nonOptionalProperty': undefined, //no ts error
88
- 'files.0.nonOptionalProperty': set('new name'),
89
- 'files.1.nonOptionalProperty': set(undefined), //throws ts error, undefined cant be a value.
99
+ import { experimental_extendableUpdate } from 'maraj'
100
+
101
+ const newData = experimental_extendableUpdate(data, {
102
+ 'profile.age': 28, // new prop
103
+ 'profile.name': 'Lucas',
104
+ 'works.10': 'gs', // new index, items 2 to 9 are left empty
90
105
  })
91
106
  ```
92
107
 
93
- ## Creating a Strict Immutable Update
108
+ A number in the path creates a mutable array when nothing exists there yet. To get an object instead, set it first:
109
+
110
+ ```ts
111
+ experimental_extendableUpdate({}, { 'files.1.name': 'new name' })
112
+ // { files: [<empty>, { name: 'new name' }] }
113
+
114
+ experimental_extendableUpdate({}, {
115
+ 'files': {},
116
+ 'files.1.name': 'new name',
117
+ })
118
+ // { files: { '1': { name: 'new name' } } }
119
+ ```
94
120
 
95
- Arguments:
121
+ ## set
96
122
 
97
- - Original Object
98
- - Update Object
99
- - Options (to change JS behaviour on non existing props and indexes)
100
- - 'add': Adds non existing property/index.
101
- - 'skip': Skips the update in the non-existing prop/index and still applies the rest of the possible updates.
102
- - 'throw': Throws a JS error, usefull working with indexes making sure there are no "empty" indexes created if a new index is added which is bigger than the length of the current array.
123
+ Returns a function update that always returns the given value. Use it to:
103
124
 
104
- ## Example
125
+ - Set a function as a value (a plain function would be called as an update).
126
+ - Get a stricter type when TypeScript mixes up an optional prop with one that can be `undefined` (usually props of items inside arrays): `set(undefined)` is an error.
105
127
 
106
- ```js
107
- import { update } from "maraj";
128
+ ```ts
129
+ import { update, set } from 'maraj'
108
130
 
109
- const data = {
110
- profile: { name: "Lukasz" },
111
- works: ["soum", "fi"],
112
- history: [
113
- { title: "soum", size: 90, available: true, info: ["info1"] },
114
- { title: "fi", size: 30, available: false, info: ["infoA"] },
115
- ]
116
- }
131
+ const button = { label: 'Save', onClick: () => {}, files: [{ name: 'one' }] }
117
132
 
118
- const newUpdatedObject = update(data, {
119
- "profile.name": "Lucas", //update values
120
- "works.1": "gs", //nested array update
121
- "history.1.available": (currentValue) => (!currentValue), //functional update
122
- "works": (v) => [...v, 'portfolio'] //add new value to array
133
+ update(button, {
134
+ 'onClick': set(() => console.log('clicked')),
135
+ 'files.0.name': set('new name'),
123
136
  })
124
137
  ```
125
138
 
126
- ## Creating an Immutable Update allowing new props to be added
127
-
128
- Arguments:
139
+ ## remove
129
140
 
130
- - Original Object
131
- - Update Object
141
+ Returns a copy of an object without the given keys, or of an array without the given indexes. Use it inside a function update.
132
142
 
133
- ## Example
143
+ - Objects: only optional keys can be removed (TypeScript).
144
+ - Arrays: indexes as numbers or strings (`1`, `'1'`), in any order; repeated or out-of-range indexes are ignored.
145
+ - Anything other than an object literal or array throws.
134
146
 
135
- ```js
136
- import { extendableUpdate } from "maraj";
147
+ ```ts
148
+ import { update, remove } from 'maraj'
137
149
 
138
- const data = {
139
- profile: { name: "Lukasz" },
140
- works: ["soum", "fi"],
141
- history: [
142
- { title: "soum", size: 90, available: true, info: ["info1"] },
143
- { title: "fi", size: 30, available: false, info: ["infoA"] },
144
- ]
145
- }
150
+ type Person = { profile: { name: string, nickname?: string }, works: string[] }
151
+ const person: Person = { profile: { name: 'Lukasz', nickname: 'Luk' }, works: ['soum', 'fi', 'gs'] }
146
152
 
147
- const newUpdatedObject = extendableUpdate(data, {
148
- "profile.age": 28, //add new property
149
- "profile.name": "Lucas", //update values
150
- "works.10": "gs", //increases the array (filling it up with empty/undefined) and sets the value in the index of 10 to "gs"
153
+ update(person, {
154
+ 'profile': (profile) => remove(profile, 'nickname'), // removes "nickname"
155
+ 'works': (works) => remove(works, [1, '0']), // ['gs']
151
156
  })
152
157
  ```
153
158
 
154
- ## Remove Utility
159
+ ## select
155
160
 
156
- Removes the **key of an object** or the **index of an array** in the key provided in the **update.**
161
+ Returns the value in a dot path, or `undefined` if the path doesn't exist. `''` returns the whole state.
157
162
 
158
- ```js
159
- import {remove} from 'maraj'
163
+ - Only own props are read: `'profile.toString'` and `'tags.map'` give `undefined`, and so does `'size'` on a `Map`.
164
+ - `null` or `undefined` in the middle of the path gives `undefined`.
165
+ - A key with dots (`{ 'a.b': 1 }`) is found when it matches the whole rest of the path.
160
166
 
161
- const newUpdatedObject = update(person,{
162
- 'profile': (value) => remove(value, 'name'), // returns new value without the "name" prop
163
- 'profile': (value) => remove(value, ['name', 'lastname']), // returns new value without the "name" and "lastname" prop
164
- 'works': (value) => remove(value, 1), // returns new array without the provided index (1)
165
- 'works': (value) => remove(value, [1, "0"]), // returns new array without the provided index "0" and "1"
166
- })
167
+ ```ts
168
+ import { select } from 'maraj'
169
+
170
+ const work1SizeSelector = (state) => select(state, 'history.1.size') // state.history[1].size
167
171
  ```
168
172
 
169
- ## Select Utility
173
+ ## splitPathAtLastKey
170
174
 
171
- Selects the value of an object/array from a string-dot-path. Usefull in state management libraries.
175
+ Splits a dot path into the path before the last key and the last key (for example, to get an id).
172
176
 
173
- ```js
174
- import {select} from 'maraj'
177
+ ```ts
178
+ import { splitPathAtLastKey } from 'maraj'
175
179
 
176
- const stringPath = 'works.1.size'
180
+ const [remainingPath, lastKey] = splitPathAtLastKey('projects.1.data.size')
181
+ // remainingPath = 'projects.1.data', lastKey = 'size'
177
182
 
178
- const work1SizeSelector = (state) => select(state, stringPath) // will behave as (store) => store[works][1][size]
183
+ splitPathAtLastKey('size') // ['', 'size']
179
184
  ```
180
185
 
181
- ## SplitPathAtLastKey Utility
186
+ ## Type helpers
182
187
 
183
- Usefull when you want to retrieve the last key (for example an id).
188
+ - `strictType<T, U>(value)`: returns `value` as `U`, and TypeScript errors when `value` has props `U` doesn't have (plain assignment allows them).
189
+ - `strict(value)` (or `s`): returns `value` unchanged, typed as `Strict<T>`.
190
+ - `isObjectLiteral(value)`: `true` when the prototype is `Object.prototype` (`{}`, `{ a: 1 }`); `false` for arrays, `null`, `Object.create(null)`, `Date`, `Map`, class instances and objects from another realm (`node:vm`, iframes).
191
+ - `isStringIndex(key)`: `true` when `key` is an integer written as `parseInt` would write it (`'1'`); `false` for `'01'`, `'1.5'`, `''` and `'NaN'`. `parseStringIndex(key)` returns that number, or `null`.
192
+ - `forEachEntry(object, (key, value) => ...)`: `Object.keys(object).forEach` with typed keys and values.
184
193
 
185
- Splits string path (example:'projects.1.data.size' ) into two:
194
+ ```ts
195
+ import { strictType } from 'maraj'
186
196
 
187
- - remainingPath: the remaining path (in this case 'projects.1.data')
188
- - lastKey: the last key in the path (in this case 'size')
197
+ type User = { name: string, age?: number }
198
+ const saveUser = (user: User) => { /* ... */ }
189
199
 
190
- ```js
191
- import { splitPathAtLastKey } from 'maraj'
200
+ const user = { name: 'Ana', admin: true }
201
+ saveUser(user) // no error
202
+ saveUser(strictType<typeof user, User>(user)) // error: "admin" isn't in User
203
+ ```
192
204
 
193
- const stringPath = 'works.1.size'
205
+ ## Limitations
194
206
 
195
- const [remainingPath, lastKey] = splitPathAtLastKey(stringPath)
196
- // lastKey = 'size'
197
- // remainingPath = 'works.1'
198
- ```
207
+ - **Keys with dots** (`{ 'this.is.dotted.key': 123 }`) work only when they already exist and the path ends on them. The path `'a.b.c.d'` works on:
208
+ - `{ 'a.b.c.d': ... }`
209
+ - `{ a: { 'b.c.d': ... } }`
210
+ - `{ a: { b: { 'c.d': ... } } }`
211
+ - `{ a: { b: { c: { d: ... } } } }`
199
212
 
200
- ## Set Utility
213
+ Paths into the value of a dotted key don't: with `{ a: { 'b.c': { d: 1 } } }`, `'a.b.c'` works but `'a.b.c.d'` creates `a.b.c.d` next to `'b.c'`: `{ a: { 'b.c': { d: 1 }, b: { c: { d: ... } } } }`.
201
214
 
202
- 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.
215
+ - **`UpdateObject` with a computed key** in an object literal is a TypeScript error even when the types are right. Assign the key after creating the object:
203
216
 
204
217
  ```ts
205
- const updatedValue = update(data, {
206
- 'files.0.optionalProperty': 'new name',
207
- 'files.1.optionalProperty': undefined, //no ts error
208
- 'files.0.optionalProperty': set('new name'),
209
- 'files.1.optionalProperty': set(undefined), //throws ts error, undefined cant be a value.
210
- })
218
+ import type { DotPaths, UpdateObject, UpdateValue } from 'maraj'
219
+
220
+ const updateField = <T, TPath extends DotPaths<T>>(state: T, path: TPath, newValue: UpdateValue<T, TPath>) => {
221
+ // ts error, even if the typing is correct (TypeScript issue)
222
+ const updateObject1: UpdateObject<T> = { [path]: newValue }
223
+
224
+ // no ts error
225
+ const updateObject2: UpdateObject<T> = {}
226
+ updateObject2[path] = newValue
227
+ return update(state, updateObject2)
228
+ }
211
229
  ```
230
+
231
+ ## Benchmarks
232
+
233
+ <!-- bench:start -->
234
+ Median speed against maraj in 11 scenarios (higher is faster)
235
+
236
+ ```text
237
+ dot-prop-immutable ██████████████████████████████ 1.26x
238
+ maraj ████████████████████████ 1.00x
239
+ timm ██████████████████ 0.77x
240
+ object-path-immutable ██████████████ 0.58x
241
+ immer (no autofreeze) ████████████ 0.49x
242
+ immutability-helper ██████████ 0.41x
243
+ lodash/fp █████████ 0.38x
244
+ mutative ████████ 0.35x
245
+ immer ███████ 0.30x
246
+ ```
247
+ <!-- bench:end -->
248
+
249
+ `1.50x` means 1.5 times as many updates per second as maraj. The scenarios go from one top-level prop to 100 paths in a 1,000-item array: see [benchmarks/README.md](benchmarks/README.md) for each one, the machine and how to run them.
package/dist/index.d.mts CHANGED
@@ -6,13 +6,11 @@
6
6
  /** Dot-paths of an array. */
7
7
  type ArrayPaths<TInnerType, TAllowedTypes> = TInnerType extends TAllowedTypes ? TInnerType extends PrimitivesAndNativeObjects ? ExtendedArrayIndexes : ExtendedArrayIndexes | `${ExtendedArrayIndexes}.${DotPathsImplementation<TInnerType, TAllowedTypes>}` : `${ExtendedArrayIndexes}.${DotPathsImplementation<TInnerType, TAllowedTypes>}`;
8
8
  /** Dot-paths of an object. */
9
- type ObjectPaths<T, TAllowedTypes> = {
10
- [Key in keyof T & (string | number)]: T[Key] extends TAllowedTypes ? T[Key] extends PrimitivesAndNativeObjects ? `${Key}` : `${Key}` | `${Key}.${DotPathsImplementation<T[Key], TAllowedTypes>}` : `${Key}.${DotPathsImplementation<T[Key], TAllowedTypes>}`;
11
- }[keyof T & (string | number)] & {};
9
+ type ObjectPaths<T, TAllowedTypes> = { [Key in keyof T & (string | number)]: T[Key] extends TAllowedTypes ? T[Key] extends PrimitivesAndNativeObjects ? `${Key}` : `${Key}` | `${Key}.${DotPathsImplementation<T[Key], TAllowedTypes>}` : `${Key}.${DotPathsImplementation<T[Key], TAllowedTypes>}`; }[keyof T & (string | number)];
12
10
  /** Dot-paths of a tuple. */
13
- type TuplePaths<T, TAllowedTypes> = ObjectPaths<Omit<T, keyof any[]>, TAllowedTypes> & {};
14
- /** Dot-paths implementation */
15
- type DotPathsImplementation<T, TAllowedTypes = any> = T extends ReadonlyArray<infer U> ? T extends Tuple ? TuplePaths<T, TAllowedTypes> : ArrayPaths<U, TAllowedTypes> : T extends PrimitivesAndNativeObjects ? never : ObjectPaths<T, TAllowedTypes>;
11
+ type TuplePaths<T, TAllowedTypes> = ObjectPaths<Required<Omit<T, keyof any[]>>, TAllowedTypes>;
12
+ /** Dot-paths implementation, Required<T> so optional props count as their type ('nickname?: string' is a string) */
13
+ type DotPathsImplementation<T, TAllowedTypes = any> = T extends ReadonlyArray<infer U> ? T extends Tuple ? TuplePaths<T, TAllowedTypes> : ArrayPaths<U, TAllowedTypes> : T extends PrimitivesAndNativeObjects ? never : ObjectPaths<Required<T>, TAllowedTypes>;
16
14
  /** Literal union of all posible dot-paths which satisfy provided types as second argument (default: any). */
17
15
  type DotPaths<T, TAllowedTypes = any> = KeyOf<T, TAllowedTypes> | DotPathsImplementation<T, TAllowedTypes>;
18
16
  /**
@@ -20,15 +18,17 @@ type DotPaths<T, TAllowedTypes = any> = KeyOf<T, TAllowedTypes> | DotPathsImplem
20
18
  * VALUE OF A NESTED PROPERTY PROVIDING A DOT PATH
21
19
  * --------------------------------------------
22
20
  */
23
- /** Nested type implementation. */
24
- type ValueInPath<TObject, TPath> = TPath extends keyof TObject ? TObject[TPath] : TPath extends '' ? TObject : TPath extends LiteralIndex ? never : TPath extends `${number}` ? TObject extends ReadonlyArray<infer U> ? U : never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? ValueInPath<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? TObject extends ReadonlyArray<infer U> ? ValueInPath<U, R> : never : never : never;
21
+ /** Tuple without rest items ([number, string], not [string, ...number[]]): an index that isn't a key is out of range. */
22
+ type IsFixedTuple<T> = T extends Tuple ? number extends T['length'] ? false : true : false;
23
+ /** Nested type implementation, goes into each object of a union ({ name } | null -> 'name' is string). */
24
+ type ValueInPath<TObject, TPath> = TObject extends unknown ? TPath extends keyof TObject ? TObject[TPath] : TPath extends '' ? TObject : TPath extends LiteralIndex ? never : TPath extends `${number}` ? IsFixedTuple<TObject> extends true ? never : TObject extends ReadonlyArray<infer U> ? U : never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? ValueInPath<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? IsFixedTuple<TObject> extends true ? never : TObject extends ReadonlyArray<infer U> ? ValueInPath<U, R> : never : never : never : never;
25
25
  /**
26
26
  * --------------------------------------------
27
27
  * RETURNED VALUE OF A NESTED PROPERTY PROVIDING A DOT PATH
28
28
  * --------------------------------------------
29
29
  */
30
- /** Returned nested type implementation. */
31
- type ReturnedValueInPath<TObject, TPath> = TPath extends '' ? TObject : TPath extends keyof TObject ? TPath extends number ? TObject extends ReadonlyArray<infer U> ? (U | undefined) : TObject[TPath] : TObject[TPath] : TPath extends `${number}` ? TObject extends ReadonlyArray<infer U> ? (U | undefined) : never : TPath extends LiteralIndex ? never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? ReturnedValueInPath<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? TObject extends ReadonlyArray<infer U> ? (ReturnedValueInPath<U, R> | undefined) : never : never : never;
30
+ /** Returned nested type implementation, null or undefined inside the path -> undefined. */
31
+ type ReturnedValueInPath<TObject, TPath> = TPath extends '' ? TObject : TObject extends null | undefined ? undefined : TPath extends keyof TObject ? TPath extends number ? TObject extends ReadonlyArray<infer U> ? (U | undefined) : TObject[TPath] : TObject[TPath] : TPath extends `${number}` ? IsFixedTuple<TObject> extends true ? never : TObject extends ReadonlyArray<infer U> ? (U | undefined) : never : TPath extends LiteralIndex ? never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? ReturnedValueInPath<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? IsFixedTuple<TObject> extends true ? never : TObject extends ReadonlyArray<infer U> ? (ReturnedValueInPath<U, R> | undefined) : never : never : never;
32
32
  /**
33
33
  * --------------------------------------------
34
34
  * UPDATE OBJECT WITH DOT PATH IMPLEMENTATION
@@ -37,9 +37,7 @@ type ReturnedValueInPath<TObject, TPath> = TPath extends '' ? TObject : TPath ex
37
37
  /** Value of a dot-path update object, can be the value or a function. */
38
38
  type UpdateValue<TObject, TPath> = ValueInPath<TObject, TPath> extends never ? never : ValueInPath<TObject, TPath> | ((fieldValue: ValueInPath<TObject, TPath>) => Strict<ValueInPath<TObject, TPath>>);
39
39
  /** Dot-path update object, used to give updates to the updateImmutably function. */
40
- type UpdateObject<TObject, TPaths extends DotPaths<TObject> = DotPaths<TObject>> = {
41
- [Path in TPaths]?: UpdateValue<TObject, Path>;
42
- };
40
+ type UpdateObject<TObject, TPaths extends DotPaths<TObject> = DotPaths<TObject>> = { [Path in TPaths]?: UpdateValue<TObject, Path>; };
43
41
  /**
44
42
  * --------------------------------------------
45
43
  * EXTRACT TYPE FROM DOT-PATH UPDATE OBJECT
@@ -47,78 +45,54 @@ type UpdateObject<TObject, TPaths extends DotPaths<TObject> = DotPaths<TObject>>
47
45
  */
48
46
  /** Merging two objects */
49
47
  /** Keys which arent dot-path keys */
50
- type NonDotPathKeys<T> = {
51
- [Key in keyof T]: Key extends `${infer K}.${string}` ? K : Key;
52
- }[keyof T];
48
+ type NonDotPathKeys<T> = { [Key in keyof T]: Key extends `${infer K}.${string}` ? K : Key; }[keyof T];
53
49
  /** Keys which are an index */
54
- type KeysRepresentingAnIndex<T> = {
55
- [Key in keyof T]: Key extends `${number}.${string}` ? never : Key;
56
- }[keyof T];
50
+ type KeysRepresentingAnIndex<T> = { [Key in keyof T]: Key extends `${number}.${string}` ? never : Key; }[keyof T];
57
51
  /** Keys which start with an index */
58
- type KeyPathsStartingWithAnIndex<T> = {
59
- [Key in keyof T]: Key extends `${number}.${string}` ? Key : never;
60
- }[keyof T];
52
+ type KeyPathsStartingWithAnIndex<T> = { [Key in keyof T]: Key extends `${number}.${string}` ? Key : never; }[keyof T];
61
53
  /** Keys which are paths and its first key-path is removed */
62
- type NestedPathKeys<T, K extends string | number> = {
63
- [Key in keyof T]: Key extends `${K}.${infer U}` ? U : never;
64
- }[keyof T];
54
+ type NestedPathKeys<T, K extends string | number> = { [Key in keyof T]: Key extends `${K}.${infer U}` ? U : never; }[keyof T];
65
55
  /** Extract value if its a function */
66
- type Value<T> = T extends (...args: any) => any ? ReturnType<T> : T;
56
+ type Value<T> = T extends ((...args: any) => any) ? ReturnType<T> : T;
67
57
  /** To check if an empty object is provided */
68
58
  type EmptyObject = Record<PropertyKey, never>;
69
59
  /** Used to extract the type from a dot-path update object. */
70
- 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>]> | {
71
- [Key2 in U]: Value<T[KeyPathsStartingWithAnIndex<T>]>;
72
- }> : {
73
- [Key1 in NonDotPathKeys<T> & string]: Key1 extends keyof T ? Value<T[Key1]> extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<{
74
- [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
75
- }> extends Array<infer W> ? Array<U | W> : Array<U> & ExtractTypeFromDotPathUpdateObject<{
76
- [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
77
- }> : Value<T[Key1]> : ExtractTypeFromDotPathUpdateObject<{
78
- [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
79
- }>;
80
- } & unknown;
60
+ 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>]> | { [Key2 in U]: Value<T[KeyPathsStartingWithAnIndex<T>]>; }> : { [Key1 in NonDotPathKeys<T> & string]: Key1 extends keyof T ? Value<T[Key1]> extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<{ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>; }> extends Array<infer W> ? Array<U | W> : Array<U> & ExtractTypeFromDotPathUpdateObject<{ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>; }> : Value<T[Key1]> : ExtractTypeFromDotPathUpdateObject<{ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>; }>; } & unknown;
81
61
  /** New expanded value after aplying updates including new properties */
82
62
  type ExtendedUpdate<TObject, TDotPathUpdateObject extends Record<PropertyKey, any>> = TObject extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject> extends Array<infer W> ? Array<U | W> : TObject & ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject> : TObject & ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject>;
83
-
84
63
  /** Index literal type to improve DX at managing updates. */
85
64
  declare const LITERAL_INDEX = "${number}";
86
-
87
65
  /** Primitives and browser native objects. */
88
- type PrimitivesAndNativeObjects = null | undefined | string | number | boolean | symbol | bigint | Date | FileList | File;
66
+ type PrimitivesAndNativeObjects = null | undefined | string | number | boolean | symbol | bigint | Date | FileList | File | Blob;
89
67
  /** Tuple type, it has to have at least property "0" */
90
68
  type Tuple = ReadonlyArray<any> & {
91
- "0": any;
69
+ "0": any;
92
70
  };
93
71
  /** Array indexes type. */
94
72
  type LiteralIndex = typeof LITERAL_INDEX;
95
73
  type ExtendedArrayIndexes = number | `${number}` | LiteralIndex;
96
- /** Object Literal */
74
+ /** Object Literal, File and Blob still match it (they have no symbol to reject them by) */
97
75
  type ObjectLiteral = {
98
- [x: string]: any;
99
- [x: number]: any;
100
- [x: symbol]: any;
101
- [Symbol.iterator]?: never;
102
- [Symbol.toPrimitive]?: never;
103
- [Symbol.hasInstance]?: never;
104
- [Symbol.unscopables]?: never;
105
- [Symbol.matchAll]?: never;
76
+ [x: string]: any;
77
+ [x: number]: any;
78
+ [x: symbol]: any;
79
+ [Symbol.iterator]?: never;
80
+ [Symbol.toPrimitive]?: never;
81
+ [Symbol.hasInstance]?: never;
82
+ [Symbol.unscopables]?: never;
83
+ [Symbol.matchAll]?: never;
84
+ [Symbol.toStringTag]?: never;
106
85
  };
107
86
  /** Strict key */
108
87
  declare const strictSymbol: unique symbol;
109
- type Strict<T> = T extends ObjectLiteral ? T & {
110
- [strictSymbol]: DotPaths<T>;
88
+ type Strict<T> = T extends PrimitivesAndNativeObjects ? T : T extends ObjectLiteral ? T & {
89
+ [strictSymbol]: DotPaths<T>;
111
90
  } : T;
112
91
  /** Smart KeyOf (doesnt include the "strict" key) */
113
- type KeyOf<T, TAllowedValuesInKeys = any> = T extends ObjectLiteral ? T extends Strict<infer U> ? KeysOfObject<U, TAllowedValuesInKeys> : KeysOfObject<T, TAllowedValuesInKeys> : T extends Tuple ? KeysOfObject<Omit<T, keyof any[]>, TAllowedValuesInKeys> : T extends ReadonlyArray<infer U> ? U extends TAllowedValuesInKeys ? ExtendedArrayIndexes : never : never;
114
- type KeysOfObject<T, TAllowedValuesInKeys> = {
115
- [Key in keyof T]: T[Key] extends TAllowedValuesInKeys ? Key : never;
116
- }[keyof T];
117
-
92
+ type KeyOf<T, TAllowedValuesInKeys = any> = T extends PrimitivesAndNativeObjects ? never : T extends ObjectLiteral ? T extends Strict<infer U> ? KeysOfObject<U, TAllowedValuesInKeys> : KeysOfObject<T, TAllowedValuesInKeys> : T extends Tuple ? KeysOfObject<Omit<T, keyof any[]>, TAllowedValuesInKeys> : T extends ReadonlyArray<infer U> ? U extends TAllowedValuesInKeys ? ExtendedArrayIndexes : never : never;
93
+ type KeysOfObject<T, TAllowedValuesInKeys> = { [Key in keyof T]-?: Required<T>[Key] extends TAllowedValuesInKeys ? Key : never; }[keyof T];
118
94
  /** Returns only the optional keys of an object. */
119
- type OptionalKeys<T> = {
120
- [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
121
- }[keyof T];
95
+ type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never; }[keyof T];
122
96
  /** Removes properties or indexes, order doesn't matter. */
123
97
  type Remove = <T extends ObjectLiteral | any[]>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : OptionalKeys<T> | (OptionalKeys<T>)[]) => T;
124
98
  /**
@@ -127,11 +101,10 @@ type Remove = <T extends ObjectLiteral | any[]>(value: T, keys: T extends any[]
127
101
  * @param keys Keys to remove.
128
102
  * @returns New object/array without the provided keys/indexes.
129
103
  */
130
- declare const remove: Remove;
131
-
104
+ export declare const remove: Remove;
132
105
  type Select = {
133
- <T, TPath extends DotPaths<T>>(state: T, path: TPath): ReturnedValueInPath<T, TPath>;
134
- <T, TPath extends ''>(state: T, path: TPath): T;
106
+ <T, TPath extends DotPaths<T>>(state: T, path: TPath): ReturnedValueInPath<T, TPath>;
107
+ <T, TPath extends ''>(state: T, path: TPath): T;
135
108
  };
136
109
  /**
137
110
  * Returns the value from a given dot path of an object/array or undefined if doesn't exist.
@@ -139,8 +112,7 @@ type Select = {
139
112
  * @param path Path indicating the value to extract.
140
113
  * @returns Value of the provided path or undefined.
141
114
  */
142
- declare const select: Select;
143
-
115
+ export declare const select: Select;
144
116
  type PathWithoutLastKey = string;
145
117
  type LastKey = string;
146
118
  type SplitDotPathAtLastKey = <T extends string>(dotPath: T) => [PathWithoutLastKey, LastKey];
@@ -149,21 +121,19 @@ type SplitDotPathAtLastKey = <T extends string>(dotPath: T) => [PathWithoutLastK
149
121
  * @param dotPath A dot path.
150
122
  * @returns [pathWithoutLastKey, lastKey] or ['', lastKey].
151
123
  */
152
- declare const splitPathAtLastKey: SplitDotPathAtLastKey;
153
-
124
+ export declare const splitPathAtLastKey: SplitDotPathAtLastKey;
154
125
  type Set = <T>(newValue: T) => (v: unknown) => T;
155
126
  /**
156
127
  * Allows to set strongly typed values (removing inacurate "undefined" inference) in nested values inside arrays.
157
128
  * @param newValue New value.
158
129
  * @returns The same value.
159
130
  */
160
- declare const set: Set;
161
-
131
+ export declare const set: Set;
162
132
  type OptionValues = 'error' | 'skip' | 'add';
163
133
  type Options = {
164
- onNewProps?: OptionValues;
165
- onNewIndexes?: OptionValues;
166
- onPathsIntersectedByPrimitiveValues?: OptionValues;
134
+ onNewProps?: OptionValues;
135
+ onNewIndexes?: OptionValues;
136
+ onPathsIntersectedByPrimitiveValues?: OptionValues;
167
137
  };
168
138
  type ImmutableUpdate = <TObject, TAllowedDotPathKeys extends DotPaths<TObject> = DotPaths<TObject>>(state: TObject, dotPathUpdateObject: UpdateObject<TObject, TAllowedDotPathKeys>, options?: Options) => TObject;
169
139
  /**
@@ -175,7 +145,7 @@ type ImmutableUpdate = <TObject, TAllowedDotPathKeys extends DotPaths<TObject> =
175
145
  * @param options Options to change JS behaviour on non-existent fields.
176
146
  * @returns New Updated Object.
177
147
  */
178
- declare const update: ImmutableUpdate;
148
+ export declare const update: ImmutableUpdate;
179
149
  type ExtendableImmutableUpdate = <TObject, TUpdateObject extends UpdateObject<TObject> = UpdateObject<TObject>>(state: TObject, dotPathUpdateObject: TUpdateObject) => ExtendedUpdate<TObject, TUpdateObject>;
180
150
  /**
181
151
  * Creates a new object applying immutable updates using dot-path syntax.
@@ -185,26 +155,21 @@ type ExtendableImmutableUpdate = <TObject, TUpdateObject extends UpdateObject<TO
185
155
  * @param dotPathUpdateObject Dot Path Update Object.
186
156
  * @returns New Updated Object.
187
157
  */
188
- declare const experimental_extendableUpdate: ExtendableImmutableUpdate;
189
-
158
+ export declare const experimental_extendableUpdate: ExtendableImmutableUpdate;
190
159
  type ForEachEntry = <T extends ObjectLiteral>(object: T, callbackfn: (key: keyof T, value: T[keyof T]) => void) => void;
191
- declare const forEachEntry: ForEachEntry;
192
-
160
+ export declare const forEachEntry: ForEachEntry;
193
161
  /** Checks if any provided value is an object literal or not */
194
162
  type IsObjectLiteral = (valueToTest: unknown) => valueToTest is ObjectLiteral;
195
- declare const isObjectLiteral: IsObjectLiteral;
196
-
163
+ export declare const isObjectLiteral: IsObjectLiteral;
197
164
  /** Adds strict property to an object literal */
198
165
  type StrictFn = <T>(value: T) => Strict<T>;
199
- declare const strict: StrictFn;
200
- declare const s: StrictFn;
166
+ export declare const strict: StrictFn;
167
+ export declare const s: StrictFn;
201
168
  /** Checks the strictness of a type passed to a function or callback without 3rd party implementation */
202
- type StrictValueWrapper = <T extends U, U>(v: T extends U ? DotPaths<T> extends DotPaths<U> ? T : Exclude<DotPaths<T>, DotPaths<U>> : never) => U;
203
- declare const strictValueWrapper: StrictValueWrapper;
204
-
169
+ type StrictType = <T extends U, U>(v: T extends U ? DotPaths<T> extends DotPaths<U> ? T : Exclude<DotPaths<T>, DotPaths<U>> : never) => U;
170
+ export declare const strictType: StrictType;
205
171
  type ParseStringIndex = (stringKey: string) => number | null;
206
- declare const parseStringIndex: ParseStringIndex;
172
+ export declare const parseStringIndex: ParseStringIndex;
207
173
  type IsStringIndex = (stringKey: string) => stringKey is `${number}`;
208
- declare const isStringIndex: IsStringIndex;
209
-
210
- export { type DotPaths, type ExtendedArrayIndexes, type KeyOf, type LiteralIndex, type ObjectLiteral, type PrimitivesAndNativeObjects, type ReturnedValueInPath, type Strict, type Tuple, type UpdateObject, type UpdateValue, type ValueInPath, experimental_extendableUpdate, forEachEntry, isObjectLiteral, isStringIndex, parseStringIndex, remove, s, select, set, splitPathAtLastKey, strict, strictValueWrapper, update };
174
+ export declare const isStringIndex: IsStringIndex;
175
+ export type { DotPaths, ExtendedArrayIndexes, KeyOf, LiteralIndex, ObjectLiteral, PrimitivesAndNativeObjects, ReturnedValueInPath, Strict, Tuple, UpdateObject, UpdateValue, ValueInPath };
package/dist/index.d.ts CHANGED
@@ -6,13 +6,11 @@
6
6
  /** Dot-paths of an array. */
7
7
  type ArrayPaths<TInnerType, TAllowedTypes> = TInnerType extends TAllowedTypes ? TInnerType extends PrimitivesAndNativeObjects ? ExtendedArrayIndexes : ExtendedArrayIndexes | `${ExtendedArrayIndexes}.${DotPathsImplementation<TInnerType, TAllowedTypes>}` : `${ExtendedArrayIndexes}.${DotPathsImplementation<TInnerType, TAllowedTypes>}`;
8
8
  /** Dot-paths of an object. */
9
- type ObjectPaths<T, TAllowedTypes> = {
10
- [Key in keyof T & (string | number)]: T[Key] extends TAllowedTypes ? T[Key] extends PrimitivesAndNativeObjects ? `${Key}` : `${Key}` | `${Key}.${DotPathsImplementation<T[Key], TAllowedTypes>}` : `${Key}.${DotPathsImplementation<T[Key], TAllowedTypes>}`;
11
- }[keyof T & (string | number)] & {};
9
+ type ObjectPaths<T, TAllowedTypes> = { [Key in keyof T & (string | number)]: T[Key] extends TAllowedTypes ? T[Key] extends PrimitivesAndNativeObjects ? `${Key}` : `${Key}` | `${Key}.${DotPathsImplementation<T[Key], TAllowedTypes>}` : `${Key}.${DotPathsImplementation<T[Key], TAllowedTypes>}`; }[keyof T & (string | number)];
12
10
  /** Dot-paths of a tuple. */
13
- type TuplePaths<T, TAllowedTypes> = ObjectPaths<Omit<T, keyof any[]>, TAllowedTypes> & {};
14
- /** Dot-paths implementation */
15
- type DotPathsImplementation<T, TAllowedTypes = any> = T extends ReadonlyArray<infer U> ? T extends Tuple ? TuplePaths<T, TAllowedTypes> : ArrayPaths<U, TAllowedTypes> : T extends PrimitivesAndNativeObjects ? never : ObjectPaths<T, TAllowedTypes>;
11
+ type TuplePaths<T, TAllowedTypes> = ObjectPaths<Required<Omit<T, keyof any[]>>, TAllowedTypes>;
12
+ /** Dot-paths implementation, Required<T> so optional props count as their type ('nickname?: string' is a string) */
13
+ type DotPathsImplementation<T, TAllowedTypes = any> = T extends ReadonlyArray<infer U> ? T extends Tuple ? TuplePaths<T, TAllowedTypes> : ArrayPaths<U, TAllowedTypes> : T extends PrimitivesAndNativeObjects ? never : ObjectPaths<Required<T>, TAllowedTypes>;
16
14
  /** Literal union of all posible dot-paths which satisfy provided types as second argument (default: any). */
17
15
  type DotPaths<T, TAllowedTypes = any> = KeyOf<T, TAllowedTypes> | DotPathsImplementation<T, TAllowedTypes>;
18
16
  /**
@@ -20,15 +18,17 @@ type DotPaths<T, TAllowedTypes = any> = KeyOf<T, TAllowedTypes> | DotPathsImplem
20
18
  * VALUE OF A NESTED PROPERTY PROVIDING A DOT PATH
21
19
  * --------------------------------------------
22
20
  */
23
- /** Nested type implementation. */
24
- type ValueInPath<TObject, TPath> = TPath extends keyof TObject ? TObject[TPath] : TPath extends '' ? TObject : TPath extends LiteralIndex ? never : TPath extends `${number}` ? TObject extends ReadonlyArray<infer U> ? U : never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? ValueInPath<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? TObject extends ReadonlyArray<infer U> ? ValueInPath<U, R> : never : never : never;
21
+ /** Tuple without rest items ([number, string], not [string, ...number[]]): an index that isn't a key is out of range. */
22
+ type IsFixedTuple<T> = T extends Tuple ? number extends T['length'] ? false : true : false;
23
+ /** Nested type implementation, goes into each object of a union ({ name } | null -> 'name' is string). */
24
+ type ValueInPath<TObject, TPath> = TObject extends unknown ? TPath extends keyof TObject ? TObject[TPath] : TPath extends '' ? TObject : TPath extends LiteralIndex ? never : TPath extends `${number}` ? IsFixedTuple<TObject> extends true ? never : TObject extends ReadonlyArray<infer U> ? U : never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? ValueInPath<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? IsFixedTuple<TObject> extends true ? never : TObject extends ReadonlyArray<infer U> ? ValueInPath<U, R> : never : never : never : never;
25
25
  /**
26
26
  * --------------------------------------------
27
27
  * RETURNED VALUE OF A NESTED PROPERTY PROVIDING A DOT PATH
28
28
  * --------------------------------------------
29
29
  */
30
- /** Returned nested type implementation. */
31
- type ReturnedValueInPath<TObject, TPath> = TPath extends '' ? TObject : TPath extends keyof TObject ? TPath extends number ? TObject extends ReadonlyArray<infer U> ? (U | undefined) : TObject[TPath] : TObject[TPath] : TPath extends `${number}` ? TObject extends ReadonlyArray<infer U> ? (U | undefined) : never : TPath extends LiteralIndex ? never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? ReturnedValueInPath<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? TObject extends ReadonlyArray<infer U> ? (ReturnedValueInPath<U, R> | undefined) : never : never : never;
30
+ /** Returned nested type implementation, null or undefined inside the path -> undefined. */
31
+ type ReturnedValueInPath<TObject, TPath> = TPath extends '' ? TObject : TObject extends null | undefined ? undefined : TPath extends keyof TObject ? TPath extends number ? TObject extends ReadonlyArray<infer U> ? (U | undefined) : TObject[TPath] : TObject[TPath] : TPath extends `${number}` ? IsFixedTuple<TObject> extends true ? never : TObject extends ReadonlyArray<infer U> ? (U | undefined) : never : TPath extends LiteralIndex ? never : TPath extends `${infer K}.${infer R}` ? K extends keyof TObject ? ReturnedValueInPath<TObject[K], R> : K extends LiteralIndex ? never : K extends `${number}` ? IsFixedTuple<TObject> extends true ? never : TObject extends ReadonlyArray<infer U> ? (ReturnedValueInPath<U, R> | undefined) : never : never : never;
32
32
  /**
33
33
  * --------------------------------------------
34
34
  * UPDATE OBJECT WITH DOT PATH IMPLEMENTATION
@@ -37,9 +37,7 @@ type ReturnedValueInPath<TObject, TPath> = TPath extends '' ? TObject : TPath ex
37
37
  /** Value of a dot-path update object, can be the value or a function. */
38
38
  type UpdateValue<TObject, TPath> = ValueInPath<TObject, TPath> extends never ? never : ValueInPath<TObject, TPath> | ((fieldValue: ValueInPath<TObject, TPath>) => Strict<ValueInPath<TObject, TPath>>);
39
39
  /** Dot-path update object, used to give updates to the updateImmutably function. */
40
- type UpdateObject<TObject, TPaths extends DotPaths<TObject> = DotPaths<TObject>> = {
41
- [Path in TPaths]?: UpdateValue<TObject, Path>;
42
- };
40
+ type UpdateObject<TObject, TPaths extends DotPaths<TObject> = DotPaths<TObject>> = { [Path in TPaths]?: UpdateValue<TObject, Path>; };
43
41
  /**
44
42
  * --------------------------------------------
45
43
  * EXTRACT TYPE FROM DOT-PATH UPDATE OBJECT
@@ -47,78 +45,54 @@ type UpdateObject<TObject, TPaths extends DotPaths<TObject> = DotPaths<TObject>>
47
45
  */
48
46
  /** Merging two objects */
49
47
  /** Keys which arent dot-path keys */
50
- type NonDotPathKeys<T> = {
51
- [Key in keyof T]: Key extends `${infer K}.${string}` ? K : Key;
52
- }[keyof T];
48
+ type NonDotPathKeys<T> = { [Key in keyof T]: Key extends `${infer K}.${string}` ? K : Key; }[keyof T];
53
49
  /** Keys which are an index */
54
- type KeysRepresentingAnIndex<T> = {
55
- [Key in keyof T]: Key extends `${number}.${string}` ? never : Key;
56
- }[keyof T];
50
+ type KeysRepresentingAnIndex<T> = { [Key in keyof T]: Key extends `${number}.${string}` ? never : Key; }[keyof T];
57
51
  /** Keys which start with an index */
58
- type KeyPathsStartingWithAnIndex<T> = {
59
- [Key in keyof T]: Key extends `${number}.${string}` ? Key : never;
60
- }[keyof T];
52
+ type KeyPathsStartingWithAnIndex<T> = { [Key in keyof T]: Key extends `${number}.${string}` ? Key : never; }[keyof T];
61
53
  /** Keys which are paths and its first key-path is removed */
62
- type NestedPathKeys<T, K extends string | number> = {
63
- [Key in keyof T]: Key extends `${K}.${infer U}` ? U : never;
64
- }[keyof T];
54
+ type NestedPathKeys<T, K extends string | number> = { [Key in keyof T]: Key extends `${K}.${infer U}` ? U : never; }[keyof T];
65
55
  /** Extract value if its a function */
66
- type Value<T> = T extends (...args: any) => any ? ReturnType<T> : T;
56
+ type Value<T> = T extends ((...args: any) => any) ? ReturnType<T> : T;
67
57
  /** To check if an empty object is provided */
68
58
  type EmptyObject = Record<PropertyKey, never>;
69
59
  /** Used to extract the type from a dot-path update object. */
70
- 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>]> | {
71
- [Key2 in U]: Value<T[KeyPathsStartingWithAnIndex<T>]>;
72
- }> : {
73
- [Key1 in NonDotPathKeys<T> & string]: Key1 extends keyof T ? Value<T[Key1]> extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<{
74
- [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
75
- }> extends Array<infer W> ? Array<U | W> : Array<U> & ExtractTypeFromDotPathUpdateObject<{
76
- [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
77
- }> : Value<T[Key1]> : ExtractTypeFromDotPathUpdateObject<{
78
- [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>;
79
- }>;
80
- } & unknown;
60
+ 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>]> | { [Key2 in U]: Value<T[KeyPathsStartingWithAnIndex<T>]>; }> : { [Key1 in NonDotPathKeys<T> & string]: Key1 extends keyof T ? Value<T[Key1]> extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<{ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>; }> extends Array<infer W> ? Array<U | W> : Array<U> & ExtractTypeFromDotPathUpdateObject<{ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>; }> : Value<T[Key1]> : ExtractTypeFromDotPathUpdateObject<{ [Key2 in NestedPathKeys<T, Key1>]: Value<T[`${Key1}.${Key2}`]>; }>; } & unknown;
81
61
  /** New expanded value after aplying updates including new properties */
82
62
  type ExtendedUpdate<TObject, TDotPathUpdateObject extends Record<PropertyKey, any>> = TObject extends Array<infer U> ? ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject> extends Array<infer W> ? Array<U | W> : TObject & ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject> : TObject & ExtractTypeFromDotPathUpdateObject<TDotPathUpdateObject>;
83
-
84
63
  /** Index literal type to improve DX at managing updates. */
85
64
  declare const LITERAL_INDEX = "${number}";
86
-
87
65
  /** Primitives and browser native objects. */
88
- type PrimitivesAndNativeObjects = null | undefined | string | number | boolean | symbol | bigint | Date | FileList | File;
66
+ type PrimitivesAndNativeObjects = null | undefined | string | number | boolean | symbol | bigint | Date | FileList | File | Blob;
89
67
  /** Tuple type, it has to have at least property "0" */
90
68
  type Tuple = ReadonlyArray<any> & {
91
- "0": any;
69
+ "0": any;
92
70
  };
93
71
  /** Array indexes type. */
94
72
  type LiteralIndex = typeof LITERAL_INDEX;
95
73
  type ExtendedArrayIndexes = number | `${number}` | LiteralIndex;
96
- /** Object Literal */
74
+ /** Object Literal, File and Blob still match it (they have no symbol to reject them by) */
97
75
  type ObjectLiteral = {
98
- [x: string]: any;
99
- [x: number]: any;
100
- [x: symbol]: any;
101
- [Symbol.iterator]?: never;
102
- [Symbol.toPrimitive]?: never;
103
- [Symbol.hasInstance]?: never;
104
- [Symbol.unscopables]?: never;
105
- [Symbol.matchAll]?: never;
76
+ [x: string]: any;
77
+ [x: number]: any;
78
+ [x: symbol]: any;
79
+ [Symbol.iterator]?: never;
80
+ [Symbol.toPrimitive]?: never;
81
+ [Symbol.hasInstance]?: never;
82
+ [Symbol.unscopables]?: never;
83
+ [Symbol.matchAll]?: never;
84
+ [Symbol.toStringTag]?: never;
106
85
  };
107
86
  /** Strict key */
108
87
  declare const strictSymbol: unique symbol;
109
- type Strict<T> = T extends ObjectLiteral ? T & {
110
- [strictSymbol]: DotPaths<T>;
88
+ type Strict<T> = T extends PrimitivesAndNativeObjects ? T : T extends ObjectLiteral ? T & {
89
+ [strictSymbol]: DotPaths<T>;
111
90
  } : T;
112
91
  /** Smart KeyOf (doesnt include the "strict" key) */
113
- type KeyOf<T, TAllowedValuesInKeys = any> = T extends ObjectLiteral ? T extends Strict<infer U> ? KeysOfObject<U, TAllowedValuesInKeys> : KeysOfObject<T, TAllowedValuesInKeys> : T extends Tuple ? KeysOfObject<Omit<T, keyof any[]>, TAllowedValuesInKeys> : T extends ReadonlyArray<infer U> ? U extends TAllowedValuesInKeys ? ExtendedArrayIndexes : never : never;
114
- type KeysOfObject<T, TAllowedValuesInKeys> = {
115
- [Key in keyof T]: T[Key] extends TAllowedValuesInKeys ? Key : never;
116
- }[keyof T];
117
-
92
+ type KeyOf<T, TAllowedValuesInKeys = any> = T extends PrimitivesAndNativeObjects ? never : T extends ObjectLiteral ? T extends Strict<infer U> ? KeysOfObject<U, TAllowedValuesInKeys> : KeysOfObject<T, TAllowedValuesInKeys> : T extends Tuple ? KeysOfObject<Omit<T, keyof any[]>, TAllowedValuesInKeys> : T extends ReadonlyArray<infer U> ? U extends TAllowedValuesInKeys ? ExtendedArrayIndexes : never : never;
93
+ type KeysOfObject<T, TAllowedValuesInKeys> = { [Key in keyof T]-?: Required<T>[Key] extends TAllowedValuesInKeys ? Key : never; }[keyof T];
118
94
  /** Returns only the optional keys of an object. */
119
- type OptionalKeys<T> = {
120
- [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
121
- }[keyof T];
95
+ type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never; }[keyof T];
122
96
  /** Removes properties or indexes, order doesn't matter. */
123
97
  type Remove = <T extends ObjectLiteral | any[]>(value: T, keys: T extends any[] ? (number | `${number}` | (number | `${number}`)[]) : OptionalKeys<T> | (OptionalKeys<T>)[]) => T;
124
98
  /**
@@ -127,11 +101,10 @@ type Remove = <T extends ObjectLiteral | any[]>(value: T, keys: T extends any[]
127
101
  * @param keys Keys to remove.
128
102
  * @returns New object/array without the provided keys/indexes.
129
103
  */
130
- declare const remove: Remove;
131
-
104
+ export declare const remove: Remove;
132
105
  type Select = {
133
- <T, TPath extends DotPaths<T>>(state: T, path: TPath): ReturnedValueInPath<T, TPath>;
134
- <T, TPath extends ''>(state: T, path: TPath): T;
106
+ <T, TPath extends DotPaths<T>>(state: T, path: TPath): ReturnedValueInPath<T, TPath>;
107
+ <T, TPath extends ''>(state: T, path: TPath): T;
135
108
  };
136
109
  /**
137
110
  * Returns the value from a given dot path of an object/array or undefined if doesn't exist.
@@ -139,8 +112,7 @@ type Select = {
139
112
  * @param path Path indicating the value to extract.
140
113
  * @returns Value of the provided path or undefined.
141
114
  */
142
- declare const select: Select;
143
-
115
+ export declare const select: Select;
144
116
  type PathWithoutLastKey = string;
145
117
  type LastKey = string;
146
118
  type SplitDotPathAtLastKey = <T extends string>(dotPath: T) => [PathWithoutLastKey, LastKey];
@@ -149,21 +121,19 @@ type SplitDotPathAtLastKey = <T extends string>(dotPath: T) => [PathWithoutLastK
149
121
  * @param dotPath A dot path.
150
122
  * @returns [pathWithoutLastKey, lastKey] or ['', lastKey].
151
123
  */
152
- declare const splitPathAtLastKey: SplitDotPathAtLastKey;
153
-
124
+ export declare const splitPathAtLastKey: SplitDotPathAtLastKey;
154
125
  type Set = <T>(newValue: T) => (v: unknown) => T;
155
126
  /**
156
127
  * Allows to set strongly typed values (removing inacurate "undefined" inference) in nested values inside arrays.
157
128
  * @param newValue New value.
158
129
  * @returns The same value.
159
130
  */
160
- declare const set: Set;
161
-
131
+ export declare const set: Set;
162
132
  type OptionValues = 'error' | 'skip' | 'add';
163
133
  type Options = {
164
- onNewProps?: OptionValues;
165
- onNewIndexes?: OptionValues;
166
- onPathsIntersectedByPrimitiveValues?: OptionValues;
134
+ onNewProps?: OptionValues;
135
+ onNewIndexes?: OptionValues;
136
+ onPathsIntersectedByPrimitiveValues?: OptionValues;
167
137
  };
168
138
  type ImmutableUpdate = <TObject, TAllowedDotPathKeys extends DotPaths<TObject> = DotPaths<TObject>>(state: TObject, dotPathUpdateObject: UpdateObject<TObject, TAllowedDotPathKeys>, options?: Options) => TObject;
169
139
  /**
@@ -175,7 +145,7 @@ type ImmutableUpdate = <TObject, TAllowedDotPathKeys extends DotPaths<TObject> =
175
145
  * @param options Options to change JS behaviour on non-existent fields.
176
146
  * @returns New Updated Object.
177
147
  */
178
- declare const update: ImmutableUpdate;
148
+ export declare const update: ImmutableUpdate;
179
149
  type ExtendableImmutableUpdate = <TObject, TUpdateObject extends UpdateObject<TObject> = UpdateObject<TObject>>(state: TObject, dotPathUpdateObject: TUpdateObject) => ExtendedUpdate<TObject, TUpdateObject>;
180
150
  /**
181
151
  * Creates a new object applying immutable updates using dot-path syntax.
@@ -185,26 +155,21 @@ type ExtendableImmutableUpdate = <TObject, TUpdateObject extends UpdateObject<TO
185
155
  * @param dotPathUpdateObject Dot Path Update Object.
186
156
  * @returns New Updated Object.
187
157
  */
188
- declare const experimental_extendableUpdate: ExtendableImmutableUpdate;
189
-
158
+ export declare const experimental_extendableUpdate: ExtendableImmutableUpdate;
190
159
  type ForEachEntry = <T extends ObjectLiteral>(object: T, callbackfn: (key: keyof T, value: T[keyof T]) => void) => void;
191
- declare const forEachEntry: ForEachEntry;
192
-
160
+ export declare const forEachEntry: ForEachEntry;
193
161
  /** Checks if any provided value is an object literal or not */
194
162
  type IsObjectLiteral = (valueToTest: unknown) => valueToTest is ObjectLiteral;
195
- declare const isObjectLiteral: IsObjectLiteral;
196
-
163
+ export declare const isObjectLiteral: IsObjectLiteral;
197
164
  /** Adds strict property to an object literal */
198
165
  type StrictFn = <T>(value: T) => Strict<T>;
199
- declare const strict: StrictFn;
200
- declare const s: StrictFn;
166
+ export declare const strict: StrictFn;
167
+ export declare const s: StrictFn;
201
168
  /** Checks the strictness of a type passed to a function or callback without 3rd party implementation */
202
- type StrictValueWrapper = <T extends U, U>(v: T extends U ? DotPaths<T> extends DotPaths<U> ? T : Exclude<DotPaths<T>, DotPaths<U>> : never) => U;
203
- declare const strictValueWrapper: StrictValueWrapper;
204
-
169
+ type StrictType = <T extends U, U>(v: T extends U ? DotPaths<T> extends DotPaths<U> ? T : Exclude<DotPaths<T>, DotPaths<U>> : never) => U;
170
+ export declare const strictType: StrictType;
205
171
  type ParseStringIndex = (stringKey: string) => number | null;
206
- declare const parseStringIndex: ParseStringIndex;
172
+ export declare const parseStringIndex: ParseStringIndex;
207
173
  type IsStringIndex = (stringKey: string) => stringKey is `${number}`;
208
- declare const isStringIndex: IsStringIndex;
209
-
210
- export { type DotPaths, type ExtendedArrayIndexes, type KeyOf, type LiteralIndex, type ObjectLiteral, type PrimitivesAndNativeObjects, type ReturnedValueInPath, type Strict, type Tuple, type UpdateObject, type UpdateValue, type ValueInPath, experimental_extendableUpdate, forEachEntry, isObjectLiteral, isStringIndex, parseStringIndex, remove, s, select, set, splitPathAtLastKey, strict, strictValueWrapper, update };
174
+ export declare const isStringIndex: IsStringIndex;
175
+ export type { DotPaths, ExtendedArrayIndexes, KeyOf, LiteralIndex, ObjectLiteral, PrimitivesAndNativeObjects, ReturnedValueInPath, Strict, Tuple, UpdateObject, UpdateValue, ValueInPath };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var m=Object.defineProperty;var R=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var W=Object.prototype.hasOwnProperty;var N=(e,t)=>{for(var r in t)m(e,r,{get:t[r],enumerable:!0})},F=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of C(t))!W.call(e,s)&&s!==r&&m(e,s,{get:()=>t[s],enumerable:!(n=R(t,s))||n.enumerable});return e};var B=e=>F(m({},"__esModule",{value:!0}),e);var J={};N(J,{experimental_extendableUpdate:()=>A,forEachEntry:()=>v,isObjectLiteral:()=>l,isStringIndex:()=>y,parseStringIndex:()=>E,remove:()=>L,s:()=>V,select:()=>P,set:()=>w,splitPathAtLastKey:()=>g,strict:()=>T,strictValueWrapper:()=>D,update:()=>S});module.exports=B(J);var l=e=>!!e&&Object.getPrototypeOf(e)===Object.prototype,q=Symbol();var H={[q]:[""]};var L=(e,t)=>{if(!Array.isArray(e)&&!l(e))throw'Wrong type provided in "remove" function';let r=Array.isArray(t)?t:[t];if(Array.isArray(e)){let s=Symbol(),o=[...e];return r.forEach(i=>{o[i]=s}),o.filter(i=>i!==s)}let n={...e};return r.forEach(s=>delete n[s]),n};var P=(e,t)=>{if(typeof t!="string")throw new Error("path is not a string");if(!t)return e;let r=t.split("."),n=e;for(;r.length;){let s=r[0],o=r.join(".");if(Object.hasOwn(n,o))return n[o];if(!Object.hasOwn(n,s))return;n=n[s],r.shift()}return n};var g=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 E=e=>{let t=parseInt(e);return t.toString()===e?t:null},y=e=>parseInt(e).toString()===e;var K=e=>JSON.stringify(e),f=e=>Array.isArray(e),U=(e,t,r={})=>{if(!l(e)&&!f(e))throw new Error(`"${K(e)}" is not an literalObject/array`);if(!l(t))throw new Error('wrong "updateObject" type provided');let n=Array.isArray(e)?[...e]:{...e},s={};for(let[o,x]of Object.entries(t)){if(o==="")continue;let i=o.split("."),d=s,p=n;for(;i.length>1;){let O=i.join(".");if(Object.hasOwn(p,O)){i=[O];break}let a=i[0],b=p[a],h=l(b),j=f(b);if(!h&&!j&&!(b===void 0)){if(r.onPathsIntersectedByPrimitiveValues==="error")throw new Error(`Property "${a}" in "${o}" is a primitive`);if(r.onPathsIntersectedByPrimitiveValues==="skip")continue}if(!d[a]){if(!h&&!j){let k=f(p)&&y(a),$=y(i[1]),I=k?r.onNewIndexes:r.onNewProps;if(I==="error")throw new Error(`${a} doesn't exist in "${o}"`);if(I==="skip")continue;p[a]=$?[]:{}}else p[a]=j?[...b]:{...b};d[a]={}}p=p[a],d=d[a],i.shift()}let c=i[0],u=p;if(!Object.hasOwn(u,c)){let a=f(u)&&y(c)?r.onNewIndexes:r.onNewProps;if(a==="error")throw new Error(`Property/Index "${c}" doesnt exist in the path "${o}". Its value is: "${K(u)}"`);if(a==="skip")continue}u[c]=typeof x=="function"?x(u[c]):x,delete d[c]}return n},S=(e,t,r)=>U(e,t,r),A=(e,t)=>U(e,t);var v=(e,t)=>Object.keys(e).forEach(r=>t(r,e[r]));var T=e=>e,V=T,D=e=>e;0&&(module.exports={experimental_extendableUpdate,forEachEntry,isObjectLiteral,isStringIndex,parseStringIndex,remove,s,select,set,splitPathAtLastKey,strict,strictValueWrapper,update});
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=e=>!!e&&Object.getPrototypeOf(e)===Object.prototype,t=(t,n)=>{if(!Array.isArray(t)&&!e(t))throw`Wrong type provided in "remove" function`;let r=Array.isArray(n)?n:[n];if(Array.isArray(t)){let e=Symbol(),n=[...t];return r.forEach(t=>{n[t]=e}),n.filter(t=>t!==e)}let i={...t};return r.forEach(e=>delete i[e]),i},n=(e,t)=>{if(typeof t!=`string`)throw Error(`path is not a string`);if(!t)return e;let n=t.split(`.`),r=e;for(;n.length;){if(r==null)return;let e=n[0],t=n.join(`.`);if(Object.hasOwn(r,t))return r[t];if(!Object.hasOwn(r,e))return;r=r[e],n.shift()}return r},r=e=>{if(typeof e!=`string`)throw Error(`Wrong dotPath type: ${e}`);let t=e.lastIndexOf(`.`)+1;return[t?e.slice(0,t-1):``,e.slice(t)]},i=e=>()=>e,a=e=>{let t=parseInt(e);return e!==`NaN`&&t.toString()===e?t:null},o=e=>e!==`NaN`&&parseInt(e).toString()===e,s=e=>JSON.stringify(e),c=e=>Array.isArray(e),l=(t,n,r={})=>{if(!e(t)&&!c(t))throw Error(`"${s(t)}" is not an literalObject/array`);if(!e(n))throw Error(`wrong "updateObject" type provided`);let i=Array.isArray(t)?[...t]:{...t},a=Object.create(null),l=Object.entries(n),u=l.length===1;nextUpdate:for(let[t,n]of l){if(t===``)continue;let l=t.split(`.`),d=a;if(l.includes(`__proto__`))throw Error(`"__proto__" is not allowed in "${t}"`);let f=i,p=l[l.length-1],m=0;for(let n=0;n<l.length-1;n++){let i=t.slice(m);if(Object.hasOwn(f,i)){p=i;break}let a=l[n],s=Object.hasOwn(f,a)?f[a]:void 0;m+=a.length+1;let h=d[a];if(h){f=s,d=h;continue}let g=e(s),_=c(s);if(!g&&!_&&s!==void 0){if(r.onPathsIntersectedByPrimitiveValues===`error`)throw Error(`Property "${a}" in "${t}" is a primitive`);if(r.onPathsIntersectedByPrimitiveValues===`skip`)continue nextUpdate}if(!g&&!_){let e=c(f)&&o(a),i=o(l[n+1]),s=e?r.onNewIndexes:r.onNewProps;if(s===`error`)throw Error(`${a} doesn't exist in "${t}"`);if(s===`skip`)continue nextUpdate;f[a]=i?[]:{}}else f[a]=_?[...s]:{...s};f=f[a],!u&&(d[a]=Object.create(null),d=d[a])}let h=f,g=!Object.hasOwn(h,p);if(g){let e=c(h)&&o(p)?r.onNewIndexes:r.onNewProps;if(e===`error`)throw Error(`Property/Index "${p}" doesnt exist in the path "${t}". Its value is: "${s(h)}"`);if(e===`skip`)continue}let _=g?void 0:h[p];h[p]=typeof n==`function`?n(_):n,delete d[p]}return i},u=(e,t,n)=>l(e,t,n),d=(e,t)=>l(e,t),f=(e,t)=>Object.keys(e).forEach(n=>t(n,e[n])),p=e=>e,m=p,h=e=>e;exports.experimental_extendableUpdate=d,exports.forEachEntry=f,exports.isObjectLiteral=e,exports.isStringIndex=o,exports.parseStringIndex=a,exports.remove=t,exports.s=m,exports.select=n,exports.set=i,exports.splitPathAtLastKey=r,exports.strict=p,exports.strictType=h,exports.update=u;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var l=e=>!!e&&Object.getPrototypeOf(e)===Object.prototype,w=Symbol();var C={[w]:[""]};var E=(e,t)=>{if(!Array.isArray(e)&&!l(e))throw'Wrong type provided in "remove" function';let r=Array.isArray(t)?t:[t];if(Array.isArray(e)){let i=Symbol(),a=[...e];return r.forEach(o=>{a[o]=i}),a.filter(o=>o!==i)}let n={...e};return r.forEach(i=>delete n[i]),n};var K=(e,t)=>{if(typeof t!="string")throw new Error("path is not a string");if(!t)return e;let r=t.split("."),n=e;for(;r.length;){let i=r[0],a=r.join(".");if(Object.hasOwn(n,a))return n[a];if(!Object.hasOwn(n,i))return;n=n[i],r.shift()}return n};var U=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 S=e=>()=>e;var A=e=>{let t=parseInt(e);return t.toString()===e?t:null},b=e=>parseInt(e).toString()===e;var h=e=>JSON.stringify(e),f=e=>Array.isArray(e),I=(e,t,r={})=>{if(!l(e)&&!f(e))throw new Error(`"${h(e)}" is not an literalObject/array`);if(!l(t))throw new Error('wrong "updateObject" type provided');let n=Array.isArray(e)?[...e]:{...e},i={};for(let[a,x]of Object.entries(t)){if(a==="")continue;let o=a.split("."),y=i,p=n;for(;o.length>1;){let O=o.join(".");if(Object.hasOwn(p,O)){o=[O];break}let s=o[0],u=p[s],m=l(u),j=f(u);if(!m&&!j&&!(u===void 0)){if(r.onPathsIntersectedByPrimitiveValues==="error")throw new Error(`Property "${s}" in "${a}" is a primitive`);if(r.onPathsIntersectedByPrimitiveValues==="skip")continue}if(!y[s]){if(!m&&!j){let P=f(p)&&b(s),g=b(o[1]),T=P?r.onNewIndexes:r.onNewProps;if(T==="error")throw new Error(`${s} doesn't exist in "${a}"`);if(T==="skip")continue;p[s]=g?[]:{}}else p[s]=j?[...u]:{...u};y[s]={}}p=p[s],y=y[s],o.shift()}let c=o[0],d=p;if(!Object.hasOwn(d,c)){let s=f(d)&&b(c)?r.onNewIndexes:r.onNewProps;if(s==="error")throw new Error(`Property/Index "${c}" doesnt exist in the path "${a}". Its value is: "${h(d)}"`);if(s==="skip")continue}d[c]=typeof x=="function"?x(d[c]):x,delete y[c]}return n},v=(e,t,r)=>I(e,t,r),V=(e,t)=>I(e,t);var D=(e,t)=>Object.keys(e).forEach(r=>t(r,e[r]));var L=e=>e,k=L,$=e=>e;export{V as experimental_extendableUpdate,D as forEachEntry,l as isObjectLiteral,b as isStringIndex,A as parseStringIndex,E as remove,k as s,K as select,S as set,U as splitPathAtLastKey,L as strict,$ as strictValueWrapper,v as update};
1
+ const e=e=>!!e&&Object.getPrototypeOf(e)===Object.prototype,t=(t,n)=>{if(!Array.isArray(t)&&!e(t))throw`Wrong type provided in "remove" function`;let r=Array.isArray(n)?n:[n];if(Array.isArray(t)){let e=Symbol(),n=[...t];return r.forEach(t=>{n[t]=e}),n.filter(t=>t!==e)}let i={...t};return r.forEach(e=>delete i[e]),i},n=(e,t)=>{if(typeof t!=`string`)throw Error(`path is not a string`);if(!t)return e;let n=t.split(`.`),r=e;for(;n.length;){if(r==null)return;let e=n[0],t=n.join(`.`);if(Object.hasOwn(r,t))return r[t];if(!Object.hasOwn(r,e))return;r=r[e],n.shift()}return r},r=e=>{if(typeof e!=`string`)throw Error(`Wrong dotPath type: ${e}`);let t=e.lastIndexOf(`.`)+1;return[t?e.slice(0,t-1):``,e.slice(t)]},i=e=>()=>e,a=e=>{let t=parseInt(e);return e!==`NaN`&&t.toString()===e?t:null},o=e=>e!==`NaN`&&parseInt(e).toString()===e,s=e=>JSON.stringify(e),c=e=>Array.isArray(e),l=(t,n,r={})=>{if(!e(t)&&!c(t))throw Error(`"${s(t)}" is not an literalObject/array`);if(!e(n))throw Error(`wrong "updateObject" type provided`);let i=Array.isArray(t)?[...t]:{...t},a=Object.create(null),l=Object.entries(n),u=l.length===1;nextUpdate:for(let[t,n]of l){if(t===``)continue;let l=t.split(`.`),d=a;if(l.includes(`__proto__`))throw Error(`"__proto__" is not allowed in "${t}"`);let f=i,p=l[l.length-1],m=0;for(let n=0;n<l.length-1;n++){let i=t.slice(m);if(Object.hasOwn(f,i)){p=i;break}let a=l[n],s=Object.hasOwn(f,a)?f[a]:void 0;m+=a.length+1;let h=d[a];if(h){f=s,d=h;continue}let g=e(s),_=c(s);if(!g&&!_&&s!==void 0){if(r.onPathsIntersectedByPrimitiveValues===`error`)throw Error(`Property "${a}" in "${t}" is a primitive`);if(r.onPathsIntersectedByPrimitiveValues===`skip`)continue nextUpdate}if(!g&&!_){let e=c(f)&&o(a),i=o(l[n+1]),s=e?r.onNewIndexes:r.onNewProps;if(s===`error`)throw Error(`${a} doesn't exist in "${t}"`);if(s===`skip`)continue nextUpdate;f[a]=i?[]:{}}else f[a]=_?[...s]:{...s};f=f[a],!u&&(d[a]=Object.create(null),d=d[a])}let h=f,g=!Object.hasOwn(h,p);if(g){let e=c(h)&&o(p)?r.onNewIndexes:r.onNewProps;if(e===`error`)throw Error(`Property/Index "${p}" doesnt exist in the path "${t}". Its value is: "${s(h)}"`);if(e===`skip`)continue}let _=g?void 0:h[p];h[p]=typeof n==`function`?n(_):n,delete d[p]}return i},u=(e,t,n)=>l(e,t,n),d=(e,t)=>l(e,t),f=(e,t)=>Object.keys(e).forEach(n=>t(n,e[n])),p=e=>e,m=p,h=e=>e;export{d as experimental_extendableUpdate,f as forEachEntry,e as isObjectLiteral,o as isStringIndex,a as parseStringIndex,t as remove,m as s,n as select,i as set,r as splitPathAtLastKey,p as strict,h as strictType,u as update};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "maraj",
3
- "version": "2.10.0",
4
- "description": "A lightweight 600 bytes gzipped alternative for immutable updates while dealing with objects and arrays",
3
+ "version": "3.0.0",
4
+ "description": "A lightweight (~1.2 KB gzipped) alternative for immutable updates while dealing with objects and arrays",
5
5
  "keywords": [
6
6
  "shallow",
7
7
  "shallow copy",
@@ -21,11 +21,11 @@
21
21
  "scripts": {
22
22
  "dev": "vitest",
23
23
  "test": "vitest run",
24
- "build": "tsup src/index.ts --minify --format cjs,esm --dts",
24
+ "build": "tsdown",
25
25
  "lint": "tsc",
26
26
  "trace": "tsc --generateTrace trace",
27
27
  "ci": "npm run lint && npm run test && npm run build",
28
- "release": "npm run lint && npm run test && npm run build && changeset publish"
28
+ "release": "node scripts/check-changelog.mjs && npm run build && changeset publish"
29
29
  },
30
30
  "repository": {
31
31
  "type": "git",
@@ -36,11 +36,11 @@
36
36
  },
37
37
  "homepage": "https://github.com/Lukasz17git/maraj#readme",
38
38
  "devDependencies": {
39
- "@changesets/cli": "^2.27.1",
40
- "@microsoft/api-extractor": "^7.38.5",
41
- "@typescript/analyze-trace": "^0.10.1",
42
- "tsup": "^8.0.1",
43
- "typescript": "^5.3.3",
44
- "vitest": "^0.28.4"
39
+ "@changesets/cli": "^3.0.3",
40
+ "@microsoft/api-extractor": "^7.59.4",
41
+ "@typescript/analyze-trace": "^0.11.1",
42
+ "tsdown": "^0.23.1",
43
+ "typescript": "^7.0.2",
44
+ "vitest": "^5.0.5"
45
45
  }
46
46
  }