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

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 ADDED
@@ -0,0 +1,486 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Base class for all objects in design.
5
+ */
6
+ class Base {
7
+ /**
8
+ * Initialize the base class.
9
+ * @param {String} name
10
+ */
11
+ constructor () {
12
+ this.uid = crypto.randomUUID();
13
+ }
14
+
15
+ getType () {
16
+ return this.type;
17
+ }
18
+ }
19
+
20
+ class DALEngineError extends Error {
21
+ constructor (message) {
22
+ super(message);
23
+ }
24
+ }
25
+
26
+ class InvalidTransitionError extends DALEngineError {
27
+ constructor (from, to) {
28
+ super(`Invalid transition from "${from}" to "${to}"`);
29
+
30
+ this.name = "InvalidTransitionError";
31
+ this.prevBehavior = from;
32
+ this.nextBehavior = to;
33
+ }
34
+ }
35
+
36
+ class UnknownBehaviorError extends DALEngineError {
37
+ constructor (behaviorName) {
38
+ super(`The behavior named "${behaviorName}" was not found in graph.`);
39
+
40
+ this.name = "UnknownBehaviorError";
41
+ this.behaviorName = behaviorName;
42
+ }
43
+ }
44
+
45
+ let ENGINE_TYPES = {
46
+ BEHAVIOR: 1,
47
+ INVARIANT: 2,
48
+ PARTICIPANT: 3,
49
+ PRIMITIVE: 4,
50
+ BEHAVIORAL_CONTROL_GRAPH: 5,
51
+ GRAPH_NODE: 6,
52
+ };
53
+ ENGINE_TYPES = Object.freeze(ENGINE_TYPES);
54
+
55
+ var ENGINE_TYPES$1 = ENGINE_TYPES;
56
+
57
+ /**
58
+ * Class representing a Invariant in the design.
59
+ */
60
+ class Invariant extends Base {
61
+ /**
62
+ * Initialize the Invariant.
63
+ * @param {String} name
64
+ * @param args
65
+ */
66
+ constructor (args) {
67
+ super();
68
+ this.type = ENGINE_TYPES$1.INVARIANT;
69
+ 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
+ }
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Loads the participant from a JSON object.
82
+ * @param {Object} invariantJSON
83
+ */
84
+ loadInvariantFromJSON (invariantJSON) {
85
+ for (const [key, value] of Object.entries(invariantJSON)) {
86
+ this[key] = value;
87
+ } // Reset these because they are set by the execution
88
+ this.invariantViolated = null;
89
+ this.value = null;
90
+ }
91
+
92
+ /**
93
+ * Evaluate the invariant.
94
+ * @param {*} value
95
+ * @returns {Boolean}
96
+ */
97
+ evaluate (value) {
98
+ this.invariantViolated = false;
99
+ if (this.rule.type === "minLength") {
100
+ this.enforceMinLength(value);
101
+ }
102
+ return this.invariantViolated;
103
+ }
104
+
105
+ /**
106
+ * Enforce the string min length invariant
107
+ * @param value
108
+ */
109
+ enforceMinLength (value) {
110
+ if ("keys" in this.rule) {
111
+ for (let i = 0; i < this.rule["keys"].length; i++) {
112
+ console.log(this.rule["keys"][i], value);
113
+ value = value[this.rule["keys"][i]];
114
+ }
115
+ } if (value === null || typeof value !== "string" || value.length < this.rule.value) {
116
+ this.invariantViolated = true;
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Class representing a participant in the design.
123
+ */
124
+ class Participant extends Base {
125
+ /**
126
+ * Initialize the semantic participant.
127
+ * @param {String} name
128
+ * @param args
129
+ */
130
+ constructor (args) {
131
+ super();
132
+ this.type = ENGINE_TYPES$1.INVARIANT;
133
+ this.invariants = [];
134
+ 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
+ }
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Loads the participant from a JSON object.
146
+ * @param {Object} participantJSON
147
+ */
148
+ loadParticipantFromJSON (participantJSON) {
149
+ for (const [key, value] of Object.entries(participantJSON)) {
150
+ if (key === "invariants") {
151
+ value.forEach(node => this.invariants.push(new Invariant(node)));
152
+ } else {
153
+ this[key] = participantJSON[key];
154
+ }
155
+ } }
156
+
157
+ /**
158
+ * Adds an invariant to the participant.
159
+ * @param {Invariant} invariant
160
+ * @returns
161
+ */
162
+ addInvariant (invariant) {
163
+ this.invariants.push(invariant);
164
+ return invariant;
165
+ }
166
+
167
+ /**
168
+ * Sets the value of the participant.
169
+ * @param {*} value
170
+ */
171
+ setValue (value) {
172
+ this.value = value;
173
+ }
174
+
175
+ /**
176
+ * Enforces the particiants invariants.
177
+ * @returns {Boolean}
178
+ */
179
+ enforceInvariants () {
180
+ this.invariantViolated = false;
181
+ this.invariantViolationCount = 0;
182
+ for (let i = 0; i < this.invariants.length; i++) {
183
+ if (this.invariants[i].evaluate(this.value)) {
184
+ this.invariantViolated = true;
185
+ this.invariantViolationCount++;
186
+ }
187
+ }
188
+ return this.invariantViolated;
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Class representing a Behavior in the design.
194
+ */
195
+ class Behavior extends Base {
196
+ /**
197
+ * Initialize the Behavior.
198
+ * @param {String} name
199
+ * @param args
200
+ */
201
+ constructor (args) {
202
+ super();
203
+ this.type = ENGINE_TYPES$1.BEHAVIOR;
204
+ this.participants = [];
205
+ 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
+ }
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Loads the behavior from a JSON object.
217
+ * @param {Object} behaviorJSON
218
+ */
219
+ loadBehaviorFromJSON (behaviorJSON) {
220
+ for (const [key, value] of Object.entries(behaviorJSON)) {
221
+ if (key === "participants") {
222
+ value.forEach(node => this.participants.push(new Participant(node)));
223
+ } else {
224
+ this[key] = behaviorJSON[key];
225
+ }
226
+ } }
227
+
228
+ /**
229
+ * Adds a participant to the behavior.
230
+ * @param {Participant} participant
231
+ * @returns
232
+ */
233
+ addParticpant (participant) {
234
+ this.participants.push(participant);
235
+ return participant;
236
+ }
237
+
238
+ /**
239
+ * Set the participant value.
240
+ * @param {String} participantName
241
+ * @param {*} value
242
+ */
243
+ 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
+ }
253
+ }
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Class representing a behavioral control graph node.
259
+ */
260
+ class GraphNode extends Base {
261
+ /**
262
+ * Initialize the node.
263
+ * @param {String} name
264
+ * @param args
265
+ */
266
+ constructor (args) {
267
+ super();
268
+ this.type = ENGINE_TYPES$1.GRAPH_NODE;
269
+ this.goToBehaviors = [];
270
+ if (typeof args === "object" && args !== null) {
271
+ if (Object.hasOwn(args, "uid")) {
272
+ this.loadNodeFromJSON(args);
273
+ } else {
274
+ this.behavior = args.behavior;
275
+ this.goToBehaviors = args.goToBehaviors;
276
+ }
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Loads the nodes from a JSON object.
282
+ * @param {Object} nodesJSON
283
+ */
284
+ loadNodeFromJSON (nodesJSON) {
285
+ for (const [key, value] of Object.entries(nodesJSON)) {
286
+ if (key === "behavior") {
287
+ this.behavior = new Behavior(value);
288
+ } else if (key === "goToBehaviors") {
289
+ value.forEach(node => this.goToBehaviors.push(new Behavior(node)));
290
+ } else {
291
+ this[key] = nodesJSON[key];
292
+ }
293
+ } }
294
+
295
+ /**
296
+ * Checks if the provided behavior name is a valid
297
+ * behavior that the control flow selects as a
298
+ * result of the this nodes state transformation. i.e.
299
+ * is this behavior in the goToBehavior list.
300
+ * @param {String} behaviorName
301
+ * @returns {Boolean}
302
+ */
303
+ isValidGoToBehavior (behaviorName) {
304
+ for (let i = 0; i < this.goToBehaviors.length; i++) {
305
+ const behavior = this.goToBehaviors[i];
306
+ if (behavior.name === behaviorName) {
307
+ return true;
308
+ }
309
+ }
310
+ return false;
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Class representing the behavioral control graph.
316
+ */
317
+ class BehavioralControlGraph extends Base {
318
+ /**
319
+ * Initialize the behavioral control graph.
320
+ * @param {String} name
321
+ * @param args
322
+ */
323
+ constructor (args) {
324
+ super();
325
+ this.type = ENGINE_TYPES$1.BEHAVIORAL_CONTROL_GRAPH;
326
+ this.nodes = [];
327
+ if (typeof args === "object" && args !== null) {
328
+ if (Object.hasOwn(args, "uid")) {
329
+ this.loadGraphFromJSON(args);
330
+ } else {
331
+ this.name = args.name;
332
+ }
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Loads the graph from a JSON object..
338
+ * @param {Object} graphJson
339
+ */
340
+ loadGraphFromJSON (graphJson) {
341
+ for (const [key, value] of Object.entries(graphJson)) {
342
+ if (key === "nodes") {
343
+ value.forEach(node => this.nodes.push(new GraphNode(node)));
344
+ } else {
345
+ this[key] = graphJson[key];
346
+ }
347
+ } }
348
+
349
+ /**
350
+ * Adds a node to the graph.
351
+ * @param {Behavior} behavior
352
+ * @param {Array} goToBehaviors
353
+ * @returns
354
+ */
355
+ addNode (behavior, goToBehaviors) {
356
+ const node = new GraphNode({
357
+ behavior: behavior,
358
+ goToBehaviors: goToBehaviors,
359
+ });
360
+ this.nodes.push(node);
361
+ return node;
362
+ }
363
+
364
+
365
+ /**
366
+ * Finds the given node given the behavior name.
367
+ * @param {String} behaviorName
368
+ * @throws {UnknownBehaviorError} Raised when the provided behavior
369
+ * does not exist in the graph.
370
+ * @returns
371
+ */
372
+ findNode (behaviorName) {
373
+ for (let i = 0; i < this.nodes.length; i++) {
374
+ const behavior = this.nodes[i].behavior;
375
+ if (behavior.name === behaviorName) {
376
+ return this.nodes[i];
377
+ }
378
+ }
379
+ throw new UnknownBehaviorError(behaviorName);
380
+ }
381
+
382
+ /**
383
+ * Sets the active node given the behavior name.
384
+ * The execution provides the next observed
385
+ * behavior and the active node indicates if
386
+ * if it is a valid transition.
387
+ *
388
+ *
389
+ * @param {String} behaviorName
390
+ */
391
+ setCurrentBehavior (behaviorName) {
392
+ const node = this.findNode(behaviorName);
393
+ /**
394
+ * TODO: Ensure it is atomic because the execution
395
+ * will only set a behavior when its the first one.
396
+ * It will walk using goToBehavior after that.
397
+ */
398
+ this.currentNode = node;
399
+ }
400
+
401
+ /**
402
+ * Check if the observed behavior is a valid transition
403
+ * given the current node.
404
+ * @param {String} nextBehaviorName
405
+ * @throws {InvalidTransitionError} Raised when the provided
406
+ * behavior is not a valid transition.
407
+ */
408
+ goToBehavior (nextBehaviorName) {
409
+ if (this.currentNode.isValidGoToBehavior(nextBehaviorName)) {
410
+ this.currentNode = this.findNode(nextBehaviorName);
411
+ } else {
412
+ throw new InvalidTransitionError(this.currentNode.behavior.name, nextBehaviorName);
413
+ }
414
+ }
415
+ }
416
+
417
+ /**
418
+ * An object representing an engine written in Design
419
+ * abstraction language. It exposes functions
420
+ * configure the engine through the DAL specification.
421
+ *
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.
425
+ */
426
+ class DALEngine {
427
+ constructor (args) {
428
+ this.graph = new BehavioralControlGraph();
429
+ for (const [key, value] of Object.entries(args)) {
430
+ this[key] = value;
431
+ }
432
+ }
433
+
434
+ /**
435
+ * Exports the behavioral control graph to JSON text.
436
+ * @returns {String}
437
+ */
438
+ serialize () {
439
+ return JSON.stringify(this.graph);
440
+ }
441
+
442
+ /**
443
+ * Import the behavioral control graph from JSON text.
444
+ * @param {String} jsonText
445
+ */
446
+ deserialize (jsonText) {
447
+ this.graph = new BehavioralControlGraph();
448
+ this.graph.loadGraphFromJSON(JSON.parse(jsonText));
449
+ }
450
+
451
+ /**
452
+ * Creates a participant.
453
+ * @param {Object} args
454
+ * @returns {Participant}
455
+ */
456
+ 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
+ return new Participant(args);
461
+ }
462
+
463
+ /**
464
+ * Creates a behavior.
465
+ * @param {Object} args
466
+ * @returns {Behavior}
467
+ */
468
+ createBehavior (args) {
469
+ // TODO: Validate that the args have the necessary keys
470
+ // and raise custom error if they are missing.
471
+ return new Behavior(args);
472
+ }
473
+
474
+ /**
475
+ * Creates an invariant.
476
+ * @param {Object} args
477
+ * @returns {Invariant}
478
+ */
479
+ createInvariant (args) {
480
+ // TODO: Validate that the args have the necessary keys
481
+ // and raise custom error if they are missing.
482
+ return new Invariant(args);
483
+ }
484
+ }
485
+
486
+ exports.DALEngine = DALEngine;