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 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 `map` when the walk should build a result:
128
+ Use `{ shallow: true }` to visit dictionary entries as whole values:
129
129
 
130
130
  ```ts
131
- import { map, set } from 'objwalk'
131
+ import { each } from 'objwalk'
132
132
 
133
- const cleaned = map({ name: ' Natalia ', tags: [' ts '] }, (_key, value, store, path) => {
134
- if (typeof value === 'string') {
135
- set(store, path, value.trim())
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
- `map` returns its store and ignores callback return values. Both walkers expect
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
- | Build a result while walking | [`map(data, callback, store?)`](SPEC.md#map) |
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, store?)` | Supplied store or a new object | Callback builds or mutates the store |
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` and `map` accept `any`;
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: any,
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. Without root visits, callback keys are
198
- typed as `WalkKey`; enabled or dynamic root options use `WalkKey | undefined`.
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<TStore extends Record<string, any> = Record<string, any>> = (
221
- key: WalkKey,
222
- value: any,
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
- ) => void
226
- ```
248
+ ) => TResult
227
249
 
228
- `map(data, callback, store?)` uses the default `each` traversal for objects and arrays,
229
- excluding the root. The callback receives the original value and the same store
230
- on every visit. A supplied store is returned by identity; otherwise the store is
231
- a new `{}`. The generic store type is preserved in the return type.
250
+ type MapOptions = {
251
+ descend?: EachOptions['descend']
252
+ shallow?: boolean
253
+ }
254
+ ```
232
255
 
233
- All callback return values, including `false`, are ignored. `map` exposes neither
234
- root/descend options nor literal segments. Errors propagate and completed store
235
- mutations remain applied. Cycles are not detected.
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: any, path: string, segments: WalkKey[]) => unknown;
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<TStore extends Record<string, any> = Record<string, any>> = (key: WalkKey, value: any, store: TStore, path: string) => void;
52
- declare const map: <TStore extends Record<string, any> = Record<string, any>>(data: any, callback: MapCallback<TStore>, store?: TStore) => TStore;
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: any, path: string, segments: WalkKey[]) => unknown;
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<TStore extends Record<string, any> = Record<string, any>> = (key: WalkKey, value: any, store: TStore, path: string) => void;
52
- declare const map: <TStore extends Record<string, any> = Record<string, any>>(data: any, callback: MapCallback<TStore>, store?: TStore) => TStore;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "objwalk",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "description": "Small utilities for reading, writing, walking, and reshaping nested JavaScript objects.",
5
5
  "keywords": [
6
6
  "object",
@@ -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
- | Build a store while walking | `map(data, (key, value, store, path) => { ... }, store?)`; returns the store, ignores callback return values. |
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. `map` continues to
112
- ignore callback return values for both objects and arrays.
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;