@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 +0 -0
- package/.swo +0 -0
- package/.swp +0 -0
- package/CHANGES +12 -0
- package/lib/index.js +211 -17
- package/lib/multilevel-state-machine-ser-des.js +109 -0
- package/package.json +10 -7
- package/test/.swp +0 -0
- package/test/app.js +31 -42
- package/test/package.json +2 -3
- package/test/scxml/main.scxml +32 -0
- package/test/scxml/sub1.scxml +25 -0
- package/test/scxml/sub2.scxml +33 -0
- package/test/scxml/sub3.scxml +24 -0
- package/test/scxml/test.scxml +17 -0
- package/test/scxml/test2.scxml +33 -0
- package/test/scxml/test2sub1.scxml +20 -0
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": "
|
|
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.
|
|
13
|
-
"@scion-scxml/dashboard": "^1.0.
|
|
14
|
-
"@scion-scxml/monitor-middleware": "^1.0.
|
|
15
|
-
"@scion-scxml/scxml": "^4.3.
|
|
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
|
-
|
|
30
|
-
scionMiddleware({
|
|
31
|
-
app,
|
|
32
|
-
scxml,
|
|
33
|
-
pathToScxmlSrcDir: path.join(__dirname, 'scxml')
|
|
34
|
-
})
|
|
34
|
+
const dbo = db.db("mydb");
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
65
|
-
|
|
48
|
+
// catch 404 and forward to error handler
|
|
49
|
+
app.use(function(req, res, next) {
|
|
50
|
+
next(createError(404));
|
|
51
|
+
});
|
|
66
52
|
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
//
|
|
72
|
-
|
|
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.
|
|
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
|
+
|