@player-ui/player 0.3.1-next.1 → 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.
Files changed (47) hide show
  1. package/dist/index.cjs.js +899 -336
  2. package/dist/index.d.ts +275 -93
  3. package/dist/index.esm.js +890 -334
  4. package/dist/player.dev.js +11429 -0
  5. package/dist/player.prod.js +2 -0
  6. package/package.json +16 -5
  7. package/src/binding/binding.ts +8 -0
  8. package/src/binding/index.ts +14 -4
  9. package/src/binding/resolver.ts +2 -4
  10. package/src/binding-grammar/custom/index.ts +17 -9
  11. package/src/controllers/constants/index.ts +9 -5
  12. package/src/controllers/{data.ts → data/controller.ts} +62 -61
  13. package/src/controllers/data/index.ts +1 -0
  14. package/src/controllers/data/utils.ts +42 -0
  15. package/src/controllers/flow/controller.ts +16 -12
  16. package/src/controllers/flow/flow.ts +6 -1
  17. package/src/controllers/index.ts +1 -1
  18. package/src/controllers/validation/binding-tracker.ts +42 -19
  19. package/src/controllers/validation/controller.ts +375 -148
  20. package/src/controllers/view/asset-transform.ts +4 -1
  21. package/src/controllers/view/controller.ts +20 -3
  22. package/src/data/dependency-tracker.ts +14 -0
  23. package/src/data/local-model.ts +25 -1
  24. package/src/data/model.ts +60 -8
  25. package/src/data/noop-model.ts +2 -0
  26. package/src/expressions/evaluator-functions.ts +24 -2
  27. package/src/expressions/evaluator.ts +38 -34
  28. package/src/expressions/index.ts +1 -0
  29. package/src/expressions/parser.ts +116 -44
  30. package/src/expressions/types.ts +50 -17
  31. package/src/expressions/utils.ts +143 -1
  32. package/src/player.ts +60 -46
  33. package/src/plugins/default-exp-plugin.ts +57 -0
  34. package/src/plugins/flow-exp-plugin.ts +2 -2
  35. package/src/schema/schema.ts +28 -9
  36. package/src/string-resolver/index.ts +26 -9
  37. package/src/types.ts +6 -3
  38. package/src/validator/binding-map-splice.ts +59 -0
  39. package/src/validator/index.ts +1 -0
  40. package/src/validator/types.ts +11 -3
  41. package/src/validator/validation-middleware.ts +58 -6
  42. package/src/view/parser/index.ts +51 -3
  43. package/src/view/plugins/applicability.ts +1 -1
  44. package/src/view/plugins/string-resolver.ts +35 -9
  45. package/src/view/plugins/template-plugin.ts +1 -6
  46. package/src/view/resolver/index.ts +119 -54
  47. 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
- resolvedAST: Node.Node,
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
- const { node: childResolvedAST } =
264
- this.getPreviousResult(childAST) || {};
265
- if (!childResolvedAST) return;
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
- const resolvedAST = previousResult.node;
283
- repopulateASTMapFromCache(resolvedAST, node);
324
+ // Point the root of the cached node to the new resolved node.
325
+ previousResult.node.parent = parentNode;
284
326
 
285
- this.hooks.afterNodeUpdate.call(node, parent, update);
327
+ repopulateASTMapFromCache(previousResult, node, parent);
286
328
 
287
329
  return update;
288
330
  }
289
331
 
290
- const resolvedAST = this.hooks.beforeResolve.call(node, resolveOptions) ?? {
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?.forEach((child) => {
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
- let { updated: childUpdated, value: childValue } = computedChildTree;
323
- const { node: childNode, dependencies: childTreeDeps } =
324
- computedChildTree;
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(): Map<BindingInstance, ValidationResponse> | undefined;
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
- binding: BindingLike
34
- ): ValidationResponse | undefined;
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: boolean;
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(type: ValidationTypes.DisplayTarget): Array<ValidationResponse>;
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 {