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