maraj 2.11.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 +178 -140
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,211 +1,249 @@
|
|
|
1
|
-
|
|
2
1
|
# Maraj
|
|
3
2
|
|
|
4
|
-
A
|
|
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
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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
|
-
|
|
14
|
+
```sh
|
|
15
|
+
npm install maraj
|
|
16
|
+
```
|
|
16
17
|
|
|
17
18
|
```ts
|
|
18
|
-
|
|
19
|
+
import { update } from 'maraj'
|
|
19
20
|
|
|
20
|
-
const
|
|
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
|
|
25
|
+
'modified': (value) => !value,
|
|
23
26
|
'files.0.filename': 'zero',
|
|
24
27
|
})
|
|
25
|
-
//
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
45
|
+
Returns a new object with the updates; `state` is never changed.
|
|
45
46
|
|
|
46
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
'
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
'
|
|
89
|
-
'
|
|
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
|
-
|
|
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
|
-
|
|
121
|
+
## set
|
|
96
122
|
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
107
|
-
import { update } from
|
|
128
|
+
```ts
|
|
129
|
+
import { update, set } from 'maraj'
|
|
108
130
|
|
|
109
|
-
const
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
##
|
|
127
|
-
|
|
128
|
-
Arguments:
|
|
139
|
+
## remove
|
|
129
140
|
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
136
|
-
import {
|
|
147
|
+
```ts
|
|
148
|
+
import { update, remove } from 'maraj'
|
|
137
149
|
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
##
|
|
159
|
+
## select
|
|
155
160
|
|
|
156
|
-
|
|
161
|
+
Returns the value in a dot path, or `undefined` if the path doesn't exist. `''` returns the whole state.
|
|
157
162
|
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
##
|
|
173
|
+
## splitPathAtLastKey
|
|
170
174
|
|
|
171
|
-
|
|
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
|
-
```
|
|
174
|
-
import {
|
|
177
|
+
```ts
|
|
178
|
+
import { splitPathAtLastKey } from 'maraj'
|
|
175
179
|
|
|
176
|
-
const
|
|
180
|
+
const [remainingPath, lastKey] = splitPathAtLastKey('projects.1.data.size')
|
|
181
|
+
// remainingPath = 'projects.1.data', lastKey = 'size'
|
|
177
182
|
|
|
178
|
-
|
|
183
|
+
splitPathAtLastKey('size') // ['', 'size']
|
|
179
184
|
```
|
|
180
185
|
|
|
181
|
-
##
|
|
186
|
+
## Type helpers
|
|
182
187
|
|
|
183
|
-
|
|
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
|
-
|
|
194
|
+
```ts
|
|
195
|
+
import { strictType } from 'maraj'
|
|
186
196
|
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
type User = { name: string, age?: number }
|
|
198
|
+
const saveUser = (user: User) => { /* ... */ }
|
|
189
199
|
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
205
|
+
## Limitations
|
|
194
206
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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 t.toString()===e?t:null},o=e=>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=
|
|
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
|
-
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 t.toString()===e?t:null},o=e=>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=
|
|
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": "
|
|
4
|
-
"description": "A lightweight
|
|
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",
|