@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.
- package/dist/Player.native.js +11630 -0
- package/dist/Player.native.js.map +1 -0
- package/dist/cjs/index.cjs +5626 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/{index.esm.js → index.legacy-esm.js} +2044 -1667
- package/dist/{index.cjs.js → index.mjs} +2052 -1761
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -63
- package/src/__tests__/data.test.ts +498 -0
- package/src/__tests__/flow.test.ts +312 -0
- package/src/__tests__/helpers/action-exp.plugin.ts +22 -0
- package/src/__tests__/helpers/actions.flow.ts +67 -0
- package/src/__tests__/helpers/binding.plugin.ts +125 -0
- package/src/__tests__/helpers/expression.plugin.ts +88 -0
- package/src/__tests__/helpers/transform-plugin.ts +19 -0
- package/src/__tests__/helpers/validation.flow.ts +56 -0
- package/src/__tests__/player.test.ts +597 -0
- package/src/__tests__/string-resolver.test.ts +186 -0
- package/src/__tests__/validation.test.ts +3555 -0
- package/src/__tests__/view.test.ts +715 -0
- package/src/binding/__tests__/binding.test.ts +113 -0
- package/src/binding/__tests__/index.test.ts +208 -0
- package/src/binding/__tests__/resolver.test.ts +83 -0
- package/src/binding/binding.ts +6 -6
- package/src/binding/index.ts +34 -34
- package/src/binding/resolver.ts +19 -19
- package/src/binding/utils.ts +7 -7
- package/src/binding-grammar/__tests__/parser.test.ts +64 -0
- package/src/binding-grammar/__tests__/test-utils/ast-cases.ts +198 -0
- package/src/binding-grammar/__tests__/test-utils/perf-test.ts +66 -0
- package/src/binding-grammar/ast.ts +11 -11
- package/src/binding-grammar/custom/index.ts +19 -22
- package/src/binding-grammar/ebnf/index.ts +20 -21
- package/src/binding-grammar/ebnf/types.ts +13 -13
- package/src/binding-grammar/index.ts +4 -4
- package/src/binding-grammar/parsimmon/index.ts +14 -14
- package/src/controllers/constants/__tests__/index.test.ts +106 -0
- package/src/controllers/constants/index.ts +3 -3
- package/src/controllers/constants/utils.ts +4 -4
- package/src/controllers/data/controller.ts +22 -22
- package/src/controllers/data/index.ts +1 -1
- package/src/controllers/data/utils.ts +7 -7
- package/src/controllers/flow/__tests__/controller.test.ts +195 -0
- package/src/controllers/flow/__tests__/flow.test.ts +381 -0
- package/src/controllers/flow/controller.ts +13 -13
- package/src/controllers/flow/flow.ts +23 -23
- package/src/controllers/flow/index.ts +2 -2
- package/src/controllers/index.ts +5 -5
- package/src/controllers/validation/binding-tracker.ts +71 -59
- package/src/controllers/validation/controller.ts +104 -104
- package/src/controllers/validation/index.ts +2 -2
- package/src/controllers/view/asset-transform.ts +20 -20
- package/src/controllers/view/controller.ts +27 -27
- package/src/controllers/view/index.ts +4 -4
- package/src/controllers/view/store.ts +3 -3
- package/src/controllers/view/types.ts +7 -7
- package/src/data/__tests__/__snapshots__/dependency-tracker.test.ts.snap +64 -0
- package/src/data/__tests__/dependency-tracker.test.ts +146 -0
- package/src/data/__tests__/local-model.test.ts +46 -0
- package/src/data/__tests__/model.test.ts +78 -0
- package/src/data/dependency-tracker.ts +16 -16
- package/src/data/index.ts +4 -4
- package/src/data/local-model.ts +6 -6
- package/src/data/model.ts +17 -17
- package/src/data/noop-model.ts +1 -1
- package/src/expressions/__tests__/__snapshots__/parser.test.ts.snap +854 -0
- package/src/expressions/__tests__/evaluator-functions.test.ts +47 -0
- package/src/expressions/__tests__/evaluator.test.ts +410 -0
- package/src/expressions/__tests__/parser.test.ts +115 -0
- package/src/expressions/__tests__/utils.test.ts +44 -0
- package/src/expressions/evaluator-functions.ts +6 -6
- package/src/expressions/evaluator.ts +71 -67
- package/src/expressions/index.ts +4 -4
- package/src/expressions/parser.ts +102 -105
- package/src/expressions/types.ts +29 -21
- package/src/expressions/utils.ts +32 -21
- package/src/index.ts +13 -13
- package/src/logger/__tests__/consoleLogger.test.ts +46 -0
- package/src/logger/__tests__/noopLogger.test.ts +13 -0
- package/src/logger/__tests__/proxyLogger.test.ts +31 -0
- package/src/logger/__tests__/tapableLogger.test.ts +41 -0
- package/src/logger/consoleLogger.ts +9 -9
- package/src/logger/index.ts +5 -5
- package/src/logger/noopLogger.ts +1 -1
- package/src/logger/proxyLogger.ts +6 -6
- package/src/logger/tapableLogger.ts +7 -7
- package/src/logger/types.ts +2 -2
- package/src/player.ts +60 -58
- package/src/plugins/default-exp-plugin.ts +10 -10
- package/src/plugins/default-view-plugin.ts +29 -0
- package/src/plugins/flow-exp-plugin.ts +6 -6
- package/src/schema/__tests__/schema.test.ts +243 -0
- package/src/schema/index.ts +2 -2
- package/src/schema/schema.ts +24 -24
- package/src/schema/types.ts +4 -4
- package/src/string-resolver/__tests__/index.test.ts +361 -0
- package/src/string-resolver/index.ts +17 -17
- package/src/types.ts +17 -17
- package/src/utils/__tests__/replaceParams.test.ts +33 -0
- package/src/utils/index.ts +1 -1
- package/src/utils/replaceParams.ts +1 -1
- package/src/validator/__tests__/binding-map-splice.test.ts +53 -0
- package/src/validator/__tests__/validation-middleware.test.ts +127 -0
- package/src/validator/binding-map-splice.ts +5 -5
- package/src/validator/index.ts +4 -4
- package/src/validator/registry.ts +1 -1
- package/src/validator/types.ts +13 -13
- package/src/validator/validation-middleware.ts +15 -15
- package/src/view/__tests__/view.immutable.test.ts +269 -0
- package/src/view/__tests__/view.test.ts +959 -0
- package/src/view/builder/index.test.ts +69 -0
- package/src/view/builder/index.ts +3 -3
- package/src/view/index.ts +5 -5
- package/src/view/parser/__tests__/__snapshots__/parser.test.ts.snap +394 -0
- package/src/view/parser/__tests__/parser.test.ts +264 -0
- package/src/view/parser/index.ts +43 -33
- package/src/view/parser/types.ts +11 -11
- package/src/view/parser/utils.ts +5 -5
- package/src/view/plugins/__tests__/__snapshots__/template.test.ts.snap +278 -0
- package/src/view/plugins/__tests__/applicability.test.ts +265 -0
- package/src/view/plugins/__tests__/string.test.ts +122 -0
- package/src/view/plugins/__tests__/template.test.ts +724 -0
- package/src/view/plugins/applicability.ts +19 -19
- package/src/view/plugins/index.ts +4 -5
- package/src/view/plugins/options.ts +1 -1
- package/src/view/plugins/string-resolver.ts +22 -22
- package/src/view/plugins/switch.ts +22 -23
- package/src/view/plugins/template-plugin.ts +26 -27
- package/src/view/resolver/__tests__/dependencies.test.ts +321 -0
- package/src/view/resolver/__tests__/edgecases.test.ts +626 -0
- package/src/view/resolver/index.ts +42 -42
- package/src/view/resolver/types.ts +21 -20
- package/src/view/resolver/utils.ts +9 -9
- package/src/view/view.ts +32 -22
- package/types/binding/binding.d.ts +50 -0
- package/types/binding/index.d.ts +29 -0
- package/types/binding/resolver.d.ts +26 -0
- package/types/binding/utils.d.ts +12 -0
- package/types/binding-grammar/ast.d.ts +67 -0
- package/types/binding-grammar/custom/index.d.ts +4 -0
- package/types/binding-grammar/ebnf/index.d.ts +4 -0
- package/types/binding-grammar/ebnf/types.d.ts +75 -0
- package/types/binding-grammar/index.d.ts +5 -0
- package/types/binding-grammar/parsimmon/index.d.ts +4 -0
- package/types/controllers/constants/index.d.ts +45 -0
- package/types/controllers/constants/utils.d.ts +6 -0
- package/types/controllers/data/controller.d.ts +45 -0
- package/types/controllers/data/index.d.ts +2 -0
- package/types/controllers/data/utils.d.ts +14 -0
- package/types/controllers/flow/controller.d.ts +25 -0
- package/types/controllers/flow/flow.d.ts +50 -0
- package/types/controllers/flow/index.d.ts +3 -0
- package/types/controllers/index.d.ts +6 -0
- package/types/controllers/validation/binding-tracker.d.ts +32 -0
- package/types/controllers/validation/controller.d.ts +151 -0
- package/types/controllers/validation/index.d.ts +3 -0
- package/types/controllers/view/asset-transform.d.ts +19 -0
- package/types/controllers/view/controller.d.ts +37 -0
- package/types/controllers/view/index.d.ts +5 -0
- package/types/controllers/view/store.d.ts +20 -0
- package/types/controllers/view/types.d.ts +16 -0
- package/types/data/dependency-tracker.d.ts +49 -0
- package/types/data/index.d.ts +5 -0
- package/types/data/local-model.d.ts +16 -0
- package/types/data/model.d.ts +86 -0
- package/types/data/noop-model.d.ts +13 -0
- package/types/expressions/evaluator-functions.d.ts +15 -0
- package/types/expressions/evaluator.d.ts +52 -0
- package/types/expressions/index.d.ts +5 -0
- package/types/expressions/parser.d.ts +10 -0
- package/types/expressions/types.d.ts +144 -0
- package/types/expressions/utils.d.ts +12 -0
- package/types/index.d.ts +14 -0
- package/types/logger/consoleLogger.d.ts +17 -0
- package/types/logger/index.d.ts +6 -0
- package/types/logger/noopLogger.d.ts +10 -0
- package/types/logger/proxyLogger.d.ts +15 -0
- package/types/logger/tapableLogger.d.ts +23 -0
- package/types/logger/types.d.ts +6 -0
- package/types/player.d.ts +101 -0
- package/types/plugins/default-exp-plugin.d.ts +9 -0
- package/types/plugins/default-view-plugin.d.ts +9 -0
- package/types/plugins/flow-exp-plugin.d.ts +11 -0
- package/types/schema/index.d.ts +3 -0
- package/types/schema/schema.d.ts +36 -0
- package/types/schema/types.d.ts +38 -0
- package/types/string-resolver/index.d.ts +30 -0
- package/types/types.d.ts +73 -0
- package/types/utils/index.d.ts +2 -0
- package/types/utils/replaceParams.d.ts +9 -0
- package/types/validator/binding-map-splice.d.ts +10 -0
- package/types/validator/index.d.ts +5 -0
- package/types/validator/registry.d.ts +11 -0
- package/types/validator/types.d.ts +53 -0
- package/types/validator/validation-middleware.d.ts +36 -0
- package/types/view/builder/index.d.ts +35 -0
- package/types/view/index.d.ts +6 -0
- package/types/view/parser/index.d.ts +52 -0
- package/types/view/parser/types.d.ts +109 -0
- package/types/view/parser/utils.d.ts +6 -0
- package/types/view/plugins/applicability.d.ts +10 -0
- package/types/view/plugins/index.d.ts +5 -0
- package/types/view/plugins/options.d.ts +4 -0
- package/types/view/plugins/string-resolver.d.ts +13 -0
- package/types/view/plugins/switch.d.ts +14 -0
- package/types/view/plugins/template-plugin.d.ts +33 -0
- package/types/view/resolver/index.d.ts +73 -0
- package/types/view/resolver/types.d.ts +129 -0
- package/types/view/resolver/utils.d.ts +11 -0
- package/types/view/view.d.ts +37 -0
- package/dist/index.d.ts +0 -1814
- package/dist/player.dev.js +0 -11472
- package/dist/player.prod.js +0 -2
- package/src/view/plugins/plugin.ts +0 -21
package/src/schema/schema.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { SyncWaterfallHook } from
|
|
2
|
-
import type { Schema as SchemaType, Formatting } from
|
|
1
|
+
import { SyncWaterfallHook } from "tapable-ts";
|
|
2
|
+
import type { Schema as SchemaType, Formatting } from "@player-ui/types";
|
|
3
3
|
|
|
4
|
-
import type { BindingInstance } from
|
|
5
|
-
import type { ValidationProvider, ValidationObject } from
|
|
6
|
-
import type { FormatDefinition, FormatOptions, FormatType } from
|
|
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:
|
|
131
|
-
trigger:
|
|
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 ===
|
|
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 ?
|
|
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
|
|
package/src/schema/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Formatting } from
|
|
1
|
+
import type { Formatting } from "@player-ui/types";
|
|
2
2
|
|
|
3
|
-
export type FormatOptions = Omit<Formatting.Reference,
|
|
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
|
|
2
|
-
import type { Expression } from
|
|
3
|
-
import type { DataModelWithParser } from
|
|
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
|
-
|
|
98
|
-
expStrWithBrackets.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 !==
|
|
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 !==
|
|
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 !==
|
|
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
|
|
174
|
+
case "string": {
|
|
175
175
|
return resolveDataRefsInString(val as string, options) as unknown as T;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
case
|
|
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
|
}
|