@media-quest/engine 0.0.1 → 0.0.3

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 (65) hide show
  1. package/dist/public-api.d.mts +543 -0
  2. package/dist/public-api.d.ts +543 -0
  3. package/dist/public-api.js +2187 -0
  4. package/dist/public-api.mjs +2150 -0
  5. package/package.json +10 -3
  6. package/src/Delement/AudioContainer.ts +169 -0
  7. package/src/Delement/DAuto-play.ts +36 -0
  8. package/src/Delement/DElement.ts +263 -0
  9. package/src/Delement/DImg.ts +78 -0
  10. package/src/Delement/DStyle-utils.ts +616 -0
  11. package/src/Delement/DStyle.ts +165 -0
  12. package/src/Delement/DText.ts +29 -0
  13. package/src/Delement/Ddiv.ts +38 -0
  14. package/src/Delement/VideoContainer.ts +199 -0
  15. package/src/Delement/css.spec.ts +36 -0
  16. package/src/Delement/css.ts +46 -0
  17. package/src/commands/DCommand.ts +62 -0
  18. package/src/commands/DCommandBus.ts +60 -0
  19. package/src/common/DMaybe.ts +46 -0
  20. package/src/common/DTimestamp.ts +20 -0
  21. package/src/common/DTmestamp.spec.ts +11 -0
  22. package/src/common/result.ts +41 -0
  23. package/src/dto/AnimationDto.ts +4 -0
  24. package/src/dto/DElement.dto.ts +50 -0
  25. package/src/dto/SchemaDto.ts +65 -0
  26. package/src/engine/DPage.ts +55 -0
  27. package/src/engine/SchemaEngine.ts +210 -0
  28. package/src/engine/element-factory.ts +52 -0
  29. package/src/engine/scale.spec.ts +38 -0
  30. package/src/engine/scale.ts +70 -0
  31. package/src/event-handlers/DEventHandler.ts +29 -0
  32. package/src/events/DEvents.ts +94 -0
  33. package/src/events/event-bus.spec.ts +21 -0
  34. package/src/events/event-bus.ts +81 -0
  35. package/src/kladd/context-menu-manager.ts +56 -0
  36. package/src/player/dplayer.spec.ts +108 -0
  37. package/src/player/dplayer.ts +70 -0
  38. package/src/player/history-que.spec.ts +45 -0
  39. package/src/player/history-que.ts +38 -0
  40. package/src/player/next-que.spec.ts +108 -0
  41. package/src/player/next-que.ts +93 -0
  42. package/src/public-api.ts +18 -5
  43. package/src/rules/__test__/complex-condition.spec.ts +15 -0
  44. package/src/rules/__test__/conditon.spec.ts +124 -0
  45. package/src/rules/__test__/numeric-condition.spec.ts +84 -0
  46. package/src/rules/__test__/rule-engine.spec.ts +354 -0
  47. package/src/rules/__test__/rule-evaluation.spec.ts +140 -0
  48. package/src/rules/__test__/string-condition.spec.ts +41 -0
  49. package/src/rules/condition.ts +191 -0
  50. package/src/rules/fact.ts +18 -0
  51. package/src/rules/rule-engine.ts +46 -0
  52. package/src/rules/rule.ts +40 -0
  53. package/src/services/DMedia-manager.spec.ts +27 -0
  54. package/src/services/DMedia-manager.ts +182 -0
  55. package/src/services/resource-provider.ts +33 -0
  56. package/src/services/sequence-manager.spec.ts +168 -0
  57. package/src/services/sequence-manager.ts +132 -0
  58. package/src/state/Dstate.spec.ts +7 -0
  59. package/src/state/Dstate.ts +105 -0
  60. package/src/state/boolean-property.ts +69 -0
  61. package/src/state/state-service.spec.ts +307 -0
  62. package/src/state/state-service.ts +251 -0
  63. package/src/state/state-testing-helpers.ts +59 -0
  64. package/src/utils/DUtil.ts +109 -0
  65. package/tsconfig.json +4 -3
@@ -0,0 +1,307 @@
1
+ import { DState } from "./Dstate";
2
+ import { Condition } from "../rules/condition";
3
+ import { Fact } from "../rules/fact";
4
+ import { Ok } from "../common/result";
5
+ import { StateService } from "./state-service";
6
+ import { _P, _Q } from "./state-testing-helpers";
7
+ import { EventBus } from "../events/event-bus";
8
+ import { DCommandBus } from "../commands/DCommandBus";
9
+ import { StateCommand } from "../commands/DCommand";
10
+
11
+ let eventBus = new EventBus();
12
+ let commandBus = new DCommandBus();
13
+ let stateContainer = new StateService(eventBus, commandBus, [], []);
14
+ const allVariables = Object.values(_P).map((p) => p.propDefinition);
15
+ const allDerived = Object.values(_Q);
16
+
17
+ const createCommand = (mutation: DState.StateMutation): StateCommand => {
18
+ return { kind: "STATE_MUTATE_COMMAND", target: "STATE", targetId: "STATE", payload: { mutation } };
19
+ };
20
+
21
+ describe("State-container", () => {
22
+ beforeEach(() => {
23
+ eventBus = new EventBus();
24
+ stateContainer = new StateService(eventBus, commandBus, allVariables, allDerived);
25
+ });
26
+
27
+ it("can add variables, and update", () => {
28
+ stateContainer = new StateService(eventBus, commandBus, [_P.propA.propDefinition]);
29
+
30
+ commandBus.emit(_P.propA.getSetFalseCommand());
31
+ const result = stateContainer.getPropAsFact(_P.propA.propName);
32
+ expect(result.isOk()).toBe(true);
33
+
34
+ // expect(result.errors.length).toBe(1);
35
+ });
36
+
37
+ it("can not update non-excisting variable", () => {
38
+ const p = _P.propB;
39
+ stateContainer = new StateService(eventBus, commandBus, [p.propDefinition]);
40
+ const invalidName = "invalid-variable-name";
41
+ commandBus.emit({
42
+ kind: "STATE_MUTATE_COMMAND",
43
+ target: "STATE",
44
+ targetId: "STATE",
45
+ payload: { mutation: { kind: "set-number", value: 1, propName: invalidName } },
46
+ });
47
+ const result0 = stateContainer.getPropAsFact(invalidName);
48
+ expect(result0.isOk()).toBe(false);
49
+ });
50
+ it("can solve condition, and initial value is false.: ", () => {
51
+ const p = _P.propD;
52
+ expect(stateContainer.isMatched(p.getIsTrueCondition())).toBe(false);
53
+ expect(stateContainer.isMatched(p.getIsFalseCondition())).toBe(true);
54
+ });
55
+
56
+ it("Solve will change after update: ", () => {
57
+ expect(stateContainer.isMatched(_P.propA.getIsFalseCondition())).toBe(true);
58
+ commandBus.emit(_P.propA.getSetTrueCommand());
59
+ expect(stateContainer.isMatched(_P.propA.getIsFalseCondition())).toBe(false);
60
+ });
61
+
62
+ it("Can also add audio-played count facts, and get results. ", () => {
63
+ const variable: DState.NumericProp = {
64
+ _type: "number",
65
+ propName: "audio-play-count-audio-1",
66
+ propDescription: "Number of times audio 1 has played through.",
67
+ };
68
+ stateContainer = new StateService(eventBus, commandBus, [variable]);
69
+ const condition: Condition = {
70
+ kind: "numeric-condition",
71
+ referenceId: variable.propName,
72
+ referenceLabel: variable.propDescription ?? "",
73
+ valueLabel: "Audio played at least 1 time.",
74
+ value: 0,
75
+ operator: "greater-then",
76
+ };
77
+
78
+ expect(stateContainer.isMatched(condition)).toBe(false);
79
+
80
+ const command = createCommand({
81
+ propName: variable.propName,
82
+ kind: "increment-number",
83
+ stepSize: 1,
84
+ ifNotExistThenSetTo: 1,
85
+ });
86
+ commandBus.emit(command);
87
+ // expect(res.success).toBe(true);
88
+ // expect(stateContainer.isMatched(condition)).toBe(true);
89
+ expect(stateContainer.isMatched(condition)).toBe(true);
90
+ });
91
+
92
+ it("Can create and update properties in state", () => {
93
+ const numProp1: DState.NumericProp = { _type: "number", propName: "numProp1", propDescription: "" };
94
+ const stringProp1: DState.StringProp = {
95
+ _type: "string",
96
+ propName: "stringProp1",
97
+ initialValue: "initial",
98
+ propDescription: "",
99
+ };
100
+ const stringProp2: DState.StringProp = { _type: "string", propName: "stringProp2", propDescription: "" };
101
+ const numericProp2: DState.NumericProp = {
102
+ _type: "number",
103
+ propName: "numProp2",
104
+ initialValue: 100,
105
+ propDescription: "",
106
+ };
107
+ stateContainer = new StateService(eventBus, commandBus, [numProp1, stringProp1, numericProp2, stringProp2]);
108
+
109
+ commandBus.emit(
110
+ createCommand({
111
+ propName: numProp1.propName,
112
+ kind: "increment-number",
113
+ stepSize: 1,
114
+ ifNotExistThenSetTo: 1,
115
+ })
116
+ );
117
+ const resultIntialString = stateContainer.getPropAsFact(stringProp1.propName) as Ok<Fact.String>;
118
+ const resultIntialStringEmpty = stateContainer.getPropAsFact(stringProp2.propName);
119
+
120
+ expect(resultIntialStringEmpty.isFailure()).toBe(true);
121
+ expect(resultIntialString.value.value).toBe(stringProp1.initialValue);
122
+ const result1 = stateContainer.getPropAsFact(numProp1.propName) as Ok<Fact.Numeric>;
123
+ expect(result1.value.value).toBe(1);
124
+ commandBus.emit(createCommand({ propName: numProp1.propName, kind: "set-number", value: 10 }));
125
+ const result10 = stateContainer.getPropAsFact(numProp1.propName) as Ok<Fact.Numeric>;
126
+ expect(result10.value.value).toBe(10);
127
+
128
+ // NUMPROP2
129
+ const numProp2InitialResult = stateContainer.getPropAsFact(numericProp2.propName) as Ok<Fact.Numeric>;
130
+ expect(numProp2InitialResult.value.value).toBe(100);
131
+ commandBus.emit(
132
+ createCommand({
133
+ propName: numericProp2.propName,
134
+ kind: "decrement-number",
135
+ stepSize: 10,
136
+ ifNotExistThenSetTo: 1,
137
+ })
138
+ );
139
+ const numProp2Res90 = stateContainer.getPropAsFact(numericProp2.propName) as Ok<Fact.Numeric>;
140
+ expect(numProp2Res90.value.value).toBe(90);
141
+
142
+ commandBus.emit(createCommand({ propName: stringProp1.propName, kind: "set-string", value: "updated-value" }));
143
+ const stringResUpdated = stateContainer.getPropAsFact(stringProp1.propName) as Ok<Fact.String>;
144
+ expect(stringResUpdated.value.value).toBe("updated-value");
145
+ });
146
+
147
+ it("Can have a counter variable in state. ", () => {
148
+ stateContainer = new StateService(eventBus, commandBus, [
149
+ { _type: "number", propDescription: "", propName: "page-count", initialValue: 0 },
150
+ ]);
151
+ const condition: Condition = {
152
+ kind: "numeric-condition",
153
+ referenceId: "page-count",
154
+ referenceLabel: "Number of pages seen today",
155
+ valueLabel: "Audio played at least 1 time.",
156
+ value: 10,
157
+ operator: "eq",
158
+ };
159
+
160
+ expect(stateContainer.isMatched(condition)).toBe(false);
161
+ commandBus.emit(
162
+ createCommand({
163
+ propName: condition.referenceId,
164
+ kind: "increment-number",
165
+ stepSize: 1,
166
+ ifNotExistThenSetTo: 1,
167
+ })
168
+ );
169
+ commandBus.emit(
170
+ createCommand({
171
+ propName: condition.referenceId,
172
+ kind: "increment-number",
173
+ stepSize: 5,
174
+ ifNotExistThenSetTo: 5,
175
+ })
176
+ );
177
+ commandBus.emit(
178
+ createCommand({
179
+ propName: condition.referenceId,
180
+ kind: "increment-number",
181
+ stepSize: 4,
182
+ ifNotExistThenSetTo: 4,
183
+ })
184
+ );
185
+ expect(stateContainer.isMatched(condition)).toBe(true);
186
+ });
187
+
188
+ it("Can get state-schema:", () => {
189
+ const s = stateContainer.getState();
190
+
191
+ expect(s.propCount).toBe(7);
192
+ expect(s.state[_P.propA.propName]).toBe(0);
193
+ expect(s.propArray.find((p) => p.propName === "not-defined")).toBe(undefined);
194
+ expect(s.propArray.find((p) => p.propName === _P.propB.propName)).toBeDefined();
195
+ });
196
+
197
+ it("Check that condition can be matched:", () => {
198
+ const simpleFn = (refId: string): Condition.Simple => {
199
+ if (Math.random() < 0.5) {
200
+ return {
201
+ kind: "string-condition",
202
+ referenceId: refId,
203
+ referenceLabel: "label for " + refId,
204
+ value: "dummy value",
205
+ operator: "not-eq",
206
+ valueLabel: "Is False",
207
+ };
208
+ } else {
209
+ return {
210
+ kind: "numeric-condition",
211
+ referenceId: refId,
212
+ referenceLabel: "Label for " + refId,
213
+ value: 0,
214
+ operator: "not-eq",
215
+ valueLabel: "Is False",
216
+ };
217
+ }
218
+ };
219
+
220
+ const complex = (all: Condition.Simple[], some: Condition.Simple[]): Condition.Complex => {
221
+ return { kind: "complex-condition", name: "", all, some };
222
+ };
223
+ const a = _P.propA;
224
+ const b = _P.propB;
225
+ const c = _P.propC;
226
+
227
+ expect(stateContainer.canBeMatched(simpleFn(a.propName))).toBe(true);
228
+ expect(stateContainer.canBeMatched(simpleFn("invalid"))).toBe(false);
229
+ expect(
230
+ stateContainer.canBeMatched(complex([simpleFn(a.propName), simpleFn(b.propName)], [simpleFn(c.propName)]))
231
+ ).toBe(true);
232
+ });
233
+
234
+ it("Can mutate state TODO!! Query", () => {
235
+ const { propA, propB, propC } = _P;
236
+ const s0 = stateContainer.getState();
237
+
238
+ expect(s0.propNames.length).toBe(7);
239
+ expect(s0.propCount).toBe(7);
240
+ expect(s0.propArray.length).toBe(6);
241
+
242
+ expect(s0.state[propA.propName]).toBe(0);
243
+ commandBus.emit(propA.getSetTrueCommand());
244
+ commandBus.emit(propB.getSetTrueCommand());
245
+ const s1 = stateContainer.getState();
246
+ expect(s1.state[propA.propName]).toBe(1);
247
+ commandBus.emit(propA.getSetFalseCommand());
248
+
249
+ commandBus.emit(propB.getSetFalseCommand());
250
+ const s2 = stateContainer.getState();
251
+ expect(s2.state[propA.propName]).toBe(0);
252
+ });
253
+
254
+ it("Will only emit query when changed changed event.", (done) => {
255
+ const { A_or_B_or_C_Query } = _Q;
256
+ let count = 0;
257
+ eventBus.subscribe((event) => {
258
+ if (event.kind === "STATE_QUERY_RESULT_CHANGED_EVENT") {
259
+ if (event.data.queryName === A_or_B_or_C_Query.name) {
260
+ count += 1;
261
+ if (count === 1) {
262
+ expect(event.data.curr).toBe(true);
263
+ }
264
+ if (count === 2) {
265
+ expect(event.data.curr).toBe(false);
266
+ done();
267
+ }
268
+ }
269
+ }
270
+ }, "Will only emit query when changed event.");
271
+ // 1 CHANGES QUERY
272
+ commandBus.emit(_P.propA.getSetTrueCommand());
273
+ // 2 NO CHANGE
274
+ commandBus.emit(_P.propA.getSetTrueCommand());
275
+ // 3 NO CHANGE
276
+ commandBus.emit(_P.propA.getSetTrueCommand());
277
+ // 4 CHANGES QUERY
278
+ commandBus.emit(_P.propA.getSetFalseCommand());
279
+ });
280
+
281
+ it("Can upsert props", (done) => {
282
+ const { A_or_B_or_C_Query } = _Q;
283
+ let count = 0;
284
+ eventBus.subscribe((event) => {
285
+ if (event.kind === "STATE_QUERY_RESULT_CHANGED_EVENT") {
286
+ if (event.data.queryName === A_or_B_or_C_Query.name) {
287
+ count += 1;
288
+ if (count === 1) {
289
+ expect(event.data.curr).toBe(true);
290
+ }
291
+ if (count === 2) {
292
+ expect(event.data.curr).toBe(false);
293
+ done();
294
+ }
295
+ }
296
+ }
297
+ }, "Can upsert props");
298
+ // 1 CHANGES QUERY
299
+ commandBus.emit(_P.propA.getSetTrueCommand());
300
+ // 2 NO CHANGE
301
+ commandBus.emit(_P.propA.getSetTrueCommand());
302
+ // 3 NO CHANGE
303
+ commandBus.emit(_P.propA.getSetTrueCommand());
304
+ // 4 CHANGES QUERY
305
+ commandBus.emit(_P.propA.getSetFalseCommand());
306
+ });
307
+ });
@@ -0,0 +1,251 @@
1
+ import { Fact } from "../rules/fact";
2
+ import { Result } from "../common/result";
3
+ import { Condition } from "../rules/condition";
4
+ import { DState } from "./Dstate";
5
+ import { DEventDispatcher } from "../events/event-bus";
6
+ import { DTimestamp } from "../common/DTimestamp";
7
+ import { QueryChangedEvent } from "../events/DEvents";
8
+ import { DCommandBus } from "../commands/DCommandBus";
9
+
10
+ export class StateService {
11
+ private readonly TAG = " [ STATE_SERVICE ] :";
12
+ private readonly factsString = new Map<string, Fact.String>();
13
+ private readonly factsNumeric = new Map<string, Fact.Numeric>();
14
+ private readonly propDefinitions = new Map<string, DState.Prop>();
15
+ private readonly queries = new Map<string, { query: DState.StateQuery; lastResult: boolean }>();
16
+ private readonly unsubscribeCommands: () => void;
17
+ constructor(
18
+ private readonly eventBus: DEventDispatcher,
19
+ private readonly commandBus: DCommandBus,
20
+ private readonly props: ReadonlyArray<DState.Prop>,
21
+ private readonly queryList: ReadonlyArray<DState.StateQuery> = [],
22
+ ) {
23
+ props.forEach((prop) => {
24
+ this.registerProperty(prop);
25
+ });
26
+ const facts = this.getAllFacts();
27
+ queryList.forEach((definition) => {
28
+ this.registerQuery(definition, facts);
29
+ });
30
+
31
+ this.evaluateQueries();
32
+ this.unsubscribeCommands = this.commandBus.subscribe((command) => {
33
+ if (command.kind === "STATE_MUTATE_COMMAND") {
34
+ this.mutation(command.payload.mutation);
35
+ }
36
+ }, this.TAG);
37
+ }
38
+
39
+ destroy() {
40
+ this.unsubscribeCommands();
41
+ }
42
+
43
+ private registerQuery(query: DState.StateQuery, currentFacts: ReadonlyArray<Fact>) {
44
+ const result = Condition.evaluate(query.condition, currentFacts);
45
+ this.queries.set(query.name, { query: query, lastResult: result });
46
+ this.emitQueryChangedEvent({ queryName: query.name, prev: result, curr: result });
47
+ }
48
+
49
+ private registerProperty(prop: DState.Prop) {
50
+ if (this.propDefinitions.has(prop.propName)) {
51
+ // LOGGING
52
+ console.warn("Prop excists already.", prop.propName);
53
+ }
54
+
55
+ this.propDefinitions.set(prop.propName, prop);
56
+
57
+ if (prop.initialValue === undefined) {
58
+ return;
59
+ }
60
+
61
+ if (prop._type === "string") {
62
+ const fact = DState.stringPropToFact(prop, prop.initialValue);
63
+ this.factsString.set(fact.referenceId, fact);
64
+ }
65
+ if (prop._type === "number") {
66
+ const fact = DState.numericPropToFact(prop, prop.initialValue);
67
+ this.factsNumeric.set(fact.referenceId, fact);
68
+ }
69
+ }
70
+
71
+ private mutateString(prop: DState.StringProp, value: string) {
72
+ const curr = this.factsString.get(prop.propName);
73
+ if (curr) {
74
+ const updated: Fact.String = { ...curr, value };
75
+ this.setFact(updated);
76
+ } else {
77
+ const created: Fact.String = {
78
+ kind: "string-fact",
79
+ referenceId: prop.propName,
80
+ referenceLabel: "label for: " + prop.propName,
81
+ value,
82
+ label: "Value: " + value,
83
+ };
84
+ this.setFact(created);
85
+ }
86
+ }
87
+
88
+ private mutateNumber(prop: DState.NumericProp, mutation: DState.NumberMutations) {
89
+ const curr = this.factsNumeric.get(prop.propName);
90
+ if (!curr) {
91
+ switch (mutation.kind) {
92
+ case "set-number":
93
+ this.setFact(DState.numericPropToFact(prop, mutation.value));
94
+ break;
95
+ case "decrement-number":
96
+ this.setFact(DState.numericPropToFact(prop, mutation.ifNotExistThenSetTo));
97
+ break;
98
+ case "increment-number":
99
+ this.setFact(DState.numericPropToFact(prop, mutation.ifNotExistThenSetTo));
100
+ break;
101
+ default:
102
+ const check: never = mutation;
103
+ }
104
+ }
105
+ if (curr) {
106
+ switch (mutation.kind) {
107
+ case "set-number":
108
+ this.setFact({ ...curr, value: mutation.value });
109
+ break;
110
+ case "decrement-number":
111
+ this.setFact({ ...curr, value: curr.value - mutation.stepSize });
112
+ break;
113
+ case "increment-number":
114
+ this.setFact({ ...curr, value: curr.value + mutation.stepSize });
115
+ break;
116
+ default:
117
+ const check: never = mutation;
118
+ }
119
+ }
120
+ }
121
+
122
+ private evaluateQueries(): ReadonlyArray<{ queryName: string; prev: boolean; curr: boolean; didChange: boolean }> {
123
+ const facts = this.getAllFacts();
124
+ const all: Array<{ queryName: string; prev: boolean; curr: boolean; didChange: boolean }> = [];
125
+ this.queries.forEach((q) => {
126
+ const prev = q.lastResult;
127
+
128
+ const curr = Condition.evaluate(q.query.condition, facts);
129
+ const didChange = prev !== curr;
130
+ q.lastResult = curr;
131
+ all.push({ queryName: q.query.name, prev, curr, didChange });
132
+ // def.value = value;
133
+ });
134
+ return all;
135
+ }
136
+
137
+ private mutation(mutation: DState.StateMutation): { success: boolean } {
138
+ const propDef = this.propDefinitions.get(mutation.propName);
139
+ if (!propDef) {
140
+ // TODO LOGGING
141
+ return { success: false };
142
+ }
143
+
144
+ if (propDef._type === "string" && DState.isStringMutation(mutation)) {
145
+ this.mutateString(propDef, mutation.value);
146
+ }
147
+
148
+ if (propDef._type === "number" && DState.isNumberMutation(mutation)) {
149
+ this.mutateNumber(propDef, mutation);
150
+ }
151
+ const queryResults = this.evaluateQueries();
152
+ const changedResult = queryResults.filter((r) => r.didChange);
153
+ changedResult.forEach((res) => {
154
+ this.emitQueryChangedEvent({ queryName: res.queryName, prev: res.prev, curr: res.curr });
155
+ });
156
+
157
+ return { success: true };
158
+ }
159
+
160
+ private emitQueryChangedEvent(data: { queryName: string; prev: boolean; curr: boolean }) {
161
+ const queryChangedEvent: QueryChangedEvent = {
162
+ kind: "STATE_QUERY_RESULT_CHANGED_EVENT",
163
+ producer: "STATE-SERVICE",
164
+ producerId: "STATE-SERVICE",
165
+ timestamp: DTimestamp.now(),
166
+ data,
167
+ };
168
+ const { queryName, curr, prev } = queryChangedEvent.data;
169
+ // console.log("[ QUERY_CHANGED " + queryName + "] : " + prev + " -> " + curr);
170
+ this.eventBus.emit(queryChangedEvent);
171
+ }
172
+
173
+ getPropAsFact(propName: string): Result<Fact> {
174
+ const propDef = this.propDefinitions.get(propName);
175
+ if (!propDef) {
176
+ return Result.failure(
177
+ "No definition form property ny name " + propName + " is registered. (Pass in constructor.)",
178
+ );
179
+ }
180
+ const value = this.factsString.get(propName) ?? this.factsNumeric.get(propName);
181
+
182
+ return value ? Result.ok(value) : Result.failure("Property " + propName + " has no value.");
183
+ }
184
+
185
+ private setFact(fact: Fact) {
186
+ switch (fact.kind) {
187
+ case "numeric-fact":
188
+ this.factsNumeric.set(fact.referenceId, fact);
189
+ break;
190
+ case "string-fact":
191
+ this.factsString.set(fact.referenceId, fact);
192
+ break;
193
+ default:
194
+ const check: never = fact;
195
+ }
196
+ }
197
+
198
+ getState() {
199
+ const props: Record<string, string | number | null | boolean> = {};
200
+ this.propDefinitions.forEach((def) => {
201
+ let value: string | number | null = null;
202
+ const maybeFact = this.getAnyFact(def.propName);
203
+ if (maybeFact) {
204
+ value = maybeFact.value;
205
+ }
206
+ props[def.propName] = value;
207
+ });
208
+ this.queries.forEach((query) => {
209
+ props[query.query.name] = query.lastResult;
210
+ });
211
+
212
+ const propNames = Object.keys(props);
213
+ const propCount = propNames.length;
214
+ const propArray = [...this.propDefinitions.values()];
215
+
216
+ return { propCount, propNames, propArray, state: props };
217
+ }
218
+
219
+ private getAnyFact(propName: string): Fact | false {
220
+ return this.factsString.get(propName) ?? this.factsNumeric.get(propName) ?? false;
221
+ }
222
+
223
+ private getAllFacts(): ReadonlyArray<Fact> {
224
+ return [...this.factsNumeric.values(), ...this.factsString.values()];
225
+ }
226
+
227
+ /**
228
+ * Will check that all referenceIds used in Condition is registered as a valid property in state.
229
+ * @param condition
230
+ */
231
+ canBeMatched(condition: Condition): boolean {
232
+ const simpleConditions = Condition.getAllSimpleConditions(condition);
233
+ let hasAll = true;
234
+
235
+ const allIds = simpleConditions.map((c) => c.referenceId);
236
+
237
+ const missingIds: string[] = [];
238
+ allIds.forEach((id) => {
239
+ if (!this.propDefinitions.has(id)) {
240
+ hasAll = false;
241
+ missingIds.push(id);
242
+ }
243
+ });
244
+ console.log(this.TAG, missingIds);
245
+
246
+ return hasAll;
247
+ }
248
+ isMatched(condition: Condition) {
249
+ return Condition.evaluate(condition, this.getAllFacts());
250
+ }
251
+ }
@@ -0,0 +1,59 @@
1
+ import { DState } from "./Dstate";
2
+ import { BooleanStateProperty } from "./boolean-property";
3
+
4
+ const propA = new BooleanStateProperty("a", false, "a-description");
5
+ const propB = new BooleanStateProperty("b", false, "b-description");
6
+ const propC = new BooleanStateProperty("c", false, "c-description");
7
+ const propD = new BooleanStateProperty("d", false, "d-description");
8
+ const propE = new BooleanStateProperty("e", false, "e-description");
9
+ const propF = new BooleanStateProperty("f", false, "f-description");
10
+
11
+ const A_or_B_or_C_Query: DState.StateQuery = {
12
+ name: "a_or_b_or_c",
13
+ condition: {
14
+ kind: "complex-condition",
15
+ name: "audio-controls-are-blocked",
16
+ some: [
17
+ {
18
+ kind: "numeric-condition",
19
+ referenceId: propA.propName,
20
+ operator: "eq",
21
+ value: 1,
22
+ valueLabel: "TRUE",
23
+ referenceLabel: propA.propName,
24
+ },
25
+ {
26
+ kind: "numeric-condition",
27
+ referenceId: propB.propName,
28
+ value: 1,
29
+ operator: "eq",
30
+ valueLabel: "TRUE",
31
+ referenceLabel: propB.propName,
32
+ },
33
+ {
34
+ kind: "numeric-condition",
35
+ referenceId: propC.propName,
36
+ value: 1,
37
+ operator: "eq",
38
+ valueLabel: "TRUE",
39
+ referenceLabel: propC.propName,
40
+ },
41
+ ],
42
+ all: [],
43
+ },
44
+ };
45
+ export const _Q = {
46
+ A_or_B_or_C_Query,
47
+ };
48
+
49
+ export const _P = {
50
+ propA,
51
+ propB,
52
+ propC,
53
+ propD,
54
+ propE,
55
+ propF,
56
+ } as const;
57
+
58
+ // export const DEFAULT_STATE_PROPS_LIST = Object.values(DEFAULT_STATE_PROPS);
59
+ // export const DEFAULT_STATE_DERIVED_LIST = Object.values(DERIVED_STATE);