@media-quest/builder 0.0.4 → 0.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@media-quest/builder",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Builder library for Media-quest schemas",
5
5
  "main": "dist/public-api.js",
6
6
  "module": "dist/public-api.mjs",
@@ -9,10 +9,10 @@
9
9
  "scripts": {
10
10
  "check": "tsc --watch --noEmit",
11
11
  "clean": "rimraf dist",
12
- "build": "npm run clean && tsup src/public-api.ts --format cjs,esm --dts",
12
+ "build": "npm run clean && tsup src/public-api.ts --sourcemap inline --format cjs,esm --dts",
13
13
  "prepublishOnly": "npm run build"
14
14
  },
15
15
  "dependencies": {
16
- "@media-quest/engine": "0.0.4"
16
+ "@media-quest/engine": "0.0.6"
17
17
  }
18
18
  }
@@ -192,17 +192,16 @@ export class BuilderSchema {
192
192
  this.pages.forEach((page, index) => {
193
193
  const pageVariables = page.getQuestionVariables(prefix, index);
194
194
  qVars.push(...pageVariables);
195
- const pageId = page.prefix;
196
195
  const mainText = page.mainText.text;
197
196
  const jumpAction: JumpToPageAction = {
198
197
  kind: "jump-to-page",
199
- pageId: pageId,
198
+ pageId: page.id,
200
199
  pageNumber: index,
201
200
  mainText: page.mainText.text,
202
201
  };
203
202
  const excludePageAction: ExcludeByPageAction = {
204
203
  kind: "exclude-by-pageId",
205
- pageId,
204
+ pageId: page.id,
206
205
  pageNumber: index,
207
206
  mainText,
208
207
  };
@@ -9,10 +9,7 @@ import { BuilderObject } from "../BuilderObject";
9
9
  import { TagActionManager } from "./tag-action-manager";
10
10
  import { PageActionManager } from "./page-action-manager";
11
11
  import { JumpToActionManager } from "./jump-to-action-manager";
12
- import { Condition, DUtil, PageQueCommand, PageQueRules, Rule } from "@media-quest/engine";
13
- import { ExcludeByPageAction, ExcludeByTagAction, JumpToPageAction } from "./RuleAction";
14
- import { RuleBuilderTestUtils } from "./RuleBuilder-test-utils";
15
- import jumpToPageAction = RuleBuilderTestUtils.jumpToPageAction;
12
+ import { Condition, PageQueCommand, PageQueRules } from "@media-quest/engine";
16
13
 
17
14
  export interface BuilderRuleDto {
18
15
  readonly type: ConditionGroupType;
@@ -135,18 +132,18 @@ export class BuilderRule extends BuilderObject<"builder-rule", BuilderRuleDto> {
135
132
  };
136
133
  return dto;
137
134
  }
138
- toEngineRule(): PageQueRules {
135
+ toEngineRule(modulePrefix: string): PageQueRules {
139
136
  const conditions: Condition[] = [];
140
137
  this._conditions.forEach((c) => {
141
138
  if (c) {
142
139
  if (c instanceof BuilderCondition) {
143
- const simpleCondition = c.toEngineCondition();
140
+ const simpleCondition = c.toEngineCondition(modulePrefix);
144
141
  if (simpleCondition) {
145
142
  conditions.push(simpleCondition);
146
143
  }
147
144
  }
148
145
  if (c instanceof BuilderConditionGroup) {
149
- const complexCondition = c.toEngineConditionComplex();
146
+ const complexCondition = c.toEngineConditionComplex(modulePrefix);
150
147
  if (complexCondition) conditions.push(complexCondition);
151
148
  }
152
149
  }
@@ -190,9 +187,7 @@ export class BuilderRule extends BuilderObject<"builder-rule", BuilderRuleDto> {
190
187
  pageQueCommands.push(excludeTagsCommand);
191
188
  }
192
189
 
193
- const id = DUtil.randomObjectId();
194
190
  const rule: PageQueRules = {
195
- id: "",
196
191
  description: this.name,
197
192
  all,
198
193
  some,
@@ -7,13 +7,13 @@ const ConditionGroupType = {
7
7
  all: true,
8
8
  any: true,
9
9
  count: true,
10
- range: true,
11
10
  };
12
11
 
13
12
  export type ConditionGroupType = keyof typeof ConditionGroupType;
14
13
  export interface BuilderConditionGroupDto {
15
14
  readonly kind: "condition-group";
16
15
  readonly name: string;
16
+ readonly count?: number;
17
17
  readonly type: ConditionGroupType;
18
18
  readonly conditions: ReadonlyArray<BuilderConditionDto>;
19
19
  }
@@ -82,7 +82,7 @@ export class BuilderConditionGroup extends BuilderObject<"builder-condition-grou
82
82
  };
83
83
  }
84
84
 
85
- toEngineConditionComplex(): Condition.Complex | false {
85
+ toEngineConditionComplex(modulePrefix: string): Condition.Complex | false {
86
86
  const comp: Condition.Complex = {
87
87
  kind: "complex-condition",
88
88
  all: [],
@@ -91,7 +91,7 @@ export class BuilderConditionGroup extends BuilderObject<"builder-condition-grou
91
91
  };
92
92
  const conditions: Condition.Simple[] = [];
93
93
  this.conditions.forEach((c) => {
94
- const maybeSimple = c.toEngineCondition();
94
+ const maybeSimple = c.toEngineCondition(modulePrefix);
95
95
  if (maybeSimple) {
96
96
  conditions.push(maybeSimple);
97
97
  }
@@ -103,7 +103,7 @@ export class BuilderCondition extends BuilderObject<"builder-condition", Builder
103
103
  return { isValid: true };
104
104
  }
105
105
 
106
- toEngineCondition(): Condition.Simple | false {
106
+ toEngineCondition(modulePrefix: string): Condition.Simple | false {
107
107
  const val = this.value;
108
108
  const op = this._operator;
109
109
  const v = this._variable;
@@ -115,7 +115,7 @@ export class BuilderCondition extends BuilderObject<"builder-condition", Builder
115
115
  kind: "numeric-condition",
116
116
  value: val.value,
117
117
  valueLabel: val.label,
118
- referenceId: v.varId,
118
+ referenceId: modulePrefix + "_" + v.varId,
119
119
  referenceLabel: v.label,
120
120
  operator: "eq",
121
121
  };
@@ -16,14 +16,14 @@ const BuilderOperatorSymbols = {
16
16
  empty: true,
17
17
  notEmpty: true,
18
18
  startsWith: true,
19
- endsWith: true
19
+ endsWith: true,
20
20
  } as const;
21
21
 
22
22
  export type BuilderOperator = keyof typeof BuilderOperatorSymbols;
23
23
 
24
24
  export namespace BuilderOperator {
25
- export const is = (symbol: string): symbol is BuilderOperator => {
26
- if (typeof symbol !== 'string') {
25
+ export const is = (symbol?: string): symbol is BuilderOperator => {
26
+ if (typeof symbol !== "string") {
27
27
  return false;
28
28
  }
29
29
  return Object.keys(BuilderOperatorSymbols).includes(symbol);
@@ -0,0 +1,211 @@
1
+ import { BuilderOperator } from "../condition/Builder-operator";
2
+ import { BuilderConditionDto } from "../condition/Builder-condition";
3
+ import { BuilderConditionGroupDto, ConditionGroupType } from "../condition/Builder-condition-group";
4
+ import { BuilderRuleDto } from "../Builder-rule";
5
+
6
+ type SolveErrorReason =
7
+ | "INVALID_FACT_TYPE"
8
+ | "INVALID_OPERATOR"
9
+ | "INVALID_VALUE"
10
+ | "INVALID_VARIABLE"
11
+ | "UNIMPLEMENTED_VARIABLE_TYPE"
12
+ | "UNIMPLEMENTED_OPERATOR";
13
+
14
+ type TrueResult = { readonly type: "IS_TRUE" };
15
+ type FalseResult = { readonly type: "IS_FALSE" };
16
+ type MissingFactsResult = { readonly type: "MISSING_FACTS"; readonly missingVariables: ReadonlyArray<string> };
17
+ type ErrorResult = {
18
+ readonly type: "HAS_ERROR";
19
+ readonly reason: SolveErrorReason;
20
+ readonly data: Record<string, string>;
21
+ };
22
+ export type EvalResult = FalseResult | TrueResult | MissingFactsResult | ErrorResult;
23
+
24
+ export interface Fact2 {
25
+ readonly variableType: "numeric";
26
+ readonly value: number;
27
+ readonly valueLabel: string;
28
+ readonly variableId: string;
29
+ readonly variableLabel: string;
30
+ }
31
+
32
+ export class FactCollection {
33
+ public static isFact = (value: unknown): value is Fact2 => {
34
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
35
+ return false;
36
+ }
37
+ const fact = value as Partial<Fact2>;
38
+ if (typeof fact.variableId !== "string" || fact.variableId.length === 0) {
39
+ return false;
40
+ }
41
+ if (typeof fact.variableLabel !== "string") {
42
+ return false;
43
+ }
44
+ if (typeof fact.value !== "number") {
45
+ return false;
46
+ }
47
+ if (typeof fact.valueLabel !== "string") {
48
+ return false;
49
+ }
50
+ // NB: This is a temporary check until we have more variable types.
51
+ if (typeof fact.variableType !== "string" || fact.variableType !== "numeric") {
52
+ return false;
53
+ }
54
+
55
+ return true;
56
+ };
57
+ public static create = (facts: ReadonlyArray<Fact2>): FactCollection => {
58
+ return new FactCollection(facts);
59
+ };
60
+
61
+ private readonly _facts: ReadonlyArray<Fact2>;
62
+
63
+ private constructor(facts: ReadonlyArray<Fact2>) {
64
+ if (!Array.isArray(facts)) {
65
+ console.log("Invalid facts", facts);
66
+ this._facts = [];
67
+ } else {
68
+ this._facts = [...facts];
69
+ }
70
+ }
71
+ byId(variableId: string): Fact2 | false {
72
+ const result = this._facts.find((fact) => fact.variableId === variableId);
73
+ if (!result) {
74
+ return false;
75
+ }
76
+ return { ...result };
77
+ }
78
+ }
79
+ interface FactEvaluator {
80
+ isTrue(facts: FactCollection): boolean;
81
+ evaluate(facts: FactCollection): EvalResult;
82
+ }
83
+ interface IsValid {
84
+ isValid(): boolean;
85
+ }
86
+ export class Condition implements FactEvaluator, IsValid {
87
+ public static create = (dto: BuilderConditionDto): Condition => {
88
+ return new Condition(dto);
89
+ };
90
+ private constructor(private readonly dto: BuilderConditionDto) {}
91
+ isTrue(facts: FactCollection): boolean {
92
+ const dto = this.dto;
93
+ const op = dto.operator;
94
+ const value = dto.value;
95
+ const varId = dto.variableId;
96
+ if (!BuilderOperator.is(op)) {
97
+ return false;
98
+ }
99
+
100
+ if (typeof value !== "number") {
101
+ return false;
102
+ }
103
+
104
+ const fact = facts.byId(this.dto.variableId);
105
+
106
+ if (!FactCollection.isFact(fact)) {
107
+ return false;
108
+ }
109
+
110
+ if (fact.variableType !== "numeric" && typeof fact.value !== "number") {
111
+ return false;
112
+ }
113
+
114
+ if (op === "equal") {
115
+ return fact.value === value;
116
+ }
117
+
118
+ if (op === "notEqual") {
119
+ return fact.value !== value;
120
+ }
121
+
122
+ return false;
123
+ }
124
+ isValid(): boolean {
125
+ return false;
126
+ }
127
+
128
+ evaluate(facts: FactCollection): EvalResult {
129
+ return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
130
+ }
131
+ }
132
+ export class ConditionGroup implements FactEvaluator, IsValid {
133
+ public static readonly create = (dto: BuilderConditionGroupDto): ConditionGroup => {
134
+ return new ConditionGroup(dto);
135
+ };
136
+
137
+ private readonly _conditions: ReadonlyArray<Condition>;
138
+ private constructor(private readonly dto: BuilderConditionGroupDto) {
139
+ this._conditions = dto.conditions.map(Condition.create);
140
+ }
141
+
142
+ isTrue(facts: FactCollection): boolean {
143
+ const results = this._conditions.map((condition) => condition.isTrue(facts));
144
+ let trueCount = 0;
145
+ let falseCount = 0;
146
+ results.forEach((results) => {
147
+ if (results) {
148
+ trueCount++;
149
+ } else {
150
+ falseCount++;
151
+ }
152
+ });
153
+ if (trueCount === 0 || falseCount === 0) {
154
+ return false;
155
+ }
156
+ const type = this.dto.type;
157
+
158
+ if (type === "all" && trueCount === this._conditions.length) {
159
+ return true;
160
+ }
161
+
162
+ if (type === "any" && trueCount > 0) {
163
+ return true;
164
+ }
165
+
166
+ const minLimit = this.dto.count;
167
+ if (type === "count" && typeof minLimit === "number" && trueCount >= minLimit) {
168
+ return true;
169
+ }
170
+
171
+ return false;
172
+ }
173
+
174
+ isValid(): boolean {
175
+ return true;
176
+ }
177
+
178
+ evaluate(facts: FactCollection): EvalResult {
179
+ return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
180
+ }
181
+ }
182
+ export class Rule2 implements FactEvaluator {
183
+ readonly name: string;
184
+ private readonly _conditions: ReadonlyArray<Condition | ConditionGroup>;
185
+ public static readonly create = (dto: BuilderRuleDto): Rule2 => {
186
+ return new Rule2(dto);
187
+ };
188
+ private _count = -1;
189
+ constructor(private readonly dto: BuilderRuleDto) {
190
+ this.name = dto.name;
191
+ const conditions: Array<Condition | ConditionGroup> = [];
192
+ dto.conditions.forEach((condition) => {
193
+ if (condition.kind === "condition-group") {
194
+ conditions.push(ConditionGroup.create(condition));
195
+ } else if (condition.kind === "condition") {
196
+ conditions.push(Condition.create(condition));
197
+ } else {
198
+ console.log("Unknown condition", condition);
199
+ }
200
+ });
201
+ this._conditions = conditions;
202
+ }
203
+
204
+ isTrue(facts: FactCollection): boolean {
205
+ return false;
206
+ }
207
+
208
+ evaluate(facts: FactCollection): EvalResult {
209
+ return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
210
+ }
211
+ }
@@ -1,5 +1,6 @@
1
1
  import { AbstractThemeCompiler } from "./AbstractThemeCompiler";
2
2
  import type { BuilderSchemaDto } from "../Builder-schema";
3
+ import { BuilderSchema } from "../Builder-schema";
3
4
  import type { BuilderPageDto } from "../Builder-page";
4
5
  import { DStateProps } from "./standard-props";
5
6
  import { ThemeUtils } from "./theme-utils";
@@ -19,12 +20,10 @@ import {
19
20
  Fact,
20
21
  PageDto,
21
22
  PageQueCommand,
22
- PageQueRules,
23
23
  Rule,
24
24
  SchemaDto,
25
25
  } from "@media-quest/engine";
26
26
  import { AudioFile } from "../media-files";
27
- import { BuilderSchema } from "../Builder-schema";
28
27
  import { BuilderRule } from "../rulebuilder";
29
28
 
30
29
  const U = DUtil;
@@ -41,7 +40,7 @@ export class DefaultThemeCompiler extends AbstractThemeCompiler<IDefaultTheme> {
41
40
  const ruleInput = builderSchema.getRuleInput();
42
41
  const pageQueRules: Rule<PageQueCommand, never>[] = [];
43
42
  source.rules.forEach((rule) => {
44
- const engineRule = BuilderRule.fromDto(rule, ruleInput).toEngineRule();
43
+ const engineRule = BuilderRule.fromDto(rule, ruleInput).toEngineRule(source.prefix);
45
44
  if (!Rule.isEmpty(engineRule)) {
46
45
  pageQueRules.push(engineRule);
47
46
  } else {
@@ -55,8 +54,10 @@ export class DefaultThemeCompiler extends AbstractThemeCompiler<IDefaultTheme> {
55
54
  }
56
55
 
57
56
  compile(source: BuilderSchemaDto): SchemaDto {
58
- const pages = source.pages.map((p) => this.compilePage(p, source.id));
57
+ const pages = source.pages.map((p) => this.compilePage(p, source.prefix));
59
58
  const rules = this.compileRules(source);
59
+ console.log(pages.map((p) => p.tags));
60
+
60
61
  const dto: SchemaDto = {
61
62
  backgroundColor: source.backgroundColor,
62
63
  baseHeight: source.baseHeight,
@@ -117,9 +118,10 @@ export class DefaultThemeCompiler extends AbstractThemeCompiler<IDefaultTheme> {
117
118
  };
118
119
  return dto;
119
120
  }
120
- private compilePage(page: BuilderPageDto, _moduleId: string): PageDto {
121
- // console.log(moduleId);
121
+ private compilePage(page: BuilderPageDto, modulePrefix: string): PageDto {
122
+ // console.log(_moduleId);
122
123
  // const textElement
124
+ const tags = page.tags ?? [];
123
125
  const { nextButton, mainText, id, mainMedia, _type } = page;
124
126
  const elements: DElementDto[] = [];
125
127
  const audioResourcesDto: DAudioDto[] = [];
@@ -135,7 +137,8 @@ export class DefaultThemeCompiler extends AbstractThemeCompiler<IDefaultTheme> {
135
137
  }
136
138
 
137
139
  if (_type === "question") {
138
- const { buttons, question } = this.compileQuestion(id, page);
140
+ const variableId = modulePrefix + "_" + page.prefix;
141
+ const { buttons, question } = this.compileQuestion(id, page, variableId);
139
142
  // console.log(question);
140
143
  elements.push(...buttons, question);
141
144
  }
@@ -200,7 +203,7 @@ export class DefaultThemeCompiler extends AbstractThemeCompiler<IDefaultTheme> {
200
203
  elements,
201
204
  id,
202
205
  mainVideoId,
203
- tags: [],
206
+ tags: [...tags],
204
207
  video: videoResources,
205
208
  };
206
209
  return pageDto;
@@ -331,6 +334,7 @@ export class DefaultThemeCompiler extends AbstractThemeCompiler<IDefaultTheme> {
331
334
  private compileQuestion(
332
335
  pageId: string,
333
336
  page: BuilderPageDto,
337
+ variableId: string,
334
338
  ): {
335
339
  question: DTextDto;
336
340
  buttons: DDivDto[];
@@ -352,7 +356,7 @@ export class DefaultThemeCompiler extends AbstractThemeCompiler<IDefaultTheme> {
352
356
  const buttons = q.options.map((o) => {
353
357
  const btns = this.compileButton(pageId, o, {
354
358
  kind: "response-button",
355
- questionId: "",
359
+ questionId: variableId,
356
360
  });
357
361
  return btns;
358
362
  });
package/tsconfig.json CHANGED
@@ -9,6 +9,6 @@
9
9
  "skipLibCheck": true,
10
10
  "forceConsistentCasingInFileNames": true,
11
11
  "rootDir": "./src",
12
- "outDir": "./dist",
13
- }
12
+ "outDir": "./dist"
13
+ },
14
14
  }
@@ -1,87 +0,0 @@
1
- import { BuilderVariableType } from "./RuleVariable";
2
- import { BuilderOperator } from "./condition/Builder-operator";
3
- import { BuilderConditionDto } from "./condition/Builder-condition";
4
-
5
- type ConditionType = "all" | "any" | "";
6
-
7
- type SolveResultType = "TRUE" | "FALSE" | "NOT_ENOUGH_DATA" | "ERROR";
8
-
9
- type SolveErrorReason =
10
- | "INVALID_FACT_TYPE"
11
- | "INVALID_OPERATOR"
12
- | "INVALID_VALUE"
13
- | "INVALID_VARIABLE"
14
- | "UNIMPLEMENTED_VARIABLE_TYPE"
15
- | "UNIMPLEMENTED_OPERATOR";
16
-
17
- type IsTrue = { readonly type: "IS_TRUE" };
18
- type IsFalse = { readonly type: "IS_FALSE" };
19
- type IsMissingFacts = { readonly type: "MISSING_FACTS"; readonly missingVariables: ReadonlyArray<string> };
20
- type HasError = {
21
- readonly type: "HAS_ERROR";
22
- readonly reason: SolveErrorReason;
23
- readonly data: Record<string, string>;
24
- };
25
- export type SolveResult = IsFalse | IsTrue | IsMissingFacts | HasError;
26
-
27
- export interface Fact2 {
28
- readonly variableType: "numeric";
29
- readonly value: number;
30
- readonly valueLabel: string;
31
- readonly variableId: string;
32
- readonly variableLabel: string;
33
- }
34
-
35
- class FactCollection {
36
- constructor(private readonly facts: ReadonlyArray<Fact2>) {}
37
- byId(variableId: string): Fact2 | false {
38
- const result = this.facts.find((fact) => fact.variableId === variableId);
39
- if (!result) {
40
- return false;
41
- }
42
- return result;
43
- }
44
- }
45
- interface Solvable {
46
- isTrue(facts: FactCollection): boolean;
47
- solve(facts: FactCollection): SolveResult;
48
- }
49
- interface IsValid {
50
- isValid(): boolean;
51
- }
52
- export class Condition implements Solvable, IsValid {
53
- constructor(private readonly dto: BuilderConditionDto) {}
54
- isTrue(facts: FactCollection): boolean {
55
- const op = this.dto.operator;
56
- if (!BuilderOperator.is(op)) {
57
- return false;
58
- }
59
-
60
- const fact = facts.byId(this.dto.variableId);
61
-
62
- if (!fact) {
63
- return false;
64
- }
65
-
66
- if (fact.variableType !== "numeric" && typeof fact.value !== "number") {
67
- return false;
68
- }
69
-
70
- return false;
71
- }
72
- isValid(): boolean {
73
- return false;
74
- }
75
-
76
- solve(facts: FactCollection): SolveResult {
77
- return { type: "HAS_ERROR", reason: "UNIMPLEMENTED_VARIABLE_TYPE", data: {} }; // TODO
78
- }
79
- }
80
- export class ConditionGroup {}
81
- export class Rule2 {
82
- private _count = -1;
83
- constructor(
84
- private readonly dto: any,
85
- private readonly _ruleInput: any,
86
- ) {}
87
- }