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 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,16 +79,34 @@ 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);
77
88
  }
78
89
  }
79
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);
101
+ }
102
+ expectedAttributes.forEach((attr) => {
103
+ if (!(attr in args)) {
104
+ throw new MissingAttributes("Invariant", attr);
105
+ }
106
+ this[attr] = args[attr];
107
+ });
108
+ }
109
+
80
110
  /**
81
111
  * Loads the participant from a JSON object.
82
112
  * @param {Object} invariantJSON
@@ -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,16 +200,34 @@ 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);
141
209
  }
142
210
  }
143
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);
222
+ }
223
+ expectedAttributes.forEach((attr) => {
224
+ if (!(attr in args)) {
225
+ throw new MissingAttributes("Participant", attr);
226
+ }
227
+ this[attr] = args[attr];
228
+ });
229
+ }
230
+
144
231
  /**
145
232
  * Loads the participant from a JSON object.
146
233
  * @param {Object} participantJSON
@@ -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,16 +301,34 @@ 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);
212
310
  }
213
311
  }
214
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);
323
+ }
324
+ expectedAttributes.forEach((attr) => {
325
+ if (!(attr in args)) {
326
+ throw new MissingAttributes("Behavior", attr);
327
+ }
328
+ this[attr] = args[attr];
329
+ });
330
+ }
331
+
215
332
  /**
216
333
  * Loads the behavior from a JSON object.
217
334
  * @param {Object} behaviorJSON
@@ -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
  /**
@@ -266,13 +387,13 @@ class GraphNode extends Base {
266
387
  constructor (args) {
267
388
  super();
268
389
  this.type = ENGINE_TYPES$1.GRAPH_NODE;
269
- this.goToBehaviors = [];
390
+ this.goToBehaviorsIds = [];
270
391
  if (typeof args === "object" && args !== null) {
271
392
  if (Object.hasOwn(args, "uid")) {
272
393
  this.loadNodeFromJSON(args);
273
394
  } else {
274
395
  this.behavior = args.behavior;
275
- this.goToBehaviors = args.goToBehaviors;
396
+ this.goToBehaviorsIds = args.goToBehaviorsIds;
276
397
  }
277
398
  }
278
399
  }
@@ -285,13 +406,22 @@ class GraphNode extends Base {
285
406
  for (const [key, value] of Object.entries(nodesJSON)) {
286
407
  if (key === "behavior") {
287
408
  this.behavior = new Behavior(value);
288
- } else if (key === "goToBehaviors") {
289
- value.forEach(node => this.goToBehaviors.push(new Behavior(node)));
409
+ } else if (key === "goToBehaviorsIds") {
410
+ value.forEach(behaviorId => this.goToBehaviorsIds.push(behaviorId));
290
411
  } else {
291
412
  this[key] = nodesJSON[key];
292
413
  }
293
414
  } }
294
415
 
416
+ /**
417
+ * Adds a behavior name to the list of behaviors that this
418
+ * node transitions to.
419
+ * @param {String} behaviorId ID of behavior.
420
+ */
421
+ addGoToBehavior (behaviorId) {
422
+ this.goToBehaviorsIds.push(behaviorId);
423
+ }
424
+
295
425
  /**
296
426
  * Checks if the provided behavior name is a valid
297
427
  * behavior that the control flow selects as a
@@ -301,9 +431,9 @@ class GraphNode extends Base {
301
431
  * @returns {Boolean}
302
432
  */
303
433
  isValidGoToBehavior (behaviorName) {
304
- for (let i = 0; i < this.goToBehaviors.length; i++) {
305
- const behavior = this.goToBehaviors[i];
306
- if (behavior.name === behaviorName) {
434
+ for (let i = 0; i < this.goToBehaviorsIds.length; i++) {
435
+ const behaviorId = this.goToBehaviorsIds[i];
436
+ if (behaviorId === behaviorName) {
307
437
  return true;
308
438
  }
309
439
  }
@@ -349,13 +479,13 @@ class BehavioralControlGraph extends Base {
349
479
  /**
350
480
  * Adds a node to the graph.
351
481
  * @param {Behavior} behavior
352
- * @param {Array} goToBehaviors
482
+ * @param {Array} goToBehaviorsIds
353
483
  * @returns
354
484
  */
355
- addNode (behavior, goToBehaviors) {
485
+ addNode (behavior, goToBehaviorsIds) {
356
486
  const node = new GraphNode({
357
487
  behavior: behavior,
358
- goToBehaviors: goToBehaviors,
488
+ goToBehaviorsIds: goToBehaviorsIds,
359
489
  });
360
490
  this.nodes.push(node);
361
491
  return node;
@@ -412,6 +542,20 @@ class BehavioralControlGraph extends Base {
412
542
  throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
413
543
  }
414
544
  }
545
+
546
+ /**
547
+ * Exports the graph represented using mermaid syntax for visualization.
548
+ * @returns {String} Graph as mermaid diagram.
549
+ */
550
+ exportAsMermaid () {
551
+ let mermaid = "flowchart TD\n";
552
+ this.nodes.forEach((node) => {
553
+ node.goToBehaviorsIds.forEach((behaviorId) => {
554
+ mermaid += ` ${node.behavior.name} --> ${behaviorId}\n`;
555
+ });
556
+ });
557
+ return mermaid;
558
+ }
415
559
  }
416
560
 
417
561
  /**
@@ -419,16 +563,37 @@ class BehavioralControlGraph extends Base {
419
563
  * abstraction language. It exposes functions
420
564
  * configure the engine through the DAL specification.
421
565
  *
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.
566
+ * The design specified in this engine is mapped onto
567
+ * the implementation using abstraction ids. The
568
+ * implementation is then instrumented and the resulting
569
+ * execution trace is fed back into the engine and is
570
+ * automatically debugged by transforming the execution
571
+ * into the behavior of the design and enforcing the
572
+ * invariants.
425
573
  */
426
574
  class DALEngine {
427
575
  constructor (args) {
428
576
  this.graph = new BehavioralControlGraph();
429
- for (const [key, value] of Object.entries(args)) {
430
- this[key] = value;
577
+ this.loadArgs(args);
578
+ }
579
+
580
+ /**
581
+ * Loads the provided arguments.
582
+ * @throws {MissingAttributes} Thrown when required attr is not present.
583
+ * @param {Object} args
584
+ */
585
+ loadArgs (args) {
586
+ const expectedAttributes = ["name"];
587
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
588
+ // Not an object, so all attributes are missing.
589
+ throw new MissingAttributes("Engine", expectedAttributes);
431
590
  }
591
+ expectedAttributes.forEach((attr) => {
592
+ if (!(attr in args)) {
593
+ throw new MissingAttributes("Engine", attr);
594
+ }
595
+ this[attr] = args[attr];
596
+ });
432
597
  }
433
598
 
434
599
  /**
@@ -454,9 +619,6 @@ class DALEngine {
454
619
  * @returns {Participant}
455
620
  */
456
621
  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
622
  return new Participant(args);
461
623
  }
462
624
 
@@ -466,8 +628,6 @@ class DALEngine {
466
628
  * @returns {Behavior}
467
629
  */
468
630
  createBehavior (args) {
469
- // TODO: Validate that the args have the necessary keys
470
- // and raise custom error if they are missing.
471
631
  return new Behavior(args);
472
632
  }
473
633
 
@@ -477,10 +637,45 @@ class DALEngine {
477
637
  * @returns {Invariant}
478
638
  */
479
639
  createInvariant (args) {
480
- // TODO: Validate that the args have the necessary keys
481
- // and raise custom error if they are missing.
482
640
  return new Invariant(args);
483
641
  }
642
+
643
+
644
+ /**
645
+ * Returns the node in the graph with the given behavior name.
646
+ * @param {String} behaviorId
647
+ * @returns {GraphNode}
648
+ */
649
+ getNode (behaviorId) {
650
+ return this.graph.findNode(behaviorId);
651
+ }
652
+
653
+ /**
654
+ * Adds a node to the graph with the given behaviorId and goToBehaviors.
655
+ * @param {String} behaviorId
656
+ * @param {Array} goToBehaviorsIds
657
+ * @returns {GraphNode}
658
+ */
659
+ addNode (behaviorId, goToBehaviorsIds) {
660
+ const behavior = this.createBehavior({name: behaviorId});
661
+ return this.graph.addNode(behavior, goToBehaviorsIds);
662
+ }
663
+
664
+ /**
665
+ * Adds a goToBehavior to the node with the given behaviorId.
666
+ * @param {String} behaviorId
667
+ * @param {String|Array} goToBehaviorIds
668
+ * @returns {GraphNode}
669
+ */
670
+ addGoToBehavior (behaviorId, goToBehaviorIds) {
671
+ const node = this.graph.findNode(behaviorId);
672
+ if (Array.isArray(goToBehaviorIds)) {
673
+ node.goToBehaviorsIds.push(...goToBehaviorIds);
674
+ } else {
675
+ node.goToBehaviorsIds.push(goToBehaviorIds);
676
+ }
677
+ return node;
678
+ }
484
679
  }
485
680
 
486
681
  exports.DALEngine = DALEngine;