@media-quest/engine 0.0.3 → 0.0.4
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/public-api.d.mts +1 -1
- package/dist/public-api.d.ts +1 -1
- package/dist/public-api.js +1 -1
- package/dist/public-api.mjs +1 -1
- package/package.json +1 -1
- package/src/commands/DCommandBus.ts +46 -46
- package/src/rules/rule-engine.ts +29 -30
- package/src/rules/rule.ts +25 -25
package/dist/public-api.d.mts
CHANGED
|
@@ -380,7 +380,7 @@ interface DAudioDto {
|
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
interface Rule<OnSuccessAction, OnFailureAction> {
|
|
383
|
-
readonly id
|
|
383
|
+
readonly id?: string;
|
|
384
384
|
readonly description: string;
|
|
385
385
|
readonly all: ReadonlyArray<Condition>;
|
|
386
386
|
readonly some: ReadonlyArray<Condition>;
|
package/dist/public-api.d.ts
CHANGED
|
@@ -380,7 +380,7 @@ interface DAudioDto {
|
|
|
380
380
|
}
|
|
381
381
|
|
|
382
382
|
interface Rule<OnSuccessAction, OnFailureAction> {
|
|
383
|
-
readonly id
|
|
383
|
+
readonly id?: string;
|
|
384
384
|
readonly description: string;
|
|
385
385
|
readonly all: ReadonlyArray<Condition>;
|
|
386
386
|
readonly some: ReadonlyArray<Condition>;
|
package/dist/public-api.js
CHANGED
|
@@ -1032,7 +1032,7 @@ var RuleEngine = class {
|
|
|
1032
1032
|
} else if (Rule.solve(rule, facts)) {
|
|
1033
1033
|
const match = {
|
|
1034
1034
|
ruleDescription: rule.description,
|
|
1035
|
-
matchingRuleId: rule.id,
|
|
1035
|
+
matchingRuleId: rule.id ?? "no-id-given",
|
|
1036
1036
|
actionList: [...rule.onSuccess]
|
|
1037
1037
|
};
|
|
1038
1038
|
matching.push(match);
|
package/dist/public-api.mjs
CHANGED
|
@@ -996,7 +996,7 @@ var RuleEngine = class {
|
|
|
996
996
|
} else if (Rule.solve(rule, facts)) {
|
|
997
997
|
const match = {
|
|
998
998
|
ruleDescription: rule.description,
|
|
999
|
-
matchingRuleId: rule.id,
|
|
999
|
+
matchingRuleId: rule.id ?? "no-id-given",
|
|
1000
1000
|
actionList: [...rule.onSuccess]
|
|
1001
1001
|
};
|
|
1002
1002
|
matching.push(match);
|
package/package.json
CHANGED
|
@@ -2,59 +2,59 @@ import { DCommand } from "./DCommand";
|
|
|
2
2
|
import { DTimestamp } from "../common/DTimestamp";
|
|
3
3
|
|
|
4
4
|
export interface DCommandStore {
|
|
5
|
-
|
|
5
|
+
subscribe(callback: () => void, subscriberId: string): void;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export interface DCommandDispatcher {
|
|
9
|
-
|
|
9
|
+
emit(command: DCommand): void;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
interface CommandSubscriberData {
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
readonly subscriberId: string;
|
|
14
|
+
readonly callback: (command: DCommand) => void;
|
|
15
15
|
}
|
|
16
16
|
export class DCommandBus implements DCommandStore, DCommandDispatcher {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
this.subscribers.forEach((subscriber) => {
|
|
42
|
-
subscriber.callback(command);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
getStats() {
|
|
47
|
-
return {
|
|
48
|
-
commands: [...this.commandLog],
|
|
49
|
-
subscribers: [...this.subscribers],
|
|
50
|
-
subscribersCount: this.subscribers.size,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
private logCommand(command: DCommand) {
|
|
55
|
-
console.groupCollapsed(this.TAG + " " + command.kind);
|
|
56
|
-
console.log("TargetID : " + command.targetId);
|
|
57
|
-
console.log(command.payload);
|
|
58
|
-
console.groupEnd();
|
|
17
|
+
private readonly TAG = "[ COMMAND_BUS ] ";
|
|
18
|
+
logCommands = false;
|
|
19
|
+
private readonly commandLog: Array<DCommand & { timestamp: DTimestamp }> = [];
|
|
20
|
+
readonly subscribers = new Set<CommandSubscriberData>();
|
|
21
|
+
// readonly sub
|
|
22
|
+
|
|
23
|
+
subscribe(cb: (command: DCommand) => void, subscriberId: string) {
|
|
24
|
+
const sub: CommandSubscriberData = {
|
|
25
|
+
subscriberId,
|
|
26
|
+
callback: cb,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
this.subscribers.add(sub);
|
|
30
|
+
return () => {
|
|
31
|
+
this.subscribers.delete(sub);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
emit(command: DCommand) {
|
|
36
|
+
const timestamp = DTimestamp.now();
|
|
37
|
+
this.commandLog.push({ ...command, timestamp });
|
|
38
|
+
if (this.logCommands) {
|
|
39
|
+
this.logCommand(command);
|
|
59
40
|
}
|
|
41
|
+
this.subscribers.forEach((subscriber) => {
|
|
42
|
+
subscriber.callback(command);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getStats() {
|
|
47
|
+
return {
|
|
48
|
+
commands: [...this.commandLog],
|
|
49
|
+
subscribers: [...this.subscribers],
|
|
50
|
+
subscribersCount: this.subscribers.size,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private logCommand(command: DCommand) {
|
|
55
|
+
console.groupCollapsed(this.TAG + " " + command.kind);
|
|
56
|
+
console.log("TargetID : " + command.targetId);
|
|
57
|
+
console.log(command.payload);
|
|
58
|
+
console.groupEnd();
|
|
59
|
+
}
|
|
60
60
|
}
|
package/src/rules/rule-engine.ts
CHANGED
|
@@ -1,46 +1,45 @@
|
|
|
1
1
|
import { Fact } from "./fact";
|
|
2
2
|
import { Rule } from "./rule";
|
|
3
|
-
import { PageQueCommand } from "../commands/DCommand";
|
|
4
3
|
|
|
5
4
|
export interface SolveResult<S, F> {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
matching: ReadonlyArray<Match<S, F>>;
|
|
6
|
+
errors: ReadonlyArray<RuleEngineError>;
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
export interface Match<S, F> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
readonly matchingRuleId: string;
|
|
11
|
+
readonly ruleDescription: string;
|
|
12
|
+
readonly actionList: ReadonlyArray<S> | ReadonlyArray<F>;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
export interface RuleEngineError {
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
readonly kind?: string;
|
|
17
|
+
readonly message: string;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
export class RuleEngine<S, F> {
|
|
22
|
-
|
|
21
|
+
constructor() {}
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
23
|
+
solveAll(rules: Rule<S, F>[], facts: Fact[]): SolveResult<S, F> {
|
|
24
|
+
const errors: RuleEngineError[] = [];
|
|
25
|
+
const matching: Match<S, F>[] = [];
|
|
26
|
+
rules.forEach((rule) => {
|
|
27
|
+
if (Rule.isEmpty(rule)) {
|
|
28
|
+
errors.push({ message: "Empty rule: " + rule.id });
|
|
29
|
+
} else if (Rule.solve(rule, facts)) {
|
|
30
|
+
const match: Match<S, F> = {
|
|
31
|
+
ruleDescription: rule.description,
|
|
32
|
+
matchingRuleId: rule.id ?? "no-id-given",
|
|
33
|
+
actionList: [...rule.onSuccess],
|
|
34
|
+
};
|
|
35
|
+
matching.push(match);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return { matching, errors };
|
|
39
|
+
}
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
solve(rule: Rule<S, F>, facts: Fact[]): boolean {
|
|
42
|
+
// TODO Validate, and Return result
|
|
43
|
+
return Rule.solve(rule, facts);
|
|
44
|
+
}
|
|
46
45
|
}
|
package/src/rules/rule.ts
CHANGED
|
@@ -3,38 +3,38 @@ import { Fact } from "./fact";
|
|
|
3
3
|
import { DUtil } from "../utils/DUtil";
|
|
4
4
|
|
|
5
5
|
export interface Rule<OnSuccessAction, OnFailureAction> {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
readonly id?: string;
|
|
7
|
+
readonly description: string;
|
|
8
|
+
readonly all: ReadonlyArray<Condition>;
|
|
9
|
+
readonly some: ReadonlyArray<Condition>;
|
|
10
|
+
readonly onSuccess: ReadonlyArray<OnSuccessAction>;
|
|
11
|
+
readonly onFailure: ReadonlyArray<OnFailureAction>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export namespace Rule {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Validates that the rule is valid.
|
|
17
|
+
* @param rule
|
|
18
|
+
*/
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
export const isEmpty = (rule: Rule<any, any>): boolean => {
|
|
21
|
+
const emptyConditions = rule.all.length === 0 && rule.some.length === 0;
|
|
22
|
+
const emptyActions = rule.onSuccess.length === 0 && rule.onFailure.length === 0;
|
|
23
|
+
return emptyConditions || emptyActions;
|
|
24
|
+
};
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
export const solve = (rule: Rule<any, any>, facts: ReadonlyArray<Fact>): boolean => {
|
|
27
|
+
if (rule.some.length === 0 && rule.all.length === 0) {
|
|
28
|
+
// TODO RETURN WARNING? OR LOGGING ?
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const someSolved = rule.some.map((condition) => Condition.evaluate(condition, facts));
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
const someResult = someSolved.length === 0 || someSolved.some(DUtil.isTrue);
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
const allSolved = rule.all.map((condition) => Condition.evaluate(condition, facts)).every(DUtil.isTrue);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
return allSolved && someResult;
|
|
39
|
+
};
|
|
40
40
|
}
|