@peter.naydenov/walk-async 1.0.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/Changelog.md ADDED
@@ -0,0 +1,8 @@
1
+ # Release History
2
+
3
+ ### 1.0.0 (2022-09-18)
4
+ - [x] Initial code;
5
+ - [x] Test package;
6
+ - [x] Documentation;
7
+
8
+
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Peter Naydenov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,253 @@
1
+ # Walk-async (@peter.naydenov/walk-async)
2
+
3
+ Creates an immutable copies of javascript data structures(objects, arrays or mixed). Can execute callback functions on every object property(objectCallback) and/or every primitive property(keyCallback). Callbacks can modify result object during the walk process. Mask, filter or substitute values during the copy process.
4
+
5
+ ```js
6
+
7
+ walk ({
8
+ data // (required) Any JS object structure
9
+ , objectCallback // (optional) Function executed on each object property
10
+ , keyCallback // (optional) Function executed on each primitive property
11
+ })
12
+ .then ( result => {
13
+ // Result will become a exact deep copy of "data"
14
+ // - if callbacks are not defined
15
+ // - if callbacks are resolved with "value" without modification
16
+ })
17
+ ```
18
+
19
+ It is very simular to [`@peter.naydenov/walk`](https://github.com/PeterNaydenov/walk) but there are some differences:
20
+ - `walk-async` returns a result as a promise;
21
+ - `walk-async` can execute async operations inside callback methods;
22
+ - `walk-async` keyCallback can return as a result object, array or primitives.
23
+ - `walk` keyCallback can return only primitives;
24
+ - `walk` can not execute another walk call inside callback functions;
25
+
26
+
27
+
28
+ ## кeyCallback
29
+ Function **keyCallback** of the `walk-async` could be used also as a deep '**forEach**' method no matter of the type of the object(object or array).
30
+
31
+ ```js
32
+ function keyCallbackFn ({ value, key, breadcrumbs, resolve, reject }) {
33
+ // value: value of the property. Only primitives;
34
+ // key: key of the property;
35
+ // breadcrumbs: location of the property;
36
+ // resolve: function that will resolve the callback promise. Provide the result as argument;
37
+ // reject: function that can cancel the copy of that property;
38
+ // Important: key callback should be resolved or rejected.
39
+ }
40
+
41
+ const result = await walk ({ data, keyCallback : keyCallbackFn }); // It's the short way to provide only key-callback. Callback functions are optional.
42
+ // walk ({ data, keyCallback, objectCallback }); // If both callbacks are available
43
+ ```
44
+
45
+
46
+ ## Object-callback
47
+
48
+ Optional callback function that is started on each object property. Function should resolve or reject. Rejection of the property will remove it from the result.
49
+
50
+ ```js
51
+ function objectCallbackFn ({ value, key, breadcrumbs, resolve, reject }) {
52
+ // value: each object property during the walk;
53
+ // key: key of the object property;
54
+ // breadcrumbs: location of the object;
55
+ // resolve: function that will resolve the callback promise. Provide the result as argument;
56
+ // reject: function that can cancel the copy of that property;
57
+ // Important: Object callback should be resolved or rejected.
58
+ }
59
+
60
+ walk ({
61
+ data
62
+ , keyCallback: keyCallbackFn
63
+ , objectCallback : objectCallbackFn
64
+ })
65
+ .then ( resultOfWalk => {
66
+ // do something with the result of walk
67
+ })
68
+ ```
69
+
70
+ **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.**
71
+
72
+ Skip key-callbacks by not providing a keyCallback function.
73
+ ```js
74
+ let result = await walk ({ data }) // ignore key-callbacks
75
+ ```
76
+
77
+
78
+ ## Installation
79
+
80
+ Install for node.js projects by writing in your terminal:
81
+
82
+ ```
83
+ npm install @peter.naydenov/walk-async
84
+ ```
85
+
86
+ Once it has been installed, it can be used by writing this line of JavaScript:
87
+ ```js
88
+ let walk = require ( '@peter.naydenov/walk-async' )
89
+ ```
90
+
91
+ or
92
+
93
+ ```js
94
+ import walk from '@peter.naydenov/walk-async'
95
+ ```
96
+
97
+ **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.
98
+
99
+ Note:
100
+ Library is using 'generator functions'. If support for old browsers
101
+ is required, add a polyfill for 'generators'.
102
+
103
+
104
+
105
+
106
+
107
+ ## How to use it
108
+
109
+ ### Deep copy
110
+ ```js
111
+ const myCopy = await walk ({ data:x }) // where x is some javascript data structure
112
+ ```
113
+
114
+
115
+
116
+ ### Deep 'forEach'
117
+ ```js
118
+ let x = {
119
+ ls : [ 1,2,3 ]
120
+ , name : 'Peter'
121
+ , props : {
122
+ eyeColor: 'blue'
123
+ , age : 47
124
+ , height : 176
125
+ , sizes : [12,33,12,21]
126
+ }
127
+ };
128
+
129
+ function keyFn ({value,key, breadcrumbs, resolve}) {
130
+ console.log (`${key} ----> ${value}`) // Show each each primitive couples key->value
131
+ console.log ( `Property location >> ${breadcrumbs}`)
132
+ // example for breadcrumbs: 'age' will looks like this : 'root/props/age'
133
+ resolve ( value )
134
+ }
135
+
136
+ walk ({ data:x, keyCallback: keyFn })
137
+ .then ( result => {
138
+ // result is a deep copy of x
139
+ })
140
+ ```
141
+
142
+
143
+ ### Ignore a key
144
+
145
+ ```js
146
+ let x = {
147
+ ls : [ 1,2,3 ]
148
+ , name : 'Peter'
149
+ , props : {
150
+ eyeColor: 'blue'
151
+ , age : 47
152
+ , height : 176
153
+ , sizes : [12,33,12,21]
154
+ }
155
+ };
156
+ function keyFn ({value,key,resolve,reject}) {
157
+ if ( key === 'name' ) reject ()
158
+ else resolve ( value )
159
+ }
160
+
161
+ walk ({
162
+ data : x
163
+ , keyCallback : keyFn
164
+ })
165
+ .then ( result => {
166
+ // result will copy all properties from x without the property 'name'.
167
+ // result.name === undefined
168
+ })
169
+ ```
170
+
171
+
172
+ ### Mask values
173
+
174
+ ```js
175
+ let x = {
176
+ ls : [ 1,2,3 ]
177
+ , name : 'Peter'
178
+ , props : {
179
+ eyeColor: 'blue'
180
+ , age : 47
181
+ , height : 176
182
+ , sizes : [12,33,12,21]
183
+ }
184
+ };
185
+ walk ({
186
+ data:x
187
+ , keyCallback : ({resolve}) => resolve('xxx')
188
+ })
189
+ .then ( result => {
190
+ // 'result' will have the same structure as 'x' but all values are 'xxx'
191
+ // {
192
+ // ls : [ 'xxx','xxx','xxx' ]
193
+ // , name : 'xxx'
194
+ // , props : {
195
+ // eyeColor: 'xxx'
196
+ // , age : 'xxx'
197
+ // , height : 'xxx'
198
+ // , sizes : ['xxx','xxx','xxx','xxx']
199
+ // }
200
+ // }
201
+ })
202
+ ```
203
+
204
+ ### Change object on condition
205
+
206
+ ```js
207
+ let x = {
208
+ ls : [ 1,2,3 ]
209
+ , name : 'Peter'
210
+ , props : {
211
+ eyeColor: 'blue'
212
+ , age : 48
213
+ , height : 176
214
+ , sizes : [12,33,12,21]
215
+ }
216
+ };
217
+
218
+ function objectCallback ({ value:obj, key, resolve }) {
219
+ const {age, height} = obj;
220
+ if ( age && age > 30 ) {
221
+ resolve ({ age, height })
222
+ return
223
+ }
224
+ resolve ( obj )
225
+ }
226
+
227
+ walk ({
228
+ data: x
229
+ , objectCallback
230
+ })
231
+ .then ( result => {
232
+ // 'result.props' will have only 'age' and 'height' properties.
233
+ // {
234
+ // ls : [ 1,2,3 ]
235
+ // , name : 'Peter'
236
+ // , props : {
237
+ // age : 48
238
+ // , height : 176
239
+ // }
240
+ // }
241
+ })
242
+ ```
243
+
244
+
245
+
246
+ ## Links
247
+ - [Release history](Changelog.md)
248
+
249
+ ## Credits
250
+ '@peter.naydenov/walk-async' was created and supported by Peter Naydenov.
251
+
252
+ ## License
253
+ '@peter.naydenov/walk-async' is released under the MIT License.
@@ -0,0 +1 @@
1
+ var walk;(()=>{var e={406:e=>{"use strict";function n(){let e,n;const t=new Promise(((o,t)=>{e=o,n=t}));return{promise:t,done:e,cancel:n,onComplete:o(t)}}function o(e){return function(n){e.then((e=>n(e)))}}e.exports=function(e){let t,r=!1;return e?(t=function(e){let t=e.map((e=>n())),r=t.map((e=>e.promise));return t.promises=r,t.onComplete=o(Promise.all(r)),t}(e),r=!0):t=n(),t.timeout=function(e,n){let t;return t=e?Promise.all(n.promises):n.promise,function(e,r){let s,l=new Promise(((n,o)=>{s=setTimeout((()=>{n(r),Promise.resolve(t)}),e)}));return t.then((()=>clearTimeout(s))),n.onComplete=o(Promise.race([t,l])),n}}(r,t),t}},880:(e,n,o)=>{const t=o(406);async function r(e,n,o){for await(const n of e)await n.next();o.done(n)}function s(e){return e instanceof Array?"array":"object"==typeof e?"object":"simple"}async function*l(e,n,o,t,r){yield await a(e,n,o,t,r)}function a(e,n,o,r,a){let[c,i]=r,u=Object.keys(e),p=t(u),m=t();return u.forEach(((u,m)=>{let d=s(e[u]),f=e[u],$="simple"!==d&&null!=i,y=t(),b=t(),h=t(),k="array"===s(n),v=!isNaN(u);$?i({resolve:y.done,reject:()=>y.done(null),value:f,key:u,breadcrumbs:`${a}/${u}`}):y.done("$$cancel"),y.onComplete((e=>{"$$cancel"!==e&&(f=e,d=s(f)),null!=f?"simple"===d?(c||b.done("$$noUpdates"),c({resolve:b.done,reject:()=>b.done(null),value:f,key:u,breadcrumbs:`${a}/${u}`})):b.done("$$cancel"):p[m].done()})),b.onComplete((e=>{if(null!=e)if("$$cancel"!==e){if("$$noUpdates"!==e&&(f=e,d=s(f)),"simple"===d){const o=function(e,n){return n instanceof Array&&!isNaN(e)}(u,n);return o?n.push(e):n[u]=e,void p[m].done("key")}h.done()}else h.done();else p[m].done("nulls")})),h.onComplete((()=>{if("object"===d){const e={};k&&v?n.push(e):n[u]=e,o.push(l(f,e,o,r,`${a}/${u}`)),p[m].done("object")}if("array"===d){const e=[];k&&v?n.push(e):n[u]=e,o.push(l(f,e,o,r,`${a}/${u}`)),p[m].done("array")}}))})),p.onComplete((e=>m.done())),m.promise}e.exports=function(e){let n,{data:o,objectCallback:l=null,keyCallback:c=null}=e,i=s(o),u=[],p="root",m=[c,l],d=t();switch(i){case"array":n=[],a(o,n,u,m,p).then((()=>r(u,n,d)));break;case"object":n={},a(o,n,u,m,p).then((()=>{r(u,n,d)}));break;case"simple":d.done(o)}return d.promise}}},n={},o=function o(t){var r=n[t];if(void 0!==r)return r.exports;var s=n[t]={exports:{}};return e[t](s,s.exports,o),s.exports}(880);walk=o})();
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@peter.naydenov/walk-async",
3
+ "version": "1.0.0",
4
+ "description": "Deep copy with fine control during the process. Can be used also as a deep forEach function",
5
+ "main": "src/main.js",
6
+ "scripts": {
7
+ "test": "mocha test",
8
+ "cover": "nyc mocha test",
9
+ "build": "webpack --config ./webpack.config.js"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/PeterNaydenov/walk-async.git"
14
+ },
15
+ "author": "Peter Naydenov",
16
+ "license": "MIT",
17
+ "dependencies": {
18
+ "ask-for-promise": "1.3.1"
19
+ },
20
+ "devDependencies": {
21
+ "@babel/core": "^7.19.1",
22
+ "@babel/preset-env": "^7.19.1",
23
+ "babel-loader": "^8.2.5",
24
+ "chai": "^4.3.6",
25
+ "mocha": "^10.0.0",
26
+ "nyc": "^15.1.0",
27
+ "webpack": "^5.74.0",
28
+ "webpack-cli": "^4.10.0"
29
+ },
30
+ "nyc": {
31
+ "include": [
32
+ "src/**/*.js"
33
+ ],
34
+ "exclude": [
35
+ "node_modules",
36
+ "test",
37
+ "test-data"
38
+ ],
39
+ "reporter": [
40
+ "lcov",
41
+ "text-summary"
42
+ ]
43
+ },
44
+ "browserslist": "> 2%, not dead"
45
+ }
package/src/main.js ADDED
@@ -0,0 +1,191 @@
1
+
2
+ /**
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,
8
+ * add a polyfill for 'generators'.
9
+ *
10
+ * History notes:
11
+ * - Walk-async was born on September 18th, 2022
12
+ *
13
+ */
14
+
15
+
16
+
17
+ const askForPromise = require ( "ask-for-promise" );
18
+
19
+
20
+
21
+ function walk ({
22
+ data:origin
23
+ , objectCallback = null
24
+ , keyCallback = null
25
+ }) {
26
+ let
27
+ type = findType ( origin )
28
+ , result
29
+ , extend = []
30
+ , breadcrumbs = 'root'
31
+ , cb = [ keyCallback, objectCallback ]
32
+ , end = askForPromise ()
33
+ ;
34
+ switch ( type ) {
35
+ case 'array' :
36
+ result = []
37
+ copyObject ( origin, result, extend, cb, breadcrumbs )
38
+ .then ( () => goNext ( extend, result, end ))
39
+ break
40
+ case 'object' :
41
+ result = {}
42
+ copyObject ( origin, result, extend, cb, breadcrumbs )
43
+ .then ( () => {
44
+ goNext ( extend, result, end )
45
+ })
46
+ break
47
+ case 'simple' :
48
+ end.done ( origin )
49
+ } // switch type
50
+ return end.promise
51
+ } // walk func.
52
+
53
+
54
+
55
+ async function goNext ( extend, result, end ) {
56
+ for await ( const plus of extend ) {
57
+ await plus.next ()
58
+ }
59
+ end.done ( result )
60
+ } // goNext func.
61
+
62
+
63
+
64
+ function findType ( x ) {
65
+ if ( x instanceof Array ) return 'array'
66
+ if ( typeof x === 'object' ) return 'object'
67
+ return 'simple'
68
+ } // findType func.
69
+
70
+
71
+
72
+ async function* generateList ( data, location, ex, cb, breadcrumbs ) {
73
+ yield await copyObject ( data , location, ex, cb, breadcrumbs )
74
+ } // generateList func.
75
+
76
+
77
+ function validateForInsertion ( k, result ) {
78
+ const inArray = result instanceof Array;
79
+ if ( !inArray ) return false
80
+ const isNumber = !isNaN ( k );
81
+ if ( isNumber ) return true
82
+ else return false
83
+ } // insertInArray func.
84
+
85
+
86
+
87
+ function copyObject ( origin, result, extend, cb, breadcrumbs ) {
88
+ let
89
+ [ keyCallback, objectCallback ] = cb
90
+ , keys = Object.keys ( origin )
91
+ , executeCallback = askForPromise ( keys )
92
+ , finish = askForPromise ()
93
+ ;
94
+ keys.forEach ( (k,i) => {
95
+ let
96
+ type = findType(origin[k])
97
+ , item = origin[k]
98
+ , hasObjectCallback = ( type !== 'simple' && objectCallback != null )
99
+ , objectCallbackTask = askForPromise ()
100
+ , keyCallbackTask = askForPromise ()
101
+ , finishWithCallbacks = askForPromise ()
102
+ , resultIsArray = (findType (result) === 'array')
103
+ , keyNumber = !isNaN ( k )
104
+ ;
105
+
106
+ if ( hasObjectCallback ) {
107
+ objectCallback ({
108
+ resolve : objectCallbackTask.done
109
+ , reject : () => objectCallbackTask.done ( null )
110
+ , value : item
111
+ , key : k
112
+ , breadcrumbs : `${breadcrumbs}/${k}`
113
+ })
114
+ }
115
+ else {
116
+ objectCallbackTask.done ( '$$cancel' )
117
+ }
118
+
119
+ objectCallbackTask.onComplete ( res => {
120
+ if ( res !== '$$cancel' ) {
121
+ item = res
122
+ type = findType ( item )
123
+ }
124
+ if ( item == null ) {
125
+ executeCallback[i].done ()
126
+ return
127
+ }
128
+ if ( type === 'simple' ) {
129
+ if ( !keyCallback ) keyCallbackTask.done ( '$$noUpdates' )
130
+ keyCallback ({
131
+ resolve : keyCallbackTask.done
132
+ , reject : () => keyCallbackTask.done ( null )
133
+ , value : item
134
+ , key : k
135
+ , breadcrumbs : `${breadcrumbs}/${k}`
136
+ });
137
+ }
138
+ else {
139
+ keyCallbackTask.done ( '$$cancel' )
140
+ }
141
+ }) // objectCallbackTask complete
142
+
143
+ keyCallbackTask.onComplete ( value => {
144
+ if ( value == null ) {
145
+ executeCallback[i].done ( 'nulls' )
146
+ return
147
+ }
148
+ if ( value === '$$cancel' ) {
149
+ finishWithCallbacks.done ()
150
+ return
151
+ }
152
+ if ( value !== '$$noUpdates' ) {
153
+ item = value
154
+ type = findType ( item )
155
+ }
156
+ if ( type === 'simple' ) {
157
+ const canInsert = validateForInsertion ( k, result )
158
+ if ( canInsert ) result.push ( value )
159
+ else result [k] = value
160
+ executeCallback[i].done ('key')
161
+ return
162
+ }
163
+ finishWithCallbacks.done ()
164
+ }) // keyCallbackTask complete
165
+
166
+ finishWithCallbacks.onComplete ( () => {
167
+ if ( type === 'object' ) {
168
+ const newObject = {};
169
+ if ( resultIsArray && keyNumber ) result.push ( newObject )
170
+ else result[k] = newObject
171
+ extend.push ( generateList( item, newObject, extend, cb, `${breadcrumbs}/${k}` ) )
172
+ executeCallback[i].done ('object')
173
+ }
174
+ if ( type === 'array' ) {
175
+ const newArray = [];
176
+ if ( resultIsArray && keyNumber ) result.push ( newArray )
177
+ else result[k] = newArray
178
+ extend.push ( generateList( item, newArray, extend, cb, `${breadcrumbs}/${k}` ) )
179
+ executeCallback[i].done ('array')
180
+ }
181
+ })
182
+ }) // forEach k
183
+ executeCallback.onComplete ( r => finish.done () )
184
+ return finish.promise
185
+ } // copyObject func.
186
+
187
+
188
+
189
+ module.exports = walk
190
+
191
+
@@ -0,0 +1,79 @@
1
+ "use strict"
2
+
3
+ const
4
+ chai = require ( 'chai' )
5
+ , expect = chai.expect
6
+ , walk = require ( '../src/main.js' )
7
+ ;
8
+
9
+
10
+ describe ( 'Walk-async -> Deep copy', () => {
11
+
12
+ it ( 'Copy a primitive value', () => {
13
+ let x = 12;
14
+ walk ({data:x})
15
+ .then ( r => {
16
+ x = 64
17
+ expect ( r ).to.be.equal ( 12 )
18
+ expect ( r !== x )
19
+ })
20
+ }) // it copy a primitives
21
+
22
+
23
+
24
+ it ( 'Copy array of strings', () => {
25
+ let x = [ 'one', 'two', 'three' ];
26
+ walk ({data:x})
27
+ .then ( r => {
28
+ x.push ( 'four' )
29
+ expect ( r ).to.have.length ( 3 )
30
+ })
31
+ }) // it copy array of strings
32
+
33
+
34
+
35
+ it ( 'Copy a single level deep object', () => {
36
+ let
37
+ x = { name:'Peter', age: 47 }
38
+ , z = x
39
+ ;
40
+ walk ( {data:x})
41
+ .then ( r => {
42
+ x.test = 'hello'
43
+ expect ( r ).to.not.have.property ( 'test' )
44
+ expect ( z ).to.have.property ( 'test' )
45
+ })
46
+ }) // it copy a single level deep object
47
+
48
+
49
+
50
+ it ( 'Copy a mixed structure', () => {
51
+ let
52
+ x = {
53
+ ls : [ 1,2,3 ]
54
+ , name : 'Peter'
55
+ , props : {
56
+ eyeColor: 'blue'
57
+ , age : 47
58
+ , height : 176
59
+ , sizes : [12,33,12,21]
60
+ }
61
+ }
62
+ ;
63
+ walk ( x )
64
+ .then ( r => {
65
+ r.props.sizes.push ( 66 )
66
+ x.props.sizes[0] = 222222
67
+ x.props.test = 'hello'
68
+
69
+ expect ( x.props.sizes ).to.have.length ( 4 )
70
+ expect ( r.props.sizes ).to.have.length ( 5 )
71
+ expect ( x.props.sizes[0] !== r.props.sizes[0])
72
+
73
+ expect ( r.props ).to.not.have.property ( 'test' )
74
+ })
75
+ }) // it Copy a mixed structure
76
+
77
+ }) // describe
78
+
79
+
@@ -0,0 +1,151 @@
1
+ "use strict"
2
+
3
+ const
4
+ chai = require ( 'chai' )
5
+ , expect = chai.expect
6
+ , walk = require ( '../src/main.js' )
7
+ ;
8
+
9
+
10
+ describe ( 'Walk-async -> keyCallback function', () => {
11
+
12
+ it ( 'Hide a property', () => {
13
+ let
14
+ x = {
15
+ ls : [ 1,2,3 ]
16
+ , name : 'Peter'
17
+ , props : {
18
+ eyeColor: 'blue'
19
+ , age : 47
20
+ , height : 176
21
+ , sizes : [12,33,12,21]
22
+ }
23
+ }
24
+ ;
25
+ walk ({
26
+ data : x
27
+ , keyCallback : ({
28
+ key : k
29
+ , value
30
+ , resolve
31
+ , reject
32
+ }) => {
33
+ if ( k === 'name' ) reject ()
34
+ else resolve ( value )
35
+ }
36
+ })
37
+ .then ( r => {
38
+ expect ( r ).to.not.have.property ( 'name' )
39
+ })
40
+ }) // it Hide a property
41
+
42
+
43
+
44
+ it ( 'Provide a structure. Hide values approaches', () => {
45
+ let
46
+ x = {
47
+ ls : [ 1,2,3 ]
48
+ , name : 'Peter'
49
+ , props : {
50
+ eyeColor: 'blue'
51
+ , age : 47
52
+ , height : 176
53
+ , sizes : [12,33,12,21]
54
+ }
55
+ };
56
+ walk ({
57
+ data:x
58
+ , keyCallback : ({ resolve, reject, breadcrumbs }) => {
59
+ const hasSizes = breadcrumbs.includes('root/props/sizes');
60
+ if ( hasSizes ) reject ()
61
+ else resolve ( 'xxx' )
62
+ }
63
+ })
64
+ .then ( r => {
65
+ expect ( r.name ).to.be.equal ( 'xxx' )
66
+ expect ( r.props.age ).to.be.equal ( 'xxx' )
67
+ expect ( r.props.sizes.length ).to.be.equal ( 0 )
68
+ })
69
+ }) // it Provide a structure
70
+
71
+
72
+ it ( 'No properties. Just structures', () => {
73
+ let
74
+ x = {
75
+ ls : [ 1,2,3 ]
76
+ , name : 'Peter'
77
+ , props : {
78
+ eyeColor: 'blue'
79
+ , age : 47
80
+ , height : 176
81
+ , sizes : [12,33,12,21]
82
+ }
83
+ };
84
+ walk ({
85
+ data : x
86
+ , keyCallback : ({reject}) => reject ()
87
+ })
88
+ .then ( r => {
89
+ expect ( r ).to.have.property ( 'ls' )
90
+ expect ( r ).to.have.property ( 'props' )
91
+ expect ( r.props ).to.have.property ( 'sizes' )
92
+
93
+ expect ( r ).to.not.have.property ( 'name' )
94
+ expect ( r.props ).to.not.have.property ( 'age' )
95
+
96
+ expect ( r.ls.length ).to.be.equal ( 0 )
97
+ expect ( r.props.sizes.length ).to.be.equal ( 0 )
98
+ })
99
+ }) // it No properties
100
+
101
+
102
+ it ( 'Convert primitive property to object', () => {
103
+ let
104
+ x = [ 'peter', 'ivan', 'petkan', 'rosica' ]
105
+ , data = {
106
+ "ivan" : { name: 'Ivan', age: 45, gender: 'male' }
107
+ , "petkan" : { name: 'Petkan', age: 32, gender: 'male' }
108
+ , "rosica" : { name: "Rosica", age: 75, gender: 'female'}
109
+ }
110
+ ;
111
+ function keyCallback ({ value, resolve, reject }) {
112
+ const person= data[value];
113
+ if ( person ) {
114
+ if ( person.age > 33 ) resolve ( person )
115
+ else reject ()
116
+ return
117
+ }
118
+ else resolve ( value )
119
+ }
120
+ walk ({
121
+ data : x
122
+ , keyCallback
123
+ })
124
+ .then ( r => {
125
+ expect ( r.length ).to.be.equal ( 3 ) // ignore 'petkan'
126
+ expect ( r[0] ).to.be.equal ( 'Peter' ) // no description for Peter available
127
+ expect ( r[1]['name']).to.be.equal ( 'Ivan' )
128
+ expect ( r[2]['name']).to.be.equal ( 'Rosica' )
129
+ })
130
+ }) // it Convert primitive property to object
131
+
132
+
133
+
134
+ it ( 'Properties of the array object', () => {
135
+ let data = [ 'peter', 'ivan' ];
136
+ data.group = 'work'
137
+
138
+ function keyCallback ({value, resolve }) {
139
+ resolve ( value )
140
+ }
141
+
142
+ walk ({ data, keyCallback })
143
+ .then ( r => {
144
+ expect ( r.length ).to.be.equal ( 2 )
145
+ expect ( r.group ).to.be.equal ( 'work' )
146
+ })
147
+ }) // it Properties of the array
148
+
149
+ }) // describe
150
+
151
+
@@ -0,0 +1,249 @@
1
+ "use strict"
2
+
3
+ const
4
+ chai = require ( 'chai' )
5
+ , expect = chai.expect
6
+ , walk = require ( '../src/main.js' )
7
+ ;
8
+
9
+
10
+ describe ( 'Walk-async -> objectCallback function', () => {
11
+
12
+ it ( 'Object callback function only', () => {
13
+ let
14
+ x = {
15
+ ls : [ 1,2,3 ]
16
+ , name : 'Peter'
17
+ , props : {
18
+ eyeColor: 'blue'
19
+ , age : 47
20
+ , height : 176
21
+ , sizes : [12,33,12,21]
22
+ }
23
+ };
24
+
25
+ function oCallbackFn ({value,resolve}) {
26
+ const { age, height } = value;
27
+ if ( age === 47 ) resolve ({ eyeColor:'dark', height })
28
+ else resolve ( value )
29
+ }
30
+
31
+ walk ( {
32
+ data : x
33
+ , objectCallback : oCallbackFn
34
+ })
35
+ .then ( r => {
36
+ expect ( r.props ).to.not.have.property ( 'age' )
37
+ expect ( r.props ).to.not.have.property ( 'sizes' )
38
+ expect ( r.props.eyeColor ).to.be.equal ( 'dark' )
39
+ })
40
+ }) // it object callback
41
+
42
+
43
+
44
+ it ( 'Object callback returns null', () => {
45
+ let
46
+ x = {
47
+ ls : [ 1,2,3 ]
48
+ , name : 'Peter'
49
+ , props : {
50
+ eyeColor: 'blue'
51
+ , age : 47
52
+ , height : 176
53
+ , sizes : [12,33,12,21]
54
+ }
55
+ };
56
+
57
+ function oCallbackFn ({ value:o, resolve, reject }) {
58
+ const { sizes } = o;
59
+ if ( sizes ) reject ()
60
+ else resolve (o)
61
+ }
62
+
63
+ walk ({
64
+ data : x
65
+ , objectCallback : oCallbackFn
66
+ })
67
+ .then ( r => {
68
+ expect ( r ).to.not.have.property ( 'props' )
69
+ })
70
+ }) // it object callback null
71
+
72
+
73
+
74
+ it ( 'Object callback returns a string', () => {
75
+ let
76
+ x = {
77
+ ls : [ 1,2,3 ]
78
+ , name : 'Peter'
79
+ , props : {
80
+ eyeColor: 'blue'
81
+ , age : 47
82
+ , height : 176
83
+ , sizes : [12,33,12,21]
84
+ }
85
+ };
86
+
87
+ function oCallbackFn ({ value:o, resolve }) {
88
+ const { sizes } = o;
89
+ if ( sizes ) resolve ( 'list' )
90
+ else resolve ( o )
91
+ }
92
+
93
+ walk ({
94
+ data: x
95
+ , objectCallback : oCallbackFn
96
+ })
97
+ .then ( r => {
98
+ expect ( r ).to.have.property ( 'props' )
99
+ expect ( r.props ).to.be.equal ( 'list' )
100
+ })
101
+ }) // it object callback null
102
+
103
+
104
+
105
+ it ( 'Object callback changes the data', () => {
106
+ let
107
+ x = {
108
+ ls : [ 1,2,3 ]
109
+ , name : 'Peter'
110
+ , props : {
111
+ eyeColor: 'blue'
112
+ , age : 47
113
+ , height : 176
114
+ , sizes : [12,33,12,21]
115
+ }
116
+ };
117
+
118
+ function oCallbackFn ({ value:o, resolve }) {
119
+ const { sizes } = o;
120
+ if ( sizes ) o.sizes = [ 'list of sizes' ]
121
+ resolve ( o )
122
+ }
123
+
124
+ walk ({
125
+ data : x
126
+ , objectCallback : oCallbackFn
127
+ })
128
+ .then ( r => {
129
+ expect ( r ).to.have.property ( 'props' )
130
+ expect ( r.props ).to.have.property ( 'sizes' )
131
+ expect ( r.props.sizes ).to.have.length ( 1 )
132
+ expect ( r.props.sizes[0]).to.be.equal ( 'list of sizes' )
133
+ })
134
+ }) // it object callback changes the data
135
+
136
+
137
+
138
+ it ( 'Object callback checks key', () => {
139
+ let
140
+ x = {
141
+ ls : [ 1,2,3 ]
142
+ , name : 'Peter'
143
+ , props : {
144
+ eyeColor: 'blue'
145
+ , age : 47
146
+ , height : 176
147
+ , sizes : [12,33,12,21]
148
+ }
149
+ };
150
+
151
+ function oCallbackFn ({ value:o, key, resolve, reject }) {
152
+ if ( key === 'props' ) reject ()
153
+ resolve ( o )
154
+ }
155
+
156
+ walk ({
157
+ data: x
158
+ , objectCallback: oCallbackFn
159
+ })
160
+ .then ( r => {
161
+ expect ( r ).to.not.have.property ( 'props' )
162
+ })
163
+ }) // it Object callback checks key
164
+
165
+
166
+
167
+ it ( 'Object callback checks breadcrumbs', () => {
168
+ let
169
+ x = {
170
+ ls : [ 1,2,3 ]
171
+ , name : 'Peter'
172
+ , props : {
173
+ eyeColor: 'blue'
174
+ , age : 47
175
+ , height : 176
176
+ , sizes : [12,33,12,21]
177
+ }
178
+ };
179
+
180
+ function oCallbackFn ({ value:o, breadcrumbs, resolve, reject } ) {
181
+ if ( breadcrumbs === 'root/props' ) reject ()
182
+ resolve ( o )
183
+ }
184
+
185
+ walk ({
186
+ data : x
187
+ , objectCallback : oCallbackFn
188
+ })
189
+ .then ( r => {
190
+ expect ( r ).to.not.have.property ( 'props' )
191
+ })
192
+ }) // it Object callback checks breadcrumbs
193
+
194
+
195
+
196
+ it ( 'Prevent array empty items', () => {
197
+ let
198
+ x = [
199
+ { id: 1 }
200
+ , { id: 2 }
201
+ , { id: 3 }
202
+ , { id: 5 }
203
+ ];
204
+
205
+ function oCallbackFn ({ value:o, resolve, reject }) {
206
+ if ( o.id === 5 ) resolve ( o )
207
+ reject ()
208
+ }
209
+
210
+ walk ({
211
+ data : x
212
+ , objectCallback : oCallbackFn
213
+ })
214
+ .then ( r => {
215
+ expect ( r.length ).to.be.equal ( 1 )
216
+ })
217
+ }) // it Prevent array empty items
218
+
219
+
220
+
221
+ it ( 'Prevent array empty items 2', () => {
222
+ let
223
+ x = [
224
+ [1]
225
+ , [2]
226
+ , [3]
227
+ , [5]
228
+ ];
229
+
230
+ function oCallbackFn ({ value:o, resolve, reject }) {
231
+ if ( o[0] === 5 ) resolve ( o )
232
+ reject ()
233
+ }
234
+
235
+ walk ({
236
+ data : x
237
+ , objectCallback : oCallbackFn
238
+ })
239
+ .then ( r => {
240
+ expect ( r.length ).to.be.equal ( 1 )
241
+ expect ( r[0] ).to.be.equal ( 5 )
242
+ })
243
+ }) // it Prevent array empty items 2
244
+
245
+
246
+
247
+ }) // describe
248
+
249
+
@@ -0,0 +1,83 @@
1
+ "use strict"
2
+
3
+ const
4
+ chai = require ( 'chai' )
5
+ , expect = chai.expect
6
+ , walk = require ( '../src/main.js' )
7
+ , askForPromise = require ( 'ask-for-promise' )
8
+ ;
9
+
10
+
11
+ describe ( 'Walk-async -> Asynchronous tests', () => {
12
+
13
+ it ( 'Call walk inside other walk', () => {
14
+ let data = {
15
+ users : [
16
+ {
17
+ name: 'Peter'
18
+ , friends : ['Tisho', 'Radostin', 'Tony', 'Kalin' ]
19
+ }
20
+ , {
21
+ name: 'Joro'
22
+ , friends : [ 'Katya', 'Ivan', 'Stefan', 'Valentin' ]
23
+ }
24
+ ]
25
+ }
26
+ , friendsWithPermition = [ 'Kalin', 'Aleks', 'Tony', 'Ivan' ]
27
+ ;
28
+
29
+ async function filterObj ({value:obj, task, breadcrumbs }) {
30
+ if ( obj.friends ) {
31
+ function filterNames ({value:person, key, breadcrumbs, task:scanning} ) {
32
+ let validated = friendsWithPermition.includes(person)
33
+ if ( validated ) scanning.done ( person )
34
+ else scanning.done ( null )
35
+ } // filterNames func.
36
+ walk ({
37
+ data: obj.friends
38
+ , keyCallback : filterNames
39
+ })
40
+ .then ( r => {
41
+ obj.friends = r
42
+ task.done ( obj )
43
+ })
44
+ return
45
+ }
46
+ // if ( obj. ) {
47
+ // function filterNames ({value:user, key, breadcrumbs, task:scanning} ) {
48
+ // let valid = friendsWithPermition.includes(user) ;
49
+ // // if ( valid ) console.log ( user )
50
+ // if ( valid ) scanning ( user )
51
+ // else scanning ( null )
52
+ // } // filterNames func.
53
+ // walk ({
54
+ // data : obj
55
+ // , keyCallback : filterNames
56
+ // }).then ( r => {
57
+ // // console.log ( r )
58
+ // task.done ( r )
59
+ // })
60
+ // return
61
+ // }
62
+ // console.log ( 'Object callback' )
63
+ // console.log ( obj )
64
+ task.done (obj)
65
+ } // filterObj func.
66
+
67
+ // let res = walk ( data, [null, filterObj])
68
+ // console.log ( res.users[0] )
69
+ // console.log ( )
70
+ // walk ( data )
71
+ walk ({
72
+ data
73
+ , objectCallback : filterObj
74
+ })
75
+ .then ( x => {
76
+ console.log ( x.users[0] )
77
+ })
78
+ // console.log ( x )
79
+
80
+
81
+
82
+ }) // it call walk inside other walk
83
+ }) // describe
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ entry: './src/main.js'
5
+ , mode: 'production'
6
+ , output: {
7
+ filename: "walk-async.min.js"
8
+ , path: __dirname + "/dist"
9
+ , library: 'walk'
10
+ , libraryTarget: "var"
11
+ , umdNamedDefine : true
12
+ }
13
+ , optimization: {}
14
+ , module: {
15
+ rules: [
16
+ {
17
+ test: /\.js$/
18
+ , exclude: /node_modules/
19
+ , use : {
20
+ loader: 'babel-loader'
21
+ , options : { presets: ['@babel/preset-env']}
22
+ }
23
+ }
24
+ ]
25
+ }
26
+ }