The engine. Executes passed BPMN definitions.
Creates a new Engine.
Arguments:
options
: Optional options, passed to environment:name
: optional name of engine,source
: BPMN 2.0 definition source as stringmoddleOptions
: optional bpmn-moddle optionsmoddleContext
: optional BPMN 2.0 definition moddle contextscripts
: optional inline script handler, defaults to vm module handling, i.e. JavaScriptLogger
: optional Logger factory, defaults to debug loggerelements
: optional object with element type mapping override
Returns:
name
: engine namebroker
: engine brokerstate
: engine statestopped
: boolean stoppedexecution
: current engine executionenvironment
: engine environmentlogger
: engine loggerasync execute()
: execute definitionasync getDefinitionById()
: get definition by idasync getDefinitions()
: get all definitionsasync getState()
: get execution serialized staterecover()
: recover from stateasync resume()
: resume executionstop()
: stop executionwaitFor()
: wait for engine events, returns Promise
const {Engine} = require('bpmn-engine');
const fs = require('fs');
const engine = Engine({
name: 'mother of all',
source: fs.readFileSync('./test/resources/mother-of-all.bpmn'),
moddleOptions: {
camunda: require('camunda-bpmn-moddle/resources/camunda')
}
});
Execute definition with:
options
: Optional object with options to override the initial engine optionslistener
: Listen for activity events, anEventEmitter
objectvariables
: Optional object with instance variablesservices
: Optional object with service functions
callback
: optional callbackerr
: Error if anyexecution
: Engine execution
Execute options overrides the initial options passed to the engine before executing the definition.
const {Engine} = require('bpmn-engine');
const {EventEmitter} = require('events');
const source = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<process id="theProcess" isExecutable="true">
<dataObjectReference id="inputFromUserRef" dataObjectRef="inputFromUser" />
<dataObject id="inputFromUser" />
<startEvent id="theStart" />
<userTask id="userTask">
<ioSpecification id="inputSpec">
<dataOutput id="userInput" />
</ioSpecification>
<dataOutputAssociation id="associatedWith" sourceRef="userInput" targetRef="inputFromUserRef" />
</userTask>
<endEvent id="theEnd" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="userTask" />
<sequenceFlow id="flow2" sourceRef="userTask" targetRef="theEnd" />
</process>
</definitions>`;
const engine = Engine({
name: 'first',
source,
variables: {
data: {
inputFromUser: 0,
}
}
});
const listener = new EventEmitter();
listener.on('wait', (elementApi) => {
elementApi.owner.logger.debug(`<${elementApi.executionId} (${elementApi.id})> signal with io`, elementApi.content.ioSpecification);
elementApi.signal({
ioSpecification: {
dataOutputs: [{
id: 'userInput',
value: 2
}]
}
});
});
engine.execute({
listener,
variables: {
data: {
inputFromUser: 1,
}
}
}, (err, execution) => {
if (err) throw err;
console.log('completed with overridden listener', execution.environment.output);
});
An EventEmitter
object with listeners. Event names are composed by activity event name and activity id, e.g. wait-userTask
.
const {Engine} = require('bpmn-engine');
const {EventEmitter} = require('events');
const source = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<process id="theProcess" isExecutable="true">
<userTask id="userTask" />
</process>
</definitions>`;
const engine = Engine({
name: 'first listener',
source
});
const listener = new EventEmitter();
listener.on('activity.enter', (elementApi, engineApi) => {
console.log(`${elementApi.type} <${elementApi.id}> of ${engineApi.name} is entered`);
});
listener.on('wait', (elemntApi, instance) => {
console.log(`${elemntApi.type} <${elemntApi.id}> of ${instance.name} is waiting for input`);
elemntApi.signal('don´t wait for me');
});
engine.execute({
listener
});
Execution variables are passed as the first argument to #execute
.
const {Engine} = require('bpmn-engine');
const fs = require('fs');
const engine = Engine({
name: 'using variables',
source: fs.readFileSync('./test/resources/simple-task.bpmn')
});
const variables = {
input: 1
};
engine.execute({
variables
}, (err, engineApi) => {
if (err) throw err;
console.log('completed');
});
A service is a function exposed on environment.services
.
const {Engine} = require('bpmn-engine');
const bent = require('bent');
const source = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<process id="theProcess" isExecutable="true">
<startEvent id="theStart" />
<scriptTask id="scriptTask" scriptFormat="Javascript">
<script>
<![CDATA[
const get = environment.services.get;
const self = this;
get('https://example.com/test').then((body) => {
environment.variables.scriptTaskCompleted = true;
next(null, {result: body});
}).catch(next)
]]>
</script>
</scriptTask>
<endEvent id="theEnd" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="scriptTask" />
<sequenceFlow id="flow2" sourceRef="scriptTask" targetRef="theEnd" />
</process>
</definitions>`;
const engine = Engine({
name: 'services doc',
source
});
engine.execute({
services: {
get: bent('json')
}
}, (err, engineApi) => {
if (err) throw err;
console.log('completed', engineApi.name, engineApi.environment.variables);
});
Get definition by id, returns Promise
Get all definitions
const {Engine} = require('bpmn-engine');
const source = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="Definition_42" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<process id="theProcess" isExecutable="true">
<startEvent id="theStart" />
<userTask id="userTask" />
<endEvent id="theEnd" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="userTask" />
<sequenceFlow id="flow2" sourceRef="userTask" targetRef="theEnd" />
</process>
</definitions>`;
const engine = Engine({
source
});
engine.getDefinitions().then((definitions) => {
console.log('Loaded', definitions[0].id);
console.log('The definition comes with process', definitions[0].getProcesses()[0].id);
});
Get state of a running execution. Listener events wait
and start
are recommended when saving state.
The saved state will include the following content:
state
:running
oridle
engineVersion
: module package versionmoddleOptions
: Engine moddleOptionsdefinitions
: List of definitionsstate
: State of definition,pending
,running
, orcompleted
processes
: Object with processes with id as keyvariables
: Execution variablesservices
: Execution serviceschildren
: List of child statesentered
: Boolean indicating if the child is currently executing
const {Engine} = require('bpmn-engine');
const {EventEmitter} = require('events');
const fs = require('fs');
const processXml = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<process id="theProcess" isExecutable="true">
<startEvent id="theStart" />
<userTask id="userTask" />
<endEvent id="theEnd" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="userTask" />
<sequenceFlow id="flow2" sourceRef="userTask" targetRef="theEnd" />
</process>
</definitions>`;
const engine = Engine({
source: processXml
});
const listener = new EventEmitter();
let state;
listener.once('wait-userTask', () => {
state = engine.getState();
fs.writeFileSync('./tmp/some-random-id.json', JSON.stringify(state, null, 2));
console.log(JSON.stringify(state, null, 2));
});
listener.once('start', () => {
state = engine.getState();
fs.writeFileSync('./tmp/some-random-id.json', JSON.stringify(state, null, 2));
});
engine.execute({
listener
}, (err) => {
if (err) throw err;
});
Stop execution. The instance is terminated.
const {Engine} = require('bpmn-engine');
const {EventEmitter} = require('events');
const source = `
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<process id="theProcess" isExecutable="true">
<startEvent id="theStart" />
<userTask id="userTask" />
<endEvent id="theEnd" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="userTask" />
<sequenceFlow id="flow2" sourceRef="userTask" targetRef="theEnd" />
</process>
</definitions>`;
const engine = Engine({
source
});
const listener = new EventEmitter();
let state;
listener.once('wait', () => {
engine.stop();
state = engine.getState();
});
engine.execute({
variables: {
executionId: 'some-random-id'
},
listener
}, (err) => {
if (err) throw err;
});
Recover engine from state.
Arguments:
state
: engine staterecoverOptions
: optional object with options that will completely override the options passed to the engine at init
const {Engine} = require('bpmn-engine');
const state = fetchSomeState();
const engine = Engine().recover(state);
Resume execution function with previously saved engine state.
const {Engine} = require('bpmn-engine');
const {EventEmitter} = require('events');
const state = fetchSomeState();
const engine = Engine().recover(state);
const listener = new EventEmitter();
engine.resume({listener}, () => {
console.log('completed');
});
Engine emits the following events:
error
: An non-recoverable error has occurredend
: Execution completed
Each activity and flow emits events when changing state.
activity.enter
: An activity is enteredactivity.start
: An activity is startedactivity.wait
: The activity is postponed for some reason, e.g. a user task is waiting to be signaled or a message is expectedactivity.end
: An activity has ended successfullyactivity.leave
: The execution left the activityactivity.stop
: Activity run was stoppedactivity.throw
: An recoverable error was thrownactivity.error
: An non-recoverable error has occurred
Events are emitted with api with execution properties
name
: engine namestate
: state of execution, i.e running or idlestopped
: is the execution stoppedenvironment
: engine environmentdefinitions
: executing definitionsstop()
: stop executiongetState
()`: get execution serializable stategetPostponed
()`: get activities in a postponed state
flow.take
: The sequence flow was takenflow.discard
: The sequence flow was discardedflow.looped
: The sequence is looped
Expressions come in the form of ${<variables or services>.<property name>}
.
The following expressions are supported:
-
${variables.input}
- resolves to the variable input -
${variables.input[0]}
- resolves to first item of the variable input array -
${variables.input[-1]}
- resolves to last item of the variable input array -
${variables.input[spaced name]}
- resolves to the variable input object propertyspaced name
-
${services.getInput}
- return the service functiongetInput
-
${services.getInput()}
- executes the service functiongetInput
with the argument{services, variables}
-
${services.isBelow(variables.input,2)}
- executes the service functionisBelow
with result ofvariable.input
value and 2
and, as utility:
${true}
- return Boolean valuetrue
${false}
- return Boolean valuefalse
Expressions are supported by many elements, e.g.:
- MultiInstanceLoopCharacteristics
camunda:collection
: variable collection, e.g.${variables.list}
- ServiceTask
camunda:expression
element value. moddleOptionsrequire('camunda-bpmn-moddle/resources/camunda')
must be used.
- SequenceFlow
conditionExpression
element value
- TimerEvent
timeDuration
element value
Expressions in expressions is not supported.