@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/package.json
CHANGED
|
@@ -1,77 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@player-ui/player",
|
|
3
|
-
"version": "0.8.0
|
|
4
|
-
"
|
|
5
|
-
"publishConfig": {
|
|
6
|
-
"registry": "https://registry.npmjs.org"
|
|
7
|
-
},
|
|
8
|
-
"peerDependencies": {},
|
|
3
|
+
"version": "0.8.0-next.0",
|
|
4
|
+
"main": "dist/cjs/index.cjs",
|
|
9
5
|
"dependencies": {
|
|
10
|
-
"@player-ui/partial-match-registry": "0.8.0
|
|
11
|
-
"@player-ui/
|
|
6
|
+
"@player-ui/partial-match-registry": "0.8.0-next.0",
|
|
7
|
+
"@player-ui/make-flow": "0.8.0-next.0",
|
|
8
|
+
"@player-ui/types": "0.8.0-next.0",
|
|
9
|
+
"@types/dlv": "^1.1.4",
|
|
10
|
+
"@types/parsimmon": "^1.10.0",
|
|
11
|
+
"arr-flatten": "^1.1.0",
|
|
12
12
|
"dequal": "^2.0.2",
|
|
13
|
+
"dlv": "^1.1.3",
|
|
14
|
+
"ebnf": "^1.9.0",
|
|
15
|
+
"error-polyfill": "^0.1.3",
|
|
13
16
|
"p-defer": "^3.0.0",
|
|
17
|
+
"parsimmon": "^1.12.0",
|
|
14
18
|
"queue-microtask": "^1.2.3",
|
|
15
19
|
"tapable-ts": "^0.2.3",
|
|
16
|
-
"parsimmon": "^1.12.0",
|
|
17
|
-
"@types/parsimmon": "^1.10.0",
|
|
18
|
-
"arr-flatten": "^1.1.0",
|
|
19
|
-
"ebnf": "^1.9.0",
|
|
20
20
|
"timm": "^1.6.2",
|
|
21
|
-
"error-polyfill": "^0.1.3",
|
|
22
21
|
"ts-nested-error": "^1.2.1",
|
|
23
|
-
"
|
|
22
|
+
"tslib": "^2.6.2"
|
|
24
23
|
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
24
|
+
"module": "dist/index.legacy-esm.js",
|
|
25
|
+
"types": "types/index.d.ts",
|
|
26
|
+
"bundle": "dist/Player.native.js",
|
|
28
27
|
"sideEffects": false,
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
},
|
|
37
|
-
"homepage": "https://player-ui.github.io",
|
|
38
|
-
"contributors": [
|
|
39
|
-
{
|
|
40
|
-
"name": "Adam Dierkens",
|
|
41
|
-
"url": "https://github.com/adierkens"
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"name": "Spencer Hamm",
|
|
45
|
-
"url": "https://github.com/spentacular"
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
"name": "Harris Borawski",
|
|
49
|
-
"url": "https://github.com/hborawski"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"name": "Jeremiah Zucker",
|
|
53
|
-
"url": "https://github.com/sugarmanz"
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"name": "Ketan Reddy",
|
|
57
|
-
"url": "https://github.com/KetanReddy"
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
"name": "Brocollie08",
|
|
61
|
-
"url": "https://github.com/brocollie08"
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
"name": "Kelly Harrop",
|
|
65
|
-
"url": "https://github.com/kharrop"
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
"name": "Alejandro Fimbres",
|
|
69
|
-
"url": "https://github.com/lexfm"
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"name": "Rafael Campos",
|
|
73
|
-
"url": "https://github.com/rafbcampos"
|
|
28
|
+
"exports": {
|
|
29
|
+
"./package.json": "./package.json",
|
|
30
|
+
"./dist/index.css": "./dist/index.css",
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./types/index.d.ts",
|
|
33
|
+
"import": "./dist/index.mjs",
|
|
34
|
+
"default": "./dist/cjs/index.cjs"
|
|
74
35
|
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"src",
|
|
40
|
+
"types"
|
|
75
41
|
],
|
|
76
|
-
"
|
|
42
|
+
"peerDependencies": {}
|
|
77
43
|
}
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
import { describe, it, beforeEach, vitest, test, expect } from "vitest";
|
|
2
|
+
import { BindingParser } from "../binding";
|
|
3
|
+
import { LocalModel } from "../data";
|
|
4
|
+
import { ValidationMiddleware } from "../validator";
|
|
5
|
+
import type { Logger } from "..";
|
|
6
|
+
import { DataController } from "..";
|
|
7
|
+
import type { ReadOnlyDataController } from "../controllers/data/utils";
|
|
8
|
+
|
|
9
|
+
test("works with basic data", () => {
|
|
10
|
+
const model = {
|
|
11
|
+
foo: {
|
|
12
|
+
bar: "baz",
|
|
13
|
+
},
|
|
14
|
+
bar: "foo",
|
|
15
|
+
baz: [{ foo: "1" }],
|
|
16
|
+
};
|
|
17
|
+
const localData = new LocalModel(model);
|
|
18
|
+
|
|
19
|
+
const parser = new BindingParser({ get: localData.get, set: localData.set });
|
|
20
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
21
|
+
|
|
22
|
+
controller.hooks.resolveDataStages.tap("basic", () => [localData]);
|
|
23
|
+
|
|
24
|
+
expect(controller.get("foo")).toStrictEqual(model.foo);
|
|
25
|
+
expect(controller.get("foo.bar")).toStrictEqual(model.foo.bar);
|
|
26
|
+
expect(controller.get("baz.0.foo")).toStrictEqual(model.baz[0].foo);
|
|
27
|
+
|
|
28
|
+
controller.set({ "foo.baz": "bar" });
|
|
29
|
+
expect(controller.get("foo.baz")).toStrictEqual("bar");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("works with path segments starting with numbers", () => {
|
|
33
|
+
const model = {
|
|
34
|
+
foo: {
|
|
35
|
+
"5f4704fd-adab-49df-bcbc-5aedb04194f9": {
|
|
36
|
+
bar: "baz",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
const localData = new LocalModel(model);
|
|
41
|
+
|
|
42
|
+
const parser = new BindingParser({ get: localData.get, set: localData.set });
|
|
43
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
44
|
+
|
|
45
|
+
controller.hooks.resolveDataStages.tap("basic", () => [localData]);
|
|
46
|
+
|
|
47
|
+
expect(controller.get("foo.5f4704fd-adab-49df-bcbc-5aedb04194f9.bar")).toBe(
|
|
48
|
+
"baz",
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("works with nested model refs", () => {
|
|
53
|
+
const model = {
|
|
54
|
+
foo: {
|
|
55
|
+
bar: "baz",
|
|
56
|
+
},
|
|
57
|
+
other: "bar",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const localData = new LocalModel(model);
|
|
61
|
+
|
|
62
|
+
const parser = new BindingParser({ get: localData.get, set: localData.set });
|
|
63
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
64
|
+
|
|
65
|
+
controller.hooks.resolveDataStages.tap("basic", () => [localData]);
|
|
66
|
+
|
|
67
|
+
expect(controller.get("foo.{{other}}")).toStrictEqual(model.foo.bar);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("works with variable indexes", () => {
|
|
71
|
+
const model = {
|
|
72
|
+
foo: [{ bar: "AAA" }, { bar: "BBB" }],
|
|
73
|
+
baz: 1,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const localData = new LocalModel(model);
|
|
77
|
+
|
|
78
|
+
const parser = new BindingParser({ get: localData.get, set: localData.set });
|
|
79
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
80
|
+
|
|
81
|
+
controller.hooks.resolveDataStages.tap("basic", () => [localData]);
|
|
82
|
+
|
|
83
|
+
expect(controller.get("foo[{{baz}}].bar")).toStrictEqual("BBB");
|
|
84
|
+
controller.set([["baz", 0]]);
|
|
85
|
+
expect(controller.get("foo[{{baz}}].bar")).toStrictEqual("AAA");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("works with updates", () => {
|
|
89
|
+
const model = {
|
|
90
|
+
foo: [{ UUID: "not baz" }],
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const localData = new LocalModel(model);
|
|
94
|
+
|
|
95
|
+
const parser = new BindingParser({ get: localData.get, set: localData.set });
|
|
96
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
97
|
+
|
|
98
|
+
controller.hooks.resolveDataStages.tap("basic", () => [localData]);
|
|
99
|
+
|
|
100
|
+
controller.set({
|
|
101
|
+
"foo[UUID='baz'].blah": "blah",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(controller.get("foo.0.UUID")).toStrictEqual(model.foo[0].UUID);
|
|
105
|
+
expect(controller.get("foo.1.UUID")).toStrictEqual("baz");
|
|
106
|
+
expect(controller.get("foo.1.blah")).toStrictEqual("blah");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("delete", () => {
|
|
110
|
+
test("requires binding", () => {
|
|
111
|
+
const model = {
|
|
112
|
+
foo: {
|
|
113
|
+
bar: "Some Data",
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const localData = new LocalModel(model);
|
|
118
|
+
|
|
119
|
+
const parser = new BindingParser({
|
|
120
|
+
get: localData.get,
|
|
121
|
+
set: localData.set,
|
|
122
|
+
});
|
|
123
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
124
|
+
|
|
125
|
+
controller.hooks.resolveDataStages.tap("Local", () => [localData]);
|
|
126
|
+
|
|
127
|
+
expect(() => controller.delete(undefined as any)).toThrow(
|
|
128
|
+
"Invalid arguments: delete expects a data path (string)",
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("does nothing if not in dataModel", () => {
|
|
133
|
+
const model = {
|
|
134
|
+
foo: {
|
|
135
|
+
bar: "Some Data",
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
const localData = new LocalModel(model);
|
|
139
|
+
|
|
140
|
+
const parser = new BindingParser({
|
|
141
|
+
get: localData.get,
|
|
142
|
+
set: localData.set,
|
|
143
|
+
});
|
|
144
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
145
|
+
controller.hooks.resolveDataStages.tap("Local", () => [localData]);
|
|
146
|
+
|
|
147
|
+
controller.delete("foo.baz");
|
|
148
|
+
|
|
149
|
+
expect(controller.get("")).toStrictEqual({
|
|
150
|
+
foo: { bar: "Some Data" },
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("deletes property", () => {
|
|
155
|
+
const model = {
|
|
156
|
+
foo: {
|
|
157
|
+
bar: "Some Data",
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
const localData = new LocalModel(model);
|
|
161
|
+
|
|
162
|
+
const parser = new BindingParser({
|
|
163
|
+
get: localData.get,
|
|
164
|
+
set: localData.set,
|
|
165
|
+
});
|
|
166
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
167
|
+
controller.hooks.resolveDataStages.tap("Local", () => [localData]);
|
|
168
|
+
|
|
169
|
+
controller.delete("foo.bar");
|
|
170
|
+
|
|
171
|
+
expect(controller.get("")).toStrictEqual({ foo: {} });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("deletes array item", () => {
|
|
175
|
+
const model = {
|
|
176
|
+
foo: ["Some Data"],
|
|
177
|
+
};
|
|
178
|
+
const localData = new LocalModel(model);
|
|
179
|
+
|
|
180
|
+
const parser = new BindingParser({
|
|
181
|
+
get: localData.get,
|
|
182
|
+
set: localData.set,
|
|
183
|
+
});
|
|
184
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
185
|
+
controller.hooks.resolveDataStages.tap("Local", () => [localData]);
|
|
186
|
+
|
|
187
|
+
controller.delete("foo.0");
|
|
188
|
+
|
|
189
|
+
expect(controller.get("")).toStrictEqual({ foo: [] });
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("doesn't delete data that is out of range", () => {
|
|
193
|
+
const model = {
|
|
194
|
+
foo: ["Some Data"],
|
|
195
|
+
};
|
|
196
|
+
const localData = new LocalModel(model);
|
|
197
|
+
|
|
198
|
+
const parser = new BindingParser({
|
|
199
|
+
get: localData.get,
|
|
200
|
+
set: localData.set,
|
|
201
|
+
});
|
|
202
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
203
|
+
controller.hooks.resolveDataStages.tap("Local", () => [localData]);
|
|
204
|
+
|
|
205
|
+
controller.delete("foo.1");
|
|
206
|
+
|
|
207
|
+
expect(controller.get("")).toStrictEqual({ foo: ["Some Data"] });
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("deletes root property", () => {
|
|
211
|
+
const model = {
|
|
212
|
+
foo: {
|
|
213
|
+
bar: "Some Data",
|
|
214
|
+
},
|
|
215
|
+
baz: "Other data",
|
|
216
|
+
};
|
|
217
|
+
const localData = new LocalModel(model);
|
|
218
|
+
|
|
219
|
+
const parser = new BindingParser({
|
|
220
|
+
get: localData.get,
|
|
221
|
+
set: localData.set,
|
|
222
|
+
});
|
|
223
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
224
|
+
controller.hooks.resolveDataStages.tap("Local", () => [localData]);
|
|
225
|
+
|
|
226
|
+
controller.delete("foo");
|
|
227
|
+
|
|
228
|
+
expect(controller.get("")).toStrictEqual({ baz: "Other data" });
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("deletes nothing for a blank binding", () => {
|
|
232
|
+
const model = {
|
|
233
|
+
foo: {
|
|
234
|
+
bar: "Some Data",
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
const localData = new LocalModel(model);
|
|
238
|
+
|
|
239
|
+
const parser = new BindingParser({
|
|
240
|
+
get: localData.get,
|
|
241
|
+
set: localData.set,
|
|
242
|
+
});
|
|
243
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
244
|
+
controller.hooks.resolveDataStages.tap("Local", () => [localData]);
|
|
245
|
+
|
|
246
|
+
controller.delete("");
|
|
247
|
+
|
|
248
|
+
expect(controller.get("")).toStrictEqual({
|
|
249
|
+
foo: {
|
|
250
|
+
bar: "Some Data",
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe("formatting", () => {
|
|
257
|
+
it("formats data", () => {
|
|
258
|
+
const localData = new LocalModel({});
|
|
259
|
+
|
|
260
|
+
const parser = new BindingParser({
|
|
261
|
+
get: localData.get,
|
|
262
|
+
set: localData.set,
|
|
263
|
+
});
|
|
264
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
265
|
+
|
|
266
|
+
controller.hooks.format.tap("test", (val) => {
|
|
267
|
+
if (val === "should-format") {
|
|
268
|
+
return "formatted!";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return val;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
controller.hooks.deformat.tap("test", (val) => {
|
|
275
|
+
if (val === "should-deformat") {
|
|
276
|
+
return "deformatted!";
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return val;
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
controller.set([["foo.bar", "should-format"]], { formatted: true });
|
|
283
|
+
expect(controller.get("foo.bar")).toBe("should-format");
|
|
284
|
+
expect(controller.get("foo.bar", { formatted: true })).toBe("formatted!");
|
|
285
|
+
|
|
286
|
+
controller.set([["foo.baz", "should-deformat"]]);
|
|
287
|
+
expect(controller.get("foo.baz")).toBe("should-deformat");
|
|
288
|
+
|
|
289
|
+
controller.set([["foo.baz", "should-deformat"]], { formatted: true });
|
|
290
|
+
expect(controller.get("foo.baz")).toBe("deformatted!");
|
|
291
|
+
expect(controller.get("foo.baz", { formatted: false })).toBe(
|
|
292
|
+
"deformatted!",
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
describe("serialization", () => {
|
|
298
|
+
it("can hook into serializing", () => {
|
|
299
|
+
const localData = new LocalModel();
|
|
300
|
+
const parser = new BindingParser({
|
|
301
|
+
get: localData.get,
|
|
302
|
+
set: localData.set,
|
|
303
|
+
});
|
|
304
|
+
const controller = new DataController(
|
|
305
|
+
{ testData: 0 },
|
|
306
|
+
{ pathResolver: parser },
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
controller.hooks.serialize.tap("test", (dataModel) => {
|
|
310
|
+
return {
|
|
311
|
+
...dataModel,
|
|
312
|
+
keys: Object.keys(dataModel),
|
|
313
|
+
};
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
expect(controller.serialize()).toStrictEqual({
|
|
317
|
+
testData: 0,
|
|
318
|
+
keys: ["testData"],
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it("doesnt include invalid data", () => {
|
|
323
|
+
const localData = new LocalModel();
|
|
324
|
+
const parser = new BindingParser({
|
|
325
|
+
get: localData.get,
|
|
326
|
+
set: localData.set,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const dc = new DataController(
|
|
330
|
+
{ valid: true, invalid: false },
|
|
331
|
+
{
|
|
332
|
+
pathResolver: parser,
|
|
333
|
+
middleware: [
|
|
334
|
+
new ValidationMiddleware((binding, model) => {
|
|
335
|
+
if (
|
|
336
|
+
binding.asString() === "invalid" &&
|
|
337
|
+
model.get(binding) !== false
|
|
338
|
+
) {
|
|
339
|
+
return {
|
|
340
|
+
severity: "error",
|
|
341
|
+
message: "Nope",
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}),
|
|
345
|
+
],
|
|
346
|
+
},
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
expect(dc?.serialize()).toStrictEqual({
|
|
350
|
+
valid: true,
|
|
351
|
+
invalid: false,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
dc.set([["invalid", true]]);
|
|
355
|
+
|
|
356
|
+
expect(dc?.serialize()).toStrictEqual({
|
|
357
|
+
valid: true,
|
|
358
|
+
invalid: false,
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
expect(dc.get("", { includeInvalid: true })).toStrictEqual({
|
|
362
|
+
valid: true,
|
|
363
|
+
invalid: true,
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
describe("default value", () => {
|
|
369
|
+
it("gets/sets with default", () => {
|
|
370
|
+
const model = {
|
|
371
|
+
foo: "foo",
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
const localData = new LocalModel(model);
|
|
375
|
+
|
|
376
|
+
const parser = new BindingParser({
|
|
377
|
+
get: localData.get,
|
|
378
|
+
set: localData.set,
|
|
379
|
+
});
|
|
380
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
381
|
+
|
|
382
|
+
controller.hooks.resolveDefaultValue.tap("test", (b) => {
|
|
383
|
+
if (b.asString() === "foo") {
|
|
384
|
+
return "FOO";
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (b.asString() === "bar") {
|
|
388
|
+
return "BAR";
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
controller.hooks.resolveDataStages.tap("basic", () => [localData]);
|
|
393
|
+
|
|
394
|
+
expect(controller.get("bar")).toBe("BAR");
|
|
395
|
+
|
|
396
|
+
controller.set([["foo", undefined]]);
|
|
397
|
+
expect(controller.get("foo")).toBe("FOO");
|
|
398
|
+
|
|
399
|
+
// The data isn't actually set though
|
|
400
|
+
expect(controller.get("")).toStrictEqual({ foo: undefined });
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it("should not send update for deeply equal data", () => {
|
|
405
|
+
const model = {
|
|
406
|
+
user: {
|
|
407
|
+
name: "frodo",
|
|
408
|
+
age: 3,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const localData = new LocalModel(model);
|
|
413
|
+
|
|
414
|
+
const parser = new BindingParser({
|
|
415
|
+
get: localData.get,
|
|
416
|
+
set: localData.set,
|
|
417
|
+
});
|
|
418
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
419
|
+
controller.hooks.resolveDataStages.tap("basic", () => [localData]);
|
|
420
|
+
|
|
421
|
+
const onUpdateCallback = vitest.fn();
|
|
422
|
+
controller.hooks.onUpdate.tap("test", onUpdateCallback);
|
|
423
|
+
|
|
424
|
+
controller.set([["user", { name: "frodo", age: 3 }]]);
|
|
425
|
+
|
|
426
|
+
expect(onUpdateCallback).not.toBeCalled();
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it("should handle deleting non-existent value + parent value", () => {
|
|
430
|
+
const model = {
|
|
431
|
+
user: {
|
|
432
|
+
name: "frodo",
|
|
433
|
+
age: 3,
|
|
434
|
+
},
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const localData = new LocalModel(model);
|
|
438
|
+
|
|
439
|
+
const parser = new BindingParser({
|
|
440
|
+
get: localData.get,
|
|
441
|
+
set: localData.set,
|
|
442
|
+
});
|
|
443
|
+
const controller = new DataController({}, { pathResolver: parser });
|
|
444
|
+
controller.hooks.resolveDataStages.tap("basic", () => [localData]);
|
|
445
|
+
|
|
446
|
+
controller.delete("user.email");
|
|
447
|
+
|
|
448
|
+
expect(controller.get("user")).toStrictEqual({
|
|
449
|
+
name: "frodo",
|
|
450
|
+
age: 3,
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
controller.delete("foo.bar");
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
describe("Read Only Data Controller", () => {
|
|
457
|
+
let readOnlyController: ReadOnlyDataController;
|
|
458
|
+
let logger: Logger;
|
|
459
|
+
|
|
460
|
+
beforeEach(() => {
|
|
461
|
+
const localData = new LocalModel();
|
|
462
|
+
const parser = new BindingParser({
|
|
463
|
+
get: localData.get,
|
|
464
|
+
set: localData.set,
|
|
465
|
+
});
|
|
466
|
+
logger = {
|
|
467
|
+
trace: vitest.fn(),
|
|
468
|
+
debug: vitest.fn(),
|
|
469
|
+
info: vitest.fn(),
|
|
470
|
+
warn: vitest.fn(),
|
|
471
|
+
error: vitest.fn(),
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const controller = new DataController(
|
|
475
|
+
{ some: { data: true } },
|
|
476
|
+
{ pathResolver: parser, logger },
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
readOnlyController = controller.makeReadOnly();
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
it("Reads data", () => {
|
|
483
|
+
expect(readOnlyController.get("some.data")).toStrictEqual(true);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it("Logs error on set", () => {
|
|
487
|
+
expect(readOnlyController.set([["some.data", false]])).toStrictEqual([]);
|
|
488
|
+
expect(logger.error).toBeCalledWith(
|
|
489
|
+
"Error: Tried to set in a read only instance of the DataController",
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
it("Logs error on delete", () => {
|
|
493
|
+
readOnlyController.delete("some.data");
|
|
494
|
+
expect(logger.error).toBeCalledWith(
|
|
495
|
+
"Error: Tried to delete in a read only instance of the DataController",
|
|
496
|
+
);
|
|
497
|
+
});
|
|
498
|
+
});
|