├── .editorconfig
├── .github
└── workflows
│ └── build.yaml
├── .gitignore
├── .mocharc.json
├── .nvmrc
├── .prettierignore
├── .prettierrc.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appveyor.yml
├── docs
├── API.md
├── Examples.md
└── Upgrade.md
├── eslint.config.js
├── images
└── mother-of-all.png
├── lib
└── index.cjs
├── package.json
├── rollup.config.js
├── scripts
└── generate-api-toc.js
├── src
├── JavaScripts.js
├── Logger.js
├── extensions
│ └── ProcessOutputDataObject.js
├── getOptionsAndCallback.js
└── index.js
├── test
├── Engine-test.js
├── feature
│ ├── Engine-feature.js
│ ├── backward-compatibility-feature.js
│ ├── call-activity-feature.js
│ ├── conditional-event-feature.js
│ ├── engine-output-feature.js
│ ├── extend-feature.js
│ ├── issues-feature.js
│ ├── issues
│ │ ├── issue-163-feature.js
│ │ ├── issue-172-feature.js
│ │ ├── issue-187-feature.js
│ │ └── issue-199-feature.js
│ ├── multiple-sources-feature.js
│ ├── resume-feature.js
│ ├── scripts-feature.js
│ ├── shake-feature.js
│ └── timers-feature.js
├── helpers
│ ├── factory.js
│ └── testHelpers.js
├── issues-test.js
├── lib
│ └── JavaScripts-test.js
└── resources
│ ├── JsExtension.js
│ ├── README.md
│ ├── bound-error-and-timer.bpmn
│ ├── bound-error.bpmn
│ ├── boundary-non-interupting-timer.bpmn
│ ├── boundary-timeout.bpmn
│ ├── call-activity.bpmn
│ ├── conditional-bound-js-event.bpmn
│ ├── diagram_1.bpmn
│ ├── forms.bpmn
│ ├── issue-139.bpmn
│ ├── issue-163.bpmn
│ ├── issue-187.bpmn
│ ├── issue-19.bpmn
│ ├── issue-4.bpmn
│ ├── js-bpmn-moddle.json
│ ├── lanes.bpmn
│ ├── loop.bpmn
│ ├── messaging.bpmn
│ ├── mother-of-all.bpmn
│ ├── multiple-endEvents.bpmn
│ ├── multiple-joins.bpmn
│ ├── multiple-multiple-inbound.bpmn
│ ├── one-or-the-other.bpmn
│ ├── send-signal.bpmn
│ ├── service-task-io.bpmn
│ ├── service-task-operation.bpmn
│ ├── service-task.bpmn
│ ├── signals.bpmn
│ ├── simple-task.bpmn
│ ├── sub-process.bpmn
│ ├── succeeding-joins.bpmn
│ ├── task-multiple-inbound.bpmn
│ ├── task-with-multi-instance-loop.bpmn
│ ├── timer-event.bpmn
│ ├── timer-start-event.bpmn
│ └── timers.bpmn
├── tsconfig.json
└── types
└── bpmn-engine.d.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = true
10 |
11 | # Set default charset and indentation style
12 | [*.{js,json,html,dust,styl,yml,md}]
13 | charset = utf-8
14 | indent_style = space
15 | indent_size = 2
16 | trim_trailing_whitespace = true
17 | max_line_length = 160
18 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - master
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | strategy:
13 | matrix:
14 | node-version: [18, 20, 22, latest]
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v4
18 | - name: Use Node.js ${{ matrix.node-version }}
19 | uses: actions/setup-node@v4
20 | with:
21 | node-version: ${{ matrix.node-version }}
22 | - run: npm i
23 | - run: npm run test:lcov
24 | - name: Coveralls
25 | uses: coverallsapp/github-action@v2
26 | with:
27 | github-token: ${{ secrets.GITHUB_TOKEN }}
28 | flag-name: run-${{ matrix.node-version }}
29 | parallel: true
30 |
31 | finish:
32 | needs: build
33 | runs-on: ubuntu-latest
34 | steps:
35 | - name: Coveralls Finished
36 | uses: coverallsapp/github-action@v2
37 | with:
38 | github-token: ${{ secrets.GITHUB_TOKEN }}
39 | parallel-finished: true
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependency directory and npm log
2 | node_modules
3 | npm-debug.log
4 | tmp
5 |
6 | # Code coverage results
7 | /coverage
8 | coverage*
9 |
10 | # Built package
11 | /*.tgz
12 |
13 | # Project files
14 | .tern-port
15 | .tern-project
16 | .idea
17 | *.iml
18 | .vscode
19 |
20 | # Compiled client resources
21 | /public/
22 |
23 | # Revision file
24 | config/_revision
25 |
26 | # OS X
27 | .DS_Store
28 |
29 | # vim
30 | *.swp
31 | *.swo
32 |
33 | # eslint
34 | .eslintcache
35 |
36 | package-lock.json
37 |
38 | *.log
39 |
--------------------------------------------------------------------------------
/.mocharc.json:
--------------------------------------------------------------------------------
1 | {
2 | "recursive": true,
3 | "reporter": "spec",
4 | "timeout": 1000,
5 | "ui": "mocha-cakes-2",
6 | "require": ["chai/register-expect.js"]
7 | }
8 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | lib/**/*.cjs
2 | node_modules/
3 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "printWidth": 140,
4 | "singleQuote": true,
5 | "trailingComma": "es5"
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paed01/bpmn-engine/ef5552e39a0095e02547b6d2ba688a17e223ed52/LICENSE
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bpmn-engine
2 |
3 | [](https://www.repostatus.org/#active)
4 |
5 | [](https://github.com/paed01/bpmn-engine/actions/workflows/build.yaml)[](https://ci.appveyor.com/project/paed01/bpmn-engine/branch/master)[](https://coveralls.io/github/paed01/bpmn-engine?branch=master)
6 |
7 | # Introduction
8 |
9 | BPMN 2.0 execution engine. Open source javascript workflow engine.
10 |
11 | - [API](/docs/API.md)
12 | - [Changelog](/CHANGELOG.md)
13 | - [Examples](/docs/Examples.md)
14 | - [Upgrade version](/docs/Upgrade.md)
15 | - [Supported elements](#supported-elements)
16 | - [Debug](#debug)
17 | - [Example process](#a-pretty-image-of-a-process)
18 | - [Acknowledgments](#acknowledgments)
19 |
20 | # Supported elements
21 |
22 | See [bpmn-elements](https://github.com/paed01/bpmn-elements) for supported elements. The engine only support elements and attributes included in the BPMN 2.0 scheme, but can be extended to understand other schemas and elements.
23 |
24 | The aim is to, at least, have BPMN 2.0 [core support](https://www.omg.org/bpmn/Samples/Elements/Core_BPMN_Elements.htm).
25 |
26 | # Debug
27 |
28 | This package is shipped with [debug](https://github.com/debug-js/debug) activated with environment variable `DEBUG=bpmn-engine:*`. You can also provide your own logger.
29 |
30 | More granular debugging can be achieved by filtering on element type:
31 |
32 | ```sh
33 | DEBUG=*scripttask*,*:error:*
34 | ```
35 |
36 | or on Windows PowerShell:
37 |
38 | ```powershell
39 | $env:DEBUG='bpmn-engine:*'
40 | ```
41 |
42 | and to turn it off you need to:
43 |
44 | ```powershell
45 | $env:DEBUG=''
46 | ```
47 |
48 | # A pretty image of a process
49 |
50 | 
51 |
52 | # Acknowledgments
53 |
54 | The **bpmn-engine** resides upon the excellent library [bpmn-io/bpmn-moddle](https://github.com/bpmn-io/bpmn-moddle) developed by [bpmn.io](https://bpmn.io/)
55 |
56 | All diagrams are designed with [Camunda modeler](https://camunda.com/download/modeler/).
57 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | matrix:
3 | - nodejs_version: '18'
4 | - nodejs_version: '20'
5 |
6 | platform:
7 | - x64
8 |
9 | install:
10 | - ps: Install-Product node $env:nodejs_version $env:platform
11 | - npm install
12 |
13 | build: off
14 |
15 | test_script:
16 | - node --version
17 | - npm --version
18 | - npm run wintest
19 |
--------------------------------------------------------------------------------
/docs/Upgrade.md:
--------------------------------------------------------------------------------
1 | # Upgrade
2 |
3 | # < v14
4 |
5 | Since v14 of the engine output is no longer shared between definition and processes. To upgrade a saved state before version 14 you can run the following script that adds process environment to state.
6 |
7 | ```javascript
8 | export function upgradeStateToVersion14(state) {
9 | const stateVersion = getSemverVersion(state.engineVersion);
10 | if (!stateVersion || stateVersion.major >= 14) return state;
11 |
12 | return polyfillProcessEnvironment(state);
13 | }
14 |
15 | function polyfillProcessEnvironment(state) {
16 | if (!state.definitions?.length) return state;
17 |
18 | const polyfilledState = JSON.parse(JSON.stringify(state));
19 | for (const definition of polyfilledState.definitions) {
20 | if (!definition.environment) continue;
21 | if (!definition.execution) continue;
22 | if (!definition.execution.processes) continue;
23 |
24 | for (const bp of definition.execution.processes) {
25 | addProcessEnvironment(definition.environment, bp);
26 | }
27 | }
28 |
29 | return polyfilledState;
30 | }
31 |
32 | function addProcessEnvironment(environment, processState) {
33 | processState.environment = JSON.parse(JSON.stringify(environment));
34 | }
35 |
36 | function getSemverVersion(version) {
37 | if (typeof version !== 'string') return;
38 | const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
39 | if (!match) return;
40 | const [, major, minor, patch] = match;
41 | return {
42 | major: Number(major),
43 | minor: Number(minor),
44 | patch: Number(patch),
45 | };
46 | }
47 | ```
48 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import js from '@eslint/js';
2 | import globals from 'globals';
3 |
4 | const rules = {
5 | 'dot-notation': [2, { allowKeywords: true }],
6 | 'eol-last': 2,
7 | eqeqeq: 2,
8 | 'linebreak-style': ['error', 'unix'],
9 | 'no-alert': 2,
10 | 'no-array-constructor': 2,
11 | 'no-caller': 2,
12 | 'no-catch-shadow': 2,
13 | 'no-console': 1,
14 | 'no-eval': 2,
15 | 'no-extend-native': 2,
16 | 'no-extra-bind': 2,
17 | 'no-fallthrough': 'off',
18 | 'no-implied-eval': 2,
19 | 'no-iterator': 2,
20 | 'no-label-var': 2,
21 | 'no-labels': 2,
22 | 'no-lone-blocks': 2,
23 | 'no-loop-func': 2,
24 | 'no-multi-spaces': 2,
25 | 'no-multi-str': 2,
26 | 'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0, maxBOF: 0 }],
27 | 'no-new-func': 2,
28 | 'no-new-object': 2,
29 | 'no-new-wrappers': 2,
30 | 'no-octal-escape': 2,
31 | 'no-path-concat': 2,
32 | 'no-process-exit': 2,
33 | 'no-proto': 2,
34 | 'no-prototype-builtins': 2,
35 | 'no-return-assign': 2,
36 | 'no-script-url': 2,
37 | 'no-sequences': 2,
38 | 'no-shadow-restricted-names': 2,
39 | 'no-shadow': 2,
40 | 'no-spaced-func': 2,
41 | 'no-trailing-spaces': 2,
42 | 'no-undef-init': 2,
43 | 'no-undef': 2,
44 | 'no-underscore-dangle': 0,
45 | 'no-unused-expressions': 2,
46 | 'no-unused-vars': 2,
47 | 'no-use-before-define': 0,
48 | 'no-var': 2,
49 | 'no-with': 2,
50 | 'prefer-const': ['error', { destructuring: 'all' }],
51 | 'require-atomic-updates': 0,
52 | 'require-await': 2,
53 | 'semi-spacing': [2, { before: false, after: true }],
54 | semi: [2, 'always'],
55 | 'space-before-blocks': 2,
56 | 'space-before-function-paren': [2, { anonymous: 'never', named: 'never' }],
57 | 'space-infix-ops': 2,
58 | 'space-unary-ops': [2, { words: true, nonwords: false }],
59 | 'unicode-bom': ['error', 'never'],
60 | yoda: [2, 'never'],
61 | };
62 |
63 | export default [
64 | js.configs.recommended,
65 | {
66 | languageOptions: {
67 | parserOptions: {
68 | sourceType: 'module',
69 | ecmaVersion: 2020,
70 | },
71 | },
72 | rules,
73 | },
74 | {
75 | files: ['src/**/*.js', 'scripts/**/*.js'],
76 | languageOptions: {
77 | globals: {
78 | ...globals.nodeBuiltin,
79 | },
80 | },
81 | },
82 | {
83 | files: ['test/**/*.js'],
84 | languageOptions: {
85 | globals: {
86 | ...globals.nodeBuiltin,
87 | ...globals.mocha,
88 | expect: 'readonly',
89 | beforeEachScenario: 'readonly',
90 | Buffer: 'readonly',
91 | Feature: 'readonly',
92 | Scenario: 'readonly',
93 | Given: 'readonly',
94 | When: 'readonly',
95 | Then: 'readonly',
96 | And: 'readonly',
97 | But: 'readonly',
98 | },
99 | },
100 | rules: {
101 | 'no-unused-expressions': 0,
102 | 'no-var': 1,
103 | },
104 | },
105 | {
106 | ignores: ['coverage/**/*', 'node_modules/**/*', 'tmp/*', 'lib/*'],
107 | },
108 | ];
109 |
--------------------------------------------------------------------------------
/images/mother-of-all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paed01/bpmn-engine/ef5552e39a0095e02547b6d2ba688a17e223ed52/images/mother-of-all.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bpmn-engine",
3 | "description": "BPMN 2.0 execution engine. Open source javascript workflow engine.",
4 | "version": "25.0.0",
5 | "type": "module",
6 | "module": "./src/index.js",
7 | "main": "./lib/index.cjs",
8 | "types": "./types/bpmn-engine.d.ts",
9 | "sideEffects": false,
10 | "repository": {
11 | "type": "git",
12 | "url": "git://github.com/paed01/bpmn-engine.git"
13 | },
14 | "author": {
15 | "name": "Pål Edman",
16 | "url": "https://github.com/paed01"
17 | },
18 | "engines": {
19 | "node": ">=18"
20 | },
21 | "files": [
22 | "lib/",
23 | "src/",
24 | "types/"
25 | ],
26 | "scripts": {
27 | "test": "mocha -p -R @bonniernews/hot-bev",
28 | "posttest": "npm run lint && npm run toc && npm run dist",
29 | "lint": "eslint . --cache && prettier . -c --cache",
30 | "wintest": "mocha",
31 | "cov:html": "c8 -r html -r text mocha -p -R @bonniernews/hot-bev",
32 | "test:lcov": "c8 -r lcov mocha && npm run lint",
33 | "toc": "node scripts/generate-api-toc ./docs/API.md,./docs/Examples.md",
34 | "test-md": "texample ./docs/API.md,./docs/Examples.md,./docs/Upgrade.md",
35 | "dist": "rollup -c",
36 | "prepack": "npm run dist"
37 | },
38 | "keywords": [
39 | "workflow",
40 | "engine",
41 | "process",
42 | "automation",
43 | "bpmn",
44 | "bpmn 2"
45 | ],
46 | "license": "MIT",
47 | "licenses": [
48 | {
49 | "type": "MIT",
50 | "url": "https://github.com/paed01/bpmn-engine/master/LICENSE"
51 | }
52 | ],
53 | "devDependencies": {
54 | "@bonniernews/hot-bev": "^0.4.0",
55 | "@rollup/plugin-commonjs": "^28.0.0",
56 | "@types/bpmn-moddle": "^5.1.6",
57 | "@types/node": "^18.19.31",
58 | "bent": "^7.3.12",
59 | "c8": "^10.0.0",
60 | "camunda-bpmn-moddle": "^7.0.1",
61 | "chai": "^5.1.0",
62 | "chronokinesis": "^6.0.0",
63 | "eslint": "^9.10.0",
64 | "markdown-toc": "^1.2.0",
65 | "mocha": "^11.0.1",
66 | "mocha-cakes-2": "^3.3.0",
67 | "prettier": "^3.2.5",
68 | "rollup": "^4.10.0",
69 | "texample": "^0.0.8"
70 | },
71 | "dependencies": {
72 | "bpmn-elements": "^17.0.0",
73 | "bpmn-moddle": "^9.0.1",
74 | "debug": "^4.3.7",
75 | "moddle-context-serializer": "^4.3.0"
76 | },
77 | "peerDependencies": {
78 | "smqp": ">=9"
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'node:module';
2 | import { fileURLToPath } from 'node:url';
3 |
4 | import commonjs from '@rollup/plugin-commonjs';
5 |
6 | const nodeRequire = createRequire(fileURLToPath(import.meta.url));
7 | const { module, main, dependencies, peerDependencies } = nodeRequire('./package.json');
8 |
9 | export default {
10 | input: module,
11 | plugins: [
12 | commonjs({
13 | sourceMap: false,
14 | }),
15 | ],
16 | output: [
17 | {
18 | file: main,
19 | format: 'cjs',
20 | exports: 'named',
21 | footer: 'module.exports = Object.assign(exports.default, exports);',
22 | },
23 | ],
24 | external: ['node:module', 'node:url', 'node:vm', 'node:events', ...Object.keys({ ...dependencies, ...peerDependencies })],
25 | };
26 |
--------------------------------------------------------------------------------
/scripts/generate-api-toc.js:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 | import { createRequire } from 'node:module';
3 | import { fileURLToPath } from 'node:url';
4 | import Toc from 'markdown-toc';
5 |
6 | const nodeRequire = createRequire(fileURLToPath(import.meta.url));
7 | const { version } = nodeRequire('../package.json');
8 |
9 | const filenames = getFileNames();
10 |
11 | function getFileNames() {
12 | const arg = process.argv[2] || './API.md';
13 | return arg.split(',');
14 | }
15 |
16 | function generate(filename) {
17 | const api = fs.readFileSync(filename, 'utf8');
18 | const tocOptions = {
19 | bullets: '-',
20 | slugify(text) {
21 | return text
22 | .toLowerCase()
23 | .replace(/\s/g, '-')
24 | .replace(/[^\w-]/g, '');
25 | },
26 | };
27 |
28 | const output = Toc.insert(api, tocOptions).replace(
29 | /(.|\n)*/,
30 | '\n\n# ' + version + ' API Reference\n\n'
31 | );
32 |
33 | fs.writeFileSync(filename, output);
34 | }
35 |
36 | filenames.forEach(generate);
37 |
--------------------------------------------------------------------------------
/src/JavaScripts.js:
--------------------------------------------------------------------------------
1 | import { Script } from 'node:vm';
2 |
3 | export default function Scripts(disableDummy) {
4 | if (!(this instanceof Scripts)) return new Scripts(disableDummy);
5 | this.scripts = new Map();
6 | this.disableDummy = disableDummy;
7 | }
8 |
9 | Scripts.prototype.register = function register({ id, type, behaviour, logger, environment }) {
10 | let scriptBody, language;
11 |
12 | switch (type) {
13 | case 'bpmn:SequenceFlow': {
14 | if (!behaviour.conditionExpression) return;
15 | language = behaviour.conditionExpression.language;
16 | if (!language) return;
17 | scriptBody = behaviour.conditionExpression.body;
18 | break;
19 | }
20 | default: {
21 | language = behaviour.scriptFormat;
22 | scriptBody = behaviour.script;
23 | }
24 | }
25 |
26 | const filename = `${type}/${id}`;
27 | if (!language || !scriptBody) {
28 | if (this.disableDummy) return;
29 | const script = new DummyScript(language, filename, logger);
30 | this.scripts.set(id, script);
31 | return script;
32 | }
33 |
34 | if (!/^(javascript|js)$/i.test(language)) return;
35 |
36 | const script = new JavaScript(language, filename, scriptBody, environment);
37 | this.scripts.set(id, script);
38 |
39 | return script;
40 | };
41 |
42 | Scripts.prototype.getScript = function getScript(language, { id }) {
43 | return this.scripts.get(id);
44 | };
45 |
46 | function JavaScript(language, filename, scriptBody, environment) {
47 | this.id = filename;
48 | this.script = new Script(scriptBody, { filename });
49 | this.language = language;
50 | this.environment = environment;
51 | }
52 |
53 | JavaScript.prototype.execute = function execute(executionContext, callback) {
54 | const timers = this.environment.timers.register(executionContext);
55 | return this.script.runInNewContext({ ...executionContext, ...timers, next: callback });
56 | };
57 |
58 | function DummyScript(language, filename, logger) {
59 | this.id = filename;
60 | this.isDummy = true;
61 | this.language = language;
62 | this.logger = logger;
63 | }
64 |
65 | DummyScript.prototype.execute = function execute(executionContext, callback) {
66 | const { id, executionId } = executionContext.content;
67 | this.logger.debug(`<${executionId} (${id})> passthrough dummy script ${this.language || 'esperanto'}`);
68 | callback();
69 | };
70 |
--------------------------------------------------------------------------------
/src/Logger.js:
--------------------------------------------------------------------------------
1 | import Debug from 'debug';
2 |
3 | export default function Logger(scope) {
4 | return {
5 | debug: Debug('bpmn-engine:' + scope),
6 | error: Debug('bpmn-engine:error:' + scope),
7 | warn: Debug('bpmn-engine:warn:' + scope),
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/src/extensions/ProcessOutputDataObject.js:
--------------------------------------------------------------------------------
1 | const kDataObjectDef = Symbol.for('data object definition');
2 |
3 | export default function ProcessOutputDataObject(dataObjectDef, { environment }) {
4 | this[kDataObjectDef] = dataObjectDef;
5 | this.environment = environment;
6 | this.behaviour = dataObjectDef.behaviour;
7 | this.name = dataObjectDef.name;
8 | this.parent = dataObjectDef.parent;
9 | }
10 |
11 | Object.defineProperties(ProcessOutputDataObject.prototype, {
12 | id: {
13 | get() {
14 | return this[kDataObjectDef].id;
15 | },
16 | },
17 | type: {
18 | get() {
19 | return this[kDataObjectDef].type;
20 | },
21 | },
22 | });
23 |
24 | ProcessOutputDataObject.prototype.read = function readDataObject(broker, exchange, routingKeyPrefix, messageProperties) {
25 | const environment = this.environment;
26 | const { id, name, type } = this;
27 | const value = environment.variables.data?.[this.id];
28 | return broker.publish(exchange, `${routingKeyPrefix}response`, { id, name, type, value }, messageProperties);
29 | };
30 |
31 | ProcessOutputDataObject.prototype.write = function writeDataObject(broker, exchange, routingKeyPrefix, value, messageProperties) {
32 | const environment = this.environment;
33 | const { id, name, type } = this;
34 |
35 | const data = (environment.variables.data = environment.variables.data || {});
36 | data[id] = value;
37 |
38 | const outputData = (environment.output.data = environment.output.data || {});
39 | outputData[id] = value;
40 | return broker.publish(exchange, `${routingKeyPrefix}response`, { id, name, type, value }, messageProperties);
41 | };
42 |
--------------------------------------------------------------------------------
/src/getOptionsAndCallback.js:
--------------------------------------------------------------------------------
1 | export default function getOptionsAndCallback(optionsOrCallback, callback) {
2 | let options;
3 | if (typeof optionsOrCallback === 'function') {
4 | callback = optionsOrCallback;
5 | } else {
6 | options = optionsOrCallback;
7 | }
8 |
9 | return [options, callback];
10 | }
11 |
--------------------------------------------------------------------------------
/test/feature/backward-compatibility-feature.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'node:events';
2 | import { Engine } from '../../src/index.js';
3 |
4 | Feature('Backward compatability', () => {
5 | Scenario('Version 13 state without process environment', () => {
6 | let engine, listener;
7 | Given('a bpmn source that access environment variables', () => {
8 | const source = `
9 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
31 |
32 |
33 | `;
34 |
35 | engine = Engine({
36 | name: 'Engine feature',
37 | source,
38 | settings: {
39 | mySettings: { bar: 'baz' },
40 | },
41 | services: {
42 | serviceFn() {},
43 | },
44 | });
45 | });
46 |
47 | And('a listener', () => {
48 | listener = new EventEmitter();
49 | });
50 |
51 | let waiting;
52 | And('listening once for wait', () => {
53 | waiting = new Promise((resolve) => {
54 | listener.once('wait', (api) => {
55 | resolve(api);
56 | });
57 | });
58 | });
59 |
60 | When('source is executed', () => {
61 | engine.execute({ listener });
62 | });
63 |
64 | And('user task is in a waiting state', async () => {
65 | await waiting;
66 | });
67 |
68 | let state;
69 | Then('process state is saved without environment', async () => {
70 | await engine.stop();
71 |
72 | state = await engine.getState();
73 |
74 | expect(state).to.have.property('state', 'stopped');
75 | expect(state.definitions).to.have.length(1);
76 | expect(state.definitions[0].source).to.be.ok;
77 | expect(state.definitions[0].execution.processes[0]).to.be.ok;
78 | });
79 |
80 | Given('in version 13 and below process state environment output is shared with definition', () => {
81 | state.engineVersion = '13.0.0';
82 | state.definitions[0].environment.output = state.definitions[0].execution.processes[0].environment.output;
83 | });
84 |
85 | And('process state environment is not present', () => {
86 | delete state.definitions[0].execution.processes[0].environment;
87 | });
88 |
89 | let recovered;
90 | When('engine is recovered with old state in latest version', () => {
91 | const upgradedState = upgradeStateToVersion14(state);
92 | recovered = Engine({
93 | name: 'Recovered engine',
94 | }).recover(upgradedState);
95 | });
96 |
97 | let execution;
98 | And('resumed', async () => {
99 | execution = await recovered.resume();
100 | });
101 |
102 | let ended;
103 | And('user task is signaled', () => {
104 | ended = recovered.waitFor('end');
105 |
106 | execution.signal({ id: 'task' });
107 | });
108 |
109 | Then('run completes', async () => {
110 | await ended;
111 | expect(execution.environment.output.result).to.deep.include({
112 | foo: 'bar',
113 | bar: 'baz',
114 | fields: 'run.execute',
115 | content: 'theProcess',
116 | });
117 | expect(execution.environment.output.result).to.have.property('properties');
118 | });
119 | });
120 | });
121 |
122 | function upgradeStateToVersion14(state) {
123 | const stateVersion = getSemverVersion(state.engineVersion);
124 | if (!stateVersion || stateVersion.major >= 14) return state;
125 |
126 | return polyfillProcessEnvironment(state);
127 | }
128 |
129 | function polyfillProcessEnvironment(state) {
130 | if (!state.definitions?.length) return state;
131 |
132 | const polyfilledState = JSON.parse(JSON.stringify(state));
133 | for (const definition of polyfilledState.definitions) {
134 | if (!definition.environment) continue;
135 | if (!definition.execution) continue;
136 | if (!definition.execution.processes) continue;
137 |
138 | for (const bp of definition.execution.processes) {
139 | addProcessEnvironment(definition.environment, bp);
140 | }
141 | }
142 |
143 | return polyfilledState;
144 | }
145 |
146 | function addProcessEnvironment(environment, processState) {
147 | processState.environment = JSON.parse(JSON.stringify(environment));
148 | }
149 |
150 | function getSemverVersion(version) {
151 | if (typeof version !== 'string') return;
152 | const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
153 | if (!match) return;
154 | const [, major, minor, patch] = match;
155 | return {
156 | major: Number(major),
157 | minor: Number(minor),
158 | patch: Number(patch),
159 | };
160 | }
161 |
--------------------------------------------------------------------------------
/test/feature/engine-output-feature.js:
--------------------------------------------------------------------------------
1 | import { Engine } from '../../src/index.js';
2 |
3 | Feature('Engine output', () => {
4 | Scenario('Process completes with output', () => {
5 | let engine;
6 | Given('a process with output', () => {
7 | const source = `
8 |
9 |
10 |
11 |
16 |
17 |
18 | `;
19 |
20 | engine = new Engine({ source });
21 | });
22 |
23 | let end;
24 | When('ran', () => {
25 | end = engine.waitFor('end');
26 | engine.execute();
27 | });
28 |
29 | Then('run completes', () => {
30 | return end;
31 | });
32 |
33 | And('engine state contain hoisted process output', async () => {
34 | expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
35 | });
36 |
37 | Given('a process with call activity and a called process', () => {
38 | const source = `
39 |
40 |
41 |
42 |
43 |
44 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
60 | `;
61 |
62 | engine = new Engine({ source });
63 | });
64 |
65 | When('ran', () => {
66 | end = engine.waitFor('end');
67 | engine.execute();
68 | });
69 |
70 | Then('run completes', () => {
71 | return end;
72 | });
73 |
74 | And('engine state contain hoisted main process output', async () => {
75 | expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
76 | });
77 | });
78 |
79 | Scenario('process fails', () => {
80 | let engine;
81 | Given('a process with output', () => {
82 | const source = `
83 |
84 |
85 |
86 |
94 |
95 |
96 | `;
97 |
98 | engine = new Engine({ source });
99 | });
100 |
101 | let errored;
102 | When('ran', () => {
103 | errored = engine.waitFor('error');
104 | engine.execute();
105 | });
106 |
107 | Then('run fails', () => {
108 | return errored;
109 | });
110 |
111 | And('engine state contain hoisted process output', async () => {
112 | expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
113 | });
114 | });
115 | });
116 |
--------------------------------------------------------------------------------
/test/feature/issues/issue-163-feature.js:
--------------------------------------------------------------------------------
1 | import { Engine } from '../../../src/index.js';
2 | import * as factory from '../../helpers/factory.js';
3 |
4 | Feature('issue 163', () => {
5 | Scenario('Get sub process postponed', () => {
6 | let engine, execution;
7 | When('one usertask and two user tasks in sub process and finally a user task', async () => {
8 | const source = factory.resource('issue-163.bpmn');
9 |
10 | engine = Engine({
11 | source,
12 | });
13 |
14 | execution = await engine.execute();
15 | });
16 |
17 | let postponed;
18 | Then('execution waits for both first user task', () => {
19 | postponed = execution.getPostponed();
20 | expect(postponed).to.have.length(1);
21 | expect(postponed.find(({ type }) => type === 'bpmn:UserTask')).to.be.ok;
22 | });
23 |
24 | When('first user task is signaled', () => {
25 | execution.signal({ id: postponed[0].id });
26 | });
27 |
28 | Then('execution has started sub process', () => {
29 | postponed = execution.getPostponed();
30 | expect(postponed).to.have.length(1);
31 | expect(postponed.find(({ type }) => type === 'bpmn:SubProcess')).to.be.ok;
32 | });
33 |
34 | let subPostponed;
35 | And('first user task in sub process is waiting to be signaled', () => {
36 | subPostponed = postponed[0].getPostponed();
37 | expect(subPostponed).to.have.length(2);
38 | expect(subPostponed.find(({ type }) => type === 'bpmn:UserTask')).to.be.ok;
39 | });
40 |
41 | When('first subprocess user task is signaled', () => {
42 | execution.signal({ id: subPostponed.find(({ type }) => type === 'bpmn:UserTask').id });
43 | });
44 |
45 | Then('execution is still waiting for sub process', () => {
46 | postponed = execution.getPostponed();
47 | expect(postponed).to.have.length(1);
48 | expect(postponed.find(({ type }) => type === 'bpmn:SubProcess')).to.be.ok;
49 | });
50 |
51 | And('second user task in sub process is waiting to be signaled', () => {
52 | subPostponed = postponed[0].getPostponed();
53 | expect(subPostponed).to.have.length(2);
54 | expect(subPostponed.find(({ type }) => type === 'bpmn:UserTask')).to.be.ok;
55 | });
56 |
57 | When('second subprocess user task is signaled', () => {
58 | execution.signal({ id: subPostponed.find(({ type }) => type === 'bpmn:UserTask').id });
59 | });
60 |
61 | Then('execution is waiting for final user task', () => {
62 | postponed = execution.getPostponed();
63 | expect(postponed).to.have.length(1);
64 | expect(postponed.find(({ type }) => type === 'bpmn:UserTask')).to.be.ok;
65 | });
66 |
67 | let ended;
68 | When('final user task is signaled', () => {
69 | ended = engine.waitFor('end');
70 | execution.signal({ id: postponed[0].id });
71 | });
72 |
73 | Then('execution completes', () => {
74 | return ended;
75 | });
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/test/feature/issues/issue-172-feature.js:
--------------------------------------------------------------------------------
1 | import { Engine } from '../../../src/index.js';
2 |
3 | Feature('issue 172', () => {
4 | Scenario('Postpone intermediate throw event by formatting', () => {
5 | let engine;
6 | const agiCalls = [];
7 | Given('start event, intermediate throw event, and end event', () => {
8 | const source = `
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | `;
18 |
19 | engine = Engine({
20 | name: 'issue-172',
21 | source,
22 | services: {
23 | agi(...args) {
24 | return new Promise((resolve) =>
25 | process.nextTick(() => {
26 | agiCalls.push(args);
27 | resolve({ called: true });
28 | })
29 | );
30 | },
31 | },
32 | extensions: {
33 | dingdong(activity) {
34 | if (activity.type === 'bpmn:Process') return;
35 | return {
36 | activate() {
37 | activity.on(
38 | 'enter',
39 | async (elementApi) => {
40 | activity.broker.publish('format', 'run.format.start', { endRoutingKey: 'run.format.complete' });
41 | const formatting = await elementApi.environment.services.agi(elementApi.content);
42 | activity.broker.publish('format', 'run.format.complete', formatting);
43 | },
44 | { consumerTag: 'format-on-enter' }
45 | );
46 | },
47 | deactivate() {
48 | activity.broker.cancel('format-on-enter');
49 | },
50 | };
51 | },
52 | },
53 | });
54 | });
55 |
56 | let end;
57 | const activityEnded = [];
58 | When('executed', () => {
59 | end = engine.waitFor('end');
60 | engine.broker.subscribeTmp('event', 'activity.end', (_, message) => activityEnded.push(message.content), { noAck: true });
61 | return engine.execute();
62 | });
63 |
64 | Then('execution completed', () => {
65 | return end;
66 | });
67 |
68 | And('start event is formatted', () => {
69 | expect(agiCalls).to.have.length(3);
70 | expect(agiCalls[0][0].type).to.equal('bpmn:StartEvent');
71 | expect(activityEnded).to.have.length(3);
72 | expect(activityEnded[0].type).to.equal('bpmn:StartEvent');
73 | expect(activityEnded[0].called).to.be.true;
74 | });
75 |
76 | And('intermediate throw event is formatted', () => {
77 | expect(agiCalls[1][0].type).to.equal('bpmn:IntermediateThrowEvent');
78 | expect(activityEnded[1].type).to.equal('bpmn:IntermediateThrowEvent');
79 | expect(activityEnded[1].called).to.be.true;
80 | });
81 |
82 | And('end event is formatted', () => {
83 | expect(agiCalls[2][0].type).to.equal('bpmn:EndEvent');
84 | expect(activityEnded[2].type).to.equal('bpmn:EndEvent');
85 | expect(activityEnded[2].called).to.be.true;
86 | });
87 | });
88 | });
89 |
--------------------------------------------------------------------------------
/test/feature/issues/issue-187-feature.js:
--------------------------------------------------------------------------------
1 | import { Engine } from '../../../src/index.js';
2 | import * as factory from '../../helpers/factory.js';
3 | import { camundaBpmnModdle as camunda } from '../../helpers/testHelpers.js';
4 |
5 | const source = factory.resource('issue-187.bpmn');
6 |
7 | Feature('issue 187 - Complete condition in multi-instance loop with custom expression parser', () => {
8 | Scenario('user task is looped over list of handlers', () => {
9 | let engine;
10 | Given('start event, intermediate throw event, and end event', () => {
11 | engine = getEngine187();
12 | });
13 |
14 | let end;
15 | When('executed', () => {
16 | end = engine.waitFor('end');
17 | return engine.execute();
18 | });
19 |
20 | And('first manual task is signaled', () => {
21 | engine.execution.signal({ id: engine.execution.getPostponed()[0].id });
22 | });
23 |
24 | And('first user task is signaled', () => {
25 | engine.execution.signal({ id: engine.execution.getPostponed()[0].id });
26 | });
27 |
28 | let loopedTask;
29 | Then('looped user task is waiting', () => {
30 | loopedTask = engine.execution.getPostponed().find((api) => api.content.isMultiInstance);
31 | expect(loopedTask).to.be.ok;
32 | });
33 |
34 | When('first handler signals with pass', () => {
35 | engine.execution.signal({ executionId: loopedTask.getExecuting().pop().executionId, pass: true });
36 | });
37 |
38 | Then('first handler iterated user task is waiting', () => {
39 | loopedTask = engine.execution.getPostponed().find((api) => api.content.isMultiInstance);
40 | expect(loopedTask).to.be.ok;
41 | });
42 |
43 | When('second and third handler signals with pass', () => {
44 | engine.execution.signal({ executionId: loopedTask.getExecuting().pop().executionId, pass: true });
45 | engine.execution.signal({ executionId: loopedTask.getExecuting().pop().executionId, pass: true });
46 | });
47 |
48 | let task;
49 | Then('a modify manual task is waiting', () => {
50 | task = engine.execution.getPostponed().pop();
51 | expect(task).to.be.ok;
52 | });
53 |
54 | When('manual task is signalled', () => {
55 | engine.execution.signal({ id: task.id });
56 | });
57 |
58 | Then('execution loops back to first user task', () => {
59 | task = engine.execution.getPostponed().pop();
60 | expect(task).to.be.ok;
61 | });
62 |
63 | When('first user task is signalled again', () => {
64 | engine.execution.signal({ id: task.id });
65 | });
66 |
67 | Then('withdrawal task is waiting', () => {
68 | task = engine.execution.getPostponed().find(({ id }) => id === 'task_with_draw');
69 | expect(task).to.be.ok;
70 | });
71 |
72 | When('withdrawal task is signalled', () => {
73 | engine.execution.signal({ id: task.id });
74 | });
75 |
76 | Then('handler loop is cancelled', () => {
77 | expect(engine.execution.getPostponed().find((api) => api.content.isMultiInstance)).to.not.be.ok;
78 | });
79 |
80 | And('modify manual task is waiting again', () => {
81 | task = engine.execution.getPostponed().pop();
82 | expect(task.type).to.equal('bpmn:ManualTask');
83 | });
84 |
85 | When('manual task is signalled', () => {
86 | engine.execution.signal({ id: task.id });
87 | });
88 |
89 | Then('execution loops back to first user task', () => {
90 | task = engine.execution.getPostponed().pop();
91 | expect(task).to.be.ok;
92 | });
93 |
94 | When('first user task is signalled again', () => {
95 | engine.execution.signal({ id: task.id });
96 | });
97 |
98 | Then('first handler iterated user task is waiting again', () => {
99 | loopedTask = engine.execution.getPostponed().find((api) => api.content.isMultiInstance);
100 | expect(loopedTask).to.be.ok;
101 | });
102 |
103 | When('first handler decides to break', () => {
104 | engine.execution.signal({ executionId: loopedTask.getExecuting().pop().executionId, break: true });
105 | });
106 |
107 | Then('run completes', () => {
108 | return end;
109 | });
110 |
111 | When('running again', () => {
112 | engine = getEngine187();
113 | end = engine.waitFor('end');
114 | return engine.execute();
115 | });
116 |
117 | And('first manual and user tasks are signaled', () => {
118 | engine.execution.signal({ id: engine.execution.getPostponed()[0].id });
119 | engine.execution.signal({ id: engine.execution.getPostponed()[0].id });
120 | });
121 |
122 | When('all handlers signals with with reject', () => {
123 | loopedTask = engine.execution.getPostponed().find((api) => api.content.isMultiInstance);
124 |
125 | engine.execution.signal({ executionId: loopedTask.getExecuting().pop().executionId, pass: false });
126 | engine.execution.signal({ executionId: loopedTask.getExecuting().pop().executionId, pass: false });
127 | engine.execution.signal({ executionId: loopedTask.getExecuting().pop().executionId, pass: false });
128 | });
129 |
130 | Then('run completes', () => {
131 | return end;
132 | });
133 | });
134 | });
135 |
136 | function getEngine187() {
137 | const values = {};
138 | return new Engine({
139 | name: 'issue-187',
140 | moddleOptions: {
141 | camunda,
142 | },
143 | source,
144 | variables: {
145 | values,
146 | handlers: [{ id: 1 }, { id: 2 }, { id: 3 }],
147 | },
148 | services: {
149 | set_value: function setValue(key, value) {
150 | values[key] = value;
151 | },
152 | get_value(key) {
153 | return values[key];
154 | },
155 | get_handlers() {
156 | return Promise.resolve([{ id: 1 }, { id: 2 }, { id: 3 }]);
157 | },
158 | gen_remind_task() {},
159 | },
160 | extensions: {
161 | inputOutputExtension,
162 | },
163 | });
164 | }
165 |
166 | function inputOutputExtension(element) {
167 | if (element.type === 'bpmn:Process') return;
168 |
169 | let io;
170 | const resultVariable = element.behaviour.resultVariable;
171 | if (element.behaviour.extensionElements?.values) {
172 | const extendValues = element.behaviour.extensionElements.values;
173 | io = extendValues.reduce((result, extension) => {
174 | if (extension.$type === 'camunda:InputOutput') {
175 | result.input = extension.inputParameters;
176 | result.output = extension.outputParameters;
177 | }
178 | return result;
179 | }, {});
180 | }
181 |
182 | return {
183 | activate() {
184 | if (io) element.on('enter', onEnter, { consumerTag: 'format_on_enter' });
185 | element.on('end', onEnd, { consumerTag: 'format_on_end' });
186 | },
187 | deactivate() {
188 | element.broker.cancel('format_on_enter');
189 | element.broker.cancel('format_on_end');
190 | },
191 | };
192 |
193 | function onEnter(elementApi) {
194 | const input = io?.input?.reduce((result, { name, value }) => {
195 | result[name] = elementApi.resolveExpression(value);
196 | return result;
197 | }, {});
198 |
199 | element.broker.publish('format', 'run.io', { input });
200 | }
201 |
202 | function onEnd(elementApi) {
203 | if ('output' in elementApi.content) {
204 | elementApi.environment.output[resultVariable || elementApi.id] = elementApi.content.output;
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/test/feature/issues/issue-199-feature.js:
--------------------------------------------------------------------------------
1 | import { Engine } from '../../../src/index.js';
2 |
3 | const source = `
4 |
5 |
6 |
7 |
8 | Flow_1
9 |
10 |
11 |
12 | Flow_1
13 | Flow_2
14 | this.environment.services.log('hello from Script task 1');
15 | next();
16 |
17 |
18 |
19 | Flow_2
20 | Flow_3
21 |
22 |
23 |
24 | Flow_3
25 | Flow_4
26 | this.environment.services.log('hello from Script task 2');
27 | next();
28 |
29 |
30 |
31 | Flow_4
32 |
33 |
34 |
35 | `;
36 |
37 | Feature('issue 199 - Issue with Script Tasks After State Recovery in bpmn-engine', () => {
38 | Scenario('execute, recover resume with same engine instance', () => {
39 | let engine;
40 | Given('an engine with user task flanked by two script tasks', () => {
41 | engine = new Engine({
42 | name: 'first',
43 | source,
44 | services: {
45 | log() {},
46 | },
47 | });
48 | });
49 |
50 | let execution;
51 | When('executed', async () => {
52 | execution = await engine.execute();
53 | });
54 |
55 | Then('engine should be in a running state', () => {
56 | expect(execution.state).to.equal('running');
57 | });
58 |
59 | let end;
60 | When('user task is signalled', () => {
61 | end = engine.waitFor('end');
62 | execution.signal({ id: 'UserTask_1' });
63 | });
64 |
65 | Then('run completed', () => {
66 | return end;
67 | });
68 |
69 | Given('a new engine instance', () => {
70 | engine = new Engine({
71 | name: 'second',
72 | source,
73 | services: {
74 | log() {},
75 | },
76 | });
77 | });
78 |
79 | let state;
80 | When('executed and get state', async () => {
81 | execution = await engine.execute();
82 | state = execution.getState();
83 | });
84 |
85 | Then('engine should be in a running state', () => {
86 | expect(execution.state).to.equal('running');
87 | });
88 |
89 | Given('execution is stopped', () => {
90 | return execution.stop();
91 | });
92 |
93 | When('same instance is recovered with options', () => {
94 | engine.recover(state, {
95 | services: {
96 | log() {},
97 | },
98 | });
99 | });
100 |
101 | And('resumed', () => {
102 | engine.resume();
103 | });
104 |
105 | Then('engine is still in a running state', () => {
106 | expect(engine.execution.state).to.equal('running');
107 | });
108 |
109 | When('resumed execution user task is signalled', () => {
110 | end = engine.waitFor('end');
111 | engine.execution.signal({ id: 'UserTask_1' });
112 | });
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/test/feature/scripts-feature.js:
--------------------------------------------------------------------------------
1 | import { Engine } from '../../src/index.js';
2 |
3 | Feature('Scripts', () => {
4 | Scenario('Process with scripts', () => {
5 | let engine, source;
6 | Given('a bpmn source with empty script task', () => {
7 | source = `
8 |
10 |
11 |
12 |
13 | `;
14 | });
15 |
16 | And('an engine loaded with disabled dummy script', () => {
17 | engine = Engine({
18 | name: 'Script feature',
19 | source,
20 | disableDummyScript: true,
21 | });
22 | });
23 |
24 | let error;
25 | When('source is executed', async () => {
26 | error = await engine.execute().catch((err) => err);
27 | });
28 |
29 | Then('execution errored', () => {
30 | expect(error).to.match(/not registered/);
31 | });
32 |
33 | When('ran again falsy disable dummy script', () => {
34 | engine = Engine({
35 | name: 'Script feature',
36 | source,
37 | disableDummyScript: false,
38 | });
39 | });
40 |
41 | let end;
42 | When('source is executed', async () => {
43 | end = engine.waitFor('end');
44 | error = await engine.execute().catch((err) => err);
45 | });
46 |
47 | Then('execution completed', () => {
48 | return end;
49 | });
50 | });
51 |
52 | Scenario('Process with setTimeout in inline scripts task', () => {
53 | let engine, source;
54 | Given('a bpmn source with script task with setTimeout', () => {
55 | source = `
56 |
58 |
59 |
60 |
65 |
66 |
67 | `;
68 | });
69 |
70 | let end;
71 | When('source is executed', () => {
72 | engine = Engine({
73 | name: 'Script feature',
74 | source,
75 | disableDummyScript: true,
76 | });
77 |
78 | end = engine.waitFor('end');
79 |
80 | engine.execute();
81 | });
82 |
83 | Then('execution completed', () => {
84 | return end;
85 | });
86 | });
87 |
88 | Scenario('Process with long running timer can be stopped', () => {
89 | let engine, source;
90 | Given('a bpmn source with script task with setTimeout', () => {
91 | source = `
92 |
94 |
95 |
96 |
101 |
102 |
103 | `;
104 | });
105 |
106 | When('source is executed', () => {
107 | engine = Engine({
108 | name: 'Script feature',
109 | source,
110 | disableDummyScript: true,
111 | });
112 |
113 | engine.execute();
114 | });
115 |
116 | Then('timer is running', () => {
117 | expect(engine.environment.timers.executing).to.have.length(1);
118 | });
119 |
120 | When('execution is stopped', () => {
121 | return engine.stop();
122 | });
123 |
124 | Then('timer is stopped', () => {
125 | expect(engine.environment.timers.executing).to.have.length(0);
126 | });
127 | });
128 | });
129 |
--------------------------------------------------------------------------------
/test/feature/shake-feature.js:
--------------------------------------------------------------------------------
1 | import * as testHelpers from '../helpers/testHelpers.js';
2 | import { Engine } from '../../src/index.js';
3 |
4 | Feature('Shake', () => {
5 | Scenario('Determine run sequences for an activity', () => {
6 | let engine, source;
7 | Given('a bpmn source with user tasks', () => {
8 | source = `
9 |
12 |
13 |
14 |
15 |
16 |
17 | `;
18 | });
19 |
20 | let definition;
21 | And('an engine loaded with extension for fetching form and saving output', async () => {
22 | engine = Engine({
23 | name: 'Shake feature',
24 | sourceContext: await testHelpers.context(source),
25 | });
26 |
27 | [definition] = await engine.getDefinitions();
28 | });
29 |
30 | let result;
31 | When('start task is shaken', () => {
32 | result = definition.shake('task1');
33 | });
34 |
35 | Then('run sequences are determined', () => {
36 | expect(result).to.have.property('task1').with.length(1);
37 | expect(result.task1[0]).to.have.property('sequence').with.length(3);
38 | });
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/test/helpers/factory.js:
--------------------------------------------------------------------------------
1 | import fs from 'node:fs';
2 | import path from 'node:path';
3 |
4 | export function valid(definitionId) {
5 | if (!definitionId) definitionId = 'valid';
6 | return `
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | next(null, true);
18 |
19 |
20 | `;
21 | }
22 |
23 | export function invalid() {
24 | return `
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | next(null, false);
35 |
36 |
37 | `;
38 | }
39 |
40 | export function userTask(userTaskId = 'userTask', definitionId = 'Def_1') {
41 | return `
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | input_1
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | `;
66 | }
67 |
68 | export function multipleInbound() {
69 | return `
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | `;
84 | }
85 |
86 | export function resource(name) {
87 | return fs.readFileSync(path.join('./test/resources', name));
88 | }
89 |
--------------------------------------------------------------------------------
/test/helpers/testHelpers.js:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'node:module';
2 | import { fileURLToPath } from 'node:url';
3 |
4 | import BpmnModdle from 'bpmn-moddle';
5 | import * as Elements from 'bpmn-elements';
6 | import Logger from '../../src/Logger.js';
7 | import serializer, { TypeResolver } from 'moddle-context-serializer';
8 |
9 | const nodeRequire = createRequire(fileURLToPath(import.meta.url));
10 | export const camundaBpmnModdle = nodeRequire('camunda-bpmn-moddle/resources/camunda.json');
11 |
12 | export async function context(source, options) {
13 | const logger = Logger('test-helpers:context');
14 | const moddleCtx = await moddleContext(source, options);
15 |
16 | if (moddleCtx.warnings) {
17 | moddleCtx.warnings.forEach(({ error, message, element, property }) => {
18 | if (error) return logger.error(message);
19 | logger.error(`<${element.id}> ${property}:`, message);
20 | });
21 | }
22 |
23 | const types = TypeResolver({
24 | ...Elements,
25 | ...options?.elements,
26 | });
27 |
28 | return serializer(moddleCtx, types, options?.extendFn);
29 | }
30 |
31 | export function moddleContext(source, options) {
32 | const bpmnModdle = new BpmnModdle(options);
33 | return bpmnModdle.fromXML(Buffer.isBuffer(source) ? source.toString() : source);
34 | }
35 |
36 | export function serializeModdleContext({ rootElement, rootHandler, elementsById, references, warnings }) {
37 | const serializedRoot = JSON.parse(JSON.stringify(rootElement || rootHandler.element));
38 |
39 | const clonedContext = {
40 | rootElement: serializedRoot,
41 | elementsById: JSON.parse(JSON.stringify(elementsById)),
42 | references: JSON.parse(JSON.stringify(references)),
43 | warnings: warnings.slice(),
44 | };
45 | return clonedContext;
46 | }
47 |
--------------------------------------------------------------------------------
/test/issues-test.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'node:events';
2 | import { Engine } from '../src/index.js';
3 | import * as factory from './helpers/factory.js';
4 |
5 | describe('issues', () => {
6 | describe('issue 19 - save state', () => {
7 | it('make sure there is something to save on activity start event', async () => {
8 | const messages = [];
9 | const services = {
10 | timeout: (cb, time) => {
11 | setTimeout(cb, time);
12 | },
13 | log: (message) => {
14 | messages.push(message);
15 | },
16 | };
17 |
18 | const listener = new EventEmitter();
19 | const engine = new Engine({
20 | name: 'Engine',
21 | source: factory.resource('issue-19.bpmn'),
22 | listener,
23 | });
24 |
25 | const states = [];
26 |
27 | listener.on('activity.start', (activity, engineApi) => {
28 | states.push(engineApi.getState());
29 | });
30 |
31 | const end = engine.waitFor('end');
32 |
33 | await engine.execute({
34 | listener,
35 | variables: {
36 | timeout: 100,
37 | },
38 | services,
39 | });
40 |
41 | await end;
42 |
43 | expect(states).to.have.length(7);
44 |
45 | for (const state of states) {
46 | expect(state).to.have.property('definitions').with.length(1);
47 | expect(state.definitions[0]).to.have.property('execution');
48 | expect(state.definitions[0].execution).to.have.property('processes').with.length(1);
49 | expect(state.definitions[0].execution.processes[0]).to.have.property('execution');
50 | expect(state.definitions[0].execution.processes[0].execution).to.have.property('children').with.length(7);
51 |
52 | const children = state.definitions[0].execution.processes[0].execution.children;
53 |
54 | expect(children.map(({ id }) => id)).to.eql(['Start', 'Parallel1', 'Task_A', 'Task_B', 'Parallel2', 'Task_C', 'End']);
55 | }
56 |
57 | let [Start, Parallel1, Task_A, Task_B, Parallel2, Task_C, End] = states[0].definitions[0].execution.processes[0].execution.children;
58 | expect(Start, 'state 0 Start').to.have.property('status', 'started');
59 | expect(Parallel1, 'state 0 Parallel1').to.not.have.property('status');
60 |
61 | [Start, Parallel1, Task_A, Task_B, Parallel2, Task_C, End] = states[1].definitions[0].execution.processes[0].execution.children;
62 | expect(Start, 'state 1 Start').to.have.property('status', 'end');
63 | expect(Parallel1, 'state 1 Parallel1').to.have.property('status', 'started');
64 | expect(Task_A, 'state 1 Task_A').to.not.have.property('status');
65 |
66 | [Start, Parallel1, Task_A, Task_B, Parallel2, Task_C, End] = states[2].definitions[0].execution.processes[0].execution.children;
67 | expect(Parallel1, 'state 2 Parallel1').to.have.property('status', 'end');
68 | expect(Task_A, 'state 2 Task_A').to.have.property('status', 'started');
69 | expect(Task_B, 'state 2 Task_B').to.not.have.property('status');
70 |
71 | [Start, Parallel1, Task_A, Task_B, Parallel2, Task_C, End] = states[3].definitions[0].execution.processes[0].execution.children;
72 | expect(Parallel1, 'state 3 Parallel1').to.have.property('status', 'end');
73 | expect(Task_A, 'state 3 Task_A').to.not.have.property('status');
74 | expect(Task_B, 'state 3 Task_B').to.have.property('status', 'started');
75 | expect(Parallel2, 'state 3 Parallel2').to.not.have.property('status');
76 |
77 | [Start, Parallel1, Task_A, Task_B, Parallel2, Task_C, End] = states[4].definitions[0].execution.processes[0].execution.children;
78 | expect(Task_A, 'state 4 Task_A').to.not.have.property('status');
79 | expect(Task_B, 'state 4 Task_B').to.have.property('status', 'end');
80 | expect(Parallel2, 'state 4 Parallel2').to.have.property('status', 'started');
81 |
82 | [Start, Parallel1, Task_A, Task_B, Parallel2, Task_C, End] = states[5].definitions[0].execution.processes[0].execution.children;
83 | expect(Parallel2, 'state 5 Parallel2').to.have.property('status', 'end');
84 | expect(Task_C, 'state 5 Task_C').to.have.property('status', 'started');
85 | expect(End, 'state 5 End').to.not.have.property('status');
86 |
87 | [Start, Parallel1, Task_A, Task_B, Parallel2, Task_C, End] = states[6].definitions[0].execution.processes[0].execution.children;
88 | expect(Task_C, 'state 6 Task_C').to.have.property('status', 'end');
89 | expect(End, 'state 6 End').to.have.property('status', 'started');
90 | });
91 | });
92 |
93 | describe('issue 74 - setting engine output from script', () => {
94 | const source = `
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | environment.output.test = "set from script"; next()
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | `;
119 |
120 | it('engine output is not altered during execution', async () => {
121 | const listener = new EventEmitter();
122 | const engine = new Engine({
123 | name: 'Engine',
124 | source,
125 | listener,
126 | });
127 |
128 | listener.on('activity.wait', (api) => {
129 | // When we get to the UserTask, the ScriptTask should be completed and set the output
130 | expect(api.environment.output.test, 'execution output').to.equal('set from script');
131 | expect(engine.environment.output.test, 'engine output').to.be.undefined;
132 | });
133 |
134 | await engine.execute({ listener });
135 | });
136 |
137 | it('merges execution output to engine output from a script at engine completion', async () => {
138 | const listener = new EventEmitter();
139 | const engine = new Engine({
140 | name: 'Engine',
141 | source,
142 | listener,
143 | });
144 |
145 | listener.on('activity.wait', (exec) => exec.signal());
146 |
147 | await engine.execute({ listener });
148 |
149 | expect(engine.environment.output.test).to.equal('set from script');
150 | });
151 |
152 | it('resumes with correct state', async () => {
153 | let state;
154 |
155 | const listener = new EventEmitter();
156 | const engine = new Engine({
157 | name: 'Engine',
158 | source,
159 | listener,
160 | });
161 |
162 | const resumeListener = new EventEmitter();
163 | const resumeEngine = new Engine({
164 | name: 'Engine',
165 | source,
166 | listener: resumeListener,
167 | });
168 |
169 | resumeListener.on('activity.wait', (exec) => {
170 | expect(exec.environment.output.test).to.equal('set from script');
171 | });
172 |
173 | listener.on('activity.wait', (exec) => {
174 | expect(exec.environment.output.test).to.equal('set from script');
175 |
176 | engine.getState().then((s) => {
177 | state = s;
178 | engine.stop();
179 | });
180 | });
181 |
182 | await engine.execute({ listener });
183 |
184 | resumeEngine.recover(state);
185 |
186 | await resumeEngine.resume({ listener: resumeListener });
187 | });
188 | });
189 | });
190 |
--------------------------------------------------------------------------------
/test/lib/JavaScripts-test.js:
--------------------------------------------------------------------------------
1 | import { JavaScripts } from '../../src/index.js';
2 |
3 | describe('JavaScripts', () => {
4 | it('can be invoked without new', () => {
5 | JavaScripts();
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/test/resources/JsExtension.js:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'module';
2 | import { fileURLToPath } from 'url';
3 |
4 | const safePattern = /[./\\#*:\s]/g;
5 |
6 | const nodeRequire = createRequire(fileURLToPath(import.meta.url));
7 | export const moddleOptions = nodeRequire('./js-bpmn-moddle.json');
8 |
9 | export function extension(activity, context) {
10 | const resultVariable = ResultVariableIo(activity, context);
11 | const formKey = FormKey(activity, context);
12 |
13 | return {
14 | type: 'js:extension',
15 | extensions: { resultVariable, formKey },
16 | activate(msg) {
17 | if (resultVariable) resultVariable.activate(msg);
18 | if (formKey) formKey.activate(msg);
19 | },
20 | deactivate() {
21 | if (resultVariable) resultVariable.deactivate();
22 | if (formKey) formKey.deactivate();
23 | },
24 | };
25 | }
26 |
27 | function ResultVariableIo(activity, context) {
28 | const { id, logger, behaviour } = activity;
29 | const { result } = behaviour;
30 | if (!result) return;
31 |
32 | const { broker } = activity;
33 | const { environment } = context;
34 |
35 | const type = 'js:resultvariable';
36 | let activityConsumer;
37 |
38 | return {
39 | type,
40 | activate,
41 | deactivate,
42 | };
43 |
44 | function deactivate() {
45 | if (activityConsumer) activityConsumer = activityConsumer.cancel();
46 | }
47 |
48 | function activate() {
49 | if (activityConsumer) return;
50 | activityConsumer = broker.subscribeTmp('event', 'activity.end', onActivityEnd, { noAck: true });
51 | }
52 |
53 | function onActivityEnd(_, message) {
54 | const resultName = environment.resolveExpression(result, message.content);
55 | logger.debug(`<${id}>`, 'js:extension save to', `"${resultName}"`);
56 |
57 | environment.output[resultName] = message.content.output;
58 | }
59 | }
60 |
61 | function FormKey(activity, context) {
62 | const { id, logger, behaviour } = activity;
63 | const { formKey } = behaviour;
64 | if (!formKey) return;
65 |
66 | const { broker } = activity;
67 | const { environment } = context;
68 |
69 | const type = 'js:formkey';
70 | const safeType = brokerSafeId(type).toLowerCase();
71 | let activityConsumer;
72 |
73 | return {
74 | type,
75 | activate,
76 | deactivate,
77 | };
78 |
79 | function deactivate() {
80 | if (activityConsumer) activityConsumer = activityConsumer.cancel();
81 | }
82 |
83 | function activate() {
84 | if (activityConsumer) return;
85 | activityConsumer = broker.subscribeTmp('event', 'activity.start', onActivityStart, { noAck: true });
86 | }
87 |
88 | function onActivityStart(_, message) {
89 | const formKeyValue = environment.resolveExpression(formKey, message);
90 | logger.debug(`<${id}> apply form`);
91 |
92 | broker.publish('format', `run.${safeType}.start`, {
93 | form: {
94 | type,
95 | key: formKeyValue,
96 | },
97 | });
98 | }
99 | }
100 |
101 | function brokerSafeId(id) {
102 | return id.replace(safePattern, '_');
103 | }
104 |
--------------------------------------------------------------------------------
/test/resources/README.md:
--------------------------------------------------------------------------------
1 | # Resources
2 |
3 | All modeled with [Camunda modeler](https://camunda.org/download/modeler/).
4 |
--------------------------------------------------------------------------------
/test/resources/bound-error-and-timer.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow1
6 |
7 |
8 |
9 | flow3
10 |
11 |
12 |
13 | flow1
14 | flow2
15 |
21 |
22 |
23 |
24 | flow2
25 |
26 |
27 |
28 | flow3
29 |
30 |
31 |
32 | SequenceFlow_0blswmr
33 |
34 | PT0.05S
35 |
36 |
37 |
38 |
39 | SequenceFlow_0blswmr
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/test/resources/bound-error.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow1
6 |
7 |
8 |
9 | flow3
10 |
11 |
12 |
13 | flow1
14 | flow2
15 | if (!variables.input) {
16 | next(new Error("Input is missing"));
17 | } else if (variables.input === 2) {
18 | } else {
19 | next();
20 | }
21 |
22 |
23 |
24 | flow2
25 |
26 |
27 |
28 | flow3
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/test/resources/boundary-non-interupting-timer.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_0pw2ggp
6 |
7 |
8 | SequenceFlow_0pw2ggp
9 | SequenceFlow_0ipd9l9
10 |
11 |
12 |
13 | SequenceFlow_1t341i2
14 |
15 |
16 |
17 | SequenceFlow_1muatwf
18 |
19 | PT0.01S
20 |
21 |
22 |
23 |
24 |
25 | SequenceFlow_0ipd9l9
26 | SequenceFlow_1muatwf
27 | SequenceFlow_1t341i2
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/test/resources/boundary-timeout.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow1
6 |
7 |
8 | flow4
9 |
10 |
11 |
12 |
13 |
14 |
15 | flow1
16 | flow2
17 |
18 |
19 | flow3
20 |
21 | PT0.1S
22 |
23 |
24 |
25 |
26 |
27 |
28 | flow3
29 | flow2
30 | flow4
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/test/resources/call-activity.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Flow_1j9hgwz
10 |
11 |
12 | Flow_1j9hgwz
13 | Flow_1w33u3r
14 |
15 |
16 | Flow_1w33u3r
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/test/resources/conditional-bound-js-event.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | to-task
6 |
7 |
8 |
9 | to-end
10 |
11 |
12 |
13 | to-task
14 | to-end
15 |
16 |
17 | to-end-cond
18 |
19 |
20 |
21 | to-end-cond
22 |
23 | next(null, properties.type === 'signal');
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/test/resources/diagram_1.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_0jv2k1t
6 |
7 |
8 |
9 | SequenceFlow_0y5jed8
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | http://example.com/api/${name}/${index}
19 | ${true}
20 |
21 |
22 | ${result[0].statusCode}
23 |
24 | get
25 |
26 |
27 | SequenceFlow_0jv2k1t
28 | SequenceFlow_0y5jed8
29 |
30 | 10
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/test/resources/forms.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | SequenceFlow_0lj5hpw
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ${variables.suggestedStartDate}
27 |
28 |
29 |
30 | SequenceFlow_0lj5hpw
31 | SequenceFlow_0apdac1
32 |
33 |
34 | SequenceFlow_0apdac1
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/test/resources/issue-139.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flow_0dk0y7o
6 |
7 |
8 | Flow_0dk0y7o
9 | Flow_076cdg5
10 |
11 |
12 | DataObjectReference_11hq8qm
13 | Property_0qusu4o
14 |
15 |
16 |
17 |
18 | Flow_076cdg5
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/test/resources/issue-163.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flow_0jrmoo3
6 |
7 |
8 |
9 | Flow_0jrmoo3
10 | Flow_0qs3el8
11 |
12 |
13 | Flow_0qs3el8
14 | Flow_1rh83yq
15 |
16 | Flow_00x1izw
17 |
18 |
19 | Flow_00x1izw
20 |
21 |
22 |
23 |
24 |
25 | Flow_1rh83yq
26 | Flow_0tefdeh
27 |
28 |
29 |
30 | Flow_0tefdeh
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/test/resources/issue-19.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | startToParallel1
6 |
7 |
8 | startToParallel1
9 | Parallel1ToTaskA
10 | Parallel1ToTaskB
11 |
12 |
13 | Parallel1ToTaskA
14 | TaskAToParallel2
15 |
16 |
17 | Parallel1ToTaskB
18 | TaskBToParallel2
19 | { this.environment.services.log('Resume Task B!'); next() }, this.environment.variables.timeout);
22 | ]]>
23 |
24 |
25 | TaskAToParallel2
26 | TaskBToParallel2
27 | Parallel2ToTaskC
28 |
29 |
30 | Parallel2ToTaskC
31 | TaskCToEnd
32 |
33 |
34 | TaskCToEnd
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/test/resources/issue-4.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_1
6 |
7 |
8 |
9 | SequenceFlow_1sxkbig
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | send-email
20 |
21 |
22 |
23 |
24 |
25 |
26 | SequenceFlow_1
27 | SequenceFlow_2
28 |
29 |
30 | SequenceFlow_2
31 | SequenceFlow_1sxkbig
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/test/resources/js-bpmn-moddle.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Node bpmn-engine",
3 | "uri": "http://paed01.github.io/bpmn-engine/schema/2017/08/bpmn",
4 | "prefix": "js",
5 | "xml": {
6 | "tagAlias": "lowerCase"
7 | },
8 | "types": [
9 | {
10 | "name": "Task",
11 | "isAbstract": true,
12 | "extends": ["bpmn:Task"],
13 | "properties": [
14 | {
15 | "name": "result",
16 | "isAttr": true,
17 | "type": "String"
18 | }
19 | ]
20 | },
21 | {
22 | "name": "Output",
23 | "superClass": ["Element"]
24 | },
25 | {
26 | "name": "Collectable",
27 | "isAbstract": true,
28 | "extends": ["bpmn:MultiInstanceLoopCharacteristics"],
29 | "properties": [
30 | {
31 | "name": "collection",
32 | "isAttr": true,
33 | "type": "String"
34 | },
35 | {
36 | "name": "elementVariable",
37 | "isAttr": true,
38 | "type": "String"
39 | }
40 | ]
41 | },
42 | {
43 | "name": "FormSupported",
44 | "isAbstract": true,
45 | "extends": ["bpmn:StartEvent", "bpmn:UserTask"],
46 | "properties": [
47 | {
48 | "name": "formKey",
49 | "isAttr": true,
50 | "type": "String"
51 | }
52 | ]
53 | }
54 | ]
55 | }
56 |
--------------------------------------------------------------------------------
/test/resources/lanes.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | mainStartEvent
13 | task1
14 | mainEndEvent
15 | intermediate
16 |
17 |
18 |
19 | flow1
20 |
21 |
22 |
23 |
24 | Empty
25 | I'm done
26 | 10
27 |
28 |
29 | flow1
30 | flow2
31 |
32 |
33 | flow3
34 |
35 |
36 | flow2
37 | flow3
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | flow-p-1
47 |
48 |
49 |
50 | flow-p-1
51 | flow-p-2
52 |
53 |
54 |
55 |
56 | Done!
57 |
58 | `${messageToMain} Aswell!`;
59 |
60 |
61 |
62 | flow-p-2
63 | flow-p-3
64 |
65 |
66 | flow-p-3
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
--------------------------------------------------------------------------------
/test/resources/loop.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow1
6 |
7 |
8 |
9 | flow2
10 | flow5
11 | flow3
12 |
13 |
14 |
15 | flow5
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | flow3
24 | flow4
25 |
27 |
28 |
29 | flow1
30 | flow4
31 | flow2
32 | next();
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/test/resources/messaging.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | SequenceFlow_146nqos
11 |
12 |
13 | SequenceFlow_1p64wos
14 |
15 |
16 |
17 |
18 | ${variables.user}
19 | ${variables.to}
20 |
21 |
22 | SequenceFlow_1ivyh5l
23 | SequenceFlow_1p64wos
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | SequenceFlow_146nqos
32 | SequenceFlow_1ivyh5l
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | SequenceFlow_1cdyq00
42 |
43 |
44 |
45 |
46 | ${from}
47 | ${to}
48 |
49 |
50 | SequenceFlow_1cdyq00
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/test/resources/multiple-endEvents.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow1
6 |
7 |
8 |
9 | flow2
10 |
11 |
12 |
13 |
14 | flow3
15 | flow4
16 |
18 |
19 |
20 |
21 | flow5
22 |
23 |
24 |
25 |
26 | flow6
27 | flow7
28 |
30 |
31 |
32 | flow7
33 |
34 |
35 |
36 | flow1
37 | flow2
38 | flow3
39 |
40 |
41 | flow4
42 | flow5
43 | flow6
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/test/resources/multiple-multiple-inbound.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_0q2oaww
6 |
7 |
8 |
9 |
10 | ${variables.condition1}
11 | ${variables.condition2}
12 |
13 |
14 | SequenceFlow_0q2oaww
15 | default-flow-2
16 | default-flow-1
17 | taskflow-1
18 |
19 |
20 |
21 |
22 |
23 | ${true}
24 |
25 |
26 | taskflow-1
27 | condflow-1
28 | default-flow-1
29 |
30 |
31 |
32 |
33 |
34 | ${true}
35 |
36 |
37 | condflow-1
38 | condflow-2
39 | default-flow-2
40 |
41 |
42 | ${variables.tookCondition1}
43 |
44 |
45 | condflow-2
46 |
47 |
48 | ${variables.tookCondition2}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/test/resources/one-or-the-other.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flow_0geshlo
6 |
7 |
8 |
9 | Flow_0geshlo
10 | Flow_1scac6z
11 | Flow_1sz3hry
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Flow_0v64u42
20 |
21 |
22 | Flow_07mced2
23 | Flow_0jihxmk
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Flow_1scac6z
34 | Flow_07mced2
35 |
36 |
37 | Flow_1sz3hry
38 | Flow_1kngrgq
39 |
40 |
41 |
42 | Flow_0jihxmk
43 | Flow_1l5wpwy
44 | Flow_0v64u42
45 |
46 |
47 |
48 | Flow_1kngrgq
49 | Flow_1l5wpwy
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/test/resources/send-signal.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | to-set-spot-price
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ${content.output.form.newPrice}
15 |
16 |
17 | to-set-spot-price
18 | to-signal-new-price
19 |
20 |
21 | to-end
22 |
23 |
24 |
25 |
26 | ${environment.output.spotPrice}
27 | ${environment.output.spotPrice}
28 |
29 |
30 | to-signal-new-price
31 | to-end
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/test/resources/service-task-io.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_0d7zaqt
6 |
7 |
8 |
9 | SequenceFlow_0sekure
10 |
11 |
12 |
13 |
14 |
15 |
16 | variables.apiPath
17 |
18 | ${true}
19 |
20 |
21 |
22 | ${result[1]}
23 |
24 |
25 |
26 |
27 |
28 | SequenceFlow_0d7zaqt
29 | SequenceFlow_0sekure
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/test/resources/service-task-operation.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | inputMessage
12 | outputMessage
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/test/resources/service-task.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow1
6 |
7 |
8 |
9 | flow2
10 |
11 |
12 |
13 | flow1
14 | flow2
15 |
16 |
17 | flow3
18 |
19 |
20 |
21 |
22 | flow3
23 |
24 |
25 |
26 | flow4
27 |
28 | PT0.05S
29 |
30 |
31 |
32 | flow4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/test/resources/simple-task.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | PT0.01S
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/test/resources/sub-process.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow1
6 |
7 |
8 |
9 | flow1
10 | flow2
11 |
12 |
13 | subFlow1
14 |
15 |
16 | subFlow1
17 |
19 |
20 |
21 |
22 | flow2
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/test/resources/succeeding-joins.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_01pzds7
6 |
7 |
8 |
9 | SequenceFlow_01pzds7
10 | SequenceFlow_1yhsttk
11 | SequenceFlow_0f0dkku
12 | SequenceFlow_0kfqg1l
13 |
14 |
15 | SequenceFlow_1yhsttk
16 | flow-up-join1
17 | SequenceFlow_05akcgw
18 |
19 |
20 |
21 |
22 | SequenceFlow_1334ker
23 |
24 |
25 |
26 | ${variables.takeJoin1}
27 |
28 |
29 | SequenceFlow_0kfqg1l
30 | SequenceFlow_01624fc
31 | SequenceFlow_12ip5vh
32 |
33 |
34 |
35 |
36 | SequenceFlow_0f0dkku
37 | flow-up-join1
38 | SequenceFlow_01624fc
39 | SequenceFlow_14a3u6g
40 |
41 |
42 |
43 |
44 | ${variables.takeJoin2}
45 |
46 |
47 | SequenceFlow_14a3u6g
48 | SequenceFlow_05akcgw
49 | SequenceFlow_12ip5vh
50 | SequenceFlow_1334ker
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
--------------------------------------------------------------------------------
/test/resources/task-multiple-inbound.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_0jtx7d0
6 |
7 |
8 | SequenceFlow_0reamwp
9 | flow2
10 |
11 |
12 |
13 | flow2
14 | flow3
15 | SequenceFlow_0ruy5f5
16 |
23 |
24 |
25 | endFlow
26 |
27 |
28 | flow3
29 |
30 | PT0.1S
31 |
32 |
33 |
34 |
35 | SequenceFlow_0ruy5f5
36 | endFlow
37 | loopFlow
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | SequenceFlow_0jtx7d0
47 | loopFlow
48 | SequenceFlow_0reamwp
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/test/resources/task-with-multi-instance-loop.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_00cfpc8
6 |
7 |
8 |
9 | SequenceFlow_00cfpc8
10 | SequenceFlow_0givraz
11 |
12 | 10
13 | ${services.loopCondition}
14 |
15 |
17 |
18 |
19 | SequenceFlow_0givraz
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/test/resources/timer-event.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_0evszjw
6 |
7 |
8 | SequenceFlow_0evszjw
9 | SequenceFlow_01f3n3g
10 |
11 |
12 |
13 |
14 | SequenceFlow_01f3n3g
15 | SequenceFlow_02raz3t
16 |
17 | PT0.05S
18 |
19 |
20 |
21 | SequenceFlow_02raz3t
22 | SequenceFlow_1au7566
23 |
24 |
25 |
26 | SequenceFlow_1au7566
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/test/resources/timer-start-event.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_009rhrh
6 |
7 |
8 | SequenceFlow_009rhrh
9 | SequenceFlow_013jd7g
10 |
11 |
12 |
13 |
14 | SequenceFlow_013jd7g
15 | SequenceFlow_0ttpgeb
16 | SequenceFlow_0efld9d
17 |
18 |
19 |
20 | SequenceFlow_0efld9d
21 |
22 |
23 |
24 | SequenceFlow_0ttpgeb
25 |
26 | PT0.05S
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/test/resources/timers.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | to-task
6 |
7 | R3/PT10H
8 |
9 |
10 |
11 |
12 | to-duration-end
13 |
14 | PT1M
15 |
16 |
17 |
18 | to-end
19 |
20 |
21 |
22 | to-duration-end
23 |
24 |
25 |
26 |
27 | to-catch-date
28 | to-user-due
29 |
30 | ${environment.variables.catchDate}
31 |
32 |
33 |
34 |
35 | to-user-due
36 | to-end
37 |
38 |
39 | to-task
40 | to-catch-date
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src/**/*", "types"],
3 | "compilerOptions": {
4 | "emitDeclarationOnly": true,
5 | "sourceMap": false,
6 | "rootDir": "src",
7 | "lib": ["es2017"],
8 | "target": "es2017",
9 | "outDir": "./tmp/ignore",
10 | "declaration": true,
11 | "noEmitOnError": true,
12 | "noErrorTruncation": true,
13 | "allowJs": true,
14 | "checkJs": false,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": false,
18 | "allowSyntheticDefaultImports": true,
19 | "strict": true,
20 | "stripInternal": true,
21 | "noImplicitThis": true,
22 | "noUnusedLocals": true,
23 | "noUnusedParameters": true,
24 | "typeRoots": ["./node_modules/@types"],
25 | "paths": {
26 | "types": ["./types/bpmn-engine.js"]
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------