@player-ui/player 0.8.0--canary.307.9621 → 0.8.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.
Files changed (214) hide show
  1. package/dist/Player.native.js +11630 -0
  2. package/dist/Player.native.js.map +1 -0
  3. package/dist/cjs/index.cjs +5626 -0
  4. package/dist/cjs/index.cjs.map +1 -0
  5. package/dist/{index.esm.js → index.legacy-esm.js} +2044 -1667
  6. package/dist/{index.cjs.js → index.mjs} +2052 -1761
  7. package/dist/index.mjs.map +1 -0
  8. package/package.json +29 -63
  9. package/src/__tests__/data.test.ts +498 -0
  10. package/src/__tests__/flow.test.ts +312 -0
  11. package/src/__tests__/helpers/action-exp.plugin.ts +22 -0
  12. package/src/__tests__/helpers/actions.flow.ts +67 -0
  13. package/src/__tests__/helpers/binding.plugin.ts +125 -0
  14. package/src/__tests__/helpers/expression.plugin.ts +88 -0
  15. package/src/__tests__/helpers/transform-plugin.ts +19 -0
  16. package/src/__tests__/helpers/validation.flow.ts +56 -0
  17. package/src/__tests__/player.test.ts +597 -0
  18. package/src/__tests__/string-resolver.test.ts +186 -0
  19. package/src/__tests__/validation.test.ts +3555 -0
  20. package/src/__tests__/view.test.ts +715 -0
  21. package/src/binding/__tests__/binding.test.ts +113 -0
  22. package/src/binding/__tests__/index.test.ts +208 -0
  23. package/src/binding/__tests__/resolver.test.ts +83 -0
  24. package/src/binding/binding.ts +6 -6
  25. package/src/binding/index.ts +34 -34
  26. package/src/binding/resolver.ts +19 -19
  27. package/src/binding/utils.ts +7 -7
  28. package/src/binding-grammar/__tests__/parser.test.ts +64 -0
  29. package/src/binding-grammar/__tests__/test-utils/ast-cases.ts +198 -0
  30. package/src/binding-grammar/__tests__/test-utils/perf-test.ts +66 -0
  31. package/src/binding-grammar/ast.ts +11 -11
  32. package/src/binding-grammar/custom/index.ts +19 -22
  33. package/src/binding-grammar/ebnf/index.ts +20 -21
  34. package/src/binding-grammar/ebnf/types.ts +13 -13
  35. package/src/binding-grammar/index.ts +4 -4
  36. package/src/binding-grammar/parsimmon/index.ts +14 -14
  37. package/src/controllers/constants/__tests__/index.test.ts +106 -0
  38. package/src/controllers/constants/index.ts +3 -3
  39. package/src/controllers/constants/utils.ts +4 -4
  40. package/src/controllers/data/controller.ts +22 -22
  41. package/src/controllers/data/index.ts +1 -1
  42. package/src/controllers/data/utils.ts +7 -7
  43. package/src/controllers/flow/__tests__/controller.test.ts +195 -0
  44. package/src/controllers/flow/__tests__/flow.test.ts +381 -0
  45. package/src/controllers/flow/controller.ts +13 -13
  46. package/src/controllers/flow/flow.ts +23 -23
  47. package/src/controllers/flow/index.ts +2 -2
  48. package/src/controllers/index.ts +5 -5
  49. package/src/controllers/validation/binding-tracker.ts +71 -59
  50. package/src/controllers/validation/controller.ts +104 -104
  51. package/src/controllers/validation/index.ts +2 -2
  52. package/src/controllers/view/asset-transform.ts +20 -20
  53. package/src/controllers/view/controller.ts +27 -27
  54. package/src/controllers/view/index.ts +4 -4
  55. package/src/controllers/view/store.ts +3 -3
  56. package/src/controllers/view/types.ts +7 -7
  57. package/src/data/__tests__/__snapshots__/dependency-tracker.test.ts.snap +64 -0
  58. package/src/data/__tests__/dependency-tracker.test.ts +146 -0
  59. package/src/data/__tests__/local-model.test.ts +46 -0
  60. package/src/data/__tests__/model.test.ts +78 -0
  61. package/src/data/dependency-tracker.ts +16 -16
  62. package/src/data/index.ts +4 -4
  63. package/src/data/local-model.ts +6 -6
  64. package/src/data/model.ts +17 -17
  65. package/src/data/noop-model.ts +1 -1
  66. package/src/expressions/__tests__/__snapshots__/parser.test.ts.snap +854 -0
  67. package/src/expressions/__tests__/evaluator-functions.test.ts +47 -0
  68. package/src/expressions/__tests__/evaluator.test.ts +410 -0
  69. package/src/expressions/__tests__/parser.test.ts +115 -0
  70. package/src/expressions/__tests__/utils.test.ts +44 -0
  71. package/src/expressions/evaluator-functions.ts +6 -6
  72. package/src/expressions/evaluator.ts +71 -67
  73. package/src/expressions/index.ts +4 -4
  74. package/src/expressions/parser.ts +102 -105
  75. package/src/expressions/types.ts +29 -21
  76. package/src/expressions/utils.ts +32 -21
  77. package/src/index.ts +13 -13
  78. package/src/logger/__tests__/consoleLogger.test.ts +46 -0
  79. package/src/logger/__tests__/noopLogger.test.ts +13 -0
  80. package/src/logger/__tests__/proxyLogger.test.ts +31 -0
  81. package/src/logger/__tests__/tapableLogger.test.ts +41 -0
  82. package/src/logger/consoleLogger.ts +9 -9
  83. package/src/logger/index.ts +5 -5
  84. package/src/logger/noopLogger.ts +1 -1
  85. package/src/logger/proxyLogger.ts +6 -6
  86. package/src/logger/tapableLogger.ts +7 -7
  87. package/src/logger/types.ts +2 -2
  88. package/src/player.ts +60 -58
  89. package/src/plugins/default-exp-plugin.ts +10 -10
  90. package/src/plugins/default-view-plugin.ts +29 -0
  91. package/src/plugins/flow-exp-plugin.ts +6 -6
  92. package/src/schema/__tests__/schema.test.ts +243 -0
  93. package/src/schema/index.ts +2 -2
  94. package/src/schema/schema.ts +24 -24
  95. package/src/schema/types.ts +4 -4
  96. package/src/string-resolver/__tests__/index.test.ts +361 -0
  97. package/src/string-resolver/index.ts +17 -17
  98. package/src/types.ts +17 -17
  99. package/src/utils/__tests__/replaceParams.test.ts +33 -0
  100. package/src/utils/index.ts +1 -1
  101. package/src/utils/replaceParams.ts +1 -1
  102. package/src/validator/__tests__/binding-map-splice.test.ts +53 -0
  103. package/src/validator/__tests__/validation-middleware.test.ts +127 -0
  104. package/src/validator/binding-map-splice.ts +5 -5
  105. package/src/validator/index.ts +4 -4
  106. package/src/validator/registry.ts +1 -1
  107. package/src/validator/types.ts +13 -13
  108. package/src/validator/validation-middleware.ts +15 -15
  109. package/src/view/__tests__/view.immutable.test.ts +269 -0
  110. package/src/view/__tests__/view.test.ts +959 -0
  111. package/src/view/builder/index.test.ts +69 -0
  112. package/src/view/builder/index.ts +3 -3
  113. package/src/view/index.ts +5 -5
  114. package/src/view/parser/__tests__/__snapshots__/parser.test.ts.snap +394 -0
  115. package/src/view/parser/__tests__/parser.test.ts +264 -0
  116. package/src/view/parser/index.ts +43 -33
  117. package/src/view/parser/types.ts +11 -11
  118. package/src/view/parser/utils.ts +5 -5
  119. package/src/view/plugins/__tests__/__snapshots__/template.test.ts.snap +278 -0
  120. package/src/view/plugins/__tests__/applicability.test.ts +265 -0
  121. package/src/view/plugins/__tests__/string.test.ts +122 -0
  122. package/src/view/plugins/__tests__/template.test.ts +724 -0
  123. package/src/view/plugins/applicability.ts +19 -19
  124. package/src/view/plugins/index.ts +4 -5
  125. package/src/view/plugins/options.ts +1 -1
  126. package/src/view/plugins/string-resolver.ts +22 -22
  127. package/src/view/plugins/switch.ts +22 -23
  128. package/src/view/plugins/template-plugin.ts +26 -27
  129. package/src/view/resolver/__tests__/dependencies.test.ts +321 -0
  130. package/src/view/resolver/__tests__/edgecases.test.ts +626 -0
  131. package/src/view/resolver/index.ts +42 -42
  132. package/src/view/resolver/types.ts +21 -20
  133. package/src/view/resolver/utils.ts +9 -9
  134. package/src/view/view.ts +32 -22
  135. package/types/binding/binding.d.ts +50 -0
  136. package/types/binding/index.d.ts +29 -0
  137. package/types/binding/resolver.d.ts +26 -0
  138. package/types/binding/utils.d.ts +12 -0
  139. package/types/binding-grammar/ast.d.ts +67 -0
  140. package/types/binding-grammar/custom/index.d.ts +4 -0
  141. package/types/binding-grammar/ebnf/index.d.ts +4 -0
  142. package/types/binding-grammar/ebnf/types.d.ts +75 -0
  143. package/types/binding-grammar/index.d.ts +5 -0
  144. package/types/binding-grammar/parsimmon/index.d.ts +4 -0
  145. package/types/controllers/constants/index.d.ts +45 -0
  146. package/types/controllers/constants/utils.d.ts +6 -0
  147. package/types/controllers/data/controller.d.ts +45 -0
  148. package/types/controllers/data/index.d.ts +2 -0
  149. package/types/controllers/data/utils.d.ts +14 -0
  150. package/types/controllers/flow/controller.d.ts +25 -0
  151. package/types/controllers/flow/flow.d.ts +50 -0
  152. package/types/controllers/flow/index.d.ts +3 -0
  153. package/types/controllers/index.d.ts +6 -0
  154. package/types/controllers/validation/binding-tracker.d.ts +32 -0
  155. package/types/controllers/validation/controller.d.ts +151 -0
  156. package/types/controllers/validation/index.d.ts +3 -0
  157. package/types/controllers/view/asset-transform.d.ts +19 -0
  158. package/types/controllers/view/controller.d.ts +37 -0
  159. package/types/controllers/view/index.d.ts +5 -0
  160. package/types/controllers/view/store.d.ts +20 -0
  161. package/types/controllers/view/types.d.ts +16 -0
  162. package/types/data/dependency-tracker.d.ts +49 -0
  163. package/types/data/index.d.ts +5 -0
  164. package/types/data/local-model.d.ts +16 -0
  165. package/types/data/model.d.ts +86 -0
  166. package/types/data/noop-model.d.ts +13 -0
  167. package/types/expressions/evaluator-functions.d.ts +15 -0
  168. package/types/expressions/evaluator.d.ts +52 -0
  169. package/types/expressions/index.d.ts +5 -0
  170. package/types/expressions/parser.d.ts +10 -0
  171. package/types/expressions/types.d.ts +144 -0
  172. package/types/expressions/utils.d.ts +12 -0
  173. package/types/index.d.ts +14 -0
  174. package/types/logger/consoleLogger.d.ts +17 -0
  175. package/types/logger/index.d.ts +6 -0
  176. package/types/logger/noopLogger.d.ts +10 -0
  177. package/types/logger/proxyLogger.d.ts +15 -0
  178. package/types/logger/tapableLogger.d.ts +23 -0
  179. package/types/logger/types.d.ts +6 -0
  180. package/types/player.d.ts +101 -0
  181. package/types/plugins/default-exp-plugin.d.ts +9 -0
  182. package/types/plugins/default-view-plugin.d.ts +9 -0
  183. package/types/plugins/flow-exp-plugin.d.ts +11 -0
  184. package/types/schema/index.d.ts +3 -0
  185. package/types/schema/schema.d.ts +36 -0
  186. package/types/schema/types.d.ts +38 -0
  187. package/types/string-resolver/index.d.ts +30 -0
  188. package/types/types.d.ts +73 -0
  189. package/types/utils/index.d.ts +2 -0
  190. package/types/utils/replaceParams.d.ts +9 -0
  191. package/types/validator/binding-map-splice.d.ts +10 -0
  192. package/types/validator/index.d.ts +5 -0
  193. package/types/validator/registry.d.ts +11 -0
  194. package/types/validator/types.d.ts +53 -0
  195. package/types/validator/validation-middleware.d.ts +36 -0
  196. package/types/view/builder/index.d.ts +35 -0
  197. package/types/view/index.d.ts +6 -0
  198. package/types/view/parser/index.d.ts +52 -0
  199. package/types/view/parser/types.d.ts +109 -0
  200. package/types/view/parser/utils.d.ts +6 -0
  201. package/types/view/plugins/applicability.d.ts +10 -0
  202. package/types/view/plugins/index.d.ts +5 -0
  203. package/types/view/plugins/options.d.ts +4 -0
  204. package/types/view/plugins/string-resolver.d.ts +13 -0
  205. package/types/view/plugins/switch.d.ts +14 -0
  206. package/types/view/plugins/template-plugin.d.ts +33 -0
  207. package/types/view/resolver/index.d.ts +73 -0
  208. package/types/view/resolver/types.d.ts +129 -0
  209. package/types/view/resolver/utils.d.ts +11 -0
  210. package/types/view/view.d.ts +37 -0
  211. package/dist/index.d.ts +0 -1814
  212. package/dist/player.dev.js +0 -11472
  213. package/dist/player.prod.js +0 -2
  214. package/src/view/plugins/plugin.ts +0 -21
@@ -1,16 +1,16 @@
1
- import { SyncWaterfallHook } from 'tapable-ts';
2
- import type { Schema as SchemaType, Formatting } from '@player-ui/types';
1
+ import { SyncWaterfallHook } from "tapable-ts";
2
+ import type { Schema as SchemaType, Formatting } from "@player-ui/types";
3
3
 
4
- import type { BindingInstance } from '../binding';
5
- import type { ValidationProvider, ValidationObject } from '../validator';
6
- import type { FormatDefinition, FormatOptions, FormatType } from './types';
4
+ import type { BindingInstance } from "../binding";
5
+ import type { ValidationProvider, ValidationObject } from "../validator";
6
+ import type { FormatDefinition, FormatOptions, FormatType } from "./types";
7
7
 
8
8
  /** A function that returns itself */
9
9
  const identify = (val: any) => val;
10
10
 
11
11
  /** Expand the authored schema into a set of paths -> DataTypes */
12
12
  export function parse(
13
- schema: SchemaType.Schema
13
+ schema: SchemaType.Schema,
14
14
  ): Map<string, SchemaType.DataTypes> {
15
15
  const expandedPaths = new Map<string, SchemaType.DataTypes>();
16
16
 
@@ -41,29 +41,29 @@ export function parse(
41
41
  Object.entries(node).forEach(([prop, type]) => {
42
42
  const nestedPath = [...path, prop];
43
43
 
44
- const nestedPathStr = nestedPath.join('.');
44
+ const nestedPathStr = nestedPath.join(".");
45
45
 
46
46
  if (expandedPaths.has(nestedPathStr)) {
47
47
  // We've gone in a loop. Panic
48
48
  throw new Error(
49
- "Path has already been processed. There's either a loop somewhere or a bug"
49
+ "Path has already been processed. There's either a loop somewhere or a bug",
50
50
  );
51
51
  }
52
52
 
53
53
  if (visited.has(type.type)) {
54
54
  throw new Error(
55
- `Path already contained type: ${type.type}. This likely indicates a loop in the schema`
55
+ `Path already contained type: ${type.type}. This likely indicates a loop in the schema`,
56
56
  );
57
57
  }
58
58
 
59
59
  expandedPaths.set(nestedPathStr, type);
60
60
 
61
61
  if (type.isArray) {
62
- nestedPath.push('[]');
62
+ nestedPath.push("[]");
63
63
  }
64
64
 
65
65
  if (type.isRecord) {
66
- nestedPath.push('{}');
66
+ nestedPath.push("{}");
67
67
  }
68
68
 
69
69
  if (type.type && schema[type.type]) {
@@ -117,7 +117,7 @@ export class SchemaController implements ValidationProvider {
117
117
  }
118
118
 
119
119
  getValidationsForBinding(
120
- binding: BindingInstance
120
+ binding: BindingInstance,
121
121
  ): Array<ValidationObject> | undefined {
122
122
  const typeDef = this.getApparentType(binding);
123
123
 
@@ -127,8 +127,8 @@ export class SchemaController implements ValidationProvider {
127
127
 
128
128
  // Set the defaults for schema-level validations
129
129
  return typeDef.validation.map((vRef) => ({
130
- severity: 'error',
131
- trigger: 'change',
130
+ severity: "error",
131
+ trigger: "change",
132
132
  ...vRef,
133
133
  }));
134
134
  }
@@ -141,22 +141,22 @@ export class SchemaController implements ValidationProvider {
141
141
 
142
142
  let bindingArray = binding.asArray();
143
143
  let normalized = bindingArray
144
- .map((p) => (typeof p === 'number' ? '[]' : p))
145
- .join('.');
144
+ .map((p) => (typeof p === "number" ? "[]" : p))
145
+ .join(".");
146
146
 
147
147
  if (normalized) {
148
148
  this.bindingSchemaNormalizedCache.set(binding, normalized);
149
- bindingArray = normalized.split('.');
149
+ bindingArray = normalized.split(".");
150
150
  }
151
151
 
152
152
  bindingArray.forEach((item) => {
153
153
  const recordBinding = bindingArray
154
- .map((p) => (p === item ? '{}' : p))
155
- .join('.');
154
+ .map((p) => (p === item ? "{}" : p))
155
+ .join(".");
156
156
 
157
157
  if (this.schema.get(recordBinding)) {
158
158
  this.bindingSchemaNormalizedCache.set(binding, recordBinding);
159
- bindingArray = recordBinding.split('.');
159
+ bindingArray = recordBinding.split(".");
160
160
  normalized = recordBinding;
161
161
  }
162
162
  });
@@ -167,12 +167,12 @@ export class SchemaController implements ValidationProvider {
167
167
  public getType(binding: BindingInstance): SchemaType.DataTypes | undefined {
168
168
  return this.hooks.resolveTypeForBinding.call(
169
169
  this.schema.get(this.normalizeBinding(binding)),
170
- binding
170
+ binding,
171
171
  );
172
172
  }
173
173
 
174
174
  public getApparentType(
175
- binding: BindingInstance
175
+ binding: BindingInstance,
176
176
  ): SchemaType.DataTypes | undefined {
177
177
  const schemaType = this.getType(binding);
178
178
 
@@ -201,7 +201,7 @@ export class SchemaController implements ValidationProvider {
201
201
  }
202
202
 
203
203
  public getFormatterForType(
204
- formatReference: Formatting.Reference
204
+ formatReference: Formatting.Reference,
205
205
  ): FormatDefinition<unknown, unknown> | undefined {
206
206
  const { type: formatType, ...options } = formatReference;
207
207
 
@@ -226,7 +226,7 @@ export class SchemaController implements ValidationProvider {
226
226
  * If no formatter is registered, it will return undefined
227
227
  */
228
228
  public getFormatter(
229
- binding: BindingInstance
229
+ binding: BindingInstance,
230
230
  ): FormatDefinition<unknown, unknown> | undefined {
231
231
  const type = this.getApparentType(binding);
232
232
 
@@ -1,6 +1,6 @@
1
- import type { Formatting } from '@player-ui/types';
1
+ import type { Formatting } from "@player-ui/types";
2
2
 
3
- export type FormatOptions = Omit<Formatting.Reference, 'type'>;
3
+ export type FormatOptions = Omit<Formatting.Reference, "type">;
4
4
 
5
5
  /**
6
6
  * The return types for the schema don't include options.
@@ -8,7 +8,7 @@ export type FormatOptions = Omit<Formatting.Reference, 'type'>;
8
8
  */
9
9
  export type FormatFunction<From, To = From, Options = unknown> = (
10
10
  val: From,
11
- options?: Options
11
+ options?: Options,
12
12
  ) => To | undefined;
13
13
 
14
14
  export type FormatHandler<From, To = From> = (val: From) => To;
@@ -30,7 +30,7 @@ export interface FormatDefinition<DataModelType, UserDisplayType> {
30
30
  export interface FormatType<
31
31
  DataModelType,
32
32
  UserDisplayType = DataModelType,
33
- Options = undefined
33
+ Options = undefined,
34
34
  > {
35
35
  /**
36
36
  * The name of the formatter.
@@ -0,0 +1,361 @@
1
+ import { test, expect, describe } from "vitest";
2
+ import type { Expression } from "@player-ui/types";
3
+ import { Player, PlayerPlugin } from "../../player";
4
+ import type { InProgressState } from "../../types";
5
+ import { BindingParser } from "../../binding";
6
+ import { LocalModel, withParser } from "../../data";
7
+ import { resolveDataRefs, resolveExpressionsInString } from "..";
8
+
9
+ class PhoneFormatterPlugin implements PlayerPlugin {
10
+ name = "phone-formatter";
11
+ apply(player: Player) {
12
+ player.hooks.schema.tap(this.name, (schema) => {
13
+ schema.addDataTypes([
14
+ {
15
+ type: "PhoneType",
16
+ format: {
17
+ type: "phone",
18
+ },
19
+ },
20
+ ]);
21
+
22
+ schema.addFormatters([
23
+ {
24
+ name: "phone",
25
+ format: (value) => {
26
+ return value.replace(/(\d{3})(\d{3})(\d{4})/, "$1-$2-$3");
27
+ },
28
+ deformat: (value) => {
29
+ return value.replace(/-/g, "");
30
+ },
31
+ },
32
+ ]);
33
+ });
34
+ }
35
+ }
36
+
37
+ test("works on basic data", () => {
38
+ const localModel = new LocalModel({
39
+ adam: {
40
+ age: 26,
41
+ },
42
+ index: 1,
43
+ person: {
44
+ first: "adam",
45
+ last: "dierkens",
46
+ },
47
+ name: "{{person.first}} {{person.last}}",
48
+ pets: [
49
+ {
50
+ name: "frodo",
51
+ type: "cat",
52
+ },
53
+ {
54
+ name: "ginger",
55
+ type: "dog",
56
+ },
57
+ ],
58
+ });
59
+
60
+ const bindingParser = new BindingParser({
61
+ get: localModel.get,
62
+ set: localModel.set,
63
+ evaluate: () => undefined,
64
+ });
65
+
66
+ const model = withParser(localModel, bindingParser.parse);
67
+
68
+ const options = {
69
+ model,
70
+ evaluate: (exp: Expression) => exp,
71
+ };
72
+
73
+ expect(
74
+ resolveDataRefs("Adam is {{adam.age}} years old", options),
75
+ ).toStrictEqual("Adam is 26 years old");
76
+
77
+ expect(
78
+ resolveDataRefs("My name is {{person.first}} {{person.last}}", options),
79
+ ).toStrictEqual("My name is adam dierkens");
80
+
81
+ expect(resolveDataRefs("My name is {{name}}", options)).toStrictEqual(
82
+ "My name is adam dierkens",
83
+ );
84
+
85
+ expect(resolveDataRefs("{{name}}", options)).toStrictEqual("adam dierkens");
86
+
87
+ expect(
88
+ resolveDataRefs('My cat is named {{pets[type="cat"].name}}', options),
89
+ ).toStrictEqual("My cat is named frodo");
90
+
91
+ expect(
92
+ resolveDataRefs("Name: {{pets.{{index}}.name}}", options),
93
+ ).toStrictEqual("Name: ginger");
94
+ });
95
+
96
+ test("replaces data w/ raw value if only data ref", () => {
97
+ const localModel = new LocalModel({ foo: 100 });
98
+
99
+ const bindingParser = new BindingParser({
100
+ get: localModel.get,
101
+ set: localModel.set,
102
+ evaluate: () => undefined,
103
+ });
104
+
105
+ const model = withParser(localModel, bindingParser.parse);
106
+
107
+ expect(
108
+ resolveDataRefs("{{foo}}", {
109
+ model,
110
+ evaluate: (exp) => exp,
111
+ }),
112
+ ).toStrictEqual(100);
113
+ });
114
+
115
+ test("works on objects and arrays", () => {
116
+ const localModel = new LocalModel({
117
+ adam: {
118
+ age: 26,
119
+ },
120
+ person: {
121
+ first: "adam",
122
+ last: "dierkens",
123
+ },
124
+ pets: [
125
+ {
126
+ name: "frodo",
127
+ type: "cat",
128
+ },
129
+ {
130
+ name: "ginger",
131
+ type: "dog",
132
+ },
133
+ ],
134
+ });
135
+
136
+ const bindingParser = new BindingParser({
137
+ get: localModel.get,
138
+ set: localModel.set,
139
+ evaluate: () => undefined,
140
+ });
141
+
142
+ const model = withParser(localModel, bindingParser.parse.bind(bindingParser));
143
+
144
+ expect(
145
+ resolveDataRefs(
146
+ [
147
+ "I have a {{pets.0.type}} named {{pets.0.name}}",
148
+ "I have a {{pets.1.type}} named {{pets.1.name}}",
149
+ ],
150
+ {
151
+ model,
152
+ evaluate: (exp) => exp,
153
+ },
154
+ ),
155
+ ).toStrictEqual(["I have a cat named frodo", "I have a dog named ginger"]);
156
+ });
157
+
158
+ test("handles undefined object", () => {
159
+ const localModel = new LocalModel({
160
+ adam: {
161
+ age: 26,
162
+ },
163
+ person: {
164
+ first: "adam",
165
+ last: "dierkens",
166
+ },
167
+ pets: [
168
+ {
169
+ name: "frodo",
170
+ type: "cat",
171
+ },
172
+ {
173
+ name: "ginger",
174
+ type: "dog",
175
+ },
176
+ ],
177
+ });
178
+
179
+ const bindingParser = new BindingParser({
180
+ get: localModel.get,
181
+ set: localModel.set,
182
+ evaluate: () => undefined,
183
+ });
184
+
185
+ const model = withParser(localModel, bindingParser.parse.bind(bindingParser));
186
+
187
+ expect(
188
+ resolveDataRefs(null, {
189
+ model,
190
+ evaluate: (exp) => exp,
191
+ }),
192
+ ).toBeNull();
193
+ });
194
+
195
+ test("resolves expressions", () => {
196
+ const localModel = new LocalModel({
197
+ adam: {
198
+ age: 26,
199
+ },
200
+ index: 1,
201
+ person: {
202
+ first: "adam",
203
+ last: "dierkens",
204
+ },
205
+ pets: [
206
+ {
207
+ name: "frodo",
208
+ type: "cat",
209
+ },
210
+ {
211
+ name: "ginger",
212
+ type: "dog",
213
+ },
214
+ ],
215
+ });
216
+
217
+ const bindingParser = new BindingParser({
218
+ get: localModel.get,
219
+ set: localModel.set,
220
+ evaluate: () => undefined,
221
+ });
222
+
223
+ const model = withParser(localModel, bindingParser.parse);
224
+
225
+ const options = {
226
+ model,
227
+ evaluate: (exp: Expression) => {
228
+ if (exp === '{{person.first}} + " " + {{person.last}}') {
229
+ return "adam dierkens";
230
+ }
231
+
232
+ if (exp === "{{adam.age}} + 10") {
233
+ return 36;
234
+ }
235
+ },
236
+ };
237
+
238
+ expect(
239
+ resolveExpressionsInString(
240
+ 'Hello @[{{person.first}} + " " + {{person.last}}]@',
241
+ options,
242
+ ),
243
+ ).toBe("Hello adam dierkens");
244
+
245
+ expect(resolveDataRefs("@[{{adam.age}} + 10]@", options)).toBe(36);
246
+ });
247
+
248
+ describe("Returns unformatted values for requests", () => {
249
+ const player = new Player({ plugins: [new PhoneFormatterPlugin()] });
250
+
251
+ const endStateFlow = {
252
+ id: "minimal-player-response-format",
253
+ topic: "MOCK",
254
+ schema: {
255
+ ROOT: {
256
+ phoneNumber: {
257
+ type: "PhoneType",
258
+ default: "false",
259
+ },
260
+ },
261
+ },
262
+ data: {
263
+ phoneNumber: "1234567890",
264
+ },
265
+ views: [
266
+ {
267
+ actions: [
268
+ {
269
+ asset: {
270
+ id: "action-1",
271
+ type: "action",
272
+ value: "Next",
273
+ label: {
274
+ asset: {
275
+ id: "Action-Label-Next",
276
+ type: "text",
277
+ value: "Continue",
278
+ },
279
+ },
280
+ },
281
+ },
282
+ ],
283
+ id: "KitchenSink-View1",
284
+ title: {
285
+ asset: {
286
+ id: "KitchenSink-View1-Title",
287
+ type: "text",
288
+ value: "{{phoneNumber}}",
289
+ },
290
+ },
291
+ type: "questionAnswer",
292
+ },
293
+ ],
294
+ navigation: {
295
+ BEGIN: "KitchenSinkFlow",
296
+ KitchenSinkFlow: {
297
+ END_Done: {
298
+ outcome: "{{phoneNumber}}",
299
+ state_type: "END",
300
+ },
301
+ VIEW_KitchenSink_1: {
302
+ ref: "KitchenSink-View1",
303
+ state_type: "VIEW",
304
+ transitions: {
305
+ param: "END_invokeWithParam",
306
+ "*": "END_Done",
307
+ },
308
+ },
309
+ END_invokeWithParam: {
310
+ state_type: "END",
311
+ outcome: "{{phoneNumber}}",
312
+ param: {
313
+ type: "someTopic",
314
+ topicId: "someTopicId",
315
+ navData: {
316
+ topic: "someTopic",
317
+ op: "EDIT",
318
+ param: {
319
+ phone: "{{phoneNumber}}",
320
+ },
321
+ },
322
+ },
323
+ },
324
+ startState: "VIEW_KitchenSink_1",
325
+ },
326
+ },
327
+ };
328
+
329
+ test("unformatted endState", async () => {
330
+ player.start(endStateFlow as any);
331
+
332
+ const state = player.getState() as InProgressState;
333
+
334
+ state.controllers.flow.transition("foo");
335
+
336
+ const { flowResult } = state;
337
+
338
+ const result = await flowResult;
339
+
340
+ expect(result.endState).toStrictEqual({
341
+ outcome: "1234567890",
342
+ state_type: "END",
343
+ });
344
+ });
345
+
346
+ test('unformatted "param"', async () => {
347
+ player.start(endStateFlow as any);
348
+
349
+ const state = player.getState() as InProgressState;
350
+
351
+ state.controllers.flow.transition("param");
352
+
353
+ const { flowResult } = state;
354
+
355
+ const result = await flowResult;
356
+
357
+ const param = result.endState.param as any;
358
+
359
+ expect(param.navData.param.phone).toBe("1234567890");
360
+ });
361
+ });
@@ -1,9 +1,9 @@
1
- import { setIn } from 'timm';
2
- import type { Expression } from '@player-ui/types';
3
- import type { DataModelWithParser } from '../data';
1
+ import { setIn } from "timm";
2
+ import type { Expression } from "@player-ui/types";
3
+ import type { DataModelWithParser } from "../data";
4
4
 
5
- const DOUBLE_OPEN_CURLY = '{{';
6
- const DOUBLE_CLOSE_CURLY = '}}';
5
+ const DOUBLE_OPEN_CURLY = "{{";
6
+ const DOUBLE_CLOSE_CURLY = "}}";
7
7
 
8
8
  export interface Options {
9
9
  /**
@@ -52,7 +52,7 @@ export function findNextExp(str: string) {
52
52
  // Move everything over and bump our close count by 1
53
53
  count++;
54
54
  workingString = workingString.substring(
55
- nextOpenCurly + DOUBLE_OPEN_CURLY.length
55
+ nextOpenCurly + DOUBLE_OPEN_CURLY.length,
56
56
  );
57
57
  offset += nextOpenCurly + DOUBLE_OPEN_CURLY.length;
58
58
  } else {
@@ -60,7 +60,7 @@ export function findNextExp(str: string) {
60
60
  // Decrement our count and updates offsets
61
61
  count--;
62
62
  workingString = workingString.substring(
63
- nextCloseCurly + DOUBLE_CLOSE_CURLY.length
63
+ nextCloseCurly + DOUBLE_CLOSE_CURLY.length,
64
64
  );
65
65
  offset += nextCloseCurly + DOUBLE_CLOSE_CURLY.length;
66
66
  }
@@ -79,7 +79,7 @@ export function findNextExp(str: string) {
79
79
  /** Finds any subset of the string wrapped in @[]@ and evaluates it as an expression */
80
80
  export function resolveExpressionsInString(
81
81
  val: string,
82
- { evaluate }: Options
82
+ { evaluate }: Options,
83
83
  ): string {
84
84
  if (!evaluate) {
85
85
  return val;
@@ -94,8 +94,8 @@ export function resolveExpressionsInString(
94
94
  const matchStart = newVal.indexOf(expStrWithBrackets);
95
95
 
96
96
  const expString = expStrWithBrackets.substr(
97
- '@['.length,
98
- expStrWithBrackets.length - '@['.length - ']@'.length
97
+ "@[".length,
98
+ expStrWithBrackets.length - "@[".length - "]@".length,
99
99
  );
100
100
  const expValue = evaluate(expString);
101
101
 
@@ -103,7 +103,7 @@ export function resolveExpressionsInString(
103
103
  if (
104
104
  matchStart === 0 &&
105
105
  expStrWithBrackets === val &&
106
- typeof expValue !== 'string'
106
+ typeof expValue !== "string"
107
107
  ) {
108
108
  return expValue;
109
109
  }
@@ -126,7 +126,7 @@ export function resolveDataRefsInString(val: string, options: Options): string {
126
126
 
127
127
  if (
128
128
  !model ||
129
- typeof workingString !== 'string' ||
129
+ typeof workingString !== "string" ||
130
130
  workingString.indexOf(DOUBLE_OPEN_CURLY) === -1
131
131
  ) {
132
132
  return workingString;
@@ -145,7 +145,7 @@ export function resolveDataRefsInString(val: string, options: Options): string {
145
145
  const binding = workingString
146
146
  .substring(
147
147
  start + DOUBLE_OPEN_CURLY.length,
148
- end - DOUBLE_OPEN_CURLY.length
148
+ end - DOUBLE_OPEN_CURLY.length,
149
149
  )
150
150
  .trim();
151
151
 
@@ -156,7 +156,7 @@ export function resolveDataRefsInString(val: string, options: Options): string {
156
156
  if (
157
157
  start === 0 &&
158
158
  end === workingString.length &&
159
- typeof evaledVal !== 'string'
159
+ typeof evaledVal !== "string"
160
160
  ) {
161
161
  return evaledVal;
162
162
  }
@@ -171,11 +171,11 @@ export function resolveDataRefsInString(val: string, options: Options): string {
171
171
  /** Traverse the thing and replace any model refs */
172
172
  function traverseObject<T>(val: T, options: Options): T {
173
173
  switch (typeof val) {
174
- case 'string': {
174
+ case "string": {
175
175
  return resolveDataRefsInString(val as string, options) as unknown as T;
176
176
  }
177
177
 
178
- case 'object': {
178
+ case "object": {
179
179
  if (!val) return val;
180
180
  // TODO: Do we care refs in keys?
181
181
  const keys = Object.keys(val);
@@ -186,7 +186,7 @@ function traverseObject<T>(val: T, options: Options): T {
186
186
  newVal = setIn(
187
187
  newVal as any,
188
188
  [key],
189
- traverseObject((val as any)[key], options)
189
+ traverseObject((val as any)[key], options),
190
190
  ) as any;
191
191
  });
192
192
  }