objwalk 1.3.1 → 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 +134 -4
- package/dist/index.cjs +62 -2
- package/dist/index.d.cts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +58 -1
- package/package.json +3 -2
- package/skills/objwalk/SKILL.md +105 -0
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
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
|
+
|
|
7
|
+
> Coding agent? Load the [objwalk skill](skills/objwalk/SKILL.md).
|
|
6
8
|
|
|
7
9
|
## Install
|
|
8
10
|
|
|
@@ -13,7 +15,7 @@ npm install objwalk
|
|
|
13
15
|
## Usage
|
|
14
16
|
|
|
15
17
|
```ts
|
|
16
|
-
import { each, equally, extract, map, merge, pick, set } from 'objwalk'
|
|
18
|
+
import { each, equally, extract, flush, map, merge, parse, pick, set, stringify } from 'objwalk'
|
|
17
19
|
```
|
|
18
20
|
|
|
19
21
|
## pick
|
|
@@ -94,6 +96,38 @@ set(data, 'items[0].title', 'First')
|
|
|
94
96
|
|
|
95
97
|
`set` mutates the object passed to it and returns the same object.
|
|
96
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
|
+
|
|
97
131
|
## equally
|
|
98
132
|
|
|
99
133
|
Deeply compare two values for structural equality.
|
|
@@ -214,24 +248,120 @@ The callback receives:
|
|
|
214
248
|
|
|
215
249
|
`map` returns `store`.
|
|
216
250
|
|
|
251
|
+
## parse and stringify
|
|
252
|
+
|
|
253
|
+
Parse JSON or accept a ready value, and serialize values with optional error handling.
|
|
254
|
+
Exceptions return `undefined`, unless a fallback is provided or `onError` throws.
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
import { parse, stringify, type ParseOptions, type StringifyOptions } from 'objwalk'
|
|
258
|
+
|
|
259
|
+
// Signatures:
|
|
260
|
+
// parse(raw: unknown, options?: ParseOptions): unknown
|
|
261
|
+
// stringify(value: unknown, options?: StringifyOptions): string | undefined
|
|
262
|
+
|
|
263
|
+
const value = parse('{"a":1}') // { a: 1 }, typed as unknown
|
|
264
|
+
const valueOrNull = parse('{', { fallback: null }) // null
|
|
265
|
+
const text = stringify(value, { fallback: 'null', space: 2 })
|
|
266
|
+
const object = { a: 1 }
|
|
267
|
+
const array = [1, 2]
|
|
268
|
+
|
|
269
|
+
parse(object) === object // true
|
|
270
|
+
parse(array) === array // true
|
|
271
|
+
|
|
272
|
+
parse('[]') // []
|
|
273
|
+
stringify({}) // '{}'
|
|
274
|
+
stringify([]) // '[]'
|
|
275
|
+
stringify(null) // 'null'
|
|
276
|
+
stringify('{}') // '"{}"'
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Root values can be objects, arrays, strings, numbers, booleans, or `null`;
|
|
280
|
+
neither function requires an expected shape or restricts values to objects.
|
|
281
|
+
`parse` returns `unknown`; successful parsing does not validate the expected data structure.
|
|
282
|
+
Validate the result before using it as a specific type. There is no `parse<T>()` overload.
|
|
283
|
+
|
|
284
|
+
`parse` accepts `unknown`. Plain objects (with `Object.prototype` or a `null` prototype) and
|
|
285
|
+
arrays are returned by reference, without cloning, serialization, mutation, or calling `reviver`.
|
|
286
|
+
Other inputs use `JSON.parse(typeof raw === 'string' ? raw : String(raw), reviver)`.
|
|
287
|
+
ArrayBuffer, DataView, and typed-array views use this text path; there is no binary decoder.
|
|
288
|
+
For example, a Node.js Buffer containing JSON text can be parsed through its native string conversion.
|
|
289
|
+
Errors during input recognition or string conversion follow the same `onError`/`fallback` contract.
|
|
290
|
+
|
|
291
|
+
| Option | `ParseOptions` | `StringifyOptions` |
|
|
292
|
+
| --- | --- | --- |
|
|
293
|
+
| `fallback` | `unknown`, default `undefined` | `string`, default `undefined` |
|
|
294
|
+
| `onError` | `(error: unknown) => void` | `(error: unknown) => void` |
|
|
295
|
+
| `reviver` | Native `JSON.parse` callback, only for the text path | — |
|
|
296
|
+
| `replacer` | — | Native callback or array of strings/numbers (also accepts `null`) |
|
|
297
|
+
| `space` | — | `string` or `number` for native JSON formatting |
|
|
298
|
+
|
|
299
|
+
Callbacks retain native `this` binding. For example, a reviver can transform or delete properties,
|
|
300
|
+
and a replacer can select or transform values:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
parse('{"count":2,"secret":true}', {
|
|
304
|
+
reviver: (key, value) => key === 'secret' ? undefined : value,
|
|
305
|
+
}) // { count: 2 }
|
|
306
|
+
|
|
307
|
+
stringify({ name: 'Ada', secret: true }, { replacer: ['name'], space: 2 })
|
|
308
|
+
// '{\n "name": "Ada"\n}'
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
On an exception, `onError` receives the original error once, then the function returns `fallback`.
|
|
312
|
+
The callback's return value is ignored. Exceptions from `reviver`, `replacer`, `toJSON`, or getters
|
|
313
|
+
follow the same rule. If `onError` throws, its exception propagates to the caller:
|
|
314
|
+
|
|
315
|
+
```ts
|
|
316
|
+
const requiredText = stringify(value, {
|
|
317
|
+
onError: (error) => {
|
|
318
|
+
throw error
|
|
319
|
+
},
|
|
320
|
+
}) // still string | undefined
|
|
321
|
+
|
|
322
|
+
stringify(1n, { fallback: 'null' }) // 'null' (BigInt throws natively)
|
|
323
|
+
stringify(undefined, { fallback: 'null' }) // undefined (no exception)
|
|
324
|
+
parse('false', { fallback: true }) // false
|
|
325
|
+
parse('null', { fallback: {} }) // null
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Fallbacks apply only to exceptions: valid `0`, `false`, `''`, and `null` are preserved.
|
|
329
|
+
Likewise, a reviver returning `undefined` for the root does not trigger a fallback.
|
|
330
|
+
Top-level `undefined`, functions, and symbols serialize to native `undefined` without calling
|
|
331
|
+
`onError`. Native handling of these values inside objects/arrays, `NaN`, `Infinity`, and `toJSON`
|
|
332
|
+
is preserved. Circular references throw natively and use the same error handling as BigInt.
|
|
333
|
+
The wrappers do not mutate inputs, undo callback side effects, log errors, or repair invalid JSON.
|
|
334
|
+
|
|
217
335
|
## Path syntax
|
|
218
336
|
|
|
219
337
|
| Syntax | Description |
|
|
220
338
|
| --- | --- |
|
|
221
|
-
| `user.name` | Read or
|
|
222
|
-
| `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. |
|
|
223
341
|
| `user.{name,email}` | Pick several keys from one object. |
|
|
224
342
|
| `user.{*}` | List keys from one object. |
|
|
225
343
|
| `items[n].title` | Map every item in an array during `extract`. |
|
|
226
344
|
|
|
345
|
+
## Agent skill
|
|
346
|
+
|
|
347
|
+
The package includes an [objwalk skill](skills/objwalk/SKILL.md) in `skills/objwalk/`.
|
|
348
|
+
After installation, point your agent at `node_modules/objwalk/skills/objwalk/SKILL.md`.
|
|
349
|
+
If your agent uses a skill directory, link the entire `skills/objwalk` folder, not just `SKILL.md`.
|
|
350
|
+
|
|
351
|
+
The skill explains the existing object utilities and JSON helpers, and directs agents to use
|
|
352
|
+
them without redundant wrappers that only repeat object checks.
|
|
353
|
+
|
|
227
354
|
## API
|
|
228
355
|
|
|
229
356
|
```ts
|
|
230
357
|
pick(data, path)
|
|
231
358
|
set(data, path, value)
|
|
359
|
+
flush(data, path)
|
|
232
360
|
extract(data, mapping)
|
|
233
361
|
each(data, callback)
|
|
234
362
|
map(data, callback)
|
|
235
363
|
merge(target, ...sources)
|
|
236
364
|
equally(a, b)
|
|
365
|
+
parse(raw, options?) // unknown
|
|
366
|
+
stringify(value, options?) // string | undefined
|
|
237
367
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -24,10 +24,13 @@ __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,
|
|
30
|
+
parse: () => parse,
|
|
29
31
|
pick: () => pick,
|
|
30
|
-
set: () => set
|
|
32
|
+
set: () => set,
|
|
33
|
+
stringify: () => stringify
|
|
31
34
|
});
|
|
32
35
|
module.exports = __toCommonJS(index_exports);
|
|
33
36
|
|
|
@@ -272,6 +275,27 @@ var merge = (target, ...sources) => {
|
|
|
272
275
|
return target;
|
|
273
276
|
};
|
|
274
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
|
+
|
|
275
299
|
// src/deepEqual.ts
|
|
276
300
|
var deepEqual = (x, y, store) => {
|
|
277
301
|
if (Number.isNaN(x) && Number.isNaN(y)) {
|
|
@@ -363,14 +387,50 @@ var clone = (value) => {
|
|
|
363
387
|
};
|
|
364
388
|
return cloneValue(value);
|
|
365
389
|
};
|
|
390
|
+
|
|
391
|
+
// src/parse.ts
|
|
392
|
+
var parse = (raw, { fallback, onError, reviver } = {}) => {
|
|
393
|
+
try {
|
|
394
|
+
if (Array.isArray(raw)) {
|
|
395
|
+
return raw;
|
|
396
|
+
}
|
|
397
|
+
if (typeof raw === "object" && raw !== null) {
|
|
398
|
+
const isBinary = raw instanceof ArrayBuffer || ArrayBuffer.isView(raw);
|
|
399
|
+
const prototype = Object.getPrototypeOf(raw);
|
|
400
|
+
if (isBinary === false && (prototype === Object.prototype || prototype === null)) {
|
|
401
|
+
return raw;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return JSON.parse(typeof raw === "string" ? raw : String(raw), reviver);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
onError?.(error);
|
|
407
|
+
return fallback;
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// src/stringify.ts
|
|
412
|
+
var stringify = (value, { fallback, onError, replacer, space } = {}) => {
|
|
413
|
+
try {
|
|
414
|
+
if (typeof replacer === "function") {
|
|
415
|
+
return JSON.stringify(value, replacer, space);
|
|
416
|
+
}
|
|
417
|
+
return JSON.stringify(value, replacer, space);
|
|
418
|
+
} catch (error) {
|
|
419
|
+
onError?.(error);
|
|
420
|
+
return fallback;
|
|
421
|
+
}
|
|
422
|
+
};
|
|
366
423
|
// Annotate the CommonJS export names for ESM import in node:
|
|
367
424
|
0 && (module.exports = {
|
|
368
425
|
clone,
|
|
369
426
|
each,
|
|
370
427
|
equally,
|
|
371
428
|
extract,
|
|
429
|
+
flush,
|
|
372
430
|
map,
|
|
373
431
|
merge,
|
|
432
|
+
parse,
|
|
374
433
|
pick,
|
|
375
|
-
set
|
|
434
|
+
set,
|
|
435
|
+
stringify
|
|
376
436
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -3,6 +3,17 @@ type PickOptions = {
|
|
|
3
3
|
inherited?: boolean;
|
|
4
4
|
ignore?: readonly string[];
|
|
5
5
|
};
|
|
6
|
+
type ParseOptions = {
|
|
7
|
+
fallback?: unknown;
|
|
8
|
+
onError?: (error: unknown) => void;
|
|
9
|
+
reviver?: Parameters<typeof JSON.parse>[1];
|
|
10
|
+
};
|
|
11
|
+
type StringifyOptions = {
|
|
12
|
+
fallback?: string;
|
|
13
|
+
onError?: (error: unknown) => void;
|
|
14
|
+
replacer?: ParseOptions['reviver'] | Parameters<typeof JSON.stringify>[1];
|
|
15
|
+
space?: string | number;
|
|
16
|
+
};
|
|
6
17
|
|
|
7
18
|
type EachCallback = (key: WalkKey, value: any, path: string) => void;
|
|
8
19
|
declare const each: (data: any, callback: EachCallback) => void;
|
|
@@ -21,8 +32,14 @@ declare const merge: <T extends ObjwalkObject$1>(target: T, ...sources: unknown[
|
|
|
21
32
|
type ObjwalkObject = Record<string, any>;
|
|
22
33
|
declare const set: (data: ObjwalkObject | undefined, path: string, value: any) => ObjwalkObject;
|
|
23
34
|
|
|
35
|
+
declare const flush: (data: ObjwalkObject$1 | undefined, path: string) => ObjwalkObject$1;
|
|
36
|
+
|
|
24
37
|
declare const equally: (a: unknown, b: unknown) => boolean;
|
|
25
38
|
|
|
26
39
|
declare const clone: <T>(value: T) => T;
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
declare const parse: (raw: unknown, { fallback, onError, reviver }?: ParseOptions) => unknown;
|
|
42
|
+
|
|
43
|
+
declare const stringify: (value: unknown, { fallback, onError, replacer, space }?: StringifyOptions) => string | undefined;
|
|
44
|
+
|
|
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
|
@@ -3,6 +3,17 @@ type PickOptions = {
|
|
|
3
3
|
inherited?: boolean;
|
|
4
4
|
ignore?: readonly string[];
|
|
5
5
|
};
|
|
6
|
+
type ParseOptions = {
|
|
7
|
+
fallback?: unknown;
|
|
8
|
+
onError?: (error: unknown) => void;
|
|
9
|
+
reviver?: Parameters<typeof JSON.parse>[1];
|
|
10
|
+
};
|
|
11
|
+
type StringifyOptions = {
|
|
12
|
+
fallback?: string;
|
|
13
|
+
onError?: (error: unknown) => void;
|
|
14
|
+
replacer?: ParseOptions['reviver'] | Parameters<typeof JSON.stringify>[1];
|
|
15
|
+
space?: string | number;
|
|
16
|
+
};
|
|
6
17
|
|
|
7
18
|
type EachCallback = (key: WalkKey, value: any, path: string) => void;
|
|
8
19
|
declare const each: (data: any, callback: EachCallback) => void;
|
|
@@ -21,8 +32,14 @@ declare const merge: <T extends ObjwalkObject$1>(target: T, ...sources: unknown[
|
|
|
21
32
|
type ObjwalkObject = Record<string, any>;
|
|
22
33
|
declare const set: (data: ObjwalkObject | undefined, path: string, value: any) => ObjwalkObject;
|
|
23
34
|
|
|
35
|
+
declare const flush: (data: ObjwalkObject$1 | undefined, path: string) => ObjwalkObject$1;
|
|
36
|
+
|
|
24
37
|
declare const equally: (a: unknown, b: unknown) => boolean;
|
|
25
38
|
|
|
26
39
|
declare const clone: <T>(value: T) => T;
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
declare const parse: (raw: unknown, { fallback, onError, reviver }?: ParseOptions) => unknown;
|
|
42
|
+
|
|
43
|
+
declare const stringify: (value: unknown, { fallback, onError, replacer, space }?: StringifyOptions) => string | undefined;
|
|
44
|
+
|
|
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)) {
|
|
@@ -330,13 +351,49 @@ var clone = (value) => {
|
|
|
330
351
|
};
|
|
331
352
|
return cloneValue(value);
|
|
332
353
|
};
|
|
354
|
+
|
|
355
|
+
// src/parse.ts
|
|
356
|
+
var parse = (raw, { fallback, onError, reviver } = {}) => {
|
|
357
|
+
try {
|
|
358
|
+
if (Array.isArray(raw)) {
|
|
359
|
+
return raw;
|
|
360
|
+
}
|
|
361
|
+
if (typeof raw === "object" && raw !== null) {
|
|
362
|
+
const isBinary = raw instanceof ArrayBuffer || ArrayBuffer.isView(raw);
|
|
363
|
+
const prototype = Object.getPrototypeOf(raw);
|
|
364
|
+
if (isBinary === false && (prototype === Object.prototype || prototype === null)) {
|
|
365
|
+
return raw;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return JSON.parse(typeof raw === "string" ? raw : String(raw), reviver);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
onError?.(error);
|
|
371
|
+
return fallback;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/stringify.ts
|
|
376
|
+
var stringify = (value, { fallback, onError, replacer, space } = {}) => {
|
|
377
|
+
try {
|
|
378
|
+
if (typeof replacer === "function") {
|
|
379
|
+
return JSON.stringify(value, replacer, space);
|
|
380
|
+
}
|
|
381
|
+
return JSON.stringify(value, replacer, space);
|
|
382
|
+
} catch (error) {
|
|
383
|
+
onError?.(error);
|
|
384
|
+
return fallback;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
333
387
|
export {
|
|
334
388
|
clone,
|
|
335
389
|
each,
|
|
336
390
|
equally,
|
|
337
391
|
extract,
|
|
392
|
+
flush,
|
|
338
393
|
map,
|
|
339
394
|
merge,
|
|
395
|
+
parse,
|
|
340
396
|
pick,
|
|
341
|
-
set
|
|
397
|
+
set,
|
|
398
|
+
stringify
|
|
342
399
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "objwalk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Small utilities for reading, writing, walking, and reshaping nested JavaScript objects.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"object",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"files": [
|
|
41
41
|
"dist",
|
|
42
|
-
"README.md"
|
|
42
|
+
"README.md",
|
|
43
|
+
"skills"
|
|
43
44
|
],
|
|
44
45
|
"scripts": {
|
|
45
46
|
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: objwalk
|
|
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
|
+
---
|
|
5
|
+
|
|
6
|
+
# objwalk
|
|
7
|
+
|
|
8
|
+
**objwalk is a library for working with JavaScript objects. Extra wrappers whose
|
|
9
|
+
only purpose is to check whether a value is an object before calling objwalk are
|
|
10
|
+
redundant.** Use the public functions directly within their input contracts.
|
|
11
|
+
Do not add `safePick`, `safeMerge`, `parseObject`, `isObjectPayload`, or a custom
|
|
12
|
+
JSON decoder to repeat behaviour already owned by the library.
|
|
13
|
+
|
|
14
|
+
Application-specific validation of required fields and allowed values still
|
|
15
|
+
belongs at untyped boundaries. `parse` returns `unknown`; it does not prove a
|
|
16
|
+
domain type. Other functions keep their declared input contracts: do not bypass
|
|
17
|
+
them with type assertions or assume every method accepts arbitrary `unknown`.
|
|
18
|
+
|
|
19
|
+
Named imports only:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { clone, each, equally, extract, flush, map, merge, parse, pick, set, stringify } from 'objwalk'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Choose the existing operation
|
|
26
|
+
|
|
27
|
+
| Need | Function and behaviour |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| Read a nested value | `pick(data, path, options?)`; unresolved paths return `undefined`. |
|
|
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. |
|
|
32
|
+
| Reshape an object | `extract(source, mapping)`; mapping keys are destination paths, values are source paths. |
|
|
33
|
+
| Visit nested values | `each(data, (key, value, path) => { ... })`; visits containers and leaves, excluding the root. |
|
|
34
|
+
| Build a store while walking | `map(data, (key, value, store, path) => { ... }, store?)`; returns the store, ignores callback return values. |
|
|
35
|
+
| Deep merge | `merge(target, ...sources)`; mutates and returns the target, merges plain objects, replaces arrays and non-plain values. |
|
|
36
|
+
| Deep copy | `clone(value)`; preserves `Date`, `RegExp`, symbol keys, and circular references. |
|
|
37
|
+
| Deep comparison | `equally(a, b)`; supports `NaN`, `Date`, `RegExp`, and circular references. |
|
|
38
|
+
|
|
39
|
+
`set`, `flush`, and `merge` mutate their targets. Use `clone` first only when the task
|
|
40
|
+
requires an independent copy. `each` and `map` do not support circular traversal;
|
|
41
|
+
do not infer that capability from `clone` or `equally`.
|
|
42
|
+
|
|
43
|
+
## Paths and reshaping
|
|
44
|
+
|
|
45
|
+
| Syntax | Use |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `user.name` | Nested property. |
|
|
48
|
+
| `items[0].title` | Explicit array index. |
|
|
49
|
+
| `user.{name,email}` | Select several keys with `pick`; a single selected key returns its value. |
|
|
50
|
+
| `user.{*}` | List object keys with `pick`. |
|
|
51
|
+
| `items[n].title` | Map array items in `extract`, with `[n]` in both source and destination paths. |
|
|
52
|
+
|
|
53
|
+
`[n]` is an `extract` mapping feature, not a general wildcard for `pick`, `set`, or `flush`.
|
|
54
|
+
An array of source paths in `extract` selects the first result that is not
|
|
55
|
+
`undefined`, preserving `null`, `false`, `0`, and empty strings.
|
|
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
|
+
|
|
65
|
+
`pick` reads inherited properties by default. Use its existing options when own
|
|
66
|
+
properties or ignored keys are required:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
pick(data, 'user.name', {
|
|
70
|
+
inherited: false,
|
|
71
|
+
ignore: ['__proto__', 'prototype', 'constructor'],
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## JSON and ready values
|
|
76
|
+
|
|
77
|
+
Call `parse(raw, options?)` directly with `raw: unknown`. The library chooses
|
|
78
|
+
between ready values and text; do not surround it with `typeof`, `Array.isArray`,
|
|
79
|
+
`ArrayBuffer.isView`, or prototype checks to make that choice.
|
|
80
|
+
|
|
81
|
+
- Plain objects with `Object.prototype` or a null prototype, and arrays, return
|
|
82
|
+
by the same reference. They are not copied or traversed; `reviver` is not called.
|
|
83
|
+
- Other inputs use `JSON.parse(typeof raw === 'string' ? raw : String(raw), reviver)`.
|
|
84
|
+
ArrayBuffer, DataView, and typed-array views take this text path. A Buffer with
|
|
85
|
+
JSON text can use its native string conversion; there is no binary decoder.
|
|
86
|
+
- Parsed roots may be objects, arrays, strings, numbers, booleans, or `null`.
|
|
87
|
+
There is no object-only mode and no `parse<T>()` overload.
|
|
88
|
+
- `stringify(value, options?)` always uses native `JSON.stringify`, including
|
|
89
|
+
`replacer` callback/array and `space`. `stringify('{}')` returns `'"{}"'`.
|
|
90
|
+
|
|
91
|
+
Both functions return `undefined` on an exception by default. Options include
|
|
92
|
+
`fallback` and `onError(error: unknown)`. `parse` also accepts native `reviver`.
|
|
93
|
+
`ParseOptions` and `StringifyOptions` are public named type exports.
|
|
94
|
+
|
|
95
|
+
`onError` receives the original exception once. Its return value is ignored;
|
|
96
|
+
if it throws, that exception propagates. Errors from input recognition, string
|
|
97
|
+
conversion, reviver, replacer, toJSON, and getters follow the same contract.
|
|
98
|
+
|
|
99
|
+
Fallback applies only to exceptions. Preserve valid falsy results and native
|
|
100
|
+
`undefined`: `stringify(undefined, { fallback: 'null' })` still returns
|
|
101
|
+
`undefined`. Neither fallback nor a throwing `onError` changes stringify's
|
|
102
|
+
return type of `string | undefined`.
|
|
103
|
+
|
|
104
|
+
Full usage: [README](../../README.md). In a consumer project, read the installed
|
|
105
|
+
package's README and declarations when checking the available version's API.
|