bpmn-elements 6.0.1 → 7.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.
Files changed (116) hide show
  1. package/CHANGELOG.md +322 -0
  2. package/README.md +3 -0
  3. package/dist/index.js +52 -44
  4. package/dist/src/Api.js +77 -76
  5. package/dist/src/Context.js +169 -175
  6. package/dist/src/Environment.js +90 -102
  7. package/dist/src/EventBroker.js +89 -88
  8. package/dist/src/ExtensionsMapper.js +2 -2
  9. package/dist/src/MessageFormatter.js +164 -95
  10. package/dist/src/Scripts.js +6 -2
  11. package/dist/src/activity/Activity.js +1106 -916
  12. package/dist/src/activity/ActivityExecution.js +342 -297
  13. package/dist/src/activity/Dummy.js +3 -3
  14. package/dist/src/definition/Definition.js +498 -444
  15. package/dist/src/definition/DefinitionExecution.js +722 -409
  16. package/dist/src/error/Errors.js +17 -7
  17. package/dist/src/eventDefinitions/CancelEventDefinition.js +190 -150
  18. package/dist/src/eventDefinitions/CompensateEventDefinition.js +194 -161
  19. package/dist/src/eventDefinitions/ConditionalEventDefinition.js +197 -135
  20. package/dist/src/eventDefinitions/ErrorEventDefinition.js +207 -165
  21. package/dist/src/eventDefinitions/EscalationEventDefinition.js +175 -141
  22. package/dist/src/eventDefinitions/EventDefinitionExecution.js +157 -129
  23. package/dist/src/eventDefinitions/LinkEventDefinition.js +174 -149
  24. package/dist/src/eventDefinitions/MessageEventDefinition.js +213 -176
  25. package/dist/src/eventDefinitions/SignalEventDefinition.js +203 -161
  26. package/dist/src/eventDefinitions/TerminateEventDefinition.js +21 -23
  27. package/dist/src/eventDefinitions/TimerEventDefinition.js +243 -228
  28. package/dist/src/events/BoundaryEvent.js +180 -144
  29. package/dist/src/events/EndEvent.js +18 -23
  30. package/dist/src/events/IntermediateCatchEvent.js +44 -58
  31. package/dist/src/events/IntermediateThrowEvent.js +18 -23
  32. package/dist/src/events/StartEvent.js +109 -94
  33. package/dist/src/flows/Association.js +94 -100
  34. package/dist/src/flows/MessageFlow.js +86 -103
  35. package/dist/src/flows/SequenceFlow.js +173 -182
  36. package/dist/src/gateways/EventBasedGateway.js +88 -84
  37. package/dist/src/gateways/ExclusiveGateway.js +13 -16
  38. package/dist/src/gateways/InclusiveGateway.js +11 -14
  39. package/dist/src/gateways/ParallelGateway.js +11 -14
  40. package/dist/src/getPropertyValue.js +34 -34
  41. package/dist/src/io/BpmnIO.js +17 -14
  42. package/dist/src/io/EnvironmentDataObject.js +33 -29
  43. package/dist/src/io/EnvironmentDataStore.js +33 -29
  44. package/dist/src/io/EnvironmentDataStoreReference.js +35 -31
  45. package/dist/src/io/InputOutputSpecification.js +177 -168
  46. package/dist/src/io/Properties.js +117 -124
  47. package/dist/src/messageHelper.js +1 -1
  48. package/dist/src/process/Process.js +433 -359
  49. package/dist/src/process/ProcessExecution.js +744 -645
  50. package/dist/src/shared.js +2 -2
  51. package/dist/src/tasks/CallActivity.js +160 -0
  52. package/dist/src/tasks/LoopCharacteristics.js +309 -330
  53. package/dist/src/tasks/ReceiveTask.js +233 -182
  54. package/dist/src/tasks/ScriptTask.js +35 -41
  55. package/dist/src/tasks/ServiceImplementation.js +13 -20
  56. package/dist/src/tasks/ServiceTask.js +82 -75
  57. package/dist/src/tasks/SignalTask.js +97 -93
  58. package/dist/src/tasks/StandardLoopCharacteristics.js +1 -1
  59. package/dist/src/tasks/SubProcess.js +195 -175
  60. package/dist/src/tasks/Task.js +17 -19
  61. package/index.js +2 -0
  62. package/package.json +13 -13
  63. package/src/Api.js +65 -59
  64. package/src/Context.js +138 -141
  65. package/src/Environment.js +88 -100
  66. package/src/EventBroker.js +67 -68
  67. package/src/ExtensionsMapper.js +2 -2
  68. package/src/MessageFormatter.js +132 -74
  69. package/src/activity/Activity.js +915 -775
  70. package/src/activity/ActivityExecution.js +293 -247
  71. package/src/activity/Dummy.js +2 -2
  72. package/src/definition/Definition.js +436 -401
  73. package/src/definition/DefinitionExecution.js +603 -343
  74. package/src/error/Errors.js +11 -6
  75. package/src/eventDefinitions/CancelEventDefinition.js +164 -121
  76. package/src/eventDefinitions/CompensateEventDefinition.js +158 -124
  77. package/src/eventDefinitions/ConditionalEventDefinition.js +147 -104
  78. package/src/eventDefinitions/ErrorEventDefinition.js +190 -131
  79. package/src/eventDefinitions/EscalationEventDefinition.js +139 -101
  80. package/src/eventDefinitions/EventDefinitionExecution.js +127 -95
  81. package/src/eventDefinitions/LinkEventDefinition.js +160 -129
  82. package/src/eventDefinitions/MessageEventDefinition.js +178 -121
  83. package/src/eventDefinitions/SignalEventDefinition.js +162 -106
  84. package/src/eventDefinitions/TerminateEventDefinition.js +19 -19
  85. package/src/eventDefinitions/TimerEventDefinition.js +202 -167
  86. package/src/events/BoundaryEvent.js +156 -115
  87. package/src/events/EndEvent.js +15 -18
  88. package/src/events/IntermediateCatchEvent.js +40 -44
  89. package/src/events/IntermediateThrowEvent.js +15 -18
  90. package/src/events/StartEvent.js +84 -50
  91. package/src/flows/Association.js +98 -112
  92. package/src/flows/MessageFlow.js +81 -97
  93. package/src/flows/SequenceFlow.js +146 -160
  94. package/src/gateways/EventBasedGateway.js +75 -68
  95. package/src/gateways/ExclusiveGateway.js +8 -13
  96. package/src/gateways/InclusiveGateway.js +8 -13
  97. package/src/gateways/ParallelGateway.js +8 -13
  98. package/src/getPropertyValue.js +34 -33
  99. package/src/io/BpmnIO.js +16 -15
  100. package/src/io/EnvironmentDataObject.js +29 -18
  101. package/src/io/EnvironmentDataStore.js +29 -18
  102. package/src/io/EnvironmentDataStoreReference.js +31 -20
  103. package/src/io/InputOutputSpecification.js +154 -157
  104. package/src/io/Properties.js +95 -97
  105. package/src/process/Process.js +374 -333
  106. package/src/process/ProcessExecution.js +606 -554
  107. package/src/tasks/CallActivity.js +130 -0
  108. package/src/tasks/LoopCharacteristics.js +290 -289
  109. package/src/tasks/ReceiveTask.js +174 -107
  110. package/src/tasks/ScriptTask.js +27 -30
  111. package/src/tasks/ServiceImplementation.js +13 -18
  112. package/src/tasks/ServiceTask.js +67 -60
  113. package/src/tasks/SignalTask.js +77 -52
  114. package/src/tasks/StandardLoopCharacteristics.js +1 -1
  115. package/src/tasks/SubProcess.js +184 -157
  116. package/src/tasks/Task.js +15 -19
@@ -1,15 +1,19 @@
1
- import {ActivityError} from '../error/Errors';
2
- import {cloneContent, cloneMessage, unshiftParent} from '../messageHelper';
1
+ import {RunError} from '../error/Errors';
2
+ import {cloneContent, cloneMessage, unshiftParent, cloneParent} from '../messageHelper';
3
3
 
4
4
  export default function LoopCharacteristics(activity, loopCharacteristics) {
5
- const {id, broker, environment} = activity;
6
- const {batchSize = 50} = environment.settings;
5
+ this.activity = activity;
6
+ this.loopCharacteristics = loopCharacteristics;
7
7
  const {type = 'LoopCharacteristics', behaviour = {}} = loopCharacteristics;
8
- const {isSequential = false, collection: collectionExpression, elementVariable = 'item'} = behaviour;
8
+ this.type = type;
9
+ const {isSequential = false, collection} = behaviour;
10
+ this.isSequential = isSequential;
11
+ this.collection = collection;
9
12
 
10
13
  let completionCondition, startCondition, loopCardinality;
11
14
  if ('loopCardinality' in behaviour) loopCardinality = behaviour.loopCardinality;
12
15
  else if ('loopMaximum' in behaviour) loopCardinality = behaviour.loopMaximum;
16
+ this.loopCardinality = loopCardinality;
13
17
 
14
18
  if (behaviour.loopCondition) {
15
19
  if (behaviour.testBefore) startCondition = behaviour.loopCondition;
@@ -19,323 +23,320 @@ export default function LoopCharacteristics(activity, loopCharacteristics) {
19
23
  completionCondition = behaviour.completionCondition;
20
24
  }
21
25
 
22
- const loopType = getLoopType();
23
- if (!loopType) return;
26
+ if (collection) {
27
+ this.loopType = 'collection';
28
+ this.elementVariable = behaviour.elementVariable || 'item';
29
+ } else if (completionCondition) this.loopType = 'complete condition';
30
+ else if (startCondition) this.loopType = 'start condition';
31
+ else if (loopCardinality) this.loopType = 'cardinality';
24
32
 
25
- const {debug} = environment.Logger(type.toLowerCase());
26
- const executeConsumerTag = '_execute-q-multi-instance-tag';
27
- broker.cancel(executeConsumerTag);
33
+ this.characteristics = null;
34
+ this.execution = null;
35
+ }
28
36
 
29
- const apiConsumerTag = '_api-multi-instance-tag';
30
- broker.cancel(apiConsumerTag);
37
+ LoopCharacteristics.prototype.execute = function execute(executeMessage) {
38
+ if (!executeMessage) throw new TypeError('LoopCharacteristics execution requires message');
39
+ const chr = this.characteristics = this.characteristics || new Characteristics(this.activity, this.loopCharacteristics, executeMessage);
40
+ if (chr.cardinality === 0) return chr.complete();
31
41
 
32
- let loopSettings;
42
+ const execution = this.isSequential ? new SequentialLoopCharacteristics(this.activity, chr) : new ParallelLoopCharacteristics(this.activity, chr);
43
+ return execution.execute(executeMessage);
44
+ };
33
45
 
34
- const characteristicsApi = {
35
- type,
36
- loopType,
37
- collection: collectionExpression,
38
- elementVariable,
39
- isSequential,
40
- loopCardinality,
41
- execute,
42
- };
46
+ function SequentialLoopCharacteristics(activity, characteristics) {
47
+ this.activity = activity;
48
+ this.id = activity.id;
49
+ this.characteristics = characteristics;
50
+ }
43
51
 
44
- return characteristicsApi;
52
+ SequentialLoopCharacteristics.prototype.execute = function execute(executeMessage) {
53
+ const {routingKey: executeRoutingKey, redelivered: isRedelivered} = executeMessage.fields || {};
54
+ const chr = this.characteristics;
55
+ if (!chr.cardinality && !chr.startCondition && !chr.completionCondition) {
56
+ throw new RunError(`<${this.id}> cardinality, collection, or condition is required in sequential loops`, executeMessage);
57
+ }
45
58
 
46
- function getLoopType() {
47
- if (collectionExpression) return 'collection';
48
- if (completionCondition) return 'complete condition';
49
- if (startCondition) return 'start condition';
50
- if (loopCardinality) return 'cardinality';
59
+ let startIndex = 0;
60
+ if (isRedelivered && executeRoutingKey === 'execute.iteration.next') {
61
+ startIndex = executeMessage.content.index;
51
62
  }
63
+ chr.subscribe(this._onCompleteMessage.bind(this));
52
64
 
53
- function execute(executeMessage) {
54
- if (!executeMessage) throw new Error('LoopCharacteristics execution requires message');
55
- const {routingKey: executeRoutingKey, redelivered: isRedelivered} = executeMessage.fields || {};
56
- const {executionId: parentExecutionId} = executeMessage.content;
57
- if (!getCharacteristics()) return;
65
+ return this._startNext(startIndex, isRedelivered);
66
+ };
58
67
 
59
- try {
60
- return isSequential ? executeSequential() : executeParallel();
61
- } catch (err) {
62
- return activity.emitFatal(new ActivityError(err.message, executeMessage, err), executeMessage.content);
63
- }
68
+ SequentialLoopCharacteristics.prototype._startNext = function startNext(index, ignoreIfExecuting) {
69
+ const chr = this.characteristics;
70
+ const content = chr.next(index);
71
+ if (!content) return;
64
72
 
65
- function executeSequential() {
66
- const {cardinality, getContent: getStartContent} = getCharacteristics();
67
- if (cardinality === 0) return complete();
68
- if (!cardinality && !startCondition && !completionCondition) return activity.emitFatal(new ActivityError(`<${id}> cardinality, collection, or condition is required in sequential loops`, executeMessage), getStartContent());
69
-
70
- let startIndex = 0;
71
-
72
- if (isRedelivered && executeRoutingKey === 'execute.iteration.next') {
73
- startIndex = executeMessage.content.index;
74
- }
75
- subscribe(onCompleteMessage);
76
-
77
- return startNext(startIndex, isRedelivered);
78
-
79
- function startNext(index, ignoreIfExecuting) {
80
- const content = next(index);
81
- if (!content) return;
82
-
83
- const characteristics = getCharacteristics();
84
- if (startCondition && isConditionMet(startCondition, {content})) {
85
- debug(`<${parentExecutionId} (${id})> start condition met`);
86
- return;
87
- }
88
-
89
- debug(`<${content.executionId} (${id})>`, ignoreIfExecuting ? 'resume' : 'start', `sequential iteration index ${content.index}`);
90
- broker.publish('execution', 'execute.iteration.next', {
91
- ...content,
92
- ...characteristics.getContent(),
93
- index,
94
- preventComplete: true,
95
- output: characteristics.output.slice(),
96
- state: 'iteration.next',
97
- });
98
-
99
- broker.publish('execution', 'execute.start', {...content, ignoreIfExecuting});
100
- return content;
101
- }
102
-
103
- function onCompleteMessage(_, message) {
104
- const {content} = message;
105
- const loopOutput = getCharacteristics().output;
106
- if (content.output !== undefined) loopOutput[content.index] = content.output;
107
-
108
- broker.publish('execution', 'execute.iteration.completed', {
109
- ...message.content,
110
- ...getCharacteristics().getContent(),
111
- preventComplete: true,
112
- output: loopOutput.slice(),
113
- state: 'iteration.completed',
114
- });
115
-
116
- if (isConditionMet(completionCondition, message, loopOutput)) {
117
- debug(`<${parentExecutionId} (${id})> complete condition met`);
118
- } else if (startNext(content.index + 1)) return;
119
-
120
- debug(`<${parentExecutionId} (${id})> sequential loop completed`);
121
-
122
- return complete(content);
123
- }
124
-
125
- function complete(content) {
126
- stop();
127
-
128
- const {getContent, output} = getCharacteristics();
129
-
130
- return broker.publish('execution', 'execute.completed', {
131
- ...content,
132
- ...getContent(),
133
- output,
134
- });
135
- }
136
- }
73
+ if (chr.isStartConditionMet({content})) {
74
+ chr.debug('start condition met');
75
+ return;
76
+ }
137
77
 
138
- function executeParallel() {
139
- const {cardinality, getContent: getStartContent} = getCharacteristics();
140
-
141
- if (cardinality === 0) return complete();
142
- if (!cardinality) return activity.emitFatal(new ActivityError(`<${id}> cardinality or collection is required in parallel loops`, executeMessage), getStartContent());
143
-
144
- let index = 0, running = 0;
145
- if (isRedelivered) {
146
- if (!isNaN(executeMessage.content.index)) index = executeMessage.content.index;
147
- if (!isNaN(executeMessage.content.running)) running = executeMessage.content.running;
148
- }
149
- subscribe(onCompleteMessage);
150
-
151
- if (isRedelivered) return;
152
-
153
- return startBatch();
154
-
155
- function startBatch() {
156
- const {output: loopOutput, getContent} = getCharacteristics();
157
- const batch = [];
158
-
159
- let startContent = next(index);
160
- do {
161
- debug(`<${parentExecutionId} (${id})> start parallel iteration index ${index}`);
162
- batch.push(startContent);
163
- running++;
164
- index++;
165
-
166
- if (index >= cardinality || running >= batchSize) {
167
- break;
168
- }
169
- } while ((startContent = next(index)));
170
-
171
- broker.publish('execution', 'execute.iteration.batch', {
172
- ...getContent(),
173
- index,
174
- running,
175
- output: loopOutput,
176
- preventComplete: true,
177
- });
178
-
179
- for (const content of batch) {
180
- broker.publish('execution', 'execute.start', content);
181
- }
182
- }
183
-
184
- function onCompleteMessage(_, message) {
185
- const {content} = message;
186
- const {output: loopOutput} = getCharacteristics();
187
- if (content.output !== undefined) loopOutput[content.index] = content.output;
188
-
189
- running--;
190
-
191
- broker.publish('execution', 'execute.iteration.completed', {
192
- ...content,
193
- ...getCharacteristics().getContent(),
194
- index,
195
- running,
196
- output: loopOutput,
197
- state: 'iteration.completed',
198
- preventComplete: true,
199
- });
200
-
201
- if (running <= 0 && !next(index)) {
202
- return complete(content);
203
- }
204
-
205
- if (isConditionMet(completionCondition, message)) {
206
- return complete(content);
207
- }
208
-
209
- if (running <= 0) {
210
- running = 0;
211
- startBatch();
212
- }
213
- }
214
-
215
- function complete(content) {
216
- stop();
217
-
218
- const {getContent, output} = getCharacteristics();
219
-
220
- return broker.publish('execution', 'execute.completed', {
221
- ...content,
222
- ...getContent(),
223
- output,
224
- });
225
- }
226
- }
78
+ chr.debug(`${ignoreIfExecuting ? 'resume' : 'start'} sequential iteration index ${content.index}`);
79
+ const broker = this.activity.broker;
80
+ broker.publish('execution', 'execute.iteration.next', {
81
+ ...content,
82
+ ...chr.getContent(),
83
+ index,
84
+ preventComplete: true,
85
+ output: chr.output.slice(),
86
+ state: 'iteration.next',
87
+ });
88
+
89
+ broker.publish('execution', 'execute.start', {...content, ignoreIfExecuting});
90
+ return content;
91
+ };
92
+
93
+ SequentialLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMessage(_, message) {
94
+ const {content} = message;
95
+ const chr = this.characteristics;
96
+ const loopOutput = chr.output;
97
+
98
+ if (content.output !== undefined) loopOutput[content.index] = content.output;
99
+
100
+ this.activity.broker.publish('execution', 'execute.iteration.completed', {
101
+ ...message.content,
102
+ ...chr.getContent(),
103
+ preventComplete: true,
104
+ output: loopOutput.slice(),
105
+ state: 'iteration.completed',
106
+ });
107
+
108
+ if (chr.isCompletionConditionMet(message, loopOutput)) {
109
+ chr.debug('complete condition met');
110
+ } else if (this._startNext(content.index + 1)) return;
111
+
112
+ chr.debug('sequential loop completed');
113
+
114
+ return chr.complete(content);
115
+ };
116
+
117
+ function ParallelLoopCharacteristics(activity, characteristics) {
118
+ this.activity = activity;
119
+ this.id = activity.id;
120
+ this.characteristics = characteristics;
121
+ this.running = 0;
122
+ this.index = 0;
123
+ }
227
124
 
228
- function next(index) {
229
- const executionId = `${parentExecutionId}_${index}`;
125
+ ParallelLoopCharacteristics.prototype.execute = function execute(executeMessage) {
126
+ const chr = this.characteristics;
127
+ if (!chr.cardinality) throw new RunError(`<${this.id}> cardinality or collection is required in parallel loops`, executeMessage);
230
128
 
231
- const {cardinality, collection, parent, getContent} = getCharacteristics();
232
- const content = {
233
- ...getContent(),
234
- isRootScope: undefined,
235
- executionId,
236
- isMultiInstance: true,
237
- index,
238
- parent,
239
- };
129
+ const isRedelivered = executeMessage.fields.redelivered;
130
+ if (isRedelivered) {
131
+ if (!isNaN(executeMessage.content.index)) this.index = executeMessage.content.index;
132
+ if (!isNaN(executeMessage.content.running)) this.running = executeMessage.content.running;
133
+ }
134
+ chr.subscribe(this._onCompleteMessage.bind(this));
135
+
136
+ if (isRedelivered) return;
240
137
 
241
- if (isComplete(content)) return;
138
+ return this._startBatch();
139
+ };
242
140
 
243
- if (collection) {
244
- content[elementVariable] = collection[index];
245
- }
141
+ ParallelLoopCharacteristics.prototype._startBatch = function startBatch() {
142
+ const chr = this.characteristics;
143
+ const cardinality = chr.cardinality;
144
+ const batch = [];
246
145
 
247
- return content;
146
+ let startContent = chr.next(this.index);
147
+ do {
148
+ chr.debug(`start parallel iteration index ${this.index}`);
149
+ batch.push(startContent);
150
+ this.running++;
151
+ this.index++;
248
152
 
249
- function isComplete() {
250
- if (cardinality > 0 && index >= cardinality) return true;
251
- if (collection && index >= collection.length) return true;
252
- }
153
+ if (this.index >= cardinality || this.running >= chr.batchSize) {
154
+ break;
253
155
  }
156
+ } while ((startContent = chr.next(this.index)));
157
+
158
+ const broker = this.activity.broker;
159
+ broker.publish('execution', 'execute.iteration.batch', {
160
+ ...chr.getContent(),
161
+ index: this.index,
162
+ running: this.running,
163
+ output: chr.output,
164
+ preventComplete: true,
165
+ });
166
+
167
+ for (const content of batch) {
168
+ broker.publish('execution', 'execute.start', content);
169
+ }
170
+ };
171
+
172
+ ParallelLoopCharacteristics.prototype._onCompleteMessage = function onCompleteMessage(_, message) {
173
+ const chr = this.characteristics;
174
+ const {content} = message;
175
+ if (content.output !== undefined) chr.output[content.index] = content.output;
176
+
177
+ this.running--;
178
+
179
+ this.activity.broker.publish('execution', 'execute.iteration.completed', {
180
+ ...content,
181
+ ...chr.getContent(),
182
+ index: this.index,
183
+ running: this.running,
184
+ output: chr.output,
185
+ state: 'iteration.completed',
186
+ preventComplete: true,
187
+ });
188
+
189
+ if (this.running <= 0 && !chr.next(this.index)) {
190
+ return chr.complete(content);
191
+ }
254
192
 
255
- function getCharacteristics() {
256
- if (loopSettings) return loopSettings;
193
+ if (chr.isCompletionConditionMet(message)) {
194
+ return chr.complete(content);
195
+ }
196
+
197
+ if (this.running <= 0) {
198
+ this.running = 0;
199
+ this._startBatch();
200
+ }
201
+ };
257
202
 
258
- const collection = getCollection();
259
- const cardinality = getCardinality(collection);
203
+ function Characteristics(activity, loopCharacteristics, executeMessage) {
204
+ this.activity = activity;
205
+ const behaviour = this.behaviour = loopCharacteristics.behaviour || {};
206
+ this.message = executeMessage;
260
207
 
261
- const messageContent = {
262
- ...cloneContent(executeMessage.content),
263
- loopCardinality: cardinality,
264
- isSequential,
265
- output: undefined,
266
- };
208
+ const type = this.type = loopCharacteristics.type || 'LoopCharacteristics';
209
+ this.id = activity.id;
210
+ this.broker = activity.broker;
211
+ this.parentExecutionId = executeMessage.content.executionId;
267
212
 
268
- if (cardinality !== undefined && isNaN(cardinality) || cardinality < 0) {
269
- return activity.emitFatal(new ActivityError(`<${id}> invalid loop cardinality >${cardinality}<`, executeMessage), messageContent);
270
- }
213
+ this.isSequential = behaviour.isSequential || false;
214
+ this.output = executeMessage.content.output || [];
215
+ this.parent = unshiftParent(executeMessage.content.parent, executeMessage.content);
271
216
 
272
- const output = executeMessage.content.output || [];
217
+ if ('loopCardinality' in behaviour) this.loopCardinality = behaviour.loopCardinality;
218
+ else if ('loopMaximum' in behaviour) this.loopCardinality = behaviour.loopMaximum;
273
219
 
274
- const parent = unshiftParent(executeMessage.content.parent, executeMessage.content);
220
+ if (behaviour.loopCondition) {
221
+ if (behaviour.testBefore) this.startCondition = behaviour.loopCondition;
222
+ else this.completionCondition = behaviour.loopCondition;
223
+ }
224
+ if (behaviour.completionCondition) {
225
+ this.completionCondition = behaviour.completionCondition;
226
+ }
275
227
 
276
- loopSettings = {
277
- cardinality,
278
- collection,
279
- messageContent,
280
- output,
281
- parent,
282
- getContent() {
283
- return cloneContent(messageContent);
284
- },
285
- };
228
+ const collection = this.collection = this.getCollection();
229
+ if (collection) {
230
+ this.elementVariable = behaviour.elementVariable || 'item';
231
+ }
232
+ this.cardinality = this.getCardinality(collection);
286
233
 
287
- return loopSettings;
288
- }
234
+ this.onApiMessage = this.onApiMessage.bind(this);
289
235
 
290
- function getCardinality(collection) {
291
- const collectionLen = Array.isArray(collection) ? collection.length : undefined;
292
- if (!loopCardinality) {
293
- return collectionLen;
294
- }
295
- const value = environment.resolveExpression(loopCardinality, executeMessage);
296
- if (value === undefined) return collectionLen;
297
- return Number(value);
298
- }
236
+ const environment = activity.environment;
237
+ this.logger = environment.Logger(type.toLowerCase());
238
+ this.batchSize = environment.settings.batchSize || 50;
239
+ }
299
240
 
300
- function getCollection() {
301
- if (!collectionExpression) return;
302
- debug(`<${id}> has collection`);
303
- return environment.resolveExpression(collectionExpression, executeMessage);
304
- }
241
+ Characteristics.prototype.getContent = function getContent() {
242
+ return {
243
+ ...cloneContent(this.message.content),
244
+ loopCardinality: this.cardinality,
245
+ isSequential: this.isSequential,
246
+ output: undefined,
247
+ };
248
+ };
249
+
250
+ Characteristics.prototype.next = function next(index) {
251
+ const cardinality = this.cardinality;
252
+ if (cardinality > 0 && index >= cardinality) return;
253
+
254
+ const collection = this.collection;
255
+ if (collection && index >= collection.length) return;
256
+
257
+ const content = {
258
+ ...this.getContent(),
259
+ isRootScope: undefined,
260
+ executionId: `${this.parentExecutionId}_${index}`,
261
+ isMultiInstance: true,
262
+ parent: cloneParent(this.parent),
263
+ index,
264
+ };
305
265
 
306
- function subscribe(onIterationCompleteMessage) {
307
- broker.subscribeTmp('api', `activity.*.${parentExecutionId}`, onApiMessage, {noAck: true, consumerTag: apiConsumerTag}, {priority: 400});
308
- broker.subscribeTmp('execution', 'execute.*', onComplete, {noAck: true, consumerTag: executeConsumerTag, priority: 300});
309
-
310
- function onComplete(routingKey, message, ...args) {
311
- if (!message.content.isMultiInstance) return;
312
- switch (routingKey) {
313
- case 'execute.cancel':
314
- case 'execute.completed':
315
- return onIterationCompleteMessage(routingKey, message, ...args);
316
- }
317
- }
318
- }
266
+ if (collection) {
267
+ content[this.elementVariable] = collection[index];
319
268
  }
320
269
 
321
- function onApiMessage(_, message) {
322
- switch (message.properties.type) {
323
- case 'stop':
324
- case 'discard':
325
- stop();
326
- break;
270
+ return content;
271
+ };
272
+
273
+ Characteristics.prototype.getCardinality = function getCardinality(collection) {
274
+ const collectionLen = this.collection && Array.isArray(collection) ? collection.length : undefined;
275
+ if (!this.loopCardinality) {
276
+ return collectionLen;
277
+ }
278
+ const value = this.activity.environment.resolveExpression(this.loopCardinality, this.message);
279
+ if (value !== undefined && isNaN(value) || value < 0) {
280
+ throw new RunError(`<${this.id}> invalid loop cardinality >${value}<`, this.message);
281
+ }
282
+ if (value === undefined) return collectionLen;
283
+ return Number(value);
284
+ };
285
+
286
+ Characteristics.prototype.getCollection = function getCollection() {
287
+ const collectionExpression = this.behaviour.collection;
288
+ if (!collectionExpression) return;
289
+ return this.activity.environment.resolveExpression(collectionExpression, this.message);
290
+ };
291
+
292
+ Characteristics.prototype.isStartConditionMet = function isStartConditionMet(message) {
293
+ if (!this.startCondition) return false;
294
+ return this.activity.environment.resolveExpression(this.startCondition, cloneMessage(message));
295
+ };
296
+
297
+ Characteristics.prototype.isCompletionConditionMet = function isCompletionConditionMet(message) {
298
+ if (!this.completionCondition) return false;
299
+ return this.activity.environment.resolveExpression(this.completionCondition, cloneMessage(message, {loopOutput: this.output}));
300
+ };
301
+
302
+ Characteristics.prototype.complete = function complete(content) {
303
+ this.stop();
304
+
305
+ return this.broker.publish('execution', 'execute.completed', {
306
+ ...content,
307
+ ...this.getContent(),
308
+ output: this.output,
309
+ });
310
+ };
311
+
312
+ Characteristics.prototype.subscribe = function subscribe(onIterationCompleteMessage) {
313
+ this.broker.subscribeTmp('api', `activity.*.${this.parentExecutionId}`, this.onApiMessage, {noAck: true, consumerTag: '_api-multi-instance-tag'}, {priority: 400});
314
+ this.broker.subscribeTmp('execution', 'execute.*', onComplete, {noAck: true, consumerTag: '_execute-q-multi-instance-tag', priority: 300});
315
+
316
+ function onComplete(routingKey, message, ...args) {
317
+ if (!message.content.isMultiInstance) return;
318
+ switch (routingKey) {
319
+ case 'execute.cancel':
320
+ case 'execute.completed':
321
+ return onIterationCompleteMessage(routingKey, message, ...args);
327
322
  }
328
323
  }
329
-
330
- function stop() {
331
- broker.cancel(executeConsumerTag);
332
- broker.cancel(apiConsumerTag);
324
+ };
325
+
326
+ Characteristics.prototype.onApiMessage = function onApiMessage(_, message) {
327
+ switch (message.properties.type) {
328
+ case 'stop':
329
+ case 'discard':
330
+ this.stop();
331
+ break;
333
332
  }
333
+ };
334
334
 
335
- function isConditionMet(condition, message, loopOutput) {
336
- if (!condition) return false;
337
- const testContext = cloneMessage(message, {loopOutput});
338
- return environment.resolveExpression(condition, testContext);
339
- }
340
- }
335
+ Characteristics.prototype.stop = function stop() {
336
+ this.broker.cancel('_execute-q-multi-instance-tag');
337
+ this.broker.cancel('_api-multi-instance-tag');
338
+ };
341
339
 
340
+ Characteristics.prototype.debug = function debug(msg) {
341
+ this.logger.debug(`<${this.parentExecutionId} (${this.id})> ${msg}`);
342
+ };