@scion-scxml/express-middleware 1.0.0 → 2.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/.swn ADDED
Binary file
package/.swo ADDED
Binary file
package/.swp ADDED
Binary file
package/CHANGES ADDED
@@ -0,0 +1,12 @@
1
+ == 2.0.0 ==
2
+
3
+ Modify API handleScxmlEvent to accept kwArgs. Allow handleScxmlEvent to accept an optional executionContext param
4
+
5
+ == 1.0.3 ==
6
+
7
+ Fix issue in scion express middleware: on initializing the new session, it was not waiting for the asynchronous database write to finish before calling the callback, so a subsequent event could be processed before the the serialized session was finished writing to the database
8
+
9
+
10
+ == 1.0.2 ==
11
+
12
+ Added support for automatic serialization to secondary storage (mongodb) of multi-level state machine on processing event and creation of new state machine instance.
package/lib/index.js CHANGED
@@ -1,6 +1,11 @@
1
1
  const path = require('path');
2
2
  const express = require('express')
3
3
  const monitorMiddlewareClient = require('@scion-scxml/monitor-middleware/client')
4
+ const {
5
+ initializeRootSessionToSerializeAutomaticallyOnBigStepEndAndInvokedSessionInitialized,
6
+ handleInterpreterBigStepEnd
7
+ } = require('./multilevel-state-machine-ser-des')
8
+ const uuid = require('uuid')
4
9
 
5
10
  /*
6
11
  * options:
@@ -8,7 +13,8 @@ const monitorMiddlewareClient = require('@scion-scxml/monitor-middleware/client'
8
13
  module.exports = function ({
9
14
  app,
10
15
  scxml,
11
- pathToScxmlSrcDir
16
+ pathToScxmlSrcDir,
17
+ db
12
18
  }){
13
19
 
14
20
  //set the mime type
@@ -16,10 +22,8 @@ module.exports = function ({
16
22
 
17
23
  //serve static files
18
24
  const scxmlSrcDirExpressMountPath = '/scion/static/src'
19
- app.use(scxmlSrcDirExpressMountPath, express.static(pathToScxmlSrcDir))
20
25
 
21
26
  const pathToDashboard = path.dirname(path.dirname(path.dirname(require.resolve('@scion-scxml/dashboard'))));
22
- app.use('/scion/static/dashboard', express.static(pathToDashboard))
23
27
 
24
28
  //init sse
25
29
  monitorMiddlewareClient.init(scxml, {broadcast})
@@ -27,20 +31,6 @@ module.exports = function ({
27
31
  let sseResponses = new Set();
28
32
  let messageCount = 0;
29
33
 
30
- //TODO: prefix this with "scion"
31
- app.get('/api/update-stream', function(req, res){
32
- res.writeHead(200, {
33
- 'Content-Type': 'text/event-stream',
34
- 'Cache-Control': 'no-cache',
35
- 'Connection': 'keep-alive'
36
- });
37
- res.write('\n');
38
- sseResponses.add(res);
39
- req.on("close", function() {
40
- sseResponses.delete(res);
41
- });
42
- });
43
-
44
34
  function broadcast(messageName, messageData){
45
35
  //rewrite the docUrl
46
36
  messageData.meta.docUrl = path.join(scxmlSrcDirExpressMountPath, path.basename(messageData.meta.docUrl))
@@ -50,5 +40,209 @@ module.exports = function ({
50
40
  res.write("data: " + JSON.stringify(messageData) + '\n\n'); // Note the extra newline
51
41
  }
52
42
  }
43
+
44
+ const modelCache = {}
45
+ const fnModelCache = {}
46
+
47
+ function initModel({scxmlName, executionContext}, cb){
48
+ scxml.pathToModel(`${pathToScxmlSrcDir}/${scxmlName}.scxml`, function(err, model){
49
+ if(err){
50
+ return cb(err);
51
+ }
52
+ modelCache[scxmlName] = model
53
+ model.prepare(function(err, fnModel) {
54
+ if(err){
55
+ return cb(err);
56
+ }
57
+ fnModelCache[scxmlName] = fnModel
58
+ return cb(null, fnModel)
59
+ }, executionContext)
60
+ })
61
+ }
62
+
63
+ function initModelOrFetchFromCache({scxmlName, executionContext}, cb){
64
+ if(!fnModelCache[scxmlName]){
65
+ initModel({scxmlName, executionContext}, (err, fnModel) => {
66
+ if(err) {
67
+ return cb(err)
68
+ }
69
+ cb(null, fnModel)
70
+ })
71
+ } else {
72
+ cb(null, fnModelCache[scxmlName])
73
+ }
74
+ }
75
+
76
+ scxml.core.BaseInterpreter.generateSessionid = uuid.v4
77
+ scxml.core.BaseInterpreter.doSend = doSend
78
+
79
+ // TODO write a custom invoker based on invokeNewSession
80
+ //core.InterpreterScriptingContext.invokers = customInvokeTypes; //TODO: set up default invokers
81
+
82
+ function invokeNewSession({scxmlName, sessionid, executionContext }, cb){
83
+ initModelOrFetchFromCache({ scxmlName, executionContext }, (err, fnModel) => {
84
+
85
+ if(err) return cb(err);
86
+
87
+ //instantiate the interpreter
88
+ const sc1 = new scxml.core.Statechart(fnModel, {doSend, sessionid});
89
+ initializeRootSessionToSerializeAutomaticallyOnBigStepEndAndInvokedSessionInitialized(null, sc1, db)
90
+ handleInterpreterBigStepEnd(null, sc1, db, 'once', (err) => {
91
+ if(err) return cb(err)
92
+
93
+ //save the snapshot to the database
94
+ const snapshot = sc1.getSnapshot()
95
+ const sessionId = sc1.opts.sessionid
96
+
97
+ cb(null, {sessionId, snapshot})
98
+ })
99
+
100
+ sc1.start();
101
+ })
102
+ }
103
+
104
+
105
+
106
+ function doSend(session, event){
107
+ //console.log('session', session)
108
+ const scxmlName = path.basename(session._model.docUrl, '.scxml')
109
+ handleScxmlEvent({scxmlName, sessionId: session.opts.sessionid, event, cb: (err, newSnapshot) => {
110
+ if(err) throw err
111
+ console.log('new snapshot for event', JSON.stringify(event,4,4), newSnapshot)
112
+ }})
113
+ }
114
+
115
+ //TODO: use the database as an event queue to support multi-tenancy (horizontal scaling)
116
+ function handleScxmlEvent({scxmlName, sessionId, executionContext, event, cb}){
117
+ //console.log('handleScxmlEvent, scxmlName, sessionId, event', scxmlName, sessionId, event)
118
+ initModelOrFetchFromCache({scxmlName, executionContext}, (err, fnModel) => {
119
+
120
+ if(err) return cb(err)
121
+
122
+ db.findOne({sessionid: sessionId}, (err, dbResult) => {
123
+
124
+ if(!dbResult){
125
+ console.error("Session not found", scxmlName, sessionId)
126
+ return cb(null, null)
127
+ }
128
+
129
+ const {
130
+ sessionid,
131
+ docUrl,
132
+ invokeid,
133
+ parentSession : parentSessionStub,
134
+ snapshot,
135
+ invokeMap : serializedInvokeMap,
136
+ } = dbResult
137
+
138
+ // complete the stub session
139
+ if(parentSessionStub){
140
+ parentSessionStub.cancel = cancelSession
141
+ }
142
+
143
+ if(err) return cb(err)
144
+
145
+ // populate the invoke map with stub sessions
146
+ const invokeMap = {}
147
+ if(serializedInvokeMap){
148
+ Object.entries(serializedInvokeMap).map( ([key, value]) => {
149
+ invokeMap[key] = new Promise((resolve, reject) => {
150
+ value.cancel = cancelSession
151
+ resolve(value);
152
+ });
153
+ })
154
+ }
155
+
156
+ //instantiate the interpreter
157
+ const sc1 = new scxml.core.Statechart(fnModel, {
158
+ snapshot,
159
+ sessionid: sessionId,
160
+ doSend,
161
+ parentSession: parentSessionStub,
162
+ _invokeMap : invokeMap,
163
+ invokeid
164
+ });
165
+ initializeRootSessionToSerializeAutomaticallyOnBigStepEndAndInvokedSessionInitialized(parentSessionStub, sc1, db)
166
+ handleInterpreterBigStepEnd(parentSessionStub, sc1, db, 'once', (err) => {
167
+
168
+ if(err) return cb(err)
169
+
170
+ //save the snapshot to the database
171
+ const newSnapshot = sc1.getSnapshot()
172
+
173
+ if(cb) cb(null, newSnapshot)
174
+ })
175
+
176
+ sc1.gen(event)
177
+ })
178
+ })
179
+ }
180
+
181
+ function cancelSession(){
182
+ // TODO: we would need to clear any timeout events. This means clearing any events with timeouts that are still on the queue originating from this session. Will add this when we have chosen an asynchronous queue.
183
+ // for now, we just delete him in the database
184
+ db.deleteOne({sessionid: this.opts.sessionid}, (err) => {
185
+ if(err) throw err;
186
+ })
187
+ }
188
+
189
+ if(app){
190
+ app.use(scxmlSrcDirExpressMountPath, express.static(pathToScxmlSrcDir))
191
+ app.use('/scion/static/dashboard', express.static(pathToDashboard))
192
+ //TODO: prefix this with "scion"
193
+ app.get('/api/update-stream', function(req, res){
194
+ res.writeHead(200, {
195
+ 'Content-Type': 'text/event-stream',
196
+ 'Cache-Control': 'no-cache',
197
+ 'Connection': 'keep-alive',
198
+ 'X-Accel-Buffering': 'no'
199
+ });
200
+ res.write('\n');
201
+ sseResponses.add(res);
202
+ req.on("close", function() {
203
+ sseResponses.delete(res);
204
+ });
205
+ });
206
+
207
+ // API to init new session
208
+ app.post('/scion/:scxmlName', (req, res, next) => {
209
+ const scxmlName = req.params.scxmlName
210
+ invokeNewSession({scxmlName}, (err, {sessionId, snapshot}) => {
211
+ res.json({
212
+ sessionId,
213
+ snapshot
214
+ })
215
+ })
216
+ });
217
+
218
+ // API to send event to session
219
+ app.post('/scion/:scxmlName/:sessionId', (req, res) => {
220
+
221
+ const scxmlName = req.params.scxmlName,
222
+ sessionId = req.params.sessionId;
223
+
224
+ const evt = req.body
225
+
226
+ //read the sessionId
227
+ // TODO: handle error where sessionId does not exist
228
+ handleScxmlEvent({scxmlName, sessionId, event, cb: (err, newSnapshot) => {
229
+
230
+ if(err) throw err;
231
+
232
+ res.json({
233
+ sessionId,
234
+ snapshot : newSnapshot
235
+ })
236
+ }})
237
+
238
+ //rehydrate the session
239
+ })
240
+ }
241
+
242
+ return {
243
+ invokeNewSession,
244
+ sendEvent: handleScxmlEvent,
245
+ }
246
+
53
247
  }
54
248
 
@@ -0,0 +1,109 @@
1
+ const fs = require('fs')
2
+ const core = require('@scion-scxml/core')
3
+ const util = require('util')
4
+
5
+ function serializeInvokeMap(invokeMap){
6
+ const invokeIds = Object.keys(invokeMap)
7
+ return Promise.all(
8
+ invokeIds.map( invokeId =>
9
+ invokeMap[invokeId]
10
+ )
11
+ ).then(invokedSessions => {
12
+ const o = {}
13
+ invokedSessions.forEach( (session, index) => o[invokeIds[index]] = {
14
+ opts : {
15
+ sessionid : session.opts.sessionid,
16
+ },
17
+ _model : {
18
+ docUrl : session._model.docUrl
19
+ }
20
+ })
21
+ return o
22
+ })
23
+ }
24
+
25
+ function handleInterpreterBigStepEnd(parentSession, interpreter, dbAdapter, method, cb){
26
+ interpreter[method || 'on']('onBigStepEnd',(e) => {
27
+ if((e && e.name === "done.state.$generated-scxml-0") || interpreter.isFinal()) return;
28
+ // persist the state machine state
29
+ const sessionid = interpreter.opts.sessionid,
30
+ invokeid = interpreter.opts.invokeid,
31
+ snapshot = interpreter.getSnapshot(),
32
+ docUrl = interpreter._model.docUrl;
33
+
34
+ //console.log(sessionid, invokeid, interpreter.opts._invokeMap)
35
+ //console.log('persist state machine snapshot', snapshot)
36
+
37
+ //upsert
38
+ const query = { sessionid };
39
+ const update = { $set: {
40
+ parentSession: parentSession && {
41
+ opts : {
42
+ sessionid : parentSession.opts.sessionid,
43
+ },
44
+ _model : {
45
+ docUrl : parentSession._model.docUrl
46
+ }
47
+ },
48
+ sessionid,
49
+ invokeid,
50
+ snapshot,
51
+ docUrl,
52
+ }};
53
+ const options = { upsert: true };
54
+ console.log('upsert onBigStepEnd', query, JSON.stringify(update, 4, 4))
55
+ dbAdapter.updateOne(query, JSON.parse(JSON.stringify(update)), options, cb || ((err, result) => {if(err) throw err;}));
56
+ })
57
+ }
58
+
59
+ function handleInvokedSessionInitialized(rootSession, dbAdapter, invokedInterpreter){
60
+
61
+ //clear the serialized session on exit
62
+ invokedInterpreter.on('onExitInterpreter', () => {
63
+ const sessionid = invokedInterpreter.opts.sessionid;
64
+
65
+ dbAdapter.deleteOne({sessionid}, (err, result) => {if(err) throw err;});
66
+ });
67
+
68
+ process.nextTick(() => {
69
+ serializeInvokeMap(rootSession._scriptingContext._invokeMap).then(serializedInvokeMap => {
70
+
71
+ const sessionid = rootSession.opts.sessionid,
72
+ invokeid = rootSession.opts.invokeid,
73
+ snapshot = rootSession.getSnapshot(),
74
+ docUrl = rootSession._model.docUrl;
75
+
76
+ //update invokeMap on the root session
77
+ const query1 = { sessionid: rootSession.opts.sessionid };
78
+ const update1 = { $set: { invokeMap : serializedInvokeMap }};
79
+ dbAdapter.updateOne(query1, JSON.parse(JSON.stringify(update1)), {}, (err, result) => {if(err) throw err;});
80
+ //console.log('upsert 1 onInvokedSessionInitialized', query1, util.inspect(update1, {depth: null}))
81
+
82
+ //upsert a session
83
+ const query2 = { sessionid };
84
+ const update2 = { $set: {
85
+ sessionid,
86
+ invokeid,
87
+ snapshot,
88
+ docUrl,
89
+ invokeMap : serializedInvokeMap
90
+ }};
91
+ const options = { upsert: true };
92
+ //console.log('upsert 2 onInvokedSessionInitialized', query2, util.inspect(update2, {depth: null}))
93
+ dbAdapter.updateOne(query2, JSON.parse(JSON.stringify(update2)), options, (err, result) => {if(err) throw err;});
94
+ })
95
+ })
96
+
97
+ handleInterpreterBigStepEnd(rootSession, invokedInterpreter, dbAdapter)
98
+ }
99
+
100
+ function initializeRootSessionToSerializeAutomaticallyOnBigStepEndAndInvokedSessionInitialized(parentSession, session, dbAdapter, handleParentSession){
101
+ if(handleParentSession) handleInterpreterBigStepEnd(parentSession, session, dbAdapter);
102
+ session.on('onInvokedSessionInitialized', handleInvokedSessionInitialized.bind(this, session, dbAdapter));
103
+ }
104
+
105
+ module.exports = {
106
+ initializeRootSessionToSerializeAutomaticallyOnBigStepEndAndInvokedSessionInitialized,
107
+ handleInterpreterBigStepEnd
108
+ }
109
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scion-scxml/express-middleware",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -9,10 +9,13 @@
9
9
  "author": "",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
- "@scion-scxml/core": "^2.6.22",
13
- "@scion-scxml/dashboard": "^1.0.24",
14
- "@scion-scxml/monitor-middleware": "^1.0.19",
15
- "@scion-scxml/scxml": "^4.3.25",
16
- "express": "^4.17.1"
17
- }
12
+ "@scion-scxml/core": "^2.6.24",
13
+ "@scion-scxml/dashboard": "^1.0.26",
14
+ "@scion-scxml/monitor-middleware": "^1.0.21",
15
+ "@scion-scxml/scxml": "^4.3.27",
16
+ "express": "^4.17.1",
17
+ "mongodb": "^3.6.8",
18
+ "uuid": "^8.3.2"
19
+ },
20
+ "gitHead": "25f3db4baa9a9aae69b014b07c7533a18b73fc8c"
18
21
  }
package/test/.swp ADDED
Binary file
package/test/app.js CHANGED
@@ -1,3 +1,4 @@
1
+ var MongoClient = require('mongodb').MongoClient;
1
2
  var path = require('path')
2
3
  var createError = require('http-errors');
3
4
  var express = require('express');
@@ -25,57 +26,45 @@ app.use(express.json());
25
26
  app.use(express.urlencoded({ extended: false }));
26
27
  app.use(cookieParser());
27
28
 
29
+ const url = "mongodb://localhost:27017/";
30
+ MongoClient.connect(url, function(err, db) {
31
+ if (err) throw err;
32
+ console.log("Database created!");
28
33
 
29
- //init the scion middleware
30
- scionMiddleware({
31
- app,
32
- scxml,
33
- pathToScxmlSrcDir: path.join(__dirname, 'scxml')
34
- })
34
+ const dbo = db.db("mydb");
35
35
 
36
- app.use(express.static(path.join(__dirname, 'public')));
37
-
38
- app.use('/', indexRouter);
39
- app.use('/users', usersRouter);
40
-
41
- // catch 404 and forward to error handler
42
- app.use(function(req, res, next) {
43
- next(createError(404));
44
- });
45
-
46
- // error handler
47
- app.use(function(err, req, res, next) {
48
- // set locals, only providing error in development
49
- res.locals.message = err.message;
50
- res.locals.error = req.app.get('env') === 'development' ? err : {};
51
-
52
- // render the error page
53
- res.status(err.status || 500);
54
- res.render('error');
55
- });
36
+ scionMiddleware({
37
+ app,
38
+ scxml,
39
+ pathToScxmlSrcDir: path.join(__dirname, 'scxml'),
40
+ db: dbo.collection('sessions')
41
+ })
56
42
 
43
+ app.use(express.static(path.join(__dirname, 'public')));
57
44
 
58
- //init an SCXML file and simulate it
59
- scxml.pathToModel(path.join(__dirname,'scxml', 'doc.scxml'), function(err,model){
60
- if(err) throw err;
61
- model.prepare((err, fnModel) => {
62
- if(err) throw err;
45
+ app.use('/', indexRouter);
46
+ app.use('/users', usersRouter);
63
47
 
64
- //instantiate the interpreter
65
- const rootSession = new scxml.core.Statechart(fnModel);
48
+ // catch 404 and forward to error handler
49
+ app.use(function(req, res, next) {
50
+ next(createError(404));
51
+ });
66
52
 
67
- rootSession.on('onInvokedSessionInitialized', function(invokedInterpreter){
68
- invokedInterpreter._scriptingContext = rootSession._scriptingContext; //FIXME: workaround for bug in inter-session communication: https://gitlab.com/scion-scxml/scion/issues/3
69
- });
53
+ // error handler
54
+ app.use(function(err, req, res, next) {
55
+ // set locals, only providing error in development
56
+ res.locals.message = err.message;
57
+ res.locals.error = req.app.get('env') === 'development' ? err : {};
70
58
 
71
- //start the machine
72
- rootSession.start();
59
+ // render the error page
60
+ res.status(err.status || 500);
61
+ res.render('error');
62
+ });
63
+
64
+ });
73
65
 
74
- }, {
75
- console: console,
76
- })
77
- })
78
66
 
79
67
 
80
68
 
81
69
  module.exports = app;
70
+
package/test/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scion-scxml/express-middleware-test",
3
- "version": "0.0.0",
3
+ "version": "0.0.1",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "start": "node ./bin/www"
@@ -12,6 +12,5 @@
12
12
  "http-errors": "~1.6.3",
13
13
  "jade": "~1.11.0",
14
14
  "morgan": "~1.9.1"
15
- },
16
- "private": true
15
+ }
17
16
  }
@@ -0,0 +1,32 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <scxml name="Scxml"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://www.w3.org/2005/07/scxml
5
+ https://www.w3.org/2011/04/SCXML/scxml.xsd"
6
+ xmlns="http://www.w3.org/2005/07/scxml"
7
+ xmlns:conf="http://www.w3.org/2005/scxml-conformance" datamodel="ecmascript" version="1.0">
8
+
9
+ <state>
10
+ <state id="m1">
11
+ <invoke id="sub1" type="http://www.w3.org/TR/scxml/" src="sub1.scxml" autoforward="true" />
12
+ <transition event="done.invoke.sub1" target="m2"/>
13
+ </state>
14
+
15
+ <state id="m2">
16
+ <invoke id="sub2" type="http://www.w3.org/TR/scxml/" src="sub2.scxml" autoforward="true" />
17
+ <transition event="done.invoke.sub2" target="m3"/>
18
+ </state>
19
+
20
+ <state id="m3">
21
+ <invoke id="sub3" type="http://www.w3.org/TR/scxml/" src="sub3.scxml" autoforward="true" />
22
+ <transition event="done.invoke.sub3" target="end"/>
23
+ </state>
24
+
25
+ <final id="end">
26
+ <onentry>
27
+ <log expr="'Done.'" label="INFO"/>
28
+ </onentry>
29
+ </final>
30
+
31
+ </state>
32
+ </scxml>
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <scxml name="Scxml"
3
+ xmlns="http://www.w3.org/2005/07/scxml"
4
+ xmlns:conf="http://www.w3.org/2005/scxml-conformance" datamodel="ecmascript" version="1.0">
5
+
6
+ <state>
7
+ <transition event="*">
8
+ <log expr="`sub1 EVENT: ${_event.name}, ${_event.data}`" label="INFO"/>
9
+ </transition>
10
+
11
+ <transition event="error.*">
12
+ <log expr="_event" label="ERROR"/>
13
+ </transition>
14
+
15
+ <state id="start">
16
+ <onentry>
17
+ <log expr="`Entered sub1 start`" label="INFO"/>
18
+ </onentry>
19
+
20
+ <transition event="tick" cond="_event.data === 1" target="end"/>
21
+ </state>
22
+
23
+ </state>
24
+ <final id="end"/>
25
+ </scxml>
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <scxml name="Scxml"
3
+ xmlns="http://www.w3.org/2005/07/scxml"
4
+ xmlns:conf="http://www.w3.org/2005/scxml-conformance" datamodel="ecmascript" version="1.0">
5
+
6
+ <state>
7
+ <transition event="*">
8
+ <log expr="`sub2 EVENT: ${_event.name}, ${_event.data}`" label="INFO"/>
9
+ </transition>
10
+
11
+ <transition event="error.*">
12
+ <log expr="_event" label="ERROR"/>
13
+ </transition>
14
+
15
+ <state id="start">
16
+ <onentry>
17
+ <log expr="`Entered sub2 start`" label="INFO"/>
18
+ </onentry>
19
+
20
+ <transition event="tick" cond="_event.data === 2" target="middle"/>
21
+ </state>
22
+
23
+ <state id="middle">
24
+ <onentry>
25
+ <log expr="`Entered sub2 middle`" label="INFO"/>
26
+ </onentry>
27
+
28
+ <transition event="tick" cond="_event.data === 3" target="end"/>
29
+ </state>
30
+
31
+ </state>
32
+ <final id="end"/>
33
+ </scxml>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <scxml name="Scxml"
3
+ xmlns="http://www.w3.org/2005/07/scxml"
4
+ xmlns:conf="http://www.w3.org/2005/scxml-conformance" datamodel="ecmascript" version="1.0">
5
+
6
+ <state>
7
+ <transition event="*">
8
+ <log expr="`sub3 EVENT: ${_event.name}, ${_event.data}`" label="INFO"/>
9
+ </transition>
10
+
11
+ <transition event="error.*">
12
+ <log expr="_event" label="ERROR"/>
13
+ </transition>
14
+
15
+ <state id="start">
16
+ <onentry>
17
+ <log expr="`Entered sub3 start`" label="INFO"/>
18
+ </onentry>
19
+
20
+ <transition event="tick" cond="_event.data === 4" target="end"/>
21
+ </state>
22
+ </state>
23
+ <final id="end"/>
24
+ </scxml>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <scxml
3
+ datamodel="ecmascript"
4
+ xmlns="http://www.w3.org/2005/07/scxml"
5
+ version="1.0">
6
+
7
+ <state id="a">
8
+ <transition target="b" event="t1"/>
9
+ </state>
10
+
11
+ <state id="b">
12
+ <transition target="a" event="t2"/>
13
+ </state>
14
+
15
+ </scxml>
16
+
17
+
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <scxml name="Scxml"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://www.w3.org/2005/07/scxml
5
+ https://www.w3.org/2011/04/SCXML/scxml.xsd"
6
+ xmlns="http://www.w3.org/2005/07/scxml"
7
+ xmlns:conf="http://www.w3.org/2005/scxml-conformance" datamodel="ecmascript" version="1.0">
8
+
9
+ <datamodel>
10
+ <data id="x" expr="0"/>
11
+ </datamodel>
12
+
13
+ <state>
14
+ <transition event="*">
15
+ <log expr="`test2 EVENT: ${_event.name}, ${JSON.stringify(_event.data)}`" label="INFO"/>
16
+ </transition>
17
+ <state id="m1">
18
+ <invoke id="test2sub1" type="http://www.w3.org/TR/scxml/" src="test2sub1.scxml"/>
19
+ <state id="m1.1">
20
+ <transition event="test2sub1.ready" target="m1.2"/>
21
+ </state>
22
+ <state id="m1.2">
23
+ <onentry>
24
+ <log label="x" expr="x"/>
25
+ <send target="#_test2sub1" event="foo" delay="1s">
26
+ <param name="x" expr="x+1"/>
27
+ </send>
28
+ </onentry>
29
+ <transition event="done.invoke.test2sub1" target="m1"/>
30
+ </state>
31
+ </state>
32
+ </state>
33
+ </scxml>
@@ -0,0 +1,20 @@
1
+ <?xml version="1.0" encoding="UTF-8" ?>
2
+ <scxml name="Scxml"
3
+ xmlns="http://www.w3.org/2005/07/scxml"
4
+ xmlns:conf="http://www.w3.org/2005/scxml-conformance" datamodel="ecmascript" version="1.0">
5
+
6
+ <state>
7
+ <transition event="*">
8
+ <log expr="`test2sub1 EVENT: ${_event.name}, ${JSON.stringify(_event.data)}`" label="INFO"/>
9
+ </transition>
10
+ <state id="initial">
11
+ <onentry>
12
+ <send target="#_parent" event="test2sub1.ready" delay="1s"/>
13
+ </onentry>
14
+ <transition event="foo" target="end"/>
15
+ </state>
16
+ </state>
17
+
18
+ <final id="end"/>
19
+ </scxml>
20
+