dal-engine-core-js-lib-dev 0.0.0 → 0.0.2
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 +246 -51
- package/dist/index.esm.js +246 -51
- package/package.json +1 -1
- package/src/BehavioralControlGraph/BehavioralControlGraph.js +18 -4
- package/src/BehavioralControlGraph/GraphNode.js +16 -7
- package/src/DALEngine.js +64 -12
- package/src/Errors/MissingAttributes.js +15 -0
- package/src/Members/Behavior.js +38 -15
- package/src/Members/Invariant.js +68 -9
- package/src/Members/Participant.js +38 -6
- package/tests/DALEngine.test.js +50 -33
- package/tests/Invariant.test.js +15 -7
- package/tests/SimpleDesign.test.js +31 -0
- package/tests/simple_design_temp.json +1 -0
package/dist/index.esm.js
CHANGED
|
@@ -52,6 +52,18 @@ ENGINE_TYPES$1 = Object.freeze(ENGINE_TYPES$1);
|
|
|
52
52
|
|
|
53
53
|
var ENGINE_TYPES = ENGINE_TYPES$1;
|
|
54
54
|
|
|
55
|
+
class MissingAttributes extends DALEngineError {
|
|
56
|
+
constructor (type, attribute) {
|
|
57
|
+
let msg;
|
|
58
|
+
if (Array.isArray(attribute) && attribute.length > 1) {
|
|
59
|
+
msg = `"${type}" must be initialized with the attributes "${attribute}".`;
|
|
60
|
+
} else {
|
|
61
|
+
msg = `"${type}" must be initialized with the attribute "${attribute}".`;
|
|
62
|
+
}
|
|
63
|
+
super(msg);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
55
67
|
/**
|
|
56
68
|
* Class representing a Invariant in the design.
|
|
57
69
|
*/
|
|
@@ -65,16 +77,34 @@ class Invariant extends Base {
|
|
|
65
77
|
super();
|
|
66
78
|
this.type = ENGINE_TYPES.INVARIANT;
|
|
67
79
|
this.invariantViolated = false;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
80
|
+
this.invariantType = null;
|
|
81
|
+
this.traceId = null;
|
|
82
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
83
|
+
this.loadInvariantFromJSON(args);
|
|
84
|
+
} else {
|
|
85
|
+
this.loadArgs(args);
|
|
75
86
|
}
|
|
76
87
|
}
|
|
77
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Loads the provided arguments.
|
|
91
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
92
|
+
* @param {Object} args
|
|
93
|
+
*/
|
|
94
|
+
loadArgs (args) {
|
|
95
|
+
const expectedAttributes = ["name", "rule"];
|
|
96
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
97
|
+
// Not an object, so all attributes are missing.
|
|
98
|
+
throw new MissingAttributes("Invariant", expectedAttributes);
|
|
99
|
+
}
|
|
100
|
+
expectedAttributes.forEach((attr) => {
|
|
101
|
+
if (!(attr in args)) {
|
|
102
|
+
throw new MissingAttributes("Invariant", attr);
|
|
103
|
+
}
|
|
104
|
+
this[attr] = args[attr];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
78
108
|
/**
|
|
79
109
|
* Loads the participant from a JSON object.
|
|
80
110
|
* @param {Object} invariantJSON
|
|
@@ -107,13 +137,52 @@ class Invariant extends Base {
|
|
|
107
137
|
enforceMinLength (value) {
|
|
108
138
|
if ("keys" in this.rule) {
|
|
109
139
|
for (let i = 0; i < this.rule["keys"].length; i++) {
|
|
110
|
-
console.log(this.rule["keys"][i], value);
|
|
111
140
|
value = value[this.rule["keys"][i]];
|
|
112
141
|
}
|
|
113
142
|
} if (value === null || typeof value !== "string" || value.length < this.rule.value) {
|
|
114
143
|
this.invariantViolated = true;
|
|
115
144
|
}
|
|
116
145
|
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Sets the invariant type.
|
|
150
|
+
*
|
|
151
|
+
* The two types of invariants are: Intrinsic and Substrate
|
|
152
|
+
*
|
|
153
|
+
* Substrate invariants are learnt by the design from the
|
|
154
|
+
* environment.
|
|
155
|
+
*
|
|
156
|
+
* Intrinsic invariants are arrived at naturally from the
|
|
157
|
+
* designs control flow, data dependencies and semantic assumptions.
|
|
158
|
+
*
|
|
159
|
+
* @param {String} invariantType
|
|
160
|
+
*/
|
|
161
|
+
setInvariantType (invariantType) {
|
|
162
|
+
// TODO: Add validation for invariantType.
|
|
163
|
+
this.invariantType = invariantType;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* This function adds the trace id which can be used for
|
|
168
|
+
* automated testing.
|
|
169
|
+
*
|
|
170
|
+
* Substrate invariants correspond to a trace that represents
|
|
171
|
+
* an environment that reveals a limitation of the substrate,
|
|
172
|
+
* thus motivating the invariant. It is also the environment
|
|
173
|
+
* in which an implementation can prove that it respects this
|
|
174
|
+
* invariant.
|
|
175
|
+
*
|
|
176
|
+
* Intrisinc invariants correspond to a trace that represents
|
|
177
|
+
* a factory default environment that enables the implementation
|
|
178
|
+
* to prove that it respects the invariant.
|
|
179
|
+
*
|
|
180
|
+
* @param {String} traceId ID of trace used for automated testing.
|
|
181
|
+
*/
|
|
182
|
+
setTraceId (traceId) {
|
|
183
|
+
// TODO: Add validation for traceId.
|
|
184
|
+
this.traceId = traceId;
|
|
185
|
+
}
|
|
117
186
|
}
|
|
118
187
|
|
|
119
188
|
/**
|
|
@@ -129,16 +198,34 @@ class Participant extends Base {
|
|
|
129
198
|
super();
|
|
130
199
|
this.type = ENGINE_TYPES.INVARIANT;
|
|
131
200
|
this.invariants = [];
|
|
201
|
+
this.abstractionId = null;
|
|
132
202
|
this.invariantViolated = false;
|
|
133
|
-
if (typeof args === "object" && args
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
this.name = args.name;
|
|
138
|
-
}
|
|
203
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
204
|
+
this.loadParticipantFromJSON(args);
|
|
205
|
+
} else {
|
|
206
|
+
this.loadArgs(args);
|
|
139
207
|
}
|
|
140
208
|
}
|
|
141
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Loads the provided arguments.
|
|
212
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
213
|
+
* @param {Object} args
|
|
214
|
+
*/
|
|
215
|
+
loadArgs (args) {
|
|
216
|
+
const expectedAttributes = ["name"];
|
|
217
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
218
|
+
// Not an object, so all attributes are missing.
|
|
219
|
+
throw new MissingAttributes("Participant", expectedAttributes);
|
|
220
|
+
}
|
|
221
|
+
expectedAttributes.forEach((attr) => {
|
|
222
|
+
if (!(attr in args)) {
|
|
223
|
+
throw new MissingAttributes("Participant", attr);
|
|
224
|
+
}
|
|
225
|
+
this[attr] = args[attr];
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
142
229
|
/**
|
|
143
230
|
* Loads the participant from a JSON object.
|
|
144
231
|
* @param {Object} participantJSON
|
|
@@ -185,6 +272,18 @@ class Participant extends Base {
|
|
|
185
272
|
}
|
|
186
273
|
return this.invariantViolated;
|
|
187
274
|
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Map the abstraction ID from the execution to the participant.
|
|
278
|
+
*
|
|
279
|
+
* This abstraction id will be used to assign a value to the
|
|
280
|
+
* participant from the execution using the logged abstraction id.
|
|
281
|
+
*
|
|
282
|
+
* @param {String} abstractionId
|
|
283
|
+
*/
|
|
284
|
+
mapAbstraction (abstractionId) {
|
|
285
|
+
this.abstractionId = abstractionId;
|
|
286
|
+
}
|
|
188
287
|
}
|
|
189
288
|
|
|
190
289
|
/**
|
|
@@ -200,16 +299,34 @@ class Behavior extends Base {
|
|
|
200
299
|
super();
|
|
201
300
|
this.type = ENGINE_TYPES.BEHAVIOR;
|
|
202
301
|
this.participants = [];
|
|
302
|
+
this.abstractionIds = [];
|
|
203
303
|
this.invalidWorldState = false;
|
|
204
|
-
if (typeof args === "object" && args
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.name = args.name;
|
|
209
|
-
}
|
|
304
|
+
if (typeof args === "object" && Object.hasOwn(args, "uid")) {
|
|
305
|
+
this.loadBehaviorFromJSON(args);
|
|
306
|
+
} else {
|
|
307
|
+
this.loadArgs(args);
|
|
210
308
|
}
|
|
211
309
|
}
|
|
212
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Loads the provided arguments.
|
|
313
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
314
|
+
* @param {Object} args
|
|
315
|
+
*/
|
|
316
|
+
loadArgs (args) {
|
|
317
|
+
const expectedAttributes = ["name"];
|
|
318
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
319
|
+
// Not an object, so all attributes are missing.
|
|
320
|
+
throw new MissingAttributes("Behavior", expectedAttributes);
|
|
321
|
+
}
|
|
322
|
+
expectedAttributes.forEach((attr) => {
|
|
323
|
+
if (!(attr in args)) {
|
|
324
|
+
throw new MissingAttributes("Behavior", attr);
|
|
325
|
+
}
|
|
326
|
+
this[attr] = args[attr];
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
213
330
|
/**
|
|
214
331
|
* Loads the behavior from a JSON object.
|
|
215
332
|
* @param {Object} behaviorJSON
|
|
@@ -239,17 +356,21 @@ class Behavior extends Base {
|
|
|
239
356
|
* @param {*} value
|
|
240
357
|
*/
|
|
241
358
|
setParticipantValue (participantName, value) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
if (violation) {
|
|
248
|
-
this.invalidWorldState = true;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
359
|
+
const participant = this.participants.find(obj => obj.name === participantName);
|
|
360
|
+
participant.value = value;
|
|
361
|
+
const violation = participant.enforceInvariants();
|
|
362
|
+
if (violation) {
|
|
363
|
+
this.invalidWorldState = true;
|
|
251
364
|
}
|
|
252
365
|
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Maps the abstraction id from execution to the behavior.
|
|
369
|
+
* @param {String} abstractionId
|
|
370
|
+
*/
|
|
371
|
+
addMapping (abstractionId) {
|
|
372
|
+
this.abstractionIds.push(abstractionId);
|
|
373
|
+
}
|
|
253
374
|
}
|
|
254
375
|
|
|
255
376
|
/**
|
|
@@ -264,13 +385,13 @@ class GraphNode extends Base {
|
|
|
264
385
|
constructor (args) {
|
|
265
386
|
super();
|
|
266
387
|
this.type = ENGINE_TYPES.GRAPH_NODE;
|
|
267
|
-
this.
|
|
388
|
+
this.goToBehaviorsIds = [];
|
|
268
389
|
if (typeof args === "object" && args !== null) {
|
|
269
390
|
if (Object.hasOwn(args, "uid")) {
|
|
270
391
|
this.loadNodeFromJSON(args);
|
|
271
392
|
} else {
|
|
272
393
|
this.behavior = args.behavior;
|
|
273
|
-
this.
|
|
394
|
+
this.goToBehaviorsIds = args.goToBehaviorsIds;
|
|
274
395
|
}
|
|
275
396
|
}
|
|
276
397
|
}
|
|
@@ -283,13 +404,22 @@ class GraphNode extends Base {
|
|
|
283
404
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
284
405
|
if (key === "behavior") {
|
|
285
406
|
this.behavior = new Behavior(value);
|
|
286
|
-
} else if (key === "
|
|
287
|
-
value.forEach(
|
|
407
|
+
} else if (key === "goToBehaviorsIds") {
|
|
408
|
+
value.forEach(behaviorId => this.goToBehaviorsIds.push(behaviorId));
|
|
288
409
|
} else {
|
|
289
410
|
this[key] = nodesJSON[key];
|
|
290
411
|
}
|
|
291
412
|
} }
|
|
292
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Adds a behavior name to the list of behaviors that this
|
|
416
|
+
* node transitions to.
|
|
417
|
+
* @param {String} behaviorId ID of behavior.
|
|
418
|
+
*/
|
|
419
|
+
addGoToBehavior (behaviorId) {
|
|
420
|
+
this.goToBehaviorsIds.push(behaviorId);
|
|
421
|
+
}
|
|
422
|
+
|
|
293
423
|
/**
|
|
294
424
|
* Checks if the provided behavior name is a valid
|
|
295
425
|
* behavior that the control flow selects as a
|
|
@@ -299,9 +429,9 @@ class GraphNode extends Base {
|
|
|
299
429
|
* @returns {Boolean}
|
|
300
430
|
*/
|
|
301
431
|
isValidGoToBehavior (behaviorName) {
|
|
302
|
-
for (let i = 0; i < this.
|
|
303
|
-
const
|
|
304
|
-
if (
|
|
432
|
+
for (let i = 0; i < this.goToBehaviorsIds.length; i++) {
|
|
433
|
+
const behaviorId = this.goToBehaviorsIds[i];
|
|
434
|
+
if (behaviorId === behaviorName) {
|
|
305
435
|
return true;
|
|
306
436
|
}
|
|
307
437
|
}
|
|
@@ -347,13 +477,13 @@ class BehavioralControlGraph extends Base {
|
|
|
347
477
|
/**
|
|
348
478
|
* Adds a node to the graph.
|
|
349
479
|
* @param {Behavior} behavior
|
|
350
|
-
* @param {Array}
|
|
480
|
+
* @param {Array} goToBehaviorsIds
|
|
351
481
|
* @returns
|
|
352
482
|
*/
|
|
353
|
-
addNode (behavior,
|
|
483
|
+
addNode (behavior, goToBehaviorsIds) {
|
|
354
484
|
const node = new GraphNode({
|
|
355
485
|
behavior: behavior,
|
|
356
|
-
|
|
486
|
+
goToBehaviorsIds: goToBehaviorsIds,
|
|
357
487
|
});
|
|
358
488
|
this.nodes.push(node);
|
|
359
489
|
return node;
|
|
@@ -410,6 +540,20 @@ class BehavioralControlGraph extends Base {
|
|
|
410
540
|
throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
|
|
411
541
|
}
|
|
412
542
|
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Exports the graph represented using mermaid syntax for visualization.
|
|
546
|
+
* @returns {String} Graph as mermaid diagram.
|
|
547
|
+
*/
|
|
548
|
+
exportAsMermaid () {
|
|
549
|
+
let mermaid = "flowchart TD\n";
|
|
550
|
+
this.nodes.forEach((node) => {
|
|
551
|
+
node.goToBehaviorsIds.forEach((behaviorId) => {
|
|
552
|
+
mermaid += ` ${node.behavior.name} --> ${behaviorId}\n`;
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
return mermaid;
|
|
556
|
+
}
|
|
413
557
|
}
|
|
414
558
|
|
|
415
559
|
/**
|
|
@@ -417,16 +561,37 @@ class BehavioralControlGraph extends Base {
|
|
|
417
561
|
* abstraction language. It exposes functions
|
|
418
562
|
* configure the engine through the DAL specification.
|
|
419
563
|
*
|
|
420
|
-
* The
|
|
421
|
-
*
|
|
422
|
-
*
|
|
564
|
+
* The design specified in this engine is mapped onto
|
|
565
|
+
* the implementation using abstraction ids. The
|
|
566
|
+
* implementation is then instrumented and the resulting
|
|
567
|
+
* execution trace is fed back into the engine and is
|
|
568
|
+
* automatically debugged by transforming the execution
|
|
569
|
+
* into the behavior of the design and enforcing the
|
|
570
|
+
* invariants.
|
|
423
571
|
*/
|
|
424
572
|
class DALEngine {
|
|
425
573
|
constructor (args) {
|
|
426
574
|
this.graph = new BehavioralControlGraph();
|
|
427
|
-
|
|
428
|
-
|
|
575
|
+
this.loadArgs(args);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Loads the provided arguments.
|
|
580
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
581
|
+
* @param {Object} args
|
|
582
|
+
*/
|
|
583
|
+
loadArgs (args) {
|
|
584
|
+
const expectedAttributes = ["name"];
|
|
585
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
586
|
+
// Not an object, so all attributes are missing.
|
|
587
|
+
throw new MissingAttributes("Engine", expectedAttributes);
|
|
429
588
|
}
|
|
589
|
+
expectedAttributes.forEach((attr) => {
|
|
590
|
+
if (!(attr in args)) {
|
|
591
|
+
throw new MissingAttributes("Engine", attr);
|
|
592
|
+
}
|
|
593
|
+
this[attr] = args[attr];
|
|
594
|
+
});
|
|
430
595
|
}
|
|
431
596
|
|
|
432
597
|
/**
|
|
@@ -452,9 +617,6 @@ class DALEngine {
|
|
|
452
617
|
* @returns {Participant}
|
|
453
618
|
*/
|
|
454
619
|
createParticipant (args) {
|
|
455
|
-
// TODO: Validate that the args have the necessary keys
|
|
456
|
-
// and raise custom error if they are missing.
|
|
457
|
-
// Perhaps it is better to do that in the class itself.
|
|
458
620
|
return new Participant(args);
|
|
459
621
|
}
|
|
460
622
|
|
|
@@ -464,8 +626,6 @@ class DALEngine {
|
|
|
464
626
|
* @returns {Behavior}
|
|
465
627
|
*/
|
|
466
628
|
createBehavior (args) {
|
|
467
|
-
// TODO: Validate that the args have the necessary keys
|
|
468
|
-
// and raise custom error if they are missing.
|
|
469
629
|
return new Behavior(args);
|
|
470
630
|
}
|
|
471
631
|
|
|
@@ -475,10 +635,45 @@ class DALEngine {
|
|
|
475
635
|
* @returns {Invariant}
|
|
476
636
|
*/
|
|
477
637
|
createInvariant (args) {
|
|
478
|
-
// TODO: Validate that the args have the necessary keys
|
|
479
|
-
// and raise custom error if they are missing.
|
|
480
638
|
return new Invariant(args);
|
|
481
639
|
}
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Returns the node in the graph with the given behavior name.
|
|
644
|
+
* @param {String} behaviorId
|
|
645
|
+
* @returns {GraphNode}
|
|
646
|
+
*/
|
|
647
|
+
getNode (behaviorId) {
|
|
648
|
+
return this.graph.findNode(behaviorId);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Adds a node to the graph with the given behaviorId and goToBehaviors.
|
|
653
|
+
* @param {String} behaviorId
|
|
654
|
+
* @param {Array} goToBehaviorsIds
|
|
655
|
+
* @returns {GraphNode}
|
|
656
|
+
*/
|
|
657
|
+
addNode (behaviorId, goToBehaviorsIds) {
|
|
658
|
+
const behavior = this.createBehavior({name: behaviorId});
|
|
659
|
+
return this.graph.addNode(behavior, goToBehaviorsIds);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Adds a goToBehavior to the node with the given behaviorId.
|
|
664
|
+
* @param {String} behaviorId
|
|
665
|
+
* @param {String|Array} goToBehaviorIds
|
|
666
|
+
* @returns {GraphNode}
|
|
667
|
+
*/
|
|
668
|
+
addGoToBehavior (behaviorId, goToBehaviorIds) {
|
|
669
|
+
const node = this.graph.findNode(behaviorId);
|
|
670
|
+
if (Array.isArray(goToBehaviorIds)) {
|
|
671
|
+
node.goToBehaviorsIds.push(...goToBehaviorIds);
|
|
672
|
+
} else {
|
|
673
|
+
node.goToBehaviorsIds.push(goToBehaviorIds);
|
|
674
|
+
}
|
|
675
|
+
return node;
|
|
676
|
+
}
|
|
482
677
|
}
|
|
483
678
|
|
|
484
679
|
export { DALEngine };
|
package/package.json
CHANGED
|
@@ -43,13 +43,13 @@ class BehavioralControlGraph extends Base {
|
|
|
43
43
|
/**
|
|
44
44
|
* Adds a node to the graph.
|
|
45
45
|
* @param {Behavior} behavior
|
|
46
|
-
* @param {Array}
|
|
46
|
+
* @param {Array} goToBehaviorsIds
|
|
47
47
|
* @returns
|
|
48
48
|
*/
|
|
49
|
-
addNode (behavior,
|
|
49
|
+
addNode (behavior, goToBehaviorsIds) {
|
|
50
50
|
const node = new GraphNode({
|
|
51
51
|
behavior: behavior,
|
|
52
|
-
|
|
52
|
+
goToBehaviorsIds: goToBehaviorsIds,
|
|
53
53
|
});
|
|
54
54
|
this.nodes.push(node);
|
|
55
55
|
return node;
|
|
@@ -106,6 +106,20 @@ class BehavioralControlGraph extends Base {
|
|
|
106
106
|
throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Exports the graph represented using mermaid syntax for visualization.
|
|
112
|
+
* @returns {String} Graph as mermaid diagram.
|
|
113
|
+
*/
|
|
114
|
+
exportAsMermaid () {
|
|
115
|
+
let mermaid = "flowchart TD\n";
|
|
116
|
+
this.nodes.forEach((node) => {
|
|
117
|
+
node.goToBehaviorsIds.forEach((behaviorId) => {
|
|
118
|
+
mermaid += ` ${node.behavior.name} --> ${behaviorId}\n`;
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return mermaid;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
110
124
|
|
|
111
125
|
export default BehavioralControlGraph;
|
|
@@ -13,13 +13,13 @@ class GraphNode extends Base {
|
|
|
13
13
|
constructor (args) {
|
|
14
14
|
super();
|
|
15
15
|
this.type = ENGINE_TYPES.GRAPH_NODE;
|
|
16
|
-
this.
|
|
16
|
+
this.goToBehaviorsIds = [];
|
|
17
17
|
if (typeof args === "object" && args !== null) {
|
|
18
18
|
if (Object.hasOwn(args, "uid")) {
|
|
19
19
|
this.loadNodeFromJSON(args);
|
|
20
20
|
} else {
|
|
21
21
|
this.behavior = args.behavior;
|
|
22
|
-
this.
|
|
22
|
+
this.goToBehaviorsIds = args.goToBehaviorsIds;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -32,14 +32,23 @@ class GraphNode extends Base {
|
|
|
32
32
|
for (const [key, value] of Object.entries(nodesJSON)) {
|
|
33
33
|
if (key === "behavior") {
|
|
34
34
|
this.behavior = new Behavior(value);
|
|
35
|
-
} else if (key === "
|
|
36
|
-
value.forEach(
|
|
35
|
+
} else if (key === "goToBehaviorsIds") {
|
|
36
|
+
value.forEach(behaviorId => this.goToBehaviorsIds.push(behaviorId));
|
|
37
37
|
} else {
|
|
38
38
|
this[key] = nodesJSON[key];
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Adds a behavior name to the list of behaviors that this
|
|
45
|
+
* node transitions to.
|
|
46
|
+
* @param {String} behaviorId ID of behavior.
|
|
47
|
+
*/
|
|
48
|
+
addGoToBehavior (behaviorId) {
|
|
49
|
+
this.goToBehaviorsIds.push(behaviorId);
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
/**
|
|
44
53
|
* Checks if the provided behavior name is a valid
|
|
45
54
|
* behavior that the control flow selects as a
|
|
@@ -49,9 +58,9 @@ class GraphNode extends Base {
|
|
|
49
58
|
* @returns {Boolean}
|
|
50
59
|
*/
|
|
51
60
|
isValidGoToBehavior (behaviorName) {
|
|
52
|
-
for (let i = 0; i < this.
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
61
|
+
for (let i = 0; i < this.goToBehaviorsIds.length; i++) {
|
|
62
|
+
const behaviorId = this.goToBehaviorsIds[i];
|
|
63
|
+
if (behaviorId === behaviorName) {
|
|
55
64
|
return true;
|
|
56
65
|
}
|
|
57
66
|
}
|
package/src/DALEngine.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import BehavioralControlGraph from "./BehavioralControlGraph/BehavioralControlGraph";
|
|
2
|
+
import MissingAttributes from "./Errors/MissingAttributes";
|
|
2
3
|
import Behavior from "./Members/Behavior";
|
|
3
4
|
import Invariant from "./Members/Invariant";
|
|
4
5
|
import Participant from "./Members/Participant";
|
|
@@ -8,16 +9,37 @@ import Participant from "./Members/Participant";
|
|
|
8
9
|
* abstraction language. It exposes functions
|
|
9
10
|
* configure the engine through the DAL specification.
|
|
10
11
|
*
|
|
11
|
-
* The
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* The design specified in this engine is mapped onto
|
|
13
|
+
* the implementation using abstraction ids. The
|
|
14
|
+
* implementation is then instrumented and the resulting
|
|
15
|
+
* execution trace is fed back into the engine and is
|
|
16
|
+
* automatically debugged by transforming the execution
|
|
17
|
+
* into the behavior of the design and enforcing the
|
|
18
|
+
* invariants.
|
|
14
19
|
*/
|
|
15
20
|
export class DALEngine {
|
|
16
21
|
constructor (args) {
|
|
17
22
|
this.graph = new BehavioralControlGraph();
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
this.loadArgs(args);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Loads the provided arguments.
|
|
28
|
+
* @throws {MissingAttributes} Thrown when required attr is not present.
|
|
29
|
+
* @param {Object} args
|
|
30
|
+
*/
|
|
31
|
+
loadArgs (args) {
|
|
32
|
+
const expectedAttributes = ["name"];
|
|
33
|
+
if (typeof args !== "object" || args === null || Array.isArray(args)) {
|
|
34
|
+
// Not an object, so all attributes are missing.
|
|
35
|
+
throw new MissingAttributes("Engine", expectedAttributes);
|
|
20
36
|
}
|
|
37
|
+
expectedAttributes.forEach((attr) => {
|
|
38
|
+
if (!(attr in args)) {
|
|
39
|
+
throw new MissingAttributes("Engine", attr);
|
|
40
|
+
}
|
|
41
|
+
this[attr] = args[attr];
|
|
42
|
+
});
|
|
21
43
|
}
|
|
22
44
|
|
|
23
45
|
/**
|
|
@@ -43,9 +65,6 @@ export class DALEngine {
|
|
|
43
65
|
* @returns {Participant}
|
|
44
66
|
*/
|
|
45
67
|
createParticipant (args) {
|
|
46
|
-
// TODO: Validate that the args have the necessary keys
|
|
47
|
-
// and raise custom error if they are missing.
|
|
48
|
-
// Perhaps it is better to do that in the class itself.
|
|
49
68
|
return new Participant(args);
|
|
50
69
|
}
|
|
51
70
|
|
|
@@ -55,8 +74,6 @@ export class DALEngine {
|
|
|
55
74
|
* @returns {Behavior}
|
|
56
75
|
*/
|
|
57
76
|
createBehavior (args) {
|
|
58
|
-
// TODO: Validate that the args have the necessary keys
|
|
59
|
-
// and raise custom error if they are missing.
|
|
60
77
|
return new Behavior(args);
|
|
61
78
|
}
|
|
62
79
|
|
|
@@ -66,8 +83,43 @@ export class DALEngine {
|
|
|
66
83
|
* @returns {Invariant}
|
|
67
84
|
*/
|
|
68
85
|
createInvariant (args) {
|
|
69
|
-
// TODO: Validate that the args have the necessary keys
|
|
70
|
-
// and raise custom error if they are missing.
|
|
71
86
|
return new Invariant(args);
|
|
72
87
|
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Returns the node in the graph with the given behavior name.
|
|
92
|
+
* @param {String} behaviorId
|
|
93
|
+
* @returns {GraphNode}
|
|
94
|
+
*/
|
|
95
|
+
getNode (behaviorId) {
|
|
96
|
+
return this.graph.findNode(behaviorId);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Adds a node to the graph with the given behaviorId and goToBehaviors.
|
|
101
|
+
* @param {String} behaviorId
|
|
102
|
+
* @param {Array} goToBehaviorsIds
|
|
103
|
+
* @returns {GraphNode}
|
|
104
|
+
*/
|
|
105
|
+
addNode (behaviorId, goToBehaviorsIds) {
|
|
106
|
+
const behavior = this.createBehavior({name: behaviorId});
|
|
107
|
+
return this.graph.addNode(behavior, goToBehaviorsIds);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Adds a goToBehavior to the node with the given behaviorId.
|
|
112
|
+
* @param {String} behaviorId
|
|
113
|
+
* @param {String|Array} goToBehaviorIds
|
|
114
|
+
* @returns {GraphNode}
|
|
115
|
+
*/
|
|
116
|
+
addGoToBehavior (behaviorId, goToBehaviorIds) {
|
|
117
|
+
const node = this.graph.findNode(behaviorId);
|
|
118
|
+
if (Array.isArray(goToBehaviorIds)) {
|
|
119
|
+
node.goToBehaviorsIds.push(...goToBehaviorIds);
|
|
120
|
+
} else {
|
|
121
|
+
node.goToBehaviorsIds.push(goToBehaviorIds);
|
|
122
|
+
}
|
|
123
|
+
return node;
|
|
124
|
+
}
|
|
73
125
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import DALEngineError from "./DALEngineError";
|
|
2
|
+
|
|
3
|
+
class MissingAttributes extends DALEngineError {
|
|
4
|
+
constructor (type, attribute) {
|
|
5
|
+
let msg;
|
|
6
|
+
if (Array.isArray(attribute) && attribute.length > 1) {
|
|
7
|
+
msg = `"${type}" must be initialized with the attributes "${attribute}".`
|
|
8
|
+
} else {
|
|
9
|
+
msg = `"${type}" must be initialized with the attribute "${attribute}".`
|
|
10
|
+
}
|
|
11
|
+
super(msg);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default MissingAttributes;
|