function getAugmentedNamespace(n) {
|
var f = n.default;
|
if (typeof f == "function") {
|
var a = function () {
|
return f.apply(this, arguments);
|
};
|
a.prototype = f.prototype;
|
} else a = {};
|
Object.defineProperty(a, '__esModule', {value: true});
|
Object.keys(n).forEach(function (k) {
|
var d = Object.getOwnPropertyDescriptor(n, k);
|
Object.defineProperty(a, k, d.get ? d : {
|
enumerable: true,
|
get: function () {
|
return n[k];
|
}
|
});
|
});
|
return a;
|
}
|
|
/**
|
* A rule that checks that sequence flows outgoing from a
|
* conditional forking gateway or activity are
|
* either default flows _or_ have a condition attached
|
*/
|
|
var conditionalFlows = function() {
|
|
function check(node, reporter) {
|
|
if (!isConditionalForking(node)) {
|
return;
|
}
|
|
const outgoing = node.outgoing || [];
|
|
outgoing.forEach((flow) => {
|
const missingCondition = (
|
!hasCondition$2(flow) &&
|
!isDefaultFlow$1(node, flow)
|
);
|
|
if (missingCondition) {
|
reporter.report(flow.id, 'Sequence flow is missing condition', [ 'conditionExpression' ]);
|
}
|
});
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
|
// helpers /////////////////////////////
|
|
function isConditionalForking(node) {
|
|
const defaultFlow = node['default'];
|
const outgoing = node.outgoing || [];
|
|
return defaultFlow || outgoing.find(hasCondition$2);
|
}
|
|
function hasCondition$2(flow) {
|
return !!flow.conditionExpression;
|
}
|
|
function isDefaultFlow$1(node, flow) {
|
return node['default'] === flow;
|
}
|
|
/**
|
* Checks whether node is of specific bpmn type.
|
*
|
* @param {ModdleElement} node
|
* @param {String} type
|
*
|
* @return {Boolean}
|
*/
|
function is$g(node, type) {
|
|
if (type.indexOf(':') === -1) {
|
type = 'bpmn:' + type;
|
}
|
|
return (
|
(typeof node.$instanceOf === 'function')
|
? node.$instanceOf(type)
|
: node.$type === type
|
);
|
}
|
|
/**
|
* Checks whether node has any of the specified types.
|
*
|
* @param {ModdleElement} node
|
* @param {Array<String>} types
|
*
|
* @return {Boolean}
|
*/
|
function isAny$6(node, types) {
|
return types.some(function(type) {
|
return is$g(node, type);
|
});
|
}
|
|
var index_esm$1 = /*#__PURE__*/Object.freeze({
|
__proto__: null,
|
is: is$g,
|
isAny: isAny$6
|
});
|
|
var require$$0 = /*@__PURE__*/getAugmentedNamespace(index_esm$1);
|
|
const {
|
is: is$f,
|
isAny: isAny$5
|
} = require$$0;
|
|
|
/**
|
* A rule that checks the presence of an end event per scope.
|
*/
|
var endEventRequired = function() {
|
|
function hasEndEvent(node) {
|
const flowElements = node.flowElements || [];
|
|
return (
|
flowElements.some(node => is$f(node, 'bpmn:EndEvent'))
|
);
|
}
|
|
function check(node, reporter) {
|
|
if (!isAny$5(node, [
|
'bpmn:Process',
|
'bpmn:SubProcess'
|
])) {
|
return;
|
}
|
|
if (!hasEndEvent(node)) {
|
const type = is$f(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process';
|
|
reporter.report(node.id, type + ' is missing end event');
|
}
|
}
|
|
return { check };
|
};
|
|
const {
|
is: is$e
|
} = require$$0;
|
|
/**
|
* A rule that checks that start events inside an event sub-process
|
* are typed.
|
*/
|
var eventSubProcessTypedStartEvent = function() {
|
|
function check(node, reporter) {
|
|
if (!is$e(node, 'bpmn:SubProcess') || !node.triggeredByEvent) {
|
return;
|
}
|
|
const flowElements = node.flowElements || [];
|
|
flowElements.forEach(function(flowElement) {
|
|
if (!is$e(flowElement, 'bpmn:StartEvent')) {
|
return false;
|
}
|
|
const eventDefinitions = flowElement.eventDefinitions || [];
|
|
if (eventDefinitions.length === 0) {
|
reporter.report(flowElement.id, 'Start event is missing event definition', [ 'eventDefinitions' ]);
|
}
|
});
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
const {
|
isAny: isAny$4
|
} = require$$0;
|
|
/**
|
* A rule that checks that no fake join is modeled by attempting
|
* to give a task or event join semantics.
|
*
|
* Users should model a parallel joining gateway
|
* to achieve the desired behavior.
|
*/
|
var fakeJoin = function() {
|
|
function check(node, reporter) {
|
|
if (!isAny$4(node, [
|
'bpmn:Task',
|
'bpmn:Event'
|
])) {
|
return;
|
}
|
|
const incoming = node.incoming || [];
|
|
if (incoming.length > 1) {
|
reporter.report(node.id, 'Incoming flows do not join');
|
}
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
const {
|
is: is$d,
|
isAny: isAny$3
|
} = require$$0;
|
|
|
/**
|
* A rule that checks the presence of a label.
|
*/
|
var labelRequired = function() {
|
|
function check(node, reporter) {
|
|
if (isAny$3(node, [
|
'bpmn:ParallelGateway',
|
'bpmn:EventBasedGateway'
|
])) {
|
return;
|
}
|
|
// ignore joining gateways
|
if (is$d(node, 'bpmn:Gateway') && !isForking(node)) {
|
return;
|
}
|
|
if (is$d(node, 'bpmn:BoundaryEvent')) {
|
return;
|
}
|
|
// ignore sub-processes
|
if (is$d(node, 'bpmn:SubProcess')) {
|
|
// TODO(nikku): better ignore expanded sub-processes only
|
return;
|
}
|
|
// ignore sequence flow without condition
|
if (is$d(node, 'bpmn:SequenceFlow') && !hasCondition$1(node)) {
|
return;
|
}
|
|
// ignore data objects and artifacts for now
|
if (isAny$3(node, [
|
'bpmn:FlowNode',
|
'bpmn:SequenceFlow',
|
'bpmn:Participant',
|
'bpmn:Lane'
|
])) {
|
|
const name = (node.name || '').trim();
|
|
if (name.length === 0) {
|
reporter.report(node.id, 'Element is missing label/name', [ 'name' ]);
|
}
|
}
|
}
|
|
return { check };
|
};
|
|
|
// helpers ////////////////////////
|
|
function isForking(node) {
|
const outgoing = node.outgoing || [];
|
|
return outgoing.length > 1;
|
}
|
|
function hasCondition$1(node) {
|
return node.conditionExpression;
|
}
|
|
/**
|
* Flatten array, one level deep.
|
*
|
* @param {Array<?>} arr
|
*
|
* @return {Array<?>}
|
*/
|
function flatten$1(arr) {
|
return Array.prototype.concat.apply([], arr);
|
}
|
|
var nativeToString = Object.prototype.toString;
|
var nativeHasOwnProperty = Object.prototype.hasOwnProperty;
|
function isUndefined(obj) {
|
return obj === undefined;
|
}
|
function isDefined(obj) {
|
return obj !== undefined;
|
}
|
function isNil(obj) {
|
return obj == null;
|
}
|
function isArray(obj) {
|
return nativeToString.call(obj) === '[object Array]';
|
}
|
function isObject(obj) {
|
return nativeToString.call(obj) === '[object Object]';
|
}
|
function isNumber(obj) {
|
return nativeToString.call(obj) === '[object Number]';
|
}
|
function isFunction(obj) {
|
var tag = nativeToString.call(obj);
|
return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]';
|
}
|
function isString(obj) {
|
return nativeToString.call(obj) === '[object String]';
|
}
|
/**
|
* Ensure collection is an array.
|
*
|
* @param {Object} obj
|
*/
|
|
function ensureArray(obj) {
|
if (isArray(obj)) {
|
return;
|
}
|
|
throw new Error('must supply array');
|
}
|
/**
|
* Return true, if target owns a property with the given key.
|
*
|
* @param {Object} target
|
* @param {String} key
|
*
|
* @return {Boolean}
|
*/
|
|
function has(target, key) {
|
return nativeHasOwnProperty.call(target, key);
|
}
|
|
/**
|
* Find element in collection.
|
*
|
* @param {Array|Object} collection
|
* @param {Function|Object} matcher
|
*
|
* @return {Object}
|
*/
|
|
function find(collection, matcher) {
|
matcher = toMatcher(matcher);
|
var match;
|
forEach(collection, function (val, key) {
|
if (matcher(val, key)) {
|
match = val;
|
return false;
|
}
|
});
|
return match;
|
}
|
/**
|
* Find element index in collection.
|
*
|
* @param {Array|Object} collection
|
* @param {Function} matcher
|
*
|
* @return {Object}
|
*/
|
|
function findIndex(collection, matcher) {
|
matcher = toMatcher(matcher);
|
var idx = isArray(collection) ? -1 : undefined;
|
forEach(collection, function (val, key) {
|
if (matcher(val, key)) {
|
idx = key;
|
return false;
|
}
|
});
|
return idx;
|
}
|
/**
|
* Find element in collection.
|
*
|
* @param {Array|Object} collection
|
* @param {Function} matcher
|
*
|
* @return {Array} result
|
*/
|
|
function filter(collection, matcher) {
|
var result = [];
|
forEach(collection, function (val, key) {
|
if (matcher(val, key)) {
|
result.push(val);
|
}
|
});
|
return result;
|
}
|
/**
|
* Iterate over collection; returning something
|
* (non-undefined) will stop iteration.
|
*
|
* @param {Array|Object} collection
|
* @param {Function} iterator
|
*
|
* @return {Object} return result that stopped the iteration
|
*/
|
|
function forEach(collection, iterator) {
|
var val, result;
|
|
if (isUndefined(collection)) {
|
return;
|
}
|
|
var convertKey = isArray(collection) ? toNum : identity;
|
|
for (var key in collection) {
|
if (has(collection, key)) {
|
val = collection[key];
|
result = iterator(val, convertKey(key));
|
|
if (result === false) {
|
return val;
|
}
|
}
|
}
|
}
|
/**
|
* Return collection without element.
|
*
|
* @param {Array} arr
|
* @param {Function} matcher
|
*
|
* @return {Array}
|
*/
|
|
function without(arr, matcher) {
|
if (isUndefined(arr)) {
|
return [];
|
}
|
|
ensureArray(arr);
|
matcher = toMatcher(matcher);
|
return arr.filter(function (el, idx) {
|
return !matcher(el, idx);
|
});
|
}
|
/**
|
* Reduce collection, returning a single result.
|
*
|
* @param {Object|Array} collection
|
* @param {Function} iterator
|
* @param {Any} result
|
*
|
* @return {Any} result returned from last iterator
|
*/
|
|
function reduce(collection, iterator, result) {
|
forEach(collection, function (value, idx) {
|
result = iterator(result, value, idx);
|
});
|
return result;
|
}
|
/**
|
* Return true if every element in the collection
|
* matches the criteria.
|
*
|
* @param {Object|Array} collection
|
* @param {Function} matcher
|
*
|
* @return {Boolean}
|
*/
|
|
function every(collection, matcher) {
|
return !!reduce(collection, function (matches, val, key) {
|
return matches && matcher(val, key);
|
}, true);
|
}
|
/**
|
* Return true if some elements in the collection
|
* match the criteria.
|
*
|
* @param {Object|Array} collection
|
* @param {Function} matcher
|
*
|
* @return {Boolean}
|
*/
|
|
function some(collection, matcher) {
|
return !!find(collection, matcher);
|
}
|
/**
|
* Transform a collection into another collection
|
* by piping each member through the given fn.
|
*
|
* @param {Object|Array} collection
|
* @param {Function} fn
|
*
|
* @return {Array} transformed collection
|
*/
|
|
function map(collection, fn) {
|
var result = [];
|
forEach(collection, function (val, key) {
|
result.push(fn(val, key));
|
});
|
return result;
|
}
|
/**
|
* Get the collections keys.
|
*
|
* @param {Object|Array} collection
|
*
|
* @return {Array}
|
*/
|
|
function keys(collection) {
|
return collection && Object.keys(collection) || [];
|
}
|
/**
|
* Shorthand for `keys(o).length`.
|
*
|
* @param {Object|Array} collection
|
*
|
* @return {Number}
|
*/
|
|
function size(collection) {
|
return keys(collection).length;
|
}
|
/**
|
* Get the values in the collection.
|
*
|
* @param {Object|Array} collection
|
*
|
* @return {Array}
|
*/
|
|
function values(collection) {
|
return map(collection, function (val) {
|
return val;
|
});
|
}
|
/**
|
* Group collection members by attribute.
|
*
|
* @param {Object|Array} collection
|
* @param {Function} extractor
|
*
|
* @return {Object} map with { attrValue => [ a, b, c ] }
|
*/
|
|
function groupBy(collection, extractor) {
|
var grouped = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
extractor = toExtractor(extractor);
|
forEach(collection, function (val) {
|
var discriminator = extractor(val) || '_';
|
var group = grouped[discriminator];
|
|
if (!group) {
|
group = grouped[discriminator] = [];
|
}
|
|
group.push(val);
|
});
|
return grouped;
|
}
|
function uniqueBy(extractor) {
|
extractor = toExtractor(extractor);
|
var grouped = {};
|
|
for (var _len = arguments.length, collections = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
collections[_key - 1] = arguments[_key];
|
}
|
|
forEach(collections, function (c) {
|
return groupBy(c, extractor, grouped);
|
});
|
var result = map(grouped, function (val, key) {
|
return val[0];
|
});
|
return result;
|
}
|
var unionBy = uniqueBy;
|
/**
|
* Sort collection by criteria.
|
*
|
* @param {Object|Array} collection
|
* @param {String|Function} extractor
|
*
|
* @return {Array}
|
*/
|
|
function sortBy(collection, extractor) {
|
extractor = toExtractor(extractor);
|
var sorted = [];
|
forEach(collection, function (value, key) {
|
var disc = extractor(value, key);
|
var entry = {
|
d: disc,
|
v: value
|
};
|
|
for (var idx = 0; idx < sorted.length; idx++) {
|
var d = sorted[idx].d;
|
|
if (disc < d) {
|
sorted.splice(idx, 0, entry);
|
return;
|
}
|
} // not inserted, append (!)
|
|
|
sorted.push(entry);
|
});
|
return map(sorted, function (e) {
|
return e.v;
|
});
|
}
|
/**
|
* Create an object pattern matcher.
|
*
|
* @example
|
*
|
* const matcher = matchPattern({ id: 1 });
|
*
|
* let element = find(elements, matcher);
|
*
|
* @param {Object} pattern
|
*
|
* @return {Function} matcherFn
|
*/
|
|
function matchPattern(pattern) {
|
return function (el) {
|
return every(pattern, function (val, key) {
|
return el[key] === val;
|
});
|
};
|
}
|
|
function toExtractor(extractor) {
|
return isFunction(extractor) ? extractor : function (e) {
|
return e[extractor];
|
};
|
}
|
|
function toMatcher(matcher) {
|
return isFunction(matcher) ? matcher : function (e) {
|
return e === matcher;
|
};
|
}
|
|
function identity(arg) {
|
return arg;
|
}
|
|
function toNum(arg) {
|
return Number(arg);
|
}
|
|
/**
|
* Debounce fn, calling it only once if the given time
|
* elapsed between calls.
|
*
|
* Lodash-style the function exposes methods to `#clear`
|
* and `#flush` to control internal behavior.
|
*
|
* @param {Function} fn
|
* @param {Number} timeout
|
*
|
* @return {Function} debounced function
|
*/
|
function debounce(fn, timeout) {
|
var timer;
|
var lastArgs;
|
var lastThis;
|
var lastNow;
|
|
function fire(force) {
|
var now = Date.now();
|
var scheduledDiff = force ? 0 : lastNow + timeout - now;
|
|
if (scheduledDiff > 0) {
|
return schedule(scheduledDiff);
|
}
|
|
fn.apply(lastThis, lastArgs);
|
clear();
|
}
|
|
function schedule(timeout) {
|
timer = setTimeout(fire, timeout);
|
}
|
|
function clear() {
|
if (timer) {
|
clearTimeout(timer);
|
}
|
|
timer = lastNow = lastArgs = lastThis = undefined;
|
}
|
|
function flush() {
|
if (timer) {
|
fire(true);
|
}
|
|
clear();
|
}
|
|
function callback() {
|
lastNow = Date.now();
|
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
args[_key] = arguments[_key];
|
}
|
|
lastArgs = args;
|
lastThis = this; // ensure an execution is scheduled
|
|
if (!timer) {
|
schedule(timeout);
|
}
|
}
|
|
callback.flush = flush;
|
callback.cancel = clear;
|
return callback;
|
}
|
/**
|
* Throttle fn, calling at most once
|
* in the given interval.
|
*
|
* @param {Function} fn
|
* @param {Number} interval
|
*
|
* @return {Function} throttled function
|
*/
|
|
function throttle(fn, interval) {
|
var throttling = false;
|
return function () {
|
if (throttling) {
|
return;
|
}
|
|
fn.apply(void 0, arguments);
|
throttling = true;
|
setTimeout(function () {
|
throttling = false;
|
}, interval);
|
};
|
}
|
/**
|
* Bind function against target <this>.
|
*
|
* @param {Function} fn
|
* @param {Object} target
|
*
|
* @return {Function} bound function
|
*/
|
|
function bind(fn, target) {
|
return fn.bind(target);
|
}
|
|
function _typeof(obj) {
|
"@babel/helpers - typeof";
|
|
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
_typeof = function (obj) {
|
return typeof obj;
|
};
|
} else {
|
_typeof = function (obj) {
|
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
};
|
}
|
|
return _typeof(obj);
|
}
|
|
function _extends() {
|
_extends = Object.assign || function (target) {
|
for (var i = 1; i < arguments.length; i++) {
|
var source = arguments[i];
|
|
for (var key in source) {
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
target[key] = source[key];
|
}
|
}
|
}
|
|
return target;
|
};
|
|
return _extends.apply(this, arguments);
|
}
|
|
/**
|
* Convenience wrapper for `Object.assign`.
|
*
|
* @param {Object} target
|
* @param {...Object} others
|
*
|
* @return {Object} the target
|
*/
|
|
function assign(target) {
|
for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
others[_key - 1] = arguments[_key];
|
}
|
|
return _extends.apply(void 0, [target].concat(others));
|
}
|
/**
|
* Sets a nested property of a given object to the specified value.
|
*
|
* This mutates the object and returns it.
|
*
|
* @param {Object} target The target of the set operation.
|
* @param {(string|number)[]} path The path to the nested value.
|
* @param {any} value The value to set.
|
*/
|
|
function set(target, path, value) {
|
var currentTarget = target;
|
forEach(path, function (key, idx) {
|
if (typeof key !== 'number' && typeof key !== 'string') {
|
throw new Error('illegal key type: ' + _typeof(key) + '. Key should be of type number or string.');
|
}
|
|
if (key === 'constructor') {
|
throw new Error('illegal key: constructor');
|
}
|
|
if (key === '__proto__') {
|
throw new Error('illegal key: __proto__');
|
}
|
|
var nextKey = path[idx + 1];
|
var nextTarget = currentTarget[key];
|
|
if (isDefined(nextKey) && isNil(nextTarget)) {
|
nextTarget = currentTarget[key] = isNaN(+nextKey) ? {} : [];
|
}
|
|
if (isUndefined(nextKey)) {
|
if (isUndefined(value)) {
|
delete currentTarget[key];
|
} else {
|
currentTarget[key] = value;
|
}
|
} else {
|
currentTarget = nextTarget;
|
}
|
});
|
return target;
|
}
|
/**
|
* Gets a nested property of a given object.
|
*
|
* @param {Object} target The target of the get operation.
|
* @param {(string|number)[]} path The path to the nested value.
|
* @param {any} [defaultValue] The value to return if no value exists.
|
*/
|
|
function get(target, path, defaultValue) {
|
var currentTarget = target;
|
forEach(path, function (key) {
|
// accessing nil property yields <undefined>
|
if (isNil(currentTarget)) {
|
currentTarget = undefined;
|
return false;
|
}
|
|
currentTarget = currentTarget[key];
|
});
|
return isUndefined(currentTarget) ? defaultValue : currentTarget;
|
}
|
/**
|
* Pick given properties from the target object.
|
*
|
* @param {Object} target
|
* @param {Array} properties
|
*
|
* @return {Object} target
|
*/
|
|
function pick(target, properties) {
|
var result = {};
|
var obj = Object(target);
|
forEach(properties, function (prop) {
|
if (prop in obj) {
|
result[prop] = target[prop];
|
}
|
});
|
return result;
|
}
|
/**
|
* Pick all target properties, excluding the given ones.
|
*
|
* @param {Object} target
|
* @param {Array} properties
|
*
|
* @return {Object} target
|
*/
|
|
function omit(target, properties) {
|
var result = {};
|
var obj = Object(target);
|
forEach(obj, function (prop, key) {
|
if (properties.indexOf(key) === -1) {
|
result[key] = prop;
|
}
|
});
|
return result;
|
}
|
/**
|
* Recursively merge `...sources` into given target.
|
*
|
* Does support merging objects; does not support merging arrays.
|
*
|
* @param {Object} target
|
* @param {...Object} sources
|
*
|
* @return {Object} the target
|
*/
|
|
function merge(target) {
|
for (var _len2 = arguments.length, sources = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
sources[_key2 - 1] = arguments[_key2];
|
}
|
|
if (!sources.length) {
|
return target;
|
}
|
|
forEach(sources, function (source) {
|
// skip non-obj sources, i.e. null
|
if (!source || !isObject(source)) {
|
return;
|
}
|
|
forEach(source, function (sourceVal, key) {
|
if (key === '__proto__') {
|
return;
|
}
|
|
var targetVal = target[key];
|
|
if (isObject(sourceVal)) {
|
if (!isObject(targetVal)) {
|
// override target[key] with object
|
targetVal = {};
|
}
|
|
target[key] = merge(targetVal, sourceVal);
|
} else {
|
target[key] = sourceVal;
|
}
|
});
|
});
|
return target;
|
}
|
|
var index_esm = /*#__PURE__*/Object.freeze({
|
__proto__: null,
|
assign: assign,
|
bind: bind,
|
debounce: debounce,
|
ensureArray: ensureArray,
|
every: every,
|
filter: filter,
|
find: find,
|
findIndex: findIndex,
|
flatten: flatten$1,
|
forEach: forEach,
|
get: get,
|
groupBy: groupBy,
|
has: has,
|
isArray: isArray,
|
isDefined: isDefined,
|
isFunction: isFunction,
|
isNil: isNil,
|
isNumber: isNumber,
|
isObject: isObject,
|
isString: isString,
|
isUndefined: isUndefined,
|
keys: keys,
|
map: map,
|
matchPattern: matchPattern,
|
merge: merge,
|
omit: omit,
|
pick: pick,
|
reduce: reduce,
|
set: set,
|
size: size,
|
some: some,
|
sortBy: sortBy,
|
throttle: throttle,
|
unionBy: unionBy,
|
uniqueBy: uniqueBy,
|
values: values,
|
without: without
|
});
|
|
var require$$1 = /*@__PURE__*/getAugmentedNamespace(index_esm);
|
|
const {
|
is: is$c
|
} = require$$0;
|
|
const {
|
flatten
|
} = require$$1;
|
|
/**
|
* A rule that checks that there is no BPMNDI information missing for elements,
|
* which require BPMNDI.
|
*/
|
var noBpmndi = function() {
|
|
function check(node, reporter) {
|
|
if (!is$c(node, 'bpmn:Definitions')) {
|
return false;
|
}
|
|
// (1) Construct array of all BPMN elements
|
const bpmnElements = getAllBpmnElements(node.rootElements);
|
|
// (2) Filter BPMN elements without visual representation
|
const visualBpmnElements = bpmnElements.filter(hasVisualRepresentation);
|
|
// (3) Construct array of BPMNDI references
|
const diBpmnReferences = getAllDiBpmnReferences(node);
|
|
// (4) Report elements without BPMNDI
|
visualBpmnElements.forEach((element) => {
|
if (diBpmnReferences.indexOf(element.id) === -1) {
|
reporter.report(element.id, 'Element is missing bpmndi');
|
}
|
});
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
|
// helpers /////////////////////////////
|
|
/**
|
* Get all BPMN elements within a bpmn:Definitions node
|
*
|
* @param {array<ModdleElement>} rootElements - An array of Moddle rootElements
|
* @return {array<Object>} A flat array with all BPMN elements, each represented with { id: elementId, $type: elementType }
|
*
|
*/
|
function getAllBpmnElements(rootElements) {
|
return flatten(rootElements.map((rootElement) => {
|
const laneSet =
|
rootElement.laneSets && rootElement.laneSets[0] || rootElement.childLaneSet;
|
|
// Include
|
// * flowElements (e.g., tasks, sequenceFlows),
|
// * nested flowElements,
|
// * participants,
|
// * artifacts (groups),
|
// * laneSets
|
// * nested laneSets
|
// * childLaneSets
|
// * nested childLaneSets
|
// * messageFlows
|
const elements = flatten([].concat(
|
rootElement.flowElements || [],
|
(rootElement.flowElements && getAllBpmnElements(rootElement.flowElements.filter(hasFlowElements))) || [],
|
rootElement.participants || [],
|
rootElement.artifacts || [],
|
laneSet && laneSet.lanes || [],
|
laneSet && laneSet.lanes && getAllBpmnElements(laneSet.lanes.filter(hasChildLaneSet)) || [],
|
rootElement.messageFlows || []
|
));
|
|
if (elements.length > 0) {
|
return elements.map((element) => {
|
|
return {
|
id: element.id,
|
$type: element.$type
|
};
|
});
|
} else {
|
|
// We are not interested in the rest here (DI)
|
return [];
|
}
|
}));
|
}
|
|
/**
|
* Get all BPMN elements within a bpmn:Definitions node
|
*
|
* @param {ModdleElement} definitionsNode - A moddleElement representing the
|
* bpmn:Definitions element
|
* @return {array<String>} A flat array with all BPMNDI element ids part of
|
* this bpmn:Definitions node
|
*
|
*/
|
function getAllDiBpmnReferences(definitionsNode) {
|
return flatten(
|
definitionsNode.diagrams.map((diagram) => {
|
|
const diElements = diagram.plane.planeElement || [];
|
|
return diElements.map((element) => {
|
|
return element.bpmnElement.id;
|
});
|
})
|
);
|
}
|
|
function hasVisualRepresentation(element) {
|
const noVisRepresentation = [ 'bpmn:DataObject' ];
|
|
return noVisRepresentation.includes(element.$type) ? false : true;
|
}
|
|
function hasFlowElements(element) {
|
return element.flowElements ? true : false;
|
}
|
|
function hasChildLaneSet(element) {
|
return element.childLaneSet ? true : false;
|
}
|
|
var helper = {};
|
|
const {
|
is: is$b
|
} = require$$0;
|
|
/**
|
* Create a checker that disallows the given element type.
|
*
|
* @param {String} type
|
*
|
* @return {Function} ruleImpl
|
*/
|
function disallowNodeType$2(type) {
|
|
return function() {
|
|
function check(node, reporter) {
|
|
if (is$b(node, type)) {
|
reporter.report(node.id, 'Element has disallowed type <' + type + '>');
|
}
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
}
|
|
helper.disallowNodeType = disallowNodeType$2;
|
|
const disallowNodeType$1 = helper.disallowNodeType;
|
|
var noComplexGateway = disallowNodeType$1('bpmn:ComplexGateway');
|
|
const {
|
isAny: isAny$2,
|
is: is$a
|
} = require$$0;
|
|
|
/**
|
* A rule that verifies that there exists no disconnected
|
* flow elements, i.e. elements without incoming
|
* _or_ outgoing sequence flows
|
*/
|
var noDisconnected = function() {
|
|
function check(node, reporter) {
|
|
if (!isAny$2(node, [
|
'bpmn:Task',
|
'bpmn:Gateway',
|
'bpmn:SubProcess',
|
'bpmn:Event'
|
]) || node.triggeredByEvent) {
|
return;
|
}
|
|
// compensation activity and boundary events are
|
// linked visually via associations. If these associations
|
// exist we are fine, too
|
if (isCompensationLinked(node)) {
|
return;
|
}
|
|
const incoming = node.incoming || [];
|
const outgoing = node.outgoing || [];
|
|
if (!incoming.length && !outgoing.length) {
|
reporter.report(node.id, 'Element is not connected');
|
}
|
}
|
|
return {
|
check
|
};
|
};
|
|
|
// helpers /////////////////
|
|
function isCompensationBoundary(node) {
|
|
var eventDefinitions = node.eventDefinitions;
|
|
if (!is$a(node, 'bpmn:BoundaryEvent')) {
|
return false;
|
}
|
|
if (!eventDefinitions || eventDefinitions.length !== 1) {
|
return false;
|
}
|
|
return is$a(eventDefinitions[0], 'bpmn:CompensateEventDefinition');
|
}
|
|
function isCompensationActivity(node) {
|
return node.isForCompensation;
|
}
|
|
function isCompensationLinked(node) {
|
var source = isCompensationBoundary(node);
|
var target = isCompensationActivity(node);
|
|
// TODO(nikku): check, whether compensation association exists
|
return source || target;
|
}
|
|
const {
|
is: is$9
|
} = require$$0;
|
|
/**
|
* A rule that verifies that there are no disconnected
|
* flow elements, i.e. elements without incoming
|
* _or_ outgoing sequence flows
|
*/
|
var noDuplicateSequenceFlows = function() {
|
|
const keyed = {};
|
|
const outgoingReported = {};
|
const incomingReported = {};
|
|
function check(node, reporter) {
|
|
if (!is$9(node, 'bpmn:SequenceFlow')) {
|
return;
|
}
|
|
const key = flowKey(node);
|
|
if (key in keyed) {
|
reporter.report(node.id, 'SequenceFlow is a duplicate');
|
|
const sourceId = node.sourceRef.id;
|
const targetId = node.targetRef.id;
|
|
if (!outgoingReported[sourceId]) {
|
reporter.report(sourceId, 'Duplicate outgoing sequence flows');
|
|
outgoingReported[sourceId] = true;
|
}
|
|
if (!incomingReported[targetId]) {
|
reporter.report(targetId, 'Duplicate incoming sequence flows');
|
|
incomingReported[targetId] = true;
|
}
|
} else {
|
keyed[key] = node;
|
}
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
|
// helpers /////////////////
|
|
function flowKey(flow) {
|
const conditionExpression = flow.conditionExpression;
|
|
const condition = conditionExpression ? conditionExpression.body : '';
|
const source = flow.sourceRef ? flow.sourceRef.id : flow.id;
|
const target = flow.targetRef ? flow.targetRef.id : flow.id;
|
|
return source + '#' + target + '#' + condition;
|
}
|
|
const {
|
is: is$8
|
} = require$$0;
|
|
|
/**
|
* A rule that checks, whether a gateway forks and joins
|
* at the same time.
|
*/
|
var noGatewayJoinFork = function() {
|
|
function check(node, reporter) {
|
|
if (!is$8(node, 'bpmn:Gateway')) {
|
return;
|
}
|
|
const incoming = node.incoming || [];
|
const outgoing = node.outgoing || [];
|
|
if (incoming.length > 1 && outgoing.length > 1) {
|
reporter.report(node.id, 'Gateway forks and joins');
|
}
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
const {
|
isAny: isAny$1
|
} = require$$0;
|
|
|
/**
|
* A rule that checks that no implicit split is modeled
|
* starting from a task.
|
*
|
* users should model the parallel splitting gateway
|
* explicitly instead.
|
*/
|
var noImplicitSplit = function() {
|
|
function check(node, reporter) {
|
|
if (!isAny$1(node, [
|
'bpmn:Task',
|
'bpmn:Event'
|
])) {
|
return;
|
}
|
|
const outgoing = node.outgoing || [];
|
|
const outgoingWithoutCondition = outgoing.filter((flow) => {
|
return !hasCondition(flow) && !isDefaultFlow(node, flow);
|
});
|
|
if (outgoingWithoutCondition.length > 1) {
|
reporter.report(node.id, 'Flow splits implicitly');
|
}
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
|
// helpers /////////////////////////////
|
|
function hasCondition(flow) {
|
return !!flow.conditionExpression;
|
}
|
|
function isDefaultFlow(node, flow) {
|
return node['default'] === flow;
|
}
|
|
const disallowNodeType = helper.disallowNodeType;
|
|
var noInclusiveGateway = disallowNodeType('bpmn:InclusiveGateway');
|
|
const {
|
is: is$7
|
} = require$$0;
|
|
/**
|
* A rule that checks whether not more than one blank start event
|
* exists per scope.
|
*/
|
var singleBlankStartEvent = function() {
|
|
function check(node, reporter) {
|
|
if (!is$7(node, 'bpmn:FlowElementsContainer')) {
|
return;
|
}
|
|
const flowElements = node.flowElements || [];
|
|
const blankStartEvents = flowElements.filter(function(flowElement) {
|
|
if (!is$7(flowElement, 'bpmn:StartEvent')) {
|
return false;
|
}
|
|
const eventDefinitions = flowElement.eventDefinitions || [];
|
|
return eventDefinitions.length === 0;
|
});
|
|
if (blankStartEvents.length > 1) {
|
const type = is$7(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process';
|
|
reporter.report(node.id, type + ' has multiple blank start events');
|
}
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
const {
|
is: is$6
|
} = require$$0;
|
|
|
/**
|
* A rule that verifies that an event contains maximum one event definition.
|
*/
|
var singleEventDefinition = function() {
|
|
function check(node, reporter) {
|
|
if (!is$6(node, 'bpmn:Event')) {
|
return;
|
}
|
|
const eventDefinitions = node.eventDefinitions || [];
|
|
if (eventDefinitions.length > 1) {
|
reporter.report(node.id, 'Event has multiple event definitions', [ 'eventDefinitions' ]);
|
}
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
const {
|
is: is$5,
|
isAny
|
} = require$$0;
|
|
|
/**
|
* A rule that checks for the presence of a start event per scope.
|
*/
|
var startEventRequired = function() {
|
|
function hasStartEvent(node) {
|
const flowElements = node.flowElements || [];
|
|
return (
|
flowElements.some(node => is$5(node, 'bpmn:StartEvent'))
|
);
|
}
|
|
function check(node, reporter) {
|
|
if (!isAny(node, [
|
'bpmn:Process',
|
'bpmn:SubProcess'
|
])) {
|
return;
|
}
|
|
if (!hasStartEvent(node)) {
|
const type = is$5(node, 'bpmn:SubProcess') ? 'Sub process' : 'Process';
|
|
reporter.report(node.id, type + ' is missing start event');
|
}
|
}
|
|
return { check };
|
};
|
|
const {
|
is: is$4
|
} = require$$0;
|
|
|
/**
|
* A rule that checks that start events inside a normal sub-processes
|
* are blank (do not have an event definition).
|
*/
|
var subProcessBlankStartEvent = function() {
|
|
function check(node, reporter) {
|
|
if (!is$4(node, 'bpmn:SubProcess') || node.triggeredByEvent) {
|
return;
|
}
|
|
const flowElements = node.flowElements || [];
|
|
flowElements.forEach(function(flowElement) {
|
|
if (!is$4(flowElement, 'bpmn:StartEvent')) {
|
return false;
|
}
|
|
const eventDefinitions = flowElement.eventDefinitions || [];
|
|
if (eventDefinitions.length > 0) {
|
reporter.report(flowElement.id, 'Start event must be blank', [ 'eventDefinitions' ]);
|
}
|
});
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
const {
|
is: is$3
|
} = require$$0;
|
|
/**
|
* A rule that checks, whether a gateway has only one source and target.
|
*
|
* Those gateways are superfluous since they don't do anything.
|
*/
|
var superfluousGateway = function() {
|
|
function check(node, reporter) {
|
|
if (!is$3(node, 'bpmn:Gateway')) {
|
return;
|
}
|
|
const incoming = node.incoming || [];
|
const outgoing = node.outgoing || [];
|
|
if (incoming.length === 1 && outgoing.length === 1) {
|
reporter.report(node.id, 'Gateway is superfluous. It only has one source and target.');
|
}
|
}
|
|
return {
|
check
|
};
|
|
};
|
|
const {
|
is: is$2
|
} = require$$0;
|
|
|
/**
|
* Rule that reports missing targetNamespace on bpmn:Definitions.
|
*/
|
var targetNamespace = function() {
|
|
function check(node, reporter) {
|
if (is$2(node, 'bpmn:Definitions') && !node.targetNamespace) {
|
reporter.report(node.id, 'Element is missing targetNamespace');
|
}
|
}
|
|
return {
|
check: check
|
};
|
};
|
|
const {
|
is: is$1
|
} = require$$0;
|
|
|
/**
|
* Rule that reports manual tasks being used.
|
*/
|
var noManualTask = function() {
|
|
function check(node, reporter) {
|
if (is$1(node, 'bpmn:ManualTask')) {
|
reporter.report(node.id, 'Element has disallowed type bpmn:ManualTask');
|
}
|
}
|
|
return {
|
check: check
|
};
|
};
|
|
const {
|
is
|
} = require$$0;
|
|
|
/**
|
* 校验任务节点是否设置任务处理人
|
*/
|
var checkNodeUser = function () {
|
|
function check(node, reporter) {
|
if (is(node, 'bpmn:Task')) {
|
const assignee = (node.assignee || '').trim();
|
const candidateUsers = (node.candidateUsers || '').trim();
|
const candidateGroups = (node.candidateGroups || '').trim();
|
|
if (assignee.length === 0 && candidateUsers.length === 0 && candidateGroups.length === 0) {
|
reporter.report(node.id, '任务节点未设置人员信息');
|
}
|
|
const buttons = node.extensionElements?.values?.filter(ex => {
|
return ex.$type === `flowable:Buttons`;
|
}) ?? [];
|
const selectButtons = buttons.reduce((pre, current) => pre.concat(current.values), []);
|
if (buttons.length === 0 || selectButtons.length === 0) {
|
reporter.report(node.id, '任务节点未配置按钮信息');
|
}
|
}
|
}
|
|
return {
|
check: check
|
};
|
};
|
|
const cache = {};
|
|
/**
|
* A resolver that caches rules and configuration as part of the bundle,
|
* making them accessible in the browser.
|
*
|
* @param {Object} cache
|
*/
|
function Resolver() {}
|
|
Resolver.prototype.resolveRule = function(pkg, ruleName) {
|
|
const rule = cache[pkg + '/' + ruleName];
|
|
if (!rule) {
|
throw new Error('cannot resolve rule <' + pkg + '/' + ruleName + '>');
|
}
|
|
return rule;
|
};
|
|
Resolver.prototype.resolveConfig = function(pkg, configName) {
|
throw new Error(
|
'cannot resolve config <' + configName + '> in <' + pkg +'>'
|
);
|
};
|
|
const resolver = new Resolver();
|
|
const rules = {
|
"conditional-flows": "error",
|
"end-event-required": "error",
|
"event-sub-process-typed-start-event": "error",
|
"fake-join": "warn",
|
"label-required": "warn",
|
"no-bpmndi": "error",
|
"no-complex-gateway": "error",
|
"no-disconnected": "error",
|
"no-duplicate-sequence-flows": "error",
|
"no-gateway-join-fork": "error",
|
"no-implicit-split": "error",
|
"no-inclusive-gateway": "error",
|
"single-blank-start-event": "error",
|
"single-event-definition": "error",
|
"start-event-required": "error",
|
"sub-process-blank-start-event": "error",
|
"superfluous-gateway": "warn",
|
"custom/target-namespace": "error",
|
"custom/no-manual-task": "warn",
|
"custom/check-node-user": "error"
|
};
|
|
const config = {
|
rules: rules
|
};
|
|
const bundle = {
|
resolver: resolver,
|
config: config
|
};
|
cache['bpmnlint/conditional-flows'] = conditionalFlows;
|
cache['bpmnlint/end-event-required'] = endEventRequired;
|
cache['bpmnlint/event-sub-process-typed-start-event'] = eventSubProcessTypedStartEvent;
|
cache['bpmnlint/fake-join'] = fakeJoin;
|
cache['bpmnlint/label-required'] = labelRequired;
|
cache['bpmnlint/no-bpmndi'] = noBpmndi;
|
cache['bpmnlint/no-complex-gateway'] = noComplexGateway;
|
cache['bpmnlint/no-disconnected'] = noDisconnected;
|
cache['bpmnlint/no-duplicate-sequence-flows'] = noDuplicateSequenceFlows;
|
cache['bpmnlint/no-gateway-join-fork'] = noGatewayJoinFork;
|
cache['bpmnlint/no-implicit-split'] = noImplicitSplit;
|
cache['bpmnlint/no-inclusive-gateway'] = noInclusiveGateway;
|
cache['bpmnlint/single-blank-start-event'] = singleBlankStartEvent;
|
cache['bpmnlint/single-event-definition'] = singleEventDefinition;
|
cache['bpmnlint/start-event-required'] = startEventRequired;
|
cache['bpmnlint/sub-process-blank-start-event'] = subProcessBlankStartEvent;
|
cache['bpmnlint/superfluous-gateway'] = superfluousGateway;
|
cache['bpmnlint-plugin-custom/target-namespace'] = targetNamespace;
|
cache['bpmnlint-plugin-custom/no-manual-task'] = noManualTask;
|
cache['bpmnlint-plugin-custom/check-node-user'] = checkNodeUser;
|
|
export { config, bundle as default, resolver };
|