bpmn-elements 11.0.1 → 12.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,27 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ # 12.0.0
5
+
6
+ Memory issues running sequential multi-instance sub-process (MISP). All MISP executions are put in a list to be able to save state.
7
+
8
+ ## Breaking
9
+
10
+ - remove MISP execution from execution reference list when iteration is completed, discarded, or errored
11
+
12
+ # 11.1.1
13
+
14
+ - fix boundary event not cancelling task if resumed before task was resumed
15
+ - a cancelled call activity should also cancel the called process even if resumed before called process was resumed later
16
+
17
+ # 11.1.0
18
+
19
+ - bump [smqp@8](https://github.com/paed01/smqp/blob/default/CHANGELOG.md)
20
+
21
+ # 11.0.1
22
+
23
+ - update neglected type definition
24
+
4
25
  # 11.0.0
5
26
 
6
27
  - slim activity state by removing properties not needed for recover, might be breaking if state is inspected
@@ -263,7 +284,7 @@ Refactor scripts again
263
284
 
264
285
  # 2.1.0
265
286
 
266
- Transactions and compensation if canceled.
287
+ Transactions and compensation if cancelled.
267
288
 
268
289
  ## Additions
269
290
  - Add support for Transaction
package/README.md CHANGED
@@ -62,7 +62,7 @@ The following elements are tested and supported.
62
62
  - SignalEventDefinition
63
63
  - throw
64
64
  - catch
65
- - SignalTask
65
+ - [SignalTask](/docs/SignalTask.md)
66
66
  - ManualTask
67
67
  - UserTask
68
68
  - [StandardLoopCharacteristics](/docs/LoopCharacteristics.md)
@@ -25,8 +25,7 @@ const kFormatter = Symbol.for('formatter');
25
25
  const kMessageHandlers = Symbol.for('messageHandlers');
26
26
  const kStateMessage = Symbol.for('stateMessage');
27
27
  const kActivated = Symbol.for('activated');
28
- var _default = Activity;
29
- exports.default = _default;
28
+ var _default = exports.default = Activity;
30
29
  function Activity(Behaviour, activityDef, context) {
31
30
  const {
32
31
  id,
@@ -11,8 +11,7 @@ const kExecuteQ = Symbol.for('executeQ');
11
11
  const kExecuteMessage = Symbol.for('executeMessage');
12
12
  const kMessageHandlers = Symbol.for('messageHandlers');
13
13
  const kPostponed = Symbol.for('postponed');
14
- var _default = ActivityExecution;
15
- exports.default = _default;
14
+ var _default = exports.default = ActivityExecution;
16
15
  function ActivityExecution(activity, context) {
17
16
  this.activity = activity;
18
17
  this.context = context;
@@ -20,8 +20,7 @@ const kMessageHandlers = Symbol.for('messageHandlers');
20
20
  const kStateMessage = Symbol.for('stateMessage');
21
21
  const kStatus = Symbol.for('status');
22
22
  const kStopped = Symbol.for('stopped');
23
- var _default = Definition;
24
- exports.default = _default;
23
+ var _default = exports.default = Definition;
25
24
  function Definition(context, options) {
26
25
  if (!(this instanceof Definition)) return new Definition(context, options);
27
26
  if (!context) throw new Error('No context');
@@ -602,7 +602,16 @@ DefinitionExecution.prototype._onCancelCallActivity = function onCancelCallActiv
602
602
  const targetProcess = this.getProcessByExecutionId(bpExecutionId);
603
603
  if (!targetProcess) return;
604
604
  this._debug(`cancel call from <${fromParent.id}.${fromId}> to <${calledElement}>`);
605
- targetProcess.getApi().discard();
605
+ if (!targetProcess.isRunning) {
606
+ targetProcess.getApi({
607
+ content: {
608
+ id: targetProcess.id,
609
+ executionId: targetProcess.executionId
610
+ }
611
+ }).discard();
612
+ } else {
613
+ targetProcess.getApi().discard();
614
+ }
606
615
  };
607
616
  DefinitionExecution.prototype._onDelegateMessage = function onDelegateMessage(routingKey, executeMessage) {
608
617
  const content = executeMessage.content;
@@ -107,13 +107,28 @@ BoundaryEventBehaviour.prototype._onCompleted = function onCompleted(_, {
107
107
  }));
108
108
  }
109
109
  this[kCompleteContent] = content;
110
- const inbound = this[kExecuteMessage].content.inbound;
110
+ const {
111
+ inbound,
112
+ executionId
113
+ } = this[kExecuteMessage].content;
111
114
  const attachedToContent = inbound && inbound[0];
112
115
  const attachedTo = this.attachedTo;
113
- this.activity.logger.debug(`<${this.executionId} (${this.id})> cancel ${attachedTo.status} activity <${attachedToContent.executionId} (${attachedToContent.id})>`);
114
- attachedTo.getApi({
115
- content: attachedToContent
116
- }).discard();
116
+ this.activity.logger.debug(`<${executionId} (${this.id})> cancel ${attachedTo.status} activity <${attachedToContent.executionId} (${attachedToContent.id})>`);
117
+ if (content.isRecovered && !attachedTo.isRunning) {
118
+ const attachedExecuteTag = `_on-attached-execute-${executionId}`;
119
+ this[kAttachedTags].push(attachedExecuteTag);
120
+ attachedTo.broker.subscribeOnce('execution', '#', () => {
121
+ attachedTo.getApi({
122
+ content: attachedToContent
123
+ }).discard();
124
+ }, {
125
+ consumerTag: attachedExecuteTag
126
+ });
127
+ } else {
128
+ attachedTo.getApi({
129
+ content: attachedToContent
130
+ }).discard();
131
+ }
117
132
  };
118
133
  BoundaryEventBehaviour.prototype._onAttachedLeave = function onAttachedLeave(_, {
119
134
  content
@@ -11,8 +11,7 @@ var _EventBroker = require("../EventBroker.js");
11
11
  var _Api = require("../Api.js");
12
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
13
  const kCounters = Symbol.for('counters');
14
- var _default = SequenceFlow;
15
- exports.default = _default;
14
+ var _default = exports.default = SequenceFlow;
16
15
  function SequenceFlow(flowDef, {
17
16
  environment
18
17
  }) {
@@ -8,8 +8,7 @@ const propertyPattern = /(\w+)\((.*?)(?:\))|(\.|\[|^)(.+?)(?:\]|\[|\.|$)/;
8
8
  const stringConstantPattern = /^(['"])(.*)\1$/;
9
9
  const numberConstantPattern = /^\W*-?\d+(.\d+)?\W*$/;
10
10
  const negativeIndexPattern = /^-\d+$/;
11
- var _default = getPropertyValue;
12
- exports.default = _default;
11
+ var _default = exports.default = getPropertyValue;
13
12
  function getPropertyValue(inputContext, propertyPath, fnScope) {
14
13
  if (!inputContext) return;
15
14
  let resultValue;
package/dist/index.js CHANGED
@@ -398,6 +398,6 @@ var _Transaction = _interopRequireDefault(require("./tasks/Transaction.js"));
398
398
  var _Timers = require("./Timers.js");
399
399
  var ISODuration = _interopRequireWildcard(require("./iso-duration.js"));
400
400
  exports.ISODuration = ISODuration;
401
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
402
- function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
401
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
402
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
403
403
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -22,8 +22,7 @@ const kMessageHandlers = Symbol.for('messageHandlers');
22
22
  const kStateMessage = Symbol.for('stateMessage');
23
23
  const kStatus = Symbol.for('status');
24
24
  const kStopped = Symbol.for('stopped');
25
- var _default = Process;
26
- exports.default = _default;
25
+ var _default = exports.default = Process;
27
26
  function Process(processDef, context) {
28
27
  const {
29
28
  id,
@@ -8,8 +8,7 @@ var _Api = require("../Api.js");
8
8
  var _messageHelper = require("../messageHelper.js");
9
9
  var _shared = require("../shared.js");
10
10
  var _Tracker = require("../Tracker.js");
11
- var _default = ProcessExecution;
12
- exports.default = _default;
11
+ var _default = exports.default = ProcessExecution;
13
12
  const kActivated = Symbol.for('activated');
14
13
  const kActivityQ = Symbol.for('activityQ');
15
14
  const kCompleted = Symbol.for('completed');
@@ -116,14 +116,21 @@ function ParallelLoopCharacteristics(activity, characteristics) {
116
116
  this.characteristics = characteristics;
117
117
  this.running = 0;
118
118
  this.index = 0;
119
+ this.discarded = 0;
119
120
  }
120
121
  ParallelLoopCharacteristics.prototype.execute = function execute(executeMessage) {
121
122
  const chr = this.characteristics;
122
123
  if (!chr.cardinality) throw new _Errors.RunError(`<${this.id}> cardinality or collection is required in parallel loops`, executeMessage);
123
124
  const isRedelivered = executeMessage.fields.redelivered;
124
125
  if (isRedelivered) {
125
- if (!isNaN(executeMessage.content.index)) this.index = executeMessage.content.index;
126
- if (!isNaN(executeMessage.content.running)) this.running = executeMessage.content.running;
126
+ const {
127
+ index,
128
+ running,
129
+ discarded
130
+ } = executeMessage.content;
131
+ if (!isNaN(index)) this.index = index;
132
+ if (!isNaN(running)) this.running = running;
133
+ if (!isNaN(discarded)) this.discarded = discarded;
127
134
  }
128
135
  chr.subscribe(this._onCompleteMessage.bind(this));
129
136
  if (isRedelivered) return;
@@ -148,6 +155,7 @@ ParallelLoopCharacteristics.prototype._startBatch = function startBatch() {
148
155
  ...chr.getContent(),
149
156
  index: this.index,
150
157
  running: this.running,
158
+ discarded: this.discarded,
151
159
  output: chr.output,
152
160
  preventComplete: true
153
161
  });
@@ -155,27 +163,31 @@ ParallelLoopCharacteristics.prototype._startBatch = function startBatch() {
155
163
  broker.publish('execution', 'execute.start', content);
156
164
  }
157
165
  };
158
- ParallelLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMessage(_, message) {
166
+ ParallelLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMessage(routingKey, message) {
159
167
  const chr = this.characteristics;
160
168
  const {
161
169
  content
162
170
  } = message;
163
171
  if (content.output !== undefined) chr.output[content.index] = content.output;
172
+ if (routingKey === 'execute.discard') {
173
+ this.discarded++;
174
+ }
164
175
  this.running--;
165
176
  this.activity.broker.publish('execution', 'execute.iteration.completed', {
166
177
  ...content,
167
178
  ...chr.getContent(),
168
179
  index: this.index,
169
180
  running: this.running,
181
+ discarded: this.discarded,
170
182
  output: chr.output,
171
183
  state: 'iteration.completed',
172
184
  preventComplete: true
173
185
  });
174
186
  if (this.running <= 0 && !chr.next(this.index)) {
175
- return chr.complete(content);
187
+ return chr.complete(content, this.discarded === this.index);
176
188
  }
177
189
  if (chr.isCompletionConditionMet(message)) {
178
- return chr.complete(content);
190
+ return chr.complete(content, this.discarded === this.index);
179
191
  }
180
192
  if (this.running <= 0) {
181
193
  this.running = 0;
@@ -263,9 +275,9 @@ Characteristics.prototype.isCompletionConditionMet = function isCompletionCondit
263
275
  loopOutput: this.output
264
276
  }));
265
277
  };
266
- Characteristics.prototype.complete = function complete(content) {
278
+ Characteristics.prototype.complete = function complete(content, allDiscarded) {
267
279
  this.stop();
268
- return this.broker.publish('execution', 'execute.completed', {
280
+ return this.broker.publish('execution', 'execute.' + (allDiscarded ? 'discard' : 'completed'), {
269
281
  ...content,
270
282
  ...this.getContent(),
271
283
  output: this.output
@@ -286,6 +298,7 @@ Characteristics.prototype.subscribe = function subscribe(onIterationCompleteMess
286
298
  function onComplete(routingKey, message, ...args) {
287
299
  if (!message.content.isMultiInstance) return;
288
300
  switch (routingKey) {
301
+ case 'execute.discard':
289
302
  case 'execute.cancel':
290
303
  case 'execute.completed':
291
304
  return onIterationCompleteMessage(routingKey, message, ...args);
@@ -56,52 +56,36 @@ function SubProcessBehaviour(activity, context) {
56
56
  this.executionId = undefined;
57
57
  this[kExecutions] = [];
58
58
  this[kMessageHandlers] = {
59
- onApiRootMessage: this._onApiRootMessage.bind(this),
60
59
  onExecutionCompleted: this._onExecutionCompleted.bind(this)
61
60
  };
62
61
  }
63
- Object.defineProperty(SubProcessBehaviour.prototype, 'execution', {
64
- get() {
65
- return this[kExecutions][0];
66
- }
67
- });
68
- Object.defineProperty(SubProcessBehaviour.prototype, 'executions', {
69
- get() {
70
- return this[kExecutions].slice();
62
+ Object.defineProperties(SubProcessBehaviour.prototype, {
63
+ 'execution': {
64
+ get() {
65
+ return this[kExecutions][0];
66
+ }
67
+ },
68
+ 'executions': {
69
+ get() {
70
+ return this[kExecutions].slice();
71
+ }
71
72
  }
72
73
  });
73
74
  SubProcessBehaviour.prototype.execute = function execute(executeMessage) {
74
- const content = executeMessage.content;
75
- let executionId = this.executionId;
76
- if (content.isRootScope) {
77
- executionId = this.executionId = content.executionId;
75
+ const {
76
+ isRootScope,
77
+ executionId
78
+ } = executeMessage.content;
79
+ if (isRootScope) {
80
+ this.executionId = executionId;
78
81
  }
79
82
  const loopCharacteristics = this.loopCharacteristics;
80
- if (loopCharacteristics && content.isRootScope) {
81
- this.broker.subscribeTmp('api', `activity.#.${executionId}`, this[kMessageHandlers].onApiRootMessage, {
82
- noAck: true,
83
- consumerTag: `_api-${executionId}`,
84
- priority: 200
85
- });
83
+ if (loopCharacteristics && isRootScope) {
86
84
  return loopCharacteristics.execute(executeMessage);
87
85
  }
88
86
  const processExecution = this._upsertExecution(executeMessage);
89
87
  return processExecution.execute(executeMessage);
90
88
  };
91
- SubProcessBehaviour.prototype.stop = function stop() {
92
- for (const execution of this[kExecutions]) {
93
- this.broker.cancel(`_sub-process-execution-${execution.executionId}`);
94
- this.broker.cancel(`_sub-process-api-${execution.executionId}`);
95
- execution.stop();
96
- }
97
- };
98
- SubProcessBehaviour.prototype.discard = function discard() {
99
- for (const execution of this[kExecutions]) {
100
- this.broker.cancel(`_sub-process-execution-${execution.executionId}`);
101
- this.broker.cancel(`_sub-process-api-${execution.executionId}`);
102
- execution.discard();
103
- }
104
- };
105
89
  SubProcessBehaviour.prototype.getState = function getState() {
106
90
  if (this.loopCharacteristics) {
107
91
  return {
@@ -145,35 +129,22 @@ SubProcessBehaviour.prototype.getPostponed = function getPostponed() {
145
129
  return result;
146
130
  }, []);
147
131
  };
148
- SubProcessBehaviour.prototype._onApiRootMessage = function onApiRootMessage(_, message) {
149
- const messageType = message.properties.type;
150
- switch (messageType) {
151
- case 'stop':
152
- this.broker.cancel(message.fields.consumerTag);
153
- this.stop();
154
- break;
155
- case 'discard':
156
- this.broker.cancel(message.fields.consumerTag);
157
- this.discard();
158
- break;
159
- }
160
- };
161
132
  SubProcessBehaviour.prototype._upsertExecution = function upsertExecution(executeMessage) {
162
133
  const content = executeMessage.content;
163
134
  const executionId = content.executionId;
164
135
  let execution = this._getExecutionById(executionId);
165
136
  if (execution) {
166
- if (executeMessage.fields.redelivered) this._addListeners(execution, executionId);
137
+ if (executeMessage.fields.redelivered) this._addListeners(executionId);
167
138
  return execution;
168
139
  }
169
140
  const subEnvironment = this.environment.clone();
170
141
  const subContext = this.context.clone(subEnvironment, this.activity);
171
142
  execution = new _ProcessExecution.default(this.activity, subContext);
172
143
  this[kExecutions].push(execution);
173
- this._addListeners(execution, executionId);
144
+ this._addListeners(executionId);
174
145
  return execution;
175
146
  };
176
- SubProcessBehaviour.prototype._addListeners = function addListeners(processExecution, executionId) {
147
+ SubProcessBehaviour.prototype._addListeners = function addListeners(executionId) {
177
148
  this.broker.subscribeTmp('subprocess-execution', `execution.#.${executionId}`, this[kMessageHandlers].onExecutionCompleted, {
178
149
  noAck: true,
179
150
  consumerTag: `_sub-process-execution-${executionId}`
@@ -187,21 +158,14 @@ SubProcessBehaviour.prototype._onExecutionCompleted = function onExecutionComple
187
158
  switch (messageType) {
188
159
  case 'stopped':
189
160
  {
190
- broker.cancel(message.fields.consumerTag);
191
- break;
161
+ return broker.cancel(message.fields.consumerTag);
192
162
  }
163
+ case 'completed':
193
164
  case 'cancel':
194
165
  case 'discard':
195
166
  {
196
167
  broker.cancel(message.fields.consumerTag);
197
- broker.publish('execution', 'execute.' + messageType, (0, _messageHelper.cloneContent)(content));
198
- break;
199
- }
200
- case 'completed':
201
- {
202
- broker.cancel(message.fields.consumerTag);
203
- broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(content));
204
- break;
168
+ return this._completeExecution('execute.' + messageType, content);
205
169
  }
206
170
  case 'error':
207
171
  {
@@ -210,11 +174,19 @@ SubProcessBehaviour.prototype._onExecutionCompleted = function onExecutionComple
210
174
  error
211
175
  } = content;
212
176
  this.activity.logger.error(`<${this.id}>`, error);
213
- broker.publish('execution', 'execute.error', (0, _messageHelper.cloneContent)(content));
214
- break;
177
+ return this._completeExecution('execute.error', content);
215
178
  }
216
179
  }
217
180
  };
181
+ SubProcessBehaviour.prototype._completeExecution = function completeExecution(completeRoutingKey, content) {
182
+ if (this.loopCharacteristics) {
183
+ const executions = this[kExecutions];
184
+ const executionIdx = executions.findIndex(pe => pe.executionId === content.executionId);
185
+ if (executionIdx < 0) return;
186
+ executions.splice(executionIdx, 1);
187
+ }
188
+ this.broker.publish('execution', completeRoutingKey, (0, _messageHelper.cloneContent)(content));
189
+ };
218
190
  SubProcessBehaviour.prototype.getApi = function getApi(apiMessage) {
219
191
  const content = apiMessage.content;
220
192
  if (content.id === this.id) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bpmn-elements",
3
- "version": "11.0.1",
3
+ "version": "12.0.0",
4
4
  "description": "Executable workflow elements based on BPMN 2.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -45,26 +45,26 @@
45
45
  ],
46
46
  "devDependencies": {
47
47
  "@aircall/expression-parser": "^1.0.4",
48
- "@babel/cli": "^7.22.5",
49
- "@babel/core": "^7.22.5",
50
- "@babel/preset-env": "^7.22.5",
48
+ "@babel/cli": "^7.22.9",
49
+ "@babel/core": "^7.23.6",
50
+ "@babel/preset-env": "^7.23.6",
51
51
  "@babel/register": "^7.22.5",
52
52
  "@bonniernews/hot-bev": "^0.4.0",
53
53
  "bpmn-moddle": "^8.0.1",
54
54
  "c8": "^8.0.0",
55
55
  "camunda-bpmn-moddle": "^7.0.1",
56
56
  "chai": "^4.3.7",
57
- "chronokinesis": "^5.0.2",
57
+ "chronokinesis": "^6.0.0",
58
58
  "debug": "^4.3.4",
59
- "eslint": "^8.43.0",
60
- "eslint-plugin-import": "^2.27.5",
59
+ "eslint": "^8.56.0",
60
+ "eslint-plugin-import": "^2.29.1",
61
61
  "got": "^12.6.1",
62
62
  "mocha": "^10.1.0",
63
63
  "mocha-cakes-2": "^3.3.0",
64
64
  "moddle-context-serializer": "^4.0.0",
65
- "nock": "^13.3.1"
65
+ "nock": "^13.4.0"
66
66
  },
67
67
  "dependencies": {
68
- "smqp": "^7.1.4"
68
+ "smqp": "^8.0.0"
69
69
  }
70
70
  }
@@ -614,7 +614,16 @@ DefinitionExecution.prototype._onCancelCallActivity = function onCancelCallActiv
614
614
 
615
615
  this._debug(`cancel call from <${fromParent.id}.${fromId}> to <${calledElement}>`);
616
616
 
617
- targetProcess.getApi().discard();
617
+ if (!targetProcess.isRunning) {
618
+ targetProcess.getApi({
619
+ content: {
620
+ id: targetProcess.id,
621
+ executionId: targetProcess.executionId,
622
+ },
623
+ }).discard();
624
+ } else {
625
+ targetProcess.getApi().discard();
626
+ }
618
627
  };
619
628
 
620
629
  DefinitionExecution.prototype._onDelegateMessage = function onDelegateMessage(routingKey, executeMessage) {
@@ -103,13 +103,21 @@ BoundaryEventBehaviour.prototype._onCompleted = function onCompleted(_, {content
103
103
 
104
104
  this[kCompleteContent] = content;
105
105
 
106
- const inbound = this[kExecuteMessage].content.inbound;
106
+ const {inbound, executionId} = this[kExecuteMessage].content;
107
107
  const attachedToContent = inbound && inbound[0];
108
108
  const attachedTo = this.attachedTo;
109
109
 
110
- this.activity.logger.debug(`<${this.executionId} (${this.id})> cancel ${attachedTo.status} activity <${attachedToContent.executionId} (${attachedToContent.id})>`);
110
+ this.activity.logger.debug(`<${executionId} (${this.id})> cancel ${attachedTo.status} activity <${attachedToContent.executionId} (${attachedToContent.id})>`);
111
111
 
112
- attachedTo.getApi({content: attachedToContent}).discard();
112
+ if (content.isRecovered && !attachedTo.isRunning) {
113
+ const attachedExecuteTag = `_on-attached-execute-${executionId}`;
114
+ this[kAttachedTags].push(attachedExecuteTag);
115
+ attachedTo.broker.subscribeOnce('execution', '#', () => {
116
+ attachedTo.getApi({content: attachedToContent}).discard();
117
+ }, {consumerTag: attachedExecuteTag});
118
+ } else {
119
+ attachedTo.getApi({content: attachedToContent}).discard();
120
+ }
113
121
  };
114
122
 
115
123
  BoundaryEventBehaviour.prototype._onAttachedLeave = function onAttachedLeave(_, {content}) {
@@ -120,6 +120,7 @@ function ParallelLoopCharacteristics(activity, characteristics) {
120
120
  this.characteristics = characteristics;
121
121
  this.running = 0;
122
122
  this.index = 0;
123
+ this.discarded = 0;
123
124
  }
124
125
 
125
126
  ParallelLoopCharacteristics.prototype.execute = function execute(executeMessage) {
@@ -128,8 +129,10 @@ ParallelLoopCharacteristics.prototype.execute = function execute(executeMessage)
128
129
 
129
130
  const isRedelivered = executeMessage.fields.redelivered;
130
131
  if (isRedelivered) {
131
- if (!isNaN(executeMessage.content.index)) this.index = executeMessage.content.index;
132
- if (!isNaN(executeMessage.content.running)) this.running = executeMessage.content.running;
132
+ const { index, running, discarded } = executeMessage.content;
133
+ if (!isNaN(index)) this.index = index;
134
+ if (!isNaN(running)) this.running = running;
135
+ if (!isNaN(discarded)) this.discarded = discarded;
133
136
  }
134
137
  chr.subscribe(this._onCompleteMessage.bind(this));
135
138
 
@@ -160,6 +163,7 @@ ParallelLoopCharacteristics.prototype._startBatch = function startBatch() {
160
163
  ...chr.getContent(),
161
164
  index: this.index,
162
165
  running: this.running,
166
+ discarded: this.discarded,
163
167
  output: chr.output,
164
168
  preventComplete: true,
165
169
  });
@@ -169,11 +173,15 @@ ParallelLoopCharacteristics.prototype._startBatch = function startBatch() {
169
173
  }
170
174
  };
171
175
 
172
- ParallelLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMessage(_, message) {
176
+ ParallelLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMessage(routingKey, message) {
173
177
  const chr = this.characteristics;
174
178
  const {content} = message;
175
179
  if (content.output !== undefined) chr.output[content.index] = content.output;
176
180
 
181
+ if (routingKey === 'execute.discard') {
182
+ this.discarded++;
183
+ }
184
+
177
185
  this.running--;
178
186
 
179
187
  this.activity.broker.publish('execution', 'execute.iteration.completed', {
@@ -181,17 +189,18 @@ ParallelLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMe
181
189
  ...chr.getContent(),
182
190
  index: this.index,
183
191
  running: this.running,
192
+ discarded: this.discarded,
184
193
  output: chr.output,
185
194
  state: 'iteration.completed',
186
195
  preventComplete: true,
187
196
  });
188
197
 
189
198
  if (this.running <= 0 && !chr.next(this.index)) {
190
- return chr.complete(content);
199
+ return chr.complete(content, this.discarded === this.index);
191
200
  }
192
201
 
193
202
  if (chr.isCompletionConditionMet(message)) {
194
- return chr.complete(content);
203
+ return chr.complete(content, this.discarded === this.index);
195
204
  }
196
205
 
197
206
  if (this.running <= 0) {
@@ -299,10 +308,10 @@ Characteristics.prototype.isCompletionConditionMet = function isCompletionCondit
299
308
  return this.activity.environment.resolveExpression(this.completionCondition, cloneMessage(message, {loopOutput: this.output}));
300
309
  };
301
310
 
302
- Characteristics.prototype.complete = function complete(content) {
311
+ Characteristics.prototype.complete = function complete(content, allDiscarded) {
303
312
  this.stop();
304
313
 
305
- return this.broker.publish('execution', 'execute.completed', {
314
+ return this.broker.publish('execution', 'execute.' + (allDiscarded ? 'discard' : 'completed'), {
306
315
  ...content,
307
316
  ...this.getContent(),
308
317
  output: this.output,
@@ -315,7 +324,9 @@ Characteristics.prototype.subscribe = function subscribe(onIterationCompleteMess
315
324
 
316
325
  function onComplete(routingKey, message, ...args) {
317
326
  if (!message.content.isMultiInstance) return;
327
+
318
328
  switch (routingKey) {
329
+ case 'execute.discard':
319
330
  case 'execute.cancel':
320
331
  case 'execute.completed':
321
332
  return onIterationCompleteMessage(routingKey, message, ...args);
@@ -39,39 +39,32 @@ export function SubProcessBehaviour(activity, context) {
39
39
 
40
40
  this[kExecutions] = [];
41
41
  this[kMessageHandlers] = {
42
- onApiRootMessage: this._onApiRootMessage.bind(this),
43
42
  onExecutionCompleted: this._onExecutionCompleted.bind(this),
44
43
  };
45
44
  }
46
45
 
47
- Object.defineProperty(SubProcessBehaviour.prototype, 'execution', {
48
- get() {
49
- return this[kExecutions][0];
46
+ Object.defineProperties(SubProcessBehaviour.prototype, {
47
+ 'execution': {
48
+ get() {
49
+ return this[kExecutions][0];
50
+ },
50
51
  },
51
- });
52
-
53
- Object.defineProperty(SubProcessBehaviour.prototype, 'executions', {
54
- get() {
55
- return this[kExecutions].slice();
52
+ 'executions': {
53
+ get() {
54
+ return this[kExecutions].slice();
55
+ },
56
56
  },
57
57
  });
58
58
 
59
59
  SubProcessBehaviour.prototype.execute = function execute(executeMessage) {
60
- const content = executeMessage.content;
60
+ const { isRootScope, executionId } = executeMessage.content;
61
61
 
62
- let executionId = this.executionId;
63
- if (content.isRootScope) {
64
- executionId = this.executionId = content.executionId;
62
+ if (isRootScope) {
63
+ this.executionId = executionId;
65
64
  }
66
65
 
67
66
  const loopCharacteristics = this.loopCharacteristics;
68
- if (loopCharacteristics && content.isRootScope) {
69
- this.broker.subscribeTmp('api', `activity.#.${executionId}`, this[kMessageHandlers].onApiRootMessage, {
70
- noAck: true,
71
- consumerTag: `_api-${executionId}`,
72
- priority: 200,
73
- });
74
-
67
+ if (loopCharacteristics && isRootScope) {
75
68
  return loopCharacteristics.execute(executeMessage);
76
69
  }
77
70
 
@@ -79,22 +72,6 @@ SubProcessBehaviour.prototype.execute = function execute(executeMessage) {
79
72
  return processExecution.execute(executeMessage);
80
73
  };
81
74
 
82
- SubProcessBehaviour.prototype.stop = function stop() {
83
- for (const execution of this[kExecutions]) {
84
- this.broker.cancel(`_sub-process-execution-${execution.executionId}`);
85
- this.broker.cancel(`_sub-process-api-${execution.executionId}`);
86
- execution.stop();
87
- }
88
- };
89
-
90
- SubProcessBehaviour.prototype.discard = function discard() {
91
- for (const execution of this[kExecutions]) {
92
- this.broker.cancel(`_sub-process-execution-${execution.executionId}`);
93
- this.broker.cancel(`_sub-process-api-${execution.executionId}`);
94
- execution.discard();
95
- }
96
- };
97
-
98
75
  SubProcessBehaviour.prototype.getState = function getState() {
99
76
  if (this.loopCharacteristics) {
100
77
  return {
@@ -148,28 +125,13 @@ SubProcessBehaviour.prototype.getPostponed = function getPostponed() {
148
125
  }, []);
149
126
  };
150
127
 
151
- SubProcessBehaviour.prototype._onApiRootMessage = function onApiRootMessage(_, message) {
152
- const messageType = message.properties.type;
153
-
154
- switch (messageType) {
155
- case 'stop':
156
- this.broker.cancel(message.fields.consumerTag);
157
- this.stop();
158
- break;
159
- case 'discard':
160
- this.broker.cancel(message.fields.consumerTag);
161
- this.discard();
162
- break;
163
- }
164
- };
165
-
166
128
  SubProcessBehaviour.prototype._upsertExecution = function upsertExecution(executeMessage) {
167
129
  const content = executeMessage.content;
168
130
  const executionId = content.executionId;
169
131
 
170
132
  let execution = this._getExecutionById(executionId);
171
133
  if (execution) {
172
- if (executeMessage.fields.redelivered) this._addListeners(execution, executionId);
134
+ if (executeMessage.fields.redelivered) this._addListeners(executionId);
173
135
  return execution;
174
136
  }
175
137
 
@@ -179,12 +141,12 @@ SubProcessBehaviour.prototype._upsertExecution = function upsertExecution(execut
179
141
  execution = new ProcessExecution(this.activity, subContext);
180
142
  this[kExecutions].push(execution);
181
143
 
182
- this._addListeners(execution, executionId);
144
+ this._addListeners(executionId);
183
145
 
184
146
  return execution;
185
147
  };
186
148
 
187
- SubProcessBehaviour.prototype._addListeners = function addListeners(processExecution, executionId) {
149
+ SubProcessBehaviour.prototype._addListeners = function addListeners(executionId) {
188
150
  this.broker.subscribeTmp('subprocess-execution', `execution.#.${executionId}`, this[kMessageHandlers].onExecutionCompleted, {
189
151
  noAck: true,
190
152
  consumerTag: `_sub-process-execution-${executionId}`,
@@ -200,38 +162,42 @@ SubProcessBehaviour.prototype._onExecutionCompleted = function onExecutionComple
200
162
 
201
163
  switch (messageType) {
202
164
  case 'stopped': {
203
- broker.cancel(message.fields.consumerTag);
204
- break;
165
+ return broker.cancel(message.fields.consumerTag);
205
166
  }
167
+ case 'completed':
206
168
  case 'cancel':
207
169
  case 'discard': {
208
170
  broker.cancel(message.fields.consumerTag);
209
- broker.publish('execution', 'execute.' + messageType, cloneContent(content));
210
- break;
211
- }
212
- case 'completed': {
213
- broker.cancel(message.fields.consumerTag);
214
- broker.publish('execution', 'execute.completed', cloneContent(content));
215
- break;
171
+ return this._completeExecution('execute.' + messageType, content);
216
172
  }
217
173
  case 'error': {
218
174
  broker.cancel(message.fields.consumerTag);
219
175
 
220
176
  const {error} = content;
221
177
  this.activity.logger.error(`<${this.id}>`, error);
222
- broker.publish('execution', 'execute.error', cloneContent(content));
223
- break;
178
+
179
+ return this._completeExecution('execute.error', content);
224
180
  }
225
181
  }
226
182
  };
227
183
 
184
+ SubProcessBehaviour.prototype._completeExecution = function completeExecution(completeRoutingKey, content) {
185
+ if (this.loopCharacteristics) {
186
+ const executions = this[kExecutions];
187
+ const executionIdx = executions.findIndex((pe) => pe.executionId === content.executionId);
188
+ if (executionIdx < 0) return;
189
+ executions.splice(executionIdx, 1);
190
+ }
191
+
192
+ this.broker.publish('execution', completeRoutingKey, cloneContent(content));
193
+ };
194
+
228
195
  SubProcessBehaviour.prototype.getApi = function getApi(apiMessage) {
229
196
  const content = apiMessage.content;
230
197
 
231
198
  if (content.id === this.id) return;
232
199
 
233
200
  let execution;
234
-
235
201
  if ((execution = this._getExecutionById(content.parent.executionId))) {
236
202
  return execution.getApi(apiMessage);
237
203
  }