@peter.naydenov/walk-async 3.0.7 → 3.1.1
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/Changelog.md +13 -0
- package/README.md +31 -2
- package/dist/walk-async.cjs +1 -1
- package/dist/walk-async.esm.mjs +1 -1
- package/dist/walk-async.umd.js +1 -1
- package/package.json +23 -24
- package/src/main.js +35 -10
- package/types/main.d.ts +21 -2
- package/types/main.d.ts.map +1 -1
- package/vitest.config.js +8 -0
package/Changelog.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
### 3.1.1 (2026-07-12)
|
|
6
|
+
- [x] Moving to typescript v.7.x.x;
|
|
7
|
+
- [x] Changing 'mocha' testing library with vitest;
|
|
8
|
+
- [x] Converting all tests to vitest;
|
|
9
|
+
- [x] Changing coverage library from c8 to @vitest/coverage-v8;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### 3.1.0 (2026-07-07)
|
|
14
|
+
- [x] New option 'timeout'. Milliseconds. When set, the walk promise is rejected if callbacks do not resolve in time. Error message lists the breadcrumbs of the pending callbacks;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
5
18
|
### 3.0.7 (2026-07-07)
|
|
6
19
|
- [x] Fix: 'objectCallback' resolving the root object with a primitive value was crashing on null/undefined or producing broken results;
|
|
7
20
|
- [x] Fix: Own '__proto__' property was replacing the prototype of the copy instead of being copied as a regular property;
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Creates an immutable copies of javascript data structures(objects, arrays or mix
|
|
|
13
13
|
data // (required) Any JS object structure
|
|
14
14
|
, objectCallback // (optional) Function executed on each object property
|
|
15
15
|
, keyCallback // (optional) Function executed on each primitive property
|
|
16
|
+
, timeout // (optional) Milliseconds. Reject the promise if callbacks do not resolve in time
|
|
16
17
|
})
|
|
17
18
|
.then ( result => {
|
|
18
19
|
// Result will become a exact deep copy of "data"
|
|
@@ -55,7 +56,8 @@ function keyCallbackFn ({ value, key, breadcrumbs, resolve, reject }) {
|
|
|
55
56
|
// breadcrumbs: location of the property;
|
|
56
57
|
// resolve: function that will resolve the callback promise. Provide the result as argument;
|
|
57
58
|
// reject: function that can cancel the copy of that property;
|
|
58
|
-
// Important: key callback should be resolved or rejected
|
|
59
|
+
// Important: key callback should be resolved or rejected on every code path,
|
|
60
|
+
// otherwise the walk promise never settles. See section "Timeout".
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
const result = await walk ({ data, keyCallback : keyCallbackFn }); // It's the short way to provide only key-callback. Callback functions are optional.
|
|
@@ -74,7 +76,8 @@ function objectCallbackFn ({ value, key, breadcrumbs, resolve, reject }) {
|
|
|
74
76
|
// breadcrumbs: location of the object;
|
|
75
77
|
// resolve: function that will resolve the callback promise. Provide the result as argument;
|
|
76
78
|
// reject: function that can cancel the copy of that property;
|
|
77
|
-
// Important: Object callback should be resolved or rejected
|
|
79
|
+
// Important: Object callback should be resolved or rejected on every code path,
|
|
80
|
+
// otherwise the walk promise never settles. See section "Timeout".
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
walk ({
|
|
@@ -95,6 +98,32 @@ Skip key-callbacks by not providing a keyCallback function.
|
|
|
95
98
|
```
|
|
96
99
|
|
|
97
100
|
|
|
101
|
+
## Timeout
|
|
102
|
+
|
|
103
|
+
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:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
function objectCallbackFn ({ value, key, breadcrumbs, resolve, reject }) {
|
|
107
|
+
if ( breadcrumbs === 'root/props' ) return // BUG: this path never resolves
|
|
108
|
+
resolve ( value )
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
walk ({ data, objectCallback: objectCallbackFn, timeout: 5000 })
|
|
112
|
+
.catch ( err => {
|
|
113
|
+
console.error ( err.message )
|
|
114
|
+
// walk-async: timed out after 5000ms; callbacks still pending:
|
|
115
|
+
// - objectCallback at 'root/props'
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
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
|
+
|
|
98
127
|
## Installation
|
|
99
128
|
|
|
100
129
|
Install for node.js projects by writing in your terminal:
|
package/dist/walk-async.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e=require("ask-for-promise");async function o(e,o,n){for await(const o of e)await o.next();n.done(o)}function n(e){return null==e||e.nodeType?"simple":e instanceof Array?"array":"object"==typeof e?"object":"simple"}async function*
|
|
1
|
+
"use strict";var e=require("ask-for-promise");async function o(e,o,n){for await(const o of e)await o.next();n.done(o)}function n(e){return null==e||e.nodeType?"simple":e instanceof Array?"array":"object"==typeof e?"object":"simple"}async function*t(e,o,n,t,r,l){yield await a(e,o,n,t,r,...l)}function r(e,o,n){"__proto__"===o?Object.defineProperty(e,o,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[o]=n}function a(o,a,l,s,c,...i){let[d,u,p]=s,m=Object.keys(o),b=e(m),f=e();return m.forEach((m,f)=>{let y=n(o[m]),$=o[m],k="simple"!==y&&null!=u,j=e(),_=e(),C=e(),v="array"===n(a),h=!isNaN(m),w=Symbol("ignore___"),g=`${c}/${m}`,N=`objectCallback at '${g}'`,S=`keyCallback at '${g}'`;k?(p.add(N),u({resolve:j.done,reject:()=>j.done(w),value:$,key:m,breadcrumbs:g},...i)):j.done("$$cancel"),j.onComplete(e=>{if(p.delete(N),"$$cancel"!==e||d)if("$$cancel"!==e&&($=e,y=n($)),$!=w)if("simple"===y){if(!d)return void _.done("$$noUpdates");p.add(S),d({resolve:_.done,reject:()=>_.done(w),value:$,key:m,breadcrumbs:g},...i)}else _.done("$$cancel");else b.promises[f].done("ignore object");else _.done("$$noUpdates")}),_.onComplete(e=>{if(p.delete(S),e!=w)if("$$cancel"!==e){if("$$noUpdates"!==e&&($=e,y=n($)),"simple"===y){const e=function(e,o){return o instanceof Array&&!isNaN(e)}(m,a);return e?a.push($):r(a,m,$),void b.promises[f].done("key")}C.done()}else C.done();else b.promises[f].done("ignore key")}),C.onComplete(()=>{if("object"===y){const e={};v&&h?a.push(e):r(a,m,e),l.push(t($,e,l,s,g,i)),b.promises[f].done("object")}if("array"===y){const e=[];v&&h?a.push(e):r(a,m,e),l.push(t($,e,l,s,g,i)),b.promises[f].done("array")}})}),b.onComplete(e=>f.done()),f.promise}module.exports=function({data:t,objectCallback:r=null,keyCallback:l=null,timeout:s=null},...c){let i,d=n(t),u=[],p="root",m=new Set,b=[l,r,m],f=e(),y=e(),$=Symbol("ignore___");if("simple"!==d&&r?(m.add("objectCallback at 'root'"),r({resolve:y.done,reject:()=>y.done($),value:t,key:"root",breadcrumbs:p},...c)):y.done(t),y.onComplete(e=>{if(m.delete("objectCallback at 'root'"),e!==$)switch(n(e)){case"array":i=[],a(e,i,u,b,p,...c).then(()=>o(u,i,f));break;case"object":i={},a(e,i,u,b,p,...c).then(()=>{o(u,i,f)});break;case"simple":f.done(e)}else f.done("array"===d?[]:{})}),null==s)return f.promise;const k=Symbol("expired___"),j=e();return f.timeout(s,k),f.onComplete(e=>{if(e!==k)return void j.done(e);const o=[...m].map(e=>`\n - ${e}`).join("");j.cancel(new Error(`walk-async: timed out after ${s}ms; callbacks still pending:${o}`))}),j.promise};
|
package/dist/walk-async.esm.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"ask-for-promise";function o({data:o,objectCallback:
|
|
1
|
+
import e from"ask-for-promise";function o({data:o,objectCallback:a=null,keyCallback:r=null,timeout:s=null},...c){let i,d=t(o),p=[],u="root",m=new Set,b=[r,a,m],f=e(),y=e(),$=Symbol("ignore___");if("simple"!==d&&a?(m.add("objectCallback at 'root'"),a({resolve:y.done,reject:()=>y.done($),value:o,key:"root",breadcrumbs:u},...c)):y.done(o),y.onComplete(e=>{if(m.delete("objectCallback at 'root'"),e!==$)switch(t(e)){case"array":i=[],l(e,i,p,b,u,...c).then(()=>n(p,i,f));break;case"object":i={},l(e,i,p,b,u,...c).then(()=>{n(p,i,f)});break;case"simple":f.done(e)}else f.done("array"===d?[]:{})}),null==s)return f.promise;const k=Symbol("expired___"),j=e();return f.timeout(s,k),f.onComplete(e=>{if(e!==k)return void j.done(e);const o=[...m].map(e=>`\n - ${e}`).join("");j.cancel(new Error(`walk-async: timed out after ${s}ms; callbacks still pending:${o}`))}),j.promise}async function n(e,o,n){for await(const o of e)await o.next();n.done(o)}function t(e){return null==e||e.nodeType?"simple":e instanceof Array?"array":"object"==typeof e?"object":"simple"}async function*a(e,o,n,t,a,r){yield await l(e,o,n,t,a,...r)}function r(e,o,n){"__proto__"===o?Object.defineProperty(e,o,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[o]=n}function l(o,n,l,s,c,...i){let[d,p,u]=s,m=Object.keys(o),b=e(m),f=e();return m.forEach((m,f)=>{let y=t(o[m]),$=o[m],k="simple"!==y&&null!=p,j=e(),_=e(),C=e(),v="array"===t(n),h=!isNaN(m),w=Symbol("ignore___"),g=`${c}/${m}`,N=`objectCallback at '${g}'`,S=`keyCallback at '${g}'`;k?(u.add(N),p({resolve:j.done,reject:()=>j.done(w),value:$,key:m,breadcrumbs:g},...i)):j.done("$$cancel"),j.onComplete(e=>{if(u.delete(N),"$$cancel"!==e||d)if("$$cancel"!==e&&($=e,y=t($)),$!=w)if("simple"===y){if(!d)return void _.done("$$noUpdates");u.add(S),d({resolve:_.done,reject:()=>_.done(w),value:$,key:m,breadcrumbs:g},...i)}else _.done("$$cancel");else b.promises[f].done("ignore object");else _.done("$$noUpdates")}),_.onComplete(e=>{if(u.delete(S),e!=w)if("$$cancel"!==e){if("$$noUpdates"!==e&&($=e,y=t($)),"simple"===y){const e=function(e,o){return o instanceof Array&&!isNaN(e)}(m,n);return e?n.push($):r(n,m,$),void b.promises[f].done("key")}C.done()}else C.done();else b.promises[f].done("ignore key")}),C.onComplete(()=>{if("object"===y){const e={};v&&h?n.push(e):r(n,m,e),l.push(a($,e,l,s,g,i)),b.promises[f].done("object")}if("array"===y){const e=[];v&&h?n.push(e):r(n,m,e),l.push(a($,e,l,s,g,i)),b.promises[f].done("array")}})}),b.onComplete(e=>f.done()),f.promise}export{o as default};
|
package/dist/walk-async.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,o){"object"==typeof exports&&"undefined"!=typeof module?module.exports=o(require("ask-for-promise")):"function"==typeof define&&define.amd?define(["ask-for-promise"],o):(e="undefined"!=typeof globalThis?globalThis:e||self).walkAsync=o(e.askForPromise)}(this,function(e){"use strict";async function o(e,o,n){for await(const o of e)await o.next();n.done(o)}function n(e){return null==e||e.nodeType?"simple":e instanceof Array?"array":"object"==typeof e?"object":"simple"}async function*
|
|
1
|
+
!function(e,o){"object"==typeof exports&&"undefined"!=typeof module?module.exports=o(require("ask-for-promise")):"function"==typeof define&&define.amd?define(["ask-for-promise"],o):(e="undefined"!=typeof globalThis?globalThis:e||self).walkAsync=o(e.askForPromise)}(this,function(e){"use strict";async function o(e,o,n){for await(const o of e)await o.next();n.done(o)}function n(e){return null==e||e.nodeType?"simple":e instanceof Array?"array":"object"==typeof e?"object":"simple"}async function*t(e,o,n,t,r,l){yield await a(e,o,n,t,r,...l)}function r(e,o,n){"__proto__"===o?Object.defineProperty(e,o,{value:n,enumerable:!0,writable:!0,configurable:!0}):e[o]=n}function a(o,a,l,i,s,...c){let[d,u,p]=i,f=Object.keys(o),m=e(f),b=e();return f.forEach((f,b)=>{let y=n(o[f]),$=o[f],k="simple"!==y&&null!=u,j=e(),_=e(),h=e(),C="array"===n(a),v=!isNaN(f),w=Symbol("ignore___"),g=`${s}/${f}`,x=`objectCallback at '${g}'`,N=`keyCallback at '${g}'`;k?(p.add(x),u({resolve:j.done,reject:()=>j.done(w),value:$,key:f,breadcrumbs:g},...c)):j.done("$$cancel"),j.onComplete(e=>{if(p.delete(x),"$$cancel"!==e||d)if("$$cancel"!==e&&($=e,y=n($)),$!=w)if("simple"===y){if(!d)return void _.done("$$noUpdates");p.add(N),d({resolve:_.done,reject:()=>_.done(w),value:$,key:f,breadcrumbs:g},...c)}else _.done("$$cancel");else m.promises[b].done("ignore object");else _.done("$$noUpdates")}),_.onComplete(e=>{if(p.delete(N),e!=w)if("$$cancel"!==e){if("$$noUpdates"!==e&&($=e,y=n($)),"simple"===y){const e=function(e,o){return o instanceof Array&&!isNaN(e)}(f,a);return e?a.push($):r(a,f,$),void m.promises[b].done("key")}h.done()}else h.done();else m.promises[b].done("ignore key")}),h.onComplete(()=>{if("object"===y){const e={};C&&v?a.push(e):r(a,f,e),l.push(t($,e,l,i,g,c)),m.promises[b].done("object")}if("array"===y){const e=[];C&&v?a.push(e):r(a,f,e),l.push(t($,e,l,i,g,c)),m.promises[b].done("array")}})}),m.onComplete(e=>b.done()),b.promise}return function({data:t,objectCallback:r=null,keyCallback:l=null,timeout:i=null},...s){let c,d=n(t),u=[],p="root",f=new Set,m=[l,r,f],b=e(),y=e(),$=Symbol("ignore___");if("simple"!==d&&r?(f.add("objectCallback at 'root'"),r({resolve:y.done,reject:()=>y.done($),value:t,key:"root",breadcrumbs:p},...s)):y.done(t),y.onComplete(e=>{if(f.delete("objectCallback at 'root'"),e!==$)switch(n(e)){case"array":c=[],a(e,c,u,m,p,...s).then(()=>o(u,c,b));break;case"object":c={},a(e,c,u,m,p,...s).then(()=>{o(u,c,b)});break;case"simple":b.done(e)}else b.done("array"===d?[]:{})}),null==i)return b.promise;const k=Symbol("expired___"),j=e();return b.timeout(i,k),b.onComplete(e=>{if(e!==k)return void j.done(e);const o=[...f].map(e=>`\n - ${e}`).join("");j.cancel(new Error(`walk-async: timed out after ${i}ms; callbacks still pending:${o}`))}),j.promise}});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peter.naydenov/walk-async",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Deep copy with fine control during the process. Can be used also as a deep forEach function",
|
|
5
5
|
"main": ".dist/walk-async.umd.js",
|
|
6
6
|
"type": "module",
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
"./src/*": "./src/*"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"test": "
|
|
20
|
-
"
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest",
|
|
21
|
+
"cover": "vitest run --coverage",
|
|
21
22
|
"build": "rollup -c",
|
|
22
23
|
"postbuild": "tsc -p tsconfig.json"
|
|
23
24
|
},
|
|
@@ -34,29 +35,27 @@
|
|
|
34
35
|
"@rollup/plugin-commonjs": "^29.0.3",
|
|
35
36
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
36
37
|
"@rollup/plugin-terser": "^1.0.0",
|
|
37
|
-
"
|
|
38
|
-
"chai": "^6.2.2",
|
|
39
|
-
"mocha": "^11.7.6",
|
|
38
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
40
39
|
"rollup": "^4.62.2",
|
|
41
|
-
"typescript": "^
|
|
40
|
+
"typescript": "^7.0.2",
|
|
41
|
+
"vite": "^8.1.4",
|
|
42
|
+
"vitest": "3.2.4"
|
|
42
43
|
},
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"text-summary"
|
|
59
|
-
]
|
|
44
|
+
"vitest": {
|
|
45
|
+
"coverage": {
|
|
46
|
+
"include": [
|
|
47
|
+
"src/**/*.js"
|
|
48
|
+
],
|
|
49
|
+
"exclude": [
|
|
50
|
+
"node_modules",
|
|
51
|
+
"test",
|
|
52
|
+
"test-data"
|
|
53
|
+
],
|
|
54
|
+
"reporter": [
|
|
55
|
+
"lcov",
|
|
56
|
+
"text-summary"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
60
59
|
},
|
|
61
60
|
"browserslist": "> 2%, not dead",
|
|
62
61
|
"allowScripts": {
|
package/src/main.js
CHANGED
|
@@ -21,40 +21,45 @@ import askForPromise from "ask-for-promise"
|
|
|
21
21
|
* @property {Object} data - The data structure to be copied.
|
|
22
22
|
* @property {Function|null} objectCallback - A function called for every object in the data structure.
|
|
23
23
|
* @property {Function|null} keyCallback - A function called for every property in the data structure.
|
|
24
|
+
* @property {number|null} [timeout] - Milliseconds. When set, the promise is rejected if callbacks do not resolve in time. Error lists the pending callbacks.
|
|
24
25
|
*/
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
* Walk-async is a deep copy function that executes callback functions on every object and property.
|
|
31
|
-
* Callbacks are async functions.
|
|
32
|
-
*
|
|
31
|
+
* Walk-async is a deep copy function that executes callback functions on every object and property.
|
|
32
|
+
* Callbacks are async functions.
|
|
33
|
+
*
|
|
33
34
|
* @param {Options} options - Object with following properties:
|
|
34
35
|
* - data - The data structure to be copied.
|
|
35
36
|
* - objectCallback - A function that will be called for every object in the data structure.
|
|
36
37
|
* - keyCallback - A function that will be called for every property in the data structure.
|
|
38
|
+
* - timeout - Milliseconds. When set, the promise is rejected if callbacks do not resolve in time.
|
|
37
39
|
* @param {...any} args - Any additional arguments will be passed to the callback functions.
|
|
38
|
-
*
|
|
40
|
+
*
|
|
39
41
|
* @returns {Promise} - A promise that resolves to the immutable copy of the data structure.
|
|
40
42
|
*/
|
|
41
|
-
function walk ({
|
|
43
|
+
function walk ({
|
|
42
44
|
data:origin
|
|
43
45
|
, objectCallback = null
|
|
44
46
|
, keyCallback = null
|
|
47
|
+
, timeout = null
|
|
45
48
|
}, ...args ) {
|
|
46
49
|
let
|
|
47
50
|
type = findType ( origin )
|
|
48
51
|
, result
|
|
49
52
|
, extend = []
|
|
50
53
|
, breadcrumbs = 'root'
|
|
51
|
-
,
|
|
54
|
+
, pending = new Set () // Callbacks in flight. Read on timeout to report which ones never resolved.
|
|
55
|
+
, cb = [ keyCallback, objectCallback, pending ]
|
|
52
56
|
, end = askForPromise ()
|
|
53
57
|
, rootTask = askForPromise ()
|
|
54
58
|
, IGNORE = Symbol ( 'ignore___' )
|
|
55
59
|
;
|
|
56
60
|
|
|
57
61
|
if ( type !== 'simple' && objectCallback ) { // Root object callback. Executed before the result is allocated, so it can replace the root with anything.
|
|
62
|
+
pending.add ( `objectCallback at 'root'` )
|
|
58
63
|
objectCallback ({
|
|
59
64
|
resolve : rootTask.done
|
|
60
65
|
, reject : () => rootTask.done ( IGNORE )
|
|
@@ -66,6 +71,7 @@ function walk ({
|
|
|
66
71
|
else rootTask.done ( origin )
|
|
67
72
|
|
|
68
73
|
rootTask.onComplete ( item => {
|
|
74
|
+
pending.delete ( `objectCallback at 'root'` )
|
|
69
75
|
if ( item === IGNORE ) {
|
|
70
76
|
end.done ( ( type === 'array' ) ? [] : {} )
|
|
71
77
|
return
|
|
@@ -87,7 +93,20 @@ function walk ({
|
|
|
87
93
|
end.done ( item )
|
|
88
94
|
} // switch type
|
|
89
95
|
})
|
|
90
|
-
|
|
96
|
+
|
|
97
|
+
if ( timeout == null ) return end.promise
|
|
98
|
+
|
|
99
|
+
const // Watchdog. 'end.timeout' races 'end.onComplete' against a timer, but leaves 'end.promise' untouched — so return a guard promise fed from the race instead.
|
|
100
|
+
EXPIRED = Symbol ( 'expired___' )
|
|
101
|
+
, guard = askForPromise ()
|
|
102
|
+
;
|
|
103
|
+
end.timeout ( timeout, EXPIRED )
|
|
104
|
+
end.onComplete ( res => {
|
|
105
|
+
if ( res !== EXPIRED ) { guard.done ( res ); return }
|
|
106
|
+
const stuck = [...pending].map ( name => `\n - ${name}` ).join ( '' )
|
|
107
|
+
guard.cancel ( new Error ( `walk-async: timed out after ${timeout}ms; callbacks still pending:${stuck}` ) )
|
|
108
|
+
})
|
|
109
|
+
return guard.promise
|
|
91
110
|
} // walk func.
|
|
92
111
|
|
|
93
112
|
|
|
@@ -136,8 +155,8 @@ function setKey ( target, k, value ) {
|
|
|
136
155
|
|
|
137
156
|
|
|
138
157
|
function copyObject ( origin, result, extend, cb, breadcrumbs, ...args ) {
|
|
139
|
-
let
|
|
140
|
-
[ keyCallback, objectCallback ] = cb
|
|
158
|
+
let
|
|
159
|
+
[ keyCallback, objectCallback, pending ] = cb
|
|
141
160
|
, keys = Object.keys ( origin )
|
|
142
161
|
, executeCallback = askForPromise ( keys )
|
|
143
162
|
, finish = askForPromise ()
|
|
@@ -155,9 +174,12 @@ function copyObject ( origin, result, extend, cb, breadcrumbs, ...args ) {
|
|
|
155
174
|
, keyNumber = !isNaN ( k )
|
|
156
175
|
, IGNORE = Symbol ( 'ignore___' )
|
|
157
176
|
, br = `${breadcrumbs}/${k}`
|
|
177
|
+
, objectTag = `objectCallback at '${br}'`
|
|
178
|
+
, keyTag = `keyCallback at '${br}'`
|
|
158
179
|
;
|
|
159
180
|
|
|
160
181
|
if ( hasObjectCallback ) {
|
|
182
|
+
pending.add ( objectTag )
|
|
161
183
|
objectCallback ({
|
|
162
184
|
resolve : objectCallbackTask.done
|
|
163
185
|
, reject : () => objectCallbackTask.done ( IGNORE )
|
|
@@ -171,6 +193,7 @@ function copyObject ( origin, result, extend, cb, breadcrumbs, ...args ) {
|
|
|
171
193
|
}
|
|
172
194
|
|
|
173
195
|
objectCallbackTask.onComplete ( res => {
|
|
196
|
+
pending.delete ( objectTag )
|
|
174
197
|
if ( res === '$$cancel' && !keyCallback ) { // deep copy, no callbacks
|
|
175
198
|
keyCallbackTask.done ( '$$noUpdates' )
|
|
176
199
|
return
|
|
@@ -188,9 +211,10 @@ function copyObject ( origin, result, extend, cb, breadcrumbs, ...args ) {
|
|
|
188
211
|
keyCallbackTask.done ( '$$noUpdates' )
|
|
189
212
|
return
|
|
190
213
|
}
|
|
214
|
+
pending.add ( keyTag )
|
|
191
215
|
keyCallback ({
|
|
192
216
|
resolve : keyCallbackTask.done
|
|
193
|
-
, reject : () => keyCallbackTask.done ( IGNORE )
|
|
217
|
+
, reject : () => keyCallbackTask.done ( IGNORE )
|
|
194
218
|
, value : item
|
|
195
219
|
, key : k
|
|
196
220
|
, breadcrumbs : br
|
|
@@ -202,6 +226,7 @@ function copyObject ( origin, result, extend, cb, breadcrumbs, ...args ) {
|
|
|
202
226
|
}) // objectCallbackTask complete
|
|
203
227
|
|
|
204
228
|
keyCallbackTask.onComplete ( value => {
|
|
229
|
+
pending.delete ( keyTag )
|
|
205
230
|
if ( value == IGNORE ) {
|
|
206
231
|
executeCallback.promises[i].done ( 'ignore key' )
|
|
207
232
|
return
|
package/types/main.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* walk-async
|
|
3
|
+
*
|
|
4
|
+
* Alternative of deep-copy that provides much better control during creation of immutable
|
|
5
|
+
* copies of javascript data structures.
|
|
6
|
+
* Library is using 'generator functions'. If support for old browsers is required,
|
|
7
|
+
* add a polyfill for 'generators'.
|
|
8
|
+
*
|
|
9
|
+
* History notes:
|
|
10
|
+
* - Walk-async was born on September 18th, 2022
|
|
11
|
+
* - Converted to ES module on January 1st, 2024
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
2
14
|
export type Options = {
|
|
3
15
|
/**
|
|
4
16
|
* - The data structure to be copied.
|
|
@@ -12,12 +24,17 @@ export type Options = {
|
|
|
12
24
|
* - A function called for every property in the data structure.
|
|
13
25
|
*/
|
|
14
26
|
keyCallback: Function | null;
|
|
27
|
+
/**
|
|
28
|
+
* - Milliseconds. When set, the promise is rejected if callbacks do not resolve in time. Error lists the pending callbacks.
|
|
29
|
+
*/
|
|
30
|
+
timeout?: number | null;
|
|
15
31
|
};
|
|
16
32
|
/**
|
|
17
33
|
* @typedef {Object} Options
|
|
18
34
|
* @property {Object} data - The data structure to be copied.
|
|
19
35
|
* @property {Function|null} objectCallback - A function called for every object in the data structure.
|
|
20
36
|
* @property {Function|null} keyCallback - A function called for every property in the data structure.
|
|
37
|
+
* @property {number|null} [timeout] - Milliseconds. When set, the promise is rejected if callbacks do not resolve in time. Error lists the pending callbacks.
|
|
21
38
|
*/
|
|
22
39
|
/**
|
|
23
40
|
* Walk-async is a deep copy function that executes callback functions on every object and property.
|
|
@@ -27,9 +44,11 @@ export type Options = {
|
|
|
27
44
|
* - data - The data structure to be copied.
|
|
28
45
|
* - objectCallback - A function that will be called for every object in the data structure.
|
|
29
46
|
* - keyCallback - A function that will be called for every property in the data structure.
|
|
47
|
+
* - timeout - Milliseconds. When set, the promise is rejected if callbacks do not resolve in time.
|
|
30
48
|
* @param {...any} args - Any additional arguments will be passed to the callback functions.
|
|
31
49
|
*
|
|
32
50
|
* @returns {Promise} - A promise that resolves to the immutable copy of the data structure.
|
|
33
51
|
*/
|
|
34
|
-
declare function walk({ data: origin, objectCallback, keyCallback }: Options, ...args: any[]): Promise<any>;
|
|
52
|
+
declare function walk({ data: origin, objectCallback, keyCallback, timeout }: Options, ...args: any[]): Promise<any>;
|
|
53
|
+
export default walk;
|
|
35
54
|
//# sourceMappingURL=main.d.ts.map
|
package/types/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.js"],"names":[],"mappings":"AACA;;;;;;;;;;;;GAYG;AAMA,YAAkB,OAAO,GACzB;;;;IAAmB,IAAI,EAAZ,MAAM,CACjB;;;;IAA0B,cAAc,EAA7B,WAAS,IAAI,CACxB;;;;IAA0B,WAAW,EAA1B,WAAS,IAAI,CACxB;;;;IAAyB,OAAO,AAAhC,CACF,EADa,MAAM,GAAC,IAAI,CACxB;CAAA,CAAA;AAND;;;;;;GAMG;AAKH;;;;;;;;;;;;GAYG;AACH,iBAAS,IAAI,CAAE,EACG,IAAI,EAAC,MAAM,EACX,cAAqB,EACrB,WAAkB,EAClB,OAAc,EACnB,EAdF,OAcE,EAAE,GAAG,IAAI,EATR,GAAG,EASK,gBA8DrB;eAsKc,IAAI"}
|