state-machine-cat 10.1.2 → 10.1.5
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/README.md +1 -1
- package/bin/smcat.mjs +5 -0
- package/dist/commonjs/bundle.js +64 -64
- package/package.json +40 -69
- package/src/cli/actions.mjs +9 -0
- package/src/cli/file-name-to-stream.mjs +5 -6
- package/src/cli/make-description.mjs +9 -0
- package/src/cli/normalize.mjs +100 -47
- package/src/cli/validations.mjs +27 -1
- package/src/index-node.mjs +15 -47
- package/src/index.mjs +9 -9
- package/src/options.mjs +19 -13
- package/src/parse/index.mjs +13 -0
- package/src/parse/parser-helpers.mjs +70 -6
- package/src/parse/scxml/index.mjs +72 -20
- package/src/parse/scxml/normalize-machine.mjs +27 -21
- package/src/render/dot/README.md +12 -0
- package/src/render/dot/attributebuilder.mjs +7 -0
- package/src/render/dot/counter.mjs +13 -0
- package/src/render/dot/index.mjs +66 -28
- package/src/render/dot/render-dot-from-ast.js +37 -15
- package/src/render/dot/state-transformers.mjs +4 -2
- package/src/render/dot/utl.mjs +20 -0
- package/src/render/index-node.mjs +9 -3
- package/src/render/index.mjs +3 -3
- package/src/render/scjson/index.mjs +6 -0
- package/src/render/scjson/make-valid-xml-name.mjs +7 -1
- package/src/render/scxml/index.mjs +2 -0
- package/src/render/smcat/index.js +48 -14
- package/src/render/vector/dot-to-vector-native.mjs +1 -1
- package/src/render/vector/vector-native-dot-with-fallback.mjs +3 -2
- package/src/render/vector/vector-with-viz-js.mjs +3 -2
- package/src/state-machine-model.mjs +46 -4
- package/src/transform/desugar.mjs +67 -18
- package/src/transform/utl.mjs +8 -0
- package/src/version.mjs +1 -1
- package/types/state-machine-cat.d.ts +41 -14
package/src/index-node.mjs
CHANGED
|
@@ -1,52 +1,18 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
import options from "./options.mjs";
|
|
2
3
|
import parse from "./parse/index.mjs";
|
|
3
4
|
import desugar from "./transform/desugar.mjs";
|
|
4
5
|
import getRenderFunction from "./render/index-node.mjs";
|
|
5
6
|
import { version } from "./version.mjs";
|
|
6
7
|
|
|
7
|
-
const KNOWN_OPTIONS = [
|
|
8
|
-
"outputType",
|
|
9
|
-
"inputType",
|
|
10
|
-
"engine",
|
|
11
|
-
"direction",
|
|
12
|
-
"dotNodeAttrs",
|
|
13
|
-
"dotEdgeAttrs",
|
|
14
|
-
"desugar",
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
function isKnownOption(pKnownOptions) {
|
|
18
|
-
return (pCandidateString) => pKnownOptions.includes(pCandidateString);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Remove all attributes from the input object (which'd typically be
|
|
23
|
-
* originating from commander) that are not known options options so
|
|
24
|
-
* a clean object can be passed through.
|
|
25
|
-
*
|
|
26
|
-
* @param {any} pOptions - an options object e.g. as output from commander
|
|
27
|
-
* @param {string[]} pKnownOptions - a list of known options
|
|
28
|
-
* @return {any} - an options object that only contains stuff we care about
|
|
29
|
-
*/
|
|
30
|
-
function ejectUnknownOptions(pOptions, pKnownOptions) {
|
|
31
|
-
return Object.keys(pOptions)
|
|
32
|
-
.filter(isKnownOption(pKnownOptions))
|
|
33
|
-
.reduce((pAll, pKey) => {
|
|
34
|
-
// eslint-disable-next-line security/detect-object-injection
|
|
35
|
-
pAll[pKey] = pOptions[pKey];
|
|
36
|
-
return pAll;
|
|
37
|
-
}, {});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
8
|
export default {
|
|
41
9
|
/**
|
|
42
|
-
* Translates the input script to an
|
|
10
|
+
* Translates the input script to an output-script.
|
|
43
11
|
*
|
|
44
|
-
* @param {string} pScript
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* string with the rendered content if
|
|
49
|
-
* no callback was passed and no error was found
|
|
12
|
+
* @param {string|import("../types/state-machine-cat").IStateMachine} pScript
|
|
13
|
+
* The script to translate
|
|
14
|
+
* @param {import("../types/state-machine-cat").IRenderOptions} pOptions
|
|
15
|
+
* options influencing parsing & rendering.
|
|
50
16
|
* @throws {Error} if an error occurred and no callback
|
|
51
17
|
* function was passed: the error
|
|
52
18
|
*
|
|
@@ -54,13 +20,15 @@ export default {
|
|
|
54
20
|
*
|
|
55
21
|
*/
|
|
56
22
|
render(pScript, pOptions) {
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const lDesugar = options.getOptionValue(lOptions, "desugar");
|
|
23
|
+
const lStateMachine = parse.getAST(pScript, pOptions);
|
|
24
|
+
const lDesugar = options.getOptionValue(pOptions, "desugar");
|
|
60
25
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
26
|
+
// @ts-expect-error should be cast to OutputTypeType - or the getOptionValue
|
|
27
|
+
// function should be refactored to be more explicit in type it returns
|
|
28
|
+
// when we ask for outputType
|
|
29
|
+
return getRenderFunction(options.getOptionValue(pOptions, "outputType"))(
|
|
30
|
+
lDesugar ? desugar(lStateMachine) : lStateMachine,
|
|
31
|
+
pOptions
|
|
64
32
|
);
|
|
65
33
|
},
|
|
66
34
|
|
|
@@ -79,7 +47,7 @@ export default {
|
|
|
79
47
|
* - the possible values in an array of objects, each of which
|
|
80
48
|
* has the properties:
|
|
81
49
|
* - name: the value
|
|
82
|
-
*
|
|
50
|
+
* @returns {import("../types/state-machine-cat").IAllowedValues}
|
|
83
51
|
*/
|
|
84
52
|
getAllowedValues() {
|
|
85
53
|
return options.getAllowedValues();
|
package/src/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
/* eslint-disable budapestian/global-constant-pattern */
|
|
2
3
|
import options from "./options.mjs";
|
|
3
4
|
import parse from "./parse/index.mjs";
|
|
@@ -8,12 +9,11 @@ import { version as _version } from "./version.mjs";
|
|
|
8
9
|
/**
|
|
9
10
|
* Translates the input script to an output-script.
|
|
10
11
|
*
|
|
11
|
-
* @param {string} pScript
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* no callback was passed and no error was found
|
|
12
|
+
* @param {string|import("../types/state-machine-cat").IStateMachine} pScript
|
|
13
|
+
* The script to translate
|
|
14
|
+
* @param {import("../types/state-machine-cat").IRenderOptions} pOptions
|
|
15
|
+
* options influencing parsing & rendering.
|
|
16
|
+
* @return {string}
|
|
17
17
|
* @throws {Error} if an error occurred and no callback
|
|
18
18
|
* function was passed: the error
|
|
19
19
|
*
|
|
@@ -21,11 +21,11 @@ import { version as _version } from "./version.mjs";
|
|
|
21
21
|
*
|
|
22
22
|
*/
|
|
23
23
|
export function render(pScript, pOptions) {
|
|
24
|
-
const
|
|
24
|
+
const lStateMachine = parse.getAST(pScript, pOptions);
|
|
25
25
|
const lDesugar = options.getOptionValue(pOptions, "desugar");
|
|
26
26
|
|
|
27
27
|
return getRenderFunction(options.getOptionValue(pOptions, "outputType"))(
|
|
28
|
-
lDesugar ? desugar(
|
|
28
|
+
lDesugar ? desugar(lStateMachine) : lStateMachine,
|
|
29
29
|
pOptions
|
|
30
30
|
);
|
|
31
31
|
}
|
|
@@ -45,7 +45,7 @@ export const version = _version;
|
|
|
45
45
|
* - the possible values in an array of objects, each of which
|
|
46
46
|
* has the properties:
|
|
47
47
|
* - name: the value
|
|
48
|
-
*
|
|
48
|
+
* @returns {import("../types/state-machine-cat").IAllowedValues}
|
|
49
49
|
*/
|
|
50
50
|
export function getAllowedValues() {
|
|
51
51
|
return options.getAllowedValues();
|
package/src/options.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @type {import("../types/state-machine-cat").IAllowedValues} */
|
|
1
3
|
const ALLOWED_VALUES = Object.freeze({
|
|
2
4
|
inputType: {
|
|
3
5
|
default: "smcat",
|
|
@@ -6,21 +8,22 @@ const ALLOWED_VALUES = Object.freeze({
|
|
|
6
8
|
outputType: {
|
|
7
9
|
default: "svg",
|
|
8
10
|
values: [
|
|
9
|
-
{ name: "
|
|
10
|
-
{ name: "eps" },
|
|
11
|
-
{ name: "ps" },
|
|
12
|
-
{ name: "ps2" },
|
|
11
|
+
{ name: "ast" },
|
|
13
12
|
{ name: "dot" },
|
|
14
|
-
{ name: "
|
|
13
|
+
{ name: "eps" },
|
|
15
14
|
{ name: "json" },
|
|
16
|
-
{ name: "ast" },
|
|
17
|
-
{ name: "scxml" },
|
|
18
|
-
{ name: "oldsvg" },
|
|
19
|
-
{ name: "oldps2" },
|
|
20
15
|
{ name: "oldeps" },
|
|
21
|
-
{ name: "
|
|
16
|
+
{ name: "oldps" },
|
|
17
|
+
{ name: "oldps2" },
|
|
18
|
+
{ name: "oldsvg" },
|
|
22
19
|
{ name: "pdf" },
|
|
23
20
|
{ name: "png" },
|
|
21
|
+
{ name: "ps" },
|
|
22
|
+
{ name: "ps2" },
|
|
23
|
+
{ name: "scjson" },
|
|
24
|
+
{ name: "scxml" },
|
|
25
|
+
{ name: "smcat" },
|
|
26
|
+
{ name: "svg" },
|
|
24
27
|
],
|
|
25
28
|
},
|
|
26
29
|
engine: {
|
|
@@ -53,15 +56,18 @@ const ALLOWED_VALUES = Object.freeze({
|
|
|
53
56
|
* Returns the value for the option in the pOption object, and the default
|
|
54
57
|
* for that option in all other cases
|
|
55
58
|
*
|
|
56
|
-
* @param {
|
|
57
|
-
* @param {
|
|
58
|
-
* @return {
|
|
59
|
+
* @param {import("../types/state-machine-cat").IRenderOptions} pOptions - the options as passed in the api `render` function
|
|
60
|
+
* @param {keyof import("../types/state-machine-cat").IRenderOptions} pOptionName - the name of the option
|
|
61
|
+
* @return {string|boolean} value
|
|
59
62
|
*/
|
|
60
63
|
function getOptionValue(pOptions, pOptionName) {
|
|
61
64
|
// eslint-disable-next-line security/detect-object-injection
|
|
62
65
|
return pOptions?.[pOptionName] ?? ALLOWED_VALUES[pOptionName].default;
|
|
63
66
|
}
|
|
64
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @returns {import("../types/state-machine-cat").IAllowedValues}
|
|
70
|
+
*/
|
|
65
71
|
function getAllowedValues() {
|
|
66
72
|
return ALLOWED_VALUES;
|
|
67
73
|
}
|
package/src/parse/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
import Ajv from "ajv";
|
|
2
3
|
import options from "../options.mjs";
|
|
3
4
|
import { parse as parseSmCat } from "./smcat/smcat-parser.mjs";
|
|
@@ -6,6 +7,11 @@ import $schema from "./smcat-ast.schema.mjs";
|
|
|
6
7
|
|
|
7
8
|
const ajv = new Ajv();
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @param {typeof $schema} pSchema
|
|
12
|
+
* @param {any} pObject
|
|
13
|
+
* @throws {Error}
|
|
14
|
+
*/
|
|
9
15
|
function validateAgainstSchema(pSchema, pObject) {
|
|
10
16
|
if (!ajv.validate(pSchema, pObject)) {
|
|
11
17
|
throw new Error(
|
|
@@ -15,12 +21,18 @@ function validateAgainstSchema(pSchema, pObject) {
|
|
|
15
21
|
}
|
|
16
22
|
|
|
17
23
|
export default {
|
|
24
|
+
/**
|
|
25
|
+
* @param {string|import("../../types/state-machine-cat").IStateMachine} pScript
|
|
26
|
+
* @param {import("../../types/state-machine-cat").IRenderOptions} pOptions
|
|
27
|
+
* @returns {import("../../types/state-machine-cat").IStateMachine}
|
|
28
|
+
*/
|
|
18
29
|
getAST(pScript, pOptions) {
|
|
19
30
|
let lReturnValue = pScript;
|
|
20
31
|
|
|
21
32
|
if (options.getOptionValue(pOptions, "inputType") === "smcat") {
|
|
22
33
|
lReturnValue = parseSmCat(pScript);
|
|
23
34
|
} else if (options.getOptionValue(pOptions, "inputType") === "scxml") {
|
|
35
|
+
// @ts-expect-error inputType scxml => it's a string
|
|
24
36
|
lReturnValue = parseSCXML(pScript);
|
|
25
37
|
} else if (typeof pScript === "string") {
|
|
26
38
|
// json
|
|
@@ -29,6 +41,7 @@ export default {
|
|
|
29
41
|
|
|
30
42
|
validateAgainstSchema($schema, lReturnValue);
|
|
31
43
|
|
|
44
|
+
// @ts-expect-error by here lReturnValue is bound to be an IStateMachine
|
|
32
45
|
return lReturnValue;
|
|
33
46
|
},
|
|
34
47
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
/* eslint-disable security/detect-object-injection */
|
|
2
3
|
import StateMachineModel from "../state-machine-model.mjs";
|
|
3
4
|
|
|
@@ -6,10 +7,20 @@ const TRIGGER_RE_AS_A_STRING =
|
|
|
6
7
|
/* eslint security/detect-non-literal-regexp:0 */
|
|
7
8
|
const TRIGGER_RE = new RegExp(TRIGGER_RE_AS_A_STRING);
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @param {string[]} pKnownStateNames
|
|
12
|
+
* @param {string} pName
|
|
13
|
+
* @returns {boolean}
|
|
14
|
+
*/
|
|
9
15
|
function stateExists(pKnownStateNames, pName) {
|
|
10
16
|
return pKnownStateNames.includes(pName);
|
|
11
17
|
}
|
|
12
|
-
|
|
18
|
+
/**
|
|
19
|
+
* @type {{
|
|
20
|
+
* re:RegExp;
|
|
21
|
+
* stateType: import("../../types/state-machine-cat.js").StateType;
|
|
22
|
+
* }[]}
|
|
23
|
+
*/
|
|
13
24
|
const RE2STATE_TYPE = [
|
|
14
25
|
{
|
|
15
26
|
re: /initial/,
|
|
@@ -45,11 +56,19 @@ function matches(pName) {
|
|
|
45
56
|
return (pEntry) => pEntry.re.test(pName);
|
|
46
57
|
}
|
|
47
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @param {string} [pName]
|
|
61
|
+
* @returns {import("../../types/state-machine-cat.js").StateType}
|
|
62
|
+
*/
|
|
48
63
|
function getStateType(pName) {
|
|
49
64
|
return (RE2STATE_TYPE.find(matches(pName)) || { stateType: "regular" })
|
|
50
65
|
.stateType;
|
|
51
66
|
}
|
|
52
67
|
|
|
68
|
+
/**
|
|
69
|
+
* @param {string} pName
|
|
70
|
+
* @returns {import("../../types/state-machine-cat.js").IState}
|
|
71
|
+
*/
|
|
53
72
|
function initState(pName) {
|
|
54
73
|
return {
|
|
55
74
|
name: pName,
|
|
@@ -57,30 +76,45 @@ function initState(pName) {
|
|
|
57
76
|
};
|
|
58
77
|
}
|
|
59
78
|
|
|
79
|
+
/**
|
|
80
|
+
* @param {import("../../types/state-machine-cat.js").IState} pState
|
|
81
|
+
* @returns {boolean}
|
|
82
|
+
*/
|
|
60
83
|
function isComposite(pState) {
|
|
61
84
|
return Boolean(pState.statemachine);
|
|
62
85
|
}
|
|
63
86
|
|
|
87
|
+
/**
|
|
88
|
+
* @param {import("../../types/state-machine-cat.js").IStateMachine} [pStateMachine]
|
|
89
|
+
* @returns {string[]}
|
|
90
|
+
*/
|
|
64
91
|
function getAlreadyDeclaredStates(pStateMachine) {
|
|
65
|
-
const lStates = pStateMachine
|
|
92
|
+
const lStates = pStateMachine?.states ?? [];
|
|
66
93
|
|
|
67
94
|
return lStates.filter(isComposite).reduce(
|
|
68
95
|
(pAllStateNames, pThisState) =>
|
|
69
96
|
pAllStateNames.concat(getAlreadyDeclaredStates(pThisState.statemachine)),
|
|
70
|
-
lStates.map((
|
|
97
|
+
lStates.map(({ name }) => name)
|
|
71
98
|
);
|
|
72
99
|
}
|
|
73
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @param {import("../../types/state-machine-cat.js").IStateMachine} pStateMachine
|
|
103
|
+
* @param {string[]} pKnownStateNames
|
|
104
|
+
* @returns {import("../../types/state-machine-cat.js").IState[]}
|
|
105
|
+
*/
|
|
74
106
|
function extractUndeclaredStates(pStateMachine, pKnownStateNames) {
|
|
75
107
|
pKnownStateNames = pKnownStateNames
|
|
76
108
|
? pKnownStateNames
|
|
77
109
|
: getAlreadyDeclaredStates(pStateMachine);
|
|
78
110
|
|
|
79
|
-
pStateMachine.states = pStateMachine
|
|
80
|
-
const lTransitions = pStateMachine
|
|
111
|
+
pStateMachine.states = pStateMachine?.states ?? [];
|
|
112
|
+
const lTransitions = pStateMachine?.transitions ?? [];
|
|
81
113
|
|
|
82
114
|
pStateMachine.states.filter(isComposite).forEach((pState) => {
|
|
115
|
+
// @ts-expect-error isComposite guarantees the statemachine attribute exists, TS doesn't understand that yet, though
|
|
83
116
|
pState.statemachine.states = extractUndeclaredStates(
|
|
117
|
+
// @ts-expect-error isComposite guarantees the statemachine attribute exists, TS doesn't understand that yet, though
|
|
84
118
|
pState.statemachine,
|
|
85
119
|
pKnownStateNames
|
|
86
120
|
);
|
|
@@ -99,7 +133,13 @@ function extractUndeclaredStates(pStateMachine, pKnownStateNames) {
|
|
|
99
133
|
return pStateMachine.states;
|
|
100
134
|
}
|
|
101
135
|
|
|
136
|
+
/**
|
|
137
|
+
* @param {number} pInComingCount
|
|
138
|
+
* @param {number} pOutGoingCount
|
|
139
|
+
* @returns {import("../../types/state-machine-cat.js").StateType}
|
|
140
|
+
*/
|
|
102
141
|
function classifyForkJoin(pInComingCount, pOutGoingCount) {
|
|
142
|
+
/** @type {import("../../types/state-machine-cat.js").StateType} */
|
|
103
143
|
let lReturnValue = "junction";
|
|
104
144
|
|
|
105
145
|
if (pInComingCount <= 1 && pOutGoingCount > 1) {
|
|
@@ -112,10 +152,16 @@ function classifyForkJoin(pInComingCount, pOutGoingCount) {
|
|
|
112
152
|
return lReturnValue;
|
|
113
153
|
}
|
|
114
154
|
|
|
155
|
+
/**
|
|
156
|
+
* @param {import("../../types/state-machine-cat.js").IStateMachine} pStateMachine
|
|
157
|
+
* @param {StateMachineModel} pFlattenedStateMachineModel
|
|
158
|
+
* @returns {import("../../types/state-machine-cat.js").IStateMachine}
|
|
159
|
+
*/
|
|
115
160
|
function classifyForkJoins(
|
|
116
161
|
pStateMachine,
|
|
117
162
|
pFlattenedStateMachineModel = new StateMachineModel(pStateMachine)
|
|
118
163
|
) {
|
|
164
|
+
// TODO: mutates parameter
|
|
119
165
|
pStateMachine.states = pStateMachine.states.map((pState) => {
|
|
120
166
|
if (pState.type === "forkjoin" && !pState.typeExplicitlySet) {
|
|
121
167
|
const lInComingCount = pFlattenedStateMachineModel.findTransitionsByTo(
|
|
@@ -139,6 +185,11 @@ function classifyForkJoins(
|
|
|
139
185
|
return pStateMachine;
|
|
140
186
|
}
|
|
141
187
|
|
|
188
|
+
/**
|
|
189
|
+
* @param {import("../../types/state-machine-cat.js").IState} pStateOne
|
|
190
|
+
* @param {import("../../types/state-machine-cat.js").IState} pStateTwo
|
|
191
|
+
* @returns {boolean}
|
|
192
|
+
*/
|
|
142
193
|
function stateEqual(pStateOne, pStateTwo) {
|
|
143
194
|
return pStateOne.name === pStateTwo.name;
|
|
144
195
|
}
|
|
@@ -158,12 +209,17 @@ function uniq(pArray, pEqualFunction) {
|
|
|
158
209
|
}, []);
|
|
159
210
|
}
|
|
160
211
|
|
|
212
|
+
/**
|
|
213
|
+
* @param {String} pString
|
|
214
|
+
* @returns {{event?: string; cond?: string; action?: string}}
|
|
215
|
+
*/
|
|
161
216
|
function parseTransitionExpression(pString) {
|
|
162
217
|
// eslint-disable-next-line security/detect-unsafe-regex, unicorn/no-unsafe-regex
|
|
163
218
|
const lTransitionExpressionRe = /([^[/]+)?(\[[^\]]+\])?[^/]*(\/.+)?/;
|
|
164
219
|
const lReturnValue = {};
|
|
165
220
|
|
|
166
|
-
|
|
221
|
+
/** @type {RegExpMatchArray} */
|
|
222
|
+
// @ts-expect-error match has no fallback because lTransitionExpressionRe will match
|
|
167
223
|
// any string (every part is optional)
|
|
168
224
|
const lMatchResult = pString.match(lTransitionExpressionRe);
|
|
169
225
|
const lEventPos = 1;
|
|
@@ -195,6 +251,10 @@ function setIfNotEmpty(pObject, pProperty, pValue) {
|
|
|
195
251
|
setIf(pObject, pProperty, pValue, (pX) => pX && pX.length > 0);
|
|
196
252
|
}
|
|
197
253
|
|
|
254
|
+
/**
|
|
255
|
+
* @param {string} pActivityCandidate
|
|
256
|
+
* @returns {{type: string; body: string}}
|
|
257
|
+
*/
|
|
198
258
|
function extractAction(pActivityCandidate) {
|
|
199
259
|
const lMatch = pActivityCandidate.match(TRIGGER_RE);
|
|
200
260
|
const lTypePos = 1;
|
|
@@ -212,6 +272,10 @@ function extractAction(pActivityCandidate) {
|
|
|
212
272
|
};
|
|
213
273
|
}
|
|
214
274
|
|
|
275
|
+
/**
|
|
276
|
+
* @param {string} pString
|
|
277
|
+
* @returns {{type: string; body: string}[]}
|
|
278
|
+
*/
|
|
215
279
|
function extractActions(pString) {
|
|
216
280
|
return pString
|
|
217
281
|
.split(/\n\s*/g)
|
|
@@ -4,7 +4,7 @@ import he from "he";
|
|
|
4
4
|
import castArray from "lodash/castArray.js";
|
|
5
5
|
import utl from "../../transform/utl.mjs";
|
|
6
6
|
import parserHelpers from "../parser-helpers.mjs";
|
|
7
|
-
import normalizeMachine from "./normalize-machine.mjs";
|
|
7
|
+
import { normalizeMachine } from "./normalize-machine.mjs";
|
|
8
8
|
|
|
9
9
|
const formatLabel = utl.formatLabel;
|
|
10
10
|
|
|
@@ -26,6 +26,15 @@ function extractActionsFromInvokes(pInvokeTriggers) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* @param {import("./scxml").INormalizedSCXMLState} pState
|
|
31
|
+
* @returns {{type: string; body: string;}[]}
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {import("./scxml").INormalizedSCXMLState} pState
|
|
36
|
+
* @returns {any[]}
|
|
37
|
+
*/
|
|
29
38
|
function deriveActions(pState) {
|
|
30
39
|
let lReturnValue = [];
|
|
31
40
|
|
|
@@ -43,12 +52,24 @@ function deriveActions(pState) {
|
|
|
43
52
|
return lReturnValue;
|
|
44
53
|
}
|
|
45
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @param {import("../../..").StateType} pType
|
|
57
|
+
* param {import("./scxml").ISCXMLHistoryState} pState
|
|
58
|
+
* @param {any} pState
|
|
59
|
+
* @returns {import("../../..").StateType}
|
|
60
|
+
*/
|
|
46
61
|
function deriveStateType(pType, pState) {
|
|
47
62
|
return pType === "history" && pState.type === "deep" ? "deephistory" : pType;
|
|
48
63
|
}
|
|
49
64
|
|
|
65
|
+
/**
|
|
66
|
+
* @param {import("../../../types/state-machine-cat").StateType} pType
|
|
67
|
+
* returns {(pState: import("./scxml").INormalizedSCXMLState) => import("../../..").IState}
|
|
68
|
+
* @returns {(any) => import("../../..").IState}
|
|
69
|
+
*/
|
|
50
70
|
function mapState(pType) {
|
|
51
71
|
return (pState) => {
|
|
72
|
+
/** @type {import("../../../types/state-machine-cat").IState} */
|
|
52
73
|
const lReturnValue = {
|
|
53
74
|
name: pState.id,
|
|
54
75
|
type: deriveStateType(pType, pState),
|
|
@@ -73,6 +94,10 @@ function mapState(pType) {
|
|
|
73
94
|
};
|
|
74
95
|
}
|
|
75
96
|
|
|
97
|
+
/**
|
|
98
|
+
* @param {import("./scxml").ISCXMLTransition} pTransition
|
|
99
|
+
* @returns {{event?: string; cond?: string; action?: string; type?: string;}}
|
|
100
|
+
*/
|
|
76
101
|
function extractTransitionAttributesFromObject(pTransition) {
|
|
77
102
|
const lReturnValue = {};
|
|
78
103
|
|
|
@@ -95,6 +120,11 @@ function extractTransitionAttributesFromObject(pTransition) {
|
|
|
95
120
|
return lReturnValue;
|
|
96
121
|
}
|
|
97
122
|
|
|
123
|
+
/**
|
|
124
|
+
*
|
|
125
|
+
* @param {import("./scxml").ISCXMLTransition} pTransition
|
|
126
|
+
* @returns {{action?: string; label?: string;event?: string; cond?: string; type?: string}}
|
|
127
|
+
*/
|
|
98
128
|
function extractTransitionAttributes(pTransition) {
|
|
99
129
|
const lReturnValue = {};
|
|
100
130
|
|
|
@@ -120,11 +150,19 @@ function extractTransitionAttributes(pTransition) {
|
|
|
120
150
|
return lReturnValue;
|
|
121
151
|
}
|
|
122
152
|
|
|
153
|
+
/**
|
|
154
|
+
* @param {import("./scxml").INormalizedSCXMLState} pState
|
|
155
|
+
*/
|
|
123
156
|
function reduceTransition(pState) {
|
|
157
|
+
/**
|
|
158
|
+
* @param {import("./scxml").ISCXMLTransition[]} pAllTransitions
|
|
159
|
+
* @param {import("./scxml").ISCXMLTransition} pTransition
|
|
160
|
+
* @returns {import("../../..").ITransition}
|
|
161
|
+
*/
|
|
124
162
|
return (pAllTransitions, pTransition) => {
|
|
125
163
|
// in SCXML spaces denote references to multiple states
|
|
126
164
|
// => split into multiple transitions
|
|
127
|
-
const lTargets = (pTransition
|
|
165
|
+
const lTargets = (pTransition?.target ?? pState.id).split(/\s+/);
|
|
128
166
|
const lTransitionAttributes = extractTransitionAttributes(pTransition);
|
|
129
167
|
|
|
130
168
|
return pAllTransitions.concat(
|
|
@@ -139,6 +177,10 @@ function reduceTransition(pState) {
|
|
|
139
177
|
};
|
|
140
178
|
}
|
|
141
179
|
|
|
180
|
+
/**
|
|
181
|
+
* @param {import("./scxml").INormalizedSCXMLState[]} pStates
|
|
182
|
+
* @returns {import("../../../types/state-machine-cat").ITransition[]}
|
|
183
|
+
*/
|
|
142
184
|
function extractTransitions(pStates) {
|
|
143
185
|
return pStates
|
|
144
186
|
.filter((pState) =>
|
|
@@ -156,20 +198,24 @@ function extractTransitions(pStates) {
|
|
|
156
198
|
);
|
|
157
199
|
}
|
|
158
200
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
.
|
|
167
|
-
|
|
168
|
-
|
|
201
|
+
/**
|
|
202
|
+
* @param {any} pSCXMLStateMachine
|
|
203
|
+
* @returns {import("../../..").IStateMachine}
|
|
204
|
+
*/
|
|
205
|
+
function mapMachine(pSCXMLStateMachine) {
|
|
206
|
+
const lNormalizedMachine = normalizeMachine(pSCXMLStateMachine);
|
|
207
|
+
const lReturnValue = {
|
|
208
|
+
states: lNormalizedMachine.initial
|
|
209
|
+
.map(mapState("initial"))
|
|
210
|
+
.concat(lNormalizedMachine.state.map(mapState("regular")))
|
|
211
|
+
.concat(lNormalizedMachine.parallel.map(mapState("parallel")))
|
|
212
|
+
.concat(lNormalizedMachine.history.map(mapState("history")))
|
|
213
|
+
.concat(lNormalizedMachine.final.map(mapState("final"))),
|
|
214
|
+
};
|
|
169
215
|
|
|
170
|
-
const lTransitions = extractTransitions(
|
|
171
|
-
.concat(extractTransitions(
|
|
172
|
-
.concat(extractTransitions(
|
|
216
|
+
const lTransitions = extractTransitions(lNormalizedMachine.initial)
|
|
217
|
+
.concat(extractTransitions(lNormalizedMachine.state))
|
|
218
|
+
.concat(extractTransitions(lNormalizedMachine.parallel));
|
|
173
219
|
|
|
174
220
|
if (lTransitions.length > 0) {
|
|
175
221
|
lReturnValue.transitions = lTransitions;
|
|
@@ -181,20 +227,26 @@ function mapMachine(pMachine) {
|
|
|
181
227
|
* Parses SCXML into a state machine AST.
|
|
182
228
|
*
|
|
183
229
|
* @param {string} pSCXMLString The SCXML to parse
|
|
184
|
-
* @returns {IStateMachine} state machine AST
|
|
230
|
+
* @returns {import("../../../types/state-machine-cat").IStateMachine} state machine AST
|
|
185
231
|
*/
|
|
186
232
|
export function parse(pSCXMLString) {
|
|
187
|
-
const
|
|
233
|
+
const lTrimmedSCXMLString = pSCXMLString.trim();
|
|
188
234
|
|
|
189
|
-
if (fastxml.validate(
|
|
190
|
-
|
|
235
|
+
if (fastxml.validate(lTrimmedSCXMLString) === true) {
|
|
236
|
+
/** @type {import("./scxml").ISCXMLAsJSON} */
|
|
237
|
+
const lXMLAsJSON = fastxml.parse(lTrimmedSCXMLString, {
|
|
191
238
|
attributeNamePrefix: "",
|
|
192
239
|
ignoreAttributes: false,
|
|
193
240
|
tagValueProcessor: (pTagValue) => he.decode(pTagValue),
|
|
194
241
|
stopNodes: ["onentry", "onexit", "transition"],
|
|
195
242
|
});
|
|
196
243
|
|
|
197
|
-
return mapMachine(
|
|
244
|
+
return mapMachine(
|
|
245
|
+
lXMLAsJSON?.scxml ?? {
|
|
246
|
+
xmlns: "http://www.w3.org/2005/07/scxml",
|
|
247
|
+
version: "1.0",
|
|
248
|
+
}
|
|
249
|
+
);
|
|
198
250
|
}
|
|
199
251
|
throw new Error("That doesn't look like valid xml ...\n");
|
|
200
252
|
}
|
|
@@ -1,61 +1,67 @@
|
|
|
1
1
|
import castArray from "lodash/castArray.js";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* @param {Partial<import("./scxml").ISCXMLInitialState>} pInitialObject
|
|
5
|
+
* @param {string} [pId]
|
|
6
|
+
* @returns {import("./scxml").ISCXMLInitialState}
|
|
7
|
+
*/
|
|
8
|
+
function normalizeInitialFromObject(pInitialObject, pId) {
|
|
4
9
|
const lReturnValue = {
|
|
5
10
|
// ensure the 'initial' state has a unique name
|
|
6
|
-
id:
|
|
11
|
+
id: pId ? `${pId}.initial` : "initial",
|
|
7
12
|
};
|
|
8
13
|
|
|
9
|
-
if (
|
|
14
|
+
if (pInitialObject.transition) {
|
|
10
15
|
Object.assign(lReturnValue, {
|
|
11
|
-
transition: [
|
|
16
|
+
transition: [pInitialObject.transition],
|
|
12
17
|
});
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
return lReturnValue;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} pString
|
|
25
|
+
* @returns {import("./scxml").ISCXMLInitialState}
|
|
26
|
+
*/
|
|
27
|
+
function normalizeInitialFromString(pString) {
|
|
19
28
|
return {
|
|
20
29
|
id: "initial",
|
|
21
30
|
transition: [
|
|
22
31
|
{
|
|
23
|
-
target:
|
|
32
|
+
target: pString,
|
|
24
33
|
},
|
|
25
34
|
],
|
|
26
35
|
};
|
|
27
36
|
}
|
|
28
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @param {any} pMachine
|
|
40
|
+
* @returns {import("./scxml").ISCXMLInitialState[]}
|
|
41
|
+
*/
|
|
29
42
|
function normalizeInitial(pMachine) {
|
|
30
43
|
const lReturnValue = [];
|
|
31
|
-
let lInitialObject = {};
|
|
32
44
|
|
|
33
45
|
if (pMachine.initial) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// looks like an odd corner case) we won't recognize the
|
|
37
|
-
// initial
|
|
38
|
-
// the initial.id shouldn't occur (not allowed in scxml
|
|
39
|
-
// land), but smcat scxml renderer generates it nonetheless
|
|
40
|
-
if (pMachine.initial.transition || pMachine.initial.id) {
|
|
41
|
-
lInitialObject = normalizeInitialFromObject(pMachine);
|
|
46
|
+
if (typeof pMachine.initial === "string") {
|
|
47
|
+
lReturnValue.push(normalizeInitialFromString(pMachine.initial));
|
|
42
48
|
} else {
|
|
43
|
-
|
|
49
|
+
lReturnValue.push(
|
|
50
|
+
normalizeInitialFromObject(pMachine.initial, pMachine.id)
|
|
51
|
+
);
|
|
44
52
|
}
|
|
45
|
-
lReturnValue.push(lInitialObject);
|
|
46
53
|
}
|
|
47
54
|
return lReturnValue;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
/**
|
|
51
58
|
* Massages SCXML-as-json to be uniform:
|
|
52
|
-
* -
|
|
53
59
|
*
|
|
54
60
|
* @param {any} pMachine SCXML-as-json state machine
|
|
55
|
-
* @returns {
|
|
56
|
-
*
|
|
61
|
+
* @returns {import("./scxml").INormalizedSCXMLMachine} Still an SCXML-as-json state machine,
|
|
62
|
+
* but more consistent and easier to use
|
|
57
63
|
*/
|
|
58
|
-
export
|
|
64
|
+
export function normalizeMachine(pMachine) {
|
|
59
65
|
return {
|
|
60
66
|
...pMachine,
|
|
61
67
|
initial: normalizeInitial(pMachine),
|