objwalk 1.7.0 → 1.9.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 +42 -10
- package/SPEC.md +110 -20
- package/dist/index.cjs +26 -10
- package/dist/index.d.cts +35 -4
- package/dist/index.d.ts +35 -4
- package/dist/index.js +26 -10
- package/package.json +1 -1
- package/skills/objwalk/SKILL.md +40 -5
package/README.md
CHANGED
|
@@ -125,21 +125,53 @@ Return `false` to stop the entire walk. For more control, `each` also accepts
|
|
|
125
125
|
`root` and `descend` options and provides literal path segments as a fourth
|
|
126
126
|
callback argument. [Traversal details](SPEC.md#each).
|
|
127
127
|
|
|
128
|
-
Use `
|
|
128
|
+
Use `{ shallow: true }` to visit dictionary entries as whole values:
|
|
129
129
|
|
|
130
130
|
```ts
|
|
131
|
-
import {
|
|
131
|
+
import { each } from 'objwalk'
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
each({ first: { count: 1 }, second: { count: 2 } }, (key, value) => {
|
|
134
|
+
console.log(key, value.count)
|
|
135
|
+
}, { shallow: true })
|
|
136
|
+
// first 1
|
|
137
|
+
// second 2
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Here, `key` is inferred as `string` and `value` as `{ count: number }`.
|
|
141
|
+
Shallow array visits infer a numeric index and the array's element type.
|
|
142
|
+
|
|
143
|
+
Use `map` to transform values while keeping the object and array structure:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { map } from 'objwalk'
|
|
147
|
+
|
|
148
|
+
const cleaned = map({ name: ' Natalia ', tags: [' ts '] }, (_key, value) =>
|
|
149
|
+
typeof value === 'string' ? value.trim() : value
|
|
150
|
+
)
|
|
138
151
|
// { name: 'Natalia', tags: ['ts'] }
|
|
139
152
|
```
|
|
140
153
|
|
|
141
|
-
|
|
142
|
-
acyclic data
|
|
154
|
+
Each callback return becomes the value at that key or index in the new result.
|
|
155
|
+
The input is not modified by `map`. Both walkers expect acyclic data unless
|
|
156
|
+
traversal options prune the cycles.
|
|
157
|
+
|
|
158
|
+
When your callback handles each entry as a whole, use a shallow walk:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { map } from 'objwalk'
|
|
162
|
+
|
|
163
|
+
const counts = map(
|
|
164
|
+
{ editors: ['Natalia'], readers: ['Ada', 'Grace'] },
|
|
165
|
+
(_key, value) => value.length,
|
|
166
|
+
{ shallow: true }
|
|
167
|
+
)
|
|
168
|
+
// { editors: 1, readers: 2 }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Shallow dictionary keys are typed as `string`; shallow array indexes are `number`.
|
|
172
|
+
Both `each` and `map` also infer the original value type in shallow mode.
|
|
173
|
+
For selective traversal, pass `descend` in the third argument.
|
|
174
|
+
[Mapping options](SPEC.md#map).
|
|
143
175
|
|
|
144
176
|
## Merge defaults or make a copy
|
|
145
177
|
|
|
@@ -188,7 +220,7 @@ formatting, and an `onError` callback are available through options.
|
|
|
188
220
|
| Delete one or several paths | [`flush(data, path)`](SPEC.md#flush) |
|
|
189
221
|
| Reshape data with a mapping | [`extract(source, mapping)`](SPEC.md#extract) |
|
|
190
222
|
| Visit nested values | [`each(data, callback, options?)`](SPEC.md#each) |
|
|
191
|
-
|
|
|
223
|
+
| Transform values while keeping keys | [`map(data, callback, options?)`](SPEC.md#map) |
|
|
192
224
|
| Merge plain objects | [`merge(target, ...sources)`](SPEC.md#merge) |
|
|
193
225
|
| Copy supported values | [`clone(value)`](SPEC.md#clone) |
|
|
194
226
|
| Compare values or selected paths | [`equally(a, b, paths?)`](SPEC.md#equally) |
|
package/SPEC.md
CHANGED
|
@@ -30,7 +30,7 @@ type DiffOperation =
|
|
|
30
30
|
| `flush(data, path)` | Input object | Mutates the input |
|
|
31
31
|
| `extract(source, mapping)` | New result object | Selected values are not cloned |
|
|
32
32
|
| `each(data, callback, options?)` | `undefined` | Passes original values to callbacks |
|
|
33
|
-
| `map(data, callback,
|
|
33
|
+
| `map(data, callback, options?)` | New array or dictionary | Callback returns each replacement value; pruned and returned values can retain references |
|
|
34
34
|
| `merge(target, ...sources)` | Target object | Mutates the target; replacement values are not cloned |
|
|
35
35
|
| `clone(value)` | Value of the same declared generic type | Recursively copies supported objects and arrays |
|
|
36
36
|
| `equally(a, b, paths?)` | `boolean` | Does not assign to the inputs |
|
|
@@ -41,8 +41,8 @@ type DiffOperation =
|
|
|
41
41
|
`pick`, `set`, and `flush` use `{}` when their `data` argument is `undefined`.
|
|
42
42
|
`set` and `flush` still require the path argument. `pick` defaults its path to `''`.
|
|
43
43
|
Their declared data contract is `ObjwalkObject`; this is not a guarantee that all
|
|
44
|
-
functions accept every possible JavaScript value. `each`
|
|
45
|
-
`clone` is generic; `equally`, `diff`, `parse`, and `stringify` accept `unknown` inputs.
|
|
44
|
+
functions accept every possible JavaScript value. `each` accepts `any`; `map`
|
|
45
|
+
accepts dictionaries and readonly arrays; `clone` is generic; `equally`, `diff`, `parse`, and `stringify` accept `unknown` inputs.
|
|
46
46
|
`extract` requires an object and a mapping; `merge` requires an object target.
|
|
47
47
|
|
|
48
48
|
Except for the documented JSON error handling, exceptions from callbacks, getters,
|
|
@@ -163,9 +163,9 @@ does not provide an independent deep snapshot.
|
|
|
163
163
|
## each
|
|
164
164
|
|
|
165
165
|
```ts
|
|
166
|
-
type EachCallback<TKey extends WalkKey | undefined = WalkKey> = (
|
|
166
|
+
type EachCallback<TKey extends WalkKey | undefined = WalkKey, TValue = any> = (
|
|
167
167
|
key: TKey,
|
|
168
|
-
value:
|
|
168
|
+
value: TValue,
|
|
169
169
|
path: string,
|
|
170
170
|
segments: WalkKey[]
|
|
171
171
|
) => unknown
|
|
@@ -180,6 +180,7 @@ type EachEntry = {
|
|
|
180
180
|
type EachOptions = {
|
|
181
181
|
root?: boolean
|
|
182
182
|
descend?: (entry: EachEntry) => boolean
|
|
183
|
+
shallow?: boolean
|
|
183
184
|
}
|
|
184
185
|
```
|
|
185
186
|
|
|
@@ -194,8 +195,9 @@ are leaves, and built-in collection internals are not traversed.
|
|
|
194
195
|
`root` defaults to `false`. When enabled, the root is visited first with key
|
|
195
196
|
`undefined`, path `''`, and segments `[]`, even for primitive, nullish, or empty
|
|
196
197
|
inputs. Otherwise a primitive or nullish root has no visits. Object keys are
|
|
197
|
-
strings and array indexes are numbers.
|
|
198
|
-
typed as `WalkKey
|
|
198
|
+
strings and array indexes are numbers. In the default deep mode, callback keys
|
|
199
|
+
are typed as `WalkKey` without root visits; enabled or dynamic root options use
|
|
200
|
+
`WalkKey | undefined`. Shallow key and value types are described below.
|
|
199
201
|
|
|
200
202
|
Returning exactly `false` from the callback stops the entire traversal, including
|
|
201
203
|
siblings and remaining ancestor branches. Other return values, including other
|
|
@@ -207,6 +209,28 @@ both completion and early stopping.
|
|
|
207
209
|
an excluded root container. Returning `false` skips that container's children and
|
|
208
210
|
continues with siblings. It does not stop the entire traversal.
|
|
209
211
|
|
|
212
|
+
`shallow` defaults to `false`. With `shallow: true`, only immediate root children
|
|
213
|
+
are visited, plus the root when requested. Child values are passed by reference;
|
|
214
|
+
their contents are not read. `descend` is evaluated only for the root because
|
|
215
|
+
child containers are already at the shallow limit. Root pruning and stopping via
|
|
216
|
+
callback `false` keep their existing meanings. Cyclic child values are safe when
|
|
217
|
+
the shallow limit prevents following them.
|
|
218
|
+
|
|
219
|
+
With literal `shallow: true` and root visits disabled, dictionary keys are typed
|
|
220
|
+
as `string` and values as the dictionary's value type; readonly array keys are
|
|
221
|
+
`number` and values are the element type. Heterogeneous inputs produce the union
|
|
222
|
+
of their immediate value types. With root visits enabled or a dynamic root flag,
|
|
223
|
+
the key type additionally includes `undefined`, and the value type additionally
|
|
224
|
+
includes the root container's type. These key/value unions are not correlated
|
|
225
|
+
for control-flow narrowing; narrow a union value before accessing its fields.
|
|
226
|
+
|
|
227
|
+
Deep traversal, dynamic shallow flags, and unknown or mixed array/dictionary inputs retain the
|
|
228
|
+
general key and value types. An arbitrary `descend` predicate does not narrow
|
|
229
|
+
them. `EachCallback<TKey, TValue>` supports explicitly typed callbacks; `TValue`
|
|
230
|
+
defaults to `any` to preserve existing callback type declarations.
|
|
231
|
+
`EachEntry.value` in `descend` remains general because the predicate can see
|
|
232
|
+
different container levels, including the root.
|
|
233
|
+
|
|
210
234
|
Object keys are collected when entering a container; values are read as visited.
|
|
211
235
|
For arrays, native iteration captures the starting length: appended slots beyond
|
|
212
236
|
that length are not visited. Callback and `descend` receive separate copies of
|
|
@@ -217,22 +241,88 @@ not share stop state.
|
|
|
217
241
|
## map
|
|
218
242
|
|
|
219
243
|
```ts
|
|
220
|
-
type MapCallback<
|
|
221
|
-
key:
|
|
222
|
-
value:
|
|
223
|
-
store: TStore,
|
|
244
|
+
type MapCallback<TResult = unknown, TKey extends WalkKey = WalkKey, TValue = any> = (
|
|
245
|
+
key: TKey,
|
|
246
|
+
value: TValue,
|
|
224
247
|
path: string
|
|
225
|
-
) =>
|
|
226
|
-
```
|
|
248
|
+
) => TResult
|
|
227
249
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
250
|
+
type MapOptions = {
|
|
251
|
+
descend?: EachOptions['descend']
|
|
252
|
+
shallow?: boolean
|
|
253
|
+
}
|
|
254
|
+
```
|
|
232
255
|
|
|
233
|
-
|
|
234
|
-
root
|
|
235
|
-
|
|
256
|
+
`map(data, callback, options?)` transforms a dictionary or array without assigning
|
|
257
|
+
into the input. An array root produces a new array; a dictionary root produces a
|
|
258
|
+
new ordinary object. The root is excluded from callback visits. Every visited
|
|
259
|
+
property or array index retains its literal key; its value becomes the callback's
|
|
260
|
+
return value. `false`, `null`, and `undefined` are stored as ordinary results;
|
|
261
|
+
there is no callback return value for stopping, filtering, or deleting entries.
|
|
262
|
+
Callback replacements are not traversed again, including returned objects and arrays.
|
|
263
|
+
|
|
264
|
+
The callback receives `(key, value, path)`. Object keys are strings, array indexes
|
|
265
|
+
are numbers, and paths follow `each`'s descriptive string format. No accumulator or
|
|
266
|
+
store argument exists. `MapCallback`'s first generic argument is the return value
|
|
267
|
+
rather than the former store type; the optional second argument is the key type,
|
|
268
|
+
and the optional third argument is the input value type (default `any`).
|
|
269
|
+
Array results are typed as `TResult[]`; dictionary results as
|
|
270
|
+
`Record<string, TResult>`. A dictionary-or-array union input produces the matching
|
|
271
|
+
union result type. Readonly input arrays are accepted and produce mutable new arrays.
|
|
272
|
+
Deep callback values are typed as `any`, since earlier child transformations can
|
|
273
|
+
change their contents.
|
|
274
|
+
|
|
275
|
+
Deep traversal is the default. Plain objects and arrays are recursively mapped
|
|
276
|
+
before their containing value is passed to the callback. Thus callbacks run in
|
|
277
|
+
postorder: children first, then their parent, with the root still excluded.
|
|
278
|
+
A container callback receives a newly built container containing its already
|
|
279
|
+
mapped children. A pruned container callback instead receives the original value
|
|
280
|
+
by reference. `Date`, `RegExp`, other non-plain objects, and functions are leaves:
|
|
281
|
+
they are passed directly to the callback without rebuilding their internal state.
|
|
282
|
+
The root dictionary's own entries are enumerated regardless of its prototype;
|
|
283
|
+
the plain-object restriction applies to nested traversal.
|
|
284
|
+
|
|
285
|
+
Objects enumerate own enumerable string keys; inherited, symbol, and non-enumerable
|
|
286
|
+
properties are excluded. Literal dots, brackets, empty keys, and `__proto__` are
|
|
287
|
+
preserved as own data properties rather than parsed as paths. Result objects use
|
|
288
|
+
`Object.prototype`; source prototypes and descriptors are not copied. Array indexes
|
|
289
|
+
follow `each` iteration, preserving length and absent slots. An explicitly returned
|
|
290
|
+
`undefined` is an own slot, whereas an unvisited hole remains absent. Inherited
|
|
291
|
+
indexed values can be visited; extra array properties are excluded.
|
|
292
|
+
|
|
293
|
+
`descend` runs on each original traversable container before visiting children,
|
|
294
|
+
including the excluded root. It receives `EachEntry` with literal `segments`;
|
|
295
|
+
modifying these segments does not alter traversal paths. Returning `false` prunes
|
|
296
|
+
children, then the container's callback processes the original value as a whole.
|
|
297
|
+
Pruning the root produces an empty container of the same kind, with no callback
|
|
298
|
+
visits. This timing differs from `each`, which calls its callback before `descend`.
|
|
299
|
+
|
|
300
|
+
`shallow` defaults to `false`. With literal `shallow: true`, only immediate root
|
|
301
|
+
children are mapped: dictionary callback keys are typed as `string`, and statically
|
|
302
|
+
known array keys as `number`. The input value type is inferred from the dictionary
|
|
303
|
+
values or array elements, including readonly and heterogeneous inputs. A shallow
|
|
304
|
+
`Record<string, T>` or readonly `T[]` passes `T` to its callback. Deep calls, dynamic
|
|
305
|
+
shallow flags, and union inputs keep `WalkKey` and general callback values. An
|
|
306
|
+
arbitrary `descend` predicate does not narrow these types. The shallow overloads
|
|
307
|
+
use `<TResult, TValue = any>`: calls that explicitly provide only `TResult` retain
|
|
308
|
+
the default `any` value type for compatibility. Omit explicit type arguments for
|
|
309
|
+
full inference, or provide both result and value types. When both
|
|
310
|
+
options are present, `descend` is evaluated only for the root, because child
|
|
311
|
+
containers are already at the shallow limit.
|
|
312
|
+
|
|
313
|
+
Traversed containers are new. Pruned containers, special objects, and values
|
|
314
|
+
returned by the callback may share references with the input. Each occurrence of
|
|
315
|
+
a shared traversable container is mapped separately. Cycles are not detected;
|
|
316
|
+
pruning can prevent descent into a cyclic branch. Exceptions from callbacks,
|
|
317
|
+
getters, and `descend` propagate. The function does not roll back callback side
|
|
318
|
+
effects or await promises returned by callbacks.
|
|
319
|
+
|
|
320
|
+
This replaces the published accumulator-based contract. Migrate
|
|
321
|
+
`map(data, (key, value, store, path) => { store[key] = transform(value) }, store)`
|
|
322
|
+
to `map(data, (key, value, path) => transform(value), options)` when the result
|
|
323
|
+
retains input keys. Callers that build a different structure should use `each`
|
|
324
|
+
and manage their accumulator explicitly. Old stores are not accepted as options,
|
|
325
|
+
and there is no compatibility overload or `undefined` placeholder.
|
|
236
326
|
|
|
237
327
|
## merge
|
|
238
328
|
|
package/dist/index.cjs
CHANGED
|
@@ -49,10 +49,10 @@ var isWalkable = (value) => {
|
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
// src/each.ts
|
|
52
|
-
var each = (data, callback, { root = false, descend } = {}) => {
|
|
52
|
+
var each = (data, callback, { root = false, descend, shallow = false } = {}) => {
|
|
53
53
|
const walk = (value, path, segments, key) => {
|
|
54
54
|
const proceed = segments.length > 0 || root ? callback(key, value, path, [...segments]) !== false : true;
|
|
55
|
-
if (proceed && isWalkable(value) && (descend === void 0 || descend({ key, value, path, segments: [...segments] }))) {
|
|
55
|
+
if (proceed && (segments.length === 0 || shallow === false) && isWalkable(value) && (descend === void 0 || descend({ key, value, path, segments: [...segments] }))) {
|
|
56
56
|
if (Array.isArray(value)) {
|
|
57
57
|
return value.every(
|
|
58
58
|
(item, index) => walk(item, createPath(path, index, true), [...segments, index], index)
|
|
@@ -225,14 +225,6 @@ var extract = (source, mapping) => {
|
|
|
225
225
|
return result;
|
|
226
226
|
};
|
|
227
227
|
|
|
228
|
-
// src/map.ts
|
|
229
|
-
var map = (data, callback, store = {}) => {
|
|
230
|
-
each(data, (key, value, path) => {
|
|
231
|
-
callback(key, value, store, path);
|
|
232
|
-
});
|
|
233
|
-
return store;
|
|
234
|
-
};
|
|
235
|
-
|
|
236
228
|
// src/helpers/isPlainObject.ts
|
|
237
229
|
var isPlainObject = (value) => {
|
|
238
230
|
if (value !== null && typeof value === "object") {
|
|
@@ -242,6 +234,30 @@ var isPlainObject = (value) => {
|
|
|
242
234
|
return false;
|
|
243
235
|
};
|
|
244
236
|
|
|
237
|
+
// src/map.ts
|
|
238
|
+
var map = (data, callback, { descend, shallow = false } = {}) => {
|
|
239
|
+
const walk = (value, key, path, segments) => {
|
|
240
|
+
const root = key === void 0;
|
|
241
|
+
let mapped = root ? Array.isArray(value) ? [] : {} : value;
|
|
242
|
+
if ((root || Array.isArray(value) || isPlainObject(value)) && (root || shallow === false) && (descend === void 0 || descend({ key, value, path, segments: [...segments] }))) {
|
|
243
|
+
mapped = Array.isArray(value) ? Array(value.length) : {};
|
|
244
|
+
each(value, (key2, value2) => {
|
|
245
|
+
const childPath = createPath(path, key2, Array.isArray(mapped));
|
|
246
|
+
const childSegments = [...segments, key2];
|
|
247
|
+
const result = walk(value2, key2, childPath, childSegments);
|
|
248
|
+
Object.defineProperty(mapped, key2, {
|
|
249
|
+
value: result,
|
|
250
|
+
writable: true,
|
|
251
|
+
enumerable: true,
|
|
252
|
+
configurable: true
|
|
253
|
+
});
|
|
254
|
+
}, { shallow: true });
|
|
255
|
+
}
|
|
256
|
+
return key !== void 0 ? callback(key, mapped, path) : mapped;
|
|
257
|
+
};
|
|
258
|
+
return walk(data, void 0, "", []);
|
|
259
|
+
};
|
|
260
|
+
|
|
245
261
|
// src/merge.ts
|
|
246
262
|
var unsafeKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
247
263
|
var merge = (target, ...sources) => {
|
package/dist/index.d.cts
CHANGED
|
@@ -23,7 +23,7 @@ type StringifyOptions = {
|
|
|
23
23
|
space?: string | number;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
type EachCallback<TKey extends WalkKey | undefined = WalkKey> = (key: TKey, value:
|
|
26
|
+
type EachCallback<TKey extends WalkKey | undefined = WalkKey, TValue = any> = (key: TKey, value: TValue, path: string, segments: WalkKey[]) => unknown;
|
|
27
27
|
type EachEntry = {
|
|
28
28
|
key: WalkKey | undefined;
|
|
29
29
|
value: any;
|
|
@@ -33,8 +33,24 @@ type EachEntry = {
|
|
|
33
33
|
type EachOptions = {
|
|
34
34
|
root?: boolean;
|
|
35
35
|
descend?: (entry: EachEntry) => boolean;
|
|
36
|
+
shallow?: boolean;
|
|
36
37
|
};
|
|
38
|
+
type DictionaryValue<TData> = TData extends unknown ? TData[keyof TData] : never;
|
|
37
39
|
type Each = {
|
|
40
|
+
<TData extends readonly unknown[]>(data: TData, callback: EachCallback<number, TData[number]>, options: EachOptions & {
|
|
41
|
+
shallow: true;
|
|
42
|
+
root?: false;
|
|
43
|
+
}): void;
|
|
44
|
+
<TData extends Record<string, unknown>>(data: TData, callback: EachCallback<string, DictionaryValue<TData>>, options: EachOptions & {
|
|
45
|
+
shallow: true;
|
|
46
|
+
root?: false;
|
|
47
|
+
}): void;
|
|
48
|
+
<TData extends readonly unknown[]>(data: TData, callback: EachCallback<number | undefined, TData[number] | TData>, options: EachOptions & {
|
|
49
|
+
shallow: true;
|
|
50
|
+
}): void;
|
|
51
|
+
<TData extends Record<string, unknown>>(data: TData, callback: EachCallback<string | undefined, DictionaryValue<TData> | TData>, options: EachOptions & {
|
|
52
|
+
shallow: true;
|
|
53
|
+
}): void;
|
|
38
54
|
(data: any, callback: EachCallback, options?: EachOptions & {
|
|
39
55
|
root?: false;
|
|
40
56
|
}): void;
|
|
@@ -48,8 +64,23 @@ declare const pick: (data?: ObjwalkObject$1, path?: string, options?: PickOption
|
|
|
48
64
|
type ExtractMapping = Record<string, string | string[]>;
|
|
49
65
|
declare const extract: (source: ObjwalkObject$1, mapping: ExtractMapping) => ObjwalkObject$1;
|
|
50
66
|
|
|
51
|
-
type MapCallback<
|
|
52
|
-
|
|
67
|
+
type MapCallback<TResult = unknown, TKey extends WalkKey = WalkKey, TValue = any> = (key: TKey, value: TValue, path: string) => TResult;
|
|
68
|
+
type MapOptions = {
|
|
69
|
+
descend?: EachOptions['descend'];
|
|
70
|
+
shallow?: boolean;
|
|
71
|
+
};
|
|
72
|
+
type Map = {
|
|
73
|
+
<TResult, TValue = any>(data: readonly TValue[], callback: MapCallback<TResult, number, TValue>, options: MapOptions & {
|
|
74
|
+
shallow: true;
|
|
75
|
+
}): TResult[];
|
|
76
|
+
<TResult, TValue = any>(data: Record<string, TValue>, callback: MapCallback<TResult, string, TValue>, options: MapOptions & {
|
|
77
|
+
shallow: true;
|
|
78
|
+
}): Record<string, TResult>;
|
|
79
|
+
<TResult>(data: readonly unknown[], callback: MapCallback<TResult>, options?: MapOptions): TResult[];
|
|
80
|
+
<TResult>(data: Record<string, unknown>, callback: MapCallback<TResult>, options?: MapOptions): Record<string, TResult>;
|
|
81
|
+
<TResult>(data: Record<string, unknown> | readonly unknown[], callback: MapCallback<TResult>, options?: MapOptions): Record<string, TResult> | TResult[];
|
|
82
|
+
};
|
|
83
|
+
declare const map: Map;
|
|
53
84
|
|
|
54
85
|
declare const merge: <T extends ObjwalkObject$1>(target: T, ...sources: unknown[]) => T;
|
|
55
86
|
|
|
@@ -68,4 +99,4 @@ declare const parse: (raw: unknown, { fallback, onError, reviver }?: ParseOption
|
|
|
68
99
|
|
|
69
100
|
declare const stringify: (value: unknown, { fallback, onError, replacer, space }?: StringifyOptions) => string | undefined;
|
|
70
101
|
|
|
71
|
-
export { type DiffOperation, type EachCallback, type EachEntry, type EachOptions, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, diff, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
|
|
102
|
+
export { type DiffOperation, type EachCallback, type EachEntry, type EachOptions, type ExtractMapping, type MapCallback, type MapOptions, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, diff, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
|
package/dist/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ type StringifyOptions = {
|
|
|
23
23
|
space?: string | number;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
type EachCallback<TKey extends WalkKey | undefined = WalkKey> = (key: TKey, value:
|
|
26
|
+
type EachCallback<TKey extends WalkKey | undefined = WalkKey, TValue = any> = (key: TKey, value: TValue, path: string, segments: WalkKey[]) => unknown;
|
|
27
27
|
type EachEntry = {
|
|
28
28
|
key: WalkKey | undefined;
|
|
29
29
|
value: any;
|
|
@@ -33,8 +33,24 @@ type EachEntry = {
|
|
|
33
33
|
type EachOptions = {
|
|
34
34
|
root?: boolean;
|
|
35
35
|
descend?: (entry: EachEntry) => boolean;
|
|
36
|
+
shallow?: boolean;
|
|
36
37
|
};
|
|
38
|
+
type DictionaryValue<TData> = TData extends unknown ? TData[keyof TData] : never;
|
|
37
39
|
type Each = {
|
|
40
|
+
<TData extends readonly unknown[]>(data: TData, callback: EachCallback<number, TData[number]>, options: EachOptions & {
|
|
41
|
+
shallow: true;
|
|
42
|
+
root?: false;
|
|
43
|
+
}): void;
|
|
44
|
+
<TData extends Record<string, unknown>>(data: TData, callback: EachCallback<string, DictionaryValue<TData>>, options: EachOptions & {
|
|
45
|
+
shallow: true;
|
|
46
|
+
root?: false;
|
|
47
|
+
}): void;
|
|
48
|
+
<TData extends readonly unknown[]>(data: TData, callback: EachCallback<number | undefined, TData[number] | TData>, options: EachOptions & {
|
|
49
|
+
shallow: true;
|
|
50
|
+
}): void;
|
|
51
|
+
<TData extends Record<string, unknown>>(data: TData, callback: EachCallback<string | undefined, DictionaryValue<TData> | TData>, options: EachOptions & {
|
|
52
|
+
shallow: true;
|
|
53
|
+
}): void;
|
|
38
54
|
(data: any, callback: EachCallback, options?: EachOptions & {
|
|
39
55
|
root?: false;
|
|
40
56
|
}): void;
|
|
@@ -48,8 +64,23 @@ declare const pick: (data?: ObjwalkObject$1, path?: string, options?: PickOption
|
|
|
48
64
|
type ExtractMapping = Record<string, string | string[]>;
|
|
49
65
|
declare const extract: (source: ObjwalkObject$1, mapping: ExtractMapping) => ObjwalkObject$1;
|
|
50
66
|
|
|
51
|
-
type MapCallback<
|
|
52
|
-
|
|
67
|
+
type MapCallback<TResult = unknown, TKey extends WalkKey = WalkKey, TValue = any> = (key: TKey, value: TValue, path: string) => TResult;
|
|
68
|
+
type MapOptions = {
|
|
69
|
+
descend?: EachOptions['descend'];
|
|
70
|
+
shallow?: boolean;
|
|
71
|
+
};
|
|
72
|
+
type Map = {
|
|
73
|
+
<TResult, TValue = any>(data: readonly TValue[], callback: MapCallback<TResult, number, TValue>, options: MapOptions & {
|
|
74
|
+
shallow: true;
|
|
75
|
+
}): TResult[];
|
|
76
|
+
<TResult, TValue = any>(data: Record<string, TValue>, callback: MapCallback<TResult, string, TValue>, options: MapOptions & {
|
|
77
|
+
shallow: true;
|
|
78
|
+
}): Record<string, TResult>;
|
|
79
|
+
<TResult>(data: readonly unknown[], callback: MapCallback<TResult>, options?: MapOptions): TResult[];
|
|
80
|
+
<TResult>(data: Record<string, unknown>, callback: MapCallback<TResult>, options?: MapOptions): Record<string, TResult>;
|
|
81
|
+
<TResult>(data: Record<string, unknown> | readonly unknown[], callback: MapCallback<TResult>, options?: MapOptions): Record<string, TResult> | TResult[];
|
|
82
|
+
};
|
|
83
|
+
declare const map: Map;
|
|
53
84
|
|
|
54
85
|
declare const merge: <T extends ObjwalkObject$1>(target: T, ...sources: unknown[]) => T;
|
|
55
86
|
|
|
@@ -68,4 +99,4 @@ declare const parse: (raw: unknown, { fallback, onError, reviver }?: ParseOption
|
|
|
68
99
|
|
|
69
100
|
declare const stringify: (value: unknown, { fallback, onError, replacer, space }?: StringifyOptions) => string | undefined;
|
|
70
101
|
|
|
71
|
-
export { type DiffOperation, type EachCallback, type EachEntry, type EachOptions, type ExtractMapping, type MapCallback, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, diff, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
|
|
102
|
+
export { type DiffOperation, type EachCallback, type EachEntry, type EachOptions, type ExtractMapping, type MapCallback, type MapOptions, type ObjwalkObject$1 as ObjwalkObject, type ParseOptions, type PickOptions, type StringifyOptions, type WalkKey, clone, diff, each, equally, extract, flush, map, merge, parse, pick, set, stringify };
|
package/dist/index.js
CHANGED
|
@@ -12,10 +12,10 @@ var isWalkable = (value) => {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
// src/each.ts
|
|
15
|
-
var each = (data, callback, { root = false, descend } = {}) => {
|
|
15
|
+
var each = (data, callback, { root = false, descend, shallow = false } = {}) => {
|
|
16
16
|
const walk = (value, path, segments, key) => {
|
|
17
17
|
const proceed = segments.length > 0 || root ? callback(key, value, path, [...segments]) !== false : true;
|
|
18
|
-
if (proceed && isWalkable(value) && (descend === void 0 || descend({ key, value, path, segments: [...segments] }))) {
|
|
18
|
+
if (proceed && (segments.length === 0 || shallow === false) && isWalkable(value) && (descend === void 0 || descend({ key, value, path, segments: [...segments] }))) {
|
|
19
19
|
if (Array.isArray(value)) {
|
|
20
20
|
return value.every(
|
|
21
21
|
(item, index) => walk(item, createPath(path, index, true), [...segments, index], index)
|
|
@@ -188,14 +188,6 @@ var extract = (source, mapping) => {
|
|
|
188
188
|
return result;
|
|
189
189
|
};
|
|
190
190
|
|
|
191
|
-
// src/map.ts
|
|
192
|
-
var map = (data, callback, store = {}) => {
|
|
193
|
-
each(data, (key, value, path) => {
|
|
194
|
-
callback(key, value, store, path);
|
|
195
|
-
});
|
|
196
|
-
return store;
|
|
197
|
-
};
|
|
198
|
-
|
|
199
191
|
// src/helpers/isPlainObject.ts
|
|
200
192
|
var isPlainObject = (value) => {
|
|
201
193
|
if (value !== null && typeof value === "object") {
|
|
@@ -205,6 +197,30 @@ var isPlainObject = (value) => {
|
|
|
205
197
|
return false;
|
|
206
198
|
};
|
|
207
199
|
|
|
200
|
+
// src/map.ts
|
|
201
|
+
var map = (data, callback, { descend, shallow = false } = {}) => {
|
|
202
|
+
const walk = (value, key, path, segments) => {
|
|
203
|
+
const root = key === void 0;
|
|
204
|
+
let mapped = root ? Array.isArray(value) ? [] : {} : value;
|
|
205
|
+
if ((root || Array.isArray(value) || isPlainObject(value)) && (root || shallow === false) && (descend === void 0 || descend({ key, value, path, segments: [...segments] }))) {
|
|
206
|
+
mapped = Array.isArray(value) ? Array(value.length) : {};
|
|
207
|
+
each(value, (key2, value2) => {
|
|
208
|
+
const childPath = createPath(path, key2, Array.isArray(mapped));
|
|
209
|
+
const childSegments = [...segments, key2];
|
|
210
|
+
const result = walk(value2, key2, childPath, childSegments);
|
|
211
|
+
Object.defineProperty(mapped, key2, {
|
|
212
|
+
value: result,
|
|
213
|
+
writable: true,
|
|
214
|
+
enumerable: true,
|
|
215
|
+
configurable: true
|
|
216
|
+
});
|
|
217
|
+
}, { shallow: true });
|
|
218
|
+
}
|
|
219
|
+
return key !== void 0 ? callback(key, mapped, path) : mapped;
|
|
220
|
+
};
|
|
221
|
+
return walk(data, void 0, "", []);
|
|
222
|
+
};
|
|
223
|
+
|
|
208
224
|
// src/merge.ts
|
|
209
225
|
var unsafeKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
210
226
|
var merge = (target, ...sources) => {
|
package/package.json
CHANGED
package/skills/objwalk/SKILL.md
CHANGED
|
@@ -31,7 +31,7 @@ import { clone, diff, each, equally, extract, flush, map, merge, parse, pick, se
|
|
|
31
31
|
| Delete nested properties | `flush(data, path)`; accepts one path or `string[]`, deletes own properties, mutates and returns `data`; missing paths are left unchanged. |
|
|
32
32
|
| Reshape an object | `extract(source, mapping)`; mapping keys are destination paths, values are source paths. |
|
|
33
33
|
| Visit nested values | `each(data, (key, value, path, segments) => { ... }, options?)`; supports objects and arrays, root is excluded by default; returning `false` stops the entire traversal. |
|
|
34
|
-
|
|
|
34
|
+
| Transform nested values | `map(data, (key, value, path) => replacement, options?)`; returns a new array or dictionary with the same keys. |
|
|
35
35
|
| Deep merge | `merge(target, ...sources)`; mutates and returns the target, merges plain objects, replaces arrays and non-plain values. |
|
|
36
36
|
| Deep copy | `clone(value)`; preserves `Date`, `RegExp`, symbol keys, and circular references. |
|
|
37
37
|
| Deep comparison | `equally(a, b, paths?)`; supports `NaN`, `Date`, `RegExp`, and circular references; optional `string[]` compares only selected paths. |
|
|
@@ -92,7 +92,7 @@ pick(data, 'user.name', {
|
|
|
92
92
|
Object keys are strings; array indexes are numbers. Prefer segments when keys may
|
|
93
93
|
contain dots, brackets, or empty strings; the string `path` can be ambiguous.
|
|
94
94
|
|
|
95
|
-
`EachOptions` accepts `root` (default `false`) and
|
|
95
|
+
`EachOptions` accepts `root` and `shallow` (both default `false`), and
|
|
96
96
|
`descend: ({ key, value, path, segments }) => boolean`.
|
|
97
97
|
With `root: true`, the root is visited first, including primitive/nullish values,
|
|
98
98
|
with key `undefined`, path `''`, and segments `[]`. Callback keys are then typed
|
|
@@ -100,6 +100,16 @@ with key `undefined`, path `''`, and segments `[]`. Callback keys are then typed
|
|
|
100
100
|
`EachCallback<WalkKey | undefined>` for an explicitly typed root-aware callback.
|
|
101
101
|
`EachEntry` and `EachOptions` are also public named exports.
|
|
102
102
|
|
|
103
|
+
Use `each(data, callback, { shallow: true })` to visit only immediate entries.
|
|
104
|
+
Literal shallow mode infers string dictionary keys, numeric array indexes, and
|
|
105
|
+
the input value type. With root visits enabled or a dynamic root flag, key types
|
|
106
|
+
also include `undefined` and value types include the root container. Narrow the
|
|
107
|
+
value union explicitly; key/value parameters are not correlated. `descend` runs
|
|
108
|
+
only at the shallow root. Callback `false` still stops the entire traversal.
|
|
109
|
+
`EachCallback<TKey, TValue>` adds an optional value type parameter, defaulting
|
|
110
|
+
to `any` for existing explicit callback declarations. Dynamic shallow flags and
|
|
111
|
+
unknown inputs retain general callback types.
|
|
112
|
+
|
|
103
113
|
`descend` is called only for containers, after their callback, and returning false
|
|
104
114
|
skips their children. It also runs for a root container excluded from callbacks.
|
|
105
115
|
For example, `descend: ({ value }) => Array.isArray(value) === false` visits arrays
|
|
@@ -108,11 +118,36 @@ the entire traversal, including siblings and ancestor branches; other return val
|
|
|
108
118
|
are ignored. The stopped container's `descend` is not called, and its children and
|
|
109
119
|
subsequent values are not read. Object keys are collected on entering a container;
|
|
110
120
|
values are read as visited. `each` still returns `undefined`. Existing callbacks
|
|
111
|
-
that returned `false` must omit that return to keep walking.
|
|
112
|
-
|
|
113
|
-
Mutating received segments does not affect traversal. Callback/getter/descend errors
|
|
121
|
+
that returned `false` must omit that return to keep walking. Mutating received
|
|
122
|
+
segments does not affect traversal. Callback/getter/descend errors
|
|
114
123
|
propagate. Cycles are not detected.
|
|
115
124
|
|
|
125
|
+
`map(data, callback, options?)` returns a new array for an array root and a new
|
|
126
|
+
ordinary dictionary for a dictionary root. Its callback is `(key, value, path)`;
|
|
127
|
+
return the replacement value. There is no store argument. `false`, `null`, and
|
|
128
|
+
`undefined` are ordinary output values, not traversal control. To accumulate a
|
|
129
|
+
different structure, use `each` with an explicit accumulator.
|
|
130
|
+
|
|
131
|
+
Deep map callbacks run children first, then their containing value; the root is
|
|
132
|
+
excluded. Plain objects and arrays are rebuilt, while non-plain objects such as
|
|
133
|
+
Date and RegExp are passed to the callback as leaves. Callback results are not
|
|
134
|
+
traversed again. Source data is not assigned to, but returned or pruned values
|
|
135
|
+
can retain references. Array holes and literal object keys are preserved.
|
|
136
|
+
|
|
137
|
+
`MapOptions` is the third argument. `descend` receives original containers before
|
|
138
|
+
children are mapped; false prunes their children and passes the original value to
|
|
139
|
+
the callback. It also runs for the excluded root, where false produces an empty
|
|
140
|
+
result of the same kind. Literal `shallow: true` visits only immediate children,
|
|
141
|
+
typing dictionary keys as `string` and array keys as `number`. When both options
|
|
142
|
+
are present, `descend` is evaluated only for the root. Deep/dynamic calls keep
|
|
143
|
+
`WalkKey`; a custom descend predicate alone cannot narrow keys.
|
|
144
|
+
Shallow map also infers the input value type; deep/dynamic callback values stay
|
|
145
|
+
general. `MapCallback<TResult, TKey, TValue>` describes the callback's result,
|
|
146
|
+
key, and input value types. Calls that specify only `map<TResult>` retain the
|
|
147
|
+
default `any` input value type; omit explicit generics for full inference or
|
|
148
|
+
provide both result and value types.
|
|
149
|
+
This is a breaking replacement of the old store-based map, not an overload.
|
|
150
|
+
|
|
116
151
|
## Differences
|
|
117
152
|
|
|
118
153
|
`diff` recursively compares two plain objects by their own enumerable string keys;
|