@peter.naydenov/walk-async 3.1.3 → 6.1.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/.agents/skills/git-walk-async/SKILL.md +173 -0
- package/Changelog.md +26 -0
- package/Migration.guide.md +120 -0
- package/README.md +208 -73
- package/package.json +18 -8
- package/src/main.js +90 -20
- package/types/main.d.ts +115 -22
- package/types/main.d.ts.map +1 -1
- package/rollup.config.js +0 -41
- package/tsconfig.json +0 -11
- package/vitest.config.js +0 -8
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: git-walk-async
|
|
3
|
+
description: |
|
|
4
|
+
Help developers use `@peter.naydenov/walk-async` (the async `walk` from
|
|
5
|
+
the git-walk-async project, v6.0.0): write a deep copy with `await
|
|
6
|
+
walk({ data, keyCallback?, objectCallback?, timeout? })`, use the
|
|
7
|
+
`resolve` / `reject` pair inside callbacks to store or drop values, and
|
|
8
|
+
leverage the `timeout` option as a debugging safety net. Use when the
|
|
9
|
+
developer asks for a deep copy with simultaneous async transforms, a
|
|
10
|
+
deep `forEach` that can `await` work and drop or rewrite values, a
|
|
11
|
+
single-pass async clone that filters keys by name, or anything phrased
|
|
12
|
+
as "async deep copy / async deep clone / async deep walk / async deep
|
|
13
|
+
forEach" where the transforms do real async I/O. Do NOT use for: sync
|
|
14
|
+
walks (point to the sibling `@peter.naydenov/walk` instead),
|
|
15
|
+
deep-copying a `Map` / `Set` / `Date` / typed array (use
|
|
16
|
+
`structuredClone` for those), retry / abort semantics, or fixing bugs
|
|
17
|
+
in the library itself.
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# git-walk-async helper
|
|
21
|
+
|
|
22
|
+
A single-pass deep walk over a JavaScript data structure that runs two
|
|
23
|
+
optional async callbacks during the visit. Each callback can `await`
|
|
24
|
+
work and reports back via `resolve(value)` or `reject()`. A plain deep
|
|
25
|
+
copy is the no-callback case.
|
|
26
|
+
|
|
27
|
+
This is the **async sibling** of `@peter.naydenov/walk`. If your
|
|
28
|
+
callbacks don't actually need `await`, prefer the sync version — it's
|
|
29
|
+
noticeably faster because it avoids the promise machinery.
|
|
30
|
+
|
|
31
|
+
Source of truth:
|
|
32
|
+
- `src/main.js` — JSDoc on every public function and typedef (`walk`, `keyCallback`, `objectCallback`, `CallbackArgs`, `Resolve`, `Reject`, `Options`)
|
|
33
|
+
- `test/01-deepCopy.test.js` — plain deep copy
|
|
34
|
+
- `test/02-keyCallback.test.js` — primitive transforms
|
|
35
|
+
- `test/03-objectCallback.test.js` — object/array transforms
|
|
36
|
+
- `test/04-async.test.js` — real async work in callbacks
|
|
37
|
+
- `test/05-bothCallbacks.test.js` — interaction between the two
|
|
38
|
+
- `test/06-builtins.test.js` — Date / Map / Set / typed arrays / DOM nodes
|
|
39
|
+
- `README.md` — narrative docs (lead paragraph, "When to use `walk-async` vs `structuredClone`" callout, "Migrating from `@peter.naydenov/walk`" table, "Timeout" section, "Limitations")
|
|
40
|
+
|
|
41
|
+
## Procedure
|
|
42
|
+
|
|
43
|
+
1. **Map the developer's intent to the right shape of `await walk({ data, ... })` call**:
|
|
44
|
+
- "Just deep-copy this" / "give me an immutable copy" → `await walk({ data })` (no callbacks)
|
|
45
|
+
- "Drop a few keys while copying" → `await walk({ data, keyCallback })`; call `reject()` for the keys to drop
|
|
46
|
+
- "Mask a value" / "rewrite a value" → `await walk({ data, keyCallback })`; call `resolve(newValue)`
|
|
47
|
+
- "Skip a whole subtree" → `await walk({ data, objectCallback })`; call `reject()` at the right key
|
|
48
|
+
- "Await something inside the walk" (DB lookup, fetch, fs.readFile) → `await walk({ data, keyCallback: async ({ resolve, value, key }) => { const fresh = await fetch(value); resolve(fresh) } })`
|
|
49
|
+
- "Run a function on every leaf" (deep `forEach` with side effects) → `await walk({ data, keyCallback })`; the callback must still call `resolve(value)` (see gotcha below)
|
|
50
|
+
|
|
51
|
+
2. **Generate code that follows the real API contract**:
|
|
52
|
+
- ESM import: `import walk from '@peter.naydenov/walk-async'` (CJS: `require('@peter.naydenov/walk-async')`)
|
|
53
|
+
- **The call returns a Promise** — `await` it, or chain `.then`. Forgetting this is the #1 mistake.
|
|
54
|
+
- Options shape: `{ data, keyCallback?, objectCallback?, timeout? }`. Same names as the sync `walk`, plus `timeout`.
|
|
55
|
+
- Callback signature is `({ value, key, breadcrumbs, resolve, reject }, ...args)`. Always destructure the five fields from the first arg.
|
|
56
|
+
- Extra positional args passed to `walk` are forwarded to both callbacks as `...args`. Use this for context the callbacks need but you don't want to close over.
|
|
57
|
+
- **There is no `IGNORE` symbol.** The way to drop a key is `reject()` — that's it. The internal `IGNORE = Symbol('ignore___')` is private implementation detail; do not import, construct, or return it from user code. Returning it from a callback is a bug (it would be treated as a value to store, not a drop signal).
|
|
58
|
+
- The callback MUST call `resolve(...)` or `reject()` on every code path. If it doesn't, the walk promise never settles. The `timeout` option is the recommended way to catch this in development.
|
|
59
|
+
- `resolve` accepts anything a sync `walk` would accept: a primitive, a built-in like `Date` / `Map` / `Set`, or a plain object/array. A plain object/array is walked into (deferred). A `Date` / `Map` / `Set` is stored by reference.
|
|
60
|
+
|
|
61
|
+
3. **Apply the order-of-execution rules**:
|
|
62
|
+
- Within one level, keys are visited in `Object.keys` order on the current object/array.
|
|
63
|
+
- A level finishes before any nested walk starts. The new walk into a returned object/array is deferred.
|
|
64
|
+
- For the same value, `objectCallback` runs before `keyCallback`. If `objectCallback` resolves a new object, that object is what `keyCallback` sees when it processes the children.
|
|
65
|
+
- The root goes through `objectCallback` first (if defined), then its children are walked. Rejecting from the root `objectCallback` short-circuits the whole walk to `[]` (for array roots) or `{}` (for object roots).
|
|
66
|
+
- **Within a level, all keys are started concurrently** — they don't wait for each other to resolve before the next one is fired. They just have to finish before the next level begins. (This is the part where the async version earns its keep — `await` work in callbacks runs in parallel for free.)
|
|
67
|
+
|
|
68
|
+
4. **Surface only the relevant gotcha proactively** — pick at most one from the list below that applies to the current example, and only if the user is unlikely to know it:
|
|
69
|
+
- **The biggest trap: `walk-async` is `await`able.** A no-`await` call silently drops the result. `walk(...)` alone returns a Promise, not the copy.
|
|
70
|
+
- **Don't return values from callbacks.** Returning `value` or `IGNORE` from a callback is a sync-API leak — the walk ignores the return. The right call is `resolve(value)` or `reject()`. If a user just ported from sync `walk` and writes `return value` in their callback, their result will be `{}` (the empty container, because nothing was ever stored).
|
|
71
|
+
- **Callbacks must call `resolve` or `reject` on every code path.** A path that returns early or `throw`s without calling either will leave the walk promise pending forever. Either wire every path explicitly, or set `timeout` during development to surface these as a clear rejection with breadcrumbs of the stuck callbacks.
|
|
72
|
+
- **Don't `await walk-async` from inside a callback.** That's the first item on the Limitations list in the README — it corrupts the in-progress walk. If you need recursion, let the library handle it by `resolve`-ing a plain object/array from the callback.
|
|
73
|
+
- **Built-in types are not walked.** `Date`, `RegExp`, `Map`, `Set`, `WeakMap`, `WeakSet`, `ArrayBuffer`, `DataView`, typed arrays, DOM nodes, and functions are passed by reference. If the user wants a deep clone of a `Map`/`Set`/typed array, recommend `structuredClone` for that subtree, or do it themselves before calling `walk-async`.
|
|
74
|
+
- **`'simple'` is the storage type, not the return type.** A plain object or array resolved from `keyCallback` is re-typed and walked into (deferred). A `Date` resolved from `keyCallback` is `'simple'` and stored by reference. This is the v6.0.0 contract.
|
|
75
|
+
|
|
76
|
+
5. **If the request is "I just need a deep clone with no transforms"** (or the user doesn't actually need async), point them at `structuredClone` first (built into the platform, no dependency, no promise), and mention the sync `walk` as a fallback. Only use `walk-async` when real async I/O is happening inside the callbacks.
|
|
77
|
+
|
|
78
|
+
6. **If the request is for a sync walk**, route to `@peter.naydenov/walk` (separate package, similar interface, no promise machinery). The two are kept in sync semantically — same callback args, same three-outcome contract — but the sync version is faster when you don't need `await`.
|
|
79
|
+
|
|
80
|
+
## Output contract
|
|
81
|
+
|
|
82
|
+
- One focused code snippet, ESM by default (CJS if asked)
|
|
83
|
+
- The snippet must `await` the `walk` call (or chain `.then`); never produce a snippet that calls `walk` and ignores the returned Promise
|
|
84
|
+
- One line of context explaining which option(s) are used and why
|
|
85
|
+
- A pointer to the relevant source/test section if the developer wants to dig deeper
|
|
86
|
+
- Surface at most one relevant gotcha proactively, only if it applies to the example
|
|
87
|
+
- Never include a code example that uses `return value` / `return IGNORE` from a callback (those are the sync API)
|
|
88
|
+
- Never include a code example that `await`s `walk-async` from inside one of its own callbacks
|
|
89
|
+
|
|
90
|
+
## Failure handling
|
|
91
|
+
|
|
92
|
+
- The developer's use case genuinely ambiguous (e.g., "deep clone this") → point them at `structuredClone` for the no-deps no-callback case, then `walk-async` only if they want callbacks that `await`
|
|
93
|
+
- Developer reports a bug or unexpected behavior in `walk-async` itself → do NOT try to fix from this skill; route to the project source or maintainer
|
|
94
|
+
- Developer wants a walk that never settles, or hits the timeout, or sees the "callbacks still pending" error → point at the `Timeout` section in the README and the callback "every code path resolves" rule
|
|
95
|
+
- Developer asks for an API `walk-async` doesn't have (e.g., `walk.pick(...)`, `walk.omit(...)`) → do not invent it; point at the callback-factory pattern in the README ("Why one callback, not a list of methods" section) where such helpers belong outside the library
|
|
96
|
+
|
|
97
|
+
## Examples
|
|
98
|
+
|
|
99
|
+
**"Fetch fresh data for every URL string in the object, drop the rest"**
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
import walk from '@peter.naydenov/walk-async'
|
|
103
|
+
|
|
104
|
+
const result = await walk({
|
|
105
|
+
data: source,
|
|
106
|
+
keyCallback: async ({ value, resolve, reject }) => {
|
|
107
|
+
if (typeof value !== 'string' || !/^https?:\/\//.test(value)) {
|
|
108
|
+
reject() // drop non-URL primitives
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const r = await fetch(value)
|
|
113
|
+
resolve(await r.json())
|
|
114
|
+
} catch (e) {
|
|
115
|
+
reject() // drop on network failure
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
timeout: 5000, // surfaces forgotten resolve/reject with breadcrumbs
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`await` is mandatory — without it you have a Promise, not a result. `reject()` (no argument) is the only way to drop a key. The `timeout` is a debugging safety net; remove or raise it for production. See `test/04-async.test.js` for the executable pattern.
|
|
123
|
+
|
|
124
|
+
**"Drop a whole `metadata` subtree"**
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
import walk from '@peter.naydenov/walk-async'
|
|
128
|
+
|
|
129
|
+
const result = await walk({
|
|
130
|
+
data: user,
|
|
131
|
+
objectCallback: ({ key, value, resolve, reject }) =>
|
|
132
|
+
key === 'metadata' ? reject() : resolve(value)
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Rejecting from `objectCallback` drops the entire subtree (not just the immediate property). `keyCallback` is not used here, so primitive keys inside `metadata` are never visited. See `test/03-objectCallback.test.js`.
|
|
137
|
+
|
|
138
|
+
**"Deep `forEach` — log every leaf"**
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
import walk from '@peter.naydenov/walk-async'
|
|
142
|
+
|
|
143
|
+
await walk({
|
|
144
|
+
data: tree,
|
|
145
|
+
keyCallback: ({ value, key, breadcrumbs, resolve }) => {
|
|
146
|
+
console.log(`${breadcrumbs}/${key} = ${value}`)
|
|
147
|
+
resolve(value) // pass-through — required, otherwise this key is dropped
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The callback MUST call `resolve` or `reject` on every code path. A missing call leaves the walk pending — if you suspect this in your own code, set `timeout: 1000` during dev and look at which callback the rejection names. The `breadcrumbs` argument is a slash-delimited path starting with `'root'`, e.g. `'root/props/age'`. See "Deep 'forEach'" in `README.md`.
|
|
153
|
+
|
|
154
|
+
**"Port a sync `walk` call to `walk-async`"**
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
// walk (sync)
|
|
158
|
+
const result = walk ({
|
|
159
|
+
data: user,
|
|
160
|
+
keyCallback: ({ value, key, IGNORE }) => key === 'password' ? IGNORE : value
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// walk-async — same shape, just promise-based
|
|
164
|
+
const result = await walk ({
|
|
165
|
+
data: user,
|
|
166
|
+
keyCallback: ({ value, key, resolve, reject }) => {
|
|
167
|
+
if ( key === 'password' ) reject () // was: return IGNORE
|
|
168
|
+
else resolve ( value ) // was: return value
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The mechanical translation: `return X` → `resolve(X)`, `return IGNORE` → `reject()`. Add `await` to the call. Add `timeout` during dev to surface any path that forgets to resolve/reject. The "Migrating from `@peter.naydenov/walk`" section in `README.md` has the full table.
|
package/Changelog.md
CHANGED
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
### 6.1.0 (2026-09-01)
|
|
6
|
+
- [x] Feature: Added a skill at `.agents/skills/git-walk-async/SKILL.md`;
|
|
7
|
+
- [x] Packaging: Added `.agents/skills` to the `"files"` allow-list in `package.json` so the Mavis skill ships with the npm package. AI-agent users can discover the walk-async skill from `node_modules` directly, without needing access to the source repo. The same allow-list also drops the dev-only `rollup.config.js`, `tsconfig.json`, and `vitest.config.js` that were leaking under the previous `.npmignore`-only packaging;
|
|
8
|
+
- [x] Docs: Prefilled `Migration.guide.md` for every major release: sync-`@peter.naydenov/walk` → `walk-async` (the README's "Migrating from `@peter.naydenov/walk`" table, folded in), v3.x → v6.x (callback signatures tightened with exported `Resolve` / `Reject` typedefs), v3.0.x → v3.1.x (the `timeout` option, the built-in-type pass-by-reference fix from v3.1.2, and the v3.0.6/v3.0.7 root-callback bug fixes), the existing v2.x → v3.x root-callback entry, v1.x → v2.x (ES module + `dist` + `exports`), and v1.0.0 → v1.x (the early null/undefined, DOM-node, and collection-container fixes). The README still has a copy of the sync-`walk` → `walk-async` table — flag for a follow-up if you want the README trimmed to a one-liner pointer so the docs aren't split across two files;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### 6.0.0 (2026-08-08)
|
|
13
|
+
- [x] Allign version numbers with `@peter.naydenov/walk`. We jumping from 3.1.3 to 6.0.0;
|
|
14
|
+
- [x] Types: Tighten callback signatures in `types/main.d.ts`. `keyCallback` and `objectCallback` are now typed as `KeyCallback` / `ObjectCallback` over a `CallbackArgs` shape (`value`, `key`, `breadcrumbs`, `resolve`, `reject`); `resolve` and `reject` are exported as `Resolve` / `Reject` types so TypeScript users get autocomplete. Driven from JSDoc in `src/main.js`; regenerate with `npm run build`;
|
|
15
|
+
- [x] Docs: Reframe the lead paragraph so the deep copy reads as a side-effect and the callback-driven modifications as the headline;
|
|
16
|
+
- [x] Docs: Add a "When to use `walk-async` vs `structuredClone`" callout that points to the sync sibling `@peter.naydenov/walk` for the no-async case;
|
|
17
|
+
- [x] Docs: Add a "Built-in types" subsection that documents how `Date`, `RegExp`, `Map`, `Set`, `WeakMap`, `WeakSet`, `ArrayBuffer`, `DataView`, typed arrays, DOM nodes, and functions are passed by reference;
|
|
18
|
+
- [x] Docs: Tighten the `objectCallback` section to enumerate the three return-value outcomes (resolve with object/array, resolve with primitive, `reject()`);
|
|
19
|
+
- [x] Docs: Rewrite the "keyCallback" section with the three-outcome return contract, replacing the misleading "value: Only primitives" comment;
|
|
20
|
+
- [x] Docs: Add a "Skip a branch" subsection that documents calling `reject()` from `objectCallback` to drop an entire subtree;
|
|
21
|
+
- [x] Docs: Reframe the `keyCallback` intro so "forEach" is the central concept, not a secondary use case;
|
|
22
|
+
- [x] Docs: Add a "Why one callback, not a list of methods" section that explains the single-pass architecture (matters more for async — extra passes multiply awaited I/O cost) and points users toward callback factories;
|
|
23
|
+
- [x] Docs: Add an "Order of execution" section that makes the key invariants visible up front (level-internal key order, deferred nested walks, `objectCallback` before `keyCallback`, root behavior, concurrent key starts);
|
|
24
|
+
- [x] Docs: Add a "Migrating from `@peter.naydenov/walk`" section with a side-by-side mapping table (`return value` → `resolve(value)`, `return IGNORE` → `reject()`, etc.) so the sync→async move is mechanical;
|
|
25
|
+
- [x] Docs: Align the section ordering with `@peter.naydenov/walk` (Order of execution → callbacks → Why one callback → Installation → How to use it → Migrating → Timeout → Limitations → See also);
|
|
26
|
+
- [x] Docs: Add a "See also" block that explicitly positions `walk-async` next to its sync sibling;
|
|
27
|
+
- [x] Tests: Add 9 tests in `test/02-keyCallback.test.js` covering plain-object resolution, array resolution, `Date` / `Map` passed by reference, `reject()` drops, order preservation, primitive leaf, and full nested walk with arrays;
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
5
31
|
## 3.1.3 (2026-07-31)
|
|
6
32
|
- [x] Dependencies updates. Ask-for-promise to version 3.2.0;
|
|
7
33
|
|
package/Migration.guide.md
CHANGED
|
@@ -1,8 +1,94 @@
|
|
|
1
1
|
# Migration Guides
|
|
2
2
|
|
|
3
|
+
Upgrade notes for non-trivial `walk-async` releases. For the full per-release list of changes, see the [Changelog](Changelog.md); this guide focuses on what *you* have to change in your code.
|
|
4
|
+
|
|
5
|
+
Most common first, then version history newest-first.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## From `@peter.naydenov/walk` (sync sibling)
|
|
10
|
+
|
|
11
|
+
`walk-async` is the async sibling of [`@peter.naydenov/walk`](https://github.com/PeterNaydenov/walk). They share the same callback parameter names (`value`, `key`, `breadcrumbs`) and the same three-outcome contract. The only mechanical change is how you express the return value:
|
|
12
|
+
|
|
13
|
+
| `walk` (sync) | `walk-async` (async) |
|
|
14
|
+
| -------------------------------- | ---------------------------- |
|
|
15
|
+
| `return value` | `resolve(value)` |
|
|
16
|
+
| `return newObject` | `resolve(newObject)` |
|
|
17
|
+
| `return IGNORE` | `reject()` |
|
|
18
|
+
|
|
19
|
+
The options shape is identical: `{ data, keyCallback, objectCallback }`. `walk-async` adds an optional `timeout` (milliseconds). The walk call itself becomes an `await` (or `.then`).
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// walk
|
|
23
|
+
const result = walk ({
|
|
24
|
+
data: user,
|
|
25
|
+
keyCallback: ({ value, key, IGNORE }) => key === 'password' ? IGNORE : value
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// walk-async — same shape, just promise-based
|
|
29
|
+
const result = await walk ({
|
|
30
|
+
data: user,
|
|
31
|
+
keyCallback: ({ value, key, resolve, reject }) => {
|
|
32
|
+
if ( key === 'password' ) reject ()
|
|
33
|
+
else resolve ( value )
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If your callbacks don't actually need async work, prefer the sync `walk` — it's noticeably faster because it avoids the promise machinery. Reach for `walk-async` only when you have real async work inside the callbacks (database lookups, network calls, file reads, etc.) and the cost of multiple `walk` calls would be unacceptable.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## From v.3.x.x - v.6.x.x
|
|
43
|
+
|
|
44
|
+
v6 is a version-alignment release with `@peter.naydenov/walk` (the sync sibling). **Behaviour is unchanged from v.3.1.3** — every v3.x callback that worked in v3.1.3 still works identically in v6.x. The only user-facing change is on the TypeScript side.
|
|
45
|
+
|
|
46
|
+
### Callback signatures are now strictly typed
|
|
47
|
+
|
|
48
|
+
`keyCallback` and `objectCallback` are now typed as `KeyCallback` / `ObjectCallback` over a `CallbackArgs` shape, and `resolve` / `reject` are exported as `Resolve` / `Reject` typedefs so TypeScript users get autocomplete. If your callbacks already called `resolve(value)` / `reject()` correctly, they keep working unchanged at runtime; TypeScript will now catch signature mistakes at compile time.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// v3.x.x - call signatures inferred from usage; no Resolve/Reject typedefs
|
|
52
|
+
function keyCallback ({ value, key, resolve, reject }) {
|
|
53
|
+
if (key === 'password') reject()
|
|
54
|
+
else resolve(value)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// v6.x.x - same code, now type-checked against the exported Resolve / Reject
|
|
58
|
+
import type { KeyCallback, Resolve, Reject } from '@peter.naydenov/walk-async'
|
|
59
|
+
const keyCallback: KeyCallback = ({ value, key, resolve, reject }) => {
|
|
60
|
+
if (key === 'password') reject()
|
|
61
|
+
else (resolve as Resolve)(value)
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If you're on JavaScript (not TypeScript), there is nothing to change.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
## From v.3.0.x - v.3.1.x
|
|
70
|
+
|
|
71
|
+
A new optional `timeout` (milliseconds) was added. When set, the walk promise is rejected if any callback has not called `resolve` or `reject` in time; the error message lists the breadcrumbs of every still-pending callback so the broken code path can be found directly.
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// v3.0.x
|
|
75
|
+
const result = await walk ({ data, keyCallback }) // hangs forever if a callback forgets to resolve
|
|
76
|
+
|
|
77
|
+
// v3.1.x
|
|
78
|
+
const result = await walk ({ data, keyCallback, timeout: 5000 })
|
|
79
|
+
// if a callback never settles, the walk promise is rejected with:
|
|
80
|
+
// walk-async: timed out after 5000ms; callbacks still pending:
|
|
81
|
+
// - keyCallback at 'root/props/age'
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This is purely additive — the option is optional, off by default, and identical to v3.0.x when omitted. The pre-existing v3.0.6 and v3.0.7 bug fixes (`__proto__` no longer replacing the copy's prototype; top-level property named `root` no longer dropped; `objectCallback` resolving the root with a primitive no longer crashes) shipped in this window. Correct code gets more lenient, not more strict.
|
|
85
|
+
|
|
86
|
+
This is also the window when `Date`, `RegExp`, `Map`, `Set`, `WeakMap`, `WeakSet`, `ArrayBuffer`, `DataView`, and typed arrays became passed by reference (v.3.1.2). They used to come out as empty `{}` — that was almost always a bug, so the fix is a no-op for working code, but if you were somehow relying on the broken behaviour, this is the release that changed it.
|
|
87
|
+
|
|
3
88
|
|
|
4
89
|
|
|
5
90
|
## From v.2.x.x - v.3.x.x
|
|
91
|
+
|
|
6
92
|
Difference between v.2.x.x and v.3.x.x is that object callbacks are triggered on 'root' object as well. So if you have object callbacks that are not working as expected after upgrade, you need to add extra line of code for version 3.
|
|
7
93
|
|
|
8
94
|
```js
|
|
@@ -21,3 +107,37 @@ function oCallbackFn ({ value:o, key, IGNORE, breadcrumbs }) {
|
|
|
21
107
|
Everything else works the same.
|
|
22
108
|
|
|
23
109
|
|
|
110
|
+
|
|
111
|
+
## From v.1.x.x - v.2.x.x
|
|
112
|
+
|
|
113
|
+
v2 converted the package to a proper ES module and added a built `dist/` (CommonJS, ESM, UMD) plus a `package.json` `exports` map and a Rollup build pipeline.
|
|
114
|
+
|
|
115
|
+
**For most users the upgrade is automatic** — your bundler / runtime picks the right build from the `exports` map. The only breaking case is if you were reaching into the package by path:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
// v1.x.x - CommonJS; the package exports a function
|
|
119
|
+
const walk = require ( '@peter.naydenov/walk-async' )
|
|
120
|
+
|
|
121
|
+
// v2.x.x - ES module; the default export is the walk function
|
|
122
|
+
import walk from '@peter.naydenov/walk-async'
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
If you were loading source directly (`require('@peter.naydenov/walk-async/src/main')` or similar), use the package's `exports` map (`@peter.naydenov/walk-async/src/main`) instead, or just import the default.
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
## From v.1.0.0 - v.1.x.x
|
|
130
|
+
|
|
131
|
+
v1.x added a series of small but important fixes. If you are still on v.1.0.0, the relevant ones are:
|
|
132
|
+
|
|
133
|
+
### v.1.1.0 - `null` and `undefined` resolution
|
|
134
|
+
|
|
135
|
+
Returning `null` or `undefined` from a callback is now stored as the literal value (not treated as "skip"). If you were relying on `null` to mean "drop this key", switch to `reject()` (in v6.x) or to whatever the v1.x equivalent was — the API has since changed.
|
|
136
|
+
|
|
137
|
+
### v.1.2.0 - DOM nodes copied by reference
|
|
138
|
+
|
|
139
|
+
`walk-async` no longer descends into HTML DOM nodes — they are passed through by reference. This is the same contract as functions, and (added later in v.3.1.2) `Date` / `Map` / `Set` / `RegExp` / typed arrays.
|
|
140
|
+
|
|
141
|
+
### v.1.3.0 - `undefined`-valued keys preserved; collection containers
|
|
142
|
+
|
|
143
|
+
Keys whose value is `undefined` are now copied (they were being dropped in v.1.0.0). Also, callbacks can now extract data into provided collection containers during iteration.
|
package/README.md
CHANGED
|
@@ -5,59 +5,61 @@
|
|
|
5
5
|

|
|
6
6
|

|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
`walk-async` visits every member of a JavaScript data structure **once**, in a single pass. Two callbacks run during the visit:
|
|
9
|
+
|
|
10
|
+
- `objectCallback` fires on every object or array (including the root)
|
|
11
|
+
- `keyCallback` fires on every primitive property
|
|
12
|
+
|
|
13
|
+
Both callbacks can `resolve` a new value (mask, filter, or substitute) or `reject` to drop the key. Callbacks are async — they can `await` work, talk to the network, read files, anything — and the walk is built so a single pass collects all the transformations you need. The deep copy of the original data is mostly a side-effect: the real power of `walk-async` is the ability to do all your modifications in one pass, with full freedom inside a single callback.
|
|
9
14
|
|
|
10
15
|
```js
|
|
11
16
|
|
|
12
17
|
walk ({
|
|
13
|
-
data // (required) Any JS
|
|
14
|
-
, objectCallback // (optional) Function executed on each object property
|
|
15
|
-
, keyCallback // (optional) Function executed on each primitive property
|
|
16
|
-
, timeout // (optional) Milliseconds. Reject the promise if callbacks do not resolve in time
|
|
18
|
+
data // (required) Any JS data structure;
|
|
19
|
+
, objectCallback // (optional) Function executed on each object/array property;
|
|
20
|
+
, keyCallback // (optional) Function executed on each primitive property;
|
|
21
|
+
, timeout // (optional) Milliseconds. Reject the promise if callbacks do not resolve in time;
|
|
17
22
|
})
|
|
18
23
|
.then ( result => {
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// - if callbacks are resolved with "value" without modification
|
|
24
|
+
// `result` is an immutable copy of `data` if callbacks are absent
|
|
25
|
+
// or resolve with the original `value` unchanged.
|
|
22
26
|
})
|
|
23
27
|
```
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- `walk-async` keyCallback can return as a result object, array or primitives.
|
|
29
|
-
- `walk` keyCallback can return only primitives;
|
|
30
|
-
- `walk` can not execute another walk call inside callback functions;
|
|
29
|
+
> **When to use `walk-async` vs `structuredClone`.**
|
|
30
|
+
> `structuredClone` is built into modern browsers and Node 17+ and is the right tool when you just need a deep copy. Reach for `walk-async` when you want to **modify particular properties, set values conditionally, or skip whole branches** during the copy — all in a single pass over the data — and your callbacks need to do async work. If you don't need callbacks, or you don't need async, `walk-async` is overkill: use `structuredClone` for the simple case, or use the sibling [`@peter.naydenov/walk`](https://github.com/PeterNaydenov/walk) for sync callbacks.
|
|
31
|
+
|
|
31
32
|
|
|
33
|
+
## Order of execution
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
- string;
|
|
35
|
-
- number;
|
|
36
|
-
- bigint;
|
|
37
|
-
- boolean;
|
|
38
|
-
- symbol;
|
|
39
|
-
- null;
|
|
40
|
-
- undefined;
|
|
41
|
-
- array;
|
|
42
|
-
- object(data only);
|
|
43
|
-
- function;
|
|
35
|
+
A few invariants to keep in mind — they shape the order in which your callbacks fire and are easier to reason about up front than to discover in the source:
|
|
44
36
|
|
|
45
|
-
|
|
37
|
+
- **Within one level, keys are visited in `Object.keys` order.** That's the order of own enumerable string keys on the current object/array.
|
|
38
|
+
- **A level finishes before any nested walk starts.** When `walk-async` hits an object/array value (original or returned by a callback), it allocates the result container and defers the nested walk. The nested walk runs after the current level's iteration completes. New objects/arrays returned by `keyCallback` or `objectCallback` are scheduled the same way, so the iteration order of the current level is preserved.
|
|
39
|
+
- **`objectCallback` runs before `keyCallback` on the same value.** For every object/array, `objectCallback` fires first; if it resolves with a new object, that object is what gets walked; its primitive children are then handed to `keyCallback`.
|
|
40
|
+
- **The root is treated as a normal object/array.** When `data` is an object/array, the root goes through `objectCallback` first (if defined), then its children are walked. Rejecting from the root `objectCallback` short-circuits the whole walk to an empty result.
|
|
46
41
|
|
|
42
|
+
Because callbacks are async, the same level's keys are started concurrently in `Object.keys` order — they don't have to wait for each other to resolve before the next one is fired. They just have to finish before the next level begins.
|
|
47
43
|
|
|
48
44
|
|
|
49
|
-
##
|
|
50
|
-
|
|
45
|
+
## keyCallback
|
|
46
|
+
`walk-async` visits each member of the data once. `keyCallback` fires on every primitive property — types: *string, number, bigint, boolean, symbol, null, undefined, function*. Object and array values are visited by `objectCallback` instead (see below). Built-in types like `Date`, `Map`, `Set`, and typed arrays arrive at `keyCallback` as primitives and are passed through by reference — see [Built-in types](#built-in-types-date-regexp-map-set-typed-arrays-etc) below.
|
|
47
|
+
|
|
48
|
+
The value you `resolve` becomes the new value at that key:
|
|
49
|
+
|
|
50
|
+
- **Resolve a primitive (or a built-in like `Date` / `Map` / `Set`)** → stored as-is. Walk does not descend into it;
|
|
51
|
+
- **Resolve a plain object or array** → walk continues into it with `objectCallback` and `keyCallback` applied to its children (same as any original nested object/array). The new walk is deferred via the `extend` mechanism, so the iteration order of the current level is preserved;
|
|
52
|
+
- **`reject()`** → that key is dropped from the result.
|
|
51
53
|
|
|
52
54
|
```js
|
|
53
55
|
function keyCallbackFn ({ value, key, breadcrumbs, resolve, reject }) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
// value: value of the property;
|
|
57
|
+
// key: key of the property;
|
|
58
|
+
// breadcrumbs: location of the property;
|
|
59
|
+
// resolve: function. Call it with the new value to store at that key;
|
|
60
|
+
// reject: function. Call it to drop the key from the result;
|
|
61
|
+
// Important: key callback should be resolved or rejected on every code path,
|
|
62
|
+
// otherwise the walk promise never settles. See section "Timeout".
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
const result = await walk ({ data, keyCallback : keyCallbackFn }); // It's the short way to provide only key-callback. Callback functions are optional.
|
|
@@ -65,25 +67,29 @@ const result = await walk ({ data, keyCallback : keyCallbackFn }); // It's the
|
|
|
65
67
|
```
|
|
66
68
|
|
|
67
69
|
|
|
68
|
-
##
|
|
70
|
+
## objectCallback
|
|
69
71
|
|
|
70
|
-
Optional callback function that is started on each object
|
|
72
|
+
Optional callback function that is started on each object or array property, including the root. The value you `resolve` becomes the new value at that key:
|
|
73
|
+
|
|
74
|
+
- **Resolve an object or array** → walk continues into it with the other callbacks applied to its children;
|
|
75
|
+
- **Resolve a primitive** (string, number, `null`, etc.) → it is stored at that key as-is, and walk does not descend into it (primitives have no children to walk);
|
|
76
|
+
- **`reject()`** → that key is dropped from the result.
|
|
71
77
|
|
|
72
78
|
```js
|
|
73
79
|
function objectCallbackFn ({ value, key, breadcrumbs, resolve, reject }) {
|
|
74
|
-
// value: each object
|
|
75
|
-
// key: key of the object
|
|
80
|
+
// value: each object/array during the walk;
|
|
81
|
+
// key: key of the object/array;
|
|
76
82
|
// breadcrumbs: location of the object;
|
|
77
|
-
// resolve: function
|
|
78
|
-
// reject: function
|
|
83
|
+
// resolve: function. Call it with the new value to store at that key;
|
|
84
|
+
// reject: function. Call it to drop the key from the result;
|
|
79
85
|
// Important: Object callback should be resolved or rejected on every code path,
|
|
80
86
|
// otherwise the walk promise never settles. See section "Timeout".
|
|
81
87
|
}
|
|
82
88
|
|
|
83
|
-
walk ({
|
|
89
|
+
walk ({
|
|
84
90
|
data
|
|
85
91
|
, keyCallback: keyCallbackFn
|
|
86
|
-
, objectCallback : objectCallbackFn
|
|
92
|
+
, objectCallback : objectCallbackFn
|
|
87
93
|
})
|
|
88
94
|
.then ( resultOfWalk => {
|
|
89
95
|
// do something with the result of walk
|
|
@@ -92,37 +98,38 @@ walk ({
|
|
|
92
98
|
|
|
93
99
|
**IMPORTANT: Object-callbacks are executed always before key-callbacks. If we have both callbacks, then key-callbacks will be executed on the result of object-callback.**
|
|
94
100
|
|
|
95
|
-
Skip key-callbacks by not providing a keyCallback function
|
|
101
|
+
Skip key-callbacks by not providing a keyCallback function:
|
|
96
102
|
```js
|
|
97
103
|
let result = await walk ({ data }) // ignore key-callbacks
|
|
98
104
|
```
|
|
99
105
|
|
|
100
106
|
|
|
101
|
-
##
|
|
107
|
+
## Why one callback, not a list of methods
|
|
102
108
|
|
|
103
|
-
|
|
109
|
+
`walk-async` is built around a single pass over the data — every member is visited exactly once, no matter how many transformations the callbacks perform. This is the whole reason the API exposes a callback (or two, for objects vs primitives) and not a bag of pre-built methods like `omit`, `pick`, or `set`.
|
|
110
|
+
|
|
111
|
+
Each pre-built method would be another pass over the data: dropping one key, then renaming another, then masking a third — that's three O(n) cycles where one would do. With async callbacks the cost is even worse, because each extra pass pays the full round-trip of every awaited I/O operation. The cost compounds with every method you chain, and on large data it gets expensive fast.
|
|
112
|
+
|
|
113
|
+
A single `keyCallback` (and optionally `objectCallback`) lets you do every transformation you need in the same pass — drop a key, mask a value, rename another, conditionally remove a subtree, await an API call to enrich a field — all together, no extra cycles.
|
|
114
|
+
|
|
115
|
+
If you find yourself wanting a named transformation you can reuse, the natural place for it is a **callback factory**: a function that returns a `keyCallback` / `objectCallback`. Such factories can live in a separate package; they don't need to extend `walk-async` itself.
|
|
104
116
|
|
|
105
117
|
```js
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
118
|
+
// Example callback factory — not part of walk-async
|
|
119
|
+
function omitKeys (...keysToDrop) {
|
|
120
|
+
const set = new Set ( keysToDrop )
|
|
121
|
+
return ({ key, value, resolve, reject }) => {
|
|
122
|
+
if ( set.has ( key ) ) reject ()
|
|
123
|
+
else resolve ( value )
|
|
109
124
|
}
|
|
125
|
+
}
|
|
110
126
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// - objectCallback at 'root/props'
|
|
116
|
-
})
|
|
127
|
+
const result = await walk ({
|
|
128
|
+
data: user
|
|
129
|
+
, keyCallback: omitKeys ( 'password', 'token' )
|
|
130
|
+
})
|
|
117
131
|
```
|
|
118
132
|
|
|
119
|
-
The error message lists the breadcrumbs of every callback that was started but never resolved or rejected, so the broken code path can be found directly.
|
|
120
|
-
|
|
121
|
-
Notes:
|
|
122
|
-
- `timeout` is disabled by default. Without it the behavior is exactly as before;
|
|
123
|
-
- The limit applies to the whole walk, not to a single callback. Set it well above the worst-case duration of the legitimate async work inside the callbacks — it is a debugging safety net, not a scheduler;
|
|
124
|
-
- When callbacks finish in time, the timer is cleared and the result is delivered as usual.
|
|
125
|
-
|
|
126
133
|
|
|
127
134
|
## Installation
|
|
128
135
|
|
|
@@ -146,13 +153,12 @@ import walk from '@peter.naydenov/walk-async'
|
|
|
146
153
|
**Installation for browsers**: Get the file `"dist/walk-async.min.js"` and put it inside the project. Request the file from HTML page. Global variable 'walk' is available for use.
|
|
147
154
|
|
|
148
155
|
Note:
|
|
149
|
-
Library is using 'generator functions'. If support for old browsers
|
|
156
|
+
Library is using 'generator functions'. If support for old browsers
|
|
150
157
|
is required, add a polyfill for 'generators'.
|
|
151
158
|
|
|
152
159
|
|
|
153
160
|
|
|
154
161
|
|
|
155
|
-
|
|
156
162
|
## How to use it
|
|
157
163
|
|
|
158
164
|
### Deep copy
|
|
@@ -161,8 +167,30 @@ const myCopy = await walk ({ data:x }) // where x is some javascript data stru
|
|
|
161
167
|
```
|
|
162
168
|
|
|
163
169
|
|
|
170
|
+
### Built-in types (Date, RegExp, Map, Set, typed arrays, etc.)
|
|
171
|
+
`walk-async` operates on the own-enumerable-string-key model. Values whose data lives outside that model are **preserved by reference** — the same instance appears in the result:
|
|
172
|
+
|
|
173
|
+
| Type | Behavior |
|
|
174
|
+
| ---------------------------------------- | ----------------------------------------- |
|
|
175
|
+
| `Date`, `RegExp` | Passed by reference |
|
|
176
|
+
| `Map`, `Set`, `WeakMap`, `WeakSet` | Passed by reference |
|
|
177
|
+
| `ArrayBuffer`, `DataView`, typed arrays | Passed by reference |
|
|
178
|
+
| DOM nodes (`HTMLElement`, etc.) | Passed by reference |
|
|
179
|
+
| Functions | Passed by reference |
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
const x = { when: new Date ( '2024-01-15' ), tags: new Set ([ 'js' ]) }
|
|
183
|
+
const r = await walk ({ data: x })
|
|
184
|
+
r.when === x.when // true — same Date instance
|
|
185
|
+
r.tags === x.tags // true — same Set instance
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
This is the same contract used by `function` values and DOM nodes. If you need a deep copy of a `Map`/`Set`/typed array, do it yourself before calling `walk-async`, or use the platform `structuredClone` for those particular subtrees.
|
|
189
|
+
|
|
164
190
|
|
|
165
191
|
### Deep 'forEach'
|
|
192
|
+
`keyCallback` can be used as a deep `forEach` over every primitive property of the data — no matter how deeply nested. Unlike a plain `forEach`, **the callback must call `resolve` or `reject` on every code path**: that resolution is what tells `walk-async` the per-key work is done. To walk without changing anything, `resolve(value)`.
|
|
193
|
+
|
|
166
194
|
```js
|
|
167
195
|
let x = {
|
|
168
196
|
ls : [ 1,2,3 ]
|
|
@@ -175,11 +203,11 @@ let x = {
|
|
|
175
203
|
}
|
|
176
204
|
};
|
|
177
205
|
|
|
178
|
-
function keyFn ({value,key, breadcrumbs, resolve}) {
|
|
206
|
+
function keyFn ({ value, key, breadcrumbs, resolve }) {
|
|
179
207
|
console.log (`${key} ----> ${value}`) // Show each each primitive couples key->value
|
|
180
208
|
console.log ( `Property location >> ${breadcrumbs}`)
|
|
181
209
|
// example for breadcrumbs: 'age' will looks like this : 'root/props/age'
|
|
182
|
-
resolve ( value )
|
|
210
|
+
resolve ( value ) // pass-through — required, otherwise the result loses this key
|
|
183
211
|
}
|
|
184
212
|
|
|
185
213
|
walk ({ data:x, keyCallback: keyFn })
|
|
@@ -188,6 +216,48 @@ walk ({ data:x, keyCallback: keyFn })
|
|
|
188
216
|
})
|
|
189
217
|
```
|
|
190
218
|
|
|
219
|
+
`breadcrumbs` is a slash-delimited path string starting with `root` (e.g. `"root/props/age"`). Use it to know where you are in the structure; you can `.split('/')` it if you need a path array.
|
|
220
|
+
|
|
221
|
+
> Built-in types (`Date`, `Map`, `Set`, typed arrays, etc.) are reached by `keyCallback` as-is — see [Built-in types](#built-in-types-date-regexp-map-set-typed-arrays-etc) above.
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
### Skip a branch
|
|
225
|
+
Calling `reject()` from `objectCallback` drops the **entire subtree** at that key — not just the immediate property. Use it when you want to cut a whole section out of the result without having to walk into it and reject it key by key.
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
let x = {
|
|
229
|
+
name : 'Peter'
|
|
230
|
+
, password : 'secret'
|
|
231
|
+
, metadata : {
|
|
232
|
+
ip : '1.2.3.4'
|
|
233
|
+
, session : 'abc-123'
|
|
234
|
+
, device : { os: 'mac', browser: 'safari' }
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Drop a single primitive (use keyCallback)
|
|
239
|
+
const r1 = await walk ({
|
|
240
|
+
data: x
|
|
241
|
+
, keyCallback: ({ key, value, resolve, reject }) => {
|
|
242
|
+
if ( key === 'password' ) reject ()
|
|
243
|
+
else resolve ( value )
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
// r1.metadata is still fully present; only r1.password is gone.
|
|
247
|
+
|
|
248
|
+
// Drop an entire subtree (use objectCallback)
|
|
249
|
+
const r2 = await walk ({
|
|
250
|
+
data: x
|
|
251
|
+
, objectCallback: ({ key, value, resolve, reject }) => {
|
|
252
|
+
if ( key === 'metadata' ) reject ()
|
|
253
|
+
else resolve ( value )
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
// r2.password is still present; the whole r2.metadata subtree is gone.
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
`objectCallback` fires on the root too, so this also works at the top level (e.g. to short-circuit a `walk-async` by rejecting from the root call).
|
|
260
|
+
|
|
191
261
|
|
|
192
262
|
### Ignore a key
|
|
193
263
|
|
|
@@ -231,9 +301,9 @@ let x = {
|
|
|
231
301
|
, sizes : [12,33,12,21]
|
|
232
302
|
}
|
|
233
303
|
};
|
|
234
|
-
walk ({
|
|
304
|
+
walk ({
|
|
235
305
|
data:x
|
|
236
|
-
, keyCallback : ({resolve}) => resolve('xxx')
|
|
306
|
+
, keyCallback : ({resolve}) => resolve('xxx')
|
|
237
307
|
})
|
|
238
308
|
.then ( result => {
|
|
239
309
|
// 'result' will have the same structure as 'x' but all values are 'xxx'
|
|
@@ -246,7 +316,7 @@ walk ({
|
|
|
246
316
|
// , height : 'xxx'
|
|
247
317
|
// , sizes : ['xxx','xxx','xxx','xxx']
|
|
248
318
|
// }
|
|
249
|
-
// }
|
|
319
|
+
// }
|
|
250
320
|
})
|
|
251
321
|
```
|
|
252
322
|
|
|
@@ -273,9 +343,9 @@ function objectCallback ({ value:obj, key, resolve }) {
|
|
|
273
343
|
resolve ( obj )
|
|
274
344
|
}
|
|
275
345
|
|
|
276
|
-
walk ({
|
|
346
|
+
walk ({
|
|
277
347
|
data: x
|
|
278
|
-
, objectCallback
|
|
348
|
+
, objectCallback
|
|
279
349
|
})
|
|
280
350
|
.then ( result => {
|
|
281
351
|
// 'result.props' will have only 'age' and 'height' properties.
|
|
@@ -286,15 +356,80 @@ walk ({
|
|
|
286
356
|
// age : 48
|
|
287
357
|
// , height : 176
|
|
288
358
|
// }
|
|
289
|
-
// }
|
|
359
|
+
// }
|
|
290
360
|
})
|
|
291
361
|
```
|
|
292
362
|
|
|
293
363
|
|
|
364
|
+
## Migrating from `@peter.naydenov/walk`
|
|
365
|
+
|
|
366
|
+
`walk-async` is the async sibling of [`@peter.naydenov/walk`](https://github.com/PeterNaydenov/walk). They share the same callback parameter names (`value`, `key`, `breadcrumbs`) and the same three-outcome contract. The only mechanical change is how you express the return value:
|
|
367
|
+
|
|
368
|
+
| `walk` (sync) | `walk-async` (async) |
|
|
369
|
+
| -------------------------------- | ---------------------------- |
|
|
370
|
+
| `return value` | `resolve(value)` |
|
|
371
|
+
| `return newObject` | `resolve(newObject)` |
|
|
372
|
+
| `return IGNORE` | `reject()` |
|
|
294
373
|
|
|
295
|
-
|
|
374
|
+
The options shape is identical: `{ data, keyCallback, objectCallback }`. `walk-async` adds an optional `timeout` (milliseconds). The walk call itself becomes an `await` (or `.then`).
|
|
375
|
+
|
|
376
|
+
```js
|
|
377
|
+
// walk
|
|
378
|
+
const result = walk ({
|
|
379
|
+
data: user,
|
|
380
|
+
keyCallback: ({ value, key, IGNORE }) => key === 'password' ? IGNORE : value
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
// walk-async — same shape, just promise-based
|
|
384
|
+
const result = await walk ({
|
|
385
|
+
data: user,
|
|
386
|
+
keyCallback: ({ value, key, resolve, reject }) => {
|
|
387
|
+
if ( key === 'password' ) reject ()
|
|
388
|
+
else resolve ( value )
|
|
389
|
+
}
|
|
390
|
+
})
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
If your callbacks don't actually need async work, prefer the sync `walk` — it's noticeably faster because it avoids the promise machinery. Reach for `walk-async` only when you have real async work inside the callbacks (database lookups, network calls, file reads, etc.) and the cost of multiple `walk` calls would be unacceptable.
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
## Timeout
|
|
397
|
+
|
|
398
|
+
Every callback must call `resolve` or `reject` on every code path. If some path forgets to do it, the promise returned by `walk` will never settle — and by default there is no error and no hint which callback is the reason. The optional `timeout` property (milliseconds) turns that silent hang into a rejection with diagnostics:
|
|
399
|
+
|
|
400
|
+
```js
|
|
401
|
+
function objectCallbackFn ({ value, key, breadcrumbs, resolve, reject }) {
|
|
402
|
+
if ( breadcrumbs === 'root/props' ) return // BUG: this path never resolves
|
|
403
|
+
resolve ( value )
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
walk ({ data, objectCallback: objectCallbackFn, timeout: 5000 })
|
|
407
|
+
.catch ( err => {
|
|
408
|
+
console.error ( err.message )
|
|
409
|
+
// walk-async: timed out after 5000ms; callbacks still pending:
|
|
410
|
+
// - objectCallback at 'root/props'
|
|
411
|
+
})
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
The error message lists the breadcrumbs of every callback that was started but never resolved or rejected, so the broken code path can be found directly.
|
|
415
|
+
|
|
416
|
+
Notes:
|
|
417
|
+
- `timeout` is disabled by default. Without it the behavior is exactly as before;
|
|
418
|
+
- The limit applies to the whole walk, not to a single callback. Set it well above the worst-case duration of the legitimate async work inside the callbacks — it is a debugging safety net, not a scheduler;
|
|
419
|
+
- When callbacks finish in time, the timer is cleared and the result is delivered as usual.
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
## Limitations
|
|
423
|
+
- `walk-async` does not descend into built-in types (`Date`, `RegExp`, `Map`, `Set`, `WeakMap`, `WeakSet`, `ArrayBuffer`, `DataView`, typed arrays, DOM nodes, functions) — they are passed by reference, whether they appear in the input or are returned by a callback;
|
|
424
|
+
- `walk-async` can not execute another `walk-async` from inside the callbacks;
|
|
425
|
+
- The walk promise does not settle until every callback has resolved or rejected. Use the `timeout` option to turn a forgotten `resolve`/`reject` into a rejection with diagnostics.
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
## See also
|
|
429
|
+
- [`@peter.naydenov/walk`](https://github.com/PeterNaydenov/walk) — the sync sibling. Same callback shape, same three-outcome contract, no promise machinery. Use it when your callbacks don't need async work.
|
|
296
430
|
- [Release history](Changelog.md)
|
|
297
431
|
|
|
432
|
+
|
|
298
433
|
## Credits
|
|
299
434
|
'@peter.naydenov/walk-async' was created and supported by Peter Naydenov.
|
|
300
435
|
|
package/package.json
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peter.naydenov/walk-async",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "Deep copy with fine control during the process. Can be used also as a deep forEach function",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "./dist/walk-async.umd.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./types/main.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"import": "
|
|
11
|
-
"require": "
|
|
10
|
+
"import": "./dist/walk-async.esm.mjs",
|
|
11
|
+
"require": "./dist/walk-async.cjs",
|
|
12
12
|
"default": "./dist/walk-async.umd.js"
|
|
13
13
|
},
|
|
14
14
|
"./package.json": "./package.json",
|
|
15
15
|
"./dist/*": "./dist/*",
|
|
16
16
|
"./src/*": "./src/*"
|
|
17
17
|
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"types",
|
|
22
|
+
"README.md",
|
|
23
|
+
"Changelog.md",
|
|
24
|
+
"Migration.guide.md",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
".agents/skills"
|
|
27
|
+
],
|
|
18
28
|
"scripts": {
|
|
19
29
|
"test": "vitest run",
|
|
20
30
|
"test:watch": "vitest",
|
|
@@ -35,11 +45,11 @@
|
|
|
35
45
|
"@rollup/plugin-commonjs": "^29.0.3",
|
|
36
46
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
37
47
|
"@rollup/plugin-terser": "^1.0.0",
|
|
38
|
-
"@vitest/coverage-v8": "4.1.
|
|
39
|
-
"rollup": "^4.
|
|
48
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
49
|
+
"rollup": "^4.63.1",
|
|
40
50
|
"typescript": "^7.0.2",
|
|
41
|
-
"vite": "^8.2.
|
|
42
|
-
"vitest": "4.1.
|
|
51
|
+
"vite": "^8.2.2",
|
|
52
|
+
"vitest": "4.1.11"
|
|
43
53
|
},
|
|
44
54
|
"vitest": {
|
|
45
55
|
"coverage": {
|
package/src/main.js
CHANGED
|
@@ -1,44 +1,114 @@
|
|
|
1
1
|
|
|
2
2
|
/**
|
|
3
3
|
* walk-async
|
|
4
|
-
*
|
|
5
|
-
* Alternative of deep-copy that provides much better control during creation of immutable
|
|
6
|
-
* copies of javascript data structures.
|
|
7
|
-
* Library is using 'generator functions'. If support for old browsers is required,
|
|
4
|
+
*
|
|
5
|
+
* Alternative of deep-copy that provides much better control during creation of immutable
|
|
6
|
+
* copies of javascript data structures.
|
|
7
|
+
* Library is using 'generator functions'. If support for old browsers is required,
|
|
8
8
|
* add a polyfill for 'generators'.
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
10
|
* History notes:
|
|
11
11
|
* - Walk-async was born on September 18th, 2022
|
|
12
12
|
* - Converted to ES module on January 1st, 2024
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
import askForPromise from "ask-for-promise"
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
20
|
+
* Resolve the callback with the new value to store at the current key:
|
|
21
|
+
* - resolving a primitive (or a built-in like `Date` / `Map` / `Set`) → stored as-is by reference;
|
|
22
|
+
* - resolving a plain object or array → walk continues into it with the other callback applied to its children;
|
|
23
|
+
*
|
|
24
|
+
* You must call `resolve` or `reject` on every code path — otherwise the walk promise never settles
|
|
25
|
+
* (use the `timeout` option to turn that silent hang into a rejection with diagnostics).
|
|
26
|
+
*
|
|
27
|
+
* @callback Resolve
|
|
28
|
+
* @param {*} value
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Reject the callback. The current key is dropped from the result.
|
|
34
|
+
*
|
|
35
|
+
* @callback Reject
|
|
36
|
+
* @param {*} [reason]
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Arguments object received by both `keyCallback` and `objectCallback`.
|
|
42
|
+
*
|
|
43
|
+
* @typedef {object} CallbackArgs
|
|
44
|
+
* @property {*} value - The current value being processed.
|
|
45
|
+
* @property {string} key - Property key as a string.
|
|
46
|
+
* @property {string} breadcrumbs - Slash-delimited path to the current key, starting with `root` (e.g. `"root/props/age"`).
|
|
47
|
+
* @property {Resolve} resolve - Resolve the callback with the new value (see `Resolve` typedef).
|
|
48
|
+
* @property {Reject} reject - Reject the callback to drop the current key (see `Reject` typedef).
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Called once per primitive property (string, number, bigint, boolean,
|
|
53
|
+
* symbol, null, undefined, function, Date, RegExp, Map, Set, WeakMap,
|
|
54
|
+
* WeakSet, ArrayBuffer, DataView, typed arrays, DOM nodes).
|
|
55
|
+
*
|
|
56
|
+
* Resolve with the new value to store, or reject to drop the key:
|
|
57
|
+
* - resolve a primitive (or a built-in like `Date` / `Map` / `Set`) → stored as-is by reference;
|
|
58
|
+
* - resolve a plain object or array → walk continues into it with the other callback applied to its children;
|
|
59
|
+
* - reject → that key is dropped from the result.
|
|
60
|
+
*
|
|
61
|
+
* @callback KeyCallback
|
|
62
|
+
* @param {CallbackArgs} args
|
|
63
|
+
* @param {...*} rest - Any extra arguments passed to `walk()` are forwarded to the callback.
|
|
64
|
+
* @returns {void}
|
|
25
65
|
*/
|
|
26
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Called once per object or array property, including the root.
|
|
69
|
+
* The resolved value becomes the new value at that key:
|
|
70
|
+
* - resolve an object or array → walk continues into it with the other callbacks applied to its children;
|
|
71
|
+
* - resolve a primitive → it is stored at that key as-is, and walk does not descend into it (primitives have no children to walk);
|
|
72
|
+
* - reject → the key is dropped from the result.
|
|
73
|
+
*
|
|
74
|
+
* @callback ObjectCallback
|
|
75
|
+
* @param {CallbackArgs} args
|
|
76
|
+
* @param {...*} rest
|
|
77
|
+
* @returns {void}
|
|
78
|
+
*/
|
|
27
79
|
|
|
80
|
+
/**
|
|
81
|
+
* @typedef {object} Options
|
|
82
|
+
* @property {*} data - Required. Any JS data structure that will be copied.
|
|
83
|
+
* @property {KeyCallback} [keyCallback] - Optional. Executed on each primitive property.
|
|
84
|
+
* @property {ObjectCallback} [objectCallback] - Optional. Executed on each object/array property, including the root.
|
|
85
|
+
* @property {number} [timeout] - Optional. Milliseconds. When set, the promise is rejected if callbacks do not resolve in time. Error lists the pending callbacks.
|
|
86
|
+
*/
|
|
28
87
|
|
|
29
88
|
|
|
30
89
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
90
|
+
* Walk-async
|
|
91
|
+
*
|
|
92
|
+
* Async sibling of `@peter.naydenov/walk`. Visits every member of a deep
|
|
93
|
+
* JavaScript data structure once, in a single pass, and runs two optional
|
|
94
|
+
* callbacks during the visit that can mask, filter, or substitute values
|
|
95
|
+
* as the result is built.
|
|
33
96
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* - timeout - Milliseconds. When set, the promise is rejected if callbacks do not resolve in time.
|
|
39
|
-
* @param {...any} args - Any additional arguments will be passed to the callback functions.
|
|
97
|
+
* The deep copy of the original data is mostly a side-effect: the real
|
|
98
|
+
* power is the ability to do all your modifications in one pass, with full
|
|
99
|
+
* freedom inside a single callback — now with async work, awaits, and
|
|
100
|
+
* timeouts supported.
|
|
40
101
|
*
|
|
41
|
-
*
|
|
102
|
+
* @function walk
|
|
103
|
+
* @param {Options} options - Required. Object with required `data` property, two optional callback functions (`keyCallback`, `objectCallback`), and an optional `timeout`.
|
|
104
|
+
* @param {...*} args - Optional. Additional arguments forwarded to both callbacks.
|
|
105
|
+
* @returns {Promise<*>} - A promise that resolves to the immutable copy of `options.data` (with the callbacks' transformations applied).
|
|
106
|
+
* @example
|
|
107
|
+
* const result = await walk ({
|
|
108
|
+
* data: someData,
|
|
109
|
+
* keyCallback: keyCallbackFn,
|
|
110
|
+
* objectCallback: objectCallbackFn
|
|
111
|
+
* })
|
|
42
112
|
*/
|
|
43
113
|
function walk ({
|
|
44
114
|
data:origin
|
package/types/main.d.ts
CHANGED
|
@@ -11,43 +11,136 @@
|
|
|
11
11
|
* - Converted to ES module on January 1st, 2024
|
|
12
12
|
*
|
|
13
13
|
*/
|
|
14
|
+
export type Resolve = (value: any) => void;
|
|
15
|
+
export type Reject = (reason?: any) => void;
|
|
16
|
+
export type CallbackArgs = {
|
|
17
|
+
/**
|
|
18
|
+
* - The current value being processed.
|
|
19
|
+
*/
|
|
20
|
+
value: any;
|
|
21
|
+
/**
|
|
22
|
+
* - Property key as a string.
|
|
23
|
+
*/
|
|
24
|
+
key: string;
|
|
25
|
+
/**
|
|
26
|
+
* - Slash-delimited path to the current key, starting with `root` (e.g. `"root/props/age"`).
|
|
27
|
+
*/
|
|
28
|
+
breadcrumbs: string;
|
|
29
|
+
/**
|
|
30
|
+
* - Resolve the callback with the new value (see `Resolve` typedef).
|
|
31
|
+
*/
|
|
32
|
+
resolve: Resolve;
|
|
33
|
+
/**
|
|
34
|
+
* - Reject the callback to drop the current key (see `Reject` typedef).
|
|
35
|
+
*/
|
|
36
|
+
reject: Reject;
|
|
37
|
+
};
|
|
38
|
+
export type KeyCallback = (args: CallbackArgs, ...rest: any) => void;
|
|
39
|
+
export type ObjectCallback = (args: CallbackArgs, ...rest: any) => void;
|
|
14
40
|
export type Options = {
|
|
15
41
|
/**
|
|
16
|
-
* -
|
|
42
|
+
* - Required. Any JS data structure that will be copied.
|
|
17
43
|
*/
|
|
18
|
-
data:
|
|
44
|
+
data: any;
|
|
19
45
|
/**
|
|
20
|
-
* -
|
|
46
|
+
* - Optional. Executed on each primitive property.
|
|
21
47
|
*/
|
|
22
|
-
|
|
48
|
+
keyCallback?: KeyCallback;
|
|
23
49
|
/**
|
|
24
|
-
* -
|
|
50
|
+
* - Optional. Executed on each object/array property, including the root.
|
|
25
51
|
*/
|
|
26
|
-
|
|
52
|
+
objectCallback?: ObjectCallback;
|
|
27
53
|
/**
|
|
28
|
-
* - Milliseconds. When set, the promise is rejected if callbacks do not resolve in time. Error lists the pending callbacks.
|
|
54
|
+
* - Optional. Milliseconds. When set, the promise is rejected if callbacks do not resolve in time. Error lists the pending callbacks.
|
|
29
55
|
*/
|
|
30
|
-
timeout?: number
|
|
56
|
+
timeout?: number;
|
|
31
57
|
};
|
|
32
58
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
59
|
+
* Resolve the callback with the new value to store at the current key:
|
|
60
|
+
* - resolving a primitive (or a built-in like `Date` / `Map` / `Set`) → stored as-is by reference;
|
|
61
|
+
* - resolving a plain object or array → walk continues into it with the other callback applied to its children;
|
|
62
|
+
*
|
|
63
|
+
* You must call `resolve` or `reject` on every code path — otherwise the walk promise never settles
|
|
64
|
+
* (use the `timeout` option to turn that silent hang into a rejection with diagnostics).
|
|
65
|
+
*
|
|
66
|
+
* @callback Resolve
|
|
67
|
+
* @param {*} value
|
|
68
|
+
* @returns {void}
|
|
38
69
|
*/
|
|
39
70
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
71
|
+
* Reject the callback. The current key is dropped from the result.
|
|
72
|
+
*
|
|
73
|
+
* @callback Reject
|
|
74
|
+
* @param {*} [reason]
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
77
|
+
/**
|
|
78
|
+
* Arguments object received by both `keyCallback` and `objectCallback`.
|
|
79
|
+
*
|
|
80
|
+
* @typedef {object} CallbackArgs
|
|
81
|
+
* @property {*} value - The current value being processed.
|
|
82
|
+
* @property {string} key - Property key as a string.
|
|
83
|
+
* @property {string} breadcrumbs - Slash-delimited path to the current key, starting with `root` (e.g. `"root/props/age"`).
|
|
84
|
+
* @property {Resolve} resolve - Resolve the callback with the new value (see `Resolve` typedef).
|
|
85
|
+
* @property {Reject} reject - Reject the callback to drop the current key (see `Reject` typedef).
|
|
86
|
+
*/
|
|
87
|
+
/**
|
|
88
|
+
* Called once per primitive property (string, number, bigint, boolean,
|
|
89
|
+
* symbol, null, undefined, function, Date, RegExp, Map, Set, WeakMap,
|
|
90
|
+
* WeakSet, ArrayBuffer, DataView, typed arrays, DOM nodes).
|
|
91
|
+
*
|
|
92
|
+
* Resolve with the new value to store, or reject to drop the key:
|
|
93
|
+
* - resolve a primitive (or a built-in like `Date` / `Map` / `Set`) → stored as-is by reference;
|
|
94
|
+
* - resolve a plain object or array → walk continues into it with the other callback applied to its children;
|
|
95
|
+
* - reject → that key is dropped from the result.
|
|
96
|
+
*
|
|
97
|
+
* @callback KeyCallback
|
|
98
|
+
* @param {CallbackArgs} args
|
|
99
|
+
* @param {...*} rest - Any extra arguments passed to `walk()` are forwarded to the callback.
|
|
100
|
+
* @returns {void}
|
|
101
|
+
*/
|
|
102
|
+
/**
|
|
103
|
+
* Called once per object or array property, including the root.
|
|
104
|
+
* The resolved value becomes the new value at that key:
|
|
105
|
+
* - resolve an object or array → walk continues into it with the other callbacks applied to its children;
|
|
106
|
+
* - resolve a primitive → it is stored at that key as-is, and walk does not descend into it (primitives have no children to walk);
|
|
107
|
+
* - reject → the key is dropped from the result.
|
|
108
|
+
*
|
|
109
|
+
* @callback ObjectCallback
|
|
110
|
+
* @param {CallbackArgs} args
|
|
111
|
+
* @param {...*} rest
|
|
112
|
+
* @returns {void}
|
|
113
|
+
*/
|
|
114
|
+
/**
|
|
115
|
+
* @typedef {object} Options
|
|
116
|
+
* @property {*} data - Required. Any JS data structure that will be copied.
|
|
117
|
+
* @property {KeyCallback} [keyCallback] - Optional. Executed on each primitive property.
|
|
118
|
+
* @property {ObjectCallback} [objectCallback] - Optional. Executed on each object/array property, including the root.
|
|
119
|
+
* @property {number} [timeout] - Optional. Milliseconds. When set, the promise is rejected if callbacks do not resolve in time. Error lists the pending callbacks.
|
|
120
|
+
*/
|
|
121
|
+
/**
|
|
122
|
+
* Walk-async
|
|
123
|
+
*
|
|
124
|
+
* Async sibling of `@peter.naydenov/walk`. Visits every member of a deep
|
|
125
|
+
* JavaScript data structure once, in a single pass, and runs two optional
|
|
126
|
+
* callbacks during the visit that can mask, filter, or substitute values
|
|
127
|
+
* as the result is built.
|
|
42
128
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* - timeout - Milliseconds. When set, the promise is rejected if callbacks do not resolve in time.
|
|
48
|
-
* @param {...any} args - Any additional arguments will be passed to the callback functions.
|
|
129
|
+
* The deep copy of the original data is mostly a side-effect: the real
|
|
130
|
+
* power is the ability to do all your modifications in one pass, with full
|
|
131
|
+
* freedom inside a single callback — now with async work, awaits, and
|
|
132
|
+
* timeouts supported.
|
|
49
133
|
*
|
|
50
|
-
*
|
|
134
|
+
* @function walk
|
|
135
|
+
* @param {Options} options - Required. Object with required `data` property, two optional callback functions (`keyCallback`, `objectCallback`), and an optional `timeout`.
|
|
136
|
+
* @param {...*} args - Optional. Additional arguments forwarded to both callbacks.
|
|
137
|
+
* @returns {Promise<*>} - A promise that resolves to the immutable copy of `options.data` (with the callbacks' transformations applied).
|
|
138
|
+
* @example
|
|
139
|
+
* const result = await walk ({
|
|
140
|
+
* data: someData,
|
|
141
|
+
* keyCallback: keyCallbackFn,
|
|
142
|
+
* objectCallback: objectCallbackFn
|
|
143
|
+
* })
|
|
51
144
|
*/
|
|
52
145
|
declare function walk({ data: origin, objectCallback, keyCallback, timeout }: Options, ...args: any[]): Promise<any>;
|
|
53
146
|
export default walk;
|
package/types/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.js"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.js"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;AAaC,YAAU,OAAO,GACjB,CAAW,KAAK,EAAR,GACR,KAAU,IACb,CAAA;AAKG,YAAU,MAAM,GAChB,CAAY,MAAM,AAAlB,CACA,EADQ,GACR,KAAU,IACb,CAAA;AAKG,YAAkB,YAAY,GAC9B;;;;IAAuB,KAAK,EAAjB,GAAC,CACZ;;;;IAAuB,GAAG,EAAf,MAAM,CACjB;;;;IAAuB,WAAW,EAAvB,MAAM,CACjB;;;;IAAuB,OAAO,EAAnB,OAAO,CAClB;;;;IAAuB,MAAM,EAAlB,MAAM,CACpB;CAAA,CAAA;AAYG,YAAU,WAAW,GACrB,CAAsB,IAAI,EAAlB,YACR,EAAA,GACA,AADsB,IAAI,EAAf,GACX,KAAU,IACb,CAAA;AASG,YAAU,cAAc,GACxB,CAAsB,IAAI,EAAlB,YACR,EAAA,GACA,AADsB,IAAI,EAAf,GACX,KAAU,IACb,CAAA;AAGG,YAAkB,OAAO,GACzB;;;;IAA0B,IAAI,EAAnB,GAAC,CACZ;;;;IAA4B,WAAW,AAAvC,CACA,EADW,WAAW,CACtB;;;;IAA4B,cAAc,AAA1C,CACA,EADW,cAAc,CACzB;;;;IAA4B,OAAO,AAAnC,CACH,EADc,MAAM,CACpB;CAAA,CAAA;AAnED;;;;;;;;;;;GAWG;AAEH;;;;;;GAMG;AAEH;;;;;;;;;GASG;AAEH;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;GAWG;AAEH;;;;;;GAMG;AAGH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,iBAAS,IAAI,CAAE,EACG,IAAI,EAAC,MAAM,EACX,cAAqB,EACrB,WAAkB,EAClB,OAAc,EACnB,EAfD,OAeC,EAAE,GAAG,IAAI,EAdP,GAAC,EAcM,GAbR,OAAO,CAAC,GAAC,CAAC,CA2EvB;eA4Lc,IAAI"}
|
package/rollup.config.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import resolve from '@rollup/plugin-node-resolve'
|
|
2
|
-
import commonjs from '@rollup/plugin-commonjs'
|
|
3
|
-
import terser from '@rollup/plugin-terser';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export default [
|
|
7
|
-
// browser-friendly UMD build
|
|
8
|
-
{
|
|
9
|
-
input: 'src/main.js',
|
|
10
|
-
output: {
|
|
11
|
-
name: 'walkAsync',
|
|
12
|
-
file: 'dist/walk-async.umd.js',
|
|
13
|
-
format: 'umd',
|
|
14
|
-
globals: {
|
|
15
|
-
'ask-for-promise': 'askForPromise'
|
|
16
|
-
}
|
|
17
|
-
},
|
|
18
|
-
external : [ 'ask-for-promise' ],
|
|
19
|
-
plugins: [
|
|
20
|
-
resolve(), // so Rollup can find `ms`
|
|
21
|
-
commonjs() // so Rollup can convert `ms` to an ES module
|
|
22
|
-
, terser()
|
|
23
|
-
]
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
// CommonJS (for Node) and ES module (for bundlers) build.
|
|
27
|
-
// (We could have three entries in the configuration array
|
|
28
|
-
// instead of two, but it's quicker to generate multiple
|
|
29
|
-
// builds from a single configuration where possible, using
|
|
30
|
-
// an array for the `output` option, where we can specify
|
|
31
|
-
// `file` and `format` for each target)
|
|
32
|
-
{
|
|
33
|
-
input: 'src/main.js',
|
|
34
|
-
external: ['ask-for-promise'],
|
|
35
|
-
output: [
|
|
36
|
-
{ file: 'dist/walk-async.cjs' , format: 'cjs' },
|
|
37
|
-
{ file: 'dist/walk-async.esm.mjs', format: 'es' }
|
|
38
|
-
],
|
|
39
|
-
plugins: [ terser() ]
|
|
40
|
-
}
|
|
41
|
-
];
|
package/tsconfig.json
DELETED