@player-ui/player 0.13.0 → 0.14.0-next.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/dist/Player.native.js +117 -115
- package/dist/Player.native.js.map +1 -1
- package/dist/cjs/index.cjs +75 -116
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.legacy-esm.js +75 -116
- package/dist/index.mjs +75 -116
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/binding/__tests__/resolver.test.ts +5 -0
- package/src/binding/resolver.ts +5 -1
- package/src/binding-grammar/__tests__/parser.test.ts +0 -32
- package/src/binding-grammar/__tests__/test-utils/ast-cases.ts +31 -0
- package/src/binding-grammar/ast.ts +2 -2
- package/src/binding-grammar/custom/index.ts +17 -9
- package/src/view/__tests__/view.test.ts +61 -1
- package/src/view/builder/index.ts +6 -1
- package/src/view/parser/index.ts +45 -33
- package/src/view/parser/types.ts +5 -0
- package/src/view/plugins/__tests__/multi-node.test.ts +36 -0
- package/src/view/plugins/multi-node.ts +14 -14
- package/src/view/resolver/__tests__/index.test.ts +153 -0
- package/src/view/resolver/index.ts +109 -157
- package/src/view/view.ts +2 -2
- package/types/binding-grammar/ast.d.ts +2 -2
- package/types/view/builder/index.d.ts +1 -1
- package/types/view/parser/index.d.ts +38 -22
- package/types/view/parser/types.d.ts +4 -0
- package/types/view/resolver/index.d.ts +37 -25
- package/types/view/view.d.ts +1 -1
package/dist/index.legacy-esm.js
CHANGED
|
@@ -85,7 +85,7 @@ var parse = (path) => {
|
|
|
85
85
|
next();
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
|
-
const identifier = () => {
|
|
88
|
+
const identifier = (allowBoolValue = false) => {
|
|
89
89
|
if (!isIdentifierChar(ch)) {
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
@@ -96,6 +96,14 @@ var parse = (path) => {
|
|
|
96
96
|
}
|
|
97
97
|
value += ch;
|
|
98
98
|
}
|
|
99
|
+
if (allowBoolValue) {
|
|
100
|
+
if (value === "true") {
|
|
101
|
+
return toValue(true);
|
|
102
|
+
}
|
|
103
|
+
if (value === "false") {
|
|
104
|
+
return toValue(false);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
99
107
|
if (value) {
|
|
100
108
|
const maybeNumber = Number(value);
|
|
101
109
|
value = isNaN(maybeNumber) ? value : maybeNumber;
|
|
@@ -143,7 +151,7 @@ var parse = (path) => {
|
|
|
143
151
|
return modelRef;
|
|
144
152
|
}
|
|
145
153
|
};
|
|
146
|
-
const simpleSegment = () => nestedPath() ?? expression() ?? identifier();
|
|
154
|
+
const simpleSegment = (allowBoolValue = false) => nestedPath() ?? expression() ?? identifier(allowBoolValue);
|
|
147
155
|
const segment = () => {
|
|
148
156
|
const segments = [];
|
|
149
157
|
let nextSegment = simpleSegment();
|
|
@@ -156,7 +164,7 @@ var parse = (path) => {
|
|
|
156
164
|
}
|
|
157
165
|
return toConcatenatedNode(segments);
|
|
158
166
|
};
|
|
159
|
-
const optionallyQuotedSegment = () => {
|
|
167
|
+
const optionallyQuotedSegment = (allowBoolValue = false) => {
|
|
160
168
|
whitespace();
|
|
161
169
|
if (ch === SINGLE_QUOTE || ch === DOUBLE_QUOTE) {
|
|
162
170
|
const singleQuote = ch === SINGLE_QUOTE;
|
|
@@ -165,7 +173,7 @@ var parse = (path) => {
|
|
|
165
173
|
next(singleQuote ? SINGLE_QUOTE : DOUBLE_QUOTE);
|
|
166
174
|
return id;
|
|
167
175
|
}
|
|
168
|
-
return simpleSegment();
|
|
176
|
+
return simpleSegment(allowBoolValue);
|
|
169
177
|
};
|
|
170
178
|
const equals = () => {
|
|
171
179
|
if (ch !== EQUALS) {
|
|
@@ -185,7 +193,7 @@ var parse = (path) => {
|
|
|
185
193
|
whitespace();
|
|
186
194
|
if (equals()) {
|
|
187
195
|
whitespace();
|
|
188
|
-
const second = optionallyQuotedSegment();
|
|
196
|
+
const second = optionallyQuotedSegment(true);
|
|
189
197
|
value = toQuery(value, second);
|
|
190
198
|
whitespace();
|
|
191
199
|
}
|
|
@@ -387,7 +395,9 @@ function resolveBindingAST(bindingPathNode, options, hooks) {
|
|
|
387
395
|
appendPathSegments(getValueForNode(resolvedNode));
|
|
388
396
|
break;
|
|
389
397
|
case "Value":
|
|
390
|
-
appendPathSegments(
|
|
398
|
+
appendPathSegments(
|
|
399
|
+
typeof resolvedNode.value === "boolean" ? String(resolvedNode.value) : resolvedNode.value
|
|
400
|
+
);
|
|
391
401
|
break;
|
|
392
402
|
case "Query": {
|
|
393
403
|
const objToQuery = options.getValue(context.path) ?? [];
|
|
@@ -2689,24 +2699,7 @@ var EMPTY_NODE = {
|
|
|
2689
2699
|
var Parser = class {
|
|
2690
2700
|
constructor() {
|
|
2691
2701
|
this.hooks = {
|
|
2692
|
-
/**
|
|
2693
|
-
* A hook to interact with an object _before_ parsing it into an AST
|
|
2694
|
-
*
|
|
2695
|
-
* @param value - The object we're are about to parse
|
|
2696
|
-
* @returns - A new value to parse.
|
|
2697
|
-
* If undefined, the original value is used.
|
|
2698
|
-
* If null, we stop parsing this node.
|
|
2699
|
-
*/
|
|
2700
2702
|
onParseObject: new SyncWaterfallHook4(),
|
|
2701
|
-
/**
|
|
2702
|
-
* A callback to interact with an AST _after_ we parse it into the AST
|
|
2703
|
-
*
|
|
2704
|
-
* @param value - The object we parsed
|
|
2705
|
-
* @param node - The AST node we generated
|
|
2706
|
-
* @returns - A new AST node to use
|
|
2707
|
-
* If undefined, the original value is used.
|
|
2708
|
-
* If null, we ignore this node all together
|
|
2709
|
-
*/
|
|
2710
2703
|
onCreateASTNode: new SyncWaterfallHook4(),
|
|
2711
2704
|
parseNode: new SyncBailHook3()
|
|
2712
2705
|
};
|
|
@@ -2861,27 +2854,13 @@ var withContext = (model) => {
|
|
|
2861
2854
|
var Resolver = class {
|
|
2862
2855
|
constructor(root, options) {
|
|
2863
2856
|
this.hooks = {
|
|
2864
|
-
/** A hook to allow skipping of the resolution tree for a specific node */
|
|
2865
2857
|
skipResolve: new SyncWaterfallHook5(),
|
|
2866
|
-
/** An event emitted before calculating the next update */
|
|
2867
2858
|
beforeUpdate: new SyncHook3(),
|
|
2868
|
-
/** An event emitted after calculating the next update */
|
|
2869
2859
|
afterUpdate: new SyncHook3(),
|
|
2870
|
-
/** The options passed to a node to resolve it to an object */
|
|
2871
2860
|
resolveOptions: new SyncWaterfallHook5(),
|
|
2872
|
-
/** A hook to transform the AST node into a new AST node before resolving it */
|
|
2873
2861
|
beforeResolve: new SyncWaterfallHook5(),
|
|
2874
|
-
/**
|
|
2875
|
-
* A hook to transform an AST node into it's resolved value.
|
|
2876
|
-
* This runs _before_ any children are resolved
|
|
2877
|
-
*/
|
|
2878
2862
|
resolve: new SyncWaterfallHook5(),
|
|
2879
|
-
/**
|
|
2880
|
-
* A hook to transform the resolved value of an AST node.
|
|
2881
|
-
* This runs _after_ all children nodes are resolved
|
|
2882
|
-
*/
|
|
2883
2863
|
afterResolve: new SyncWaterfallHook5(),
|
|
2884
|
-
/** Called at the very end of a node's tree being updated */
|
|
2885
2864
|
afterNodeUpdate: new SyncHook3()
|
|
2886
2865
|
};
|
|
2887
2866
|
this.root = root;
|
|
@@ -2890,16 +2869,29 @@ var Resolver = class {
|
|
|
2890
2869
|
this.ASTMap = /* @__PURE__ */ new Map();
|
|
2891
2870
|
this.logger = options.logger;
|
|
2892
2871
|
this.idCache = /* @__PURE__ */ new Set();
|
|
2872
|
+
this.AsyncIdMap = /* @__PURE__ */ new Map();
|
|
2893
2873
|
}
|
|
2894
2874
|
getSourceNode(convertedAST) {
|
|
2895
2875
|
return this.ASTMap.get(convertedAST);
|
|
2896
2876
|
}
|
|
2897
|
-
update(changes) {
|
|
2877
|
+
update(changes, asyncChanges) {
|
|
2898
2878
|
this.hooks.beforeUpdate.call(changes);
|
|
2899
2879
|
const resolveCache = /* @__PURE__ */ new Map();
|
|
2900
2880
|
this.idCache.clear();
|
|
2901
2881
|
const prevASTMap = new Map(this.ASTMap);
|
|
2902
2882
|
this.ASTMap.clear();
|
|
2883
|
+
const prevAsyncIdMap = new Map(this.AsyncIdMap);
|
|
2884
|
+
const nextAsyncIdMap = /* @__PURE__ */ new Map();
|
|
2885
|
+
asyncChanges?.forEach((id) => {
|
|
2886
|
+
let current = prevAsyncIdMap.get(id);
|
|
2887
|
+
while (current && prevASTMap.has(current)) {
|
|
2888
|
+
const next = prevASTMap.get(current);
|
|
2889
|
+
if (next && this.resolveCache.has(next)) {
|
|
2890
|
+
this.resolveCache.delete(next);
|
|
2891
|
+
}
|
|
2892
|
+
current = current.parent;
|
|
2893
|
+
}
|
|
2894
|
+
});
|
|
2903
2895
|
const updated = this.computeTree(
|
|
2904
2896
|
this.root,
|
|
2905
2897
|
void 0,
|
|
@@ -2907,8 +2899,10 @@ var Resolver = class {
|
|
|
2907
2899
|
resolveCache,
|
|
2908
2900
|
toNodeResolveOptions(this.options),
|
|
2909
2901
|
void 0,
|
|
2910
|
-
prevASTMap
|
|
2902
|
+
prevASTMap,
|
|
2903
|
+
nextAsyncIdMap
|
|
2911
2904
|
);
|
|
2905
|
+
this.AsyncIdMap = nextAsyncIdMap;
|
|
2912
2906
|
this.resolveCache = resolveCache;
|
|
2913
2907
|
this.hooks.afterUpdate.call(updated.value);
|
|
2914
2908
|
return updated.value;
|
|
@@ -2953,7 +2947,7 @@ var Resolver = class {
|
|
|
2953
2947
|
});
|
|
2954
2948
|
return clonedNode;
|
|
2955
2949
|
}
|
|
2956
|
-
computeTree(node, rawParent, dataChanges, cacheUpdate, options, partiallyResolvedParent, prevASTMap) {
|
|
2950
|
+
computeTree(node, rawParent, dataChanges, cacheUpdate, options, partiallyResolvedParent, prevASTMap, nextAsyncIdMap) {
|
|
2957
2951
|
const dependencyModel = new DependencyModel(options.data.model);
|
|
2958
2952
|
dependencyModel.trackSubset("core");
|
|
2959
2953
|
const depModelWithParser = withContext(
|
|
@@ -2979,18 +2973,6 @@ var Resolver = class {
|
|
|
2979
2973
|
node,
|
|
2980
2974
|
resolveOptions
|
|
2981
2975
|
);
|
|
2982
|
-
const clonedNode = {
|
|
2983
|
-
...this.cloneNode(node),
|
|
2984
|
-
parent: partiallyResolvedParent
|
|
2985
|
-
};
|
|
2986
|
-
const resolvedAST = this.hooks.beforeResolve.call(
|
|
2987
|
-
clonedNode,
|
|
2988
|
-
resolveOptions
|
|
2989
|
-
) ?? {
|
|
2990
|
-
type: "empty" /* Empty */
|
|
2991
|
-
};
|
|
2992
|
-
const isNestedMultiNodeWithAsync = resolvedAST.type === "multi-node" /* MultiNode */ && partiallyResolvedParent?.parent?.parent?.type === "multi-node" /* MultiNode */ && partiallyResolvedParent.parent.type === "value" /* Value */ && resolvedAST.parent?.type === "asset" /* Asset */ && resolvedAST.parent.value.id.includes("async");
|
|
2993
|
-
const isNestedMultiNode = resolvedAST.type === "multi-node" /* MultiNode */ && partiallyResolvedParent?.parent?.type === "multi-node" /* MultiNode */ && partiallyResolvedParent.type === "value" /* Value */;
|
|
2994
2976
|
if (previousResult && shouldUseLastValue) {
|
|
2995
2977
|
const update2 = {
|
|
2996
2978
|
...previousResult,
|
|
@@ -3004,6 +2986,12 @@ var Resolver = class {
|
|
|
3004
2986
|
updated: false
|
|
3005
2987
|
};
|
|
3006
2988
|
cacheUpdate.set(AST, resolvedUpdate);
|
|
2989
|
+
if (resolvedUpdate.node.type === "async" /* Async */) {
|
|
2990
|
+
nextAsyncIdMap.set(resolvedUpdate.node.id, resolvedUpdate.node);
|
|
2991
|
+
}
|
|
2992
|
+
for (const key of resolvedUpdate.node.asyncNodesResolved ?? []) {
|
|
2993
|
+
nextAsyncIdMap.set(key, resolvedUpdate.node);
|
|
2994
|
+
}
|
|
3007
2995
|
const handleChildNode = (childNode) => {
|
|
3008
2996
|
const originalChildNode = prevASTMap.get(childNode) ?? childNode;
|
|
3009
2997
|
const previousChildResult = this.getPreviousResult(originalChildNode);
|
|
@@ -3028,10 +3016,22 @@ var Resolver = class {
|
|
|
3028
3016
|
repopulateASTMapFromCache(previousResult, node, rawParent);
|
|
3029
3017
|
return update2;
|
|
3030
3018
|
}
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3019
|
+
const clonedNode = {
|
|
3020
|
+
...this.cloneNode(node),
|
|
3021
|
+
parent: partiallyResolvedParent
|
|
3022
|
+
};
|
|
3023
|
+
const resolvedAST = this.hooks.beforeResolve.call(
|
|
3024
|
+
clonedNode,
|
|
3025
|
+
resolveOptions
|
|
3026
|
+
) ?? {
|
|
3027
|
+
type: "empty" /* Empty */
|
|
3028
|
+
};
|
|
3029
|
+
resolvedAST.parent = partiallyResolvedParent;
|
|
3030
|
+
if (resolvedAST.type === "async" /* Async */) {
|
|
3031
|
+
nextAsyncIdMap.set(resolvedAST.id, resolvedAST);
|
|
3032
|
+
}
|
|
3033
|
+
for (const id of resolvedAST.asyncNodesResolved ?? []) {
|
|
3034
|
+
nextAsyncIdMap.set(id, resolvedAST);
|
|
3035
3035
|
}
|
|
3036
3036
|
resolveOptions.node = resolvedAST;
|
|
3037
3037
|
this.ASTMap.set(resolvedAST, node);
|
|
@@ -3055,7 +3055,8 @@ var Resolver = class {
|
|
|
3055
3055
|
cacheUpdate,
|
|
3056
3056
|
resolveOptions,
|
|
3057
3057
|
resolvedAST,
|
|
3058
|
-
prevASTMap
|
|
3058
|
+
prevASTMap,
|
|
3059
|
+
nextAsyncIdMap
|
|
3059
3060
|
);
|
|
3060
3061
|
const {
|
|
3061
3062
|
dependencies: childTreeDeps,
|
|
@@ -3081,9 +3082,8 @@ var Resolver = class {
|
|
|
3081
3082
|
resolvedAST.children = newChildren;
|
|
3082
3083
|
} else if (resolvedAST.type === "multi-node" /* MultiNode */) {
|
|
3083
3084
|
const childValue = [];
|
|
3084
|
-
const rawParentToPassIn =
|
|
3085
|
-
|
|
3086
|
-
const newValues = resolvedAST.values.map((mValue) => {
|
|
3085
|
+
const rawParentToPassIn = node;
|
|
3086
|
+
resolvedAST.values = resolvedAST.values.map((mValue) => {
|
|
3087
3087
|
const mTree = this.computeTree(
|
|
3088
3088
|
mValue,
|
|
3089
3089
|
rawParentToPassIn,
|
|
@@ -3091,31 +3091,18 @@ var Resolver = class {
|
|
|
3091
3091
|
cacheUpdate,
|
|
3092
3092
|
resolveOptions,
|
|
3093
3093
|
resolvedAST,
|
|
3094
|
-
prevASTMap
|
|
3094
|
+
prevASTMap,
|
|
3095
|
+
nextAsyncIdMap
|
|
3095
3096
|
);
|
|
3096
3097
|
if (mTree.value !== void 0 && mTree.value !== null) {
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3098
|
+
mTree.dependencies.forEach(
|
|
3099
|
+
(bindingDep) => childDependencies.add(bindingDep)
|
|
3100
|
+
);
|
|
3101
|
+
updated = updated || mTree.updated;
|
|
3102
|
+
childValue.push(mTree.value);
|
|
3102
3103
|
}
|
|
3103
|
-
mTree.dependencies.forEach(
|
|
3104
|
-
(bindingDep) => childDependencies.add(bindingDep)
|
|
3105
|
-
);
|
|
3106
|
-
updated = updated || mTree.updated;
|
|
3107
3104
|
return mTree.node;
|
|
3108
3105
|
});
|
|
3109
|
-
if (hasAsync.length > 0) {
|
|
3110
|
-
const copy = newValues;
|
|
3111
|
-
hasAsync.forEach((index) => {
|
|
3112
|
-
if (copy[index])
|
|
3113
|
-
copy.splice(index, 1, ...unpackNode(copy[index]));
|
|
3114
|
-
});
|
|
3115
|
-
resolvedAST.values = copy;
|
|
3116
|
-
} else {
|
|
3117
|
-
resolvedAST.values = newValues;
|
|
3118
|
-
}
|
|
3119
3106
|
resolved = childValue;
|
|
3120
3107
|
}
|
|
3121
3108
|
childDependencies.forEach(
|
|
@@ -3138,37 +3125,11 @@ var Resolver = class {
|
|
|
3138
3125
|
...childDependencies
|
|
3139
3126
|
])
|
|
3140
3127
|
};
|
|
3141
|
-
this.hooks.afterNodeUpdate.call(
|
|
3142
|
-
node,
|
|
3143
|
-
isNestedMultiNode ? partiallyResolvedParent?.parent : rawParent,
|
|
3144
|
-
update
|
|
3145
|
-
);
|
|
3128
|
+
this.hooks.afterNodeUpdate.call(node, rawParent, update);
|
|
3146
3129
|
cacheUpdate.set(node, update);
|
|
3147
3130
|
return update;
|
|
3148
3131
|
}
|
|
3149
3132
|
};
|
|
3150
|
-
function unpackAndPush(item, initial) {
|
|
3151
|
-
if (item.asset.values && Array.isArray(item.asset.values)) {
|
|
3152
|
-
item.asset.values.forEach((i) => {
|
|
3153
|
-
unpackAndPush(i, initial);
|
|
3154
|
-
});
|
|
3155
|
-
} else {
|
|
3156
|
-
initial.push(item);
|
|
3157
|
-
}
|
|
3158
|
-
}
|
|
3159
|
-
function unpackNode(item) {
|
|
3160
|
-
const unpacked = [];
|
|
3161
|
-
if ("children" in item && item.children?.[0]?.value.type === "asset" /* Asset */ && (item.children?.[0]?.value).children) {
|
|
3162
|
-
if ((item.children?.[0]?.value).children?.[0]?.value.type === "multi-node" /* MultiNode */) {
|
|
3163
|
-
((item.children?.[0]?.value).children?.[0]?.value).values.forEach((value) => {
|
|
3164
|
-
unpacked.push(value);
|
|
3165
|
-
});
|
|
3166
|
-
}
|
|
3167
|
-
} else {
|
|
3168
|
-
unpacked.push(item);
|
|
3169
|
-
}
|
|
3170
|
-
return unpacked;
|
|
3171
|
-
}
|
|
3172
3133
|
|
|
3173
3134
|
// ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/core/player/src/view/view.ts
|
|
3174
3135
|
var CrossfieldProvider = class {
|
|
@@ -3222,8 +3183,8 @@ var ViewInstance = class {
|
|
|
3222
3183
|
this.initialView = initialView;
|
|
3223
3184
|
this.resolverOptions = resolverOptions;
|
|
3224
3185
|
}
|
|
3225
|
-
updateAsync() {
|
|
3226
|
-
const update = this.resolver?.update();
|
|
3186
|
+
updateAsync(asyncNode) {
|
|
3187
|
+
const update = this.resolver?.update(/* @__PURE__ */ new Set(), /* @__PURE__ */ new Set([asyncNode]));
|
|
3227
3188
|
this.lastUpdate = update;
|
|
3228
3189
|
this.hooks.onUpdate.call(update);
|
|
3229
3190
|
}
|
|
@@ -3317,11 +3278,12 @@ var Builder = class _Builder {
|
|
|
3317
3278
|
*
|
|
3318
3279
|
* @param id - the id of async node. It should be identical for each async node
|
|
3319
3280
|
*/
|
|
3320
|
-
static asyncNode(id, flatten2 = true) {
|
|
3281
|
+
static asyncNode(id, flatten2 = true, onValueReceived) {
|
|
3321
3282
|
return {
|
|
3322
3283
|
id,
|
|
3323
3284
|
type: "async" /* Async */,
|
|
3324
3285
|
flatten: flatten2,
|
|
3286
|
+
onValueReceived,
|
|
3325
3287
|
value: {
|
|
3326
3288
|
type: "value" /* Value */,
|
|
3327
3289
|
value: {
|
|
@@ -3789,7 +3751,7 @@ var MultiNodePlugin = class {
|
|
|
3789
3751
|
parser.hooks.parseNode.tap(
|
|
3790
3752
|
"multi-node",
|
|
3791
3753
|
(obj, nodeType, options, childOptions) => {
|
|
3792
|
-
if (childOptions
|
|
3754
|
+
if ((childOptions === void 0 || !hasTemplateKey(childOptions.key)) && Array.isArray(obj)) {
|
|
3793
3755
|
const values = obj.map(
|
|
3794
3756
|
(childVal) => parser.parseObject(childVal, "value" /* Value */, options)
|
|
3795
3757
|
).filter((child) => !!child);
|
|
@@ -3799,10 +3761,7 @@ var MultiNodePlugin = class {
|
|
|
3799
3761
|
const multiNode = parser.createASTNode(
|
|
3800
3762
|
{
|
|
3801
3763
|
type: "multi-node" /* MultiNode */,
|
|
3802
|
-
override: !hasTemplateValues(
|
|
3803
|
-
childOptions.parentObj,
|
|
3804
|
-
childOptions.key
|
|
3805
|
-
),
|
|
3764
|
+
override: childOptions !== void 0 && !hasTemplateValues(childOptions.parentObj, childOptions.key),
|
|
3806
3765
|
values
|
|
3807
3766
|
},
|
|
3808
3767
|
obj
|
|
@@ -3815,7 +3774,7 @@ var MultiNodePlugin = class {
|
|
|
3815
3774
|
v.parent = multiNode;
|
|
3816
3775
|
});
|
|
3817
3776
|
}
|
|
3818
|
-
return [
|
|
3777
|
+
return childOptions === void 0 ? multiNode : [
|
|
3819
3778
|
{
|
|
3820
3779
|
path: [...childOptions.path, childOptions.key],
|
|
3821
3780
|
value: multiNode
|