@player-ui/player 0.3.1-next.0 → 0.3.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/dist/index.cjs.js +899 -336
- package/dist/index.d.ts +275 -93
- package/dist/index.esm.js +890 -334
- package/dist/player.dev.js +11429 -0
- package/dist/player.prod.js +2 -0
- package/package.json +16 -5
- package/src/binding/binding.ts +8 -0
- package/src/binding/index.ts +14 -4
- package/src/binding/resolver.ts +2 -4
- package/src/binding-grammar/custom/index.ts +17 -9
- package/src/controllers/constants/index.ts +9 -5
- package/src/controllers/{data.ts → data/controller.ts} +62 -61
- package/src/controllers/data/index.ts +1 -0
- package/src/controllers/data/utils.ts +42 -0
- package/src/controllers/flow/controller.ts +16 -12
- package/src/controllers/flow/flow.ts +6 -1
- package/src/controllers/index.ts +1 -1
- package/src/controllers/validation/binding-tracker.ts +42 -19
- package/src/controllers/validation/controller.ts +375 -148
- package/src/controllers/view/asset-transform.ts +4 -1
- package/src/controllers/view/controller.ts +20 -3
- package/src/data/dependency-tracker.ts +14 -0
- package/src/data/local-model.ts +25 -1
- package/src/data/model.ts +60 -8
- package/src/data/noop-model.ts +2 -0
- package/src/expressions/evaluator-functions.ts +24 -2
- package/src/expressions/evaluator.ts +38 -34
- package/src/expressions/index.ts +1 -0
- package/src/expressions/parser.ts +116 -44
- package/src/expressions/types.ts +50 -17
- package/src/expressions/utils.ts +143 -1
- package/src/player.ts +60 -46
- package/src/plugins/default-exp-plugin.ts +57 -0
- package/src/plugins/flow-exp-plugin.ts +2 -2
- package/src/schema/schema.ts +28 -9
- package/src/string-resolver/index.ts +26 -9
- package/src/types.ts +6 -3
- package/src/validator/binding-map-splice.ts +59 -0
- package/src/validator/index.ts +1 -0
- package/src/validator/types.ts +11 -3
- package/src/validator/validation-middleware.ts +58 -6
- package/src/view/parser/index.ts +51 -3
- package/src/view/plugins/applicability.ts +1 -1
- package/src/view/plugins/string-resolver.ts +35 -9
- package/src/view/plugins/template-plugin.ts +1 -6
- package/src/view/resolver/index.ts +119 -54
- package/src/view/resolver/types.ts +48 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SyncWaterfallHook, SyncHook } from 'tapable-ts';
|
|
2
|
-
import { setIn, addLast } from 'timm';
|
|
2
|
+
import { setIn, addLast, clone } from 'timm';
|
|
3
3
|
import dlv from 'dlv';
|
|
4
4
|
import { dequal } from 'dequal';
|
|
5
5
|
import type { BindingInstance, BindingLike } from '../../binding';
|
|
@@ -42,6 +42,12 @@ const withContext = (model: DataModelWithParser): DataModelWithParser => {
|
|
|
42
42
|
...options,
|
|
43
43
|
});
|
|
44
44
|
},
|
|
45
|
+
delete: (binding: BindingLike, options?: DataModelOptions): void => {
|
|
46
|
+
return model.delete(binding, {
|
|
47
|
+
context: { model },
|
|
48
|
+
...options,
|
|
49
|
+
});
|
|
50
|
+
},
|
|
45
51
|
};
|
|
46
52
|
};
|
|
47
53
|
|
|
@@ -141,6 +147,7 @@ export class Resolver {
|
|
|
141
147
|
this.hooks.beforeUpdate.call(changes);
|
|
142
148
|
const resolveCache = new Map<Node.Node, Resolve.ResolvedNode>();
|
|
143
149
|
this.idCache.clear();
|
|
150
|
+
const prevASTMap = new Map(this.ASTMap);
|
|
144
151
|
this.ASTMap.clear();
|
|
145
152
|
|
|
146
153
|
const updated = this.computeTree(
|
|
@@ -148,7 +155,9 @@ export class Resolver {
|
|
|
148
155
|
undefined,
|
|
149
156
|
changes,
|
|
150
157
|
resolveCache,
|
|
151
|
-
toNodeResolveOptions(this.options)
|
|
158
|
+
toNodeResolveOptions(this.options),
|
|
159
|
+
undefined,
|
|
160
|
+
prevASTMap
|
|
152
161
|
);
|
|
153
162
|
this.resolveCache = resolveCache;
|
|
154
163
|
this.hooks.afterUpdate.call(updated.value);
|
|
@@ -156,6 +165,10 @@ export class Resolver {
|
|
|
156
165
|
return updated.value;
|
|
157
166
|
}
|
|
158
167
|
|
|
168
|
+
public getResolveCache() {
|
|
169
|
+
return new Map(this.resolveCache);
|
|
170
|
+
}
|
|
171
|
+
|
|
159
172
|
private getNodeID(node?: Node.Node): string | undefined {
|
|
160
173
|
if (!node) {
|
|
161
174
|
return;
|
|
@@ -206,12 +219,29 @@ export class Resolver {
|
|
|
206
219
|
return this.resolveCache.get(node);
|
|
207
220
|
}
|
|
208
221
|
|
|
222
|
+
private cloneNode(node: any) {
|
|
223
|
+
const clonedNode = clone(node);
|
|
224
|
+
|
|
225
|
+
Object.keys(clonedNode).forEach((key) => {
|
|
226
|
+
if (key === 'parent') return;
|
|
227
|
+
|
|
228
|
+
const value = clonedNode[key];
|
|
229
|
+
if (typeof value === 'object' && value !== null) {
|
|
230
|
+
clonedNode[key] = Array.isArray(value) ? [...value] : { ...value };
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return clonedNode;
|
|
235
|
+
}
|
|
236
|
+
|
|
209
237
|
private computeTree(
|
|
210
238
|
node: Node.Node,
|
|
211
239
|
parent: Node.Node | undefined,
|
|
212
240
|
dataChanges: Set<BindingInstance> | undefined,
|
|
213
241
|
cacheUpdate: Map<Node.Node, Resolve.ResolvedNode>,
|
|
214
|
-
options: Resolve.NodeResolveOptions
|
|
242
|
+
options: Resolve.NodeResolveOptions,
|
|
243
|
+
parentNode: Node.Node | undefined,
|
|
244
|
+
prevASTMap: Map<Node.Node, Node.Node>
|
|
215
245
|
): NodeUpdate {
|
|
216
246
|
const dependencyModel = new DependencyModel(options.data.model);
|
|
217
247
|
|
|
@@ -254,43 +284,65 @@ export class Resolver {
|
|
|
254
284
|
|
|
255
285
|
/** Recursively repopulate the AST map given some AST Node and it's resolved AST representation */
|
|
256
286
|
const repopulateASTMapFromCache = (
|
|
257
|
-
|
|
258
|
-
AST: Node.Node
|
|
287
|
+
resolvedNode: Resolve.ResolvedNode,
|
|
288
|
+
AST: Node.Node,
|
|
289
|
+
ASTParent: Node.Node | undefined
|
|
259
290
|
) => {
|
|
291
|
+
const { node: resolvedAST } = resolvedNode;
|
|
260
292
|
this.ASTMap.set(resolvedAST, AST);
|
|
293
|
+
const resolvedUpdate = {
|
|
294
|
+
...resolvedNode,
|
|
295
|
+
updated: false,
|
|
296
|
+
};
|
|
297
|
+
cacheUpdate.set(AST, resolvedUpdate);
|
|
298
|
+
|
|
299
|
+
/** Helper function for recursing over child node */
|
|
300
|
+
const handleChildNode = (childNode: Node.Node) => {
|
|
301
|
+
// In order to get the correct results, we need to use the node references from the last update.
|
|
302
|
+
const originalChildNode = prevASTMap.get(childNode) ?? childNode;
|
|
303
|
+
const previousChildResult = this.getPreviousResult(originalChildNode);
|
|
304
|
+
if (!previousChildResult) return;
|
|
305
|
+
|
|
306
|
+
repopulateASTMapFromCache(
|
|
307
|
+
previousChildResult,
|
|
308
|
+
originalChildNode,
|
|
309
|
+
AST
|
|
310
|
+
);
|
|
311
|
+
};
|
|
312
|
+
|
|
261
313
|
if ('children' in resolvedAST) {
|
|
262
|
-
resolvedAST.children?.forEach(({ value: childAST }) =>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
repopulateASTMapFromCache(childResolvedAST, childAST);
|
|
268
|
-
|
|
269
|
-
if (childResolvedAST.type === NodeType.MultiNode) {
|
|
270
|
-
childResolvedAST.values.forEach((mChildAST) => {
|
|
271
|
-
const { node: mChildResolvedAST } =
|
|
272
|
-
this.getPreviousResult(mChildAST) || {};
|
|
273
|
-
if (!mChildResolvedAST) return;
|
|
274
|
-
|
|
275
|
-
repopulateASTMapFromCache(mChildResolvedAST, mChildAST);
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
});
|
|
314
|
+
resolvedAST.children?.forEach(({ value: childAST }) =>
|
|
315
|
+
handleChildNode(childAST)
|
|
316
|
+
);
|
|
317
|
+
} else if (resolvedAST.type === NodeType.MultiNode) {
|
|
318
|
+
resolvedAST.values.forEach(handleChildNode);
|
|
279
319
|
}
|
|
320
|
+
|
|
321
|
+
this.hooks.afterNodeUpdate.call(AST, ASTParent, resolvedUpdate);
|
|
280
322
|
};
|
|
281
323
|
|
|
282
|
-
|
|
283
|
-
|
|
324
|
+
// Point the root of the cached node to the new resolved node.
|
|
325
|
+
previousResult.node.parent = parentNode;
|
|
284
326
|
|
|
285
|
-
|
|
327
|
+
repopulateASTMapFromCache(previousResult, node, parent);
|
|
286
328
|
|
|
287
329
|
return update;
|
|
288
330
|
}
|
|
289
331
|
|
|
290
|
-
|
|
332
|
+
// Shallow clone the node so that changes to it during the resolve steps don't impact the original.
|
|
333
|
+
// We are trusting that this becomes a deep clone once the whole node tree has been traversed.
|
|
334
|
+
const clonedNode = {
|
|
335
|
+
...this.cloneNode(node),
|
|
336
|
+
parent: parentNode,
|
|
337
|
+
};
|
|
338
|
+
const resolvedAST = this.hooks.beforeResolve.call(
|
|
339
|
+
clonedNode,
|
|
340
|
+
resolveOptions
|
|
341
|
+
) ?? {
|
|
291
342
|
type: NodeType.Empty,
|
|
292
343
|
};
|
|
293
344
|
|
|
345
|
+
resolvedAST.parent = parentNode;
|
|
294
346
|
resolveOptions.node = resolvedAST;
|
|
295
347
|
|
|
296
348
|
this.ASTMap.set(resolvedAST, node);
|
|
@@ -311,43 +363,25 @@ export class Resolver {
|
|
|
311
363
|
dependencyModel.trackSubset('children');
|
|
312
364
|
|
|
313
365
|
if ('children' in resolvedAST) {
|
|
314
|
-
resolvedAST.children?.
|
|
366
|
+
const newChildren = resolvedAST.children?.map((child) => {
|
|
315
367
|
const computedChildTree = this.computeTree(
|
|
316
368
|
child.value,
|
|
317
369
|
node,
|
|
318
370
|
dataChanges,
|
|
319
371
|
cacheUpdate,
|
|
320
|
-
resolveOptions
|
|
372
|
+
resolveOptions,
|
|
373
|
+
resolvedAST,
|
|
374
|
+
prevASTMap
|
|
321
375
|
);
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
376
|
+
const {
|
|
377
|
+
dependencies: childTreeDeps,
|
|
378
|
+
node: childNode,
|
|
379
|
+
updated: childUpdated,
|
|
380
|
+
value: childValue,
|
|
381
|
+
} = computedChildTree;
|
|
325
382
|
|
|
326
383
|
childTreeDeps.forEach((binding) => childDependencies.add(binding));
|
|
327
384
|
|
|
328
|
-
if (childNode.type === NodeType.MultiNode) {
|
|
329
|
-
childValue = [];
|
|
330
|
-
childNode.values.forEach((mValue) => {
|
|
331
|
-
const mTree = this.computeTree(
|
|
332
|
-
mValue,
|
|
333
|
-
node,
|
|
334
|
-
dataChanges,
|
|
335
|
-
cacheUpdate,
|
|
336
|
-
resolveOptions
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
if (mTree.value !== undefined && mTree.value !== null) {
|
|
340
|
-
childValue.push(mTree.value);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
mTree.dependencies.forEach((bindingDep) =>
|
|
344
|
-
childDependencies.add(bindingDep)
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
childUpdated = childUpdated || mTree.updated;
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
385
|
if (childValue) {
|
|
352
386
|
if (childNode.type === NodeType.MultiNode && !childNode.override) {
|
|
353
387
|
const arr = addLast(
|
|
@@ -361,7 +395,38 @@ export class Resolver {
|
|
|
361
395
|
}
|
|
362
396
|
|
|
363
397
|
updated = updated || childUpdated;
|
|
398
|
+
|
|
399
|
+
return { ...child, value: childNode };
|
|
364
400
|
});
|
|
401
|
+
|
|
402
|
+
resolvedAST.children = newChildren;
|
|
403
|
+
} else if (resolvedAST.type === NodeType.MultiNode) {
|
|
404
|
+
const childValue: any = [];
|
|
405
|
+
const newValues = resolvedAST.values.map((mValue) => {
|
|
406
|
+
const mTree = this.computeTree(
|
|
407
|
+
mValue,
|
|
408
|
+
node,
|
|
409
|
+
dataChanges,
|
|
410
|
+
cacheUpdate,
|
|
411
|
+
resolveOptions,
|
|
412
|
+
resolvedAST,
|
|
413
|
+
prevASTMap
|
|
414
|
+
);
|
|
415
|
+
if (mTree.value !== undefined && mTree.value !== null) {
|
|
416
|
+
childValue.push(mTree.value);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
mTree.dependencies.forEach((bindingDep) =>
|
|
420
|
+
childDependencies.add(bindingDep)
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
updated = updated || mTree.updated;
|
|
424
|
+
|
|
425
|
+
return mTree.node;
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
resolvedAST.values = newValues;
|
|
429
|
+
resolved = childValue;
|
|
365
430
|
}
|
|
366
431
|
|
|
367
432
|
childDependencies.forEach((bindingDep) =>
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
DataModelImpl,
|
|
14
14
|
DataModelOptions,
|
|
15
15
|
} from '../../data';
|
|
16
|
+
import type { ConstantsProvider } from '../../controllers/constants';
|
|
16
17
|
import type { TransitionFunction } from '../../controllers';
|
|
17
18
|
import type { ExpressionEvaluator, ExpressionType } from '../../expressions';
|
|
18
19
|
import type { ValidationResponse } from '../../validator';
|
|
@@ -20,30 +21,64 @@ import type { Logger } from '../../logger';
|
|
|
20
21
|
import type { SchemaController } from '../../schema';
|
|
21
22
|
import type { Node } from '../parser';
|
|
22
23
|
|
|
24
|
+
export interface ValidationGetResolveOptions {
|
|
25
|
+
/**
|
|
26
|
+
* If we should ignore any non-blocking validations in the return
|
|
27
|
+
* @default true
|
|
28
|
+
*/
|
|
29
|
+
ignoreNonBlocking?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PlayerUtils {
|
|
33
|
+
findPlugin<Plugin = unknown>(symbol: symbol): Plugin | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
export declare namespace Resolve {
|
|
24
37
|
export interface Validation {
|
|
25
38
|
/** Fetch the data-type for the given binding */
|
|
26
39
|
type(binding: BindingLike): Schema.DataType | undefined;
|
|
27
40
|
|
|
28
41
|
/** Get all currently applicable validation errors */
|
|
29
|
-
getAll(
|
|
42
|
+
getAll(
|
|
43
|
+
options?: ValidationGetResolveOptions
|
|
44
|
+
): Map<BindingInstance, ValidationResponse> | undefined;
|
|
30
45
|
|
|
31
46
|
/** Internal Method to lookup if there is a validation for the given binding */
|
|
32
|
-
_getValidationForBinding(
|
|
33
|
-
|
|
34
|
-
|
|
47
|
+
_getValidationForBinding(binding: BindingLike):
|
|
48
|
+
| {
|
|
49
|
+
/** Get the validation for the given binding */
|
|
50
|
+
get: (
|
|
51
|
+
options?: ValidationGetResolveOptions
|
|
52
|
+
) => ValidationResponse | undefined;
|
|
53
|
+
|
|
54
|
+
/** Get all validations for the given binding */
|
|
55
|
+
getAll: (
|
|
56
|
+
options?: ValidationGetResolveOptions
|
|
57
|
+
) => Array<ValidationResponse>;
|
|
58
|
+
}
|
|
59
|
+
| undefined;
|
|
35
60
|
|
|
36
61
|
/** Get field level error for the specific binding */
|
|
37
62
|
get(
|
|
38
63
|
binding: BindingLike,
|
|
39
64
|
options?: {
|
|
40
65
|
/** If this binding should also be tracked for validations */
|
|
41
|
-
track
|
|
42
|
-
}
|
|
66
|
+
track?: boolean;
|
|
67
|
+
} & ValidationGetResolveOptions
|
|
43
68
|
): ValidationResponse | undefined;
|
|
44
69
|
|
|
70
|
+
getValidationsForBinding(
|
|
71
|
+
binding: BindingLike,
|
|
72
|
+
options?: {
|
|
73
|
+
/** If this binding should also be tracked for validations */
|
|
74
|
+
track?: boolean;
|
|
75
|
+
} & ValidationGetResolveOptions
|
|
76
|
+
): Array<ValidationResponse>;
|
|
77
|
+
|
|
45
78
|
/** Get errors for all children regardless of section */
|
|
46
|
-
getChildren(
|
|
79
|
+
getChildren(
|
|
80
|
+
type?: ValidationTypes.DisplayTarget
|
|
81
|
+
): Array<ValidationResponse>;
|
|
47
82
|
|
|
48
83
|
/** Get errors for all children solely in this section */
|
|
49
84
|
getValidationsForSection(): Array<ValidationResponse>;
|
|
@@ -62,6 +97,9 @@ export declare namespace Resolve {
|
|
|
62
97
|
/** A logger to use */
|
|
63
98
|
logger?: Logger;
|
|
64
99
|
|
|
100
|
+
/** Utils for various useful operations */
|
|
101
|
+
utils?: PlayerUtils;
|
|
102
|
+
|
|
65
103
|
/** An optional set of validation features */
|
|
66
104
|
validation?: Validation;
|
|
67
105
|
|
|
@@ -73,6 +111,9 @@ export declare namespace Resolve {
|
|
|
73
111
|
|
|
74
112
|
/** The hub for data invariants and metaData associated with the data model */
|
|
75
113
|
schema: SchemaController;
|
|
114
|
+
|
|
115
|
+
/** The constants for messages */
|
|
116
|
+
constants?: ConstantsProvider;
|
|
76
117
|
}
|
|
77
118
|
|
|
78
119
|
export interface NodeDataOptions {
|