dal-engine-core-js-lib-dev 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/dist/index.cjs +595 -197
- package/dist/index.esm.js +595 -197
- package/package.json +1 -1
- package/src/Base.js +10 -5
- package/src/BehavioralControlGraph/BehavioralControlGraph.js +76 -37
- package/src/BehavioralControlGraph/GraphNode.js +61 -25
- package/src/BehavioralControlGraph/Graphs.js +113 -11
- package/src/DALEngine.js +154 -53
- package/src/Errors/BehaviorAlreadyExistsError.js +10 -0
- package/src/Errors/GraphWithNameExistsError.js +10 -0
- package/src/Errors/ParticipantAlreadyExistsError.js +10 -0
- package/src/Errors/TransitionAlreadyExistsError.js +11 -0
- package/src/Errors/UnknownGraph.js +10 -0
- package/src/Errors/UnknownParticipantError.js +9 -0
- package/src/Members/Behavior.js +44 -22
- package/src/Members/Invariant.js +37 -22
- package/src/Members/Participant.js +28 -20
- package/src/TYPES.js +2 -3
- package/src/helpers/isLoadedFromFile.js +15 -0
- package/tests/DALEngine.test.js +19 -0
- package/tests/Graphs.test.js +80 -0
- package/tests/Invariant.test.js +1 -0
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import Base from "../Base";
|
|
2
2
|
import MissingAttributes from "../Errors/MissingAttributes";
|
|
3
|
+
import isLoadedFromFile from "../helpers/isLoadedFromFile";
|
|
3
4
|
import ENGINE_TYPES from "../TYPES";
|
|
4
5
|
import Invariant from "./Invariant";
|
|
5
6
|
|
|
6
|
-
/**
|
|
7
|
-
* Class representing a participant in the design.
|
|
8
|
-
*/
|
|
9
7
|
class Participant extends Base {
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Class representing a participant in the design. The participants are
|
|
10
|
+
* entites in deisgn that participate in the behavior. They are mapped onto
|
|
11
|
+
* the implementation and their value is loaded from the execution. The
|
|
12
|
+
* participants define a valid world state for the behavior to happen in
|
|
13
|
+
* through invariants. When the value of the participant violates an
|
|
14
|
+
* invariant, the design has entered a semantically invalid state and is
|
|
15
|
+
* the root cause of downstream failure(s).
|
|
16
|
+
*
|
|
17
|
+
* @param {Object} args The arguments to initialize the participant.
|
|
14
18
|
*/
|
|
15
19
|
constructor (args) {
|
|
16
20
|
super();
|
|
@@ -18,11 +22,7 @@ class Participant extends Base {
|
|
|
18
22
|
this.invariants = [];
|
|
19
23
|
this.abstractionId = null;
|
|
20
24
|
this.invariantViolated = false;
|
|
21
|
-
|
|
22
|
-
this._loadParticipantFromJSON(args);
|
|
23
|
-
} else {
|
|
24
|
-
this._loadArgs(args);
|
|
25
|
-
}
|
|
25
|
+
(isLoadedFromFile(args) ? this._loadFromFile(args) : this._loadArgs(args));
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -46,9 +46,9 @@ class Participant extends Base {
|
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Loads the participant from a JSON object.
|
|
49
|
-
* @param {Object} participantJSON
|
|
49
|
+
* @param {Object} participantJSON The JSON object read from file.
|
|
50
50
|
*/
|
|
51
|
-
|
|
51
|
+
_loadFromFile (participantJSON) {
|
|
52
52
|
for (const [key, value] of Object.entries(participantJSON)) {
|
|
53
53
|
if (key === "invariants") {
|
|
54
54
|
value.forEach(node => this.invariants.push(new Invariant(node)));
|
|
@@ -60,8 +60,8 @@ class Participant extends Base {
|
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Adds an invariant to the participant.
|
|
63
|
-
* @param {Invariant} invariant
|
|
64
|
-
* @returns
|
|
63
|
+
* @param {Invariant} invariant The invariant to add.
|
|
64
|
+
* @returns {Invariant} The invariant that was added.
|
|
65
65
|
*/
|
|
66
66
|
addInvariant (invariant) {
|
|
67
67
|
this.invariants.push(invariant);
|
|
@@ -69,16 +69,18 @@ class Participant extends Base {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
|
-
* Sets the value of
|
|
73
|
-
* @param {*} value
|
|
72
|
+
* Sets the value of this participant.
|
|
73
|
+
* @param {*} value The value to set for the participant.
|
|
74
74
|
*/
|
|
75
75
|
setValue (value) {
|
|
76
76
|
this.value = value;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* Enforces the
|
|
81
|
-
*
|
|
80
|
+
* Enforces the participant's invariants. Raises a flag indicating if an
|
|
81
|
+
* invariant was violated and counts the number of invariant violations.
|
|
82
|
+
*
|
|
83
|
+
* @returns {Boolean} Returns flag indicating if any invariant was violated.
|
|
82
84
|
*/
|
|
83
85
|
enforceInvariants () {
|
|
84
86
|
this.invariantViolated = false
|
|
@@ -98,9 +100,15 @@ class Participant extends Base {
|
|
|
98
100
|
* This abstraction id will be used to assign a value to the
|
|
99
101
|
* participant from the execution using the logged abstraction id.
|
|
100
102
|
*
|
|
101
|
-
* @param {String} abstractionId
|
|
103
|
+
* @param {String} abstractionId The abstraction ID to map to the
|
|
104
|
+
* participant.
|
|
102
105
|
*/
|
|
103
106
|
mapAbstraction (abstractionId) {
|
|
107
|
+
/**
|
|
108
|
+
* TODO: Much like the behavior, I am settling on a clean way to map
|
|
109
|
+
* the participant onto the implementation without introducing new
|
|
110
|
+
* unncessary layers.
|
|
111
|
+
*/
|
|
104
112
|
this.abstractionId = abstractionId;
|
|
105
113
|
}
|
|
106
114
|
}
|
package/src/TYPES.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the provided object was loaded from a file. This is determind by
|
|
3
|
+
* checking if the argument is an object and has a "dal_engine_uid" attribute,
|
|
4
|
+
* which is added to all objects when they are created and is written to file
|
|
5
|
+
* when the object is serialized.
|
|
6
|
+
*
|
|
7
|
+
* @param {*} obj The object to check.
|
|
8
|
+
* @returns {Boolean} True if the object was loaded from file, false otherwise.
|
|
9
|
+
*/
|
|
10
|
+
const isLoadedFromFile = (obj) => {
|
|
11
|
+
return typeof obj === "object" && obj !== null
|
|
12
|
+
&& !Array.isArray(obj) && Object.hasOwn(obj, "dal_engine_uid");
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default isLoadedFromFile;
|
package/tests/DALEngine.test.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
// eslint-disable-next-line no-unused-vars
|
|
1
2
|
import {readFile, unlink, writeFile} from "fs/promises"
|
|
2
3
|
import {resolve} from "path"
|
|
3
4
|
import {describe, expect, it} from "vitest";
|
|
4
5
|
|
|
5
6
|
import {DALEngine} from "../src/DALEngine.js";
|
|
7
|
+
import BehaviorAlreadyExistsError from "../src/Errors/BehaviorAlreadyExistsError.js";
|
|
6
8
|
import InvalidTransitionError from "../src/Errors/InvalidTransitionError.js";
|
|
7
9
|
import MissingAttributes from "../src/Errors/MissingAttributes.js";
|
|
10
|
+
import TransitionAlreadyExistsError from "../src/Errors/TransitionAlreadyExistsError.js";
|
|
8
11
|
import UnknownBehaviorError from "../src/Errors/UnknownBehaviorError.js";
|
|
9
12
|
import ENGINE_TYPES from "../src/TYPES.js";
|
|
10
13
|
|
|
@@ -36,6 +39,12 @@ describe("DALEngine", () => {
|
|
|
36
39
|
expect(node.getGoToBehaviors()).toStrictEqual(goToBehaviorIds);
|
|
37
40
|
});
|
|
38
41
|
|
|
42
|
+
it("throws when a behavior with same name is added to graph", () => {
|
|
43
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
44
|
+
d.addNode("AcceptBookFromUser", []);
|
|
45
|
+
expect(() => {d.addNode("AcceptBookFromUser", [])}).toThrow(BehaviorAlreadyExistsError);
|
|
46
|
+
});
|
|
47
|
+
|
|
39
48
|
it("find node that was added using behavior name", () => {
|
|
40
49
|
const d = new DALEngine({name: "Library Manager"});
|
|
41
50
|
const node = d.addNode("AcceptBookFromUser", []);
|
|
@@ -120,6 +129,16 @@ describe("DALEngine", () => {
|
|
|
120
129
|
expect(lastInvariant).toBe(invariant);
|
|
121
130
|
});
|
|
122
131
|
|
|
132
|
+
it ("add duplicate transition and check error is raised", () => {
|
|
133
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
134
|
+
const node1 = d.addNode("AcceptBookFromUser", []);
|
|
135
|
+
const node2 = d.addNode("AddBookToBasket", []);
|
|
136
|
+
node1.addGoToBehavior("AddBookToBasket");
|
|
137
|
+
expect(() => {
|
|
138
|
+
node1.addGoToBehavior("AddBookToBasket");
|
|
139
|
+
}).toThrow(TransitionAlreadyExistsError);
|
|
140
|
+
});
|
|
141
|
+
|
|
123
142
|
it("serialize to file and deseralize from file", async () => {
|
|
124
143
|
let d = new DALEngine({name: "Library Manager"});
|
|
125
144
|
const book = d.createParticipant({name: "book"});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// eslint-disable-next-line no-unused-vars
|
|
2
|
+
import {readFile, unlink, writeFile} from "fs/promises"
|
|
3
|
+
import {resolve} from "path"
|
|
4
|
+
import {describe, expect, it} from "vitest";
|
|
5
|
+
|
|
6
|
+
import {DALEngine} from "../src/DALEngine.js";
|
|
7
|
+
import GraphWithNameExistsError from "../src/Errors/GraphWithNameExistsError";
|
|
8
|
+
|
|
9
|
+
describe("multiple graphs test", () => {
|
|
10
|
+
|
|
11
|
+
it("create multiple graphs and switch", async () => {
|
|
12
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
13
|
+
d.createGraph("graph 1");
|
|
14
|
+
d.addNode("graph1behavior", []);
|
|
15
|
+
|
|
16
|
+
d.createGraph("graph 2");
|
|
17
|
+
d.addNode("graph2behavior", []);
|
|
18
|
+
|
|
19
|
+
d.selectGraph("graph 1");
|
|
20
|
+
expect(d.getNode("graph1behavior")).toBeTruthy();
|
|
21
|
+
expect(() => d.getNode("graph2behavior")).toThrow();
|
|
22
|
+
await writeFile(resolve(__dirname, "./temp/graph1.json"), d.serialize())
|
|
23
|
+
|
|
24
|
+
d.selectGraph("graph 2");
|
|
25
|
+
expect(d.getNode("graph2behavior")).toBeTruthy();
|
|
26
|
+
expect(() => d.getNode("graph1behavior")).toThrow();
|
|
27
|
+
await writeFile(resolve(__dirname, "./temp/graph2.json"), d.serialize())
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
it("get list of graphs", async () => {
|
|
32
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
33
|
+
d.createGraph("graph 1");
|
|
34
|
+
d.addNode("graph1behavior", []);
|
|
35
|
+
|
|
36
|
+
d.createGraph("graph 2");
|
|
37
|
+
d.addNode("graph2behavior", []);
|
|
38
|
+
|
|
39
|
+
expect(d.getSelectableGraphs()).toEqual(["default graph", "graph 1", "graph 2"]);
|
|
40
|
+
|
|
41
|
+
d.selectGraph("graph 1");
|
|
42
|
+
expect(d.getNode("graph1behavior")).toBeTruthy();
|
|
43
|
+
expect(() => d.getNode("graph2behavior")).toThrow();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("create graph with existing name", async () => {
|
|
47
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
48
|
+
d.createGraph("graph 1");
|
|
49
|
+
expect(() => d.createGraph("graph 1")).toThrow(GraphWithNameExistsError);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("test that graph is removed", async () => {
|
|
53
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
54
|
+
d.createGraph("graph 1");
|
|
55
|
+
d.addNode("graph1behavior", []);
|
|
56
|
+
d.createGraph("graph 2");
|
|
57
|
+
d.addNode("graph2behavior", []);
|
|
58
|
+
expect(d.getSelectableGraphs()).toEqual(["default graph", "graph 1", "graph 2"]);
|
|
59
|
+
d.removeGraph("graph 1");
|
|
60
|
+
|
|
61
|
+
// After removing a graph, the first graph in the list is active.
|
|
62
|
+
// If there are no graphs, a default graph is created and set as active.
|
|
63
|
+
expect(d.getSelectableGraphs()).toEqual(["default graph", "graph 2"]);
|
|
64
|
+
expect(d.graph.name).toBe("default graph");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("serialize and deserialize", async () => {
|
|
68
|
+
const d = new DALEngine({name: "Library Manager"});
|
|
69
|
+
d.createGraph("graph 1");
|
|
70
|
+
d.addNode("graph1behavior", []);
|
|
71
|
+
|
|
72
|
+
d.createGraph("graph 2");
|
|
73
|
+
d.addNode("graph2behavior", []);
|
|
74
|
+
|
|
75
|
+
await writeFile(resolve(__dirname, "./temp/graphs.json"), d.serialize());
|
|
76
|
+
|
|
77
|
+
d.deserialize(await readFile(resolve(__dirname, "./temp/graphs.json"), "utf-8"));
|
|
78
|
+
expect(d.getSelectableGraphs()).toEqual(["default graph", "graph 1", "graph 2"]);
|
|
79
|
+
});
|
|
80
|
+
});
|
package/tests/Invariant.test.js
CHANGED