objwalk 1.4.0 → 1.5.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 +37 -4
- package/dist/index.cjs +23 -0
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +22 -0
- package/package.json +1 -1
- package/skills/objwalk/SKILL.md +13 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Small utilities for reading, writing, walking, and reshaping nested JavaScript objects.
|
|
4
4
|
|
|
5
|
-
`objwalk` is built around a compact path syntax, so the same mental model works for picking
|
|
5
|
+
`objwalk` is built around a compact path syntax, so the same mental model works for picking, setting, and deleting values, extracting a new object shape, and walking through nested data.
|
|
6
6
|
|
|
7
7
|
> Coding agent? Load the [objwalk skill](skills/objwalk/SKILL.md).
|
|
8
8
|
|
|
@@ -15,7 +15,7 @@ npm install objwalk
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
|
-
import { each, equally, extract, map, merge, parse, pick, set, stringify } from 'objwalk'
|
|
18
|
+
import { each, equally, extract, flush, map, merge, parse, pick, set, stringify } from 'objwalk'
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## pick
|
|
@@ -96,6 +96,38 @@ set(data, 'items[0].title', 'First')
|
|
|
96
96
|
|
|
97
97
|
`set` mutates the object passed to it and returns the same object.
|
|
98
98
|
|
|
99
|
+
## flush
|
|
100
|
+
|
|
101
|
+
Delete a property by path using native `delete` semantics.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
const data = { user: { name: 'Ada', role: 'admin' } }
|
|
105
|
+
|
|
106
|
+
flush(data, 'user.name')
|
|
107
|
+
// { user: { role: 'admin' } }
|
|
108
|
+
|
|
109
|
+
flush({}, 'path.to.key') // {}
|
|
110
|
+
flush({ user: null }, 'user.name') // { user: null }
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`flush(data, path)` mutates and returns the same object. Missing paths, `null`,
|
|
114
|
+
`undefined`, and primitive parents are left unchanged. It does not create intermediate
|
|
115
|
+
containers or remove empty parents. An empty path leaves the object unchanged.
|
|
116
|
+
Only own properties are traversed and deleted; inherited properties are left unchanged.
|
|
117
|
+
|
|
118
|
+
Array indexes use the same explicit syntax as `pick` and `set`:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const data = { items: [{ title: 'First' }, { title: 'Second' }] }
|
|
122
|
+
|
|
123
|
+
flush(data, 'items[0].title') // { items: [{}, { title: 'Second' }] }
|
|
124
|
+
flush(data, 'items[1]') // { items: [{}, <empty item>] }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Deleting an array item leaves a hole, preserves its length, and does not shift later items.
|
|
128
|
+
Selection syntax (`{name,email}`, `{*}`) and `[n]` mappings are not supported by `flush`.
|
|
129
|
+
Errors from getters, proxies, or deleting non-configurable properties propagate to the caller.
|
|
130
|
+
|
|
99
131
|
## equally
|
|
100
132
|
|
|
101
133
|
Deeply compare two values for structural equality.
|
|
@@ -304,8 +336,8 @@ The wrappers do not mutate inputs, undo callback side effects, log errors, or re
|
|
|
304
336
|
|
|
305
337
|
| Syntax | Description |
|
|
306
338
|
| --- | --- |
|
|
307
|
-
| `user.name` | Read or
|
|
308
|
-
| `items[0].title` | Read or
|
|
339
|
+
| `user.name` | Read, write, or delete a nested property. |
|
|
340
|
+
| `items[0].title` | Read, write, or delete an array item's property by index. |
|
|
309
341
|
| `user.{name,email}` | Pick several keys from one object. |
|
|
310
342
|
| `user.{*}` | List keys from one object. |
|
|
311
343
|
| `items[n].title` | Map every item in an array during `extract`. |
|
|
@@ -324,6 +356,7 @@ them without redundant wrappers that only repeat object checks.
|
|
|
324
356
|
```ts
|
|
325
357
|
pick(data, path)
|
|
326
358
|
set(data, path, value)
|
|
359
|
+
flush(data, path)
|
|
327
360
|
extract(data, mapping)
|
|
328
361
|
each(data, callback)
|
|
329
362
|
map(data, callback)
|
package/dist/index.cjs
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
each: () => each,
|
|
25
25
|
equally: () => equally,
|
|
26
26
|
extract: () => extract,
|
|
27
|
+
flush: () => flush,
|
|
27
28
|
map: () => map,
|
|
28
29
|
merge: () => merge,
|
|
29
30
|
parse: () => parse,
|
|
@@ -274,6 +275,27 @@ var merge = (target, ...sources) => {
|
|
|
274
275
|
return target;
|
|
275
276
|
};
|
|
276
277
|
|
|
278
|
+
// src/flush.ts
|
|
279
|
+
var flush = (data = {}, path) => {
|
|
280
|
+
if (path.length > 0) {
|
|
281
|
+
const keys = path.split(".");
|
|
282
|
+
const lastKey = keys.pop() ?? "";
|
|
283
|
+
const match = lastKey.match(/^(\w+)\[(\d+)\]$/);
|
|
284
|
+
let parent = getValueByPath(data, keys.join("."), { inherited: false });
|
|
285
|
+
let key = lastKey;
|
|
286
|
+
if (match) {
|
|
287
|
+
const [, arrayKey, index] = match;
|
|
288
|
+
const array = getValueByKey(parent, arrayKey, { inherited: false });
|
|
289
|
+
parent = Array.isArray(array) ? array : void 0;
|
|
290
|
+
key = index;
|
|
291
|
+
}
|
|
292
|
+
if (parent !== null && typeof parent === "object" && Object.hasOwn(parent, key)) {
|
|
293
|
+
delete parent[key];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return data;
|
|
297
|
+
};
|
|
298
|
+
|
|
277
299
|
// src/deepEqual.ts
|
|
278
300
|
var deepEqual = (x, y, store) => {
|
|
279
301
|
if (Number.isNaN(x) && Number.isNaN(y)) {
|
|
@@ -404,6 +426,7 @@ var stringify = (value, { fallback, onError, replacer, space } = {}) => {
|
|
|
404
426
|
each,
|
|
405
427
|
equally,
|
|
406
428
|
extract,
|
|
429
|
+
flush,
|
|
407
430
|
map,
|
|
408
431
|
merge,
|
|
409
432
|
parse,
|
package/dist/index.d.cts
CHANGED
|
@@ -32,6 +32,8 @@ declare const merge: <T extends ObjwalkObject$1>(target: T, ...sources: unknown[
|
|
|
32
32
|
type ObjwalkObject = Record<string, any>;
|
|
33
33
|
declare const set: (data: ObjwalkObject | undefined, path: string, value: any) => ObjwalkObject;
|
|
34
34
|
|
|
35
|
+
declare const flush: (data: ObjwalkObject$1 | undefined, path: string) => ObjwalkObject$1;
|
|
36
|
+
|
|
35
37
|
declare const equally: (a: unknown, b: unknown) => boolean;
|
|
36
38
|
|
|
37
39
|
declare const clone: <T>(value: T) => T;
|
|
@@ -40,4 +42,4 @@ declare const parse: (raw: unknown, { fallback, onError, reviver }?: ParseOption
|
|
|
40
42
|
|
|
41
43
|
declare const stringify: (value: unknown, { fallback, onError, replacer, space }?: StringifyOptions) => string | undefined;
|
|
42
44
|
|
|
43
|
-
export { type EachCallback, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, each, equally, extract, map, merge, parse, pick, set, stringify };
|
|
45
|
+
export { type EachCallback, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
|
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ declare const merge: <T extends ObjwalkObject$1>(target: T, ...sources: unknown[
|
|
|
32
32
|
type ObjwalkObject = Record<string, any>;
|
|
33
33
|
declare const set: (data: ObjwalkObject | undefined, path: string, value: any) => ObjwalkObject;
|
|
34
34
|
|
|
35
|
+
declare const flush: (data: ObjwalkObject$1 | undefined, path: string) => ObjwalkObject$1;
|
|
36
|
+
|
|
35
37
|
declare const equally: (a: unknown, b: unknown) => boolean;
|
|
36
38
|
|
|
37
39
|
declare const clone: <T>(value: T) => T;
|
|
@@ -40,4 +42,4 @@ declare const parse: (raw: unknown, { fallback, onError, reviver }?: ParseOption
|
|
|
40
42
|
|
|
41
43
|
declare const stringify: (value: unknown, { fallback, onError, replacer, space }?: StringifyOptions) => string | undefined;
|
|
42
44
|
|
|
43
|
-
export { type EachCallback, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, each, equally, extract, map, merge, parse, pick, set, stringify };
|
|
45
|
+
export { type EachCallback, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
|
package/dist/index.js
CHANGED
|
@@ -239,6 +239,27 @@ var merge = (target, ...sources) => {
|
|
|
239
239
|
return target;
|
|
240
240
|
};
|
|
241
241
|
|
|
242
|
+
// src/flush.ts
|
|
243
|
+
var flush = (data = {}, path) => {
|
|
244
|
+
if (path.length > 0) {
|
|
245
|
+
const keys = path.split(".");
|
|
246
|
+
const lastKey = keys.pop() ?? "";
|
|
247
|
+
const match = lastKey.match(/^(\w+)\[(\d+)\]$/);
|
|
248
|
+
let parent = getValueByPath(data, keys.join("."), { inherited: false });
|
|
249
|
+
let key = lastKey;
|
|
250
|
+
if (match) {
|
|
251
|
+
const [, arrayKey, index] = match;
|
|
252
|
+
const array = getValueByKey(parent, arrayKey, { inherited: false });
|
|
253
|
+
parent = Array.isArray(array) ? array : void 0;
|
|
254
|
+
key = index;
|
|
255
|
+
}
|
|
256
|
+
if (parent !== null && typeof parent === "object" && Object.hasOwn(parent, key)) {
|
|
257
|
+
delete parent[key];
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return data;
|
|
261
|
+
};
|
|
262
|
+
|
|
242
263
|
// src/deepEqual.ts
|
|
243
264
|
var deepEqual = (x, y, store) => {
|
|
244
265
|
if (Number.isNaN(x) && Number.isNaN(y)) {
|
|
@@ -368,6 +389,7 @@ export {
|
|
|
368
389
|
each,
|
|
369
390
|
equally,
|
|
370
391
|
extract,
|
|
392
|
+
flush,
|
|
371
393
|
map,
|
|
372
394
|
merge,
|
|
373
395
|
parse,
|
package/package.json
CHANGED
package/skills/objwalk/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: objwalk
|
|
3
|
-
description: Use when writing or editing JavaScript or TypeScript that imports `objwalk`, reads or
|
|
3
|
+
description: Use when writing or editing JavaScript or TypeScript that imports `objwalk`, reads, writes, or deletes nested object paths, reshapes or walks objects, or uses objwalk JSON helpers.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# objwalk
|
|
@@ -19,7 +19,7 @@ them with type assertions or assume every method accepts arbitrary `unknown`.
|
|
|
19
19
|
Named imports only:
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
|
-
import { clone, each, equally, extract, map, merge, parse, pick, set, stringify } from 'objwalk'
|
|
22
|
+
import { clone, each, equally, extract, flush, map, merge, parse, pick, set, stringify } from 'objwalk'
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Choose the existing operation
|
|
@@ -28,6 +28,7 @@ import { clone, each, equally, extract, map, merge, parse, pick, set, stringify
|
|
|
28
28
|
| --- | --- |
|
|
29
29
|
| Read a nested value | `pick(data, path, options?)`; unresolved paths return `undefined`. |
|
|
30
30
|
| Write a nested value | `set(data, path, value)`; creates intermediate containers, mutates and returns `data`. |
|
|
31
|
+
| Delete a nested property | `flush(data, path)`; deletes an own property, mutates and returns `data`; missing paths are left unchanged. |
|
|
31
32
|
| Reshape an object | `extract(source, mapping)`; mapping keys are destination paths, values are source paths. |
|
|
32
33
|
| Visit nested values | `each(data, (key, value, path) => { ... })`; visits containers and leaves, excluding the root. |
|
|
33
34
|
| Build a store while walking | `map(data, (key, value, store, path) => { ... }, store?)`; returns the store, ignores callback return values. |
|
|
@@ -35,7 +36,7 @@ import { clone, each, equally, extract, map, merge, parse, pick, set, stringify
|
|
|
35
36
|
| Deep copy | `clone(value)`; preserves `Date`, `RegExp`, symbol keys, and circular references. |
|
|
36
37
|
| Deep comparison | `equally(a, b)`; supports `NaN`, `Date`, `RegExp`, and circular references. |
|
|
37
38
|
|
|
38
|
-
`set` and `merge` mutate their targets. Use `clone` first only when the task
|
|
39
|
+
`set`, `flush`, and `merge` mutate their targets. Use `clone` first only when the task
|
|
39
40
|
requires an independent copy. `each` and `map` do not support circular traversal;
|
|
40
41
|
do not infer that capability from `clone` or `equally`.
|
|
41
42
|
|
|
@@ -49,10 +50,18 @@ do not infer that capability from `clone` or `equally`.
|
|
|
49
50
|
| `user.{*}` | List object keys with `pick`. |
|
|
50
51
|
| `items[n].title` | Map array items in `extract`, with `[n]` in both source and destination paths. |
|
|
51
52
|
|
|
52
|
-
`[n]` is an `extract` mapping feature, not a general wildcard for `pick` or `
|
|
53
|
+
`[n]` is an `extract` mapping feature, not a general wildcard for `pick`, `set`, or `flush`.
|
|
53
54
|
An array of source paths in `extract` selects the first result that is not
|
|
54
55
|
`undefined`, preserving `null`, `false`, `0`, and empty strings.
|
|
55
56
|
|
|
57
|
+
Use `flush` to remove a property rather than `set(data, path, undefined)`, which
|
|
58
|
+
leaves the property present. `flush` traverses and deletes only own properties.
|
|
59
|
+
Missing, nullish, or primitive parents and empty paths are left unchanged; no
|
|
60
|
+
containers are created and empty parents remain. Explicit array indexes work,
|
|
61
|
+
but deleting an item leaves a hole without shifting indexes or changing length.
|
|
62
|
+
Selection syntax (`{name,email}`, `{*}`) is not supported. Errors from getters,
|
|
63
|
+
proxies, or deleting non-configurable properties propagate.
|
|
64
|
+
|
|
56
65
|
`pick` reads inherited properties by default. Use its existing options when own
|
|
57
66
|
properties or ignored keys are required:
|
|
58
67
|
|