bpmn-elements 9.1.3 → 10.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,17 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ # 10.0.0
5
+
6
+ - drop iso8601-duration dependency and copy source (with licence). Export as `ISODuration`. Extend with repeat pattern parsing, e.g. `R3/PT1H` that corresponds to three repetitions every one hour
7
+ - expose `TimerEventDefinition.parse(timerType, value)` function for extension purposes
8
+ - prototype and export built-in `Timers`
9
+
10
+ # 9.2.0
11
+
12
+ - move outbound sequence flow evaluation logic from activity to sequence flow, where it belongs
13
+ - spread sequence flow evaluation result, if object, to sequence flow take message
14
+
4
15
  # 9.1.3
5
16
 
6
17
  - type declare execution scope
package/README.md CHANGED
@@ -77,3 +77,7 @@ The following elements are tested and supported.
77
77
  - Transaction
78
78
 
79
79
  All activities share the same [base](/docs/Activity.md) and and [api](/docs/SharedApi.md).
80
+
81
+ # Acknowledgments
82
+
83
+ ISO 8601 duration parser [iso8601-duration](https://www.npmjs.com/package/iso8601-duration) source is copied and extended with repeat pattern. License [MIT @ tolu](https://tolu.mit-license.org/)
@@ -17,7 +17,7 @@ function Environment(options = {}) {
17
17
  this.extensions = options.extensions;
18
18
  this.output = options.output || {};
19
19
  this.scripts = options.scripts || (0, _Scripts.Scripts)();
20
- this.timers = options.timers || (0, _Timers.Timers)();
20
+ this.timers = options.timers || new _Timers.Timers();
21
21
  this.settings = {
22
22
  ...options.settings
23
23
  };
package/dist/Timers.js CHANGED
@@ -4,57 +4,77 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Timers = Timers;
7
+ const kExecuting = Symbol.for('executing');
8
+ const kTimerApi = Symbol.for('timers api');
9
+ const MAX_DELAY = 2147483647;
7
10
  function Timers(options) {
8
- let count = 0;
9
- const executing = [];
10
- options = {
11
+ this.count = 0;
12
+ this.options = {
11
13
  setTimeout,
12
14
  clearTimeout,
13
15
  ...options
14
16
  };
15
- const timersApi = {
16
- get executing() {
17
- return executing.slice();
18
- },
19
- register,
20
- setTimeout: wrappedSetTimeout,
21
- clearTimeout: wrappedClearTimeout
22
- };
23
- return timersApi;
24
- function register(owner) {
25
- return {
26
- setTimeout: registerTimeout(owner),
27
- clearTimeout: timersApi.clearTimeout
28
- };
17
+ this[kExecuting] = [];
18
+ this.setTimeout = this.setTimeout.bind(this);
19
+ this.clearTimeout = this.clearTimeout.bind(this);
20
+ }
21
+ Object.defineProperty(Timers.prototype, 'executing', {
22
+ get() {
23
+ return this[kExecuting].slice();
29
24
  }
30
- function registerTimeout(owner) {
31
- return function registeredSetTimeout(...args) {
32
- return timersApi.setTimeout.call(owner, ...args);
33
- };
25
+ });
26
+ Timers.prototype.register = function register(owner) {
27
+ return new RegisteredTimers(this, owner);
28
+ };
29
+ Timers.prototype.setTimeout = function wrappedSetTimeout(callback, delay, ...args) {
30
+ return this._setTimeout(null, callback, delay, ...args);
31
+ };
32
+ Timers.prototype.clearTimeout = function wrappedClearTimeout(ref) {
33
+ const executing = this[kExecuting];
34
+ const idx = executing.indexOf(ref);
35
+ if (idx > -1) {
36
+ executing.splice(idx, 1);
37
+ ref.timerRef = this.options.clearTimeout(ref.timerRef);
38
+ return;
34
39
  }
35
- function wrappedSetTimeout(callback, delay, ...args) {
36
- const ref = {
37
- timerId: `timer_${count++}`,
38
- callback,
39
- delay,
40
- args,
41
- owner: this
42
- };
43
- executing.push(ref);
44
- ref.timerRef = options.setTimeout.call(null, onTimeout, delay, ...args);
45
- return ref;
46
- function onTimeout(...rargs) {
47
- const idx = executing.indexOf(ref);
48
- if (idx > -1) executing.splice(idx, 1);
49
- return callback(...rargs);
50
- }
40
+ return this.options.clearTimeout(ref);
41
+ };
42
+ Timers.prototype._setTimeout = function setTimeout(owner, callback, delay, ...args) {
43
+ const executing = this[kExecuting];
44
+ const ref = this._getReference(owner, callback, delay, args);
45
+ executing.push(ref);
46
+ if (delay < MAX_DELAY) {
47
+ ref.timerRef = this.options.setTimeout(onTimeout, ref.delay, ...ref.args);
51
48
  }
52
- function wrappedClearTimeout(ref) {
49
+ return ref;
50
+ function onTimeout(...rargs) {
53
51
  const idx = executing.indexOf(ref);
54
- if (idx > -1) {
55
- executing.splice(idx, 1);
56
- return options.clearTimeout.call(null, ref.timerRef);
57
- }
58
- return options.clearTimeout.call(null, ref);
52
+ if (idx > -1) executing.splice(idx, 1);
53
+ return callback(...rargs);
59
54
  }
55
+ };
56
+ Timers.prototype._getReference = function getReference(owner, callback, delay, args) {
57
+ return new Timer(owner, `timer_${this.count++}`, callback, delay, args);
58
+ };
59
+ function RegisteredTimers(timersApi, owner) {
60
+ this[kTimerApi] = timersApi;
61
+ this.owner = owner;
62
+ this.setTimeout = this.setTimeout.bind(this);
63
+ this.clearTimeout = this.clearTimeout.bind(this);
64
+ }
65
+ RegisteredTimers.prototype.setTimeout = function registeredSetTimeout(callback, delay, ...args) {
66
+ const timersApi = this[kTimerApi];
67
+ return timersApi._setTimeout(this.owner, callback, delay, ...args);
68
+ };
69
+ RegisteredTimers.prototype.clearTimeout = function registeredClearTimeout(ref) {
70
+ this[kTimerApi].clearTimeout(ref);
71
+ };
72
+ function Timer(owner, timerId, callback, delay, args) {
73
+ this.callback = callback;
74
+ this.delay = delay;
75
+ this.args = args;
76
+ this.owner = owner;
77
+ this.timerId = timerId;
78
+ this.expireAt = new Date(Date.now() + delay);
79
+ this.timerRef = null;
60
80
  }
@@ -837,10 +837,12 @@ Activity.prototype._doRunOutbound = function doRunOutbound(outboundList, content
837
837
  for (const outboundFlow of outboundList) {
838
838
  const {
839
839
  id: flowId,
840
- action
840
+ action,
841
+ result
841
842
  } = outboundFlow;
842
843
  this.broker.publish('run', 'run.outbound.' + action, (0, _messageHelper.cloneContent)(content, {
843
844
  flow: {
845
+ ...(result && typeof result === 'object' && result),
844
846
  ...outboundFlow,
845
847
  sequenceId: (0, _shared.getUniqueId)(`${flowId}_${action}`),
846
848
  ...(discardSequence && {
@@ -1027,26 +1029,11 @@ OutboundEvaluator.prototype.onEvaluated = function onEvaluated(routingKey, messa
1027
1029
  };
1028
1030
  OutboundEvaluator.prototype.evaluateFlow = function evaluateFlow(flow) {
1029
1031
  const broker = this.broker;
1030
- if (flow.isDefault) {
1031
- return broker.publish('execution', 'evaluate.flow.take', formatFlowAction(flow, {
1032
- action: 'take'
1033
- }), {
1034
- persistent: false
1035
- });
1036
- }
1037
- const flowCondition = flow.getCondition();
1038
- if (!flowCondition) {
1039
- return broker.publish('execution', 'evaluate.flow.take', formatFlowAction(flow, {
1040
- action: 'take'
1041
- }), {
1042
- persistent: false
1043
- });
1044
- }
1045
1032
  const {
1046
1033
  fromMessage,
1047
1034
  evaluationId
1048
1035
  } = this.evaluateArgs;
1049
- flowCondition.execute((0, _messageHelper.cloneMessage)(fromMessage), (err, result) => {
1036
+ flow.evaluate((0, _messageHelper.cloneMessage)(fromMessage), (err, result) => {
1050
1037
  if (err) return this.completed(err);
1051
1038
  const action = result ? 'take' : 'discard';
1052
1039
  return broker.publish('execution', 'evaluate.flow.' + action, formatFlowAction(flow, {
@@ -5,13 +5,12 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = TimerEventDefinition;
7
7
  var _messageHelper = require("../messageHelper.js");
8
- var _iso8601Duration = require("iso8601-duration");
8
+ var _isoDuration = require("../iso-duration.js");
9
9
  const kStopped = Symbol.for('stopped');
10
10
  const kTimerContent = Symbol.for('timerContent');
11
11
  const kTimer = Symbol.for('timer');
12
- const repeatPattern = /^\s*R(\d+)\//;
13
12
  function TimerEventDefinition(activity, eventDefinition) {
14
- const type = this.type = eventDefinition.type || 'TimerEventDefinition.js';
13
+ const type = this.type = eventDefinition.type || 'TimerEventDefinition';
15
14
  this.activity = activity;
16
15
  const environment = this.environment = activity.environment;
17
16
  this.eventDefinition = eventDefinition;
@@ -85,12 +84,14 @@ TimerEventDefinition.prototype.execute = function execute(executeMessage) {
85
84
  if (timerContent.timeout === undefined) return this._debug(`waiting for ${timerContent.timerType || 'signal'}`);
86
85
  if (timerContent.timeout <= 0) return this._completed();
87
86
  const timers = this.environment.timers.register(timerContent);
88
- this[kTimer] = timers.setTimeout(this._completed.bind(this), timerContent.timeout, {
87
+ const delay = timerContent.timeout;
88
+ this[kTimer] = timers.setTimeout(this._completed.bind(this), delay, {
89
89
  id: content.id,
90
90
  type: this.type,
91
91
  executionId,
92
92
  state: 'timeout'
93
93
  });
94
+ this._debug(`set timeout with delay ${delay}`);
94
95
  };
95
96
  TimerEventDefinition.prototype.stop = function stopTimer() {
96
97
  const timer = this[kTimer];
@@ -188,75 +189,84 @@ TimerEventDefinition.prototype._stop = function stop() {
188
189
  broker.cancel(`_api-${this.executionId}`);
189
190
  broker.cancel(`_api-delegated-${this.executionId}`);
190
191
  };
192
+ TimerEventDefinition.prototype.parse = function parse(timerType, value) {
193
+ let repeat, delay, expireAt;
194
+ switch (timerType) {
195
+ case 'timeCycle':
196
+ case 'timeDuration':
197
+ {
198
+ const parsed = (0, _isoDuration.parse)(value);
199
+ if (parsed.repeat) repeat = parsed.repeat;
200
+ delay = (0, _isoDuration.toSeconds)(parsed) * 1000;
201
+ expireAt = new Date(Date.now() + delay);
202
+ break;
203
+ }
204
+ case 'timeDate':
205
+ {
206
+ const ms = Date.parse(value);
207
+ if (!isNaN(ms)) {
208
+ expireAt = new Date(ms);
209
+ delay = Date.now() - expireAt;
210
+ } else {
211
+ throw new TypeError(`invalid timeDate >${value}<`);
212
+ }
213
+ break;
214
+ }
215
+ }
216
+ return {
217
+ expireAt,
218
+ repeat,
219
+ delay
220
+ };
221
+ };
191
222
  TimerEventDefinition.prototype._getTimers = function getTimers(executeMessage) {
192
223
  const content = executeMessage.content;
193
- const now = Date.now();
194
224
  const result = {
195
225
  ...('expireAt' in content && {
196
226
  expireAt: new Date(content.expireAt)
197
227
  })
198
228
  };
229
+ let parseErr;
199
230
  for (const t of ['timeDuration', 'timeDate', 'timeCycle']) {
200
231
  if (t in content) result[t] = content[t];else if (t in this) result[t] = this.environment.resolveExpression(this[t], executeMessage);else continue;
201
232
  let expireAtDate, repeat;
202
233
  const timerStr = result[t];
203
234
  if (timerStr) {
204
- switch (t) {
205
- case 'timeCycle':
206
- {
207
- const mRepeat = timerStr.match(repeatPattern);
208
- if (mRepeat && mRepeat.length) repeat = parseInt(mRepeat[1]);
209
- }
210
- case 'timeDuration':
211
- {
212
- const delay = this._getDurationInMilliseconds(timerStr);
213
- if (delay !== undefined) expireAtDate = new Date(now + delay);
214
- break;
215
- }
216
- case 'timeDate':
217
- {
218
- const dateStr = result[t];
219
- const ms = Date.parse(dateStr);
220
- if (!isNaN(ms)) {
221
- expireAtDate = new Date(ms);
222
- } else {
223
- this._warn(`invalid timeDate >${dateStr}<`);
224
- }
225
- break;
226
- }
235
+ try {
236
+ const {
237
+ repeat: parsedRepeat,
238
+ expireAt: parsedExpireAt
239
+ } = this.parse(t, timerStr);
240
+ repeat = parsedRepeat;
241
+ expireAtDate = parsedExpireAt;
242
+ } catch (err) {
243
+ parseErr = err;
227
244
  }
228
245
  } else {
229
- expireAtDate = new Date(now);
246
+ expireAtDate = new Date();
230
247
  }
231
248
  if (!expireAtDate) continue;
232
249
  if (!('expireAt' in result) || result.expireAt > expireAtDate) {
233
250
  result.timerType = t;
234
251
  result.expireAt = expireAtDate;
235
- if (repeat) result.repeat = repeat;
252
+ result.repeat = repeat;
236
253
  }
237
254
  }
238
255
  if ('expireAt' in result) {
239
- result.timeout = result.expireAt - now;
256
+ result.timeout = result.expireAt - Date.now();
240
257
  } else if ('timeout' in content) {
241
258
  result.timeout = content.timeout;
242
259
  } else if (!Object.keys(result).length) {
243
260
  result.timeout = 0;
244
261
  }
262
+ if (!('timeout' in result) && parseErr) {
263
+ this.logger.warn(`<${this.activity.id}> failed to parse timer: ${parseErr.message}`);
264
+ }
245
265
  if (content.inbound && 'repeat' in content.inbound[0]) {
246
266
  result.repeat = content.inbound[0].repeat;
247
267
  }
248
268
  return result;
249
269
  };
250
- TimerEventDefinition.prototype._getDurationInMilliseconds = function getDurationInMilliseconds(duration) {
251
- try {
252
- return (0, _iso8601Duration.toSeconds)((0, _iso8601Duration.parse)(duration)) * 1000;
253
- } catch (err) {
254
- this._warn(`failed to parse ${this.timerType} >${duration}<: ${err.message}`);
255
- }
256
- };
257
270
  TimerEventDefinition.prototype._debug = function debug(msg) {
258
271
  this.logger.debug(`<${this.executionId} (${this.activity.id})> ${msg}`);
259
- };
260
- TimerEventDefinition.prototype._warn = function debug(msg) {
261
- this.logger.warn(`<${this.executionId} (${this.activity.id})> ${msg}`);
262
272
  };
@@ -124,7 +124,7 @@ SequenceFlow.prototype.shake = function shake(message) {
124
124
  persistent: false,
125
125
  type: 'shake'
126
126
  });
127
- for (const s of message.content.sequence) {
127
+ for (const s of message.content.sequence || []) {
128
128
  if (s.id === this.id) return this.broker.publish('event', 'flow.shake.loop', content, {
129
129
  persistent: false,
130
130
  type: 'shake'
@@ -164,6 +164,16 @@ SequenceFlow.prototype.createMessage = function createMessage(override) {
164
164
  parent: (0, _messageHelper.cloneParent)(this.parent)
165
165
  };
166
166
  };
167
+ SequenceFlow.prototype.evaluate = function evaluate(fromMessage, callback) {
168
+ if (this.isDefault) {
169
+ return callback(null, true);
170
+ }
171
+ const flowCondition = this.getCondition();
172
+ if (!flowCondition) {
173
+ return callback(null, true);
174
+ }
175
+ flowCondition.execute(fromMessage, callback);
176
+ };
167
177
  SequenceFlow.prototype._publishEvent = function publishEvent(action, content) {
168
178
  const eventContent = this.createMessage({
169
179
  action,
package/dist/index.js CHANGED
@@ -147,6 +147,7 @@ Object.defineProperty(exports, "Group", {
147
147
  return _Dummy.default;
148
148
  }
149
149
  });
150
+ exports.ISODuration = void 0;
150
151
  Object.defineProperty(exports, "InclusiveGateway", {
151
152
  enumerable: true,
152
153
  get: function () {
@@ -321,6 +322,12 @@ Object.defineProperty(exports, "TimerEventDefinition", {
321
322
  return _TimerEventDefinition.default;
322
323
  }
323
324
  });
325
+ Object.defineProperty(exports, "Timers", {
326
+ enumerable: true,
327
+ get: function () {
328
+ return _Timers.Timers;
329
+ }
330
+ });
324
331
  Object.defineProperty(exports, "Transaction", {
325
332
  enumerable: true,
326
333
  get: function () {
@@ -381,4 +388,9 @@ var _Task = _interopRequireDefault(require("./tasks/Task.js"));
381
388
  var _TerminateEventDefinition = _interopRequireDefault(require("./eventDefinitions/TerminateEventDefinition.js"));
382
389
  var _TimerEventDefinition = _interopRequireDefault(require("./eventDefinitions/TimerEventDefinition.js"));
383
390
  var _Transaction = _interopRequireDefault(require("./tasks/Transaction.js"));
391
+ var _Timers = require("./Timers.js");
392
+ var ISODuration = _interopRequireWildcard(require("./iso-duration.js"));
393
+ exports.ISODuration = ISODuration;
394
+ 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); }
395
+ 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; }
384
396
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.end = end;
7
+ exports.parse = parse;
8
+ exports.toSeconds = toSeconds;
9
+ // License MIT @ https://tolu.mit-license.org/
10
+
11
+ const numbers = '\\d+';
12
+ const fractionalNumbers = ''.concat(numbers, '(?:[\\.,]').concat(numbers, ')?');
13
+ const datePattern = '('.concat(numbers, 'Y)?(').concat(numbers, 'M)?(').concat(numbers, 'W)?(').concat(fractionalNumbers, 'D)?');
14
+ const timePattern = 'T('.concat(fractionalNumbers, 'H)?(').concat(fractionalNumbers, 'M)?(').concat(fractionalNumbers, 'S)?');
15
+ const rPattern = '(?:R('.concat(numbers).concat(')/)?');
16
+ const iso8601 = rPattern.concat('P(?:').concat(datePattern, '(?:').concat(timePattern, ')?)');
17
+ const objMap = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'];
18
+ const defaultDuration = Object.freeze({
19
+ years: 0,
20
+ months: 0,
21
+ weeks: 0,
22
+ days: 0,
23
+ hours: 0,
24
+ minutes: 0,
25
+ seconds: 0
26
+ });
27
+
28
+ /**
29
+ * The ISO8601 regex for matching / testing durations
30
+ */
31
+ const pattern = new RegExp(iso8601);
32
+
33
+ /** Parse PnYnMnDTnHnMnS format to object */
34
+ function parse(durationString) {
35
+ const matches = durationString.replace(/,/g, '.').match(pattern);
36
+ if (!matches) {
37
+ throw new RangeError('invalid duration: ' + durationString);
38
+ }
39
+
40
+ // Slice away repeat and first entry in match-array (the input string)
41
+ const slicedMatches = matches.slice(2);
42
+ if (slicedMatches.filter(Boolean).length === 0) {
43
+ throw new RangeError('invalid duration: ' + durationString);
44
+ }
45
+ // Check only one fraction is used
46
+ if (slicedMatches.filter(v => {
47
+ return /\./.test(v || '');
48
+ }).length > 1) {
49
+ throw new RangeError('Fractions are allowed on the smallest unit in the string, e.g. P0.5D or PT1.0001S but not PT0.5M0.1S: ' + durationString);
50
+ }
51
+ const result = {};
52
+ if (matches[1]) result.repeat = Number(matches[1]);
53
+ return slicedMatches.reduce((prev, next, idx) => {
54
+ prev[objMap[idx]] = parseFloat(next || '0') || 0;
55
+ return prev;
56
+ }, result);
57
+ }
58
+
59
+ /** Convert ISO8601 duration object to an end Date. */
60
+ function end(durationInput, startDate) {
61
+ const duration = Object.assign({}, defaultDuration, durationInput);
62
+ // Create two equal timestamps, add duration to 'then' and return time difference
63
+ const timestamp = startDate.getTime();
64
+ const then = new Date(timestamp);
65
+ then.setFullYear(then.getFullYear() + duration.years);
66
+ then.setMonth(then.getMonth() + duration.months);
67
+ then.setDate(then.getDate() + duration.days);
68
+ // set time as milliseconds to get fractions working for minutes/hours
69
+ const hoursInMs = duration.hours * 3600 * 1000;
70
+ const minutesInMs = duration.minutes * 60 * 1000;
71
+ then.setMilliseconds(then.getMilliseconds() + duration.seconds * 1000 + hoursInMs + minutesInMs);
72
+ // Special case weeks
73
+ then.setDate(then.getDate() + duration.weeks * 7);
74
+ return then;
75
+ }
76
+
77
+ /** Convert ISO8601 duration object to seconds */
78
+ function toSeconds(durationInput, startDate) {
79
+ if (startDate === void 0) {
80
+ startDate = new Date();
81
+ }
82
+ const duration = Object.assign({}, defaultDuration, durationInput);
83
+ const timestamp = startDate.getTime();
84
+ const now = new Date(timestamp);
85
+ const then = end(duration, now);
86
+ const seconds = (then.getTime() - now.getTime()) / 1000;
87
+ return seconds;
88
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bpmn-elements",
3
- "version": "9.1.3",
3
+ "version": "10.0.0",
4
4
  "description": "Executable workflow elements based on BPMN 2.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -45,9 +45,9 @@
45
45
  ],
46
46
  "devDependencies": {
47
47
  "@aircall/expression-parser": "^1.0.4",
48
- "@babel/cli": "^7.21.0",
49
- "@babel/core": "^7.21.4",
50
- "@babel/preset-env": "^7.21.4",
48
+ "@babel/cli": "^7.21.5",
49
+ "@babel/core": "^7.21.8",
50
+ "@babel/preset-env": "^7.21.5",
51
51
  "@babel/register": "^7.21.0",
52
52
  "bpmn-moddle": "^8.0.1",
53
53
  "c8": "^7.13.0",
@@ -55,16 +55,15 @@
55
55
  "chai": "^4.3.7",
56
56
  "chronokinesis": "^5.0.2",
57
57
  "debug": "^4.3.4",
58
- "eslint": "^8.38.0",
58
+ "eslint": "^8.41.0",
59
59
  "eslint-plugin-import": "^2.27.5",
60
60
  "got": "^12.6.0",
61
61
  "mocha": "^10.1.0",
62
62
  "mocha-cakes-2": "^3.3.0",
63
63
  "moddle-context-serializer": "^3.2.2",
64
- "nock": "^13.2.8"
64
+ "nock": "^13.3.1"
65
65
  },
66
66
  "dependencies": {
67
- "iso8601-duration": "^2.1.1",
68
67
  "smqp": "^7.1.4"
69
68
  }
70
69
  }
@@ -24,7 +24,7 @@ export default function Environment(options = {}) {
24
24
  this.extensions = options.extensions;
25
25
  this.output = options.output || {};
26
26
  this.scripts = options.scripts || IScripts();
27
- this.timers = options.timers || Timers();
27
+ this.timers = options.timers || new Timers();
28
28
  this.settings = {...options.settings};
29
29
  this.Logger = options.Logger || DummyLogger;
30
30
  this[kServices] = options.services || {};
package/src/Timers.js CHANGED
@@ -1,56 +1,87 @@
1
- export function Timers(options) {
2
- let count = 0;
3
- const executing = [];
1
+ const kExecuting = Symbol.for('executing');
2
+ const kTimerApi = Symbol.for('timers api');
3
+
4
+ const MAX_DELAY = 2147483647;
4
5
 
5
- options = {
6
+ export function Timers(options) {
7
+ this.count = 0;
8
+ this.options = {
6
9
  setTimeout,
7
10
  clearTimeout,
8
11
  ...options,
9
12
  };
13
+ this[kExecuting] = [];
14
+ this.setTimeout = this.setTimeout.bind(this);
15
+ this.clearTimeout = this.clearTimeout.bind(this);
16
+ }
10
17
 
11
- const timersApi = {
12
- get executing() {
13
- return executing.slice();
14
- },
15
- register,
16
- setTimeout: wrappedSetTimeout,
17
- clearTimeout: wrappedClearTimeout,
18
- };
18
+ Object.defineProperty(Timers.prototype, 'executing', {
19
+ get() {
20
+ return this[kExecuting].slice();
21
+ },
22
+ });
19
23
 
20
- return timersApi;
24
+ Timers.prototype.register = function register(owner) {
25
+ return new RegisteredTimers(this, owner);
26
+ };
21
27
 
22
- function register(owner) {
23
- return {
24
- setTimeout: registerTimeout(owner),
25
- clearTimeout: timersApi.clearTimeout,
26
- };
27
- }
28
+ Timers.prototype.setTimeout = function wrappedSetTimeout(callback, delay, ...args) {
29
+ return this._setTimeout(null, callback, delay, ...args);
30
+ };
28
31
 
29
- function registerTimeout(owner) {
30
- return function registeredSetTimeout(...args) {
31
- return timersApi.setTimeout.call(owner, ...args);
32
- };
32
+ Timers.prototype.clearTimeout = function wrappedClearTimeout(ref) {
33
+ const executing = this[kExecuting];
34
+ const idx = executing.indexOf(ref);
35
+ if (idx > -1) {
36
+ executing.splice(idx, 1);
37
+ ref.timerRef = this.options.clearTimeout(ref.timerRef);
38
+ return;
33
39
  }
40
+ return this.options.clearTimeout(ref);
41
+ };
34
42
 
35
- function wrappedSetTimeout(callback, delay, ...args) {
36
- const ref = {timerId: `timer_${count++}`, callback, delay, args, owner: this};
37
- executing.push(ref);
38
- ref.timerRef = options.setTimeout.call(null, onTimeout, delay, ...args);
39
- return ref;
40
-
41
- function onTimeout(...rargs) {
42
- const idx = executing.indexOf(ref);
43
- if (idx > -1) executing.splice(idx, 1);
44
- return callback(...rargs);
45
- }
43
+ Timers.prototype._setTimeout = function setTimeout(owner, callback, delay, ...args) {
44
+ const executing = this[kExecuting];
45
+ const ref = this._getReference(owner, callback, delay, args);
46
+ executing.push(ref);
47
+ if (delay < MAX_DELAY) {
48
+ ref.timerRef = this.options.setTimeout(onTimeout, ref.delay, ...ref.args);
46
49
  }
50
+ return ref;
47
51
 
48
- function wrappedClearTimeout(ref) {
52
+ function onTimeout(...rargs) {
49
53
  const idx = executing.indexOf(ref);
50
- if (idx > -1) {
51
- executing.splice(idx, 1);
52
- return options.clearTimeout.call(null, ref.timerRef);
53
- }
54
- return options.clearTimeout.call(null, ref);
54
+ if (idx > -1) executing.splice(idx, 1);
55
+ return callback(...rargs);
55
56
  }
57
+ };
58
+
59
+ Timers.prototype._getReference = function getReference(owner, callback, delay, args) {
60
+ return new Timer(owner, `timer_${this.count++}`, callback, delay, args);
61
+ };
62
+
63
+ function RegisteredTimers(timersApi, owner) {
64
+ this[kTimerApi] = timersApi;
65
+ this.owner = owner;
66
+ this.setTimeout = this.setTimeout.bind(this);
67
+ this.clearTimeout = this.clearTimeout.bind(this);
68
+ }
69
+
70
+ RegisteredTimers.prototype.setTimeout = function registeredSetTimeout(callback, delay, ...args) {
71
+ const timersApi = this[kTimerApi];
72
+ return timersApi._setTimeout(this.owner, callback, delay, ...args);
73
+ };
74
+
75
+ RegisteredTimers.prototype.clearTimeout = function registeredClearTimeout(ref) {
76
+ this[kTimerApi].clearTimeout(ref);
77
+ };
78
+
79
+ function Timer(owner, timerId, callback, delay, args) {
80
+ this.callback = callback;
81
+ this.delay = delay;
82
+ this.args = args;
83
+ this.owner = owner;
84
+ this.timerId = timerId;
85
+ this.expireAt = new Date(Date.now() + delay);
86
+ this.timerRef = null;
56
87
  }
@@ -807,9 +807,10 @@ Activity.prototype._doOutbound = function doOutbound(fromMessage, isDiscarded, c
807
807
 
808
808
  Activity.prototype._doRunOutbound = function doRunOutbound(outboundList, content, discardSequence) {
809
809
  for (const outboundFlow of outboundList) {
810
- const {id: flowId, action} = outboundFlow;
810
+ const {id: flowId, action, result} = outboundFlow;
811
811
  this.broker.publish('run', 'run.outbound.' + action, cloneContent(content, {
812
812
  flow: {
813
+ ...(result && typeof result === 'object' && result),
813
814
  ...outboundFlow,
814
815
  sequenceId: getUniqueId(`${flowId}_${action}`),
815
816
  ...(discardSequence && {discardSequence: discardSequence.slice()}),
@@ -1000,17 +1001,8 @@ OutboundEvaluator.prototype.onEvaluated = function onEvaluated(routingKey, messa
1000
1001
 
1001
1002
  OutboundEvaluator.prototype.evaluateFlow = function evaluateFlow(flow) {
1002
1003
  const broker = this.broker;
1003
- if (flow.isDefault) {
1004
- return broker.publish('execution', 'evaluate.flow.take', formatFlowAction(flow, {action: 'take'}), {persistent: false});
1005
- }
1006
-
1007
- const flowCondition = flow.getCondition();
1008
- if (!flowCondition) {
1009
- return broker.publish('execution', 'evaluate.flow.take', formatFlowAction(flow, {action: 'take'}), {persistent: false});
1010
- }
1011
-
1012
1004
  const {fromMessage, evaluationId} = this.evaluateArgs;
1013
- flowCondition.execute(cloneMessage(fromMessage), (err, result) => {
1005
+ flow.evaluate(cloneMessage(fromMessage), (err, result) => {
1014
1006
  if (err) return this.completed(err);
1015
1007
  const action = result ? 'take' : 'discard';
1016
1008
  return broker.publish('execution', 'evaluate.flow.' + action, formatFlowAction(flow, {
@@ -1,13 +1,12 @@
1
1
  import {cloneContent} from '../messageHelper.js';
2
- import {toSeconds, parse} from 'iso8601-duration';
2
+ import {toSeconds, parse as parseIsoDuration} from '../iso-duration.js';
3
3
 
4
4
  const kStopped = Symbol.for('stopped');
5
5
  const kTimerContent = Symbol.for('timerContent');
6
6
  const kTimer = Symbol.for('timer');
7
- const repeatPattern = /^\s*R(\d+)\//;
8
7
 
9
8
  export default function TimerEventDefinition(activity, eventDefinition) {
10
- const type = this.type = eventDefinition.type || 'TimerEventDefinition.js';
9
+ const type = this.type = eventDefinition.type || 'TimerEventDefinition';
11
10
  this.activity = activity;
12
11
  const environment = this.environment = activity.environment;
13
12
  this.eventDefinition = eventDefinition;
@@ -85,12 +84,14 @@ TimerEventDefinition.prototype.execute = function execute(executeMessage) {
85
84
  if (timerContent.timeout <= 0) return this._completed();
86
85
 
87
86
  const timers = this.environment.timers.register(timerContent);
88
- this[kTimer] = timers.setTimeout(this._completed.bind(this), timerContent.timeout, {
87
+ const delay = timerContent.timeout;
88
+ this[kTimer] = timers.setTimeout(this._completed.bind(this), delay, {
89
89
  id: content.id,
90
90
  type: this.type,
91
91
  executionId,
92
92
  state: 'timeout',
93
93
  });
94
+ this._debug(`set timeout with delay ${delay}`);
94
95
  };
95
96
 
96
97
  TimerEventDefinition.prototype.stop = function stopTimer() {
@@ -175,14 +176,44 @@ TimerEventDefinition.prototype._stop = function stop() {
175
176
  broker.cancel(`_api-delegated-${this.executionId}`);
176
177
  };
177
178
 
179
+ TimerEventDefinition.prototype.parse = function parse(timerType, value) {
180
+ let repeat, delay, expireAt;
181
+ switch (timerType) {
182
+ case 'timeCycle':
183
+ case 'timeDuration': {
184
+ const parsed = parseIsoDuration(value);
185
+ if (parsed.repeat) repeat = parsed.repeat;
186
+ delay = toSeconds(parsed) * 1000;
187
+ expireAt = new Date(Date.now() + delay);
188
+ break;
189
+ }
190
+ case 'timeDate': {
191
+ const ms = Date.parse(value);
192
+ if (!isNaN(ms)) {
193
+ expireAt = new Date(ms);
194
+ delay = Date.now() - expireAt;
195
+ } else {
196
+ throw new TypeError(`invalid timeDate >${value}<`);
197
+ }
198
+ break;
199
+ }
200
+ }
201
+
202
+ return {
203
+ expireAt,
204
+ repeat,
205
+ delay,
206
+ };
207
+ };
208
+
178
209
  TimerEventDefinition.prototype._getTimers = function getTimers(executeMessage) {
179
210
  const content = executeMessage.content;
180
211
 
181
- const now = Date.now();
182
212
  const result = {
183
213
  ...('expireAt' in content && {expireAt: new Date(content.expireAt)}),
184
214
  };
185
215
 
216
+ let parseErr;
186
217
  for (const t of ['timeDuration', 'timeDate', 'timeCycle']) {
187
218
  if (t in content) result[t] = content[t];
188
219
  else if (t in this) result[t] = this.environment.resolveExpression(this[t], executeMessage);
@@ -191,47 +222,37 @@ TimerEventDefinition.prototype._getTimers = function getTimers(executeMessage) {
191
222
  let expireAtDate, repeat;
192
223
  const timerStr = result[t];
193
224
  if (timerStr) {
194
- switch (t) {
195
- case 'timeCycle': {
196
- const mRepeat = timerStr.match(repeatPattern);
197
- if (mRepeat && mRepeat.length) repeat = parseInt(mRepeat[1]);
198
- }
199
- case 'timeDuration': {
200
- const delay = this._getDurationInMilliseconds(timerStr);
201
- if (delay !== undefined) expireAtDate = new Date(now + delay);
202
- break;
203
- }
204
- case 'timeDate': {
205
- const dateStr = result[t];
206
- const ms = Date.parse(dateStr);
207
- if (!isNaN(ms)) {
208
- expireAtDate = new Date(ms);
209
- } else {
210
- this._warn(`invalid timeDate >${dateStr}<`);
211
- }
212
- break;
213
- }
225
+ try {
226
+ const {repeat: parsedRepeat, expireAt: parsedExpireAt} = this.parse(t, timerStr);
227
+ repeat = parsedRepeat;
228
+ expireAtDate = parsedExpireAt;
229
+ } catch (err) {
230
+ parseErr = err;
214
231
  }
215
232
  } else {
216
- expireAtDate = new Date(now);
233
+ expireAtDate = new Date();
217
234
  }
218
235
 
219
236
  if (!expireAtDate) continue;
220
237
  if (!('expireAt' in result) || result.expireAt > expireAtDate) {
221
238
  result.timerType = t;
222
239
  result.expireAt = expireAtDate;
223
- if (repeat) result.repeat = repeat;
240
+ result.repeat = repeat;
224
241
  }
225
242
  }
226
243
 
227
244
  if ('expireAt' in result) {
228
- result.timeout = result.expireAt - now;
245
+ result.timeout = result.expireAt - Date.now();
229
246
  } else if ('timeout' in content) {
230
247
  result.timeout = content.timeout;
231
248
  } else if (!Object.keys(result).length) {
232
249
  result.timeout = 0;
233
250
  }
234
251
 
252
+ if (!('timeout' in result) && parseErr) {
253
+ this.logger.warn(`<${this.activity.id}> failed to parse timer: ${parseErr.message}`);
254
+ }
255
+
235
256
  if (content.inbound && 'repeat' in content.inbound[0]) {
236
257
  result.repeat = content.inbound[0].repeat;
237
258
  }
@@ -239,18 +260,6 @@ TimerEventDefinition.prototype._getTimers = function getTimers(executeMessage) {
239
260
  return result;
240
261
  };
241
262
 
242
- TimerEventDefinition.prototype._getDurationInMilliseconds = function getDurationInMilliseconds(duration) {
243
- try {
244
- return toSeconds(parse(duration)) * 1000;
245
- } catch (err) {
246
- this._warn(`failed to parse ${this.timerType} >${duration}<: ${err.message}`);
247
- }
248
- };
249
-
250
263
  TimerEventDefinition.prototype._debug = function debug(msg) {
251
264
  this.logger.debug(`<${this.executionId} (${this.activity.id})> ${msg}`);
252
265
  };
253
-
254
- TimerEventDefinition.prototype._warn = function debug(msg) {
255
- this.logger.warn(`<${this.executionId} (${this.activity.id})> ${msg}`);
256
- };
@@ -101,7 +101,7 @@ SequenceFlow.prototype.shake = function shake(message) {
101
101
 
102
102
  if (content.id === this.targetId) return this.broker.publish('event', 'flow.shake.loop', content, {persistent: false, type: 'shake'});
103
103
 
104
- for (const s of message.content.sequence) {
104
+ for (const s of message.content.sequence || []) {
105
105
  if (s.id === this.id) return this.broker.publish('event', 'flow.shake.loop', content, {persistent: false, type: 'shake'});
106
106
  }
107
107
 
@@ -140,6 +140,19 @@ SequenceFlow.prototype.createMessage = function createMessage(override) {
140
140
  };
141
141
  };
142
142
 
143
+ SequenceFlow.prototype.evaluate = function evaluate(fromMessage, callback) {
144
+ if (this.isDefault) {
145
+ return callback(null, true);
146
+ }
147
+
148
+ const flowCondition = this.getCondition();
149
+ if (!flowCondition) {
150
+ return callback(null, true);
151
+ }
152
+
153
+ flowCondition.execute(fromMessage, callback);
154
+ };
155
+
143
156
  SequenceFlow.prototype._publishEvent = function publishEvent(action, content) {
144
157
  const eventContent = this.createMessage({
145
158
  action,
package/src/index.js CHANGED
@@ -46,6 +46,8 @@ import Task from './tasks/Task.js';
46
46
  import TerminateEventDefinition from './eventDefinitions/TerminateEventDefinition.js';
47
47
  import TimerEventDefinition from './eventDefinitions/TimerEventDefinition.js';
48
48
  import Transaction from './tasks/Transaction.js';
49
+ import {Timers} from './Timers.js';
50
+ import * as ISODuration from './iso-duration.js';
49
51
 
50
52
  export {
51
53
  Association,
@@ -103,4 +105,6 @@ export {
103
105
  TerminateEventDefinition,
104
106
  TimerEventDefinition,
105
107
  Transaction,
108
+ Timers,
109
+ ISODuration,
106
110
  };
@@ -0,0 +1,91 @@
1
+ // License MIT @ https://tolu.mit-license.org/
2
+
3
+ const numbers = '\\d+';
4
+ const fractionalNumbers = ''.concat(numbers, '(?:[\\.,]').concat(numbers, ')?');
5
+ const datePattern = '('.concat(numbers, 'Y)?(').concat(numbers, 'M)?(').concat(numbers, 'W)?(').concat(fractionalNumbers, 'D)?');
6
+ const timePattern = 'T('.concat(fractionalNumbers, 'H)?(').concat(fractionalNumbers, 'M)?(').concat(fractionalNumbers, 'S)?');
7
+
8
+ const rPattern = '(?:R('.concat(numbers).concat(')/)?');
9
+ const iso8601 = rPattern.concat('P(?:').concat(datePattern, '(?:').concat(timePattern, ')?)');
10
+ const objMap = [
11
+ 'years',
12
+ 'months',
13
+ 'weeks',
14
+ 'days',
15
+ 'hours',
16
+ 'minutes',
17
+ 'seconds',
18
+ ];
19
+ const defaultDuration = Object.freeze({
20
+ years: 0,
21
+ months: 0,
22
+ weeks: 0,
23
+ days: 0,
24
+ hours: 0,
25
+ minutes: 0,
26
+ seconds: 0,
27
+ });
28
+
29
+ /**
30
+ * The ISO8601 regex for matching / testing durations
31
+ */
32
+ const pattern = new RegExp(iso8601);
33
+
34
+ /** Parse PnYnMnDTnHnMnS format to object */
35
+ export function parse(durationString) {
36
+ const matches = durationString.replace(/,/g, '.').match(pattern);
37
+ if (!matches) {
38
+ throw new RangeError('invalid duration: ' + durationString);
39
+ }
40
+
41
+ // Slice away repeat and first entry in match-array (the input string)
42
+ const slicedMatches = matches.slice(2);
43
+ if (slicedMatches.filter(Boolean).length === 0) {
44
+ throw new RangeError('invalid duration: ' + durationString);
45
+ }
46
+ // Check only one fraction is used
47
+ if (slicedMatches.filter((v) => {
48
+ return /\./.test(v || '');
49
+ }).length > 1) {
50
+ throw new RangeError('Fractions are allowed on the smallest unit in the string, e.g. P0.5D or PT1.0001S but not PT0.5M0.1S: ' + durationString);
51
+ }
52
+
53
+ const result = {};
54
+ if (matches[1]) result.repeat = Number(matches[1]);
55
+
56
+ return slicedMatches.reduce((prev, next, idx) => {
57
+ prev[objMap[idx]] = parseFloat(next || '0') || 0;
58
+ return prev;
59
+ }, result);
60
+ }
61
+
62
+ /** Convert ISO8601 duration object to an end Date. */
63
+ export function end(durationInput, startDate) {
64
+ const duration = Object.assign({}, defaultDuration, durationInput);
65
+ // Create two equal timestamps, add duration to 'then' and return time difference
66
+ const timestamp = startDate.getTime();
67
+ const then = new Date(timestamp);
68
+ then.setFullYear(then.getFullYear() + duration.years);
69
+ then.setMonth(then.getMonth() + duration.months);
70
+ then.setDate(then.getDate() + duration.days);
71
+ // set time as milliseconds to get fractions working for minutes/hours
72
+ const hoursInMs = duration.hours * 3600 * 1000;
73
+ const minutesInMs = duration.minutes * 60 * 1000;
74
+ then.setMilliseconds(then.getMilliseconds() + duration.seconds * 1000 + hoursInMs + minutesInMs);
75
+ // Special case weeks
76
+ then.setDate(then.getDate() + duration.weeks * 7);
77
+ return then;
78
+ }
79
+
80
+ /** Convert ISO8601 duration object to seconds */
81
+ export function toSeconds(durationInput, startDate) {
82
+ if (startDate === void 0) {
83
+ startDate = new Date();
84
+ }
85
+ const duration = Object.assign({}, defaultDuration, durationInput);
86
+ const timestamp = startDate.getTime();
87
+ const now = new Date(timestamp);
88
+ const then = end(duration, now);
89
+ const seconds = (then.getTime() - now.getTime()) / 1000;
90
+ return seconds;
91
+ }
package/types/index.d.ts CHANGED
@@ -37,7 +37,8 @@ declare module 'bpmn-elements' {
37
37
  content: ElementMessageContent,
38
38
  }
39
39
 
40
- interface EventDefinition {
40
+ class EventDefinition {
41
+ constructor(activity: Activity, eventDefinitionElement: SerializableElement)
41
42
  get id(): string;
42
43
  get type(): string;
43
44
  get executionId(): string;
@@ -481,6 +482,13 @@ declare module 'bpmn-elements' {
481
482
  shake(message: any): number;
482
483
  getCondition(): any;
483
484
  createMessage(override?: any): object;
485
+ /**
486
+ * Evaluate flow
487
+ * Executes condition if any, default flow is
488
+ * @param fromMessage Activity message
489
+ * @param {evaluateCallback} callback Callback with evaluation result, if truthy flow should be taken
490
+ */
491
+ evaluate(fromMessage: ElementBrokerMessage, callback: (err: Error, result: any) => void): void;
484
492
  }
485
493
 
486
494
  interface MessageFlowReference {
@@ -514,15 +522,53 @@ declare module 'bpmn-elements' {
514
522
  [x: string]: any,
515
523
  }
516
524
 
517
- type wrappedSetTimeout = (handler: CallableFunction, timeout: number, ...args: unknown[]) => any;
518
- type wrappedClearTimeout = (id?: any) => void;
525
+ type wrappedSetTimeout = (handler: TimerHandler, delay: number, ...args: any[]) => Timer;
526
+ type wrappedClearTimeout = (ref: any) => void;
527
+
528
+ interface Timer {
529
+ /** The function to call when the timer elapses */
530
+ readonly callback: TimerHandler;
531
+ /** The number of milliseconds to wait before calling the callback */
532
+ readonly delay: number;
533
+ /** Optional arguments to pass when the callback is called */
534
+ readonly args?: any[];
535
+ /** Timer owner if any */
536
+ readonly owner?: any;
537
+ /** Timer Id */
538
+ readonly timerId: string;
539
+ /** Timeout, return from setTimeout */
540
+ readonly timerRef: any;
541
+ [x: string]: any;
542
+ }
543
+
544
+ interface RegisteredTimer {
545
+ owner?: any;
546
+ get setTimeout(): wrappedSetTimeout;
547
+ get clearTimeout(): wrappedClearTimeout;
548
+ }
519
549
 
520
550
  interface ITimers {
521
- get executing(): any[];
522
551
  get setTimeout(): wrappedSetTimeout;
523
552
  get clearTimeout(): wrappedClearTimeout;
524
- register(owner?: any): { setTimeout: wrappedSetTimeout, clearTimeout: wrappedClearTimeout };
525
- [x: string]: any,
553
+ register(owner?: any): RegisteredTimer;
554
+ [x: string]: any;
555
+ }
556
+
557
+ interface TimersOptions {
558
+ /** Defaults to builtin setTimeout */
559
+ setTimeout?: typeof setTimeout;
560
+ /** Defaults to builtin clearTimeout */
561
+ clearTimeout?: typeof clearTimeout;
562
+ [x: string]: any;
563
+ }
564
+
565
+ class Timers implements ITimers {
566
+ options: TimersOptions;
567
+ constructor(options?: TimersOptions);
568
+ get executing(): Timer[];
569
+ get setTimeout(): wrappedSetTimeout;
570
+ get clearTimeout(): wrappedClearTimeout;
571
+ register(owner?: any): RegisteredTimer;
526
572
  }
527
573
 
528
574
  interface IScripts {
@@ -595,7 +641,30 @@ declare module 'bpmn-elements' {
595
641
  var MessageEventDefinition: EventDefinition;
596
642
  var SignalEventDefinition: EventDefinition;
597
643
  var TerminateEventDefinition: EventDefinition;
598
- var TimerEventDefinition: EventDefinition;
644
+
645
+ const enum TimerType {
646
+ TimeCycle = 'timeCycle',
647
+ TimeDuration = 'timeDuration',
648
+ TimeDate = 'timeDate',
649
+ }
650
+
651
+ type parsedTimer = {
652
+ /** Expires at date time */
653
+ expireAt?: Date,
654
+ /** Repeat number of times */
655
+ repeat?: number,
656
+ /** Delay in milliseconds */
657
+ delay?: number,
658
+ };
659
+
660
+ class TimerEventDefinition extends EventDefinition {
661
+ /**
662
+ * Parse timer type
663
+ * @param timerType type of timer
664
+ * @param timerValue resolved expression timer string
665
+ */
666
+ parse(timerType: TimerType, timerValue: string): parsedTimer;
667
+ }
599
668
 
600
669
  class BpmnError {
601
670
  get id(): string;
@@ -634,4 +703,33 @@ declare module 'bpmn-elements' {
634
703
  code?: string;
635
704
  constructor(description: string, sourceMessage: MessageMessage, inner?: Error);
636
705
  }
706
+
707
+ interface Duration {
708
+ years?: number;
709
+ months?: number;
710
+ weeks?: number;
711
+ days?: number;
712
+ hours?: number;
713
+ minutes?: number;
714
+ seconds?: number;
715
+ repeat?: number;
716
+ }
717
+
718
+ type ISODurationApi = {
719
+ /** Parse PnYnMnDTnHnMnS format to object */
720
+ parse: (durationString: string) => Duration,
721
+ /** Convert ISO8601 duration object to an end Date. */
722
+ end: (durationInput: Duration, startDate?: Date) => Date,
723
+ /** Convert ISO8601 duration object to seconds */
724
+ toSeconds: (durationInput: Duration, startDate?: Date) => number,
725
+ }
726
+
727
+ const ISODuration: ISODurationApi;
637
728
  }
729
+
730
+ /**
731
+ * Evaluate flow callback
732
+ * @callback evaluateCallback
733
+ * @param {Error} err Evaluation error
734
+ * @param {boolean|object} evaluationResult If thruthy flow should be taken
735
+ */