bpmn-elements 16.1.0 → 16.2.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.
@@ -11,6 +11,7 @@ var _EventBroker = require("../EventBroker.js");
11
11
  var _MessageFormatter = require("../MessageFormatter.js");
12
12
  var _messageHelper = require("../messageHelper.js");
13
13
  var _Errors = require("../error/Errors.js");
14
+ var _outboundEvaluator = require("./outbound-evaluator.js");
14
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
16
  const kActivityDef = Symbol.for('activityDefinition');
16
17
  const kConsuming = Symbol.for('consuming');
@@ -84,15 +85,20 @@ function Activity(Behaviour, activityDef, context) {
84
85
  inboundTriggers = inboundSequenceFlows.slice();
85
86
  }
86
87
  const outboundSequenceFlows = context.getOutboundSequenceFlows(id);
88
+ const isParallelJoin = activityDef.isParallelGateway && inboundSequenceFlows.length > 1;
87
89
  const flows = this[kFlows] = {
88
90
  inboundSequenceFlows,
89
91
  inboundAssociations,
90
- inboundJoinFlows: new Set(),
91
92
  inboundTriggers,
92
93
  outboundSequenceFlows,
93
- outboundEvaluator: new OutboundEvaluator(this, outboundSequenceFlows)
94
+ outboundEvaluator: new _outboundEvaluator.OutboundEvaluator(this, outboundSequenceFlows),
95
+ ...(isParallelJoin && {
96
+ inboundJoinFlows: new Set(),
97
+ inboundSourceIds: new Set(inboundSequenceFlows.map(({
98
+ sourceId
99
+ }) => sourceId))
100
+ })
94
101
  };
95
- const isParallelJoin = activityDef.isParallelGateway && flows.inboundSequenceFlows.length > 1;
96
102
  this[kFlags] = {
97
103
  isEnd: flows.outboundSequenceFlows.length === 0,
98
104
  isStart: flows.inboundSequenceFlows.length === 0 && !attachedTo && !behaviour.triggeredByEvent && !isForCompensation,
@@ -508,21 +514,21 @@ Activity.prototype._onJoinInbound = function onJoinInbound(routingKey, message)
508
514
  } = message;
509
515
  const {
510
516
  inboundJoinFlows,
511
- inboundTriggers
517
+ inboundSourceIds
512
518
  } = this[kFlows];
513
519
  let alreadyTouched = false;
514
520
  const touched = new Set();
515
521
  let taken;
516
522
  for (const msg of inboundJoinFlows) {
517
- const flowId = msg.content.id;
518
- touched.add(flowId);
519
- if (flowId === content.id) {
523
+ const sourceId = msg.content.sourceId;
524
+ touched.add(sourceId);
525
+ if (sourceId === content.sourceId) {
520
526
  alreadyTouched = true;
521
527
  }
522
528
  }
523
529
  inboundJoinFlows.add(message);
524
530
  if (alreadyTouched) return;
525
- const remaining = inboundTriggers.length - touched.size - 1;
531
+ const remaining = inboundSourceIds.size - touched.size - 1;
526
532
  if (remaining) {
527
533
  return this.logger.debug(`<${this.id}> inbound ${message.content.action} from <${message.content.id}>, ${remaining} remaining`);
528
534
  }
@@ -851,11 +857,11 @@ Activity.prototype._doOutbound = function doOutbound(fromMessage, isDiscarded, c
851
857
  }
852
858
  let outboundFlows;
853
859
  if (isDiscarded) {
854
- outboundFlows = outboundSequenceFlows.map(flow => formatFlowAction(flow, {
860
+ outboundFlows = outboundSequenceFlows.map(flow => (0, _outboundEvaluator.formatFlowAction)(flow, {
855
861
  action: 'discard'
856
862
  }));
857
863
  } else if (fromContent.outbound && fromContent.outbound.length) {
858
- outboundFlows = outboundSequenceFlows.map(flow => formatFlowAction(flow, fromContent.outbound.filter(f => f.id === flow.id).pop()));
864
+ outboundFlows = outboundSequenceFlows.map(flow => (0, _outboundEvaluator.formatFlowAction)(flow, fromContent.outbound.filter(f => f.id === flow.id).pop()));
859
865
  }
860
866
  if (outboundFlows) {
861
867
  this._doRunOutbound(outboundFlows, fromContent, discardSequence);
@@ -868,25 +874,41 @@ Activity.prototype._doOutbound = function doOutbound(fromMessage, isDiscarded, c
868
874
  });
869
875
  };
870
876
  Activity.prototype._doRunOutbound = function doRunOutbound(outboundList, content, discardSequence) {
871
- for (const outboundFlow of outboundList) {
872
- const {
873
- id: flowId,
874
- action,
875
- result
876
- } = outboundFlow;
877
- this.broker.publish('run', 'run.outbound.' + action, (0, _messageHelper.cloneContent)(content, {
878
- flow: {
879
- ...(result && typeof result === 'object' && result),
880
- ...outboundFlow,
881
- sequenceId: (0, _shared.getUniqueId)(`${flowId}_${action}`),
882
- ...(discardSequence && {
883
- discardSequence: discardSequence.slice()
884
- })
877
+ if (outboundList.length === 1) {
878
+ this._publishRunOutbound(outboundList[0], content, discardSequence);
879
+ } else {
880
+ const targets = new Map();
881
+ for (const outboundFlow of outboundList) {
882
+ const prevTarget = targets.get(outboundFlow.targetId);
883
+ if (!prevTarget) {
884
+ targets.set(outboundFlow.targetId, outboundFlow);
885
+ } else if (outboundFlow.action === 'take' && outboundFlow.action !== prevTarget.action) {
886
+ targets.set(outboundFlow.targetId, outboundFlow);
885
887
  }
886
- }));
888
+ }
889
+ for (const outboundFlow of targets.values()) {
890
+ this._publishRunOutbound(outboundFlow, content, discardSequence);
891
+ }
887
892
  }
888
893
  return outboundList;
889
894
  };
895
+ Activity.prototype._publishRunOutbound = function publishRunOutbound(outboundFlow, content, discardSequence) {
896
+ const {
897
+ id: flowId,
898
+ action,
899
+ result
900
+ } = outboundFlow;
901
+ this.broker.publish('run', 'run.outbound.' + action, (0, _messageHelper.cloneContent)(content, {
902
+ flow: {
903
+ ...(result && typeof result === 'object' && result),
904
+ ...outboundFlow,
905
+ sequenceId: (0, _shared.getUniqueId)(`${flowId}_${action}`),
906
+ ...(discardSequence && {
907
+ discardSequence: discardSequence.slice()
908
+ })
909
+ }
910
+ }));
911
+ };
890
912
  Activity.prototype._onResumeMessage = function onResumeMessage(message) {
891
913
  message.ack();
892
914
  const stateMessage = this[kStateMessage];
@@ -991,128 +1013,4 @@ Activity.prototype._deactivateRunConsumers = function _deactivateRunConsumers()
991
1013
  this._pauseRunQ();
992
1014
  broker.cancel('_activity-execution');
993
1015
  this[kConsuming] = false;
994
- };
995
- function OutboundEvaluator(activity, outboundFlows) {
996
- this.activity = activity;
997
- this.broker = activity.broker;
998
- const flows = this.outboundFlows = outboundFlows.slice();
999
- const defaultFlowIdx = flows.findIndex(({
1000
- isDefault
1001
- }) => isDefault);
1002
- if (defaultFlowIdx > -1) {
1003
- const [defaultFlow] = flows.splice(defaultFlowIdx, 1);
1004
- flows.push(defaultFlow);
1005
- }
1006
- this.defaultFlowIdx = outboundFlows.findIndex(({
1007
- isDefault
1008
- }) => isDefault);
1009
- this._onEvaluated = this.onEvaluated.bind(this);
1010
- this.evaluateArgs = {};
1011
- }
1012
- OutboundEvaluator.prototype.evaluate = function evaluate(fromMessage, discardRestAtTake, callback) {
1013
- const outboundFlows = this.outboundFlows;
1014
- const args = this.evaluateArgs = {
1015
- fromMessage,
1016
- evaluationId: fromMessage.content.executionId,
1017
- discardRestAtTake,
1018
- callback,
1019
- conditionMet: false,
1020
- result: {},
1021
- takenCount: 0
1022
- };
1023
- if (!outboundFlows.length) return this.completed();
1024
- const flows = args.flows = outboundFlows.slice();
1025
- this.broker.subscribeTmp('execution', 'evaluate.flow.#', this._onEvaluated, {
1026
- consumerTag: `_flow-evaluation-${args.evaluationId}`
1027
- });
1028
- return this.evaluateFlow(flows.shift());
1029
- };
1030
- OutboundEvaluator.prototype.onEvaluated = function onEvaluated(routingKey, message) {
1031
- const content = message.content;
1032
- const {
1033
- id: flowId,
1034
- action,
1035
- evaluationId
1036
- } = message.content;
1037
- const args = this.evaluateArgs;
1038
- if (action === 'take') {
1039
- args.takenCount++;
1040
- args.conditionMet = true;
1041
- }
1042
- args.result[flowId] = content;
1043
- if ('result' in content) {
1044
- this.activity.logger.debug(`<${evaluationId} (${this.activity.id})> flow <${flowId}> evaluated to: ${!!content.result}`);
1045
- }
1046
- let nextFlow = args.flows.shift();
1047
- if (!nextFlow) return this.completed();
1048
- if (args.discardRestAtTake && args.conditionMet) {
1049
- do {
1050
- args.result[nextFlow.id] = formatFlowAction(nextFlow, {
1051
- action: 'discard'
1052
- });
1053
- } while (nextFlow = args.flows.shift());
1054
- return this.completed();
1055
- }
1056
- if (args.conditionMet && nextFlow.isDefault) {
1057
- args.result[nextFlow.id] = formatFlowAction(nextFlow, {
1058
- action: 'discard'
1059
- });
1060
- return this.completed();
1061
- }
1062
- message.ack();
1063
- this.evaluateFlow(nextFlow);
1064
- };
1065
- OutboundEvaluator.prototype.evaluateFlow = function evaluateFlow(flow) {
1066
- const broker = this.broker;
1067
- const {
1068
- fromMessage,
1069
- evaluationId
1070
- } = this.evaluateArgs;
1071
- flow.evaluate((0, _messageHelper.cloneMessage)(fromMessage), (err, result) => {
1072
- if (err) return this.completed(err);
1073
- const action = result ? 'take' : 'discard';
1074
- return broker.publish('execution', 'evaluate.flow.' + action, formatFlowAction(flow, {
1075
- action,
1076
- result,
1077
- evaluationId
1078
- }), {
1079
- persistent: false
1080
- });
1081
- });
1082
- };
1083
- OutboundEvaluator.prototype.completed = function completed(err) {
1084
- const {
1085
- callback,
1086
- evaluationId,
1087
- fromMessage,
1088
- result,
1089
- takenCount
1090
- } = this.evaluateArgs;
1091
- this.broker.cancel(`_flow-evaluation-${evaluationId}`);
1092
- if (err) return callback(err);
1093
- if (!takenCount && this.outboundFlows.length) {
1094
- const nonTakenError = new _Errors.ActivityError(`<${this.activity.id}> no conditional flow taken`, fromMessage);
1095
- return callback(nonTakenError);
1096
- }
1097
- const message = fromMessage.content.message;
1098
- const evaluationResult = [];
1099
- for (const flow of Object.values(result)) {
1100
- evaluationResult.push({
1101
- ...flow,
1102
- ...(message !== undefined && {
1103
- message
1104
- })
1105
- });
1106
- }
1107
- return callback(null, evaluationResult);
1108
- };
1109
- function formatFlowAction(flow, options) {
1110
- return {
1111
- ...options,
1112
- id: flow.id,
1113
- action: options.action,
1114
- ...(flow.isDefault && {
1115
- isDefault: true
1116
- })
1117
- };
1118
- }
1016
+ };
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.OutboundEvaluator = OutboundEvaluator;
7
+ exports.formatFlowAction = formatFlowAction;
8
+ var _Errors = require("../error/Errors.js");
9
+ var _messageHelper = require("../messageHelper.js");
10
+ function OutboundEvaluator(activity, outboundFlows) {
11
+ this.activity = activity;
12
+ this.broker = activity.broker;
13
+ const flows = this.outboundFlows = outboundFlows.slice();
14
+ const defaultFlowIdx = flows.findIndex(({
15
+ isDefault
16
+ }) => isDefault);
17
+ if (defaultFlowIdx > -1) {
18
+ const [defaultFlow] = flows.splice(defaultFlowIdx, 1);
19
+ flows.push(defaultFlow);
20
+ }
21
+ this.defaultFlowIdx = outboundFlows.findIndex(({
22
+ isDefault
23
+ }) => isDefault);
24
+ this._onEvaluated = this.onEvaluated.bind(this);
25
+ this.evaluateArgs = {};
26
+ }
27
+ OutboundEvaluator.prototype.evaluate = function evaluate(fromMessage, discardRestAtTake, callback) {
28
+ const outboundFlows = this.outboundFlows;
29
+ const args = this.evaluateArgs = {
30
+ fromMessage,
31
+ evaluationId: fromMessage.content.executionId,
32
+ discardRestAtTake,
33
+ callback,
34
+ conditionMet: false,
35
+ result: {},
36
+ takenCount: 0
37
+ };
38
+ if (!outboundFlows.length) return this.completed();
39
+ const flows = args.flows = outboundFlows.slice();
40
+ this.broker.subscribeTmp('execution', 'evaluate.flow.#', this._onEvaluated, {
41
+ consumerTag: `_flow-evaluation-${args.evaluationId}`
42
+ });
43
+ return this.evaluateFlow(flows.shift());
44
+ };
45
+ OutboundEvaluator.prototype.onEvaluated = function onEvaluated(routingKey, message) {
46
+ const content = message.content;
47
+ const {
48
+ id: flowId,
49
+ action,
50
+ evaluationId
51
+ } = message.content;
52
+ const args = this.evaluateArgs;
53
+ if (action === 'take') {
54
+ args.takenCount++;
55
+ args.conditionMet = true;
56
+ }
57
+ args.result[flowId] = content;
58
+ if ('result' in content) {
59
+ this.activity.logger.debug(`<${evaluationId} (${this.activity.id})> flow <${flowId}> evaluated to: ${!!content.result}`);
60
+ }
61
+ let nextFlow = args.flows.shift();
62
+ if (!nextFlow) return this.completed();
63
+ if (args.discardRestAtTake && args.conditionMet) {
64
+ do {
65
+ args.result[nextFlow.id] = formatFlowAction(nextFlow, {
66
+ action: 'discard'
67
+ });
68
+ } while (nextFlow = args.flows.shift());
69
+ return this.completed();
70
+ }
71
+ if (args.conditionMet && nextFlow.isDefault) {
72
+ args.result[nextFlow.id] = formatFlowAction(nextFlow, {
73
+ action: 'discard'
74
+ });
75
+ return this.completed();
76
+ }
77
+ message.ack();
78
+ this.evaluateFlow(nextFlow);
79
+ };
80
+ OutboundEvaluator.prototype.evaluateFlow = function evaluateFlow(flow) {
81
+ const broker = this.broker;
82
+ const {
83
+ fromMessage,
84
+ evaluationId
85
+ } = this.evaluateArgs;
86
+ flow.evaluate((0, _messageHelper.cloneMessage)(fromMessage), (err, result) => {
87
+ if (err) return this.completed(err);
88
+ const action = result ? 'take' : 'discard';
89
+ return broker.publish('execution', 'evaluate.flow.' + action, formatFlowAction(flow, {
90
+ action,
91
+ result,
92
+ evaluationId
93
+ }), {
94
+ persistent: false
95
+ });
96
+ });
97
+ };
98
+ OutboundEvaluator.prototype.completed = function completed(err) {
99
+ const {
100
+ callback,
101
+ evaluationId,
102
+ fromMessage,
103
+ result,
104
+ takenCount
105
+ } = this.evaluateArgs;
106
+ this.broker.cancel(`_flow-evaluation-${evaluationId}`);
107
+ if (err) return callback(err);
108
+ if (!takenCount && this.outboundFlows.length) {
109
+ const nonTakenError = new _Errors.ActivityError(`<${this.activity.id}> no conditional flow taken`, fromMessage);
110
+ return callback(nonTakenError);
111
+ }
112
+ const message = fromMessage.content.message;
113
+ const evaluationResult = [];
114
+ for (const flow of Object.values(result)) {
115
+ evaluationResult.push({
116
+ ...flow,
117
+ ...(message !== undefined && {
118
+ message
119
+ })
120
+ });
121
+ }
122
+ return callback(null, evaluationResult);
123
+ };
124
+ function formatFlowAction(flow, options) {
125
+ return {
126
+ ...options,
127
+ id: flow.id,
128
+ action: options.action,
129
+ targetId: flow.targetId,
130
+ ...(flow.isDefault && {
131
+ isDefault: true
132
+ })
133
+ };
134
+ }
@@ -19,7 +19,7 @@ function EventBasedGatewayBehaviour(activity, context) {
19
19
  this.activity = activity;
20
20
  this.broker = activity.broker;
21
21
  this.context = context;
22
- this[kTargets] = activity.outbound.map(flow => context.getActivityById(flow.targetId));
22
+ this[kTargets] = new Set(activity.outbound.map(flow => context.getActivityById(flow.targetId)));
23
23
  }
24
24
  EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage) {
25
25
  const executeContent = executeMessage.content;
@@ -30,7 +30,7 @@ EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage)
30
30
  } = executeContent;
31
31
  const targets = this[kTargets];
32
32
  this[kCompleted] = false;
33
- if (!targets.length) return this._complete(executeContent);
33
+ if (!targets.size) return this._complete(executeContent);
34
34
  for (const flow of this.activity.outbound) {
35
35
  outbound.push({
36
36
  id: flow.id,
@@ -50,9 +50,11 @@ EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage)
50
50
  consumerTag: '_api-stop-execution'
51
51
  });
52
52
  this[kCompleted] = false;
53
- if (!executeMessage.fields.redelivered) return broker.publish('execution', 'execute.outbound.take', (0, _messageHelper.cloneContent)(executeContent, {
54
- outboundTaken: true
55
- }));
53
+ if (!executeMessage.fields.redelivered) {
54
+ return broker.publish('execution', 'execute.outbound.take', (0, _messageHelper.cloneContent)(executeContent, {
55
+ outboundTaken: true
56
+ }));
57
+ }
56
58
  };
57
59
  EventBasedGatewayBehaviour.prototype._onTargetCompleted = function onTargetCompleted(executeMessage, _, message, owner) {
58
60
  const {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bpmn-elements",
3
- "version": "16.1.0",
3
+ "version": "16.2.0",
4
4
  "description": "Executable workflow elements based on BPMN 2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -5,6 +5,7 @@ import { ActivityBroker } from '../EventBroker.js';
5
5
  import { Formatter } from '../MessageFormatter.js';
6
6
  import { cloneContent, cloneParent, cloneMessage } from '../messageHelper.js';
7
7
  import { makeErrorFromMessage, ActivityError } from '../error/Errors.js';
8
+ import { OutboundEvaluator, formatFlowAction } from './outbound-evaluator.js';
8
9
 
9
10
  const kActivityDef = Symbol.for('activityDefinition');
10
11
  const kConsuming = Symbol.for('consuming');
@@ -68,16 +69,21 @@ function Activity(Behaviour, activityDef, context) {
68
69
  inboundTriggers = inboundSequenceFlows.slice();
69
70
  }
70
71
  const outboundSequenceFlows = context.getOutboundSequenceFlows(id);
72
+
73
+ const isParallelJoin = activityDef.isParallelGateway && inboundSequenceFlows.length > 1;
74
+
71
75
  const flows = (this[kFlows] = {
72
76
  inboundSequenceFlows,
73
77
  inboundAssociations,
74
- inboundJoinFlows: new Set(),
75
78
  inboundTriggers,
76
79
  outboundSequenceFlows,
77
80
  outboundEvaluator: new OutboundEvaluator(this, outboundSequenceFlows),
81
+ ...(isParallelJoin && {
82
+ inboundJoinFlows: new Set(),
83
+ inboundSourceIds: new Set(inboundSequenceFlows.map(({ sourceId }) => sourceId)),
84
+ }),
78
85
  });
79
86
 
80
- const isParallelJoin = activityDef.isParallelGateway && flows.inboundSequenceFlows.length > 1;
81
87
  this[kFlags] = {
82
88
  isEnd: flows.outboundSequenceFlows.length === 0,
83
89
  isStart: flows.inboundSequenceFlows.length === 0 && !attachedTo && !behaviour.triggeredByEvent && !isForCompensation,
@@ -502,16 +508,16 @@ Activity.prototype._onInbound = function onInbound(routingKey, message) {
502
508
 
503
509
  Activity.prototype._onJoinInbound = function onJoinInbound(routingKey, message) {
504
510
  const { content } = message;
505
- const { inboundJoinFlows, inboundTriggers } = this[kFlows];
511
+ const { inboundJoinFlows, inboundSourceIds } = this[kFlows];
506
512
  let alreadyTouched = false;
507
513
 
508
514
  const touched = new Set();
509
515
 
510
516
  let taken;
511
517
  for (const msg of inboundJoinFlows) {
512
- const flowId = msg.content.id;
513
- touched.add(flowId);
514
- if (flowId === content.id) {
518
+ const sourceId = msg.content.sourceId;
519
+ touched.add(sourceId);
520
+ if (sourceId === content.sourceId) {
515
521
  alreadyTouched = true;
516
522
  }
517
523
  }
@@ -520,7 +526,7 @@ Activity.prototype._onJoinInbound = function onJoinInbound(routingKey, message)
520
526
 
521
527
  if (alreadyTouched) return;
522
528
 
523
- const remaining = inboundTriggers.length - touched.size - 1;
529
+ const remaining = inboundSourceIds.size - touched.size - 1;
524
530
  if (remaining) {
525
531
  return this.logger.debug(`<${this.id}> inbound ${message.content.action} from <${message.content.id}>, ${remaining} remaining`);
526
532
  }
@@ -845,24 +851,44 @@ Activity.prototype._doOutbound = function doOutbound(fromMessage, isDiscarded, c
845
851
  };
846
852
 
847
853
  Activity.prototype._doRunOutbound = function doRunOutbound(outboundList, content, discardSequence) {
848
- for (const outboundFlow of outboundList) {
849
- const { id: flowId, action, result } = outboundFlow;
850
- this.broker.publish(
851
- 'run',
852
- 'run.outbound.' + action,
853
- cloneContent(content, {
854
- flow: {
855
- ...(result && typeof result === 'object' && result),
856
- ...outboundFlow,
857
- sequenceId: getUniqueId(`${flowId}_${action}`),
858
- ...(discardSequence && { discardSequence: discardSequence.slice() }),
859
- },
860
- }),
861
- );
854
+ if (outboundList.length === 1) {
855
+ this._publishRunOutbound(outboundList[0], content, discardSequence);
856
+ } else {
857
+ const targets = new Map();
858
+
859
+ for (const outboundFlow of outboundList) {
860
+ const prevTarget = targets.get(outboundFlow.targetId);
861
+ if (!prevTarget) {
862
+ targets.set(outboundFlow.targetId, outboundFlow);
863
+ } else if (outboundFlow.action === 'take' && outboundFlow.action !== prevTarget.action) {
864
+ targets.set(outboundFlow.targetId, outboundFlow);
865
+ }
866
+ }
867
+
868
+ for (const outboundFlow of targets.values()) {
869
+ this._publishRunOutbound(outboundFlow, content, discardSequence);
870
+ }
862
871
  }
872
+
863
873
  return outboundList;
864
874
  };
865
875
 
876
+ Activity.prototype._publishRunOutbound = function publishRunOutbound(outboundFlow, content, discardSequence) {
877
+ const { id: flowId, action, result } = outboundFlow;
878
+ this.broker.publish(
879
+ 'run',
880
+ 'run.outbound.' + action,
881
+ cloneContent(content, {
882
+ flow: {
883
+ ...(result && typeof result === 'object' && result),
884
+ ...outboundFlow,
885
+ sequenceId: getUniqueId(`${flowId}_${action}`),
886
+ ...(discardSequence && { discardSequence: discardSequence.slice() }),
887
+ },
888
+ }),
889
+ );
890
+ };
891
+
866
892
  Activity.prototype._onResumeMessage = function onResumeMessage(message) {
867
893
  message.ack();
868
894
 
@@ -973,128 +999,3 @@ Activity.prototype._deactivateRunConsumers = function _deactivateRunConsumers()
973
999
  broker.cancel('_activity-execution');
974
1000
  this[kConsuming] = false;
975
1001
  };
976
-
977
- function OutboundEvaluator(activity, outboundFlows) {
978
- this.activity = activity;
979
- this.broker = activity.broker;
980
- const flows = (this.outboundFlows = outboundFlows.slice());
981
- const defaultFlowIdx = flows.findIndex(({ isDefault }) => isDefault);
982
- if (defaultFlowIdx > -1) {
983
- const [defaultFlow] = flows.splice(defaultFlowIdx, 1);
984
- flows.push(defaultFlow);
985
- }
986
-
987
- this.defaultFlowIdx = outboundFlows.findIndex(({ isDefault }) => isDefault);
988
- this._onEvaluated = this.onEvaluated.bind(this);
989
- this.evaluateArgs = {};
990
- }
991
-
992
- OutboundEvaluator.prototype.evaluate = function evaluate(fromMessage, discardRestAtTake, callback) {
993
- const outboundFlows = this.outboundFlows;
994
-
995
- const args = (this.evaluateArgs = {
996
- fromMessage,
997
- evaluationId: fromMessage.content.executionId,
998
- discardRestAtTake,
999
- callback,
1000
- conditionMet: false,
1001
- result: {},
1002
- takenCount: 0,
1003
- });
1004
-
1005
- if (!outboundFlows.length) return this.completed();
1006
-
1007
- const flows = (args.flows = outboundFlows.slice());
1008
-
1009
- this.broker.subscribeTmp('execution', 'evaluate.flow.#', this._onEvaluated, {
1010
- consumerTag: `_flow-evaluation-${args.evaluationId}`,
1011
- });
1012
-
1013
- return this.evaluateFlow(flows.shift());
1014
- };
1015
-
1016
- OutboundEvaluator.prototype.onEvaluated = function onEvaluated(routingKey, message) {
1017
- const content = message.content;
1018
- const { id: flowId, action, evaluationId } = message.content;
1019
- const args = this.evaluateArgs;
1020
-
1021
- if (action === 'take') {
1022
- args.takenCount++;
1023
- args.conditionMet = true;
1024
- }
1025
-
1026
- args.result[flowId] = content;
1027
-
1028
- if ('result' in content) {
1029
- this.activity.logger.debug(`<${evaluationId} (${this.activity.id})> flow <${flowId}> evaluated to: ${!!content.result}`);
1030
- }
1031
-
1032
- let nextFlow = args.flows.shift();
1033
- if (!nextFlow) return this.completed();
1034
-
1035
- if (args.discardRestAtTake && args.conditionMet) {
1036
- do {
1037
- args.result[nextFlow.id] = formatFlowAction(nextFlow, { action: 'discard' });
1038
- } while ((nextFlow = args.flows.shift()));
1039
- return this.completed();
1040
- }
1041
-
1042
- if (args.conditionMet && nextFlow.isDefault) {
1043
- args.result[nextFlow.id] = formatFlowAction(nextFlow, { action: 'discard' });
1044
- return this.completed();
1045
- }
1046
-
1047
- message.ack();
1048
- this.evaluateFlow(nextFlow);
1049
- };
1050
-
1051
- OutboundEvaluator.prototype.evaluateFlow = function evaluateFlow(flow) {
1052
- const broker = this.broker;
1053
- const { fromMessage, evaluationId } = this.evaluateArgs;
1054
- flow.evaluate(cloneMessage(fromMessage), (err, result) => {
1055
- if (err) return this.completed(err);
1056
- const action = result ? 'take' : 'discard';
1057
- return broker.publish(
1058
- 'execution',
1059
- 'evaluate.flow.' + action,
1060
- formatFlowAction(flow, {
1061
- action,
1062
- result,
1063
- evaluationId,
1064
- }),
1065
- { persistent: false },
1066
- );
1067
- });
1068
- };
1069
-
1070
- OutboundEvaluator.prototype.completed = function completed(err) {
1071
- const { callback, evaluationId, fromMessage, result, takenCount } = this.evaluateArgs;
1072
- this.broker.cancel(`_flow-evaluation-${evaluationId}`);
1073
-
1074
- if (err) return callback(err);
1075
-
1076
- if (!takenCount && this.outboundFlows.length) {
1077
- const nonTakenError = new ActivityError(`<${this.activity.id}> no conditional flow taken`, fromMessage);
1078
- return callback(nonTakenError);
1079
- }
1080
-
1081
- const message = fromMessage.content.message;
1082
- const evaluationResult = [];
1083
- for (const flow of Object.values(result)) {
1084
- evaluationResult.push({
1085
- ...flow,
1086
- ...(message !== undefined && { message }),
1087
- });
1088
- }
1089
-
1090
- return callback(null, evaluationResult);
1091
- };
1092
-
1093
- function formatFlowAction(flow, options) {
1094
- return {
1095
- ...options,
1096
- id: flow.id,
1097
- action: options.action,
1098
- ...(flow.isDefault && { isDefault: true }),
1099
- };
1100
- }
@@ -0,0 +1,128 @@
1
+ import { ActivityError } from '../error/Errors.js';
2
+ import { cloneMessage } from '../messageHelper.js';
3
+
4
+ export function OutboundEvaluator(activity, outboundFlows) {
5
+ this.activity = activity;
6
+ this.broker = activity.broker;
7
+ const flows = (this.outboundFlows = outboundFlows.slice());
8
+ const defaultFlowIdx = flows.findIndex(({ isDefault }) => isDefault);
9
+ if (defaultFlowIdx > -1) {
10
+ const [defaultFlow] = flows.splice(defaultFlowIdx, 1);
11
+ flows.push(defaultFlow);
12
+ }
13
+
14
+ this.defaultFlowIdx = outboundFlows.findIndex(({ isDefault }) => isDefault);
15
+ this._onEvaluated = this.onEvaluated.bind(this);
16
+ this.evaluateArgs = {};
17
+ }
18
+
19
+ OutboundEvaluator.prototype.evaluate = function evaluate(fromMessage, discardRestAtTake, callback) {
20
+ const outboundFlows = this.outboundFlows;
21
+
22
+ const args = (this.evaluateArgs = {
23
+ fromMessage,
24
+ evaluationId: fromMessage.content.executionId,
25
+ discardRestAtTake,
26
+ callback,
27
+ conditionMet: false,
28
+ result: {},
29
+ takenCount: 0,
30
+ });
31
+
32
+ if (!outboundFlows.length) return this.completed();
33
+
34
+ const flows = (args.flows = outboundFlows.slice());
35
+
36
+ this.broker.subscribeTmp('execution', 'evaluate.flow.#', this._onEvaluated, {
37
+ consumerTag: `_flow-evaluation-${args.evaluationId}`,
38
+ });
39
+
40
+ return this.evaluateFlow(flows.shift());
41
+ };
42
+
43
+ OutboundEvaluator.prototype.onEvaluated = function onEvaluated(routingKey, message) {
44
+ const content = message.content;
45
+ const { id: flowId, action, evaluationId } = message.content;
46
+ const args = this.evaluateArgs;
47
+
48
+ if (action === 'take') {
49
+ args.takenCount++;
50
+ args.conditionMet = true;
51
+ }
52
+
53
+ args.result[flowId] = content;
54
+
55
+ if ('result' in content) {
56
+ this.activity.logger.debug(`<${evaluationId} (${this.activity.id})> flow <${flowId}> evaluated to: ${!!content.result}`);
57
+ }
58
+
59
+ let nextFlow = args.flows.shift();
60
+ if (!nextFlow) return this.completed();
61
+
62
+ if (args.discardRestAtTake && args.conditionMet) {
63
+ do {
64
+ args.result[nextFlow.id] = formatFlowAction(nextFlow, { action: 'discard' });
65
+ } while ((nextFlow = args.flows.shift()));
66
+ return this.completed();
67
+ }
68
+
69
+ if (args.conditionMet && nextFlow.isDefault) {
70
+ args.result[nextFlow.id] = formatFlowAction(nextFlow, { action: 'discard' });
71
+ return this.completed();
72
+ }
73
+
74
+ message.ack();
75
+ this.evaluateFlow(nextFlow);
76
+ };
77
+
78
+ OutboundEvaluator.prototype.evaluateFlow = function evaluateFlow(flow) {
79
+ const broker = this.broker;
80
+ const { fromMessage, evaluationId } = this.evaluateArgs;
81
+ flow.evaluate(cloneMessage(fromMessage), (err, result) => {
82
+ if (err) return this.completed(err);
83
+ const action = result ? 'take' : 'discard';
84
+ return broker.publish(
85
+ 'execution',
86
+ 'evaluate.flow.' + action,
87
+ formatFlowAction(flow, {
88
+ action,
89
+ result,
90
+ evaluationId,
91
+ }),
92
+ { persistent: false },
93
+ );
94
+ });
95
+ };
96
+
97
+ OutboundEvaluator.prototype.completed = function completed(err) {
98
+ const { callback, evaluationId, fromMessage, result, takenCount } = this.evaluateArgs;
99
+ this.broker.cancel(`_flow-evaluation-${evaluationId}`);
100
+
101
+ if (err) return callback(err);
102
+
103
+ if (!takenCount && this.outboundFlows.length) {
104
+ const nonTakenError = new ActivityError(`<${this.activity.id}> no conditional flow taken`, fromMessage);
105
+ return callback(nonTakenError);
106
+ }
107
+
108
+ const message = fromMessage.content.message;
109
+ const evaluationResult = [];
110
+ for (const flow of Object.values(result)) {
111
+ evaluationResult.push({
112
+ ...flow,
113
+ ...(message !== undefined && { message }),
114
+ });
115
+ }
116
+
117
+ return callback(null, evaluationResult);
118
+ };
119
+
120
+ export function formatFlowAction(flow, options) {
121
+ return {
122
+ ...options,
123
+ id: flow.id,
124
+ action: options.action,
125
+ targetId: flow.targetId,
126
+ ...(flow.isDefault && { isDefault: true }),
127
+ };
128
+ }
@@ -14,7 +14,7 @@ export function EventBasedGatewayBehaviour(activity, context) {
14
14
  this.activity = activity;
15
15
  this.broker = activity.broker;
16
16
  this.context = context;
17
- this[kTargets] = activity.outbound.map((flow) => context.getActivityById(flow.targetId));
17
+ this[kTargets] = new Set(activity.outbound.map((flow) => context.getActivityById(flow.targetId)));
18
18
  }
19
19
 
20
20
  EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage) {
@@ -23,7 +23,7 @@ EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage)
23
23
 
24
24
  const targets = this[kTargets];
25
25
  this[kCompleted] = false;
26
- if (!targets.length) return this._complete(executeContent);
26
+ if (!targets.size) return this._complete(executeContent);
27
27
 
28
28
  for (const flow of this.activity.outbound) {
29
29
  outbound.push({ id: flow.id, action: 'take' });
@@ -44,8 +44,10 @@ EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage)
44
44
  });
45
45
 
46
46
  this[kCompleted] = false;
47
- if (!executeMessage.fields.redelivered)
47
+
48
+ if (!executeMessage.fields.redelivered) {
48
49
  return broker.publish('execution', 'execute.outbound.take', cloneContent(executeContent, { outboundTaken: true }));
50
+ }
49
51
  };
50
52
 
51
53
  EventBasedGatewayBehaviour.prototype._onTargetCompleted = function onTargetCompleted(executeMessage, _, message, owner) {