dal-engine-core-js-lib-dev 0.0.0 → 0.0.1

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 CHANGED
@@ -54,6 +54,18 @@ ENGINE_TYPES = Object.freeze(ENGINE_TYPES);
54
54
 
55
55
  var ENGINE_TYPES$1 = ENGINE_TYPES;
56
56
 
57
+ class MissingAttributes extends DALEngineError {
58
+ constructor (type, attribute) {
59
+ let msg;
60
+ if (Array.isArray(attribute) && attribute.length > 1) {
61
+ msg = `"${type}" must be initialized with the attributes "${attribute}".`;
62
+ } else {
63
+ msg = `"${type}" must be initialized with the attribute "${attribute}".`;
64
+ }
65
+ super(msg);
66
+ }
67
+ }
68
+
57
69
  /**
58
70
  * Class representing a Invariant in the design.
59
71
  */
@@ -67,14 +79,32 @@ class Invariant extends Base {
67
79
  super();
68
80
  this.type = ENGINE_TYPES$1.INVARIANT;
69
81
  this.invariantViolated = false;
70
- if (typeof args === "object" && args !== null) {
71
- if (Object.hasOwn(args, "uid")) {
72
- this.loadInvariantFromJSON(args);
73
- } else {
74
- this.name = args.name;
75
- this.rule = args.rule;
76
- }
82
+ this.invariantType = null;
83
+ this.traceId = null;
84
+ if (typeof args === "object" && Object.hasOwn(args, "uid")) {
85
+ this.loadInvariantFromJSON(args);
86
+ } else {
87
+ this.loadArgs(args);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Loads the provided arguments.
93
+ * @throws {MissingAttributes} Thrown when required attr is not present.
94
+ * @param {Object} args
95
+ */
96
+ loadArgs (args) {
97
+ const expectedAttributes = ["name", "rule"];
98
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
99
+ // Not an object, so all attributes are missing.
100
+ throw new MissingAttributes("Invariant", expectedAttributes);
77
101
  }
102
+ expectedAttributes.forEach((attr) => {
103
+ if (!(attr in args)) {
104
+ throw new MissingAttributes("Invariant", attr);
105
+ }
106
+ this[attr] = args[attr];
107
+ });
78
108
  }
79
109
 
80
110
  /**
@@ -109,13 +139,52 @@ class Invariant extends Base {
109
139
  enforceMinLength (value) {
110
140
  if ("keys" in this.rule) {
111
141
  for (let i = 0; i < this.rule["keys"].length; i++) {
112
- console.log(this.rule["keys"][i], value);
113
142
  value = value[this.rule["keys"][i]];
114
143
  }
115
144
  } if (value === null || typeof value !== "string" || value.length < this.rule.value) {
116
145
  this.invariantViolated = true;
117
146
  }
118
147
  }
148
+
149
+
150
+ /**
151
+ * Sets the invariant type.
152
+ *
153
+ * The two types of invariants are: Intrinsic and Substrate
154
+ *
155
+ * Substrate invariants are learnt by the design from the
156
+ * environment.
157
+ *
158
+ * Intrinsic invariants are arrived at naturally from the
159
+ * designs control flow, data dependencies and semantic assumptions.
160
+ *
161
+ * @param {String} invariantType
162
+ */
163
+ setInvariantType (invariantType) {
164
+ // TODO: Add validation for invariantType.
165
+ this.invariantType = invariantType;
166
+ }
167
+
168
+ /**
169
+ * This function adds the trace id which can be used for
170
+ * automated testing.
171
+ *
172
+ * Substrate invariants correspond to a trace that represents
173
+ * an environment that reveals a limitation of the substrate,
174
+ * thus motivating the invariant. It is also the environment
175
+ * in which an implementation can prove that it respects this
176
+ * invariant.
177
+ *
178
+ * Intrisinc invariants correspond to a trace that represents
179
+ * a factory default environment that enables the implementation
180
+ * to prove that it respects the invariant.
181
+ *
182
+ * @param {String} traceId ID of trace used for automated testing.
183
+ */
184
+ setTraceId (traceId) {
185
+ // TODO: Add validation for traceId.
186
+ this.traceId = traceId;
187
+ }
119
188
  }
120
189
 
121
190
  /**
@@ -131,14 +200,32 @@ class Participant extends Base {
131
200
  super();
132
201
  this.type = ENGINE_TYPES$1.INVARIANT;
133
202
  this.invariants = [];
203
+ this.abstractionId = null;
134
204
  this.invariantViolated = false;
135
- if (typeof args === "object" && args !== null) {
136
- if (Object.hasOwn(args, "uid")) {
137
- this.loadParticipantFromJSON(args);
138
- } else {
139
- this.name = args.name;
140
- }
205
+ if (typeof args === "object" && Object.hasOwn(args, "uid")) {
206
+ this.loadParticipantFromJSON(args);
207
+ } else {
208
+ this.loadArgs(args);
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Loads the provided arguments.
214
+ * @throws {MissingAttributes} Thrown when required attr is not present.
215
+ * @param {Object} args
216
+ */
217
+ loadArgs (args) {
218
+ const expectedAttributes = ["name"];
219
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
220
+ // Not an object, so all attributes are missing.
221
+ throw new MissingAttributes("Participant", expectedAttributes);
141
222
  }
223
+ expectedAttributes.forEach((attr) => {
224
+ if (!(attr in args)) {
225
+ throw new MissingAttributes("Participant", attr);
226
+ }
227
+ this[attr] = args[attr];
228
+ });
142
229
  }
143
230
 
144
231
  /**
@@ -187,6 +274,18 @@ class Participant extends Base {
187
274
  }
188
275
  return this.invariantViolated;
189
276
  }
277
+
278
+ /**
279
+ * Map the abstraction ID from the execution to the participant.
280
+ *
281
+ * This abstraction id will be used to assign a value to the
282
+ * participant from the execution using the logged abstraction id.
283
+ *
284
+ * @param {String} abstractionId
285
+ */
286
+ mapAbstraction (abstractionId) {
287
+ this.abstractionId = abstractionId;
288
+ }
190
289
  }
191
290
 
192
291
  /**
@@ -202,14 +301,32 @@ class Behavior extends Base {
202
301
  super();
203
302
  this.type = ENGINE_TYPES$1.BEHAVIOR;
204
303
  this.participants = [];
304
+ this.abstractionIds = [];
205
305
  this.invalidWorldState = false;
206
- if (typeof args === "object" && args !== null) {
207
- if (Object.hasOwn(args, "uid")) {
208
- this.loadBehaviorFromJSON(args);
209
- } else {
210
- this.name = args.name;
211
- }
306
+ if (typeof args === "object" && Object.hasOwn(args, "uid")) {
307
+ this.loadBehaviorFromJSON(args);
308
+ } else {
309
+ this.loadArgs(args);
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Loads the provided arguments.
315
+ * @throws {MissingAttributes} Thrown when required attr is not present.
316
+ * @param {Object} args
317
+ */
318
+ loadArgs (args) {
319
+ const expectedAttributes = ["name"];
320
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
321
+ // Not an object, so all attributes are missing.
322
+ throw new MissingAttributes("Behavior", expectedAttributes);
212
323
  }
324
+ expectedAttributes.forEach((attr) => {
325
+ if (!(attr in args)) {
326
+ throw new MissingAttributes("Behavior", attr);
327
+ }
328
+ this[attr] = args[attr];
329
+ });
213
330
  }
214
331
 
215
332
  /**
@@ -241,17 +358,21 @@ class Behavior extends Base {
241
358
  * @param {*} value
242
359
  */
243
360
  setParticipantValue (participantName, value) {
244
- for (let i = 0; i < this.participants.length; i++) {
245
- const participant = this.participants[i];
246
- if (participant.name === participantName) {
247
- participant.value = value;
248
- const violation = participant.enforceInvariants();
249
- if (violation) {
250
- this.invalidWorldState = true;
251
- }
252
- }
361
+ const participant = this.participants.find(obj => obj.name === participantName);
362
+ participant.value = value;
363
+ const violation = participant.enforceInvariants();
364
+ if (violation) {
365
+ this.invalidWorldState = true;
253
366
  }
254
367
  }
368
+
369
+ /**
370
+ * Maps the abstraction id from execution to the behavior.
371
+ * @param {String} abstractionId
372
+ */
373
+ addMapping (abstractionId) {
374
+ this.abstractionIds.push(abstractionId);
375
+ }
255
376
  }
256
377
 
257
378
  /**
@@ -412,6 +533,20 @@ class BehavioralControlGraph extends Base {
412
533
  throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
413
534
  }
414
535
  }
536
+
537
+ /**
538
+ * Exports the graph represented using mermaid syntax for visualization.
539
+ * @returns {String} Graph as mermaid diagram.
540
+ */
541
+ exportAsMermaid () {
542
+ let mermaid = "flowchart TD\n";
543
+ this.nodes.forEach((node) => {
544
+ node.goToBehaviors.forEach((behavior) => {
545
+ mermaid += ` ${node.behavior.name} --> ${behavior.name}\n`;
546
+ });
547
+ });
548
+ return mermaid;
549
+ }
415
550
  }
416
551
 
417
552
  /**
@@ -419,9 +554,13 @@ class BehavioralControlGraph extends Base {
419
554
  * abstraction language. It exposes functions
420
555
  * configure the engine through the DAL specification.
421
556
  *
422
- * The execution of an program instrumented with the
423
- * same design be used to step through the design
424
- * while the engine automatically debugs the execution.
557
+ * The design specified in this engine is mapped onto
558
+ * the implementation using abstraction ids. The
559
+ * implementation is then instrumented and the resulting
560
+ * execution trace is fed back into the engine and is
561
+ * automatically debugged by transforming the execution
562
+ * into the behavior of the design and enforcing the
563
+ * invariants.
425
564
  */
426
565
  class DALEngine {
427
566
  constructor (args) {
@@ -454,9 +593,6 @@ class DALEngine {
454
593
  * @returns {Participant}
455
594
  */
456
595
  createParticipant (args) {
457
- // TODO: Validate that the args have the necessary keys
458
- // and raise custom error if they are missing.
459
- // Perhaps it is better to do that in the class itself.
460
596
  return new Participant(args);
461
597
  }
462
598
 
@@ -466,8 +602,6 @@ class DALEngine {
466
602
  * @returns {Behavior}
467
603
  */
468
604
  createBehavior (args) {
469
- // TODO: Validate that the args have the necessary keys
470
- // and raise custom error if they are missing.
471
605
  return new Behavior(args);
472
606
  }
473
607
 
@@ -477,8 +611,6 @@ class DALEngine {
477
611
  * @returns {Invariant}
478
612
  */
479
613
  createInvariant (args) {
480
- // TODO: Validate that the args have the necessary keys
481
- // and raise custom error if they are missing.
482
614
  return new Invariant(args);
483
615
  }
484
616
  }
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,14 +77,32 @@ class Invariant extends Base {
65
77
  super();
66
78
  this.type = ENGINE_TYPES.INVARIANT;
67
79
  this.invariantViolated = false;
68
- if (typeof args === "object" && args !== null) {
69
- if (Object.hasOwn(args, "uid")) {
70
- this.loadInvariantFromJSON(args);
71
- } else {
72
- this.name = args.name;
73
- this.rule = args.rule;
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);
86
+ }
87
+ }
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);
75
99
  }
100
+ expectedAttributes.forEach((attr) => {
101
+ if (!(attr in args)) {
102
+ throw new MissingAttributes("Invariant", attr);
103
+ }
104
+ this[attr] = args[attr];
105
+ });
76
106
  }
77
107
 
78
108
  /**
@@ -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,14 +198,32 @@ 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 !== null) {
134
- if (Object.hasOwn(args, "uid")) {
135
- this.loadParticipantFromJSON(args);
136
- } else {
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);
207
+ }
208
+ }
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);
139
220
  }
221
+ expectedAttributes.forEach((attr) => {
222
+ if (!(attr in args)) {
223
+ throw new MissingAttributes("Participant", attr);
224
+ }
225
+ this[attr] = args[attr];
226
+ });
140
227
  }
141
228
 
142
229
  /**
@@ -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,14 +299,32 @@ 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 !== null) {
205
- if (Object.hasOwn(args, "uid")) {
206
- this.loadBehaviorFromJSON(args);
207
- } else {
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);
308
+ }
309
+ }
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);
210
321
  }
322
+ expectedAttributes.forEach((attr) => {
323
+ if (!(attr in args)) {
324
+ throw new MissingAttributes("Behavior", attr);
325
+ }
326
+ this[attr] = args[attr];
327
+ });
211
328
  }
212
329
 
213
330
  /**
@@ -239,17 +356,21 @@ class Behavior extends Base {
239
356
  * @param {*} value
240
357
  */
241
358
  setParticipantValue (participantName, value) {
242
- for (let i = 0; i < this.participants.length; i++) {
243
- const participant = this.participants[i];
244
- if (participant.name === participantName) {
245
- participant.value = value;
246
- const violation = participant.enforceInvariants();
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
  /**
@@ -410,6 +531,20 @@ class BehavioralControlGraph extends Base {
410
531
  throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
411
532
  }
412
533
  }
534
+
535
+ /**
536
+ * Exports the graph represented using mermaid syntax for visualization.
537
+ * @returns {String} Graph as mermaid diagram.
538
+ */
539
+ exportAsMermaid () {
540
+ let mermaid = "flowchart TD\n";
541
+ this.nodes.forEach((node) => {
542
+ node.goToBehaviors.forEach((behavior) => {
543
+ mermaid += ` ${node.behavior.name} --> ${behavior.name}\n`;
544
+ });
545
+ });
546
+ return mermaid;
547
+ }
413
548
  }
414
549
 
415
550
  /**
@@ -417,9 +552,13 @@ class BehavioralControlGraph extends Base {
417
552
  * abstraction language. It exposes functions
418
553
  * configure the engine through the DAL specification.
419
554
  *
420
- * The execution of an program instrumented with the
421
- * same design be used to step through the design
422
- * while the engine automatically debugs the execution.
555
+ * The design specified in this engine is mapped onto
556
+ * the implementation using abstraction ids. The
557
+ * implementation is then instrumented and the resulting
558
+ * execution trace is fed back into the engine and is
559
+ * automatically debugged by transforming the execution
560
+ * into the behavior of the design and enforcing the
561
+ * invariants.
423
562
  */
424
563
  class DALEngine {
425
564
  constructor (args) {
@@ -452,9 +591,6 @@ class DALEngine {
452
591
  * @returns {Participant}
453
592
  */
454
593
  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
594
  return new Participant(args);
459
595
  }
460
596
 
@@ -464,8 +600,6 @@ class DALEngine {
464
600
  * @returns {Behavior}
465
601
  */
466
602
  createBehavior (args) {
467
- // TODO: Validate that the args have the necessary keys
468
- // and raise custom error if they are missing.
469
603
  return new Behavior(args);
470
604
  }
471
605
 
@@ -475,8 +609,6 @@ class DALEngine {
475
609
  * @returns {Invariant}
476
610
  */
477
611
  createInvariant (args) {
478
- // TODO: Validate that the args have the necessary keys
479
- // and raise custom error if they are missing.
480
612
  return new Invariant(args);
481
613
  }
482
614
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dal-engine-core-js-lib-dev",
3
- "version": "0.0.0",
3
+ "version": "0.0.1",
4
4
  "main": "dist/index.cjs",
5
5
  "module": "dist/index.esm.js",
6
6
  "type": "module",
@@ -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.goToBehaviors.forEach((behavior) => {
118
+ mermaid += ` ${node.behavior.name} --> ${behavior.name}\n`;
119
+ });
120
+ });
121
+ return mermaid;
122
+ }
123
+ };
110
124
 
111
125
  export default BehavioralControlGraph;
package/src/DALEngine.js CHANGED
@@ -8,9 +8,13 @@ import Participant from "./Members/Participant";
8
8
  * abstraction language. It exposes functions
9
9
  * configure the engine through the DAL specification.
10
10
  *
11
- * The execution of an program instrumented with the
12
- * same design be used to step through the design
13
- * while the engine automatically debugs the execution.
11
+ * The design specified in this engine is mapped onto
12
+ * the implementation using abstraction ids. The
13
+ * implementation is then instrumented and the resulting
14
+ * execution trace is fed back into the engine and is
15
+ * automatically debugged by transforming the execution
16
+ * into the behavior of the design and enforcing the
17
+ * invariants.
14
18
  */
15
19
  export class DALEngine {
16
20
  constructor (args) {
@@ -43,9 +47,6 @@ export class DALEngine {
43
47
  * @returns {Participant}
44
48
  */
45
49
  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
50
  return new Participant(args);
50
51
  }
51
52
 
@@ -55,8 +56,6 @@ export class DALEngine {
55
56
  * @returns {Behavior}
56
57
  */
57
58
  createBehavior (args) {
58
- // TODO: Validate that the args have the necessary keys
59
- // and raise custom error if they are missing.
60
59
  return new Behavior(args);
61
60
  }
62
61
 
@@ -66,8 +65,6 @@ export class DALEngine {
66
65
  * @returns {Invariant}
67
66
  */
68
67
  createInvariant (args) {
69
- // TODO: Validate that the args have the necessary keys
70
- // and raise custom error if they are missing.
71
68
  return new Invariant(args);
72
69
  }
73
70
  }
@@ -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;
@@ -1,4 +1,5 @@
1
1
  import Base from "../Base";
2
+ import MissingAttributes from "../Errors/MissingAttributes";
2
3
  import ENGINE_TYPES from "../TYPES";
3
4
  import Participant from "./Participant";
4
5
  /**
@@ -14,16 +15,34 @@ class Behavior extends Base {
14
15
  super();
15
16
  this.type = ENGINE_TYPES.BEHAVIOR;
16
17
  this.participants = [];
18
+ this.abstractionIds = [];
17
19
  this.invalidWorldState = false;
18
- if (typeof args === "object" && args !== null) {
19
- if (Object.hasOwn(args, "uid")) {
20
- this.loadBehaviorFromJSON(args);
21
- } else {
22
- this.name = args.name;
23
- }
20
+ if (typeof args === "object" && Object.hasOwn(args, "uid")) {
21
+ this.loadBehaviorFromJSON(args);
22
+ } else {
23
+ this.loadArgs(args);
24
24
  }
25
25
  }
26
26
 
27
+ /**
28
+ * Loads the provided arguments.
29
+ * @throws {MissingAttributes} Thrown when required attr is not present.
30
+ * @param {Object} args
31
+ */
32
+ loadArgs (args) {
33
+ const expectedAttributes = ["name"];
34
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
35
+ // Not an object, so all attributes are missing.
36
+ throw new MissingAttributes("Behavior", expectedAttributes);
37
+ }
38
+ expectedAttributes.forEach((attr) => {
39
+ if (!(attr in args)) {
40
+ throw new MissingAttributes("Behavior", attr);
41
+ }
42
+ this[attr] = args[attr];
43
+ });
44
+ }
45
+
27
46
  /**
28
47
  * Loads the behavior from a JSON object.
29
48
  * @param {Object} behaviorJSON
@@ -54,17 +73,21 @@ class Behavior extends Base {
54
73
  * @param {*} value
55
74
  */
56
75
  setParticipantValue (participantName, value) {
57
- for (let i = 0; i < this.participants.length; i++) {
58
- const participant = this.participants[i];
59
- if (participant.name === participantName) {
60
- participant.value = value;
61
- const violation = participant.enforceInvariants();
62
- if (violation) {
63
- this.invalidWorldState = true;
64
- }
65
- }
76
+ const participant = this.participants.find(obj => obj.name === participantName);
77
+ participant.value = value;
78
+ const violation = participant.enforceInvariants();
79
+ if (violation) {
80
+ this.invalidWorldState = true;
66
81
  }
67
82
  }
83
+
84
+ /**
85
+ * Maps the abstraction id from execution to the behavior.
86
+ * @param {String} abstractionId
87
+ */
88
+ addMapping (abstractionId) {
89
+ this.abstractionIds.push(abstractionId);
90
+ }
68
91
  }
69
92
 
70
93
  export default Behavior;
@@ -1,5 +1,7 @@
1
1
  import Base from "../Base";
2
+ import MissingAttributes from "../Errors/MissingAttributes";
2
3
  import ENGINE_TYPES from "../TYPES";
4
+
3
5
  /**
4
6
  * Class representing a Invariant in the design.
5
7
  */
@@ -12,17 +14,35 @@ class Invariant extends Base {
12
14
  constructor (args) {
13
15
  super();
14
16
  this.type = ENGINE_TYPES.INVARIANT;
15
- this.invariantViolated = false
16
- if (typeof args === "object" && args !== null) {
17
- if (Object.hasOwn(args, "uid")) {
18
- this.loadInvariantFromJSON(args);
19
- } else {
20
- this.name = args.name;
21
- this.rule = args.rule;
22
- }
17
+ this.invariantViolated = false;
18
+ this.invariantType = null;
19
+ this.traceId = null;
20
+ if (typeof args === "object" && Object.hasOwn(args, "uid")) {
21
+ this.loadInvariantFromJSON(args);
22
+ } else {
23
+ this.loadArgs(args);
23
24
  }
24
25
  }
25
26
 
27
+ /**
28
+ * Loads the provided arguments.
29
+ * @throws {MissingAttributes} Thrown when required attr is not present.
30
+ * @param {Object} args
31
+ */
32
+ loadArgs (args) {
33
+ const expectedAttributes = ["name", "rule"];
34
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
35
+ // Not an object, so all attributes are missing.
36
+ throw new MissingAttributes("Invariant", expectedAttributes);
37
+ }
38
+ expectedAttributes.forEach((attr) => {
39
+ if (!(attr in args)) {
40
+ throw new MissingAttributes("Invariant", attr);
41
+ }
42
+ this[attr] = args[attr];
43
+ });
44
+ }
45
+
26
46
  /**
27
47
  * Loads the participant from a JSON object.
28
48
  * @param {Object} invariantJSON
@@ -56,7 +76,6 @@ class Invariant extends Base {
56
76
  enforceMinLength (value) {
57
77
  if ("keys" in this.rule) {
58
78
  for (let i = 0; i < this.rule["keys"].length; i++) {
59
- console.log(this.rule["keys"][i], value)
60
79
  value = value[this.rule["keys"][i]];
61
80
  }
62
81
  };
@@ -64,6 +83,46 @@ class Invariant extends Base {
64
83
  this.invariantViolated = true;
65
84
  }
66
85
  }
86
+
87
+
88
+ /**
89
+ * Sets the invariant type.
90
+ *
91
+ * The two types of invariants are: Intrinsic and Substrate
92
+ *
93
+ * Substrate invariants are learnt by the design from the
94
+ * environment.
95
+ *
96
+ * Intrinsic invariants are arrived at naturally from the
97
+ * designs control flow, data dependencies and semantic assumptions.
98
+ *
99
+ * @param {String} invariantType
100
+ */
101
+ setInvariantType (invariantType) {
102
+ // TODO: Add validation for invariantType.
103
+ this.invariantType = invariantType;
104
+ }
105
+
106
+ /**
107
+ * This function adds the trace id which can be used for
108
+ * automated testing.
109
+ *
110
+ * Substrate invariants correspond to a trace that represents
111
+ * an environment that reveals a limitation of the substrate,
112
+ * thus motivating the invariant. It is also the environment
113
+ * in which an implementation can prove that it respects this
114
+ * invariant.
115
+ *
116
+ * Intrisinc invariants correspond to a trace that represents
117
+ * a factory default environment that enables the implementation
118
+ * to prove that it respects the invariant.
119
+ *
120
+ * @param {String} traceId ID of trace used for automated testing.
121
+ */
122
+ setTraceId (traceId) {
123
+ // TODO: Add validation for traceId.
124
+ this.traceId = traceId;
125
+ }
67
126
  }
68
127
 
69
128
  export default Invariant;
@@ -1,6 +1,8 @@
1
1
  import Base from "../Base";
2
+ import MissingAttributes from "../Errors/MissingAttributes";
2
3
  import ENGINE_TYPES from "../TYPES";
3
4
  import Invariant from "./Invariant";
5
+
4
6
  /**
5
7
  * Class representing a participant in the design.
6
8
  */
@@ -14,16 +16,34 @@ class Participant extends Base {
14
16
  super();
15
17
  this.type = ENGINE_TYPES.INVARIANT;
16
18
  this.invariants = [];
19
+ this.abstractionId = null;
17
20
  this.invariantViolated = false;
18
- if (typeof args === "object" && args !== null) {
19
- if (Object.hasOwn(args, "uid")) {
20
- this.loadParticipantFromJSON(args);
21
- } else {
22
- this.name = args.name;
23
- }
21
+ if (typeof args === "object" && Object.hasOwn(args, "uid")) {
22
+ this.loadParticipantFromJSON(args);
23
+ } else {
24
+ this.loadArgs(args);
24
25
  }
25
26
  }
26
27
 
28
+ /**
29
+ * Loads the provided arguments.
30
+ * @throws {MissingAttributes} Thrown when required attr is not present.
31
+ * @param {Object} args
32
+ */
33
+ loadArgs (args) {
34
+ const expectedAttributes = ["name"];
35
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
36
+ // Not an object, so all attributes are missing.
37
+ throw new MissingAttributes("Participant", expectedAttributes);
38
+ }
39
+ expectedAttributes.forEach((attr) => {
40
+ if (!(attr in args)) {
41
+ throw new MissingAttributes("Participant", attr);
42
+ }
43
+ this[attr] = args[attr];
44
+ });
45
+ }
46
+
27
47
  /**
28
48
  * Loads the participant from a JSON object.
29
49
  * @param {Object} participantJSON
@@ -71,6 +91,18 @@ class Participant extends Base {
71
91
  }
72
92
  return this.invariantViolated;
73
93
  }
94
+
95
+ /**
96
+ * Map the abstraction ID from the execution to the participant.
97
+ *
98
+ * This abstraction id will be used to assign a value to the
99
+ * participant from the execution using the logged abstraction id.
100
+ *
101
+ * @param {String} abstractionId
102
+ */
103
+ mapAbstraction (abstractionId) {
104
+ this.abstractionId = abstractionId;
105
+ }
74
106
  }
75
107
 
76
108
  export default Participant;
@@ -4,6 +4,7 @@ import {describe, expect, it} from "vitest";
4
4
 
5
5
  import {DALEngine} from "../src/DALEngine.js";
6
6
  import InvalidTransitionError from "../src/Errors/InvalidTransitionError.js";
7
+ import MissingAttributes from "../src/Errors/MissingAttributes.js";
7
8
  import UnknownBehaviorError from "../src/Errors/UnknownBehaviorError.js";
8
9
  import ENGINE_TYPES from "../src/TYPES.js";
9
10
 
@@ -13,6 +14,16 @@ describe("DALEngine", () => {
13
14
  expect(dalInstance.name).toBe("Library Manager");
14
15
  });
15
16
 
17
+ it("throws on missing attributes", () => {
18
+ const d = new DALEngine({name: "Library Manager"});
19
+ expect(() => {d.createBehavior()}).toThrow(MissingAttributes);
20
+ expect(() => {d.createBehavior({})}).toThrow(MissingAttributes);
21
+ expect(() => {d.createBehavior({"rule": "adsf"})}).toThrow(MissingAttributes);
22
+ expect(() => {d.createInvariant({"name": "asdf"})}).toThrow(MissingAttributes);
23
+ expect(() => {d.createParticipant()}).toThrow(MissingAttributes);
24
+ expect(() => {d.createParticipant({})}).toThrow(MissingAttributes);
25
+ });
26
+
16
27
  it("adds node to graph", () => {
17
28
  const d = new DALEngine({name: "Library Manager"});
18
29
  const behavior1 = d.createBehavior({name: "AcceptBookFromUser"});
@@ -45,9 +56,9 @@ describe("DALEngine", () => {
45
56
  const behavior1 = d.createBehavior({name: "AcceptBookFromUser"});
46
57
  const behavior2 = d.createBehavior({name: "AddBookToBasket"});
47
58
  const behavior3 = d.createBehavior({name: "AnotherBehavior"});
48
- d.graph.addNode(behavior1, [behavior2, behavior3])
49
- d.graph.addNode(behavior2, [])
50
- d.graph.addNode(behavior3, [])
59
+ d.graph.addNode(behavior1, [behavior2, behavior3]);
60
+ d.graph.addNode(behavior2, []);
61
+ d.graph.addNode(behavior3, []);
51
62
 
52
63
  // Misspell behavior name to trigger unknown behavior error
53
64
  expect(() => {
@@ -82,7 +93,16 @@ describe("DALEngine", () => {
82
93
  it("add invariant to participant", () => {
83
94
  const d = new DALEngine({name: "Library Manager"});
84
95
  const book = d.createParticipant({name: "book"});
85
- const invariant = d.createInvariant({name: "minLength"});
96
+ const invariant = d.createInvariant(
97
+ {
98
+ "name": "MinLengthConstraint",
99
+ "rule": {
100
+ "type": "minLength",
101
+ "keys": ["value", "name"],
102
+ "value": 1,
103
+ },
104
+ }
105
+ );
86
106
 
87
107
  book.addInvariant(invariant);
88
108
 
@@ -3,8 +3,17 @@ import {resolve} from "path"
3
3
  import {describe, expect, it} from "vitest";
4
4
 
5
5
  import {DALEngine} from "../src/DALEngine.js";
6
+ import MissingAttributes from "../src/Errors/MissingAttributes.js";
6
7
 
7
8
  describe("invariantTests", () => {
9
+
10
+ it("invariant throws on missing attributes", () => {
11
+ let d = new DALEngine({name: "Library Manager"});
12
+ expect(() => {d.createInvariant({"name": "asdf"})}).toThrow(MissingAttributes);
13
+ expect(() => {d.createInvariant({})}).toThrow(MissingAttributes);
14
+ expect(() => {d.createInvariant()}).toThrow(MissingAttributes);
15
+ });
16
+
8
17
  it("test invariant directly through participants", async () => {
9
18
  let d = new DALEngine({name: "Library Manager"});
10
19
 
@@ -0,0 +1,41 @@
1
+ import {readFile, unlink, writeFile} from "fs/promises"
2
+ import {resolve} from "path"
3
+ import {describe, expect, it} from "vitest";
4
+
5
+ import {DALEngine} from "../src/DALEngine.js";
6
+
7
+ describe("SimpleDesignTest", () => {
8
+
9
+ it("create simple design", async () => {
10
+ const d = new DALEngine({name: "Library Manager"});
11
+ let behavior = d.createBehavior({name: "AcceptChoiceToAddBookToBasket"});
12
+ const AcceptBookFromUser = d.createBehavior({name: "AcceptBookFromUser"});
13
+ d.graph.addNode(behavior, [AcceptBookFromUser]);
14
+ const AddBookToBasket = d.createBehavior({name: "AddBookToBasket"});
15
+ d.graph.addNode(AcceptBookFromUser, [AddBookToBasket]);
16
+
17
+ const AcceptChoiceToAuditLibrary = d.createBehavior({name: "AcceptChoiceToAuditLibrary"});
18
+ const GenerateAuditReport = d.createBehavior({name: "GenerateAuditReport"});
19
+ d.graph.addNode(AcceptChoiceToAuditLibrary, [GenerateAuditReport]);
20
+ const HandAuditToUser = d.createBehavior({name: "HandAuditToUser"});
21
+ d.graph.addNode(GenerateAuditReport, [HandAuditToUser]);
22
+
23
+ const AcceptChoiceToPlaceBooksOnShelf = d.createBehavior(
24
+ {name: "AcceptChoiceToPlaceBooksOnShelf"}
25
+ );
26
+ const GetBookFromBasket = d.createBehavior({name: "GetBookFromBasket"});
27
+ d.graph.addNode(AcceptChoiceToPlaceBooksOnShelf, [GetBookFromBasket]);
28
+ const GetFirstLetterOfBookName = d.createBehavior({name: "GetFirstLetterOfBookName"});
29
+ d.graph.addNode(GetBookFromBasket, [GetFirstLetterOfBookName]);
30
+ const CreateSlotOnBookShelf = d.createBehavior({name: "CreateSlotOnBookShelf"});
31
+ const AddBookToShelf = d.createBehavior({name: "AddBookToShelf"});
32
+ d.graph.addNode(GetFirstLetterOfBookName, [CreateSlotOnBookShelf]);
33
+ d.graph.addNode(GetFirstLetterOfBookName, [AddBookToShelf]);
34
+ d.graph.addNode(CreateSlotOnBookShelf, [AddBookToShelf]);
35
+ d.graph.addNode(AddBookToShelf, [GetBookFromBasket]);
36
+
37
+ // Output can be viewed using https://mermaid.live/
38
+ const filePath2 = resolve(__dirname, "./temp/simple_design_mermaid.txt")
39
+ await writeFile(filePath2, d.graph.exportAsMermaid())
40
+ });
41
+ });