@player-ui/player 0.4.0-next.8 → 0.4.0-next.9

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 (38) hide show
  1. package/dist/index.cjs.js +622 -325
  2. package/dist/index.d.ts +188 -72
  3. package/dist/index.esm.js +619 -327
  4. package/dist/player.dev.js +623 -326
  5. package/dist/player.prod.js +1 -1
  6. package/package.json +11 -3
  7. package/src/binding/binding.ts +8 -0
  8. package/src/binding/index.ts +1 -1
  9. package/src/controllers/constants/index.ts +9 -5
  10. package/src/controllers/data.ts +49 -52
  11. package/src/controllers/flow/controller.ts +16 -12
  12. package/src/controllers/flow/flow.ts +6 -1
  13. package/src/controllers/validation/binding-tracker.ts +42 -19
  14. package/src/controllers/validation/controller.ts +265 -85
  15. package/src/controllers/view/asset-transform.ts +4 -1
  16. package/src/controllers/view/controller.ts +19 -2
  17. package/src/data/dependency-tracker.ts +14 -0
  18. package/src/data/local-model.ts +25 -1
  19. package/src/data/model.ts +55 -8
  20. package/src/data/noop-model.ts +2 -0
  21. package/src/expressions/evaluator-functions.ts +24 -2
  22. package/src/expressions/evaluator.ts +35 -31
  23. package/src/expressions/types.ts +17 -5
  24. package/src/expressions/utils.ts +19 -0
  25. package/src/player.ts +26 -29
  26. package/src/plugins/flow-exp-plugin.ts +2 -2
  27. package/src/string-resolver/index.ts +7 -2
  28. package/src/types.ts +1 -4
  29. package/src/validator/binding-map-splice.ts +59 -0
  30. package/src/validator/index.ts +1 -0
  31. package/src/validator/types.ts +11 -3
  32. package/src/validator/validation-middleware.ts +34 -3
  33. package/src/view/parser/index.ts +44 -2
  34. package/src/view/plugins/applicability.ts +1 -1
  35. package/src/view/plugins/string-resolver.ts +8 -4
  36. package/src/view/plugins/template-plugin.ts +1 -6
  37. package/src/view/resolver/index.ts +119 -54
  38. package/src/view/resolver/types.ts +48 -7
@@ -1,4 +1,4 @@
1
- import type { Validation } from '@player-ui/types';
1
+ import type { Schema, Validation } from '@player-ui/types';
2
2
 
3
3
  import type { BindingInstance, BindingFactory } from '../binding';
4
4
  import type { DataModelWithParser } from '../data';
@@ -40,12 +40,17 @@ type RequiredValidationKeys = 'severity' | 'trigger';
40
40
  export type ValidationObject = Validation.Reference &
41
41
  Required<Pick<Validation.Reference, RequiredValidationKeys>>;
42
42
 
43
+ export type ValidationObjectWithHandler = ValidationObject & {
44
+ /** A predefined handler for this validation object */
45
+ handler?: ValidatorFunction;
46
+ };
47
+
43
48
  export interface ValidationProvider {
44
49
  getValidationsForBinding?(
45
50
  binding: BindingInstance
46
- ): Array<ValidationObject> | undefined;
51
+ ): Array<ValidationObjectWithHandler> | undefined;
47
52
 
48
- getValidationsForView?(): Array<ValidationObject> | undefined;
53
+ getValidationsForView?(): Array<ValidationObjectWithHandler> | undefined;
49
54
  }
50
55
 
51
56
  export interface ValidatorContext {
@@ -66,6 +71,9 @@ export interface ValidatorContext {
66
71
 
67
72
  /** The constants for messages */
68
73
  constants: ConstantsProvider;
74
+
75
+ /** The type in the schema that triggered the validation if there is one */
76
+ schemaType: Schema.DataType | undefined;
69
77
  }
70
78
 
71
79
  export type ValidatorFunction<Options = unknown> = (
@@ -11,6 +11,7 @@ import { toModel } from '../data';
11
11
  import type { Logger } from '../logger';
12
12
 
13
13
  import type { ValidationResponse } from './types';
14
+ import { removeBindingAndChildrenFromMap } from './binding-map-splice';
14
15
 
15
16
  /**
16
17
  * A BindingInstance with an indicator of whether or not it's a strong binding
@@ -37,17 +38,21 @@ export class ValidationMiddleware implements DataModelMiddleware {
37
38
  public validator: MiddlewareChecker;
38
39
  public shadowModelPaths: Map<BindingInstance, any>;
39
40
  private logger?: Logger;
41
+ private shouldIncludeInvalid?: (options?: DataModelOptions) => boolean;
40
42
 
41
43
  constructor(
42
44
  validator: MiddlewareChecker,
43
45
  options?: {
44
46
  /** A logger instance */
45
47
  logger?: Logger;
48
+ /** Optional function to include data staged in shadowModel */
49
+ shouldIncludeInvalid?: (options?: DataModelOptions) => boolean;
46
50
  }
47
51
  ) {
48
52
  this.validator = validator;
49
53
  this.shadowModelPaths = new Map();
50
54
  this.logger = options?.logger;
55
+ this.shouldIncludeInvalid = options?.shouldIncludeInvalid;
51
56
  }
52
57
 
53
58
  public set(
@@ -58,8 +63,11 @@ export class ValidationMiddleware implements DataModelMiddleware {
58
63
  const asModel = toModel(this, { ...options, includeInvalid: true }, next);
59
64
  const nextTransaction: BatchSetTransaction = [];
60
65
 
66
+ const includedBindings = new Set<BindingInstance>();
67
+
61
68
  transaction.forEach(([binding, value]) => {
62
69
  this.shadowModelPaths.set(binding, value);
70
+ includedBindings.add(binding);
63
71
  });
64
72
 
65
73
  const invalidBindings: Array<BindingInstance> = [];
@@ -79,7 +87,8 @@ export class ValidationMiddleware implements DataModelMiddleware {
79
87
  nextTransaction.push([validation.binding, value]);
80
88
  }
81
89
  });
82
- } else {
90
+ } else if (includedBindings.has(binding)) {
91
+ invalidBindings.push(binding);
83
92
  this.logger?.debug(
84
93
  `Invalid value for path: ${binding.asString()} - ${
85
94
  validations.severity
@@ -88,6 +97,8 @@ export class ValidationMiddleware implements DataModelMiddleware {
88
97
  }
89
98
  });
90
99
 
100
+ let validResults: Updates = [];
101
+
91
102
  if (next && nextTransaction.length > 0) {
92
103
  // defer clearing the shadow model to prevent validations that are run twice due to weak binding refs still needing the data
93
104
  nextTransaction.forEach(([binding]) =>
@@ -97,9 +108,11 @@ export class ValidationMiddleware implements DataModelMiddleware {
97
108
  if (invalidBindings.length === 0) {
98
109
  return result;
99
110
  }
111
+
112
+ validResults = result;
100
113
  }
101
114
 
102
- return invalidBindings.map((binding) => {
115
+ const invalidResults = invalidBindings.map((binding) => {
103
116
  return {
104
117
  binding,
105
118
  oldValue: asModel.get(binding),
@@ -107,6 +120,8 @@ export class ValidationMiddleware implements DataModelMiddleware {
107
120
  force: true,
108
121
  };
109
122
  });
123
+
124
+ return [...validResults, ...invalidResults];
110
125
  }
111
126
 
112
127
  public get(
@@ -116,7 +131,10 @@ export class ValidationMiddleware implements DataModelMiddleware {
116
131
  ) {
117
132
  let val = next?.get(binding, options);
118
133
 
119
- if (options?.includeInvalid === true) {
134
+ if (
135
+ this.shouldIncludeInvalid?.(options) ??
136
+ options?.includeInvalid === true
137
+ ) {
120
138
  this.shadowModelPaths.forEach((shadowValue, shadowBinding) => {
121
139
  if (shadowBinding === binding) {
122
140
  val = shadowValue;
@@ -132,4 +150,17 @@ export class ValidationMiddleware implements DataModelMiddleware {
132
150
 
133
151
  return val;
134
152
  }
153
+
154
+ public delete(
155
+ binding: BindingInstance,
156
+ options?: DataModelOptions,
157
+ next?: DataModelImpl
158
+ ) {
159
+ this.shadowModelPaths = removeBindingAndChildrenFromMap(
160
+ this.shadowModelPaths,
161
+ binding
162
+ );
163
+
164
+ return next?.delete(binding, options);
165
+ }
135
166
  }
@@ -83,6 +83,21 @@ export class Parser {
83
83
  return tapped;
84
84
  }
85
85
 
86
+ /**
87
+ * Checks if there are templated values in the object
88
+ *
89
+ * @param obj - The Parsed Object to check to see if we have a template array type for
90
+ * @param localKey - The key being checked
91
+ */
92
+ private hasTemplateValues(obj: any, localKey: string) {
93
+ return (
94
+ Object.hasOwnProperty.call(obj, 'template') &&
95
+ Array.isArray(obj?.template) &&
96
+ obj.template.length &&
97
+ obj.template.find((tmpl: any) => tmpl.output === localKey)
98
+ );
99
+ }
100
+
86
101
  public parseObject(
87
102
  obj: object,
88
103
  type: Node.ChildrenTypes = NodeType.Value,
@@ -172,6 +187,13 @@ export class Parser {
172
187
  template
173
188
  );
174
189
 
190
+ if (templateAST?.type === NodeType.MultiNode) {
191
+ templateAST.values.forEach((v) => {
192
+ // eslint-disable-next-line no-param-reassign
193
+ v.parent = templateAST;
194
+ });
195
+ }
196
+
175
197
  if (templateAST) {
176
198
  return {
177
199
  path: [...path, template.output],
@@ -199,6 +221,26 @@ export class Parser {
199
221
  NodeType.Switch
200
222
  );
201
223
 
224
+ if (
225
+ localSwitch &&
226
+ localSwitch.type === NodeType.Value &&
227
+ localSwitch.children?.length === 1 &&
228
+ localSwitch.value === undefined
229
+ ) {
230
+ const firstChild = localSwitch.children[0];
231
+
232
+ return {
233
+ ...rest,
234
+ children: [
235
+ ...children,
236
+ {
237
+ path: [...path, localKey, ...firstChild.path],
238
+ value: firstChild.value,
239
+ },
240
+ ],
241
+ };
242
+ }
243
+
202
244
  if (localSwitch) {
203
245
  return {
204
246
  ...rest,
@@ -222,7 +264,7 @@ export class Parser {
222
264
  const multiNode = this.hooks.onCreateASTNode.call(
223
265
  {
224
266
  type: NodeType.MultiNode,
225
- override: true,
267
+ override: !this.hasTemplateValues(localObj, localKey),
226
268
  values: childValues,
227
269
  },
228
270
  localValue
@@ -255,7 +297,7 @@ export class Parser {
255
297
  if (determineNodeType === NodeType.Applicability) {
256
298
  const parsedNode = this.hooks.parseNode.call(
257
299
  localValue,
258
- type,
300
+ NodeType.Value,
259
301
  options,
260
302
  determineNodeType
261
303
  );
@@ -16,7 +16,7 @@ export default class ApplicabilityPlugin implements ViewPlugin {
16
16
  if (node?.type === NodeType.Applicability) {
17
17
  const isApplicable = options.evaluate(node.expression);
18
18
 
19
- if (!isApplicable) {
19
+ if (isApplicable === false) {
20
20
  return null;
21
21
  }
22
22
 
@@ -87,15 +87,19 @@ export function resolveAllRefs(
87
87
  }
88
88
 
89
89
  /** Traverse up the node tree finding the first available 'path' */
90
- const findBasePath = (node: Node.Node): Node.PathSegment[] => {
90
+ const findBasePath = (
91
+ node: Node.Node,
92
+ resolver: Resolver
93
+ ): Node.PathSegment[] => {
91
94
  const parentNode = node.parent;
92
95
  if (!parentNode) {
93
96
  return [];
94
97
  }
95
98
 
96
99
  if ('children' in parentNode) {
100
+ const original = resolver.getSourceNode(node);
97
101
  return (
98
- parentNode.children?.find((child) => child.value === node)?.path ?? []
102
+ parentNode.children?.find((child) => child.value === original)?.path ?? []
99
103
  );
100
104
  }
101
105
 
@@ -103,7 +107,7 @@ const findBasePath = (node: Node.Node): Node.PathSegment[] => {
103
107
  return [];
104
108
  }
105
109
 
106
- return findBasePath(parentNode);
110
+ return findBasePath(parentNode, resolver);
107
111
  };
108
112
 
109
113
  /** A plugin that resolves all string references for each node */
@@ -148,7 +152,7 @@ export default class StringResolverPlugin implements ViewPlugin {
148
152
  propsToSkip = new Set(['exp']);
149
153
  }
150
154
 
151
- const nodePath = findBasePath(node);
155
+ const nodePath = findBasePath(node, resolver);
152
156
 
153
157
  /** If the path includes something that is supposed to be skipped, this node should be skipped too. */
154
158
  if (
@@ -95,16 +95,11 @@ export default class TemplatePlugin implements ViewPlugin {
95
95
  });
96
96
 
97
97
  const result: Node.MultiNode = {
98
- parent: node.parent,
99
98
  type: NodeType.MultiNode,
99
+ override: false,
100
100
  values,
101
101
  };
102
102
 
103
- result.values.forEach((innerNode) => {
104
- // eslint-disable-next-line no-param-reassign
105
- innerNode.parent = result;
106
- });
107
-
108
103
  return result;
109
104
  }
110
105
 
@@ -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 {