state-machine-cat 10.1.5 → 10.1.7

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.
Files changed (79) hide show
  1. package/bin/smcat.mjs +5 -4
  2. package/dist/commonjs/bundle.js +67 -67
  3. package/{src → dist/esm}/cli/actions.mjs +24 -40
  4. package/dist/esm/cli/attributes-parser.mjs +981 -0
  5. package/dist/esm/cli/file-name-to-stream.mjs +13 -0
  6. package/dist/esm/cli/make-description.mjs +21 -0
  7. package/dist/esm/cli/normalize.mjs +95 -0
  8. package/dist/esm/cli/validations.mjs +70 -0
  9. package/dist/esm/index-node.mjs +16 -0
  10. package/dist/esm/index.mjs +19 -0
  11. package/dist/esm/options.mjs +61 -0
  12. package/dist/esm/parse/index.mjs +27 -0
  13. package/dist/esm/parse/parser-helpers.mjs +175 -0
  14. package/dist/esm/parse/scxml/index.mjs +136 -0
  15. package/dist/esm/parse/scxml/normalize-machine.mjs +44 -0
  16. package/dist/esm/parse/smcat/smcat-parser.mjs +2924 -0
  17. package/dist/esm/parse/smcat-ast.schema.mjs +170 -0
  18. package/dist/esm/render/dot/attributebuilder.mjs +51 -0
  19. package/dist/esm/render/dot/counter.mjs +16 -0
  20. package/dist/esm/render/dot/dot.states.template.js +26 -0
  21. package/dist/esm/render/dot/dot.template.js +14 -0
  22. package/dist/esm/render/dot/index.mjs +106 -0
  23. package/dist/esm/render/dot/render-dot-from-ast.js +29 -0
  24. package/dist/esm/render/dot/state-transformers.mjs +113 -0
  25. package/dist/esm/render/dot/transition-transformers.mjs +49 -0
  26. package/dist/esm/render/dot/utl.mjs +29 -0
  27. package/dist/esm/render/index-node.mjs +28 -0
  28. package/dist/esm/render/index.mjs +20 -0
  29. package/dist/esm/render/scjson/index.mjs +121 -0
  30. package/dist/esm/render/scjson/make-valid-event-names.mjs +27 -0
  31. package/dist/esm/render/scjson/make-valid-xml-name.mjs +19 -0
  32. package/dist/esm/render/scxml/index.mjs +4 -0
  33. package/dist/esm/render/scxml/render-from-scjson.js +9 -0
  34. package/dist/esm/render/scxml/scxml.states.template.js +14 -0
  35. package/dist/esm/render/scxml/scxml.template.js +6 -0
  36. package/dist/esm/render/smcat/index.js +63 -0
  37. package/dist/esm/render/smcat/smcat.template.js +13 -0
  38. package/dist/esm/render/vector/dot-to-vector-native.mjs +35 -0
  39. package/dist/esm/render/vector/vector-native-dot-with-fallback.mjs +37 -0
  40. package/dist/esm/render/vector/vector-with-viz-js.mjs +13 -0
  41. package/dist/esm/state-machine-model.mjs +58 -0
  42. package/dist/esm/transform/desugar.mjs +77 -0
  43. package/dist/esm/transform/utl.mjs +16 -0
  44. package/dist/esm/version.mjs +1 -0
  45. package/package.json +36 -45
  46. package/src/render/dot/dot.states.template.js +1 -1
  47. package/src/render/dot/index.mjs +14 -14
  48. package/types/state-machine-cat.d.ts +4 -4
  49. package/src/cli/file-name-to-stream.mjs +0 -25
  50. package/src/cli/make-description.mjs +0 -45
  51. package/src/cli/normalize.mjs +0 -204
  52. package/src/cli/validations.mjs +0 -140
  53. package/src/index-node.mjs +0 -55
  54. package/src/index.mjs +0 -58
  55. package/src/options.mjs +0 -78
  56. package/src/parse/index.mjs +0 -47
  57. package/src/parse/parser-helpers.mjs +0 -297
  58. package/src/parse/scxml/normalize-machine.mjs +0 -73
  59. package/src/parse/smcat-ast.schema.mjs +0 -187
  60. package/src/render/dot/attributebuilder.mjs +0 -68
  61. package/src/render/dot/counter.mjs +0 -33
  62. package/src/render/dot/render-dot-from-ast.js +0 -70
  63. package/src/render/dot/state-transformers.mjs +0 -128
  64. package/src/render/dot/transition-transformers.mjs +0 -52
  65. package/src/render/dot/utl.mjs +0 -56
  66. package/src/render/index-node.mjs +0 -36
  67. package/src/render/index.mjs +0 -22
  68. package/src/render/scjson/index.mjs +0 -175
  69. package/src/render/scjson/make-valid-event-names.mjs +0 -71
  70. package/src/render/scjson/make-valid-xml-name.mjs +0 -70
  71. package/src/render/scxml/index.mjs +0 -8
  72. package/src/render/smcat/index.js +0 -129
  73. package/src/render/vector/dot-to-vector-native.mjs +0 -60
  74. package/src/render/vector/vector-native-dot-with-fallback.mjs +0 -55
  75. package/src/render/vector/vector-with-viz-js.mjs +0 -18
  76. package/src/state-machine-model.mjs +0 -124
  77. package/src/transform/desugar.mjs +0 -222
  78. package/src/transform/utl.mjs +0 -26
  79. package/src/version.mjs +0 -2
@@ -0,0 +1,13 @@
1
+ import fs from "node:fs";
2
+ export function getOutStream(pOutputTo) {
3
+ if ("-" === pOutputTo) {
4
+ return process.stdout;
5
+ }
6
+ return fs.createWriteStream(pOutputTo);
7
+ }
8
+ export function getInStream(pInputFrom) {
9
+ if ("-" === pInputFrom) {
10
+ return process.stdin;
11
+ }
12
+ return fs.createReadStream(pInputFrom);
13
+ }
@@ -0,0 +1,21 @@
1
+ import chalk from "chalk";
2
+ import indentString from "indent-string";
3
+ import wrapAnsi from "wrap-ansi";
4
+ import dotToVectorNative from "../render/vector/dot-to-vector-native.mjs";
5
+ function wrapAndIndent(pString) {
6
+ const lDogmaticMaxConsoleWidth = 78;
7
+ const lDefaultIndent = 2;
8
+ const lMaxWidth = lDogmaticMaxConsoleWidth - lDefaultIndent;
9
+ return indentString(wrapAnsi(pString, lMaxWidth), lDefaultIndent);
10
+ }
11
+ export default (pDotIsAvailable = dotToVectorNative.isAvailable({})) => {
12
+ const lDescription = "Write beautiful state charts - https://github.com/sverweij/state-machine-cat";
13
+ const lNode12Warning = "When you want to output svg and the native GraphViz isn't installed, " +
14
+ "state-machine-cat will fall back to viz.js and you might see " +
15
+ `${chalk.italic("'Invalid asm.js: Function definition doesn't match use'")}. ` +
16
+ "It's harmless and your svg will come out ok. See " +
17
+ `https://github.com/sverweij/state-machine-cat/blob/develop/docs/faq.md#viz`;
18
+ return indentString(pDotIsAvailable
19
+ ? lDescription
20
+ : `${lDescription}\n\n${wrapAndIndent(chalk.dim(lNode12Warning))}`);
21
+ };
@@ -0,0 +1,95 @@
1
+ import path from "node:path";
2
+ import options from "../options.mjs";
3
+ import { parse as parseAttributes } from "./attributes-parser.mjs";
4
+ const INPUT_EXTENSIONS = {
5
+ ".smcat": "smcat",
6
+ ".scxml": "scxml",
7
+ ".xml": "scxml",
8
+ ".json": "json",
9
+ ".ast": "json",
10
+ };
11
+ const OUTPUT_EXTENSIONS = {
12
+ ".ast": "json",
13
+ ".dot": "dot",
14
+ ".eps": "eps",
15
+ ".json": "json",
16
+ ".pdf": "pdf",
17
+ ".png": "png",
18
+ ".ps": "ps",
19
+ ".ps2": "ps2",
20
+ ".scjson": "scjson",
21
+ ".scxml": "scxml",
22
+ ".smcat": "smcat",
23
+ ".svg": "svg",
24
+ };
25
+ function classifyExtension(pString, pExtensionMap, pDefault) {
26
+ return pExtensionMap[path.extname(pString)] || pDefault;
27
+ }
28
+ function outputType2Extension(pOutputType) {
29
+ const lExceptions = {
30
+ oldeps: "eps",
31
+ oldps: "ps",
32
+ oldps2: "ps",
33
+ oldsvg: "svg",
34
+ ps2: "ps",
35
+ };
36
+ return lExceptions[pOutputType] || pOutputType;
37
+ }
38
+ function deriveOutputFromInput(pInputFrom, pOutputType) {
39
+ const lExtension = outputType2Extension(pOutputType);
40
+ if (!pInputFrom || "-" === pInputFrom) {
41
+ return "-";
42
+ }
43
+ return path
44
+ .join(path.dirname(pInputFrom), path.basename(pInputFrom, path.extname(pInputFrom)))
45
+ .concat(".")
46
+ .concat(lExtension);
47
+ }
48
+ function determineOutputTo(pOutputTo, pInputFrom, pOutputType) {
49
+ return pOutputTo ? pOutputTo : deriveOutputFromInput(pInputFrom, pOutputType);
50
+ }
51
+ function determineInputType(pInputFrom, pInputType) {
52
+ if (pInputType) {
53
+ return pInputType;
54
+ }
55
+ return classifyExtension(pInputFrom, INPUT_EXTENSIONS, options.getAllowedValues().inputType.default);
56
+ }
57
+ function determineOutputType(pOutputTo, pOutputType) {
58
+ if (pOutputType) {
59
+ return pOutputType;
60
+ }
61
+ if (pOutputTo) {
62
+ return classifyExtension(pOutputTo, OUTPUT_EXTENSIONS, options.getAllowedValues().outputType.default);
63
+ }
64
+ return options.getAllowedValues().outputType.default;
65
+ }
66
+ function determineParameter(pOptions, pParameter) {
67
+ return Object.prototype.hasOwnProperty.call(pOptions, pParameter)
68
+ ?
69
+ pOptions[pParameter]
70
+ :
71
+ options.getAllowedValues()[pParameter].default;
72
+ }
73
+ function determineDotAttributes(pOptions, pDotAttributes) {
74
+ return Boolean(pOptions?.[pDotAttributes]) &&
75
+ typeof pOptions[pDotAttributes] === "string"
76
+ ? parseAttributes(pOptions[pDotAttributes])
77
+ : [];
78
+ }
79
+ export default function normalize(pArgument = "-", pLooseOptions = {}) {
80
+ const lNormalizedInputFrom = pArgument || "-";
81
+ const lNormalizedInputType = determineInputType(lNormalizedInputFrom, pLooseOptions.inputType);
82
+ const lNormalizedOutputType = determineOutputType(pLooseOptions.outputTo, pLooseOptions.outputType);
83
+ return {
84
+ inputFrom: lNormalizedInputFrom,
85
+ inputType: lNormalizedInputType,
86
+ outputType: lNormalizedOutputType,
87
+ outputTo: determineOutputTo(pLooseOptions.outputTo, lNormalizedInputFrom, lNormalizedOutputType),
88
+ engine: determineParameter(pLooseOptions, "engine"),
89
+ direction: determineParameter(pLooseOptions, "direction"),
90
+ dotGraphAttrs: determineDotAttributes(pLooseOptions, "dotGraphAttrs"),
91
+ dotNodeAttrs: determineDotAttributes(pLooseOptions, "dotNodeAttrs"),
92
+ dotEdgeAttrs: determineDotAttributes(pLooseOptions, "dotEdgeAttrs"),
93
+ desugar: pLooseOptions?.desugar ?? false,
94
+ };
95
+ }
@@ -0,0 +1,70 @@
1
+ import fs from "node:fs";
2
+ import smcat from "../index-node.mjs";
3
+ import { parse as parseAttributes } from "./attributes-parser.mjs";
4
+ const allowedValues = smcat.getAllowedValues();
5
+ function getName(pValue) {
6
+ return pValue.name;
7
+ }
8
+ const VALID_OUTPUT_TYPES = allowedValues.outputType.values.map(getName);
9
+ const VALID_INPUT_TYPES = allowedValues.inputType.values.map(getName);
10
+ const VALID_ENGINES = allowedValues.engine.values.map(getName);
11
+ const VALID_DIRECTIONS = allowedValues.direction.values.map(getName);
12
+ function isStdout(pFilename) {
13
+ return "-" === pFilename;
14
+ }
15
+ function fileExists(pFilename) {
16
+ try {
17
+ if (!isStdout(pFilename)) {
18
+ fs.accessSync(pFilename, fs.constants.R_OK);
19
+ }
20
+ return true;
21
+ }
22
+ catch (pError) {
23
+ return false;
24
+ }
25
+ }
26
+ function validOption(pOption, pValidValues, pError) {
27
+ if (pValidValues.includes(pOption)) {
28
+ return pOption;
29
+ }
30
+ throw new Error(pError);
31
+ }
32
+ export default {
33
+ validOutputType: (pType) => validOption(pType, VALID_OUTPUT_TYPES, `\n error: '${pType}' is not a valid output type. smcat can emit:` +
34
+ `\n ${VALID_OUTPUT_TYPES.join(", ")}\n\n`),
35
+ validInputType: (pType) => validOption(pType, VALID_INPUT_TYPES, `\n error: '${pType}' is not a valid input type.` +
36
+ `\n smcat can read ${VALID_INPUT_TYPES.join(", ")}\n\n`),
37
+ validEngine: (pEngine) => validOption(pEngine, VALID_ENGINES, `\n error: '${pEngine}' is not a valid input type.` +
38
+ `\n you can choose from ${VALID_ENGINES.join(", ")}\n\n`),
39
+ validDirection: (pDirection) => validOption(pDirection, VALID_DIRECTIONS, `\n error: '${pDirection}' is not a valid direction.` +
40
+ `\n you can choose from ${VALID_DIRECTIONS.join(", ")}\n\n`),
41
+ validDotAttrs: (pDotAttributes) => {
42
+ try {
43
+ parseAttributes(pDotAttributes);
44
+ return pDotAttributes;
45
+ }
46
+ catch (pError) {
47
+ throw new Error(`Invalid dot attributes: ${pError.message}`);
48
+ }
49
+ },
50
+ validateArguments(pOptions) {
51
+ if (!pOptions.inputFrom) {
52
+ throw new Error(`\n error: Please specify an input file.\n\n`);
53
+ }
54
+ if (!pOptions.outputTo) {
55
+ throw new Error(`\n error: Please specify an output file.\n\n`);
56
+ }
57
+ if (!fileExists(pOptions.inputFrom)) {
58
+ throw new Error(`\n error: Failed to open input file '${pOptions.inputFrom}'\n\n`);
59
+ }
60
+ return pOptions;
61
+ },
62
+ validOutputTypeRE: VALID_OUTPUT_TYPES.join("|"),
63
+ defaultOutputType: allowedValues.outputType.default,
64
+ validInputTypeRE: VALID_INPUT_TYPES.join("|"),
65
+ defaultInputType: allowedValues.inputType.default,
66
+ validEngineRE: VALID_ENGINES.join("|"),
67
+ defaultEngine: allowedValues.engine.default,
68
+ validDirectionRE: VALID_DIRECTIONS.join("|"),
69
+ defaultDirection: allowedValues.direction.default,
70
+ };
@@ -0,0 +1,16 @@
1
+ import options from "./options.mjs";
2
+ import parse from "./parse/index.mjs";
3
+ import desugar from "./transform/desugar.mjs";
4
+ import getRenderFunction from "./render/index-node.mjs";
5
+ import { version } from "./version.mjs";
6
+ export default {
7
+ render(pScript, pOptions) {
8
+ const lStateMachine = parse.getAST(pScript, pOptions);
9
+ const lDesugar = options.getOptionValue(pOptions, "desugar");
10
+ return getRenderFunction(options.getOptionValue(pOptions, "outputType"))(lDesugar ? desugar(lStateMachine) : lStateMachine, pOptions);
11
+ },
12
+ version,
13
+ getAllowedValues() {
14
+ return options.getAllowedValues();
15
+ },
16
+ };
@@ -0,0 +1,19 @@
1
+ import options from "./options.mjs";
2
+ import parse from "./parse/index.mjs";
3
+ import desugar from "./transform/desugar.mjs";
4
+ import getRenderFunction from "./render/index.mjs";
5
+ import { version as _version } from "./version.mjs";
6
+ export function render(pScript, pOptions) {
7
+ const lStateMachine = parse.getAST(pScript, pOptions);
8
+ const lDesugar = options.getOptionValue(pOptions, "desugar");
9
+ return getRenderFunction(options.getOptionValue(pOptions, "outputType"))(lDesugar ? desugar(lStateMachine) : lStateMachine, pOptions);
10
+ }
11
+ export const version = _version;
12
+ export function getAllowedValues() {
13
+ return options.getAllowedValues();
14
+ }
15
+ export default {
16
+ render,
17
+ version,
18
+ getAllowedValues,
19
+ };
@@ -0,0 +1,61 @@
1
+ const ALLOWED_VALUES = Object.freeze({
2
+ inputType: {
3
+ default: "smcat",
4
+ values: [{ name: "smcat" }, { name: "json" }, { name: "scxml" }],
5
+ },
6
+ outputType: {
7
+ default: "svg",
8
+ values: [
9
+ { name: "ast" },
10
+ { name: "dot" },
11
+ { name: "eps" },
12
+ { name: "json" },
13
+ { name: "oldeps" },
14
+ { name: "oldps" },
15
+ { name: "oldps2" },
16
+ { name: "oldsvg" },
17
+ { name: "pdf" },
18
+ { name: "png" },
19
+ { name: "ps" },
20
+ { name: "ps2" },
21
+ { name: "scjson" },
22
+ { name: "scxml" },
23
+ { name: "smcat" },
24
+ { name: "svg" },
25
+ ],
26
+ },
27
+ engine: {
28
+ default: "dot",
29
+ values: [
30
+ { name: "dot" },
31
+ { name: "circo" },
32
+ { name: "fdp" },
33
+ { name: "neato" },
34
+ { name: "osage" },
35
+ { name: "twopi" },
36
+ ],
37
+ },
38
+ direction: {
39
+ default: "top-down",
40
+ values: [
41
+ { name: "top-down" },
42
+ { name: "bottom-top" },
43
+ { name: "left-right" },
44
+ { name: "right-left" },
45
+ ],
46
+ },
47
+ desugar: {
48
+ default: false,
49
+ values: [{ name: true }, { name: false }],
50
+ },
51
+ });
52
+ function getOptionValue(pOptions, pOptionName) {
53
+ return pOptions?.[pOptionName] ?? ALLOWED_VALUES[pOptionName].default;
54
+ }
55
+ function getAllowedValues() {
56
+ return ALLOWED_VALUES;
57
+ }
58
+ export default {
59
+ getAllowedValues,
60
+ getOptionValue,
61
+ };
@@ -0,0 +1,27 @@
1
+ import Ajv from "ajv";
2
+ import options from "../options.mjs";
3
+ import { parse as parseSmCat } from "./smcat/smcat-parser.mjs";
4
+ import { parse as parseSCXML } from "./scxml/index.mjs";
5
+ import $schema from "./smcat-ast.schema.mjs";
6
+ const ajv = new Ajv();
7
+ function validateAgainstSchema(pSchema, pObject) {
8
+ if (!ajv.validate(pSchema, pObject)) {
9
+ throw new Error(`The provided JSON is not a valid state-machine-cat AST: ${ajv.errorsText()}.\n`);
10
+ }
11
+ }
12
+ export default {
13
+ getAST(pScript, pOptions) {
14
+ let lReturnValue = pScript;
15
+ if (options.getOptionValue(pOptions, "inputType") === "smcat") {
16
+ lReturnValue = parseSmCat(pScript);
17
+ }
18
+ else if (options.getOptionValue(pOptions, "inputType") === "scxml") {
19
+ lReturnValue = parseSCXML(pScript);
20
+ }
21
+ else if (typeof pScript === "string") {
22
+ lReturnValue = JSON.parse(pScript);
23
+ }
24
+ validateAgainstSchema($schema, lReturnValue);
25
+ return lReturnValue;
26
+ },
27
+ };
@@ -0,0 +1,175 @@
1
+ import StateMachineModel from "../state-machine-model.mjs";
2
+ const TRIGGER_RE_AS_A_STRING = "^(entry|activity|exit)\\s*/\\s*([^\\n$]*)(\\n|$)";
3
+ const TRIGGER_RE = new RegExp(TRIGGER_RE_AS_A_STRING);
4
+ function stateExists(pKnownStateNames, pName) {
5
+ return pKnownStateNames.includes(pName);
6
+ }
7
+ const RE2STATE_TYPE = [
8
+ {
9
+ re: /initial/,
10
+ stateType: "initial",
11
+ },
12
+ {
13
+ re: /final/,
14
+ stateType: "final",
15
+ },
16
+ {
17
+ re: /parallel/,
18
+ stateType: "parallel",
19
+ },
20
+ {
21
+ re: /(deep.*history)|(history.*deep)/,
22
+ stateType: "deephistory",
23
+ },
24
+ {
25
+ re: /history/,
26
+ stateType: "history",
27
+ },
28
+ {
29
+ re: /^\^.*/,
30
+ stateType: "choice",
31
+ },
32
+ {
33
+ re: /^].*/,
34
+ stateType: "forkjoin",
35
+ },
36
+ ];
37
+ function matches(pName) {
38
+ return (pEntry) => pEntry.re.test(pName);
39
+ }
40
+ function getStateType(pName) {
41
+ return (RE2STATE_TYPE.find(matches(pName)) || { stateType: "regular" })
42
+ .stateType;
43
+ }
44
+ function initState(pName) {
45
+ return {
46
+ name: pName,
47
+ type: getStateType(pName),
48
+ };
49
+ }
50
+ function isComposite(pState) {
51
+ return Boolean(pState.statemachine);
52
+ }
53
+ function getAlreadyDeclaredStates(pStateMachine) {
54
+ const lStates = pStateMachine?.states ?? [];
55
+ return lStates.filter(isComposite).reduce((pAllStateNames, pThisState) => pAllStateNames.concat(getAlreadyDeclaredStates(pThisState.statemachine)), lStates.map(({ name }) => name));
56
+ }
57
+ function extractUndeclaredStates(pStateMachine, pKnownStateNames) {
58
+ pKnownStateNames = pKnownStateNames
59
+ ? pKnownStateNames
60
+ : getAlreadyDeclaredStates(pStateMachine);
61
+ pStateMachine.states = pStateMachine?.states ?? [];
62
+ const lTransitions = pStateMachine?.transitions ?? [];
63
+ pStateMachine.states.filter(isComposite).forEach((pState) => {
64
+ pState.statemachine.states = extractUndeclaredStates(pState.statemachine, pKnownStateNames);
65
+ });
66
+ lTransitions.forEach((pTransition) => {
67
+ if (!stateExists(pKnownStateNames, pTransition.from)) {
68
+ pKnownStateNames.push(pTransition.from);
69
+ pStateMachine.states.push(initState(pTransition.from));
70
+ }
71
+ if (!stateExists(pKnownStateNames, pTransition.to)) {
72
+ pKnownStateNames.push(pTransition.to);
73
+ pStateMachine.states.push(initState(pTransition.to));
74
+ }
75
+ });
76
+ return pStateMachine.states;
77
+ }
78
+ function classifyForkJoin(pInComingCount, pOutGoingCount) {
79
+ let lReturnValue = "junction";
80
+ if (pInComingCount <= 1 && pOutGoingCount > 1) {
81
+ lReturnValue = "fork";
82
+ }
83
+ if (pInComingCount > 1 && pOutGoingCount <= 1) {
84
+ lReturnValue = "join";
85
+ }
86
+ return lReturnValue;
87
+ }
88
+ function classifyForkJoins(pStateMachine, pFlattenedStateMachineModel = new StateMachineModel(pStateMachine)) {
89
+ pStateMachine.states = pStateMachine.states.map((pState) => {
90
+ if (pState.type === "forkjoin" && !pState.typeExplicitlySet) {
91
+ const lInComingCount = pFlattenedStateMachineModel.findTransitionsByTo(pState.name).length;
92
+ const lOutGoingCount = pFlattenedStateMachineModel.findTransitionsByFrom(pState.name).length;
93
+ pState.type = classifyForkJoin(lInComingCount, lOutGoingCount);
94
+ }
95
+ if (pState.statemachine) {
96
+ pState.statemachine = classifyForkJoins(pState.statemachine, pFlattenedStateMachineModel);
97
+ }
98
+ return pState;
99
+ });
100
+ return pStateMachine;
101
+ }
102
+ function stateEqual(pStateOne, pStateTwo) {
103
+ return pStateOne.name === pStateTwo.name;
104
+ }
105
+ function uniq(pArray, pEqualFunction) {
106
+ return pArray.reduce((pBag, pMarble) => {
107
+ const lMarbleIndex = pBag.findIndex((pBagItem) => pEqualFunction(pBagItem, pMarble));
108
+ if (lMarbleIndex > -1) {
109
+ pBag[lMarbleIndex] = pMarble;
110
+ return pBag;
111
+ }
112
+ return pBag.concat(pMarble);
113
+ }, []);
114
+ }
115
+ function parseTransitionExpression(pString) {
116
+ const lTransitionExpressionRe = /([^[/]+)?(\[[^\]]+\])?[^/]*(\/.+)?/;
117
+ const lReturnValue = {};
118
+ const lMatchResult = pString.match(lTransitionExpressionRe);
119
+ const lEventPos = 1;
120
+ const lConditionPos = 2;
121
+ const lActionPos = 3;
122
+ if (lMatchResult[lEventPos]) {
123
+ lReturnValue.event = lMatchResult[lEventPos].trim();
124
+ }
125
+ if (lMatchResult[lConditionPos]) {
126
+ lReturnValue.cond = lMatchResult[lConditionPos].slice(1, -1).trim();
127
+ }
128
+ if (lMatchResult[lActionPos]) {
129
+ lReturnValue.action = lMatchResult[lActionPos]
130
+ .slice(1, lMatchResult[lActionPos].length)
131
+ .trim();
132
+ }
133
+ return lReturnValue;
134
+ }
135
+ function setIf(pObject, pProperty, pValue, pCondition = Boolean) {
136
+ if (pCondition(pValue)) {
137
+ pObject[pProperty] = pValue;
138
+ }
139
+ }
140
+ function setIfNotEmpty(pObject, pProperty, pValue) {
141
+ setIf(pObject, pProperty, pValue, (pX) => pX && pX.length > 0);
142
+ }
143
+ function extractAction(pActivityCandidate) {
144
+ const lMatch = pActivityCandidate.match(TRIGGER_RE);
145
+ const lTypePos = 1;
146
+ const lBodyPos = 2;
147
+ if (lMatch) {
148
+ return {
149
+ type: lMatch[lTypePos],
150
+ body: lMatch[lBodyPos],
151
+ };
152
+ }
153
+ return {
154
+ type: "activity",
155
+ body: pActivityCandidate,
156
+ };
157
+ }
158
+ function extractActions(pString) {
159
+ return pString
160
+ .split(/\n\s*/g)
161
+ .map((pActivityCandidate) => pActivityCandidate.trim())
162
+ .map(extractAction);
163
+ }
164
+ export default {
165
+ initState,
166
+ extractUndeclaredStates,
167
+ classifyForkJoins,
168
+ getStateType,
169
+ stateEqual,
170
+ uniq,
171
+ parseTransitionExpression,
172
+ extractActions,
173
+ setIf,
174
+ setIfNotEmpty,
175
+ };
@@ -0,0 +1,136 @@
1
+ import fastxml from "fast-xml-parser";
2
+ import he from "he";
3
+ import castArray from "lodash/castArray.js";
4
+ import utl from "../../transform/utl.mjs";
5
+ import parserHelpers from "../parser-helpers.mjs";
6
+ import { normalizeMachine } from "./normalize-machine.mjs";
7
+ const formatLabel = utl.formatLabel;
8
+ function extractActions(pState, pActionType) {
9
+ return castArray(pState[pActionType]).map((pAction) => ({
10
+ type: pActionType === "onexit" ? "exit" : "entry",
11
+ body: he.decode(pAction).trim(),
12
+ }));
13
+ }
14
+ function extractActionsFromInvokes(pInvokeTriggers) {
15
+ return castArray(pInvokeTriggers).map((pInvokeTrigger) => {
16
+ const lId = he.decode(pInvokeTrigger.id || "").trim();
17
+ return {
18
+ type: "activity",
19
+ body: lId || he.decode(pInvokeTrigger || "").trim(),
20
+ };
21
+ });
22
+ }
23
+ function deriveActions(pState) {
24
+ let lReturnValue = [];
25
+ if (pState.onentry) {
26
+ lReturnValue = lReturnValue.concat(extractActions(pState, "onentry"));
27
+ }
28
+ if (pState.invoke) {
29
+ lReturnValue = lReturnValue.concat(extractActionsFromInvokes(pState.invoke));
30
+ }
31
+ if (pState.onexit) {
32
+ lReturnValue = lReturnValue.concat(extractActions(pState, "onexit"));
33
+ }
34
+ return lReturnValue;
35
+ }
36
+ function deriveStateType(pType, pState) {
37
+ return pType === "history" && pState.type === "deep" ? "deephistory" : pType;
38
+ }
39
+ function mapState(pType) {
40
+ return (pState) => {
41
+ const lReturnValue = {
42
+ name: pState.id,
43
+ type: deriveStateType(pType, pState),
44
+ };
45
+ if (parserHelpers.getStateType(pState.id) !== lReturnValue.type) {
46
+ lReturnValue.typeExplicitlySet = true;
47
+ }
48
+ if (pState.onentry || pState.onexit || pState.invoke) {
49
+ lReturnValue.actions = deriveActions(pState);
50
+ }
51
+ if (Object.keys(pState).some((pKey) => ["initial", "state", "history", "parallel", "final"].includes(pKey))) {
52
+ lReturnValue.statemachine = mapMachine(pState);
53
+ }
54
+ return lReturnValue;
55
+ };
56
+ }
57
+ function extractTransitionAttributesFromObject(pTransition) {
58
+ const lReturnValue = {};
59
+ if (pTransition.event) {
60
+ lReturnValue.event = pTransition.event.split(/\s+/).join("\n");
61
+ }
62
+ if (pTransition.cond) {
63
+ lReturnValue.cond = pTransition.cond;
64
+ }
65
+ if (pTransition["#text"]) {
66
+ lReturnValue.action = he.decode(pTransition["#text"]).trim();
67
+ }
68
+ if (pTransition.type) {
69
+ lReturnValue.type = pTransition.type;
70
+ }
71
+ return lReturnValue;
72
+ }
73
+ function extractTransitionAttributes(pTransition) {
74
+ const lReturnValue = {};
75
+ if (typeof pTransition === "string") {
76
+ lReturnValue.action = he.decode(pTransition).trim();
77
+ }
78
+ else {
79
+ Object.assign(lReturnValue, extractTransitionAttributesFromObject(pTransition));
80
+ }
81
+ const lLabel = formatLabel(lReturnValue.event, lReturnValue.cond, lReturnValue.action);
82
+ if (lLabel) {
83
+ lReturnValue.label = lLabel;
84
+ }
85
+ return lReturnValue;
86
+ }
87
+ function reduceTransition(pState) {
88
+ return (pAllTransitions, pTransition) => {
89
+ const lTargets = (pTransition?.target ?? pState.id).split(/\s+/);
90
+ const lTransitionAttributes = extractTransitionAttributes(pTransition);
91
+ return pAllTransitions.concat(lTargets.map((pTarget) => ({
92
+ from: pState.id,
93
+ to: pTarget,
94
+ ...lTransitionAttributes,
95
+ })));
96
+ };
97
+ }
98
+ function extractTransitions(pStates) {
99
+ return pStates
100
+ .filter((pState) => Object.prototype.hasOwnProperty.call(pState, "transition"))
101
+ .reduce((pAllTransitions, pThisState) => pAllTransitions.concat(castArray(pThisState.transition).reduce(reduceTransition(pThisState), [])), []);
102
+ }
103
+ function mapMachine(pSCXMLStateMachine) {
104
+ const lNormalizedMachine = normalizeMachine(pSCXMLStateMachine);
105
+ const lReturnValue = {
106
+ states: lNormalizedMachine.initial
107
+ .map(mapState("initial"))
108
+ .concat(lNormalizedMachine.state.map(mapState("regular")))
109
+ .concat(lNormalizedMachine.parallel.map(mapState("parallel")))
110
+ .concat(lNormalizedMachine.history.map(mapState("history")))
111
+ .concat(lNormalizedMachine.final.map(mapState("final"))),
112
+ };
113
+ const lTransitions = extractTransitions(lNormalizedMachine.initial)
114
+ .concat(extractTransitions(lNormalizedMachine.state))
115
+ .concat(extractTransitions(lNormalizedMachine.parallel));
116
+ if (lTransitions.length > 0) {
117
+ lReturnValue.transitions = lTransitions;
118
+ }
119
+ return lReturnValue;
120
+ }
121
+ export function parse(pSCXMLString) {
122
+ const lTrimmedSCXMLString = pSCXMLString.trim();
123
+ if (fastxml.validate(lTrimmedSCXMLString) === true) {
124
+ const lXMLAsJSON = fastxml.parse(lTrimmedSCXMLString, {
125
+ attributeNamePrefix: "",
126
+ ignoreAttributes: false,
127
+ tagValueProcessor: (pTagValue) => he.decode(pTagValue),
128
+ stopNodes: ["onentry", "onexit", "transition"],
129
+ });
130
+ return mapMachine(lXMLAsJSON?.scxml ?? {
131
+ xmlns: "http://www.w3.org/2005/07/scxml",
132
+ version: "1.0",
133
+ });
134
+ }
135
+ throw new Error("That doesn't look like valid xml ...\n");
136
+ }
@@ -0,0 +1,44 @@
1
+ import castArray from "lodash/castArray.js";
2
+ function normalizeInitialFromObject(pInitialObject, pId) {
3
+ const lReturnValue = {
4
+ id: pId ? `${pId}.initial` : "initial",
5
+ };
6
+ if (pInitialObject.transition) {
7
+ Object.assign(lReturnValue, {
8
+ transition: [pInitialObject.transition],
9
+ });
10
+ }
11
+ return lReturnValue;
12
+ }
13
+ function normalizeInitialFromString(pString) {
14
+ return {
15
+ id: "initial",
16
+ transition: [
17
+ {
18
+ target: pString,
19
+ },
20
+ ],
21
+ };
22
+ }
23
+ function normalizeInitial(pMachine) {
24
+ const lReturnValue = [];
25
+ if (pMachine.initial) {
26
+ if (typeof pMachine.initial === "string") {
27
+ lReturnValue.push(normalizeInitialFromString(pMachine.initial));
28
+ }
29
+ else {
30
+ lReturnValue.push(normalizeInitialFromObject(pMachine.initial, pMachine.id));
31
+ }
32
+ }
33
+ return lReturnValue;
34
+ }
35
+ export function normalizeMachine(pMachine) {
36
+ return {
37
+ ...pMachine,
38
+ initial: normalizeInitial(pMachine),
39
+ state: castArray(pMachine?.state ?? []),
40
+ parallel: castArray(pMachine?.parallel ?? []),
41
+ history: castArray(pMachine?.history ?? []),
42
+ final: castArray(pMachine?.final ?? []),
43
+ };
44
+ }