@peter.naydenov/walk-async 7.0.0 → 7.0.2
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 +17 -0
- package/README.md +3 -0
- package/dist/walk-async.cjs +1 -1
- package/dist/walk-async.esm.mjs +396 -1
- package/dist/walk-async.umd.js +1 -1
- package/package.json +7 -11
- package/skills/README.md +108 -0
- package/skills/walk-async/SKILL.md +83 -0
- package/skills/walk-async/references/callbacks.md +135 -0
- package/skills/walk-async/references/control-flow.md +94 -0
- package/skills/walk-async/references/examples.md +252 -0
- package/skills/walk-async/references/settings.md +60 -0
- package/src/copyObject.js +7 -3
- package/src/main.js +21 -13
- package/src/runCallback.js +14 -7
- package/src/runTask.js +34 -0
- package/types/copyObject.d.ts +1 -6
- package/types/copyObject.d.ts.map +1 -1
- package/types/main.d.ts.map +1 -1
- package/types/runCallback.d.ts.map +1 -1
- package/types/runTask.d.ts +3 -0
- package/types/runTask.d.ts.map +1 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Paths, settings, and cycles
|
|
2
|
+
|
|
3
|
+
## Defaults and speed tuning
|
|
4
|
+
|
|
5
|
+
All settings are enabled unless explicitly set to literal `false`:
|
|
6
|
+
|
|
7
|
+
| Setting | With `false` |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `copy` | Skip result-container allocation and insertion; resolve `undefined` |
|
|
10
|
+
| `breadcrumbs` | Skip breadcrumb preparation; omit the callback field |
|
|
11
|
+
| `parentPath` | Skip parent-array preparation; omit the callback field |
|
|
12
|
+
| `detectCycles` | Skip ancestor bookkeeping and circular-reference checks |
|
|
13
|
+
|
|
14
|
+
Settings are independent. `copy:false` alone does not disable path
|
|
15
|
+
preparation. Without callbacks, neither path is prepared automatically.
|
|
16
|
+
Omitted settings and values other than literal `false` remain enabled.
|
|
17
|
+
|
|
18
|
+
Explain optimisation in terms of unnecessary work avoided. Do not disable
|
|
19
|
+
paths the callback uses. Disable cycle detection only when visited input
|
|
20
|
+
and callback replacements are acyclic, or callbacks prune cyclic branches
|
|
21
|
+
before revisiting them. Otherwise traversal may continue indefinitely.
|
|
22
|
+
|
|
23
|
+
## Paths
|
|
24
|
+
|
|
25
|
+
`breadcrumbs` is a slash-delimited string including the current key, such
|
|
26
|
+
as `'root/profile/age'`. The root object callback receives `'root'`.
|
|
27
|
+
|
|
28
|
+
`parentPath` is a frozen, shared array excluding the current key:
|
|
29
|
+
|
|
30
|
+
| Location | `key` | `parentPath` |
|
|
31
|
+
| --- | --- | --- |
|
|
32
|
+
| Root object callback | `'root'` | `[]` |
|
|
33
|
+
| Immediate root property | Property name | `['root']` |
|
|
34
|
+
| `data.profile.age` | `'age'` | `['root','profile']` |
|
|
35
|
+
| `data.items[0].score` | `'score'` | `['root','items','0']` |
|
|
36
|
+
|
|
37
|
+
Create a full path with `[ ...parentPath, key ]`, or a breadcrumb string
|
|
38
|
+
with `[ ...parentPath, key ].join ( '/' )`. Never mutate `parentPath`.
|
|
39
|
+
Do not reconstruct property boundaries by splitting breadcrumbs: keys may
|
|
40
|
+
contain `/`. Paths use input array indexes, even when output is compacted.
|
|
41
|
+
|
|
42
|
+
Disabled fields are absent. Destructuring one gives `undefined`; check its
|
|
43
|
+
setting before using it. TypeScript declares both path fields as optional.
|
|
44
|
+
|
|
45
|
+
## Cycles and shared containers
|
|
46
|
+
|
|
47
|
+
With detection enabled, a reference back to an ancestor points to that
|
|
48
|
+
ancestor's copy, without revisiting its contents. For `data.self === data`,
|
|
49
|
+
the copied result satisfies `result.self === result`. In no-copy mode,
|
|
50
|
+
traversal stops at the circular edge.
|
|
51
|
+
|
|
52
|
+
Object callbacks still see circular properties and may remove or replace
|
|
53
|
+
them. Ancestor checks happen after callback and replacement promises settle, and
|
|
54
|
+
also apply to
|
|
55
|
+
containers returned by key callbacks. Detection works with both paths off.
|
|
56
|
+
|
|
57
|
+
Shared containers encountered on separate non-ancestor branches are copied
|
|
58
|
+
independently. For `{ a:shared, b:shared }`, `result.a !== result.b`.
|
|
59
|
+
Walk therefore preserves circular ancestor links, not general shared graph
|
|
60
|
+
identity. Choose a graph clone when preserved shared identity is required.
|
package/src/copyObject.js
CHANGED
|
@@ -8,10 +8,13 @@ import PASS, { isPass } from "./pass.js";
|
|
|
8
8
|
import IGNORE, { isIgnore } from "./ignore.js";
|
|
9
9
|
import { isFinish } from "./finish.js";
|
|
10
10
|
import runCallback from "./runCallback.js";
|
|
11
|
+
import runTask from "./runTask.js";
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
function copyObject ( resource, result, extend, cb, breadcrumbs, settings, control, parentPath, pass, parent, ancestors, pending, ...args ) {
|
|
16
|
+
return runTask ( copy() )
|
|
17
|
+
function* copy () {
|
|
15
18
|
let
|
|
16
19
|
[ keyCallback, objectCallback ] = cb
|
|
17
20
|
, keys = Object.keys ( resource )
|
|
@@ -34,7 +37,7 @@ async function copyObject ( resource, result, extend, cb, breadcrumbs, settings,
|
|
|
34
37
|
if ( type !== 'simple' && objectCallback && !control.isFinished ) {
|
|
35
38
|
const callbackArgs = createCallbackArgs ( item, k, IGNORE, br, parentPath, settings )
|
|
36
39
|
callbackArgs.PASS = PASS
|
|
37
|
-
const replacement =
|
|
40
|
+
const replacement = yield runCallback ( objectCallback, callbackArgs, pending, 'objectCallback', ...args )
|
|
38
41
|
if ( isFinish ( replacement ) ) {
|
|
39
42
|
if ( !replacement.hasValue || !keyCallback ) {
|
|
40
43
|
finish ( replacement, result, k, canInsert, settings, control )
|
|
@@ -66,7 +69,7 @@ async function copyObject ( resource, result, extend, cb, breadcrumbs, settings,
|
|
|
66
69
|
}
|
|
67
70
|
const callbackArgs = createCallbackArgs ( item, k, IGNORE, br, parentPath, settings );
|
|
68
71
|
callbackArgs.isFinished = control.isFinished
|
|
69
|
-
let keyRes =
|
|
72
|
+
let keyRes = yield runCallback ( keyCallback, callbackArgs, pending, 'keyCallback', ...args );
|
|
70
73
|
if ( isFinish ( keyRes ) ) {
|
|
71
74
|
finish ( keyRes, result, k, canInsert, settings, control )
|
|
72
75
|
return parents
|
|
@@ -114,6 +117,7 @@ async function copyObject ( resource, result, extend, cb, breadcrumbs, settings,
|
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
return parents
|
|
120
|
+
}
|
|
117
121
|
} // copyObject func.
|
|
118
122
|
|
|
119
123
|
|
package/src/main.js
CHANGED
|
@@ -20,6 +20,8 @@ import createCallbackArgs from "./createCallbackArgs.js";
|
|
|
20
20
|
import PASS, { isPass } from "./pass.js";
|
|
21
21
|
import IGNORE, { isIgnore } from "./ignore.js";
|
|
22
22
|
import runCallback from "./runCallback.js";
|
|
23
|
+
import askForPromise from "ask-for-promise";
|
|
24
|
+
import runTask from "./runTask.js";
|
|
23
25
|
import { isFinish } from "./finish.js";
|
|
24
26
|
|
|
25
27
|
|
|
@@ -179,29 +181,34 @@ import { isFinish } from "./finish.js";
|
|
|
179
181
|
* // If you modify an object with objectCallback, keyCallback will be
|
|
180
182
|
* // executed on the result of objectCallback.
|
|
181
183
|
*/
|
|
182
|
-
|
|
184
|
+
function walk (options,...args) {
|
|
185
|
+
return runTask ( start() )
|
|
186
|
+
|
|
187
|
+
function* start () {
|
|
183
188
|
const pending = new Set ();
|
|
184
189
|
const task = traverse ( options, pending, ...args );
|
|
185
|
-
if ( options.timeout == null ) return task
|
|
190
|
+
if ( options.timeout == null ) return yield task
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
timer = setTimeout ( () => {
|
|
192
|
+
const guard = askForPromise();
|
|
193
|
+
const timer = setTimeout ( () => {
|
|
190
194
|
const stuck = [...pending].map ( name => `\n - ${name}` ).join ( '' );
|
|
191
|
-
|
|
195
|
+
guard.cancel ( new Error ( `walk-async: timed out after ${options.timeout}ms; callbacks still pending:${stuck}` ) )
|
|
192
196
|
}, options.timeout )
|
|
193
|
-
|
|
197
|
+
task.then ( guard.done, guard.cancel )
|
|
194
198
|
try {
|
|
195
|
-
return
|
|
199
|
+
return yield guard.promise
|
|
196
200
|
}
|
|
197
201
|
finally {
|
|
198
202
|
clearTimeout ( timer )
|
|
199
203
|
}
|
|
204
|
+
}
|
|
200
205
|
} // walk func.
|
|
201
206
|
|
|
202
207
|
|
|
203
208
|
|
|
204
|
-
|
|
209
|
+
function traverse ( options, pending, ...args ) {
|
|
210
|
+
return runTask ( visit() )
|
|
211
|
+
function* visit () {
|
|
205
212
|
let
|
|
206
213
|
{ data:origin, keyCallback, objectCallback, settings:config } = options
|
|
207
214
|
, type = findType ( origin )
|
|
@@ -227,7 +234,7 @@ async function traverse ( options, pending, ...args ) {
|
|
|
227
234
|
const rootParent = settings.parentPath ? Object.freeze ( [] ) : undefined
|
|
228
235
|
const callbackArgs = createCallbackArgs ( origin, 'root', IGNORE, breadcrumbs, rootParent, settings )
|
|
229
236
|
callbackArgs.PASS = PASS
|
|
230
|
-
const replacement =
|
|
237
|
+
const replacement = yield runCallback ( objectCallback, callbackArgs, pending, 'objectCallback', ...args )
|
|
231
238
|
if ( isFinish ( replacement ) ) {
|
|
232
239
|
if ( !replacement.hasValue ) {
|
|
233
240
|
if ( !settings.copy ) return
|
|
@@ -252,11 +259,11 @@ async function traverse ( options, pending, ...args ) {
|
|
|
252
259
|
switch ( type ) {
|
|
253
260
|
case 'array' :
|
|
254
261
|
result = settings.copy ? [] : undefined
|
|
255
|
-
parent =
|
|
262
|
+
parent = yield copyObject ( origin, result, extend, cb, breadcrumbs, settings, control, parentPath, pass, undefined, ancestors, pending, ...args )
|
|
256
263
|
break
|
|
257
264
|
case 'object' :
|
|
258
265
|
result = settings.copy ? {} : undefined
|
|
259
|
-
parent =
|
|
266
|
+
parent = yield copyObject ( origin, result, extend, cb, breadcrumbs, settings, control, parentPath, pass, undefined, ancestors, pending, ...args )
|
|
260
267
|
break
|
|
261
268
|
case 'simple' :
|
|
262
269
|
return settings.copy ? origin : undefined
|
|
@@ -268,10 +275,11 @@ async function traverse ( options, pending, ...args ) {
|
|
|
268
275
|
control.restart = false
|
|
269
276
|
if ( settings.detectCycles ) ancestors = moveParents ( parent, job.parent, ancestors )
|
|
270
277
|
const path = settings.parentPath ? Object.freeze ([ ...job.parentPath, job.key ]) : undefined;
|
|
271
|
-
parent =
|
|
278
|
+
parent = yield copyObject ( job.data, job.location, extend, cb, job.breadcrumbs, settings, control, path, job.pass, job.parent, ancestors, pending, ...args )
|
|
272
279
|
index = control.restart ? 0 : index + 1
|
|
273
280
|
}
|
|
274
281
|
return result
|
|
282
|
+
}
|
|
275
283
|
} // traverse func.
|
|
276
284
|
|
|
277
285
|
|
package/src/runCallback.js
CHANGED
|
@@ -3,10 +3,15 @@
|
|
|
3
3
|
import IGNORE from "./ignore.js";
|
|
4
4
|
import PASS, { isPass } from "./pass.js";
|
|
5
5
|
import FINISH, { isFinish } from "./finish.js";
|
|
6
|
+
import askForPromise from "ask-for-promise";
|
|
7
|
+
import runTask from "./runTask.js";
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
function runCallback ( callback, context, pending, name, ...args ) {
|
|
12
|
+
return runTask ( run() )
|
|
13
|
+
|
|
14
|
+
function* run () {
|
|
10
15
|
let
|
|
11
16
|
task
|
|
12
17
|
, done
|
|
@@ -17,11 +22,12 @@ async function runCallback ( callback, context, pending, name, ...args ) {
|
|
|
17
22
|
// Return-based callbacks need no extra settlement promise.
|
|
18
23
|
function explicit () {
|
|
19
24
|
if ( !task ) {
|
|
20
|
-
task =
|
|
25
|
+
task = askForPromise()
|
|
26
|
+
done = task.done
|
|
21
27
|
// Observe rejected values immediately, even while the callback
|
|
22
28
|
// is still awaiting other work. The original rejection is
|
|
23
29
|
// propagated when task is awaited below.
|
|
24
|
-
task.catch ( () => {} )
|
|
30
|
+
task.promise.catch ( () => {} )
|
|
25
31
|
}
|
|
26
32
|
} // explicit func.
|
|
27
33
|
|
|
@@ -38,16 +44,17 @@ async function runCallback ( callback, context, pending, name, ...args ) {
|
|
|
38
44
|
|
|
39
45
|
pending.add ( tag )
|
|
40
46
|
try {
|
|
41
|
-
const returned =
|
|
42
|
-
const value = task ?
|
|
47
|
+
const returned = yield callback ( context, ...args );
|
|
48
|
+
const value = task ? yield task.promise : returned;
|
|
43
49
|
// Keep the callback pending while an instruction replacement settles too.
|
|
44
|
-
if ( name === 'objectCallback' && isPass ( value ) && value.hasValue ) return PASS (
|
|
45
|
-
if ( isFinish ( value ) && value.hasValue ) return FINISH (
|
|
50
|
+
if ( name === 'objectCallback' && isPass ( value ) && value.hasValue ) return PASS ( yield value.value )
|
|
51
|
+
if ( isFinish ( value ) && value.hasValue ) return FINISH ( yield value.value )
|
|
46
52
|
return value
|
|
47
53
|
}
|
|
48
54
|
finally {
|
|
49
55
|
pending.delete ( tag )
|
|
50
56
|
}
|
|
57
|
+
}
|
|
51
58
|
} // runCallback func.
|
|
52
59
|
|
|
53
60
|
|
package/src/runTask.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict"
|
|
2
|
+
|
|
3
|
+
import askForPromise from "ask-for-promise";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// Drive the traversal one step at a time, including promised callback values.
|
|
7
|
+
// Each task uses ask-for-promise for settlement and error propagation.
|
|
8
|
+
function runTask ( iterator ) {
|
|
9
|
+
const task = askForPromise();
|
|
10
|
+
|
|
11
|
+
function step ( method, value ) {
|
|
12
|
+
let next;
|
|
13
|
+
try {
|
|
14
|
+
next = iterator[method] ( value )
|
|
15
|
+
}
|
|
16
|
+
catch ( error ) {
|
|
17
|
+
task.cancel ( error )
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
if ( next.done ) {
|
|
21
|
+
task.done ( next.value )
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
const pending = askForPromise();
|
|
25
|
+
pending.done ( next.value )
|
|
26
|
+
pending.promise.then ( value => step ( 'next', value ), error => step ( 'throw', error ) )
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
step ( 'next' )
|
|
30
|
+
return task.promise
|
|
31
|
+
} // runTask func.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
export default runTask
|
package/types/copyObject.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
declare function copyObject(resource: any, result: any, extend: any, cb: any, breadcrumbs: any, settings: any, control: any, parentPath: any, pass: any, parent: any, ancestors: any, pending: any, ...args: any[]): Promise<
|
|
2
|
-
data: any;
|
|
3
|
-
location: any;
|
|
4
|
-
parent: any;
|
|
5
|
-
depth: any;
|
|
6
|
-
} | undefined>;
|
|
1
|
+
declare function copyObject(resource: any, result: any, extend: any, cb: any, breadcrumbs: any, settings: any, control: any, parentPath: any, pass: any, parent: any, ancestors: any, pending: any, ...args: any[]): Promise<any>;
|
|
7
2
|
export default copyObject;
|
|
8
3
|
//# sourceMappingURL=copyObject.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copyObject.d.ts","sourceRoot":"","sources":["../src/copyObject.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"copyObject.d.ts","sourceRoot":"","sources":["../src/copyObject.js"],"names":[],"mappings":"AAcA,iBAAS,UAAU,CAAG,QAAQ,KAAA,EAAE,MAAM,KAAA,EAAE,MAAM,KAAA,EAAE,EAAE,KAAA,EAAE,WAAW,KAAA,EAAE,QAAQ,KAAA,EAAE,OAAO,KAAA,EAAE,UAAU,KAAA,EAAE,IAAI,KAAA,EAAE,MAAM,KAAA,EAAE,SAAS,KAAA,EAAE,OAAO,KAAA,EAAE,GAAG,IAAI,OAAA,gBA0GxI;eA4Bc,UAAU"}
|
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":"AA+BI,YAAU,OAAO,GACjB,CAAW,KAAK,EAAR,GACR,KAAU,IACb,CAAA;AAMG,YAAU,MAAM,GAChB,CAAY,MAAM,AAAlB,CACA,EADQ,GACR,KAAU,IACb,CAAA;AAKG,YAAkB,WAAW,GAAnB,MAAM,CAAa;AAM7B,YAAU,cAAc,GACxB,MAAU,WACb,CAAA;AAQG,YAAU,YAAY,GACtB,CAAY,KAAK,AAAjB,CACA,EADQ,GACR,KAAU,OAAO,WAAW,EAAE,eACjC,CAAA;AAMG,YAAU,cAAc,GACxB,CAAY,KAAK,AAAjB,CACA,EADQ,GACR,KAAU,OAAO,aAAa,EAAE,iBACnC,CAAA;AAKG,YAAkB,YAAY,GAC9B;;;;IAAuB,KAAK,EAAjB,GAAC,CACZ;;;;IAAuB,GAAG,EAAf,MAAM,CACjB;;;;IAAwB,WAAW,AAAnC,CACA,EADW,MAAM,CACjB;;;;IAAmC,UAAU,AAA7C,CACA,EADW,aAAa,CAAC,MAAM,CAAC,CAChC;;;;IAAoB,OAAO,EAAhB,OAAO,CAClB;;;;IAAmB,MAAM,EAAd,MAAM,CACjB;;;;IAA2B,MAAM,EAAtB,cAAc,CACzB;;;;IAA2B,MAAM,EAAtB,cAAc,CAC5B;CAAA,CAAA;AAGG,YAAkD,eAAe,GAAvD,YAAY,GAAG;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,CAAiB;AAIjE,YAAiD,kBAAkB,GAAzD,YAAY,GAAG;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,CAAoB;AAmBnE,YAAU,WAAW,GACrB,CAAyB,IAAI,EAArB,eACR,EAAA,GACA,AADsB,IAAI,EAAf,GACX,KAAU,MAAI,OAAO,KACxB,CAAA;AAeG,YAAU,cAAc,GACxB,CAA4B,IAAI,EAAxB,kBACR,EAAA,GACA,AADsB,IAAI,EAAf,GACX,KAAU,MAAI,OAAO,KACxB,CAAA;AAGG,YAAkB,QAAQ,GAC1B;;;;IAAqB,IAAI,AAAzB,CACA,EADW,OAAO,CAClB;;;;IAAqB,WAAW,AAAhC,CACA,EADW,OAAO,CAClB;;;;IAAqB,UAAU,AAA/B,CACA,EADW,OAAO,CAClB;;;;IAAqB,YAAY,AAAjC,CACH,EADc,OAAO,CACrB;CAAA,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,CACA,EADW,MAAM,CACjB;;;;IAA4B,QAAQ,AAApC,CACH,EADc,QAAQ,CACtB;CAAA,CAAA;AAjID;;;;;;;GAOG;AAEH;;;;;;;GAOG;AAEH;;;;GAIG;AAEH;;;;;GAKG;AAEH;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AAEH;;;;;;;;;;;;GAYG;AAEH;;GAEG;AAEH;;GAEG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;;;GAMG;AAEH;;;;;;;GAOG;AAGH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,iBAAS,IAAI,CAAE,OAAO,EAdV,OAcU,EAAC,GAAG,IAAI,EAbf,GAAC,EAac,GAZhB,OAAO,CAAC,GAAC,CAAC,CAiCvB;eAsHc,IAAI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runCallback.d.ts","sourceRoot":"","sources":["../src/runCallback.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runCallback.d.ts","sourceRoot":"","sources":["../src/runCallback.js"],"names":[],"mappings":"AAUA,iBAAS,WAAW,CAAG,QAAQ,KAAA,EAAE,OAAO,KAAA,EAAE,OAAO,KAAA,EAAE,IAAI,KAAA,EAAE,GAAG,IAAI,OAAA,gBA+C/D;eAIc,WAAW"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runTask.d.ts","sourceRoot":"","sources":["../src/runTask.js"],"names":[],"mappings":"AAOA,iBAAS,OAAO,CAAG,QAAQ,KAAA,gBAuB1B;eAGc,OAAO"}
|