diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee3a802..9c43692 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
# 20.0.1
+- hoist current definition environment output to engine environment output if run fails
- Major bump `bpmn-moddle@9`
- Minor bump [`moddle-context-serializer@4.2`](https://github.com/paed01/moddle-context-serializer/blob/master/CHANGELOG.md)
- Minor bump [`bpmn-elements@13.2.0`](https://github.com/paed01/bpmn-elements/blob/master/CHANGELOG.md)
diff --git a/README.md b/README.md
index 2a74224..6ce2cc3 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
-[![Build Status](https://app.travis-ci.com/paed01/bpmn-engine.svg?branch=master)](https://app.travis-ci.com/paed01/bpmn-engine)[![Build status](https://ci.appveyor.com/api/projects/status/670n39fivq1g3nu5?svg=true)](https://ci.appveyor.com/project/paed01/bpmn-engine)[![Coverage Status](https://coveralls.io/repos/github/paed01/bpmn-engine/badge.svg?branch=master)](https://coveralls.io/github/paed01/bpmn-engine?branch=master)
+[![Build](https://github.com/paed01/bpmn-engine/actions/workflows/build.yaml/badge.svg)](https://github.com/paed01/bpmn-engine/actions/workflows/build.yaml)[![Build status](https://ci.appveyor.com/api/projects/status/670n39fivq1g3nu5?svg=true)](https://ci.appveyor.com/project/paed01/bpmn-engine)[![Coverage Status](https://coveralls.io/repos/github/paed01/bpmn-engine/badge.svg?branch=master)](https://coveralls.io/github/paed01/bpmn-engine?branch=master)
# Introduction
diff --git a/docs/API.md b/docs/API.md
index 728954f..b1ffc5f 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -1,6 +1,6 @@
-# 20.0.0 API Reference
+# 20.0.1 API Reference
diff --git a/package.json b/package.json
index 556f063..533fdd4 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "bpmn-engine",
"description": "BPMN 2.0 execution engine. Open source javascript workflow engine.",
- "version": "20.0.0",
+ "version": "20.0.1",
"type": "module",
"module": "./src/index.js",
"main": "./lib/index.cjs",
@@ -54,6 +54,7 @@
"@bonniernews/hot-bev": "^0.4.0",
"@rollup/plugin-commonjs": "^25.0.7",
"@types/bpmn-moddle": "^5.1.6",
+ "@types/node": "^18.19.31",
"bent": "^7.3.12",
"c8": "^9.1.0",
"camunda-bpmn-moddle": "^7.0.1",
diff --git a/src/index.js b/src/index.js
index 645a170..ae0ed35 100644
--- a/src/index.js
+++ b/src/index.js
@@ -467,30 +467,19 @@ Execution.prototype._onChildMessage = function onChildMessage(routingKey, messag
newState = 'idle';
break;
- case 'definition.error':
+ case 'definition.error': {
+ this._saveOutput(owner.environment.output);
this._teardownDefinition(owner);
newState = 'error';
break;
+ }
case 'activity.wait': {
if (listener) listener.emit('wait', owner.getApi(message), this);
break;
}
case 'process.end': {
- if (!message.content.output) break;
- const environment = this.environment;
-
- for (const key in message.content.output) {
- switch (key) {
- case 'data': {
- environment.output.data = environment.output.data || {};
- environment.output.data = { ...environment.output.data, ...message.content.output.data };
- break;
- }
- default: {
- environment.output[key] = message.content.output[key];
- }
- }
- }
+ if (message.content.inbound) break;
+ this._saveOutput(message.content.output);
break;
}
}
@@ -532,6 +521,21 @@ Execution.prototype._teardownDefinition = function teardownDefinition(definition
definition.broker.cancel('_engine_definition');
};
+Execution.prototype._saveOutput = function saveOutput(output) {
+ if (!output || typeof output !== 'object') return;
+
+ const environmentOutput = this.environment.output;
+
+ for (const key in output) {
+ if (key === 'data') {
+ const data = (environmentOutput.data = environmentOutput.data || {});
+ environmentOutput.data = { ...data, ...output.data };
+ } else {
+ environmentOutput[key] = output[key];
+ }
+ }
+};
+
Execution.prototype.getState = function getState() {
const definitions = [];
for (const definition of this.definitions) {
diff --git a/test/feature/engine-output-feature.js b/test/feature/engine-output-feature.js
new file mode 100644
index 0000000..3e713ab
--- /dev/null
+++ b/test/feature/engine-output-feature.js
@@ -0,0 +1,115 @@
+import { Engine } from '../../src/index.js';
+
+Feature('Engine output', () => {
+ Scenario('Process completes with output', () => {
+ let engine;
+ Given('a process with output', () => {
+ const source = `
+
+
+
+
+
+
+ `;
+
+ engine = new Engine({ source });
+ });
+
+ let end;
+ When('ran', () => {
+ end = engine.waitFor('end');
+ engine.execute();
+ });
+
+ Then('run completes', () => {
+ return end;
+ });
+
+ And('engine state contain hoisted process output', async () => {
+ expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
+ });
+
+ Given('a process with call activity and a called process', () => {
+ const source = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ engine = new Engine({ source });
+ });
+
+ When('ran', () => {
+ end = engine.waitFor('end');
+ engine.execute();
+ });
+
+ Then('run completes', () => {
+ return end;
+ });
+
+ And('engine state contain hoisted main process output', async () => {
+ expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
+ });
+ });
+
+ Scenario('process fails', () => {
+ let engine;
+ Given('a process with output', () => {
+ const source = `
+
+
+
+
+
+
+ `;
+
+ engine = new Engine({ source });
+ });
+
+ let errored;
+ When('ran', () => {
+ errored = engine.waitFor('error');
+ engine.execute();
+ });
+
+ Then('run fails', () => {
+ return errored;
+ });
+
+ And('engine state contain hoisted process output', async () => {
+ expect((await engine.getState()).environment.output).to.deep.equal({ foo: 'bar' });
+ });
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..8201c9f
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,29 @@
+{
+ "include": ["src/**/*", "types"],
+ "compilerOptions": {
+ "emitDeclarationOnly": true,
+ "sourceMap": false,
+ "rootDir": "src",
+ "lib": ["es2017"],
+ "target": "es2017",
+ "outDir": "./tmp/ignore",
+ "declaration": true,
+ "noEmitOnError": true,
+ "noErrorTruncation": true,
+ "allowJs": true,
+ "checkJs": false,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": false,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "stripInternal": true,
+ "noImplicitThis": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "typeRoots": ["./node_modules/@types"],
+ "paths": {
+ "types": ["./types/bpmn-engine.js"]
+ }
+ }
+}
diff --git a/types/bpmn-engine.d.ts b/types/bpmn-engine.d.ts
index 69e1786..403a424 100644
--- a/types/bpmn-engine.d.ts
+++ b/types/bpmn-engine.d.ts
@@ -16,6 +16,7 @@ import {
ILogger,
EnvironmentState,
IScripts,
+ Script,
} from 'bpmn-elements';
declare module 'bpmn-engine' {