objwalk 1.8.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,6 +125,21 @@ 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 `{ shallow: true }` to visit dictionary entries as whole values:
129
+
130
+ ```ts
131
+ import { each } from 'objwalk'
132
+
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
+
128
143
  Use `map` to transform values while keeping the object and array structure:
129
144
 
130
145
  ```ts
@@ -154,6 +169,7 @@ const counts = map(
154
169
  ```
155
170
 
156
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.
157
173
  For selective traversal, pass `descend` in the third argument.
158
174
  [Mapping options](SPEC.md#map).
159
175
 
package/SPEC.md CHANGED
@@ -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,9 +241,9 @@ not share stop state.
217
241
  ## map
218
242
 
219
243
  ```ts
220
- type MapCallback<TResult = unknown, TKey extends WalkKey = WalkKey> = (
244
+ type MapCallback<TResult = unknown, TKey extends WalkKey = WalkKey, TValue = any> = (
221
245
  key: TKey,
222
- value: any,
246
+ value: TValue,
223
247
  path: string
224
248
  ) => TResult
225
249
 
@@ -240,7 +264,8 @@ Callback replacements are not traversed again, including returned objects and ar
240
264
  The callback receives `(key, value, path)`. Object keys are strings, array indexes
241
265
  are numbers, and paths follow `each`'s descriptive string format. No accumulator or
242
266
  store argument exists. `MapCallback`'s first generic argument is the return value
243
- rather than the former store type; the optional second argument is the key type.
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`).
244
269
  Array results are typed as `TResult[]`; dictionary results as
245
270
  `Record<string, TResult>`. A dictionary-or-array union input produces the matching
246
271
  union result type. Readonly input arrays are accepted and produce mutable new arrays.
@@ -274,8 +299,14 @@ visits. This timing differs from `each`, which calls its callback before `descen
274
299
 
275
300
  `shallow` defaults to `false`. With literal `shallow: true`, only immediate root
276
301
  children are mapped: dictionary callback keys are typed as `string`, and statically
277
- known array keys as `number`. Deep calls, dynamic shallow flags, and union inputs
278
- keep `WalkKey`. An arbitrary `descend` predicate does not narrow keys. When both
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
279
310
  options are present, `descend` is evaluated only for the root, because child
280
311
  containers are already at the shallow limit.
281
312
 
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)
@@ -251,7 +251,7 @@ var map = (data, callback, { descend, shallow = false } = {}) => {
251
251
  enumerable: true,
252
252
  configurable: true
253
253
  });
254
- }, { descend: ({ segments: segments2 }) => segments2.length === 0 });
254
+ }, { shallow: true });
255
255
  }
256
256
  return key !== void 0 ? callback(key, mapped, path) : mapped;
257
257
  };
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,16 +64,16 @@ 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<TResult = unknown, TKey extends WalkKey = WalkKey> = (key: TKey, value: any, path: string) => TResult;
67
+ type MapCallback<TResult = unknown, TKey extends WalkKey = WalkKey, TValue = any> = (key: TKey, value: TValue, path: string) => TResult;
52
68
  type MapOptions = {
53
69
  descend?: EachOptions['descend'];
54
70
  shallow?: boolean;
55
71
  };
56
72
  type Map = {
57
- <TResult>(data: readonly unknown[], callback: MapCallback<TResult, number>, options: MapOptions & {
73
+ <TResult, TValue = any>(data: readonly TValue[], callback: MapCallback<TResult, number, TValue>, options: MapOptions & {
58
74
  shallow: true;
59
75
  }): TResult[];
60
- <TResult>(data: Record<string, unknown>, callback: MapCallback<TResult, string>, options: MapOptions & {
76
+ <TResult, TValue = any>(data: Record<string, TValue>, callback: MapCallback<TResult, string, TValue>, options: MapOptions & {
61
77
  shallow: true;
62
78
  }): Record<string, TResult>;
63
79
  <TResult>(data: readonly unknown[], callback: MapCallback<TResult>, options?: MapOptions): TResult[];
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,16 +64,16 @@ 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<TResult = unknown, TKey extends WalkKey = WalkKey> = (key: TKey, value: any, path: string) => TResult;
67
+ type MapCallback<TResult = unknown, TKey extends WalkKey = WalkKey, TValue = any> = (key: TKey, value: TValue, path: string) => TResult;
52
68
  type MapOptions = {
53
69
  descend?: EachOptions['descend'];
54
70
  shallow?: boolean;
55
71
  };
56
72
  type Map = {
57
- <TResult>(data: readonly unknown[], callback: MapCallback<TResult, number>, options: MapOptions & {
73
+ <TResult, TValue = any>(data: readonly TValue[], callback: MapCallback<TResult, number, TValue>, options: MapOptions & {
58
74
  shallow: true;
59
75
  }): TResult[];
60
- <TResult>(data: Record<string, unknown>, callback: MapCallback<TResult, string>, options: MapOptions & {
76
+ <TResult, TValue = any>(data: Record<string, TValue>, callback: MapCallback<TResult, string, TValue>, options: MapOptions & {
61
77
  shallow: true;
62
78
  }): Record<string, TResult>;
63
79
  <TResult>(data: readonly unknown[], callback: MapCallback<TResult>, options?: MapOptions): TResult[];
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)
@@ -214,7 +214,7 @@ var map = (data, callback, { descend, shallow = false } = {}) => {
214
214
  enumerable: true,
215
215
  configurable: true
216
216
  });
217
- }, { descend: ({ segments: segments2 }) => segments2.length === 0 });
217
+ }, { shallow: true });
218
218
  }
219
219
  return key !== void 0 ? callback(key, mapped, path) : mapped;
220
220
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "objwalk",
3
- "version": "1.8.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",
@@ -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
@@ -131,7 +141,11 @@ result of the same kind. Literal `shallow: true` visits only immediate children,
131
141
  typing dictionary keys as `string` and array keys as `number`. When both options
132
142
  are present, `descend` is evaluated only for the root. Deep/dynamic calls keep
133
143
  `WalkKey`; a custom descend predicate alone cannot narrow keys.
134
- `MapCallback<TResult, TKey>` describes the callback's returned value and key type.
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.
135
149
  This is a breaking replacement of the old store-based map, not an overload.
136
150
 
137
151
  ## Differences