bpmn-elements 18.0.2 → 18.0.4

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.
@@ -8,10 +8,12 @@ var _ProcessExecution = require("./ProcessExecution.js");
8
8
  var _shared = require("../shared.js");
9
9
  var _Api = require("../Api.js");
10
10
  var _EventBroker = require("../EventBroker.js");
11
+ var _MessageFormatter = require("../MessageFormatter.js");
11
12
  var _messageHelper = require("../messageHelper.js");
12
13
  var _Errors = require("../error/Errors.js");
13
14
  var _constants = require("../constants.js");
14
15
  const K_LANES = Symbol.for('lanes');
16
+ const K_FORMATTER = Symbol.for('formatter');
15
17
 
16
18
  /**
17
19
  * Owns one `<bpmn:process>`. Wraps the structural definition and orchestrates flow traversal,
@@ -49,12 +51,14 @@ function Process(processDef, context) {
49
51
  broker,
50
52
  on,
51
53
  once,
52
- waitFor
54
+ waitFor,
55
+ emitFatal
53
56
  } = (0, _EventBroker.ProcessBroker)(this);
54
57
  this.broker = broker;
55
58
  this.on = on;
56
59
  this.once = once;
57
60
  this.waitFor = waitFor;
61
+ this.emitFatal = emitFatal;
58
62
  this[_constants.K_MESSAGE_HANDLERS] = {
59
63
  onApiMessage: this._onApiMessage.bind(this),
60
64
  onRunMessage: this._onRunMessage.bind(this),
@@ -84,6 +88,14 @@ Object.defineProperties(Process.prototype, {
84
88
  return this[_constants.K_EXTENSIONS];
85
89
  }
86
90
  },
91
+ formatter: {
92
+ get() {
93
+ let formatter = this[K_FORMATTER];
94
+ if (formatter) return formatter;
95
+ formatter = this[K_FORMATTER] = new _MessageFormatter.Formatter(this);
96
+ return formatter;
97
+ }
98
+ },
87
99
  stopped: {
88
100
  get() {
89
101
  return this[_constants.K_STOPPED];
@@ -292,14 +304,28 @@ Process.prototype._deactivateRunConsumers = function deactivateRunConsumers() {
292
304
  };
293
305
 
294
306
  /** @internal */
295
- Process.prototype._onRunMessage = function onRunMessage(routingKey, message) {
307
+ Process.prototype._onRunMessage = function onRunMessage(routingKey, message, messageProperties) {
308
+ if (routingKey === 'run.resume') {
309
+ return this._onResumeMessage(message);
310
+ }
311
+ const preStatus = this[_constants.K_STATUS];
312
+ this[_constants.K_STATUS] = 'formatting';
313
+ return this.formatter.format(message, (err, formattedContent, formatted) => {
314
+ this[_constants.K_STATUS] = preStatus;
315
+ if (err) {
316
+ return this.emitFatal(err, message.content);
317
+ }
318
+ if (formatted) message.content = formattedContent;
319
+ this._continueRunMessage(routingKey, message, messageProperties);
320
+ });
321
+ };
322
+
323
+ /** @internal */
324
+ Process.prototype._continueRunMessage = function continueRunMessage(routingKey, message) {
296
325
  const {
297
326
  content,
298
327
  fields
299
328
  } = message;
300
- if (routingKey === 'run.resume') {
301
- return this._onResumeMessage(message);
302
- }
303
329
  this[_constants.K_STATE_MESSAGE] = message;
304
330
  switch (routingKey) {
305
331
  case 'run.enter':
@@ -308,6 +334,7 @@ Process.prototype._onRunMessage = function onRunMessage(routingKey, message) {
308
334
  this[_constants.K_STATUS] = 'entered';
309
335
  if (fields.redelivered) break;
310
336
  this[_constants.K_EXECUTION].delete('execution');
337
+ if (this.extensions) this.extensions.activate((0, _messageHelper.cloneMessage)(message));
311
338
  this._publishEvent('enter', content);
312
339
  break;
313
340
  }
@@ -322,6 +349,7 @@ Process.prototype._onRunMessage = function onRunMessage(routingKey, message) {
322
349
  {
323
350
  const exec = this[_constants.K_EXECUTION];
324
351
  this[_constants.K_STATUS] = 'executing';
352
+ if (fields.redelivered && this.extensions) this.extensions.activate((0, _messageHelper.cloneMessage)(message));
325
353
  const executeMessage = (0, _messageHelper.cloneMessage)(message);
326
354
  let execution = exec.get('execution');
327
355
  if (fields.redelivered && !execution) {
@@ -366,6 +394,7 @@ Process.prototype._onRunMessage = function onRunMessage(routingKey, message) {
366
394
  case 'run.leave':
367
395
  {
368
396
  this[_constants.K_STATUS] = undefined;
397
+ if (this.extensions) this.extensions.deactivate((0, _messageHelper.cloneMessage)(message));
369
398
  message.ack();
370
399
  this._deactivateRunConsumers();
371
400
  const {
@@ -394,6 +423,7 @@ Process.prototype._onResumeMessage = function onResumeMessage(message) {
394
423
  return;
395
424
  }
396
425
  if (!stateMessage.fields.redelivered) return;
426
+ if (this.extensions) this.extensions.activate((0, _messageHelper.cloneMessage)(stateMessage));
397
427
  this._debug(`resume from ${this.status}`);
398
428
  return this.broker.publish('run', stateMessage.fields.routingKey, (0, _messageHelper.cloneContent)(stateMessage.content), stateMessage.properties);
399
429
  };
@@ -530,6 +560,7 @@ Process.prototype._onApiMessage = function onApiMessage(routingKey, message) {
530
560
  Process.prototype._onStop = function onStop() {
531
561
  this[_constants.K_STOPPED] = true;
532
562
  this._deactivateRunConsumers();
563
+ if (this.extensions) this.extensions.deactivate((0, _messageHelper.cloneMessage)(this[_constants.K_STATE_MESSAGE]));
533
564
  return this._publishEvent('stop');
534
565
  };
535
566
 
@@ -116,6 +116,16 @@ ProcessExecution.prototype.execute = function execute(executeMessage) {
116
116
  });
117
117
  this[_constants.K_STOPPED] = false;
118
118
  this.environment.assignVariables(executeMessage);
119
+
120
+ // Seed input from the execute content (sub process) or a single inbound trigger (call activity forwarding its formatted input).
121
+ const content = executeMessage.content;
122
+ const inbound = content.inbound;
123
+ const input = content.input ?? (inbound?.length === 1 && inbound[0].input);
124
+ if (input) {
125
+ this.environment.assignVariables({
126
+ input
127
+ });
128
+ }
119
129
  this[K_ACTIVITY_Q] = this.broker.assertQueue(`execute-${executionId}-q`, {
120
130
  durable: true,
121
131
  autoDelete: false
@@ -73,10 +73,25 @@ CallActivityBehaviour.prototype.execute = function execute(executeMessage) {
73
73
  noAck: true,
74
74
  consumerTag: `_api-delegated-cancel-${executionId}`
75
75
  });
76
- broker.publish('event', 'activity.call', (0, _messageHelper.cloneContent)(executeContent, {
76
+ const callContent = {
77
77
  state: 'wait',
78
78
  calledElement
79
- }), {
79
+ };
80
+
81
+ // Forward the multi-instance loop context as input to the called process; any current content input takes precedence.
82
+ if (executeContent.isMultiInstance) {
83
+ const input = {
84
+ isSequential: executeContent.isSequential,
85
+ index: executeContent.index,
86
+ cardinality: executeContent.loopCardinality
87
+ };
88
+ const elementVariable = loopCharacteristics?.elementVariable;
89
+ if (elementVariable && elementVariable in executeContent) {
90
+ input[elementVariable] = executeContent[elementVariable];
91
+ }
92
+ callContent.input = Object.assign(input, executeContent.input);
93
+ }
94
+ broker.publish('event', 'activity.call', (0, _messageHelper.cloneContent)(executeContent, callContent), {
80
95
  type: 'call'
81
96
  });
82
97
  };
@@ -96,8 +96,29 @@ SubProcessBehaviour.prototype.execute = function execute(executeMessage) {
96
96
  if (loopCharacteristics && isRootScope) {
97
97
  return loopCharacteristics.execute(executeMessage);
98
98
  }
99
- const processExecution = this._upsertExecution(executeMessage);
100
- return processExecution.execute(executeMessage);
99
+
100
+ // Forward the multi-instance loop context as input to the sub process execution; any current content input takes precedence.
101
+ let message = executeMessage;
102
+ const content = executeMessage.content;
103
+ if (content.isMultiInstance) {
104
+ const input = {
105
+ isSequential: content.isSequential,
106
+ index: content.index,
107
+ cardinality: content.loopCardinality
108
+ };
109
+ const elementVariable = loopCharacteristics?.elementVariable;
110
+ if (elementVariable && elementVariable in content) {
111
+ input[elementVariable] = content[elementVariable];
112
+ }
113
+ message = (0, _messageHelper.cloneMessage)(executeMessage, {
114
+ input: {
115
+ ...input,
116
+ ...content.input
117
+ }
118
+ });
119
+ }
120
+ const processExecution = this._upsertExecution(message);
121
+ return processExecution.execute(message);
101
122
  };
102
123
  SubProcessBehaviour.prototype.getState = function getState() {
103
124
  const states = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bpmn-elements",
3
- "version": "18.0.2",
3
+ "version": "18.0.4",
4
4
  "description": "Executable workflow elements based on BPMN 2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -2,6 +2,7 @@ import { ProcessExecution } from './ProcessExecution.js';
2
2
  import { getUniqueId } from '../shared.js';
3
3
  import { ProcessApi } from '../Api.js';
4
4
  import { ProcessBroker } from '../EventBroker.js';
5
+ import { Formatter } from '../MessageFormatter.js';
5
6
  import { cloneMessage, cloneContent, cloneParent } from '../messageHelper.js';
6
7
  import { makeErrorFromMessage } from '../error/Errors.js';
7
8
  import {
@@ -17,6 +18,7 @@ import {
17
18
  } from '../constants.js';
18
19
 
19
20
  const K_LANES = Symbol.for('lanes');
21
+ const K_FORMATTER = Symbol.for('formatter');
20
22
 
21
23
  /**
22
24
  * Owns one `<bpmn:process>`. Wraps the structural definition and orchestrates flow traversal,
@@ -47,11 +49,12 @@ export function Process(processDef, context) {
47
49
  this[K_STATUS] = undefined;
48
50
  this[K_STOPPED] = false;
49
51
 
50
- const { broker, on, once, waitFor } = ProcessBroker(this);
52
+ const { broker, on, once, waitFor, emitFatal } = ProcessBroker(this);
51
53
  this.broker = broker;
52
54
  this.on = on;
53
55
  this.once = once;
54
56
  this.waitFor = waitFor;
57
+ this.emitFatal = emitFatal;
55
58
 
56
59
  this[K_MESSAGE_HANDLERS] = {
57
60
  onApiMessage: this._onApiMessage.bind(this),
@@ -83,6 +86,14 @@ Object.defineProperties(Process.prototype, {
83
86
  return this[K_EXTENSIONS];
84
87
  },
85
88
  },
89
+ formatter: {
90
+ get() {
91
+ let formatter = this[K_FORMATTER];
92
+ if (formatter) return formatter;
93
+ formatter = this[K_FORMATTER] = new Formatter(this);
94
+ return formatter;
95
+ },
96
+ },
86
97
  stopped: {
87
98
  get() {
88
99
  return this[K_STOPPED];
@@ -278,13 +289,28 @@ Process.prototype._deactivateRunConsumers = function deactivateRunConsumers() {
278
289
  };
279
290
 
280
291
  /** @internal */
281
- Process.prototype._onRunMessage = function onRunMessage(routingKey, message) {
282
- const { content, fields } = message;
283
-
292
+ Process.prototype._onRunMessage = function onRunMessage(routingKey, message, messageProperties) {
284
293
  if (routingKey === 'run.resume') {
285
294
  return this._onResumeMessage(message);
286
295
  }
287
296
 
297
+ const preStatus = this[K_STATUS];
298
+ this[K_STATUS] = 'formatting';
299
+
300
+ return this.formatter.format(message, (err, formattedContent, formatted) => {
301
+ this[K_STATUS] = preStatus;
302
+ if (err) {
303
+ return this.emitFatal(err, message.content);
304
+ }
305
+ if (formatted) message.content = formattedContent;
306
+ this._continueRunMessage(routingKey, message, messageProperties);
307
+ });
308
+ };
309
+
310
+ /** @internal */
311
+ Process.prototype._continueRunMessage = function continueRunMessage(routingKey, message) {
312
+ const { content, fields } = message;
313
+
288
314
  this[K_STATE_MESSAGE] = message;
289
315
 
290
316
  switch (routingKey) {
@@ -295,6 +321,7 @@ Process.prototype._onRunMessage = function onRunMessage(routingKey, message) {
295
321
  if (fields.redelivered) break;
296
322
 
297
323
  this[K_EXECUTION].delete('execution');
324
+ if (this.extensions) this.extensions.activate(cloneMessage(message));
298
325
  this._publishEvent('enter', content);
299
326
 
300
327
  break;
@@ -308,6 +335,7 @@ Process.prototype._onRunMessage = function onRunMessage(routingKey, message) {
308
335
  case 'run.execute': {
309
336
  const exec = this[K_EXECUTION];
310
337
  this[K_STATUS] = 'executing';
338
+ if (fields.redelivered && this.extensions) this.extensions.activate(cloneMessage(message));
311
339
  const executeMessage = cloneMessage(message);
312
340
  let execution = exec.get('execution');
313
341
  if (fields.redelivered && !execution) {
@@ -360,6 +388,7 @@ Process.prototype._onRunMessage = function onRunMessage(routingKey, message) {
360
388
  }
361
389
  case 'run.leave': {
362
390
  this[K_STATUS] = undefined;
391
+ if (this.extensions) this.extensions.deactivate(cloneMessage(message));
363
392
  message.ack();
364
393
  this._deactivateRunConsumers();
365
394
  const { output, ...rest } = content;
@@ -389,6 +418,8 @@ Process.prototype._onResumeMessage = function onResumeMessage(message) {
389
418
 
390
419
  if (!stateMessage.fields.redelivered) return;
391
420
 
421
+ if (this.extensions) this.extensions.activate(cloneMessage(stateMessage));
422
+
392
423
  this._debug(`resume from ${this.status}`);
393
424
 
394
425
  return this.broker.publish('run', stateMessage.fields.routingKey, cloneContent(stateMessage.content), stateMessage.properties);
@@ -518,6 +549,7 @@ Process.prototype._onApiMessage = function onApiMessage(routingKey, message) {
518
549
  Process.prototype._onStop = function onStop() {
519
550
  this[K_STOPPED] = true;
520
551
  this._deactivateRunConsumers();
552
+ if (this.extensions) this.extensions.deactivate(cloneMessage(this[K_STATE_MESSAGE]));
521
553
  return this._publishEvent('stop');
522
554
  };
523
555
 
@@ -112,6 +112,15 @@ ProcessExecution.prototype.execute = function execute(executeMessage) {
112
112
  this[K_STOPPED] = false;
113
113
 
114
114
  this.environment.assignVariables(executeMessage);
115
+
116
+ // Seed input from the execute content (sub process) or a single inbound trigger (call activity forwarding its formatted input).
117
+ const content = executeMessage.content;
118
+ const inbound = content.inbound;
119
+ const input = content.input ?? (inbound?.length === 1 && inbound[0].input);
120
+ if (input) {
121
+ this.environment.assignVariables({ input });
122
+ }
123
+
115
124
  this[K_ACTIVITY_Q] = this.broker.assertQueue(`execute-${executionId}-q`, { durable: true, autoDelete: false });
116
125
 
117
126
  if (executeMessage.fields.redelivered) {
@@ -80,17 +80,28 @@ CallActivityBehaviour.prototype.execute = function execute(executeMessage) {
80
80
  consumerTag: `_api-delegated-cancel-${executionId}`,
81
81
  });
82
82
 
83
- broker.publish(
84
- 'event',
85
- 'activity.call',
86
- cloneContent(executeContent, {
87
- state: 'wait',
88
- calledElement,
89
- }),
90
- {
91
- type: 'call',
83
+ const callContent = {
84
+ state: 'wait',
85
+ calledElement,
86
+ };
87
+
88
+ // Forward the multi-instance loop context as input to the called process; any current content input takes precedence.
89
+ if (executeContent.isMultiInstance) {
90
+ const input = {
91
+ isSequential: executeContent.isSequential,
92
+ index: executeContent.index,
93
+ cardinality: executeContent.loopCardinality,
94
+ };
95
+ const elementVariable = loopCharacteristics?.elementVariable;
96
+ if (elementVariable && elementVariable in executeContent) {
97
+ input[elementVariable] = executeContent[elementVariable];
92
98
  }
93
- );
99
+ callContent.input = Object.assign(input, executeContent.input);
100
+ }
101
+
102
+ broker.publish('event', 'activity.call', cloneContent(executeContent, callContent), {
103
+ type: 'call',
104
+ });
94
105
  };
95
106
 
96
107
  CallActivityBehaviour.prototype._onDelegatedApiMessage = function onDelegatedApiMessage(
@@ -1,6 +1,6 @@
1
1
  import { Activity } from '../activity/Activity.js';
2
2
  import { ProcessExecution } from '../process/ProcessExecution.js';
3
- import { cloneContent } from '../messageHelper.js';
3
+ import { cloneContent, cloneMessage } from '../messageHelper.js';
4
4
 
5
5
  const K_EXECUTIONS = Symbol.for('executions');
6
6
  const K_ON_EXECUTION_COMPLETED = Symbol.for('execution completed handler');
@@ -81,8 +81,24 @@ SubProcessBehaviour.prototype.execute = function execute(executeMessage) {
81
81
  return loopCharacteristics.execute(executeMessage);
82
82
  }
83
83
 
84
- const processExecution = this._upsertExecution(executeMessage);
85
- return processExecution.execute(executeMessage);
84
+ // Forward the multi-instance loop context as input to the sub process execution; any current content input takes precedence.
85
+ let message = executeMessage;
86
+ const content = executeMessage.content;
87
+ if (content.isMultiInstance) {
88
+ const input = {
89
+ isSequential: content.isSequential,
90
+ index: content.index,
91
+ cardinality: content.loopCardinality,
92
+ };
93
+ const elementVariable = loopCharacteristics?.elementVariable;
94
+ if (elementVariable && elementVariable in content) {
95
+ input[elementVariable] = content[elementVariable];
96
+ }
97
+ message = cloneMessage(executeMessage, { input: { ...input, ...content.input } });
98
+ }
99
+
100
+ const processExecution = this._upsertExecution(message);
101
+ return processExecution.execute(message);
86
102
  };
87
103
 
88
104
  SubProcessBehaviour.prototype.getState = function getState() {
package/types/index.d.ts CHANGED
@@ -261,6 +261,8 @@ declare module 'bpmn-elements' {
261
261
  export enum ProcessStatusValue {
262
262
  /** ProcessExecution constructed, not yet started */
263
263
  Init = 'init',
264
+ /** Formatting next run message */
265
+ Formatting = 'formatting',
264
266
  /** Process run entered */
265
267
  Entered = 'entered',
266
268
  /** Process run started */
@@ -1458,6 +1460,7 @@ declare module 'bpmn-elements' {
1458
1460
  [x: string]: any;
1459
1461
  }) => import("smqp").Consumer;
1460
1462
  waitFor: (eventName: string, onMessage?: ((routingKey: string, message: ElementBrokerMessage, owner: Process) => boolean) | undefined) => Promise<IApi<Process>>;
1463
+ emitFatal: (error: Error, content?: Record<string, any>) => void;
1461
1464
  logger: ILogger;
1462
1465
  /**
1463
1466
  * Allocate an executionId and emit init event without starting the run.
@@ -359,6 +359,8 @@ export type DefinitionStatus = DefinitionStatusValue | `${DefinitionStatusValue}
359
359
  export const enum ProcessStatusValue {
360
360
  /** ProcessExecution constructed, not yet started */
361
361
  Init = 'init',
362
+ /** Formatting next run message */
363
+ Formatting = 'formatting',
362
364
  /** Process run entered */
363
365
  Entered = 'entered',
364
366
  /** Process run started */