bpmn-engine 13.0.2 → 14.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/CHANGELOG.md CHANGED
@@ -1,6 +1,13 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ # 14.0.0
5
+
6
+ ## Breaking
7
+ - Engine is prototyped, can still be invoked without new
8
+ - Bump [`bpmn-elements@8.0.0`](https://github.com/paed01/bpmn-elements/blob/master/CHANGELOG.md) with support for bpmn:CallActivity (#97)
9
+ - Bump [`smqp@6.0.0`](https://github.com/paed01/smqp/blob/default/CHANGELOG.md) with support for bpmn:CallActivity (#97)
10
+
4
11
  # 13.0.2
5
12
 
6
13
  - fix for removing wrong listener on error by @allain
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const BpmnModdle = require('bpmn-moddle');
4
4
  const DebugLogger = require('./lib/Logger');
5
- const elements = require('bpmn-elements');
5
+ const Elements = require('bpmn-elements');
6
6
  const getOptionsAndCallback = require('./lib/getOptionsAndCallback');
7
7
  const JavaScripts = require('./lib/JavaScripts');
8
8
  const ProcessOutputDataObject = require('./lib/extensions/ProcessOutputDataObject');
@@ -11,528 +11,556 @@ const {default: serializer, deserialize, TypeResolver} = require('moddle-context
11
11
  const {EventEmitter} = require('events');
12
12
  const {version: engineVersion} = require('./package.json');
13
13
 
14
- module.exports = {Engine};
14
+ const kEngine = Symbol.for('engine');
15
+ const kEnvironment = Symbol.for('environment');
16
+ const kExecuting = Symbol.for('executing');
17
+ const kExecution = Symbol.for('execution');
18
+ const kLoadedDefinitions = Symbol.for('loaded definitions');
19
+ const kOnBrokerReturn = Symbol.for('onBrokerReturn');
20
+ const kPendingSources = Symbol.for('pending sources');
21
+ const kSources = Symbol.for('sources');
22
+ const kState = Symbol.for('state');
23
+ const kStopped = Symbol.for('stopped');
24
+ const kTypeResolver = Symbol.for('type resolver');
25
+
26
+ module.exports = {Engine, Execution};
15
27
 
16
28
  function Engine(options = {}) {
17
- options = {Logger: DebugLogger, scripts: JavaScripts(options.disableDummyScript), ...options};
29
+ if (!(this instanceof Engine)) return new Engine(options);
18
30
 
19
- let {name, Logger, sourceContext} = options;
31
+ EventEmitter.call(this);
20
32
 
21
- let loadedDefinitions, execution;
22
- const logger = Logger('engine');
33
+ const opts = this.options = {
34
+ Logger: DebugLogger,
35
+ scripts: new JavaScripts(options.disableDummyScript),
36
+ ...options,
37
+ };
23
38
 
24
- const sources = [];
25
- const typeResolver = TypeResolver({
26
- ...elements,
27
- ...(options.elements || {})
28
- }, defaultTypeResolver);
39
+ this.logger = opts.Logger('engine');
29
40
 
30
- function defaultTypeResolver(elementTypes) {
31
- if (options.typeResolver) return options.typeResolver(elementTypes);
32
- elementTypes['bpmn:DataObject'] = ProcessOutputDataObject;
33
- }
41
+ this[kTypeResolver] = TypeResolver({
42
+ ...Elements,
43
+ ...(opts.elements || {})
44
+ }, opts.typeResolver || defaultTypeResolver);
34
45
 
35
- const pendingSources = [];
36
- if (options.source) pendingSources.push(serializeSource(options.source));
37
- if (options.moddleContext) pendingSources.push(serializeModdleContext(options.moddleContext));
38
- if (sourceContext) pendingSources.push(sourceContext);
39
-
40
- let environment = elements.Environment(options);
41
- const emitter = new EventEmitter();
42
-
43
- const engine = Object.assign(emitter, {
44
- logger,
45
- addSource,
46
- execute,
47
- getDefinitionById,
48
- getDefinitions,
49
- getState,
50
- recover,
51
- resume,
52
- stop,
53
- waitFor,
54
- });
46
+ this[kEnvironment] = new Elements.Environment(opts);
55
47
 
56
- const broker = Broker(engine);
48
+ const broker = this.broker = new Broker(this);
57
49
  broker.assertExchange('event', 'topic', {autoDelete: false});
58
50
 
59
- Object.defineProperty(engine, 'broker', {
60
- enumerable: true,
61
- get() {
62
- return broker;
63
- }
64
- });
51
+ this[kExecution] = null;
52
+ this[kLoadedDefinitions] = null;
53
+ this[kSources] = [];
65
54
 
66
- Object.defineProperty(engine, 'name', {
67
- enumerable: true,
68
- get() {
69
- return name;
70
- },
71
- set(value) {
72
- name = value;
73
- },
74
- });
55
+ const pendingSources = this[kPendingSources] = [];
56
+ if (opts.source) pendingSources.push(this._serializeSource(opts.source));
57
+ if (opts.moddleContext) pendingSources.push(this._serializeModdleContext(opts.moddleContext));
58
+ if (opts.sourceContext) pendingSources.push(opts.sourceContext);
59
+ }
75
60
 
76
- Object.defineProperty(engine, 'environment', {
77
- enumerable: true,
78
- get() {
79
- return environment;
80
- },
81
- });
61
+ function defaultTypeResolver(elementTypes) {
62
+ elementTypes['bpmn:DataObject'] = ProcessOutputDataObject;
63
+ elementTypes['bpmn:DataStoreReference'] = ProcessOutputDataObject;
64
+ }
82
65
 
83
- Object.defineProperty(engine, 'state', {
84
- enumerable: true,
85
- get() {
86
- if (execution) return execution.state;
87
- return 'idle';
88
- },
89
- });
66
+ Engine.prototype = Object.create(EventEmitter.prototype);
67
+
68
+ Object.defineProperty(Engine.prototype, 'name', {
69
+ enumerable: true,
70
+ get() {
71
+ return this.options.name;
72
+ },
73
+ set(value) {
74
+ this.options.name = value;
75
+ },
76
+ });
77
+
78
+ Object.defineProperty(Engine.prototype, 'environment', {
79
+ enumerable: true,
80
+ get() {
81
+ return this[kEnvironment];
82
+ },
83
+ });
84
+
85
+ Object.defineProperty(Engine.prototype, 'state', {
86
+ enumerable: true,
87
+ get() {
88
+ const execution = this.execution;
89
+ if (execution) return execution.state;
90
+ return 'idle';
91
+ },
92
+ });
93
+
94
+ Object.defineProperty(Engine.prototype, 'stopped', {
95
+ enumerable: true,
96
+ get() {
97
+ const execution = this.execution;
98
+ if (execution) return execution.stopped;
99
+ return false;
100
+ },
101
+ });
102
+
103
+ Object.defineProperty(Engine.prototype, 'execution', {
104
+ enumerable: true,
105
+ get() {
106
+ return this[kExecution];
107
+ },
108
+ });
109
+
110
+ Engine.prototype.execute = async function execute(...args) {
111
+ const [executeOptions, callback] = getOptionsAndCallback(...args);
112
+ try {
113
+ var definitions = await this._loadDefinitions(executeOptions); // eslint-disable-line no-var
114
+ } catch (err) {
115
+ if (callback) return callback(err);
116
+ throw err;
117
+ }
90
118
 
91
- Object.defineProperty(engine, 'stopped', {
92
- enumerable: true,
93
- get() {
94
- if (execution) return execution.stopped;
95
- return false;
96
- },
97
- });
119
+ const execution = this[kExecution] = new Execution(this, definitions, this.options);
120
+ return execution._execute(executeOptions, callback);
121
+ };
98
122
 
99
- Object.defineProperty(engine, 'execution', {
100
- enumerable: true,
101
- get() {
102
- return execution;
103
- },
104
- });
123
+ Engine.prototype.stop = function stop() {
124
+ const execution = this.execution;
125
+ if (!execution) return;
126
+ return execution.stop();
127
+ };
105
128
 
106
- return engine;
129
+ Engine.prototype.recover = function recover(savedState, recoverOptions) {
130
+ if (!savedState) return this;
107
131
 
108
- async function execute(...args) {
109
- const [executeOptions, callback] = getOptionsAndCallback(...args);
110
- try {
111
- var definitions = await loadDefinitions(executeOptions); // eslint-disable-line no-var
112
- } catch (err) {
113
- if (callback) return callback(err);
114
- throw err;
115
- }
132
+ let name = this.name;
133
+ if (!name) name = this.name = savedState.name;
116
134
 
117
- execution = Execution(engine, definitions, options);
118
- return execution.execute(executeOptions, callback);
119
- }
135
+ this.logger.debug(`<${name}> recover`);
120
136
 
121
- function stop() {
122
- if (!execution) return;
123
- return execution.stop();
124
- }
137
+ if (recoverOptions) this[kEnvironment] = new Elements.Environment(recoverOptions);
138
+ if (savedState.environment) this[kEnvironment] = this[kEnvironment].recover(savedState.environment);
125
139
 
126
- function recover(savedState, recoverOptions) {
127
- if (!savedState) return engine;
128
- if (!name) name = savedState.name;
140
+ if (!savedState.definitions) return this;
129
141
 
130
- logger.debug(`<${name}> recover`);
142
+ const pendingSources = this[kPendingSources];
143
+ const preSources = pendingSources.splice(0);
131
144
 
132
- if (recoverOptions) environment = elements.Environment(recoverOptions);
133
- if (savedState.environment) environment = environment.recover(savedState.environment);
145
+ const typeResolver = this[kTypeResolver];
146
+ const loadedDefinitions = this[kLoadedDefinitions] = savedState.definitions.map((dState) => {
147
+ let source;
148
+ if (dState.source) source = deserialize(JSON.parse(dState.source), typeResolver);
149
+ else source = preSources.find((s) => s.id === dState.id);
134
150
 
135
- if (!savedState.definitions) return engine;
151
+ pendingSources.push(source);
136
152
 
137
- const preSources = pendingSources.splice(0);
153
+ this.logger.debug(`<${name}> recover ${dState.type} <${dState.id}>`);
138
154
 
139
- loadedDefinitions = savedState.definitions.map((dState) => {
140
- let source;
141
- if (dState.source) source = deserialize(JSON.parse(dState.source), typeResolver);
142
- else source = preSources.find((s) => s.id === dState.id);
155
+ const definition = this._loadDefinition(source);
156
+ definition.recover(dState);
143
157
 
144
- pendingSources.push(source);
158
+ return definition;
159
+ });
145
160
 
146
- logger.debug(`<${name}> recover ${dState.type} <${dState.id}>`);
161
+ this[kExecution] = new Execution(this, loadedDefinitions, {}, true);
147
162
 
148
- const definition = loadDefinition(source);
149
- definition.recover(dState);
163
+ return this;
164
+ };
150
165
 
151
- return definition;
152
- });
166
+ Engine.prototype.resume = async function resume(...args) {
167
+ const [resumeOptions, callback] = getOptionsAndCallback(...args);
153
168
 
154
- execution = Execution(engine, loadedDefinitions, {}, true);
155
-
156
- return engine;
169
+ let execution = this.execution;
170
+ if (!execution) {
171
+ const definitions = await this.getDefinitions();
172
+ if (!definitions.length) {
173
+ const err = new Error('nothing to resume');
174
+ if (callback) return callback(err);
175
+ throw err;
176
+ }
177
+ execution = this[kExecution] = new Execution(this, definitions, this.options);
157
178
  }
158
179
 
159
- async function resume(...args) {
160
- const [resumeOptions, callback] = getOptionsAndCallback(...args);
161
-
162
- if (!execution) {
163
- const definitions = await getDefinitions();
164
- if (!definitions.length) {
165
- const err = new Error('nothing to resume');
166
- if (callback) return callback(err);
167
- throw err;
168
- }
169
- execution = Execution(engine, definitions, options);
180
+ return execution._resume(resumeOptions, callback);
181
+ };
182
+
183
+ Engine.prototype.addSource = function addSource({sourceContext: addContext} = {}) {
184
+ if (!addContext) return;
185
+ const loadedDefinitions = this[kLoadedDefinitions];
186
+ if (loadedDefinitions) loadedDefinitions.splice(0);
187
+ this[kPendingSources].push(addContext);
188
+ };
189
+
190
+ Engine.prototype.getDefinitions = async function getDefinitions(executeOptions) {
191
+ const loadedDefinitions = this[kLoadedDefinitions];
192
+ if (loadedDefinitions && loadedDefinitions.length) return loadedDefinitions;
193
+ return this._loadDefinitions(executeOptions);
194
+ };
195
+
196
+ Engine.prototype.getDefinitionById = async function getDefinitionById(id) {
197
+ return (await this.getDefinitions()).find((d) => d.id === id);
198
+ };
199
+
200
+ Engine.prototype.getState = async function getState() {
201
+ const execution = this.execution;
202
+ if (execution) return execution.getState();
203
+
204
+ const definitions = await this.getDefinitions();
205
+ return new Execution(this, definitions, this.options).getState();
206
+ };
207
+
208
+ Engine.prototype.waitFor = function waitFor(eventName) {
209
+ const self = this;
210
+ return new Promise((resolve, reject) => {
211
+ self.once(eventName, onEvent);
212
+ self.once('error', onError);
213
+
214
+ function onEvent(api) {
215
+ self.removeListener('error', onError);
216
+ resolve(api);
170
217
  }
218
+ function onError(err) {
219
+ self.removeListener(eventName, onEvent);
220
+ reject(err);
221
+ }
222
+ });
223
+ };
224
+
225
+ Engine.prototype._loadDefinitions = async function loadDefinitions(executeOptions) {
226
+ const runSources = await Promise.all(this[kPendingSources]);
227
+ const loadedDefinitions = this[kLoadedDefinitions] = runSources.map((source) => this._loadDefinition(source, executeOptions));
228
+ return loadedDefinitions;
229
+ };
230
+
231
+ Engine.prototype._loadDefinition = function loadDefinition(serializedContext, executeOptions = {}) {
232
+ const {settings, variables} = executeOptions;
233
+
234
+ const environment = this.environment;
235
+ const context = new Elements.Context(serializedContext, environment.clone({
236
+ listener: environment.options.listener,
237
+ ...executeOptions,
238
+ settings: {
239
+ ...environment.settings,
240
+ ...settings,
241
+ },
242
+ variables: {
243
+ ...environment.variables,
244
+ ...variables,
245
+ },
246
+ source: serializedContext,
247
+ }));
171
248
 
172
- return execution.resume(resumeOptions, callback);
173
- }
249
+ return new Elements.Definition(context);
250
+ };
174
251
 
175
- function addSource({sourceContext: addContext} = {}) {
176
- if (!addContext) return;
177
- if (loadedDefinitions) loadedDefinitions.splice(0);
178
- pendingSources.push(addContext);
179
- }
252
+ Engine.prototype._serializeSource = async function serializeSource(source) {
253
+ const moddleContext = await this._getModdleContext(source);
254
+ return this._serializeModdleContext(moddleContext);
255
+ };
180
256
 
181
- async function getDefinitions(executeOptions) {
182
- if (loadedDefinitions && loadedDefinitions.length) return loadedDefinitions;
183
- return loadDefinitions(executeOptions);
184
- }
257
+ Engine.prototype._serializeModdleContext = function serializeModdleContext(moddleContext) {
258
+ const serialized = serializer(moddleContext, this[kTypeResolver]);
259
+ this[kSources].push(serialized);
260
+ return serialized;
261
+ };
185
262
 
186
- async function getDefinitionById(id) {
187
- return (await getDefinitions()).find((d) => d.id === id);
188
- }
263
+ Engine.prototype._getModdleContext = function getModdleContext(source) {
264
+ const bpmnModdle = new BpmnModdle(this.options.moddleOptions);
265
+ return bpmnModdle.fromXML(Buffer.isBuffer(source) ? source.toString() : source.trim());
266
+ };
189
267
 
190
- async function getState() {
191
- if (execution) return execution.getState();
268
+ function Execution(engine, definitions, options, isRecovered = false) {
269
+ this.name = engine.name;
270
+ this.options = options;
271
+ this.definitions = definitions;
272
+ this[kState] = 'idle';
273
+ this[kStopped] = isRecovered;
274
+ this[kEnvironment] = engine.environment;
275
+ this[kEngine] = engine;
276
+ this[kExecuting] = [];
277
+ const onBrokerReturn = this[kOnBrokerReturn] = this._onBrokerReturn.bind(this);
278
+ engine.broker.on('return', onBrokerReturn);
279
+ }
192
280
 
193
- const definitions = await getDefinitions();
194
- return Execution(engine, definitions, options).getState();
195
- }
281
+ Object.defineProperty(Execution.prototype, 'state', {
282
+ enumerable: true,
283
+ get() {
284
+ return this[kState];
285
+ },
286
+ });
287
+
288
+ Object.defineProperty(Execution.prototype, 'stopped', {
289
+ enumerable: true,
290
+ get() {
291
+ return this[kStopped];
292
+ },
293
+ });
294
+
295
+ Object.defineProperty(Execution.prototype, 'broker', {
296
+ enumerable: true,
297
+ get() {
298
+ return this[kEngine].broker;
299
+ },
300
+ });
301
+
302
+ Object.defineProperty(Execution.prototype, 'environment', {
303
+ enumerable: true,
304
+ get() {
305
+ return this[kEnvironment];
306
+ },
307
+ });
308
+
309
+ Execution.prototype._execute = function execute(executeOptions, callback) {
310
+ this._setup(executeOptions);
311
+ this[kStopped] = false;
312
+ this._debug('execute');
313
+
314
+ this._addConsumerCallbacks(callback);
315
+ const definitionExecutions = this.definitions.reduce((result, definition) => {
316
+ if (!definition.getExecutableProcesses().length) return result;
317
+ result.push(definition.run());
318
+ return result;
319
+ }, []);
196
320
 
197
- async function loadDefinitions(executeOptions) {
198
- const runSources = await Promise.all(pendingSources);
199
- loadedDefinitions = runSources.map((source) => loadDefinition(source, executeOptions));
200
- return loadedDefinitions;
201
- }
202
321
 
203
- function loadDefinition(serializedContext, executeOptions = {}) {
204
- const {settings, variables} = executeOptions;
205
-
206
- const context = elements.Context(serializedContext, environment.clone({
207
- listener: environment.options.listener,
208
- ...executeOptions,
209
- settings: {
210
- ...environment.settings,
211
- ...settings,
212
- },
213
- variables: {
214
- ...environment.variables,
215
- ...variables,
216
- },
217
- source: serializedContext,
218
- }));
219
-
220
- return elements.Definition(context);
322
+ if (!definitionExecutions.length) {
323
+ const error = new Error('No executable processes');
324
+ if (!callback) return this[kEngine].emit('error', error);
325
+ return callback(error);
221
326
  }
222
327
 
223
- async function serializeSource(source) {
224
- const moddleContext = await getModdleContext(source);
225
- return serializeModdleContext(moddleContext);
226
- }
328
+ return this;
329
+ };
227
330
 
228
- function serializeModdleContext(moddleContext) {
229
- const serialized = serializer(moddleContext, typeResolver);
230
- sources.push(serialized);
231
- return serialized;
232
- }
331
+ Execution.prototype._resume = function resume(resumeOptions, callback) {
332
+ this._setup(resumeOptions);
233
333
 
234
- function getModdleContext(source) {
235
- const bpmnModdle = new BpmnModdle(options.moddleOptions);
236
- return bpmnModdle.fromXML(Buffer.isBuffer(source) ? source.toString() : source.trim());
237
- }
334
+ this[kStopped] = false;
335
+ this._debug('resume');
336
+ this._addConsumerCallbacks(callback);
238
337
 
239
- async function waitFor(eventName) {
240
- return new Promise((resolve, reject) => {
241
- engine.once(eventName, onEvent);
242
- engine.once('error', onError);
338
+ this[kExecuting].splice(0);
339
+ this.definitions.forEach((definition) => definition.resume());
243
340
 
244
- function onEvent(api) {
245
- engine.removeListener('error', onError);
246
- resolve(api);
247
- }
248
- function onError(err) {
249
- engine.removeListener(eventName, onEvent);
250
- reject(err);
251
- }
252
- });
253
- }
254
- }
341
+ return this;
342
+ };
255
343
 
256
- function Execution(engine, definitions, options, isRecovered = false) {
257
- const {environment, logger, waitFor, broker} = engine;
258
- broker.on('return', onBrokerReturn);
344
+ Execution.prototype._addConsumerCallbacks = function addConsumerCallbacks(callback) {
345
+ if (!callback) return;
259
346
 
260
- let state = 'idle';
261
- let stopped = isRecovered;
262
- const executing = [];
347
+ const broker = this.broker;
348
+ const onBrokerReturn = this[kOnBrokerReturn];
263
349
 
264
- return {
265
- ...Api(),
266
- get state() {
267
- return state;
268
- },
269
- get stopped() {
270
- return stopped;
271
- },
272
- execute,
273
- resume,
274
- };
350
+ broker.off('return', onBrokerReturn);
275
351
 
276
- function execute(executeOptions, callback) {
277
- setup(executeOptions);
278
- stopped = false;
279
- logger.debug(`<${engine.name}> execute`);
280
-
281
- addConsumerCallbacks(callback);
282
- const definitionExecutions = definitions.reduce((result, definition) => {
283
- if (!definition.getExecutableProcesses().length) return result;
284
- result.push(definition.run());
285
- return result;
286
- }, []);
287
-
288
- if (!definitionExecutions.length) {
289
- const error = new Error('No executable processes');
290
- if (!callback) return engine.emit('error', error);
291
- return callback(error);
292
- }
352
+ clearConsumers();
293
353
 
294
- return Api();
295
- }
354
+ broker.subscribeOnce('event', 'engine.stop', () => {
355
+ clearConsumers();
356
+ return callback(null, this);
357
+ }, {consumerTag: 'ctag-cb-stop'});
296
358
 
297
- function resume(resumeOptions, callback) {
298
- setup(resumeOptions);
359
+ broker.subscribeOnce('event', 'engine.end', () => {
360
+ clearConsumers();
361
+ return callback(null, this);
362
+ }, {consumerTag: 'ctag-cb-end'});
299
363
 
300
- stopped = false;
301
- logger.debug(`<${engine.name}> resume`);
302
- addConsumerCallbacks(callback);
364
+ broker.subscribeOnce('event', 'engine.error', (_, message) => {
365
+ clearConsumers();
366
+ return callback(message.content);
367
+ }, {consumerTag: 'ctag-cb-error'});
303
368
 
304
- executing.splice(0);
305
- definitions.forEach((definition) => definition.resume());
369
+ return callback;
306
370
 
307
- return Api();
371
+ function clearConsumers() {
372
+ broker.cancel('ctag-cb-stop');
373
+ broker.cancel('ctag-cb-end');
374
+ broker.cancel('ctag-cb-error');
375
+ broker.on('return', onBrokerReturn);
308
376
  }
377
+ };
309
378
 
310
- function addConsumerCallbacks(callback) {
311
- if (!callback) return;
379
+ Execution.prototype.stop = async function stop() {
380
+ const engine = this[kEngine];
381
+ const prom = engine.waitFor('stop');
382
+ this[kStopped] = true;
383
+ const timers = engine.environment.timers;
312
384
 
313
- broker.off('return', onBrokerReturn);
385
+ timers.executing.slice().forEach((ref) => timers.clearTimeout(ref));
314
386
 
315
- clearConsumers();
387
+ this[kExecuting].splice(0).forEach((d) => d.stop());
316
388
 
317
- broker.subscribeOnce('event', 'engine.stop', cbLeave, {consumerTag: 'ctag-cb-stop'});
318
- broker.subscribeOnce('event', 'engine.end', cbLeave, {consumerTag: 'ctag-cb-end'});
319
- broker.subscribeOnce('event', 'engine.error', cbError, {consumerTag: 'ctag-cb-error'});
389
+ const result = await prom;
390
+ this[kState] = 'stopped';
391
+ return result;
392
+ };
320
393
 
321
- return callback;
394
+ Execution.prototype._setup = function setup(setupOptions = {}) {
395
+ const listener = setupOptions.listener || this.options.listener;
396
+ if (listener && typeof listener.emit !== 'function') throw new Error('listener.emit is not a function');
322
397
 
323
- function cbLeave() {
324
- clearConsumers();
325
- return callback(null, Api());
326
- }
327
- function cbError(_, message) {
328
- clearConsumers();
329
- return callback(message.content);
330
- }
398
+ const onChildMessage = this._onChildMessage.bind(this);
331
399
 
332
- function clearConsumers() {
333
- broker.cancel('ctag-cb-stop');
334
- broker.cancel('ctag-cb-end');
335
- broker.cancel('ctag-cb-error');
336
- broker.on('return', onBrokerReturn);
337
- }
338
- }
400
+ for (const definition of this.definitions) {
401
+ if (listener) definition.environment.options.listener = listener;
339
402
 
340
- async function stop() {
341
- const prom = waitFor('stop');
342
- stopped = true;
343
- const timers = environment.timers;
344
- timers.executing.slice().forEach((ref) => timers.clearTimeout(ref));
345
- executing.splice(0).forEach((d) => d.stop());
346
- const result = await prom;
347
- state = 'stopped';
348
- return result;
403
+ definition.broker.subscribeTmp('event', 'definition.#', onChildMessage, {noAck: true, consumerTag: '_engine_definition'});
404
+ definition.broker.subscribeTmp('event', 'process.#', onChildMessage, {noAck: true, consumerTag: '_engine_process'});
405
+ definition.broker.subscribeTmp('event', 'activity.#', onChildMessage, {noAck: true, consumerTag: '_engine_activity'});
406
+ definition.broker.subscribeTmp('event', 'flow.#', onChildMessage, {noAck: true, consumerTag: '_engine_flow'});
349
407
  }
408
+ };
409
+
410
+ Execution.prototype._onChildMessage = function onChildMessage(routingKey, message, owner) {
411
+ const {environment: ownerEnvironment} = owner;
412
+ const listener = ownerEnvironment.options && ownerEnvironment.options.listener;
413
+ this[kState] = 'running';
414
+
415
+ let newState;
416
+ const elementApi = owner.getApi && owner.getApi(message);
417
+
418
+ switch (routingKey) {
419
+ case 'definition.resume':
420
+ case 'definition.enter': {
421
+ const executing = this[kExecuting];
422
+ const idx = executing.indexOf(owner);
423
+ if (idx > -1) break;
424
+ executing.push(owner);
425
+ break;
426
+ }
427
+ case 'definition.stop': {
428
+ this._teardownDefinition(owner);
350
429
 
351
- function setup(setupOptions = {}) {
352
- const listener = setupOptions.listener || options.listener;
353
- if (listener && typeof listener.emit !== 'function') throw new Error('listener.emit is not a function');
354
-
355
- definitions.forEach(setupDefinition);
356
-
357
- function setupDefinition(definition) {
358
- if (listener) definition.environment.options.listener = listener;
430
+ const executing = this[kExecuting];
431
+ if (executing.some((d) => d.isRunning)) break;
359
432
 
360
- definition.broker.subscribeTmp('event', 'definition.#', onChildMessage, {noAck: true, consumerTag: '_engine_definition'});
361
- definition.broker.subscribeTmp('event', 'process.#', onChildMessage, {noAck: true, consumerTag: '_engine_process'});
362
- definition.broker.subscribeTmp('event', 'activity.#', onChildMessage, {noAck: true, consumerTag: '_engine_activity'});
363
- definition.broker.subscribeTmp('event', 'flow.#', onChildMessage, {noAck: true, consumerTag: '_engine_flow'});
433
+ newState = 'stopped';
434
+ this[kStopped] = true;
435
+ break;
364
436
  }
365
- }
366
-
367
- function onChildMessage(routingKey, message, owner) {
368
- const {environment: ownerEnvironment} = owner;
369
- const listener = ownerEnvironment.options && ownerEnvironment.options.listener;
370
- state = 'running';
371
-
372
- let executionStopped, executionCompleted, executionErrored;
373
- const elementApi = owner.getApi && owner.getApi(message);
374
-
375
- switch (routingKey) {
376
- case 'definition.resume':
377
- case 'definition.enter': {
378
- const idx = executing.indexOf(owner);
379
- if (idx > -1) break;
380
- executing.push(owner);
381
- break;
382
- }
383
- case 'definition.stop':
384
- teardownDefinition(owner);
385
- if (executing.some((d) => d.isRunning)) break;
386
-
387
- executionStopped = true;
388
- stopped = true;
389
- break;
390
- case 'definition.leave':
391
- teardownDefinition(owner);
392
-
393
- if (executing.some((d) => d.isRunning)) break;
394
-
395
- executionCompleted = true;
396
- break;
397
- case 'definition.error':
398
- teardownDefinition(owner);
399
- executionErrored = true;
400
- break;
401
- case 'activity.wait': {
402
- emitListenerEvent('wait', owner.getApi(message), Api());
403
- break;
404
- }
405
- case 'process.end': {
406
- if (!message.content.output) break;
407
- for (const key in message.content.output) {
408
- switch (key) {
409
- case 'data': {
410
- environment.output.data = environment.output.data || {};
411
- environment.output.data = {...environment.output.data, ...message.content.output.data};
412
- break;
413
- }
414
- default: {
415
- environment.output[key] = message.content.output[key];
416
- }
437
+ case 'definition.leave':
438
+ this._teardownDefinition(owner);
439
+
440
+ if (this[kExecuting].some((d) => d.isRunning)) break;
441
+
442
+ newState = 'idle';
443
+ break;
444
+ case 'definition.error':
445
+ this._teardownDefinition(owner);
446
+ newState = 'error';
447
+ break;
448
+ case 'activity.wait': {
449
+ if (listener) listener.emit('wait', owner.getApi(message), this);
450
+ break;
451
+ }
452
+ case 'process.end': {
453
+ if (!message.content.output) break;
454
+ const environment = this.environment;
455
+
456
+ for (const key in message.content.output) {
457
+ switch (key) {
458
+ case 'data': {
459
+ environment.output.data = environment.output.data || {};
460
+ environment.output.data = {...environment.output.data, ...message.content.output.data};
461
+ break;
462
+ }
463
+ default: {
464
+ environment.output[key] = message.content.output[key];
417
465
  }
418
466
  }
419
- break;
420
467
  }
468
+ break;
421
469
  }
470
+ }
422
471
 
423
- emitListenerEvent(routingKey, elementApi, Api());
424
- broker.publish('event', routingKey, {...message.content}, {...message.properties, mandatory: false});
425
-
426
- if (executionStopped) {
427
- state = 'stopped';
428
- logger.debug(`<${engine.name}> stopped`);
429
- onComplete('stop');
430
- } else if (executionCompleted) {
431
- state = 'idle';
432
- logger.debug(`<${engine.name}> completed`);
433
- onComplete('end');
434
- } else if (executionErrored) {
435
- state = 'error';
436
- logger.debug(`<${engine.name}> error`);
437
- onError(message.content.error);
438
- }
472
+ if (listener) listener.emit(routingKey, elementApi, this);
439
473
 
440
- function onComplete(eventName) {
441
- broker.publish('event', `engine.${eventName}`, {}, {type: eventName});
442
- engine.emit(eventName, Api());
443
- }
474
+ const broker = this.broker;
475
+ broker.publish('event', routingKey, {...message.content}, {...message.properties, mandatory: false});
444
476
 
445
- function onError(err) {
446
- broker.publish('event', 'engine.error', err, {type: 'error', mandatory: true});
447
- }
477
+ if (!newState) return;
448
478
 
449
- function emitListenerEvent(...args) {
450
- if (!listener) return;
451
- listener.emit(...args);
452
- }
453
- }
479
+ this[kState] = newState;
454
480
 
455
- function teardownDefinition(definition) {
456
- const idx = executing.indexOf(definition);
457
- if (idx > -1) executing.splice(idx, 1);
458
-
459
- definition.broker.cancel('_engine_definition');
460
- definition.broker.cancel('_engine_process');
461
- definition.broker.cancel('_engine_activity');
462
- definition.broker.cancel('_engine_flow');
481
+ switch (newState) {
482
+ case 'stopped':
483
+ this._debug('stopped');
484
+ broker.publish('event', 'engine.stop', {}, {type: 'stop'});
485
+ return this[kEngine].emit('stop', this);
486
+ case 'idle':
487
+ this._debug('completed');
488
+ broker.publish('event', 'engine.end', {}, {type: 'end'});
489
+ return this[kEngine].emit('end', this);
490
+ case 'error':
491
+ this._debug('error');
492
+ return broker.publish('event', 'engine.error', message.content.error, {type: 'error', mandatory: true});
463
493
  }
464
-
465
- function getState() {
466
- return {
467
- name: engine.name,
468
- state,
469
- stopped,
470
- engineVersion,
471
- environment: environment.getState(),
472
- definitions: definitions.map(getDefinitionState),
473
- };
494
+ };
495
+
496
+ Execution.prototype._teardownDefinition = function teardownDefinition(definition) {
497
+ const executing = this[kExecuting];
498
+ const idx = executing.indexOf(definition);
499
+ if (idx > -1) executing.splice(idx, 1);
500
+
501
+ definition.broker.cancel('_engine_definition');
502
+ definition.broker.cancel('_engine_process');
503
+ definition.broker.cancel('_engine_activity');
504
+ definition.broker.cancel('_engine_flow');
505
+ };
506
+
507
+ Execution.prototype.getState = function getState() {
508
+ const definitions = [];
509
+ for (const definition of this.definitions) {
510
+ definitions.push({
511
+ ...definition.getState(),
512
+ source: definition.environment.options.source.serialize(),
513
+ });
474
514
  }
475
515
 
476
- function getActivityById(activityId) {
477
- for (const definition of definitions) {
478
- const activity = definition.getActivityById(activityId);
479
- if (activity) return activity;
480
- }
481
- }
516
+ return {
517
+ name: this[kEngine].name,
518
+ state: this.state,
519
+ stopped: this.stopped,
520
+ engineVersion,
521
+ environment: this.environment.getState(),
522
+ definitions,
523
+ };
524
+ };
482
525
 
483
- function getPostponed() {
484
- const defs = stopped ? definitions : executing;
485
- return defs.reduce((result, definition) => {
486
- result = result.concat(definition.getPostponed());
487
- return result;
488
- }, []);
526
+ Execution.prototype.getActivityById = function getActivityById(activityId) {
527
+ for (const definition of this.definitions) {
528
+ const activity = definition.getActivityById(activityId);
529
+ if (activity) return activity;
489
530
  }
531
+ };
490
532
 
491
- function signal(payload, {ignoreSameDefinition} = {}) {
492
- for (const definition of executing) {
493
- if (ignoreSameDefinition && payload && payload.parent && payload.parent.id === definition.id) continue;
494
- definition.signal(payload);
495
- }
496
- }
533
+ Execution.prototype.getPostponed = function getPostponed() {
534
+ const defs = this.stopped ? this.definitions : this[kExecuting];
535
+ return defs.reduce((result, definition) => {
536
+ result = result.concat(definition.getPostponed());
537
+ return result;
538
+ }, []);
539
+ };
497
540
 
498
- function cancelActivity(payload) {
499
- for (const definition of executing) {
500
- definition.cancelActivity(payload);
501
- }
541
+ Execution.prototype.signal = function signal(payload, {ignoreSameDefinition} = {}) {
542
+ for (const definition of this[kExecuting]) {
543
+ if (ignoreSameDefinition && payload && payload.parent && payload.parent.id === definition.id) continue;
544
+ definition.signal(payload);
502
545
  }
546
+ };
503
547
 
504
- function getDefinitionState(definition) {
505
- return {
506
- ...definition.getState(),
507
- source: definition.environment.options.source.serialize(),
508
- };
548
+ Execution.prototype.cancelActivity = function cancelActivity(payload) {
549
+ for (const definition of this[kExecuting]) {
550
+ definition.cancelActivity(payload);
509
551
  }
552
+ };
510
553
 
511
- function onBrokerReturn(message) {
512
- if (message.properties.type === 'error') {
513
- engine.emit('error', message.content);
514
- }
515
- }
554
+ Execution.prototype.waitFor = function waitFor(...args) {
555
+ return this[kEngine].waitFor(...args);
556
+ };
516
557
 
517
- function Api() {
518
- return {
519
- name: engine.name,
520
- get state() {
521
- return state;
522
- },
523
- get stopped() {
524
- return stopped;
525
- },
526
- broker,
527
- environment,
528
- definitions,
529
- getActivityById,
530
- getState,
531
- getPostponed,
532
- signal,
533
- cancelActivity,
534
- stop,
535
- waitFor,
536
- };
558
+ Execution.prototype._onBrokerReturn = function onBrokerReturn(message) {
559
+ if (message.properties.type === 'error') {
560
+ this[kEngine].emit('error', message.content);
537
561
  }
538
- }
562
+ };
563
+
564
+ Execution.prototype._debug = function debug(msg) {
565
+ this[kEngine].logger.debug(`<${this.name}> ${msg}`);
566
+ };
@@ -2,50 +2,49 @@
2
2
 
3
3
  const {Script} = require('vm');
4
4
 
5
- module.exports = function Scripts(disableDummy) {
6
- const scripts = {};
5
+ module.exports = Scripts;
7
6
 
8
- return {
9
- getScript,
10
- register,
11
- };
7
+ function Scripts(disableDummy) {
8
+ if (!(this instanceof Scripts)) return new Scripts(disableDummy);
9
+ this.scripts = {};
10
+ this.disableDummy = disableDummy;
11
+ }
12
12
 
13
- function register({id, type, behaviour, logger, environment}) {
14
- let scriptBody, language;
13
+ Scripts.prototype.register = function register({id, type, behaviour, logger, environment}) {
14
+ let scriptBody, language;
15
15
 
16
- switch (type) {
17
- case 'bpmn:SequenceFlow': {
18
- if (!behaviour.conditionExpression) return;
19
- language = behaviour.conditionExpression.language;
20
- if (!language) return;
21
- scriptBody = behaviour.conditionExpression.body;
22
- break;
23
- }
24
- default: {
25
- language = behaviour.scriptFormat;
26
- scriptBody = behaviour.script;
27
- }
16
+ switch (type) {
17
+ case 'bpmn:SequenceFlow': {
18
+ if (!behaviour.conditionExpression) return;
19
+ language = behaviour.conditionExpression.language;
20
+ if (!language) return;
21
+ scriptBody = behaviour.conditionExpression.body;
22
+ break;
28
23
  }
29
-
30
- const filename = `${type}/${id}`;
31
- if (!language || !scriptBody) {
32
- if (disableDummy) return;
33
- const script = new DummyScript(language, filename, logger);
34
- scripts[id] = script;
35
- return script;
24
+ default: {
25
+ language = behaviour.scriptFormat;
26
+ scriptBody = behaviour.script;
36
27
  }
28
+ }
37
29
 
38
- if (!/^javascript$/i.test(language)) return;
39
-
40
- const script = new JavaScript(language, filename, scriptBody, environment);
41
- scripts[id] = script;
42
-
30
+ const filename = `${type}/${id}`;
31
+ if (!language || !scriptBody) {
32
+ if (this.disableDummy) return;
33
+ const script = new DummyScript(language, filename, logger);
34
+ this.scripts[id] = script;
43
35
  return script;
44
36
  }
45
37
 
46
- function getScript(language, {id}) {
47
- return scripts[id];
48
- }
38
+ if (!/^javascript$/i.test(language)) return;
39
+
40
+ const script = new JavaScript(language, filename, scriptBody, environment);
41
+ this.scripts[id] = script;
42
+
43
+ return script;
44
+ };
45
+
46
+ Scripts.prototype.getScript = function getScript(language, {id}) {
47
+ return this.scripts[id];
49
48
  };
50
49
 
51
50
  function JavaScript(language, filename, scriptBody, environment) {
@@ -19,7 +19,6 @@ module.exports = function ProcessOutputDataObject(dataObjectDef, {environment})
19
19
 
20
20
  environment.output.data = environment.output.data || {};
21
21
  environment.output.data[id] = value;
22
-
23
22
  return broker.publish(exchange, `${routingKeyPrefix}response`, {id, name, type, value}, messageProperties);
24
23
  },
25
24
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bpmn-engine",
3
3
  "description": "BPMN 2.0 execution engine. Open source javascript workflow engine.",
4
- "version": "13.0.2",
4
+ "version": "14.0.0",
5
5
  "main": "index.js",
6
6
  "types": "types/bpmn-engine.d.ts",
7
7
  "repository": {
@@ -51,21 +51,21 @@
51
51
  },
52
52
  "devDependencies": {
53
53
  "bent": "^7.3.12",
54
- "camunda-bpmn-moddle": "^6.1.0",
55
- "chai": "^4.3.0",
54
+ "camunda-bpmn-moddle": "^6.1.2",
55
+ "chai": "^4.3.6",
56
56
  "chronokinesis": "^3.0.0",
57
57
  "eslint": "^7.23.0",
58
58
  "markdown-toc": "^1.2.0",
59
- "mocha": "^9.1.2",
59
+ "mocha": "^9.2.2",
60
60
  "mocha-cakes-2": "^3.3.0",
61
- "nock": "^13.0.7",
61
+ "nock": "^13.2.4",
62
62
  "nyc": "^15.1.0"
63
63
  },
64
64
  "dependencies": {
65
- "bpmn-elements": "^6.0.1",
65
+ "bpmn-elements": "^8.0.0",
66
66
  "bpmn-moddle": "^7.0.4",
67
- "debug": "^4.3.1",
68
- "moddle-context-serializer": "^1.1.1",
69
- "smqp": "^5.1.0"
67
+ "debug": "^4.3.4",
68
+ "moddle-context-serializer": "^2.0.0",
69
+ "smqp": "^6.0.0"
70
70
  }
71
71
  }