├── .github
└── workflows
│ └── build.yaml
├── .gitignore
├── .mocharc.json
├── .nvmrc
├── .prettierignore
├── .prettierrc.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── babel.config.cjs
├── dist
├── Api.js
├── Context.js
├── Environment.js
├── EventBroker.js
├── Expressions.js
├── MessageFormatter.js
├── Scripts.js
├── Timers.js
├── Tracker.js
├── activity
│ ├── Activity.js
│ ├── ActivityExecution.js
│ ├── Dummy.js
│ ├── Escalation.js
│ ├── ExecutionScope.js
│ ├── Message.js
│ ├── Signal.js
│ └── outbound-evaluator.js
├── condition.js
├── definition
│ ├── Definition.js
│ └── DefinitionExecution.js
├── error
│ ├── BpmnError.js
│ └── Errors.js
├── eventDefinitions
│ ├── CancelEventDefinition.js
│ ├── CompensateEventDefinition.js
│ ├── ConditionalEventDefinition.js
│ ├── ErrorEventDefinition.js
│ ├── EscalationEventDefinition.js
│ ├── EventDefinitionExecution.js
│ ├── LinkEventDefinition.js
│ ├── MessageEventDefinition.js
│ ├── SignalEventDefinition.js
│ ├── TerminateEventDefinition.js
│ ├── TimerEventDefinition.js
│ └── index.js
├── events
│ ├── BoundaryEvent.js
│ ├── EndEvent.js
│ ├── IntermediateCatchEvent.js
│ ├── IntermediateThrowEvent.js
│ ├── StartEvent.js
│ └── index.js
├── flows
│ ├── Association.js
│ ├── MessageFlow.js
│ ├── SequenceFlow.js
│ └── index.js
├── gateways
│ ├── EventBasedGateway.js
│ ├── ExclusiveGateway.js
│ ├── InclusiveGateway.js
│ ├── ParallelGateway.js
│ └── index.js
├── getPropertyValue.js
├── index.js
├── io
│ ├── BpmnIO.js
│ ├── EnvironmentDataObject.js
│ ├── EnvironmentDataStore.js
│ ├── EnvironmentDataStoreReference.js
│ ├── InputOutputSpecification.js
│ └── Properties.js
├── messageHelper.js
├── package.json
├── process
│ ├── Lane.js
│ ├── Process.js
│ └── ProcessExecution.js
├── shared.js
└── tasks
│ ├── CallActivity.js
│ ├── LoopCharacteristics.js
│ ├── ReceiveTask.js
│ ├── ScriptTask.js
│ ├── ServiceImplementation.js
│ ├── ServiceTask.js
│ ├── SignalTask.js
│ ├── StandardLoopCharacteristics.js
│ ├── SubProcess.js
│ ├── Task.js
│ ├── Transaction.js
│ └── index.js
├── docs
├── Activity.md
├── ActivityExecution.md
├── BpmnIO.md
├── CallActivity.md
├── ConditionalEventDefinition.md
├── Context.md
├── Definition.md
├── Environment.md
├── Examples.md
├── ExecutionScope.md
├── Expression.md
├── Extend.md
├── Extension.md
├── LoopCharacteristics.md
├── ParallelGateway.md
├── Process.md
├── Scripts.md
├── SequenceFlow.md
├── ServiceTask.md
├── SharedApi.md
├── SignalTask.md
├── StartEvent.md
├── TimerEventDefinition.md
├── Timers.md
├── activity-execution.png
├── activity-lifecycle.png
└── parallel-join-edgecase.png
├── eslint.config.js
├── eventDefinitions.d.ts
├── events.d.ts
├── flows.d.ts
├── gateways.d.ts
├── index.d.ts
├── package.json
├── src
├── Api.js
├── Context.js
├── Environment.js
├── EventBroker.js
├── Expressions.js
├── MessageFormatter.js
├── Scripts.js
├── Timers.js
├── Tracker.js
├── activity
│ ├── Activity.js
│ ├── ActivityExecution.js
│ ├── Dummy.js
│ ├── Escalation.js
│ ├── ExecutionScope.js
│ ├── Message.js
│ ├── Signal.js
│ └── outbound-evaluator.js
├── condition.js
├── definition
│ ├── Definition.js
│ └── DefinitionExecution.js
├── error
│ ├── BpmnError.js
│ └── Errors.js
├── eventDefinitions
│ ├── CancelEventDefinition.js
│ ├── CompensateEventDefinition.js
│ ├── ConditionalEventDefinition.js
│ ├── ErrorEventDefinition.js
│ ├── EscalationEventDefinition.js
│ ├── EventDefinitionExecution.js
│ ├── LinkEventDefinition.js
│ ├── MessageEventDefinition.js
│ ├── SignalEventDefinition.js
│ ├── TerminateEventDefinition.js
│ ├── TimerEventDefinition.js
│ └── index.js
├── events
│ ├── BoundaryEvent.js
│ ├── EndEvent.js
│ ├── IntermediateCatchEvent.js
│ ├── IntermediateThrowEvent.js
│ ├── StartEvent.js
│ └── index.js
├── flows
│ ├── Association.js
│ ├── MessageFlow.js
│ ├── SequenceFlow.js
│ └── index.js
├── gateways
│ ├── EventBasedGateway.js
│ ├── ExclusiveGateway.js
│ ├── InclusiveGateway.js
│ ├── ParallelGateway.js
│ └── index.js
├── getPropertyValue.js
├── index.js
├── io
│ ├── BpmnIO.js
│ ├── EnvironmentDataObject.js
│ ├── EnvironmentDataStore.js
│ ├── EnvironmentDataStoreReference.js
│ ├── InputOutputSpecification.js
│ └── Properties.js
├── messageHelper.js
├── process
│ ├── Lane.js
│ ├── Process.js
│ └── ProcessExecution.js
├── shared.js
└── tasks
│ ├── CallActivity.js
│ ├── LoopCharacteristics.js
│ ├── ReceiveTask.js
│ ├── ScriptTask.js
│ ├── ServiceImplementation.js
│ ├── ServiceTask.js
│ ├── SignalTask.js
│ ├── StandardLoopCharacteristics.js
│ ├── SubProcess.js
│ ├── Task.js
│ ├── Transaction.js
│ └── index.js
├── tasks.d.ts
├── test
├── Api-test.js
├── Context-test.js
├── Environment-test.js
├── MessageFormatter-test.js
├── Timers-test.js
├── activities-test.js
├── activity-api-test.js
├── activity
│ ├── Activity-test.js
│ ├── ActivityExecution-test.js
│ ├── ExecutionScope-test.js
│ └── activity-run-test.js
├── bpmn-elements-test.js
├── definition
│ ├── Definition-test.js
│ └── DefinitionExecution-test.js
├── error
│ ├── BpmnError-test.js
│ └── Errors-test.js
├── eventDefinitions
│ ├── CancelEventDefinition-test.js
│ ├── CompensateEventDefinition-test.js
│ ├── ConditionalEventDefinition-test.js
│ ├── ErrorEventDefinition-test.js
│ ├── EscalationEventDefinition-test.js
│ ├── EventDefinitionExecution-test.js
│ ├── LinkEventDefinition-test.js
│ ├── MessageEventDefinition-test.js
│ ├── SignalEventDefinition-test.js
│ ├── TerminateEventDefinition-test.js
│ └── TimerEventDefinition-test.js
├── events
│ ├── BoundaryEvent-test.js
│ ├── EndEvent-test.js
│ ├── IntermediateCatchEvent-test.js
│ ├── IntermediateThrowEvent-test.js
│ └── StartEvent-test.js
├── expressions-test.js
├── feature
│ ├── BoundaryEvent-feature.js
│ ├── Definition-feature.js
│ ├── EventBasedGateway-feature.js
│ ├── Process-feature.js
│ ├── activity-feature.js
│ ├── activity-io-feature.js
│ ├── activity-status-feature.js
│ ├── ad-hoc-subprocess-feature.js
│ ├── backward-compatability-feature.js
│ ├── call-activity-feature.js
│ ├── compensation-feature.js
│ ├── conditional-event-feature.js
│ ├── definition-output-feature.js
│ ├── dummy-feature.js
│ ├── environment-feature.js
│ ├── errors-feature.js
│ ├── escalation-feature.js
│ ├── expression-feature.js
│ ├── extension-feature.js
│ ├── format-feature.js
│ ├── gateway-feature.js
│ ├── io-feature.js
│ ├── issues
│ │ ├── engine-issues-feature.js
│ │ ├── exclusive-gateway-join-feature.js
│ │ ├── issue-31-feature.js
│ │ ├── issue-32-feature.js
│ │ ├── issue-39-feature.js
│ │ ├── issue-42-feature.js
│ │ ├── issues-feature.js
│ │ └── stack-overflow-feature.js
│ ├── linking-feature.js
│ ├── messaging-feature.js
│ ├── multiple-startEvent-feature.js
│ ├── noExecutableProcess-feature.js
│ ├── outbound-flows-feature.js
│ ├── parallel-gateway-feature.js
│ ├── performance-feature.js
│ ├── recover-resume-feature.js
│ ├── script-feature.js
│ ├── sequence-flow-feature.js
│ ├── service-task-feature.js
│ ├── shake-feature.js
│ ├── signal-feature.js
│ ├── stopAndResume-feature.js
│ ├── sub-process-feature.js
│ ├── swim-lanes-feature.js
│ ├── task-loop-feature.js
│ ├── timers-feature.js
│ └── transaction-feature.js
├── flows
│ ├── Association-test.js
│ ├── MessageFlow-test.js
│ └── SequenceFlow-test.js
├── gateways
│ ├── EventBasedGateway-test.js
│ ├── ExclusiveGateway-test.js
│ ├── InclusiveGateway-test.js
│ └── ParallelGateway-test.js
├── getPropertyValue-test.js
├── helpers
│ ├── JavaScripts.js
│ ├── factory.js
│ ├── setup.js
│ └── testHelpers.js
├── io
│ ├── DataObject-test.js
│ ├── InputOutputSpecification-test.js
│ └── Properties-test.js
├── messageHelper-test.js
├── package-test.js
├── process
│ ├── Process-test.js
│ └── ProcessExecution-test.js
├── resources
│ ├── README.md
│ ├── activity-execution.bpmn
│ ├── activity-lifecycle.bpmn
│ ├── ad-hoc-subprocess.bpmn
│ ├── async-decision.bpmn
│ ├── bound-error-and-timer.bpmn
│ ├── bound-error.bpmn
│ ├── boundary-non-interupting-timer.bpmn
│ ├── boundary-timeout.bpmn
│ ├── bpmn-error.bpmn
│ ├── call-activity-signal.bpmn
│ ├── call-activity.bpmn
│ ├── conditional-bound-js-event.bpmn
│ ├── conditional-flows.bpmn
│ ├── consumer_error.bpmn
│ ├── data-store-reference.bpmn
│ ├── end-compensate.bpmn
│ ├── engine-issue-105_1.bpmn
│ ├── engine-issue-105_2.bpmn
│ ├── engine-issue-139.bpmn
│ ├── engine-issue-180-message.bpmn
│ ├── engine-issue-180-signal.bpmn
│ ├── engine-issue-180.bpmn
│ ├── engine-issue-73.bpmn
│ ├── engine-issue-73_2.bpmn
│ ├── escalation-start-event.bpmn
│ ├── escalation.bpmn
│ ├── event-based-gateway-with-same-target.bpmn
│ ├── event-based-gateway.bpmn
│ ├── exclusive-gateway-as-join.bpmn
│ ├── extensions
│ │ ├── CamundaExtension.js
│ │ └── JsExtension.js
│ ├── forms.bpmn
│ ├── groups.bpmn
│ ├── issue-19.bpmn
│ ├── issue-3.bpmn
│ ├── issue-31-cm.bpmn
│ ├── issue-31.bpmn
│ ├── issue-4.bpmn
│ ├── issue-42-same-target-sequence-flows.bpmn
│ ├── join-inbound.bpmn
│ ├── js-bpmn-moddle.json
│ ├── lanes.bpmn
│ ├── link-event.bpmn
│ ├── loop.bpmn
│ ├── messageflow-target-process.bpmn
│ ├── messaging.bpmn
│ ├── misp-loopback.bpmn
│ ├── misp-parallel-loopback.bpmn
│ ├── mother-of-all-state-5.json
│ ├── mother-of-all.bpmn
│ ├── multiple-endEvents.bpmn
│ ├── multiple-joins.bpmn
│ ├── multiple-multiple-inbound.bpmn
│ ├── nested-joins.bpmn
│ ├── parallel-join-edgecase.bpmn
│ ├── process-message.bpmn
│ ├── resume_error.bpmn
│ ├── send-signal.bpmn
│ ├── service-task-io.bpmn
│ ├── service-task-operation.bpmn
│ ├── service-task.bpmn
│ ├── signal-after-signal.bpmn
│ ├── signals.bpmn
│ ├── simple-task.bpmn
│ ├── sub-process.bpmn
│ ├── succeeding-joins.bpmn
│ ├── swimlanes.bpmn
│ ├── task-multiple-inbound.bpmn
│ ├── task-with-multi-instance-loop.bpmn
│ ├── throw-compensate.bpmn
│ ├── timer-event.bpmn
│ ├── timer-start-event.bpmn
│ ├── timers.bpmn
│ ├── transaction.bpmn
│ ├── two-executable-processes.bpmn
│ └── wait-activities.bpmn
├── shared-test.js
└── tasks
│ ├── BusinessRuleTask-test.js
│ ├── CallActivity-test.js
│ ├── LoopCharacteristics-test.js
│ ├── ManualTask-test.js
│ ├── ReceiveTask-test.js
│ ├── ScriptTask-test.js
│ ├── SendTask-test.js
│ ├── ServiceTask-test.js
│ ├── SubProcess-test.js
│ ├── Task-test.js
│ ├── Transaction-test.js
│ ├── UserTask-test.js
│ └── isForCompensation-test.js
├── tsconfig.json
└── types
├── index.d.ts
└── types.d.ts
/.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 | # OS X
24 | .DS_Store
25 |
26 | # vim
27 | *.swp
28 | *.swo
29 |
30 | # eslint
31 | .eslintcache
32 |
33 | package-lock.json
34 | .nyc_output
35 |
36 | *.log
37 |
--------------------------------------------------------------------------------
/.mocharc.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": "./test/helpers/setup.js",
3 | "reporter": "spec",
4 | "recursive": true,
5 | "timeout": 1000,
6 | "ui": "mocha-cakes-2"
7 | }
8 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 18
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist/
2 | node_modules/
3 | coverage/
4 | tmp/
5 | test/resources/*.bpmn
6 | *.bpmn
7 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "printWidth": 140,
4 | "singleQuote": true,
5 | "trailingComma": "es5"
6 | }
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paed01/bpmn-elements/3fd23492027fea9720b260f29208a77ce7139580/LICENSE
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bpmn-elements
2 |
3 | [](https://github.com/paed01/bpmn-elements/actions/workflows/build.yaml)[](https://coveralls.io/github/paed01/bpmn-elements?branch=master)
4 |
5 | Isomorphic JavaScript BPMN 2.0 workflow elements suitable for bundling into frontend script or just required into your nodejs project.
6 |
7 | - [Examples](/docs/Examples.md)
8 | - [Handle extensions](/docs/Extension.md)
9 | - [Write your own behaviour](/docs/Extend.md)
10 |
11 | # Supported elements
12 |
13 | The following elements are tested and supported.
14 |
15 | - [Definition](/docs/Definition.md): Executable BPMN 2 definition
16 | - [Process](/docs/Process.md): Executes and keeps track of activity elements
17 | - AdHocSubProcess
18 | - BpmnError
19 | - BoundaryEvent
20 | - [CallActivity](/docs/CallActivity.md)
21 | - CancelEventDefinition
22 | - [ConditionalEventDefinition](/docs/ConditionalEventDefinition.md)
23 | - CompensateEventDefinition
24 | - compensate by outbound Association
25 | - [DataObject](/docs/BpmnIO.md)
26 | - [DataStore](/docs/BpmnIO.md)
27 | - [DataStoreReference](/docs/BpmnIO.md)
28 | - EndEvent
29 | - Error
30 | - ErrorEventDefinition
31 | - throw
32 | - catch
33 | - EscalationEventDefinition
34 | - throw
35 | - catch
36 | - EventBasedGateway
37 | - ExclusiveGateway
38 | - InclusiveGateway
39 | - IntermediateCatchEvent
40 | - IntermediateThrowEvent
41 | - [InputOutputSpecification](/docs/BpmnIO.md)
42 | - LinkEventDefinition
43 | - throw
44 | - catch
45 | - MessageEventDefinition
46 | - throw
47 | - catch
48 | - MessageFlow
49 | - [MultiInstanceLoopCharacteristics](/docs/LoopCharacteristics.md)
50 | - [ParallelGateway](/docs/ParallelGateway.md)
51 | - Participant
52 | - Lane: exposed on activity
53 | - [Property](/docs/BpmnIO.md)
54 | - ReceiveTask
55 | - ScriptTask
56 | - [SequenceFlow](/docs/SequenceFlow.md)
57 | - ServiceImplementation: ServiceTask implementation attribute behaviour
58 | - [ServiceTask](/docs/ServiceTask.md)
59 | - BusinessRuleTask: Same behaviour as ServiceTask
60 | - SendTask: Same behaviour as ServiceTask
61 | - Signal
62 | - SignalEventDefinition
63 | - throw
64 | - catch
65 | - [SignalTask](/docs/SignalTask.md)
66 | - ManualTask
67 | - UserTask
68 | - [StandardLoopCharacteristics](/docs/LoopCharacteristics.md)
69 | - [StartEvent](/docs/StartEvent.md)
70 | - SubProcess
71 | - Task
72 | - TerminateEventDefinition
73 | - [TimerEventDefinition](/docs/TimerEventDefinition.md)
74 | - timeDuration
75 | - timeDate
76 | - timeCycle
77 | - Transaction
78 |
79 | All activities share the same [base](/docs/Activity.md) and and [api](/docs/SharedApi.md).
80 |
--------------------------------------------------------------------------------
/babel.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = function babelRoot(api) {
2 | api.cache(true);
3 |
4 | return {
5 | presets: [
6 | [
7 | '@babel/env',
8 | {
9 | targets: {
10 | node: 'current',
11 | },
12 | },
13 | ],
14 | ],
15 | };
16 | };
17 |
--------------------------------------------------------------------------------
/dist/Api.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.ActivityApi = ActivityApi;
7 | exports.Api = Api;
8 | exports.DefinitionApi = DefinitionApi;
9 | exports.FlowApi = FlowApi;
10 | exports.ProcessApi = ProcessApi;
11 | var _messageHelper = require("./messageHelper.js");
12 | var _shared = require("./shared.js");
13 | function ActivityApi(broker, apiMessage, environment) {
14 | return new Api('activity', broker, apiMessage, environment);
15 | }
16 | function DefinitionApi(broker, apiMessage, environment) {
17 | return new Api('definition', broker, apiMessage, environment);
18 | }
19 | function ProcessApi(broker, apiMessage, environment) {
20 | return new Api('process', broker, apiMessage, environment);
21 | }
22 | function FlowApi(broker, apiMessage, environment) {
23 | return new Api('flow', broker, apiMessage, environment);
24 | }
25 | function Api(pfx, broker, sourceMessage, environment) {
26 | if (!sourceMessage) throw new Error('Api requires message');
27 | const apiMessage = (0, _messageHelper.cloneMessage)(sourceMessage);
28 | const {
29 | id,
30 | type,
31 | name,
32 | executionId
33 | } = apiMessage.content;
34 | this.id = id;
35 | this.type = type;
36 | this.name = name;
37 | this.executionId = executionId;
38 | this.environment = environment || broker.owner.environment;
39 | this.content = apiMessage.content;
40 | this.fields = apiMessage.fields;
41 | this.messageProperties = apiMessage.properties;
42 | this.broker = broker;
43 | this.owner = broker.owner;
44 | this.messagePrefix = pfx;
45 | }
46 | Api.prototype.cancel = function cancel(message, options) {
47 | this.sendApiMessage('cancel', {
48 | message
49 | }, options);
50 | };
51 | Api.prototype.discard = function discard() {
52 | this.sendApiMessage('discard');
53 | };
54 | Api.prototype.fail = function fail(error) {
55 | this.sendApiMessage('error', {
56 | error
57 | });
58 | };
59 | Api.prototype.signal = function signal(message, options) {
60 | this.sendApiMessage('signal', {
61 | message
62 | }, options);
63 | };
64 | Api.prototype.stop = function stop() {
65 | this.sendApiMessage('stop');
66 | };
67 | Api.prototype.resolveExpression = function resolveExpression(expression) {
68 | return this.environment.resolveExpression(expression, {
69 | fields: this.fields,
70 | content: this.content,
71 | properties: this.messageProperties
72 | }, this.owner);
73 | };
74 | Api.prototype.sendApiMessage = function sendApiMessage(action, content, options) {
75 | const correlationId = options?.correlationId || (0, _shared.getUniqueId)(`${this.id || this.messagePrefix}_signal`);
76 | let key = `${this.messagePrefix}.${action}`;
77 | if (this.executionId) key += `.${this.executionId}`;
78 | this.broker.publish('api', key, this.createMessage(content), {
79 | ...options,
80 | correlationId,
81 | type: action
82 | });
83 | };
84 | Api.prototype.getPostponed = function getPostponed(...args) {
85 | if (this.owner.getPostponed) return this.owner.getPostponed(...args);
86 | if (this.owner.isSubProcess && this.owner.execution) return this.owner.execution.getPostponed(...args);
87 | return [];
88 | };
89 | Api.prototype.createMessage = function createMessage(content) {
90 | return {
91 | ...this.content,
92 | ...content
93 | };
94 | };
--------------------------------------------------------------------------------
/dist/Expressions.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = Expressions;
7 | var _getPropertyValue = _interopRequireDefault(require("./getPropertyValue.js"));
8 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9 | const isExpressionPattern = /^\${(.+?)}$/;
10 | const expressionPattern = /\${(.+?)}/;
11 | function Expressions() {
12 | return {
13 | resolveExpression,
14 | isExpression,
15 | hasExpression
16 | };
17 | }
18 | function resolveExpression(templatedString, context, expressionFnContext) {
19 | let result = templatedString;
20 | while (expressionPattern.test(result)) {
21 | const expressionMatch = result.match(expressionPattern);
22 | const innerProperty = expressionMatch[1];
23 | if (innerProperty === 'true') {
24 | return true;
25 | } else if (innerProperty === 'false') {
26 | return false;
27 | } else if (innerProperty === 'null') {
28 | return null;
29 | } else {
30 | const n = Number(innerProperty);
31 | if (!isNaN(n)) return n;
32 | }
33 | const contextValue = (0, _getPropertyValue.default)(context, innerProperty, expressionFnContext);
34 | if (expressionMatch.input === expressionMatch[0]) {
35 | return contextValue;
36 | }
37 | result = result.replace(expressionMatch[0], contextValue === undefined ? '' : contextValue);
38 | }
39 | return result;
40 | }
41 | function isExpression(text) {
42 | if (!text) return false;
43 | return isExpressionPattern.test(text);
44 | }
45 | function hasExpression(text) {
46 | if (!text) return false;
47 | return expressionPattern.test(text);
48 | }
--------------------------------------------------------------------------------
/dist/Scripts.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Scripts = Scripts;
7 | function Scripts() {}
8 | Scripts.prototype.getScript = function getScript(/*scriptType, activity*/) {};
9 | Scripts.prototype.register = function register(/*activity*/) {};
--------------------------------------------------------------------------------
/dist/Timers.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.Timers = Timers;
7 | const kExecuting = Symbol.for('executing');
8 | const kTimerApi = Symbol.for('timers api');
9 | const MAX_DELAY = 2147483647;
10 | function Timers(options) {
11 | this.count = 0;
12 | this.options = {
13 | setTimeout,
14 | clearTimeout,
15 | ...options
16 | };
17 | this[kExecuting] = new Set();
18 | this.setTimeout = this.setTimeout.bind(this);
19 | this.clearTimeout = this.clearTimeout.bind(this);
20 | }
21 | Object.defineProperty(Timers.prototype, 'executing', {
22 | get() {
23 | return [...this[kExecuting]];
24 | }
25 | });
26 | Timers.prototype.register = function register(owner) {
27 | return new RegisteredTimers(this, owner);
28 | };
29 | Timers.prototype.setTimeout = function wrappedSetTimeout(callback, delay, ...args) {
30 | return this._setTimeout(null, callback, delay, ...args);
31 | };
32 | Timers.prototype.clearTimeout = function wrappedClearTimeout(ref) {
33 | if (this[kExecuting].delete(ref)) {
34 | ref.timerRef = this.options.clearTimeout(ref.timerRef);
35 | return;
36 | }
37 | return this.options.clearTimeout(ref);
38 | };
39 | Timers.prototype._setTimeout = function setTimeout(owner, callback, delay, ...args) {
40 | const executing = this[kExecuting];
41 | const ref = this._getReference(owner, callback, delay, args);
42 | executing.add(ref);
43 | if (delay < MAX_DELAY) {
44 | ref.timerRef = this.options.setTimeout(onTimeout, ref.delay, ...ref.args);
45 | }
46 | return ref;
47 | function onTimeout(...rargs) {
48 | executing.delete(ref);
49 | return callback(...rargs);
50 | }
51 | };
52 | Timers.prototype._getReference = function getReference(owner, callback, delay, args) {
53 | return new Timer(owner, `timer_${this.count++}`, callback, delay, args);
54 | };
55 | function RegisteredTimers(timersApi, owner) {
56 | this[kTimerApi] = timersApi;
57 | this.owner = owner;
58 | this.setTimeout = this.setTimeout.bind(this);
59 | this.clearTimeout = this.clearTimeout.bind(this);
60 | }
61 | RegisteredTimers.prototype.setTimeout = function registeredSetTimeout(callback, delay, ...args) {
62 | const timersApi = this[kTimerApi];
63 | return timersApi._setTimeout(this.owner, callback, delay, ...args);
64 | };
65 | RegisteredTimers.prototype.clearTimeout = function registeredClearTimeout(ref) {
66 | this[kTimerApi].clearTimeout(ref);
67 | };
68 | function Timer(owner, timerId, callback, delay, args) {
69 | this.callback = callback;
70 | this.delay = delay;
71 | this.args = args;
72 | this.owner = owner;
73 | this.timerId = timerId;
74 | this.expireAt = new Date(Date.now() + delay);
75 | this.timerRef = null;
76 | }
--------------------------------------------------------------------------------
/dist/Tracker.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.ActivityTracker = ActivityTracker;
7 | function ActivityTracker(parentId) {
8 | this.id = parentId;
9 | this.status = {
10 | wait: new Set(),
11 | execute: new Set(),
12 | timer: new Set()
13 | };
14 | }
15 | Object.defineProperty(ActivityTracker.prototype, 'activityStatus', {
16 | get() {
17 | const status = this.status;
18 | if (status.execute.size) return 'executing';
19 | if (status.timer.size) return 'timer';
20 | return status.wait.size ? 'wait' : 'idle';
21 | }
22 | });
23 | ActivityTracker.prototype.track = function track(routingKey, message) {
24 | const content = message.content;
25 | if (content.isAssociation) return;
26 | if (content.isSequenceFlow) return;
27 | if (content.isSubProcess) return;
28 | const executionId = content.executionId;
29 | switch (routingKey) {
30 | case 'activity.enter':
31 | case 'activity.discard':
32 | case 'activity.start':
33 | case 'activity.execution.completed':
34 | case 'activity.execution.error':
35 | case 'activity.end':
36 | this._executing(executionId);
37 | break;
38 | case 'activity.execution.outbound.take':
39 | case 'activity.detach':
40 | case 'activity.call':
41 | case 'activity.wait':
42 | {
43 | if (content.isMultiInstance) this._waiting(content.parent.executionId);else this._waiting(executionId);
44 | break;
45 | }
46 | case 'activity.timer':
47 | this._timer(content.parent.executionId);
48 | break;
49 | case 'activity.leave':
50 | this._leave(executionId);
51 | break;
52 | }
53 | };
54 | ActivityTracker.prototype._executing = function executing(id) {
55 | const {
56 | wait,
57 | execute
58 | } = this.status;
59 | wait.delete(id);
60 | execute.add(id);
61 | };
62 | ActivityTracker.prototype._waiting = function waiting(id) {
63 | const {
64 | wait,
65 | execute
66 | } = this.status;
67 | execute.delete(id);
68 | wait.add(id);
69 | };
70 | ActivityTracker.prototype._timer = function timerFn(id) {
71 | const {
72 | timer,
73 | execute
74 | } = this.status;
75 | execute.delete(id);
76 | timer.add(id);
77 | };
78 | ActivityTracker.prototype._leave = function leave(id) {
79 | const {
80 | wait,
81 | execute,
82 | timer
83 | } = this.status;
84 | execute.delete(id);
85 | timer.delete(id);
86 | wait.delete(id);
87 | };
--------------------------------------------------------------------------------
/dist/activity/Dummy.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = DummyActivity;
7 | var _messageHelper = require("../messageHelper.js");
8 | function DummyActivity(activityDef) {
9 | const {
10 | id,
11 | type = 'dummy',
12 | name,
13 | parent,
14 | behaviour
15 | } = activityDef;
16 | return {
17 | id,
18 | type,
19 | name,
20 | behaviour: {
21 | ...behaviour
22 | },
23 | parent: (0, _messageHelper.cloneParent)(parent),
24 | placeholder: true
25 | };
26 | }
--------------------------------------------------------------------------------
/dist/activity/Escalation.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = Escalation;
7 | function Escalation(signalDef, context) {
8 | const {
9 | id,
10 | type,
11 | name,
12 | parent: originalParent
13 | } = signalDef;
14 | const {
15 | environment
16 | } = context;
17 | const parent = {
18 | ...originalParent
19 | };
20 | return {
21 | id,
22 | type,
23 | name,
24 | parent,
25 | resolve
26 | };
27 | function resolve(executionMessage) {
28 | return {
29 | id,
30 | type,
31 | messageType: 'escalation',
32 | name: name && environment.resolveExpression(name, executionMessage),
33 | parent: {
34 | ...parent
35 | }
36 | };
37 | }
38 | }
--------------------------------------------------------------------------------
/dist/activity/ExecutionScope.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = ExecutionScope;
7 | var _messageHelper = require("../messageHelper.js");
8 | var _Errors = require("../error/Errors.js");
9 | function ExecutionScope(activity, initMessage) {
10 | const {
11 | id,
12 | type,
13 | environment,
14 | logger
15 | } = activity;
16 | const {
17 | fields,
18 | content,
19 | properties
20 | } = (0, _messageHelper.cloneMessage)(initMessage);
21 | const scope = {
22 | id,
23 | type,
24 | fields,
25 | content,
26 | properties,
27 | environment,
28 | logger,
29 | resolveExpression,
30 | ActivityError: _Errors.ActivityError,
31 | BpmnError: _Errors.BpmnError
32 | };
33 | return scope;
34 | function resolveExpression(expression) {
35 | return environment.resolveExpression(expression, scope);
36 | }
37 | }
--------------------------------------------------------------------------------
/dist/activity/Message.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = Message;
7 | function Message(messageDef, context) {
8 | const {
9 | id,
10 | type,
11 | name,
12 | parent: originalParent
13 | } = messageDef;
14 | const {
15 | environment
16 | } = context;
17 | const parent = {
18 | ...originalParent
19 | };
20 | return {
21 | id,
22 | type,
23 | name,
24 | parent,
25 | resolve
26 | };
27 | function resolve(executionMessage) {
28 | return {
29 | id,
30 | type,
31 | messageType: 'message',
32 | ...(name && {
33 | name: environment.resolveExpression(name, executionMessage)
34 | }),
35 | parent: {
36 | ...parent
37 | }
38 | };
39 | }
40 | }
--------------------------------------------------------------------------------
/dist/activity/Signal.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = Signal;
7 | function Signal(signalDef, context) {
8 | const {
9 | id,
10 | type = 'Signal',
11 | name,
12 | parent: originalParent
13 | } = signalDef;
14 | const {
15 | environment
16 | } = context;
17 | const parent = {
18 | ...originalParent
19 | };
20 | return {
21 | id,
22 | type,
23 | name,
24 | parent,
25 | resolve
26 | };
27 | function resolve(executionMessage) {
28 | return {
29 | id,
30 | type,
31 | messageType: 'signal',
32 | ...(name && {
33 | name: environment.resolveExpression(name, executionMessage)
34 | }),
35 | parent: {
36 | ...parent
37 | }
38 | };
39 | }
40 | }
--------------------------------------------------------------------------------
/dist/condition.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.ExpressionCondition = ExpressionCondition;
7 | exports.ScriptCondition = ScriptCondition;
8 | var _ExecutionScope = _interopRequireDefault(require("./activity/ExecutionScope.js"));
9 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10 | /**
11 | * Script condition
12 | * @param {import('types').ElementBase} owner
13 | * @param {any} script
14 | * @param {string} language
15 | */
16 | function ScriptCondition(owner, script, language) {
17 | this.type = 'script';
18 | this.language = language;
19 | this._owner = owner;
20 | this._script = script;
21 | }
22 |
23 | /**
24 | * Execute
25 | * @param {any} message
26 | * @param {CallableFunction} callback
27 | */
28 | ScriptCondition.prototype.execute = function execute(message, callback) {
29 | const owner = this._owner;
30 | try {
31 | return this._script.execute((0, _ExecutionScope.default)(owner, message), callback);
32 | } catch (err) {
33 | if (!callback) throw err;
34 | owner.logger.error(`<${owner.id}>`, err);
35 | callback(err);
36 | }
37 | };
38 |
39 | /**
40 | * Expression condition
41 | * @param {import('types').ElementBase} owner
42 | * @param {string} expression
43 | */
44 | function ExpressionCondition(owner, expression) {
45 | this.type = 'expression';
46 | this.expression = expression;
47 | this._owner = owner;
48 | }
49 |
50 | /**
51 | * Execute
52 | * @param {any} message
53 | * @param {CallableFunction} callback
54 | */
55 | ExpressionCondition.prototype.execute = function execute(message, callback) {
56 | const owner = this._owner;
57 | try {
58 | const result = owner.environment.resolveExpression(this.expression, message);
59 | if (callback) return callback(null, result);
60 | return result;
61 | } catch (err) {
62 | if (callback) return callback(err);
63 | throw err;
64 | }
65 | };
--------------------------------------------------------------------------------
/dist/error/BpmnError.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = BpmnErrorActivity;
7 | function BpmnErrorActivity(errorDef, context) {
8 | const {
9 | id,
10 | type,
11 | name = 'BpmnError',
12 | behaviour = {}
13 | } = errorDef;
14 | const {
15 | environment
16 | } = context;
17 | return {
18 | id,
19 | type,
20 | name,
21 | errorCode: behaviour.errorCode,
22 | resolve
23 | };
24 | function resolve(executionMessage, error) {
25 | const resolveCtx = {
26 | ...executionMessage,
27 | error
28 | };
29 | const result = {
30 | id,
31 | type,
32 | messageType: 'throw',
33 | name: name && environment.resolveExpression(name, resolveCtx),
34 | code: behaviour.errorCode && environment.resolveExpression(behaviour.errorCode, resolveCtx)
35 | };
36 | if (error) result.inner = error;
37 | return result;
38 | }
39 | }
--------------------------------------------------------------------------------
/dist/error/Errors.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.RunError = exports.BpmnError = exports.ActivityError = void 0;
7 | exports.makeErrorFromMessage = makeErrorFromMessage;
8 | var _messageHelper = require("../messageHelper.js");
9 | class ActivityError extends Error {
10 | constructor(description, sourceMessage, inner) {
11 | super(description);
12 | this.type = 'ActivityError';
13 | this.name = this.constructor.name;
14 | this.description = description;
15 | if (sourceMessage) this.source = (0, _messageHelper.cloneMessage)(sourceMessage, sourceMessage.content?.error && {
16 | error: undefined
17 | });
18 | if (inner) {
19 | this.inner = inner;
20 | if (inner.name) this.name = inner.name;
21 | if (inner.code) this.code = inner.code;
22 | }
23 | }
24 | }
25 | exports.ActivityError = ActivityError;
26 | class RunError extends ActivityError {
27 | constructor(...args) {
28 | super(...args);
29 | this.type = 'RunError';
30 | }
31 | }
32 | exports.RunError = RunError;
33 | class BpmnError extends Error {
34 | constructor(description, behaviour, sourceMessage, inner) {
35 | super(description);
36 | this.type = 'BpmnError';
37 | this.name = behaviour?.name ?? this.constructor.name;
38 | this.description = description;
39 | this.code = behaviour?.errorCode?.toString() ?? behaviour?.code;
40 | this.id = behaviour?.id;
41 | if (sourceMessage) this.source = (0, _messageHelper.cloneMessage)(sourceMessage, sourceMessage.content?.error && {
42 | error: undefined
43 | });
44 | if (inner) this.inner = inner;
45 | }
46 | }
47 | exports.BpmnError = BpmnError;
48 | function makeErrorFromMessage(errorMessage) {
49 | const {
50 | content
51 | } = errorMessage;
52 | if (isKnownError(content)) return content;
53 | const {
54 | error
55 | } = content;
56 | if (!error) return new Error(`Malformatted error message with routing key ${errorMessage.fields?.routingKey}`);
57 | if (isKnownError(error)) return error;
58 | switch (error.type) {
59 | case 'ActivityError':
60 | return new ActivityError(error.message || error.description, error.source, error.inner ? error.inner : {
61 | code: error.code,
62 | name: error.name
63 | });
64 | case 'RunError':
65 | return new RunError(error.message || error.description, error.source, error.inner ? error.inner : {
66 | code: error.code,
67 | name: error.name
68 | });
69 | case 'BpmnError':
70 | return new BpmnError(error.message || error.description, error, error.source);
71 | }
72 | return error;
73 | }
74 | function isKnownError(test) {
75 | if (test instanceof ActivityError) return test;
76 | if (test instanceof BpmnError) return test;
77 | if (test instanceof Error) return test;
78 | }
--------------------------------------------------------------------------------
/dist/eventDefinitions/TerminateEventDefinition.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = TerminateEventDefinition;
7 | var _messageHelper = require("../messageHelper.js");
8 | function TerminateEventDefinition(activity, eventDefinition) {
9 | const {
10 | id,
11 | broker,
12 | environment
13 | } = activity;
14 | const {
15 | type = 'TerminateEventDefinition'
16 | } = eventDefinition;
17 | this.id = id;
18 | this.type = type;
19 | this.activity = activity;
20 | this.broker = broker;
21 | this.logger = environment.Logger(type.toLowerCase());
22 | }
23 | TerminateEventDefinition.prototype.execute = function execute(executeMessage) {
24 | const executeContent = executeMessage.content;
25 | const throwContent = (0, _messageHelper.cloneContent)(executeContent, {
26 | state: 'terminate'
27 | });
28 | throwContent.parent = (0, _messageHelper.shiftParent)(executeContent.parent);
29 | this.logger.debug(`<${executeContent.executionId} (${executeContent.id})> terminate`);
30 | const broker = this.broker;
31 | broker.publish('event', 'process.terminate', throwContent, {
32 | type: 'terminate'
33 | });
34 | broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(executeContent));
35 | };
--------------------------------------------------------------------------------
/dist/eventDefinitions/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | Object.defineProperty(exports, "CancelEventDefinition", {
7 | enumerable: true,
8 | get: function () {
9 | return _CancelEventDefinition.default;
10 | }
11 | });
12 | Object.defineProperty(exports, "CompensateEventDefinition", {
13 | enumerable: true,
14 | get: function () {
15 | return _CompensateEventDefinition.default;
16 | }
17 | });
18 | Object.defineProperty(exports, "ConditionalEventDefinition", {
19 | enumerable: true,
20 | get: function () {
21 | return _ConditionalEventDefinition.default;
22 | }
23 | });
24 | Object.defineProperty(exports, "ErrorEventDefinition", {
25 | enumerable: true,
26 | get: function () {
27 | return _ErrorEventDefinition.default;
28 | }
29 | });
30 | Object.defineProperty(exports, "EscalationEventDefinition", {
31 | enumerable: true,
32 | get: function () {
33 | return _EscalationEventDefinition.default;
34 | }
35 | });
36 | Object.defineProperty(exports, "LinkEventDefinition", {
37 | enumerable: true,
38 | get: function () {
39 | return _LinkEventDefinition.default;
40 | }
41 | });
42 | Object.defineProperty(exports, "MessageEventDefinition", {
43 | enumerable: true,
44 | get: function () {
45 | return _MessageEventDefinition.default;
46 | }
47 | });
48 | Object.defineProperty(exports, "SignalEventDefinition", {
49 | enumerable: true,
50 | get: function () {
51 | return _SignalEventDefinition.default;
52 | }
53 | });
54 | Object.defineProperty(exports, "TerminateEventDefinition", {
55 | enumerable: true,
56 | get: function () {
57 | return _TerminateEventDefinition.default;
58 | }
59 | });
60 | Object.defineProperty(exports, "TimerEventDefinition", {
61 | enumerable: true,
62 | get: function () {
63 | return _TimerEventDefinition.default;
64 | }
65 | });
66 | var _CancelEventDefinition = _interopRequireDefault(require("./CancelEventDefinition.js"));
67 | var _CompensateEventDefinition = _interopRequireDefault(require("./CompensateEventDefinition.js"));
68 | var _ConditionalEventDefinition = _interopRequireDefault(require("./ConditionalEventDefinition.js"));
69 | var _ErrorEventDefinition = _interopRequireDefault(require("./ErrorEventDefinition.js"));
70 | var _EscalationEventDefinition = _interopRequireDefault(require("./EscalationEventDefinition.js"));
71 | var _LinkEventDefinition = _interopRequireDefault(require("./LinkEventDefinition.js"));
72 | var _MessageEventDefinition = _interopRequireDefault(require("./MessageEventDefinition.js"));
73 | var _SignalEventDefinition = _interopRequireDefault(require("./SignalEventDefinition.js"));
74 | var _TerminateEventDefinition = _interopRequireDefault(require("./TerminateEventDefinition.js"));
75 | var _TimerEventDefinition = _interopRequireDefault(require("./TimerEventDefinition.js"));
76 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
--------------------------------------------------------------------------------
/dist/events/EndEvent.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.EndEventBehaviour = EndEventBehaviour;
7 | exports.default = EndEvent;
8 | var _Activity = _interopRequireDefault(require("../activity/Activity.js"));
9 | var _EventDefinitionExecution = _interopRequireDefault(require("../eventDefinitions/EventDefinitionExecution.js"));
10 | var _messageHelper = require("../messageHelper.js");
11 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12 | const kExecution = Symbol.for('execution');
13 | function EndEvent(activityDef, context) {
14 | return new _Activity.default(EndEventBehaviour, {
15 | ...activityDef,
16 | isThrowing: true
17 | }, context);
18 | }
19 | function EndEventBehaviour(activity) {
20 | this.id = activity.id;
21 | this.type = activity.type;
22 | this.broker = activity.broker;
23 | this[kExecution] = activity.eventDefinitions && new _EventDefinitionExecution.default(activity, activity.eventDefinitions);
24 | }
25 | EndEventBehaviour.prototype.execute = function execute(executeMessage) {
26 | const execution = this[kExecution];
27 | if (execution) {
28 | return execution.execute(executeMessage);
29 | }
30 | return this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(executeMessage.content));
31 | };
--------------------------------------------------------------------------------
/dist/events/IntermediateCatchEvent.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.IntermediateCatchEventBehaviour = IntermediateCatchEventBehaviour;
7 | exports.default = IntermediateCatchEvent;
8 | var _Activity = _interopRequireDefault(require("../activity/Activity.js"));
9 | var _EventDefinitionExecution = _interopRequireDefault(require("../eventDefinitions/EventDefinitionExecution.js"));
10 | var _messageHelper = require("../messageHelper.js");
11 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12 | const kExecution = Symbol.for('execution');
13 | function IntermediateCatchEvent(activityDef, context) {
14 | return new _Activity.default(IntermediateCatchEventBehaviour, activityDef, context);
15 | }
16 | function IntermediateCatchEventBehaviour(activity) {
17 | this.id = activity.id;
18 | this.type = activity.type;
19 | this.broker = activity.broker;
20 | this[kExecution] = activity.eventDefinitions && new _EventDefinitionExecution.default(activity, activity.eventDefinitions);
21 | }
22 | IntermediateCatchEventBehaviour.prototype.execute = function execute(executeMessage) {
23 | const execution = this[kExecution];
24 | if (execution) {
25 | return execution.execute(executeMessage);
26 | }
27 | const executeContent = executeMessage.content;
28 | const executionId = executeContent.executionId;
29 | const broker = this.broker;
30 | broker.subscribeTmp('api', `activity.#.${executionId}`, this._onApiMessage.bind(this, executeMessage), {
31 | noAck: true,
32 | consumerTag: '_api-behaviour-execution'
33 | });
34 | return broker.publish('event', 'activity.wait', (0, _messageHelper.cloneContent)(executeContent));
35 | };
36 | IntermediateCatchEventBehaviour.prototype._onApiMessage = function onApiMessage(executeMessage, routingKey, message) {
37 | switch (message.properties.type) {
38 | case 'message':
39 | case 'signal':
40 | {
41 | const broker = this.broker;
42 | broker.cancel('_api-behaviour-execution');
43 | return broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(executeMessage.content, {
44 | output: message.content.message
45 | }));
46 | }
47 | case 'discard':
48 | {
49 | const broker = this.broker;
50 | broker.cancel('_api-behaviour-execution');
51 | return broker.publish('execution', 'execute.discard', (0, _messageHelper.cloneContent)(executeMessage.content));
52 | }
53 | case 'stop':
54 | {
55 | return this.broker.cancel('_api-behaviour-execution');
56 | }
57 | }
58 | };
--------------------------------------------------------------------------------
/dist/events/IntermediateThrowEvent.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.IntermediateThrowEventBehaviour = IntermediateThrowEventBehaviour;
7 | exports.default = IntermediateThrowEvent;
8 | var _Activity = _interopRequireDefault(require("../activity/Activity.js"));
9 | var _EventDefinitionExecution = _interopRequireDefault(require("../eventDefinitions/EventDefinitionExecution.js"));
10 | var _messageHelper = require("../messageHelper.js");
11 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12 | const kExecution = Symbol.for('execution');
13 | function IntermediateThrowEvent(activityDef, context) {
14 | return new _Activity.default(IntermediateThrowEventBehaviour, {
15 | ...activityDef,
16 | isThrowing: true
17 | }, context);
18 | }
19 | function IntermediateThrowEventBehaviour(activity) {
20 | this.id = activity.id;
21 | this.type = activity.type;
22 | this.broker = activity.broker;
23 | this[kExecution] = activity.eventDefinitions && new _EventDefinitionExecution.default(activity, activity.eventDefinitions);
24 | }
25 | IntermediateThrowEventBehaviour.prototype.execute = function execute(executeMessage) {
26 | const execution = this[kExecution];
27 | if (execution) {
28 | return execution.execute(executeMessage);
29 | }
30 | return this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(executeMessage.content));
31 | };
--------------------------------------------------------------------------------
/dist/events/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | Object.defineProperty(exports, "BoundaryEvent", {
7 | enumerable: true,
8 | get: function () {
9 | return _BoundaryEvent.default;
10 | }
11 | });
12 | Object.defineProperty(exports, "BoundaryEventBehaviour", {
13 | enumerable: true,
14 | get: function () {
15 | return _BoundaryEvent.BoundaryEventBehaviour;
16 | }
17 | });
18 | Object.defineProperty(exports, "EndEvent", {
19 | enumerable: true,
20 | get: function () {
21 | return _EndEvent.default;
22 | }
23 | });
24 | Object.defineProperty(exports, "EndEventBehaviour", {
25 | enumerable: true,
26 | get: function () {
27 | return _EndEvent.EndEventBehaviour;
28 | }
29 | });
30 | Object.defineProperty(exports, "IntermediateCatchEvent", {
31 | enumerable: true,
32 | get: function () {
33 | return _IntermediateCatchEvent.default;
34 | }
35 | });
36 | Object.defineProperty(exports, "IntermediateCatchEventBehaviour", {
37 | enumerable: true,
38 | get: function () {
39 | return _IntermediateCatchEvent.IntermediateCatchEventBehaviour;
40 | }
41 | });
42 | Object.defineProperty(exports, "IntermediateThrowEvent", {
43 | enumerable: true,
44 | get: function () {
45 | return _IntermediateThrowEvent.default;
46 | }
47 | });
48 | Object.defineProperty(exports, "IntermediateThrowEventBehaviour", {
49 | enumerable: true,
50 | get: function () {
51 | return _IntermediateThrowEvent.IntermediateThrowEventBehaviour;
52 | }
53 | });
54 | Object.defineProperty(exports, "StartEvent", {
55 | enumerable: true,
56 | get: function () {
57 | return _StartEvent.default;
58 | }
59 | });
60 | Object.defineProperty(exports, "StartEventBehaviour", {
61 | enumerable: true,
62 | get: function () {
63 | return _StartEvent.StartEventBehaviour;
64 | }
65 | });
66 | var _BoundaryEvent = _interopRequireWildcard(require("./BoundaryEvent.js"));
67 | var _EndEvent = _interopRequireWildcard(require("./EndEvent.js"));
68 | var _IntermediateCatchEvent = _interopRequireWildcard(require("./IntermediateCatchEvent.js"));
69 | var _IntermediateThrowEvent = _interopRequireWildcard(require("./IntermediateThrowEvent.js"));
70 | var _StartEvent = _interopRequireWildcard(require("./StartEvent.js"));
71 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
72 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
--------------------------------------------------------------------------------
/dist/flows/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | Object.defineProperty(exports, "Association", {
7 | enumerable: true,
8 | get: function () {
9 | return _Association.default;
10 | }
11 | });
12 | Object.defineProperty(exports, "MessageFlow", {
13 | enumerable: true,
14 | get: function () {
15 | return _MessageFlow.default;
16 | }
17 | });
18 | Object.defineProperty(exports, "SequenceFlow", {
19 | enumerable: true,
20 | get: function () {
21 | return _SequenceFlow.default;
22 | }
23 | });
24 | var _Association = _interopRequireDefault(require("./Association.js"));
25 | var _MessageFlow = _interopRequireDefault(require("./MessageFlow.js"));
26 | var _SequenceFlow = _interopRequireDefault(require("./SequenceFlow.js"));
27 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
--------------------------------------------------------------------------------
/dist/gateways/ExclusiveGateway.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.ExclusiveGatewayBehaviour = ExclusiveGatewayBehaviour;
7 | exports.default = ExclusiveGateway;
8 | var _Activity = _interopRequireDefault(require("../activity/Activity.js"));
9 | var _messageHelper = require("../messageHelper.js");
10 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11 | function ExclusiveGateway(activityDef, context) {
12 | return new _Activity.default(ExclusiveGatewayBehaviour, activityDef, context);
13 | }
14 | function ExclusiveGatewayBehaviour(activity) {
15 | const {
16 | id,
17 | type,
18 | broker
19 | } = activity;
20 | this.id = id;
21 | this.type = type;
22 | this.broker = broker;
23 | }
24 | ExclusiveGatewayBehaviour.prototype.execute = function execute({
25 | content
26 | }) {
27 | this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(content, {
28 | outboundTakeOne: true
29 | }));
30 | };
--------------------------------------------------------------------------------
/dist/gateways/InclusiveGateway.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.InclusiveGatewayBehaviour = InclusiveGatewayBehaviour;
7 | exports.default = InclusiveGateway;
8 | var _Activity = _interopRequireDefault(require("../activity/Activity.js"));
9 | var _messageHelper = require("../messageHelper.js");
10 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11 | function InclusiveGateway(activityDef, context) {
12 | return new _Activity.default(InclusiveGatewayBehaviour, activityDef, context);
13 | }
14 | function InclusiveGatewayBehaviour(activity) {
15 | const {
16 | id,
17 | type,
18 | broker
19 | } = activity;
20 | this.id = id;
21 | this.type = type;
22 | this.broker = broker;
23 | }
24 | InclusiveGatewayBehaviour.prototype.execute = function execute({
25 | content
26 | }) {
27 | this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(content));
28 | };
--------------------------------------------------------------------------------
/dist/gateways/ParallelGateway.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.ParallelGatewayBehaviour = ParallelGatewayBehaviour;
7 | exports.default = ParallelGateway;
8 | var _Activity = _interopRequireDefault(require("../activity/Activity.js"));
9 | var _messageHelper = require("../messageHelper.js");
10 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11 | function ParallelGateway(activityDef, context) {
12 | return new _Activity.default(ParallelGatewayBehaviour, {
13 | ...activityDef,
14 | isParallelGateway: true
15 | }, context);
16 | }
17 | function ParallelGatewayBehaviour(activity) {
18 | const {
19 | id,
20 | type,
21 | broker
22 | } = activity;
23 | this.id = id;
24 | this.type = type;
25 | this.broker = broker;
26 | }
27 | ParallelGatewayBehaviour.prototype.execute = function execute({
28 | content
29 | }) {
30 | this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(content));
31 | };
--------------------------------------------------------------------------------
/dist/gateways/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | Object.defineProperty(exports, "EventBasedGateway", {
7 | enumerable: true,
8 | get: function () {
9 | return _EventBasedGateway.default;
10 | }
11 | });
12 | Object.defineProperty(exports, "EventBasedGatewayBehaviour", {
13 | enumerable: true,
14 | get: function () {
15 | return _EventBasedGateway.EventBasedGatewayBehaviour;
16 | }
17 | });
18 | Object.defineProperty(exports, "ExclusiveGateway", {
19 | enumerable: true,
20 | get: function () {
21 | return _ExclusiveGateway.default;
22 | }
23 | });
24 | Object.defineProperty(exports, "ExclusiveGatewayBehaviour", {
25 | enumerable: true,
26 | get: function () {
27 | return _ExclusiveGateway.ExclusiveGatewayBehaviour;
28 | }
29 | });
30 | Object.defineProperty(exports, "InclusiveGateway", {
31 | enumerable: true,
32 | get: function () {
33 | return _InclusiveGateway.default;
34 | }
35 | });
36 | Object.defineProperty(exports, "InclusiveGatewayBehaviour", {
37 | enumerable: true,
38 | get: function () {
39 | return _InclusiveGateway.InclusiveGatewayBehaviour;
40 | }
41 | });
42 | Object.defineProperty(exports, "ParallelGateway", {
43 | enumerable: true,
44 | get: function () {
45 | return _ParallelGateway.default;
46 | }
47 | });
48 | Object.defineProperty(exports, "ParallelGatewayBehaviour", {
49 | enumerable: true,
50 | get: function () {
51 | return _ParallelGateway.ParallelGatewayBehaviour;
52 | }
53 | });
54 | var _EventBasedGateway = _interopRequireWildcard(require("./EventBasedGateway.js"));
55 | var _ExclusiveGateway = _interopRequireWildcard(require("./ExclusiveGateway.js"));
56 | var _InclusiveGateway = _interopRequireWildcard(require("./InclusiveGateway.js"));
57 | var _ParallelGateway = _interopRequireWildcard(require("./ParallelGateway.js"));
58 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
59 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
--------------------------------------------------------------------------------
/dist/io/BpmnIO.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = BpmnIO;
7 | function BpmnIO(activity, context) {
8 | this.activity = activity;
9 | this.context = context;
10 | this.type = 'bpmnio';
11 | const {
12 | ioSpecification: ioSpecificationDef,
13 | properties: propertiesDef
14 | } = activity.behaviour;
15 | this.specification = ioSpecificationDef && new ioSpecificationDef.Behaviour(activity, ioSpecificationDef, context);
16 | this.properties = propertiesDef && new propertiesDef.Behaviour(activity, propertiesDef, context);
17 | }
18 | Object.defineProperty(BpmnIO.prototype, 'hasIo', {
19 | get() {
20 | return this.specification || this.properties;
21 | }
22 | });
23 | BpmnIO.prototype.activate = function activate(message) {
24 | const properties = this.properties,
25 | specification = this.specification;
26 | if (properties) properties.activate(message);
27 | if (specification) specification.activate(message);
28 | };
29 | BpmnIO.prototype.deactivate = function deactivate(message) {
30 | const properties = this.properties,
31 | specification = this.specification;
32 | if (properties) properties.deactivate(message);
33 | if (specification) specification.deactivate(message);
34 | };
--------------------------------------------------------------------------------
/dist/io/EnvironmentDataObject.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = EnvironmentDataObject;
7 | function EnvironmentDataObject(dataObjectDef, {
8 | environment
9 | }) {
10 | const {
11 | id,
12 | type,
13 | name,
14 | behaviour,
15 | parent
16 | } = dataObjectDef;
17 | this.id = id;
18 | this.type = type;
19 | this.name = name;
20 | this.behaviour = behaviour;
21 | this.parent = parent;
22 | this.environment = environment;
23 | }
24 | EnvironmentDataObject.prototype.read = function read(broker, exchange, routingKeyPrefix, messageProperties) {
25 | const environment = this.environment;
26 | const value = environment.variables._data?.[this.id];
27 | const content = this._createContent(value);
28 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
29 | };
30 | EnvironmentDataObject.prototype.write = function write(broker, exchange, routingKeyPrefix, value, messageProperties) {
31 | const environment = this.environment;
32 | environment.variables._data = environment.variables._data || {};
33 | environment.variables._data[this.id] = value;
34 | const content = this._createContent(value);
35 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
36 | };
37 | EnvironmentDataObject.prototype._createContent = function createContent(value) {
38 | return {
39 | id: this.id,
40 | type: this.type,
41 | name: this.name,
42 | value
43 | };
44 | };
--------------------------------------------------------------------------------
/dist/io/EnvironmentDataStore.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = EnvironmentDataStore;
7 | function EnvironmentDataStore(dataStoreDef, {
8 | environment
9 | }) {
10 | const {
11 | id,
12 | type,
13 | name,
14 | behaviour,
15 | parent
16 | } = dataStoreDef;
17 | this.id = id;
18 | this.type = type;
19 | this.name = name;
20 | this.behaviour = behaviour;
21 | this.parent = parent;
22 | this.environment = environment;
23 | }
24 | EnvironmentDataStore.prototype.read = function read(broker, exchange, routingKeyPrefix, messageProperties) {
25 | const environment = this.environment;
26 | const value = environment.variables._data?.[this.id];
27 | const content = this._createContent(value);
28 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
29 | };
30 | EnvironmentDataStore.prototype.write = function write(broker, exchange, routingKeyPrefix, value, messageProperties) {
31 | const environment = this.environment;
32 | environment.variables._data = environment.variables._data || {};
33 | environment.variables._data[this.id] = value;
34 | const content = this._createContent(value);
35 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
36 | };
37 | EnvironmentDataStore.prototype._createContent = function createContent(value) {
38 | return {
39 | id: this.id,
40 | type: this.type,
41 | name: this.name,
42 | value
43 | };
44 | };
--------------------------------------------------------------------------------
/dist/io/EnvironmentDataStoreReference.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = EnvironmentDataStoreReference;
7 | function EnvironmentDataStoreReference(dataObjectDef, {
8 | environment
9 | }) {
10 | const {
11 | id,
12 | type,
13 | name,
14 | behaviour,
15 | parent
16 | } = dataObjectDef;
17 | this.id = id;
18 | this.type = type;
19 | this.name = name;
20 | this.behaviour = behaviour;
21 | this.parent = parent;
22 | this.environment = environment;
23 | }
24 | EnvironmentDataStoreReference.prototype.read = function read(broker, exchange, routingKeyPrefix, messageProperties) {
25 | const environment = this.environment;
26 | const value = environment.variables._data?.[this.id];
27 | const content = this._createContent(value);
28 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
29 | };
30 | EnvironmentDataStoreReference.prototype.write = function write(broker, exchange, routingKeyPrefix, value, messageProperties) {
31 | const environment = this.environment;
32 | environment.variables._data = environment.variables._data || {};
33 | environment.variables._data[this.id] = value;
34 | const content = this._createContent(value);
35 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
36 | };
37 | EnvironmentDataStoreReference.prototype._createContent = function createContent(value) {
38 | return {
39 | id: this.id,
40 | type: this.type,
41 | name: this.name,
42 | value
43 | };
44 | };
--------------------------------------------------------------------------------
/dist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "commonjs"
3 | }
4 |
--------------------------------------------------------------------------------
/dist/process/Lane.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = Lane;
7 | const kProcess = Symbol.for('process');
8 | function Lane(process, laneDefinition) {
9 | const {
10 | broker,
11 | environment
12 | } = process;
13 | const {
14 | id,
15 | type,
16 | behaviour
17 | } = laneDefinition;
18 | this[kProcess] = process;
19 | this.id = id;
20 | this.type = type;
21 | this.name = behaviour.name;
22 | this.parent = {
23 | id: process.id,
24 | type: process.type
25 | };
26 | this.behaviour = {
27 | ...behaviour
28 | };
29 | this.environment = environment;
30 | this.broker = broker;
31 | this.context = process.context;
32 | this.logger = environment.Logger(type.toLowerCase());
33 | }
34 | Object.defineProperty(Lane.prototype, 'process', {
35 | get() {
36 | return this[kProcess];
37 | }
38 | });
--------------------------------------------------------------------------------
/dist/shared.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.brokerSafeId = brokerSafeId;
7 | exports.generateId = generateId;
8 | exports.getOptionsAndCallback = getOptionsAndCallback;
9 | exports.getUniqueId = getUniqueId;
10 | const safePattern = /[./\\#*:\s]/g;
11 | function generateId() {
12 | return Math.random().toString(16).substring(2, 12);
13 | }
14 | function brokerSafeId(id) {
15 | return id.replace(safePattern, '_');
16 | }
17 | function getUniqueId(prefix) {
18 | return `${brokerSafeId(prefix)}_${generateId()}`;
19 | }
20 | function getOptionsAndCallback(optionsOrCallback, callback) {
21 | let options;
22 | if (typeof optionsOrCallback === 'function') {
23 | callback = optionsOrCallback;
24 | } else {
25 | options = optionsOrCallback;
26 | }
27 | return [options, callback];
28 | }
--------------------------------------------------------------------------------
/dist/tasks/ScriptTask.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.ScriptTaskBehaviour = ScriptTaskBehaviour;
7 | exports.default = ScriptTask;
8 | var _Activity = _interopRequireDefault(require("../activity/Activity.js"));
9 | var _ExecutionScope = _interopRequireDefault(require("../activity/ExecutionScope.js"));
10 | var _Errors = require("../error/Errors.js");
11 | var _messageHelper = require("../messageHelper.js");
12 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13 | function ScriptTask(activityDef, context) {
14 | return new _Activity.default(ScriptTaskBehaviour, activityDef, context);
15 | }
16 | function ScriptTaskBehaviour(activity) {
17 | const {
18 | id,
19 | type,
20 | behaviour
21 | } = activity;
22 | this.id = id;
23 | this.type = type;
24 | this.scriptFormat = behaviour.scriptFormat;
25 | this.loopCharacteristics = behaviour.loopCharacteristics && new behaviour.loopCharacteristics.Behaviour(activity, behaviour.loopCharacteristics);
26 | this.activity = activity;
27 | const environment = this.environment = activity.environment;
28 | environment.registerScript(activity);
29 | }
30 | ScriptTaskBehaviour.prototype.execute = function execute(executeMessage) {
31 | const executeContent = executeMessage.content;
32 | const loopCharacteristics = this.loopCharacteristics;
33 | if (loopCharacteristics && executeContent.isRootScope) {
34 | return loopCharacteristics.execute(executeMessage);
35 | }
36 | const activity = this.activity;
37 | const scriptFormat = this.scriptFormat;
38 | const script = this.environment.getScript(scriptFormat, activity, (0, _messageHelper.cloneMessage)(executeMessage));
39 | if (!script) {
40 | return activity.emitFatal(new _Errors.ActivityError(`Script format ${scriptFormat} is unsupported or was not registered for <${activity.id}>`, executeMessage), executeContent);
41 | }
42 | return script.execute((0, _ExecutionScope.default)(activity, executeMessage), scriptCallback);
43 | function scriptCallback(err, output) {
44 | if (err) {
45 | activity.logger.error(`<${executeContent.executionId} (${activity.id})>`, err);
46 | return activity.broker.publish('execution', 'execute.error', (0, _messageHelper.cloneContent)(executeContent, {
47 | error: new _Errors.ActivityError(err.message, executeMessage, err)
48 | }, {
49 | mandatory: true
50 | }));
51 | }
52 | return activity.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(executeContent, {
53 | output
54 | }));
55 | }
56 | };
--------------------------------------------------------------------------------
/dist/tasks/ServiceImplementation.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = ServiceImplementation;
7 | var _ExecutionScope = _interopRequireDefault(require("../activity/ExecutionScope.js"));
8 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9 | function ServiceImplementation(activity) {
10 | this.type = `${activity.type}:implementation`;
11 | this.implementation = activity.behaviour.implementation;
12 | this.activity = activity;
13 | }
14 | ServiceImplementation.prototype.execute = function execute(executionMessage, callback) {
15 | const activity = this.activity;
16 | const implementation = this.implementation;
17 | const serviceFn = activity.environment.resolveExpression(implementation, executionMessage);
18 | if (typeof serviceFn !== 'function') return callback(new Error(`Implementation ${implementation} did not resolve to a function`));
19 | serviceFn.call(activity, (0, _ExecutionScope.default)(activity, executionMessage), (err, ...args) => {
20 | callback(err, args);
21 | });
22 | };
--------------------------------------------------------------------------------
/dist/tasks/StandardLoopCharacteristics.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = StandardLoopCharacteristics;
7 | var _LoopCharacteristics = _interopRequireDefault(require("./LoopCharacteristics.js"));
8 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9 | function StandardLoopCharacteristics(activity, loopCharacteristics) {
10 | let {
11 | behaviour
12 | } = loopCharacteristics;
13 | behaviour = {
14 | ...behaviour,
15 | isSequential: true
16 | };
17 | return new _LoopCharacteristics.default(activity, {
18 | ...loopCharacteristics,
19 | behaviour
20 | });
21 | }
--------------------------------------------------------------------------------
/dist/tasks/Task.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.TaskBehaviour = TaskBehaviour;
7 | exports.default = Task;
8 | var _Activity = _interopRequireDefault(require("../activity/Activity.js"));
9 | var _messageHelper = require("../messageHelper.js");
10 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11 | function Task(activityDef, context) {
12 | return new _Activity.default(TaskBehaviour, activityDef, context);
13 | }
14 | function TaskBehaviour(activity) {
15 | const {
16 | id,
17 | type,
18 | behaviour,
19 | broker
20 | } = activity;
21 | this.id = id;
22 | this.type = type;
23 | this.loopCharacteristics = behaviour.loopCharacteristics && new behaviour.loopCharacteristics.Behaviour(activity, behaviour.loopCharacteristics);
24 | this.broker = broker;
25 | }
26 | TaskBehaviour.prototype.execute = function execute(executeMessage) {
27 | const executeContent = executeMessage.content;
28 | const loopCharacteristics = this.loopCharacteristics;
29 | if (loopCharacteristics && executeContent.isRootScope) {
30 | return loopCharacteristics.execute(executeMessage);
31 | }
32 | return this.broker.publish('execution', 'execute.completed', (0, _messageHelper.cloneContent)(executeContent));
33 | };
--------------------------------------------------------------------------------
/dist/tasks/Transaction.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = Transaction;
7 | var _SubProcess = _interopRequireDefault(require("./SubProcess.js"));
8 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9 | function Transaction(activityDef, context) {
10 | const transaction = {
11 | type: 'transaction',
12 | ...activityDef,
13 | isTransaction: true
14 | };
15 | const activity = (0, _SubProcess.default)(transaction, context);
16 | return activity;
17 | }
--------------------------------------------------------------------------------
/docs/ActivityExecution.md:
--------------------------------------------------------------------------------
1 | # Activity execution
2 |
3 | Shared activity execution.
4 |
5 | 
6 |
7 | ## `new ActivityExecution(activity, context)`
8 |
9 | Arguments:
10 |
11 | - `activity`: parent [activity](/docs/Activity.md) function
12 | - `context`: [shared context](/docs/Context.md)
13 |
14 | Properties:
15 |
16 | - `completed`: has execution completed
17 | - `source`: instance of activity [behaviour](/docs/Extend.md)
18 |
19 | ### `discard()`
20 |
21 | Discard execution.
22 |
23 | ### `execute(executeMessage)`
24 |
25 | Execute activity behaviour with message.
26 |
27 | ### `getApi(message)`
28 |
29 | Get activity [api](/docs/SharedApi.md).
30 |
31 | ### `getPostponed()`
32 |
33 | Get activity executions that are in a postponed state. Returns list of [api](/docs/SharedApi.md).
34 |
35 | ### `getState()`
36 |
37 | Get activity execution state.
38 |
39 | ### `recover([state])`
40 |
41 | Recover activity execution state.
42 |
43 | ### `stop()`
44 |
45 | Stop execution
46 |
--------------------------------------------------------------------------------
/docs/BpmnIO.md:
--------------------------------------------------------------------------------
1 | # Bpmn IO
2 |
3 | The implementation of data storage in this project is rather basic. It relies on object assigned to `environment.variables._data`. Use it for reference and preferably override it with your own implementation.
4 |
5 | # DataObject
6 |
7 | Process data object element
8 |
9 | # DataStoreReference
10 |
11 | Process data store reference element. Will be returned since some modellers don't add a datastore when designing.
12 |
13 | # DataStore
14 |
15 | Definition data store
16 |
17 | # InputOutputSpecification
18 |
19 | Activity input/output element
20 |
21 | # Property
22 |
23 | Activity property element
24 |
--------------------------------------------------------------------------------
/docs/CallActivity.md:
--------------------------------------------------------------------------------
1 | # CallActivity
2 |
3 | # Behaviour
4 |
5 | Call activity will wait for called process to complete.
6 |
7 | Process defined in the same definition can be started. If the process is not found the call activity expects to be signaled or cancelled.
8 |
9 | Emits `activity.call` event with `calledElement` property containing the id of the process. Expressions can be used to resolve the `calledElement` property value.
10 |
11 | If the call activity is cancelled the target process is discarded.
12 |
13 | If the target process is discarded the call activity is cancelled.
14 |
15 | If the target process throws the call activity is errored. The call activity can catch the error with a boundary error event.
16 |
--------------------------------------------------------------------------------
/docs/ConditionalEventDefinition.md:
--------------------------------------------------------------------------------
1 | # ConditionalEventDefinition
2 |
3 | ConditionalEventDefinition behaviour.
4 |
5 | - Checks condition when event is first executed
6 | - Expects to be signalled to check condition again
7 |
8 | ```javascript
9 | import { Definition } from 'bpmn-elements';
10 |
11 | import testHelpers from '../test/helpers/testHelpers.js';
12 | import factory from '../test/helpers/factory.js';
13 |
14 | const boundEventSource = factory.resource('conditional-bound-js-event.bpmn');
15 |
16 | const context = await testHelpers.context(boundEventSource);
17 | const definition = new Definition(context);
18 |
19 | const waiting = definition.waitFor('wait', (_routingKey, api) => {
20 | return !!api.content.condition;
21 | });
22 |
23 | const condition1 = definition.waitFor('activity.condition');
24 |
25 | definition.run();
26 |
27 | console.log('condition type', (await waiting).content.condition);
28 | console.log('first condition result', (await condition1).content.conditionResult);
29 |
30 | const condition2 = definition.waitFor('activity.condition');
31 | const completed = definition.waitFor('leave');
32 |
33 | definition.signal({ id: 'cond' });
34 |
35 | console.log('signal condition result', (await condition2).content.conditionResult);
36 |
37 | await completed;
38 | ```
39 |
40 | ## ConditionalEventDefinition events
41 |
42 | ### `activity.wait`
43 |
44 | Fired when condition is waiting for signal.
45 |
46 | ### `activity.condition`
47 |
48 | Fired when condition is checked.
49 |
--------------------------------------------------------------------------------
/docs/Context.md:
--------------------------------------------------------------------------------
1 | # Context
2 |
3 | Shared context.
4 |
5 | ## `new Context(serializableContext[, environment])`
6 |
7 | Arguments:
8 |
9 | - `serializableContext`: serializable context. Maybe from [moddle-context-serializer](https://www.npmjs.com/package/moddle-context-serializer)
10 | - `environment`: optional [Environment](/docs/Environment.md) instance
11 |
12 | Returns api.
13 |
14 | Properties:
15 |
16 | - `id`: definition id
17 | - `name`: definition name
18 | - `type`: definition type
19 | - `sid`: some unique id
20 | - `definitionContext`: the passed serializable context
21 | - `environment`: [Environment](/docs/Environment.md) instance
22 | - `owner`: reference to owning process or sub process
23 |
24 | ### `clone([environment])`
25 |
26 | Clone context.
27 |
28 | Arguments:
29 |
30 | - `environment`: optional new environment for cloned context
31 |
32 | Returns clone of context with new activity instances.
33 |
34 | ### `getActivities([scopeId])`
35 |
36 | Get all [activity instances](/docs/Activity.md) scoped to id.
37 |
38 | ### `getActivityById(id)`
39 |
40 | Get [activity instance](/docs/Activity.md) by id.
41 |
42 | ### `getExecutableProcesses()`
43 |
44 | Get executable processes.
45 |
46 | ### `getDataObjectById(id)`
47 |
48 | Get data object by id.
49 |
50 | ### `getMessageFlows()`
51 |
52 | Get data object by id.
53 |
54 | ### `getProcessById(id)`
55 |
56 | Get process by id.
57 |
58 | ### `getProcesses()`
59 |
60 | Get all processes.
61 |
62 | ### `getSequenceFlowById(id)`
63 |
64 | Get sequence flow instances by id.
65 |
66 | ### `getSequenceFlows(scopeId)`
67 |
68 | Get all sequence flow instances and/or scoped to id.
69 |
70 | ### `getInboundSequenceFlows(activityId)`
71 |
72 | Get activity inbound sequence flows.
73 |
74 | ### `getOutboundSequenceFlows(activityId)`
75 |
76 | Get activity outbound sequence flows.
77 |
78 | ### `loadExtensions(activity)`
79 |
80 | Load [extensions](/docs/Extension.md) for activity.
81 |
--------------------------------------------------------------------------------
/docs/Environment.md:
--------------------------------------------------------------------------------
1 | # Environment
2 |
3 | Shared environment.
4 |
5 | ## `new Environment([options])`
6 |
7 | Arguments:
8 |
9 | - `options`: optional options
10 | - `variables`: optional variables object
11 | - `output`: optional output object
12 | - `services`: optional named services object, key is name of service and value must be a function
13 | - `settings`: optional settings
14 | - `step`: boolean, true makes activity runs to go forward in steps, defaults to false
15 | - `enableDummyService`: boolean, true returns dummy service function for service task
16 | - `strict`: boolean, [strict mode](#strict-mode) defaults to false
17 | - `batchSize`: optional positive integer to control parallel loop batch size, defaults to 50
18 | - `disableTrackState`: optional boolean to disable tracking of element counters between recover and resume. State of idle elements are not returned when getting state. Recommended if running and recovering really large flows
19 | - `scripts`: [Scripts instance](/docs/Scripts.md)
20 | - `timers`: [Timers instance](/docs/Timers.md)
21 | - `expressions`: expressions handler, defaults to [Expressions instance](/docs/Expression.md)
22 | - `Logger`: optional [Logger](#logger) defaults to a dummy logger that does basically nothing but supply the required log functions
23 | - `extensions`: [extensions](/docs/Extension.md) object
24 |
25 | Properties:
26 |
27 | - `options`: initial options
28 | - `extensions`: extensions
29 | - `output`: output object
30 | - `scripts`: [Scripts instance](/docs/Scripts.md)
31 | - `expressions`: expressions handler
32 | - `services`: services
33 | - `settings`: settings object
34 | - `variables`: getter for variables object
35 | - `Logger`: passed logger initiator
36 |
37 | ### `addService(name, serviceFn)`
38 |
39 | ### `assignVariables(vars)`
40 |
41 | ### `clone([overrideOptions])`
42 |
43 | ### `getScript(scriptType, activity)`
44 |
45 | ### `getServiceByName(name)`
46 |
47 | Get service by name
48 |
49 | ### `getState()`
50 |
51 | ### `registerScript(activity)`
52 |
53 | ### `resolveExpression(expression[, message = {}, expressionFnContext])`
54 |
55 | Resolve expression.
56 |
57 | Arguments:
58 |
59 | - `expression`: expression string
60 | - `message`: optional object from where to resolve expressions, the environment instance is added by default
61 | - `expressionFnContext`: optional call context (this)
62 |
63 | ### `recover(state)`
64 |
65 | ## Strict mode
66 |
67 | If enabled Boundary event with error event definition only catches thrown Bpmn Errors.
68 |
69 | ## Logger
70 |
71 | Logger factory.
72 |
73 | ### `Logger(scope)`
74 |
75 | Create new logger for scope.
76 |
77 | Must return the following logging functions:
78 |
79 | - `debug`
80 | - `error`
81 | - `warn`
82 |
83 | ## Example implementation for nodejs
84 |
85 | ```js
86 | import Debug from 'debug';
87 |
88 | export function Logger(scope) {
89 | return {
90 | debug: Debug('bpmn-elements:' + scope),
91 | error: Debug('bpmn-elements:error:' + scope),
92 | warn: Debug('bpmn-elements:warn:' + scope),
93 | };
94 | }
95 | ```
96 |
--------------------------------------------------------------------------------
/docs/Examples.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | # ServiceTask example
4 |
5 | ```javascript
6 | import * as elements from 'bpmn-elements';
7 |
8 | import BpmnModdle from 'bpmn-moddle';
9 |
10 | import { default as serialize, TypeResolver } from 'moddle-context-serializer';
11 |
12 | const { Context, Definition } = elements;
13 | const typeResolver = TypeResolver(elements);
14 |
15 | const source = `
16 |
17 |
18 |
19 |
20 |
21 | `;
22 |
23 | run();
24 |
25 | async function run() {
26 | const moddleContext = await getModdleContext(source);
27 | const options = {
28 | Logger,
29 | services: {
30 | myService(arg, next) {
31 | next();
32 | },
33 | },
34 | };
35 | const context = new Context(serialize(moddleContext, typeResolver));
36 |
37 | const definition = new Definition(context, options);
38 | definition.run();
39 | }
40 |
41 | function getModdleContext(sourceXml) {
42 | const bpmnModdle = new BpmnModdle();
43 | return bpmnModdle.fromXML(sourceXml.trim());
44 | }
45 |
46 | function Logger(scope) {
47 | return {
48 | debug: console.debug.bind(console, 'bpmn-elements:' + scope),
49 | error: console.error.bind(console, 'bpmn-elements:' + scope),
50 | warn: console.warn.bind(console, 'bpmn-elements:' + scope),
51 | };
52 | }
53 | ```
54 |
--------------------------------------------------------------------------------
/docs/ExecutionScope.md:
--------------------------------------------------------------------------------
1 | # ExecutionScope
2 |
3 | When calling services and scripts the following scope is provided.
4 |
5 | Properties:
6 |
7 | - `id`: calling element id
8 | - `type`: calling element type
9 | - `fields`: execution message fields
10 | - `content`: execution message content
11 | - `properties`: execution message properties
12 | - `environment`: [environment](/docs/Environment.md)
13 | - `logger`: calling element [logger](/docs/Environment.md#logger) instance
14 | - `ActivityError`: reference to error class
15 | - `BpmnError`: reference to error class
16 |
17 | ## `resolveExpression(expression)`
18 |
19 | Passed to environment resolve expression function.
20 |
21 | ## `ActivityError(message, sourceMessage[, inner])`
22 |
23 | Reference to activity error.
24 |
25 | ## `BpmnError(message[, behaviour, sourceMessage, inner])`
26 |
27 | Reference to Bpmn error.
28 |
--------------------------------------------------------------------------------
/docs/Expression.md:
--------------------------------------------------------------------------------
1 | # Expressions
2 |
3 | Expressions handler interface.
4 |
5 | - `Expressions`
6 | - `resolveExpression(expression[, context, fnContext])`: resolve expression
7 | - `isExpression(testString)`: optional function to evaluate if string is an expression
8 | - `hasExpression(testString)`: optional function to evaluate if the string contains an expression
9 |
10 | # Standard
11 |
12 | ## `resolveExpression(expression[, context, fnContext])`
13 |
14 | Resolve expression.
15 |
16 | Arguments:
17 |
18 | - `expression`: expresion templated string
19 | - `context`: optional context from where to resolve expressions
20 | - `fnContext`: optional call context (this)
21 |
22 | ## `isExpression(testString)`
23 |
24 | Evaluate if a string is an expression, and only an expression, e.g. `${environment.variables.supersecret}`.
25 |
26 | ## `hasExpression(testString)`
27 |
28 | Evaluate if a string contains an expression, e.g. `${environment.variables.username}:${environment.variables.supersecret}`.
29 |
30 | ## Default expression handling
31 |
32 | Default expressions come in the form of `${.}`.
33 |
34 | The following expressions are supported:
35 |
36 | - `${environment.variables.input}` - resolves to the variable input
37 | - `${environment.variables.input[0]}` - resolves to first item of the variable input array
38 | - `${environment.variables.input[-1]}` - resolves to last item of the variable input array
39 | - `${environment.variables.input[spaced name]}` - resolves to the variable input object property `spaced name`
40 |
41 | - `${environment.services.getInput}` - return the service function `getInput`
42 | - `${environment.services.getInput()}` - executes the service function `getInput` with the argument `{services, variables}`
43 | - `${environment.services.isBelow(content.input,2)}` - executes the service function `isBelow` with result of `variable.input` value and 2
44 |
45 | - `I, ${content.id}, execute with id ${content.executionId}` - formats a string addressing content object values
46 |
47 | and, as utility:
48 |
49 | - `${true}` - return Boolean value `true`
50 | - `${false}` - return Boolean value `false`
51 |
52 | > Expressions in expressions is **not** supported and has unforeseeable outcome!
53 |
54 | # Community
55 |
56 | ## [`expression-parser`](/aircall/aircall-expression-parser)
57 |
58 | A far more advanced expression parser by [Aircall](/aircall).
59 |
60 | `npm i @aircall/expression-parser`
61 |
--------------------------------------------------------------------------------
/docs/LoopCharacteristics.md:
--------------------------------------------------------------------------------
1 | # LoopCharacteristics
2 |
3 | Task loops can made based on conditions, cardinality, and/or a collection.
4 |
5 | ## `bpmn:multiInstanceLoopCharacteristics`
6 |
7 | Multi instance loop.
8 |
9 | ### Sequential
10 |
11 | Sequential loops is the default, or `isSequential="true"` as the scheme states.
12 |
13 | ### Parallel
14 |
15 | Parallel loops, or `isSequential="false"` as the scheme states, requires either collection or cardinality.
16 |
17 | ## `bpmn:standardLoopCharacteristics`
18 |
19 | Behaves as a sequential multi instance loop.
20 |
21 | Cardinality is defined as an XML-attribute: `loopMaximum="4"`. An expression can be used as well.
22 |
23 | ## Cardinality loop
24 |
25 | Loop a fixed number of times or until number of iterations match cardinality. The cardinality body an integer or an [expression](/docs/Expression.md).
26 |
27 | ```xml
28 | ${environment.variables.maxCardinality}
29 | ```
30 |
31 | or as activity behaviour
32 |
33 | ```json
34 | {
35 | "id": "task1",
36 | "type": "bpmn:UserTask",
37 | "behaviour": {
38 | "loopCharacteristics": {
39 | "loopCardinality": "${environment.variables.maxCardinality}"
40 | }
41 | }
42 | }
43 | ```
44 |
45 | ## Conditional loop
46 |
47 | Loop until condition is met. The condition body can be a script or an [expression](/docs/Expression.md).
48 |
49 | ```xml
50 | ${environment.services.condition(content.index)}
51 | ```
52 |
53 | or as activity behaviour
54 |
55 | ```json
56 | {
57 | "id": "task1",
58 | "type": "bpmn:UserTask",
59 | "behaviour": {
60 | "loopCharacteristics": {
61 | "completionCondition": "${environment.services.condition(content.index)}"
62 | }
63 | }
64 | }
65 | ```
66 |
67 | ## Collection loop
68 |
69 | Loop all items in a list. The `collection` and `elementVariable` attributes are schema extensions. They are picked up and resolved when executing the task.
70 |
71 | ```xml
72 |
73 | ```
74 |
75 | or as activity behaviour
76 |
77 | ```json
78 | {
79 | "id": "task1",
80 | "type": "bpmn:UserTask",
81 | "behaviour": {
82 | "loopCharacteristics": {
83 | "collection": "${environment.variables.list}",
84 | "elementVariable": "listItem"
85 | }
86 | }
87 | }
88 | ```
89 |
--------------------------------------------------------------------------------
/docs/ParallelGateway.md:
--------------------------------------------------------------------------------
1 | # ParallelGateway
2 |
3 | Join or fork gateway.
4 |
5 | ## Join Edge case
6 |
7 | There is an edge case where the behaviour of a joining parallel gateway is not clear to yours truly. Suggestions on how to tackle this are appreciated.
8 |
9 | ### Case
10 |
11 | If inbound sequence flows are touched more than once before the last join inbound flow have completed: what is the expected joining gateway behaviour?
12 |
13 | ### Proposed behaviours
14 |
15 | 1. Ignore inbound flows that are touched more than once before all have been touched
16 | 2. Collect all inbound flow actions and complete join as soon as the last inbound flow is touched
17 |
18 | This project do the latter.
19 |
20 | Both behaviour comes with the same caveat.
21 |
22 | 
23 |
24 | The success of the above example is depending on how the sequence flows are ordered in the source (XML). If the flow `default` comes before `take` or `discard` the process will stall. The default flow will publish a discard initializing the join gateway. As soon as either `take` or `discard` is touched the join gateway will consider the join fulfilled and continue. Thus, the second `task` outbound flow will initiate a new join. But in vain since no more flow actions will come from `decision` gateway. The process has stalled.
25 |
26 | But, if the `default` is placed at after `take` or `discard` in the source, both of them will manage to touch the `toJoin` flow before the `default` flow is touched.
27 |
--------------------------------------------------------------------------------
/docs/Scripts.md:
--------------------------------------------------------------------------------
1 | # Scripts
2 |
3 | Inline scripts handler interface.
4 |
5 | - `Scripts`
6 | - `register(activity)`: register script
7 | - `getScript(scriptType, activity)`: get registered script
8 | - `execute(executionContext, callback)`
9 |
10 | ## `register(activity)`
11 |
12 | Register script. Called from activity behaviour.
13 |
14 | Arguments:
15 |
16 | - `activity`: [activity](/docs/Activity.md) instance
17 |
18 | ## `getScript(scriptType, activity)`
19 |
20 | Get registered script. Called from activity behaviour when executing.
21 |
22 | Arguments:
23 |
24 | - `scriptType`: script type from definition
25 | - `activity`: [activity](/docs/Activity.md) with script
26 |
27 | Must return interface with one required function that is named `execute`.
28 |
29 | The execute function will receive an [execution context](/docs/ExecutionScope.md) and a callback that should be called when the execution is completed.
30 |
31 | ## Example implementation for nodejs
32 |
33 | ```js
34 | import { Script } from 'vm';
35 |
36 | export function Scripts() {
37 | const scripts = {};
38 |
39 | return {
40 | getScript,
41 | register,
42 | };
43 |
44 | function register({ id, type, behaviour, environment }) {
45 | let scriptBody, language;
46 |
47 | switch (type) {
48 | case 'bpmn:SequenceFlow': {
49 | if (!behaviour.conditionExpression) return;
50 | language = behaviour.conditionExpression.language;
51 | if (!language) return;
52 | scriptBody = behaviour.conditionExpression.body;
53 | break;
54 | }
55 | default: {
56 | language = behaviour.scriptFormat;
57 | scriptBody = behaviour.script;
58 | }
59 | }
60 |
61 | if (!/^javascript$/i.test(language)) return;
62 |
63 | const script = new JavaScript(language, `${type}/${id}`, scriptBody, environment);
64 | scripts[id] = script;
65 |
66 | return script;
67 | }
68 |
69 | function getScript(language, { id }) {
70 | return scripts[id];
71 | }
72 | }
73 |
74 | function JavaScript(language, filename, scriptBody, environment) {
75 | this.id = filename;
76 | this.script = new Script(scriptBody, { filename });
77 | this.language = language;
78 | this.environment = environment;
79 | }
80 |
81 | JavaScript.prototype.execute = function execute(executionContext, callback) {
82 | const timers = this.environment.timers.register(executionContext);
83 | return this.script.runInNewContext({ ...executionContext, ...timers, next: callback });
84 | };
85 | ```
86 |
--------------------------------------------------------------------------------
/docs/SequenceFlow.md:
--------------------------------------------------------------------------------
1 | # SequenceFlow
2 |
3 | Sequence flow behaviour.
4 |
5 | # Conditional flows
6 |
7 | All outbound sequence flows can have conditions. Flows are evaluated in sequence. Default flow will be taken if no other flow was taken.
8 |
9 | Example source:
10 |
11 | ```xml
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | next(null, this.environment.variables.take4);
20 |
21 |
22 | ${environment.variables.take5}
23 |
24 |
25 |
26 |
27 |
28 |
29 | `;
30 | ```
31 |
32 | Sequence flows:
33 |
34 | - `to-task2`: default flow. If no other flow was taken then default flow is taken
35 | - `to-task3`: unconditional. Flow is taken
36 | - `to-task4`: script condition. Callback (next) is called with environment variable as result. If result is truthy the flow is taken, otherwise discarded
37 | - `to-task5`: expression condition. Expression will be evaluated and passed as result. If result is truthy the flow is taken, otherwise discarded
38 |
--------------------------------------------------------------------------------
/docs/ServiceTask.md:
--------------------------------------------------------------------------------
1 | # ServiceTask
2 |
3 | Service task behaviour.
4 |
5 | To define service task service function you can use an expression in the implementation attribute. The value of the implementation attribute will be picked up by the service task and resolved as an [expression](/docs/Expression.md).
6 |
7 | Example source:
8 |
9 | ```xml
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | `;
18 | ```
19 |
20 | Define your [environment](/docs/Environment.md) with the service functions.
21 |
22 | ```js
23 | new Environment({
24 | services: {
25 | get(executionContext, callback) {
26 | callback();
27 | },
28 | getService(messageContent) {
29 | return function myService(executionContext, callback) {
30 | callback();
31 | };
32 | },
33 | },
34 | });
35 | ```
36 |
37 | The expressions will be resolved when the service task executes.
38 |
39 | The service function is called with an [execution context](/docs/ExecutionScope.md) and a callback.
40 |
--------------------------------------------------------------------------------
/docs/SharedApi.md:
--------------------------------------------------------------------------------
1 | # Shared Api
2 |
3 | Activity, Process, and Definition elements share the same api interface. The element must not necessarely implement listeners for all the api calls.
4 |
5 | The Api is composed from the element event message.
6 |
7 | Api properties:
8 |
9 | - `id`: element id
10 | - `type`: element type
11 | - `name`: element name
12 | - `executionId`: current execution id
13 | - `environment`: shared [environment](/docs/Environment.md)
14 | - `fields`: message fields
15 | - `routingKey`: message routing key
16 | - `content`: message content
17 | - `id`: element id
18 | - `type`: element type
19 | - `executionId`: element execution id
20 | - `parent`: element parent
21 | - `id`: element parent id
22 | - `type`: element parent type
23 | - `executionId`: element parent unique execution id
24 | - `path`: list of parent parents
25 | - `messageProperties`: message properties,
26 | - `messageId`: message id
27 | - `owner`: api owner, i.e. the owning element instance
28 | - `broker`: element [broker](https://github.com/paed01/smqp)
29 |
30 | ### `cancel([message, options])`
31 |
32 | Cancel run. Publishes cancel message via element broker on element broker `api` exchange.
33 |
34 | Arguments:
35 |
36 | - `message`: optional object sent as message
37 | - `options`: optional object with broker message options
38 | - `delegate`: optional boolean to delegate the cancel to all interested parties
39 |
40 | ### `discard()`
41 |
42 | Discard run. Publishes discard message on element broker `api` exchange.
43 |
44 | ### `signal(message[, options])`
45 |
46 | Signal activity. Publishes signal message on element broker `api` exchange.
47 |
48 | Arguments:
49 |
50 | - `message`: signal message
51 | - `options`: optional object with broker message options
52 | - `delegate`: optional boolean to delegate the signal to all interested parties
53 |
54 | ### `fail(error)`
55 |
56 | Fail activity with error. The purpose is to fail user-/signal tasks waiting for user input. The behaviour differs between different type of activities.
57 |
58 | ### `stop()`
59 |
60 | Stop element run. Publishes stop message on element broker `api` exchange.
61 |
62 | ### `resolveExpression(expression)`
63 |
64 | Resolve expression.
65 |
66 | ### `createMessage([overrideContent])`
67 |
68 | Utility function to create new message content from the api message.
69 |
70 | ### `sendApiMessage(action[, content, options])`
71 |
72 | Utility function to publish message with element broker.
73 |
74 | Arguments:
75 |
76 | - `action`: message action, will be prefixed with the element type, e.g. `signal` will be sent as `activity.signal` if used to signal activity
77 | - `content`: optional message content
78 | - `options`: optional object with broker message options
79 |
--------------------------------------------------------------------------------
/docs/SignalTask.md:
--------------------------------------------------------------------------------
1 | # SignalTask
2 |
3 | Signal-/User-/Manual task behaviour.
4 |
5 | ```javascript
6 | import * as elements from 'bpmn-elements';
7 |
8 | import BpmnModdle from 'bpmn-moddle';
9 |
10 | import { default as serialize, TypeResolver } from 'moddle-context-serializer';
11 |
12 | const { Context, Definition } = elements;
13 | const typeResolver = TypeResolver(elements);
14 |
15 | const source = `
16 |
17 |
18 |
19 |
20 |
21 | `;
22 |
23 | (async () => {
24 | const def = await run();
25 | const [userTask] = def.getPostponed();
26 |
27 | userTask.fail(new Error('Custom errror'));
28 | })();
29 |
30 | async function run() {
31 | const moddleContext = await getModdleContext(source);
32 | const options = {
33 | Logger,
34 | };
35 | const context = new Context(serialize(moddleContext, typeResolver));
36 |
37 | const definition = new Definition(context, options);
38 | definition.run();
39 | return definition;
40 | }
41 |
42 | function getModdleContext(sourceXml) {
43 | const bpmnModdle = new BpmnModdle();
44 | return bpmnModdle.fromXML(sourceXml.trim());
45 | }
46 |
47 | function Logger(scope) {
48 | return {
49 | debug: console.debug.bind(console, 'bpmn-elements:' + scope),
50 | error: console.error.bind(console, 'bpmn-elements:' + scope),
51 | warn: console.warn.bind(console, 'bpmn-elements:' + scope),
52 | };
53 | }
54 | ```
55 |
--------------------------------------------------------------------------------
/docs/StartEvent.md:
--------------------------------------------------------------------------------
1 | # StartEvent
2 |
3 | Start event behaviour.
4 |
5 | # Form
6 |
7 | If a form property is available when start event is executed, the event will wait until signaled. But! event definitions have precedence.
8 |
9 | ```javascript
10 | import * as elements from 'bpmn-elements';
11 | import BpmnModdle from 'bpmn-moddle';
12 |
13 | import { default as serialize, TypeResolver } from 'moddle-context-serializer';
14 |
15 | const { Context, Definition } = elements;
16 | const typeResolver = TypeResolver(elements);
17 |
18 | const source = `
19 |
20 |
21 |
22 |
23 |
24 | `;
25 |
26 | const moddleOptions = {
27 | js: {
28 | name: 'Node bpmn-engine',
29 | uri: 'http://paed01.github.io/bpmn-engine/schema/2020/08/bpmn',
30 | prefix: 'js',
31 | xml: {
32 | tagAlias: 'lowerCase',
33 | },
34 | types: [
35 | {
36 | name: 'FormSupported',
37 | isAbstract: true,
38 | extends: ['bpmn:StartEvent', 'bpmn:UserTask'],
39 | properties: [
40 | {
41 | name: 'formKey',
42 | type: 'String',
43 | isAttr: true,
44 | },
45 | ],
46 | },
47 | ],
48 | },
49 | };
50 |
51 | async function run() {
52 | const moddleContext = await getModdleContext(source);
53 |
54 | const context = new Context(serialize(moddleContext, typeResolver));
55 | const definition = new Definition(context, {
56 | Logger,
57 | variables: {
58 | remoteFormUrl: 'https://exmple.com',
59 | },
60 | extensions: {
61 | addFormExtension,
62 | },
63 | });
64 |
65 | definition.once('activity.wait', (api) => {
66 | api.owner.logger.debug(api.id, 'waiting for form', api.content.form);
67 | });
68 |
69 | definition.run();
70 |
71 | function addFormExtension(activity) {
72 | const { formKey } = activity.behaviour;
73 | if (!formKey) return;
74 |
75 | const { broker } = activity;
76 | const form = formKey === 'whatsYourName' ? { givenName: { type: 'string' } } : { age: { type: 'int' } };
77 |
78 | broker.subscribeTmp(
79 | 'event',
80 | 'activity.enter',
81 | () => {
82 | broker.publish('format', 'run.input', { form });
83 | },
84 | { noAck: true }
85 | );
86 | }
87 | }
88 |
89 | function getModdleContext(sourceXml) {
90 | const bpmnModdle = new BpmnModdle(moddleOptions);
91 | return bpmnModdle.fromXML(sourceXml.trim());
92 | }
93 |
94 | function Logger(scope) {
95 | return {
96 | debug: console.debug.bind(console, 'bpmn-elements:' + scope),
97 | error: console.error.bind(console, 'bpmn-elements:' + scope),
98 | warn: console.warn.bind(console, 'bpmn-elements:' + scope),
99 | };
100 | }
101 |
102 | run();
103 | ```
104 |
--------------------------------------------------------------------------------
/docs/TimerEventDefinition.md:
--------------------------------------------------------------------------------
1 | # TimerEventDefinition
2 |
3 | TimerEventDefinition behaviour.
4 |
5 | ## TimerEventDefinition events
6 |
7 | The timer event definition publish a number of events.
8 |
9 | ### `activity.timer`
10 |
11 | Fired when the timer is started.
12 |
13 | Object with properties. A subset:
14 |
15 | - `content:` object with activity and timer information
16 | - [`timeDuration`](#timeduration): the resolved time duration if any
17 | - [`timeDate`](#timedate): the resolved expire date if any
18 | - [`timeCycle`](#timecycle): the resolved time cycle if any
19 | - `startedAt`: timer started at date
20 | - `expireAt`: timer expires at date
21 | - `repeat`: repeated timer number of repetitions, -1 means unbounded number of repetitions. NB! Only viable when used in non-interrupting BoundaryEvent
22 |
23 | ### `activity.timeout`
24 |
25 | Fired when the timer has timed out or was cancelled.
26 |
27 | Object with `activity.timer` properties and some:
28 |
29 | - `content:` object with activity and timer information
30 | - `stoppedAt`: stopped at date
31 | - `runningTime`: running for milliseconds
32 |
33 | ## `timeDuration`
34 |
35 | Default support for ISO8601 duration. Will set a timer (`setTimeout`) for the duration and then complete when timed out. Invalid ISI8601 duration will throw and stop the execution.
36 |
37 | Uses [`@0dep/piso`](https://www.npmjs.com/package/@0dep/piso) to parse duration and repetitions. Consequently [ISO8601 intervals](https://en.wikipedia.org/wiki/ISO_8601) are supported.
38 |
39 | ## `timeDate`
40 |
41 | Behaves the same as `timeDuration`. Due date will timeout immediately. An invalid date, like `2023-02-29`, will throw and stop the execution.
42 |
43 | Uses [`@0dep/piso`](https://www.npmjs.com/package/@0dep/piso) to parse date according to [ISO8601](https://en.wikipedia.org/wiki/ISO_8601).
44 |
45 | ## `timeCycle`
46 |
47 | Time cycles are parsed with [`@0dep/piso`](https://www.npmjs.com/package/@0dep/piso) that also handles ISO8601 intervals.
48 |
49 | If another format is used, e.g. cron, you need to handle that by [extending the behavior](#set-your-own-timeout). There are several modules to handle time cycles and this project tries to keep the number of dependencies to a minimum.
50 |
51 | ## Combined `timeDuration` and `timeDate`
52 |
53 | The shortest timeout will be picked to start the timer.
54 |
55 | ## Set your own timeout
56 |
57 | If the parent event start message has an `expireAt` date or `timeout` positive integer property a timer will be started.
58 |
59 | See how to format these messages [here](/docs/Extension.md).
60 |
61 | Another alternative is to override the [parse function](#timereventdefinitionparsetimertype-value).
62 |
63 | ## Api
64 |
65 | Timer event definition api.
66 |
67 | ### `TimerEventDefinition.parse(timerType, value)`
68 |
69 | Parse timer value into expire date.
70 |
71 | Arguments:
72 |
73 | - `timerType`: timer type string, one of `timeDuration`, `timeCycle`, or `timeDate`
74 | - `value`: resolved expression timer string
75 |
76 | Returns object:
77 |
78 | - `expireAt`: expires at date
79 | - `delay`: delay in milliseconds
80 | - `repeat`: repeat number of times
81 |
--------------------------------------------------------------------------------
/docs/Timers.md:
--------------------------------------------------------------------------------
1 | # Timers
2 |
3 | Timers handler. The purpose is to keep track of executing timers. Can be added to inline script context to override builtin timers.
4 |
5 | # `Timers(options)`
6 |
7 | Default timers behavior.
8 |
9 | Arguments:
10 |
11 | - `options`: optional object
12 | - `setTimeout`: optional function, defaults to builtin `setTimeout`
13 | - `clearTimeout`: optional function, defaults to builtin `clearTimeout`
14 |
15 | Returns:
16 |
17 | - `executing`: list with executing timers
18 | - `register(owner)`: register timers owner
19 | - `setTimeout`: wrapped options `setTimeout`
20 | - `clearTimeout`: wrapped options `clearTimeout`
21 |
22 | ## `register(owner)`
23 |
24 | Register timers with owner. Called from TimerEventDefinition.
25 |
26 | Arguments:
27 |
28 | - `owner`: owning object, usually the activity in question
29 |
30 | Returns:
31 |
32 | - `setTimeout`: calls `setTimeout`
33 | - `clearTimeout`: clear timeout function
34 |
35 | ## `setTimeout(callback, delay, ...args)`
36 |
37 | Adds timer to list of executing timers, calls options `setTimeout`, and returns timer.
38 |
39 | Returns timer:
40 |
41 | - `timerId`: unique id
42 | - `owner`: registered owner if any, defaults to timers instance
43 | - `timerRef`: return value of builtin or overridden `setTimeout`
44 |
45 | ## `clearTimeout(ref)`
46 |
47 | Removes timer from list of executing timers and calls options `clearTimeout` with `ref.timerRef`.
48 |
--------------------------------------------------------------------------------
/docs/activity-execution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paed01/bpmn-elements/3fd23492027fea9720b260f29208a77ce7139580/docs/activity-execution.png
--------------------------------------------------------------------------------
/docs/activity-lifecycle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paed01/bpmn-elements/3fd23492027fea9720b260f29208a77ce7139580/docs/activity-lifecycle.png
--------------------------------------------------------------------------------
/docs/parallel-join-edgecase.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paed01/bpmn-elements/3fd23492027fea9720b260f29208a77ce7139580/docs/parallel-join-edgecase.png
--------------------------------------------------------------------------------
/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-vars': 2,
46 | 'no-unused-expressions': 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 | globals: {
72 | ...globals['shared-node-browser'],
73 | ...globals.es6,
74 | },
75 | },
76 | rules,
77 | },
78 | {
79 | files: ['test/**/*.js'],
80 | languageOptions: {
81 | globals: {
82 | ...globals.node,
83 | ...globals.mocha,
84 | expect: 'readonly',
85 | beforeEachScenario: 'readonly',
86 | Buffer: 'readonly',
87 | Feature: 'readonly',
88 | Scenario: 'readonly',
89 | Given: 'readonly',
90 | When: 'readonly',
91 | Then: 'readonly',
92 | And: 'readonly',
93 | But: 'readonly',
94 | },
95 | },
96 | rules: {
97 | 'no-unused-expressions': 0,
98 | 'no-var': 1,
99 | },
100 | },
101 | {
102 | ignores: ['coverage/**/*', 'node_modules/**/*', 'tmp/*', 'dist/*'],
103 | },
104 | ];
105 |
--------------------------------------------------------------------------------
/eventDefinitions.d.ts:
--------------------------------------------------------------------------------
1 | import './types/index.js';
2 |
--------------------------------------------------------------------------------
/events.d.ts:
--------------------------------------------------------------------------------
1 | import './types/index.js';
2 |
--------------------------------------------------------------------------------
/flows.d.ts:
--------------------------------------------------------------------------------
1 | import './types/index.js';
2 |
--------------------------------------------------------------------------------
/gateways.d.ts:
--------------------------------------------------------------------------------
1 | import './types/index.js';
2 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import './types/index.js';
2 |
--------------------------------------------------------------------------------
/src/Api.js:
--------------------------------------------------------------------------------
1 | import { cloneMessage } from './messageHelper.js';
2 | import { getUniqueId } from './shared.js';
3 |
4 | export function ActivityApi(broker, apiMessage, environment) {
5 | return new Api('activity', broker, apiMessage, environment);
6 | }
7 |
8 | export function DefinitionApi(broker, apiMessage, environment) {
9 | return new Api('definition', broker, apiMessage, environment);
10 | }
11 |
12 | export function ProcessApi(broker, apiMessage, environment) {
13 | return new Api('process', broker, apiMessage, environment);
14 | }
15 |
16 | export function FlowApi(broker, apiMessage, environment) {
17 | return new Api('flow', broker, apiMessage, environment);
18 | }
19 |
20 | export function Api(pfx, broker, sourceMessage, environment) {
21 | if (!sourceMessage) throw new Error('Api requires message');
22 |
23 | const apiMessage = cloneMessage(sourceMessage);
24 |
25 | const { id, type, name, executionId } = apiMessage.content;
26 | this.id = id;
27 | this.type = type;
28 | this.name = name;
29 | this.executionId = executionId;
30 | this.environment = environment || broker.owner.environment;
31 | this.content = apiMessage.content;
32 | this.fields = apiMessage.fields;
33 | this.messageProperties = apiMessage.properties;
34 | this.broker = broker;
35 | this.owner = broker.owner;
36 | this.messagePrefix = pfx;
37 | }
38 |
39 | Api.prototype.cancel = function cancel(message, options) {
40 | this.sendApiMessage('cancel', { message }, options);
41 | };
42 |
43 | Api.prototype.discard = function discard() {
44 | this.sendApiMessage('discard');
45 | };
46 |
47 | Api.prototype.fail = function fail(error) {
48 | this.sendApiMessage('error', { error });
49 | };
50 |
51 | Api.prototype.signal = function signal(message, options) {
52 | this.sendApiMessage('signal', { message }, options);
53 | };
54 |
55 | Api.prototype.stop = function stop() {
56 | this.sendApiMessage('stop');
57 | };
58 |
59 | Api.prototype.resolveExpression = function resolveExpression(expression) {
60 | return this.environment.resolveExpression(
61 | expression,
62 | {
63 | fields: this.fields,
64 | content: this.content,
65 | properties: this.messageProperties,
66 | },
67 | this.owner
68 | );
69 | };
70 |
71 | Api.prototype.sendApiMessage = function sendApiMessage(action, content, options) {
72 | const correlationId = options?.correlationId || getUniqueId(`${this.id || this.messagePrefix}_signal`);
73 | let key = `${this.messagePrefix}.${action}`;
74 | if (this.executionId) key += `.${this.executionId}`;
75 | this.broker.publish('api', key, this.createMessage(content), { ...options, correlationId, type: action });
76 | };
77 |
78 | Api.prototype.getPostponed = function getPostponed(...args) {
79 | if (this.owner.getPostponed) return this.owner.getPostponed(...args);
80 | if (this.owner.isSubProcess && this.owner.execution) return this.owner.execution.getPostponed(...args);
81 | return [];
82 | };
83 |
84 | Api.prototype.createMessage = function createMessage(content) {
85 | return {
86 | ...this.content,
87 | ...content,
88 | };
89 | };
90 |
--------------------------------------------------------------------------------
/src/Expressions.js:
--------------------------------------------------------------------------------
1 | import getPropertyValue from './getPropertyValue.js';
2 |
3 | const isExpressionPattern = /^\${(.+?)}$/;
4 | const expressionPattern = /\${(.+?)}/;
5 |
6 | export default function Expressions() {
7 | return {
8 | resolveExpression,
9 | isExpression,
10 | hasExpression,
11 | };
12 | }
13 |
14 | function resolveExpression(templatedString, context, expressionFnContext) {
15 | let result = templatedString;
16 |
17 | while (expressionPattern.test(result)) {
18 | const expressionMatch = result.match(expressionPattern);
19 | const innerProperty = expressionMatch[1];
20 |
21 | if (innerProperty === 'true') {
22 | return true;
23 | } else if (innerProperty === 'false') {
24 | return false;
25 | } else if (innerProperty === 'null') {
26 | return null;
27 | } else {
28 | const n = Number(innerProperty);
29 | if (!isNaN(n)) return n;
30 | }
31 |
32 | const contextValue = getPropertyValue(context, innerProperty, expressionFnContext);
33 |
34 | if (expressionMatch.input === expressionMatch[0]) {
35 | return contextValue;
36 | }
37 |
38 | result = result.replace(expressionMatch[0], contextValue === undefined ? '' : contextValue);
39 | }
40 | return result;
41 | }
42 |
43 | function isExpression(text) {
44 | if (!text) return false;
45 | return isExpressionPattern.test(text);
46 | }
47 |
48 | function hasExpression(text) {
49 | if (!text) return false;
50 | return expressionPattern.test(text);
51 | }
52 |
--------------------------------------------------------------------------------
/src/Scripts.js:
--------------------------------------------------------------------------------
1 | export function Scripts() {}
2 |
3 | Scripts.prototype.getScript = function getScript(/*scriptType, activity*/) {};
4 | Scripts.prototype.register = function register(/*activity*/) {};
5 |
--------------------------------------------------------------------------------
/src/Timers.js:
--------------------------------------------------------------------------------
1 | const kExecuting = Symbol.for('executing');
2 | const kTimerApi = Symbol.for('timers api');
3 |
4 | const MAX_DELAY = 2147483647;
5 |
6 | export function Timers(options) {
7 | this.count = 0;
8 | this.options = {
9 | setTimeout,
10 | clearTimeout,
11 | ...options,
12 | };
13 | this[kExecuting] = new Set();
14 | this.setTimeout = this.setTimeout.bind(this);
15 | this.clearTimeout = this.clearTimeout.bind(this);
16 | }
17 |
18 | Object.defineProperty(Timers.prototype, 'executing', {
19 | get() {
20 | return [...this[kExecuting]];
21 | },
22 | });
23 |
24 | Timers.prototype.register = function register(owner) {
25 | return new RegisteredTimers(this, owner);
26 | };
27 |
28 | Timers.prototype.setTimeout = function wrappedSetTimeout(callback, delay, ...args) {
29 | return this._setTimeout(null, callback, delay, ...args);
30 | };
31 |
32 | Timers.prototype.clearTimeout = function wrappedClearTimeout(ref) {
33 | if (this[kExecuting].delete(ref)) {
34 | ref.timerRef = this.options.clearTimeout(ref.timerRef);
35 | return;
36 | }
37 | return this.options.clearTimeout(ref);
38 | };
39 |
40 | Timers.prototype._setTimeout = function setTimeout(owner, callback, delay, ...args) {
41 | const executing = this[kExecuting];
42 | const ref = this._getReference(owner, callback, delay, args);
43 | executing.add(ref);
44 | if (delay < MAX_DELAY) {
45 | ref.timerRef = this.options.setTimeout(onTimeout, ref.delay, ...ref.args);
46 | }
47 | return ref;
48 |
49 | function onTimeout(...rargs) {
50 | executing.delete(ref);
51 | return callback(...rargs);
52 | }
53 | };
54 |
55 | Timers.prototype._getReference = function getReference(owner, callback, delay, args) {
56 | return new Timer(owner, `timer_${this.count++}`, callback, delay, args);
57 | };
58 |
59 | function RegisteredTimers(timersApi, owner) {
60 | this[kTimerApi] = timersApi;
61 | this.owner = owner;
62 | this.setTimeout = this.setTimeout.bind(this);
63 | this.clearTimeout = this.clearTimeout.bind(this);
64 | }
65 |
66 | RegisteredTimers.prototype.setTimeout = function registeredSetTimeout(callback, delay, ...args) {
67 | const timersApi = this[kTimerApi];
68 | return timersApi._setTimeout(this.owner, callback, delay, ...args);
69 | };
70 |
71 | RegisteredTimers.prototype.clearTimeout = function registeredClearTimeout(ref) {
72 | this[kTimerApi].clearTimeout(ref);
73 | };
74 |
75 | function Timer(owner, timerId, callback, delay, args) {
76 | this.callback = callback;
77 | this.delay = delay;
78 | this.args = args;
79 | this.owner = owner;
80 | this.timerId = timerId;
81 | this.expireAt = new Date(Date.now() + delay);
82 | this.timerRef = null;
83 | }
84 |
--------------------------------------------------------------------------------
/src/Tracker.js:
--------------------------------------------------------------------------------
1 | export function ActivityTracker(parentId) {
2 | this.id = parentId;
3 | this.status = { wait: new Set(), execute: new Set(), timer: new Set() };
4 | }
5 |
6 | Object.defineProperty(ActivityTracker.prototype, 'activityStatus', {
7 | get() {
8 | const status = this.status;
9 | if (status.execute.size) return 'executing';
10 | if (status.timer.size) return 'timer';
11 | return status.wait.size ? 'wait' : 'idle';
12 | },
13 | });
14 |
15 | ActivityTracker.prototype.track = function track(routingKey, message) {
16 | const content = message.content;
17 | if (content.isAssociation) return;
18 | if (content.isSequenceFlow) return;
19 | if (content.isSubProcess) return;
20 | const executionId = content.executionId;
21 |
22 | switch (routingKey) {
23 | case 'activity.enter':
24 | case 'activity.discard':
25 | case 'activity.start':
26 | case 'activity.execution.completed':
27 | case 'activity.execution.error':
28 | case 'activity.end':
29 | this._executing(executionId);
30 | break;
31 | case 'activity.execution.outbound.take':
32 | case 'activity.detach':
33 | case 'activity.call':
34 | case 'activity.wait': {
35 | if (content.isMultiInstance) this._waiting(content.parent.executionId);
36 | else this._waiting(executionId);
37 | break;
38 | }
39 | case 'activity.timer':
40 | this._timer(content.parent.executionId);
41 | break;
42 | case 'activity.leave':
43 | this._leave(executionId);
44 | break;
45 | }
46 | };
47 |
48 | ActivityTracker.prototype._executing = function executing(id) {
49 | const { wait, execute } = this.status;
50 | wait.delete(id);
51 | execute.add(id);
52 | };
53 |
54 | ActivityTracker.prototype._waiting = function waiting(id) {
55 | const { wait, execute } = this.status;
56 | execute.delete(id);
57 | wait.add(id);
58 | };
59 |
60 | ActivityTracker.prototype._timer = function timerFn(id) {
61 | const { timer, execute } = this.status;
62 | execute.delete(id);
63 | timer.add(id);
64 | };
65 |
66 | ActivityTracker.prototype._leave = function leave(id) {
67 | const { wait, execute, timer } = this.status;
68 | execute.delete(id);
69 | timer.delete(id);
70 | wait.delete(id);
71 | };
72 |
--------------------------------------------------------------------------------
/src/activity/Dummy.js:
--------------------------------------------------------------------------------
1 | import { cloneParent } from '../messageHelper.js';
2 |
3 | export default function DummyActivity(activityDef) {
4 | const { id, type = 'dummy', name, parent, behaviour } = activityDef;
5 | return {
6 | id,
7 | type,
8 | name,
9 | behaviour: { ...behaviour },
10 | parent: cloneParent(parent),
11 | placeholder: true,
12 | };
13 | }
14 |
--------------------------------------------------------------------------------
/src/activity/Escalation.js:
--------------------------------------------------------------------------------
1 | export default function Escalation(signalDef, context) {
2 | const { id, type, name, parent: originalParent } = signalDef;
3 | const { environment } = context;
4 | const parent = { ...originalParent };
5 |
6 | return {
7 | id,
8 | type,
9 | name,
10 | parent,
11 | resolve,
12 | };
13 |
14 | function resolve(executionMessage) {
15 | return {
16 | id,
17 | type,
18 | messageType: 'escalation',
19 | name: name && environment.resolveExpression(name, executionMessage),
20 | parent: { ...parent },
21 | };
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/activity/ExecutionScope.js:
--------------------------------------------------------------------------------
1 | import { cloneMessage } from '../messageHelper.js';
2 | import { ActivityError, BpmnError } from '../error/Errors.js';
3 |
4 | export default function ExecutionScope(activity, initMessage) {
5 | const { id, type, environment, logger } = activity;
6 |
7 | const { fields, content, properties } = cloneMessage(initMessage);
8 |
9 | const scope = {
10 | id,
11 | type,
12 | fields,
13 | content,
14 | properties,
15 | environment,
16 | logger,
17 | resolveExpression,
18 | ActivityError,
19 | BpmnError,
20 | };
21 |
22 | return scope;
23 |
24 | function resolveExpression(expression) {
25 | return environment.resolveExpression(expression, scope);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/activity/Message.js:
--------------------------------------------------------------------------------
1 | export default function Message(messageDef, context) {
2 | const { id, type, name, parent: originalParent } = messageDef;
3 | const { environment } = context;
4 | const parent = { ...originalParent };
5 |
6 | return {
7 | id,
8 | type,
9 | name,
10 | parent,
11 | resolve,
12 | };
13 |
14 | function resolve(executionMessage) {
15 | return {
16 | id,
17 | type,
18 | messageType: 'message',
19 | ...(name && { name: environment.resolveExpression(name, executionMessage) }),
20 | parent: { ...parent },
21 | };
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/activity/Signal.js:
--------------------------------------------------------------------------------
1 | export default function Signal(signalDef, context) {
2 | const { id, type = 'Signal', name, parent: originalParent } = signalDef;
3 | const { environment } = context;
4 | const parent = { ...originalParent };
5 |
6 | return {
7 | id,
8 | type,
9 | name,
10 | parent,
11 | resolve,
12 | };
13 |
14 | function resolve(executionMessage) {
15 | return {
16 | id,
17 | type,
18 | messageType: 'signal',
19 | ...(name && { name: environment.resolveExpression(name, executionMessage) }),
20 | parent: { ...parent },
21 | };
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/condition.js:
--------------------------------------------------------------------------------
1 | import ExecutionScope from './activity/ExecutionScope.js';
2 |
3 | /**
4 | * Script condition
5 | * @param {import('types').ElementBase} owner
6 | * @param {any} script
7 | * @param {string} language
8 | */
9 | export function ScriptCondition(owner, script, language) {
10 | this.type = 'script';
11 | this.language = language;
12 | this._owner = owner;
13 | this._script = script;
14 | }
15 |
16 | /**
17 | * Execute
18 | * @param {any} message
19 | * @param {CallableFunction} callback
20 | */
21 | ScriptCondition.prototype.execute = function execute(message, callback) {
22 | const owner = this._owner;
23 | try {
24 | return this._script.execute(ExecutionScope(owner, message), callback);
25 | } catch (err) {
26 | if (!callback) throw err;
27 | owner.logger.error(`<${owner.id}>`, err);
28 | callback(err);
29 | }
30 | };
31 |
32 | /**
33 | * Expression condition
34 | * @param {import('types').ElementBase} owner
35 | * @param {string} expression
36 | */
37 | export function ExpressionCondition(owner, expression) {
38 | this.type = 'expression';
39 | this.expression = expression;
40 | this._owner = owner;
41 | }
42 |
43 | /**
44 | * Execute
45 | * @param {any} message
46 | * @param {CallableFunction} callback
47 | */
48 | ExpressionCondition.prototype.execute = function execute(message, callback) {
49 | const owner = this._owner;
50 | try {
51 | const result = owner.environment.resolveExpression(this.expression, message);
52 | if (callback) return callback(null, result);
53 | return result;
54 | } catch (err) {
55 | if (callback) return callback(err);
56 | throw err;
57 | }
58 | };
59 |
--------------------------------------------------------------------------------
/src/error/BpmnError.js:
--------------------------------------------------------------------------------
1 | export default function BpmnErrorActivity(errorDef, context) {
2 | const { id, type, name = 'BpmnError', behaviour = {} } = errorDef;
3 | const { environment } = context;
4 |
5 | return {
6 | id,
7 | type,
8 | name,
9 | errorCode: behaviour.errorCode,
10 | resolve,
11 | };
12 |
13 | function resolve(executionMessage, error) {
14 | const resolveCtx = { ...executionMessage, error };
15 | const result = {
16 | id,
17 | type,
18 | messageType: 'throw',
19 | name: name && environment.resolveExpression(name, resolveCtx),
20 | code: behaviour.errorCode && environment.resolveExpression(behaviour.errorCode, resolveCtx),
21 | };
22 |
23 | if (error) result.inner = error;
24 | return result;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/error/Errors.js:
--------------------------------------------------------------------------------
1 | import { cloneMessage } from '../messageHelper.js';
2 |
3 | export class ActivityError extends Error {
4 | constructor(description, sourceMessage, inner) {
5 | super(description);
6 | this.type = 'ActivityError';
7 | this.name = this.constructor.name;
8 | this.description = description;
9 | if (sourceMessage) this.source = cloneMessage(sourceMessage, sourceMessage.content?.error && { error: undefined });
10 | if (inner) {
11 | this.inner = inner;
12 | if (inner.name) this.name = inner.name;
13 | if (inner.code) this.code = inner.code;
14 | }
15 | }
16 | }
17 |
18 | export class RunError extends ActivityError {
19 | constructor(...args) {
20 | super(...args);
21 | this.type = 'RunError';
22 | }
23 | }
24 |
25 | export class BpmnError extends Error {
26 | constructor(description, behaviour, sourceMessage, inner) {
27 | super(description);
28 | this.type = 'BpmnError';
29 | this.name = behaviour?.name ?? this.constructor.name;
30 | this.description = description;
31 | this.code = behaviour?.errorCode?.toString() ?? behaviour?.code;
32 | this.id = behaviour?.id;
33 | if (sourceMessage) this.source = cloneMessage(sourceMessage, sourceMessage.content?.error && { error: undefined });
34 | if (inner) this.inner = inner;
35 | }
36 | }
37 |
38 | export function makeErrorFromMessage(errorMessage) {
39 | const { content } = errorMessage;
40 |
41 | if (isKnownError(content)) return content;
42 |
43 | const { error } = content;
44 | if (!error) return new Error(`Malformatted error message with routing key ${errorMessage.fields?.routingKey}`);
45 |
46 | if (isKnownError(error)) return error;
47 |
48 | switch (error.type) {
49 | case 'ActivityError':
50 | return new ActivityError(
51 | error.message || error.description,
52 | error.source,
53 | error.inner ? error.inner : { code: error.code, name: error.name }
54 | );
55 | case 'RunError':
56 | return new RunError(
57 | error.message || error.description,
58 | error.source,
59 | error.inner ? error.inner : { code: error.code, name: error.name }
60 | );
61 | case 'BpmnError':
62 | return new BpmnError(error.message || error.description, error, error.source);
63 | }
64 |
65 | return error;
66 | }
67 |
68 | function isKnownError(test) {
69 | if (test instanceof ActivityError) return test;
70 | if (test instanceof BpmnError) return test;
71 | if (test instanceof Error) return test;
72 | }
73 |
--------------------------------------------------------------------------------
/src/eventDefinitions/TerminateEventDefinition.js:
--------------------------------------------------------------------------------
1 | import { cloneContent, shiftParent } from '../messageHelper.js';
2 |
3 | export default function TerminateEventDefinition(activity, eventDefinition) {
4 | const { id, broker, environment } = activity;
5 | const { type = 'TerminateEventDefinition' } = eventDefinition;
6 |
7 | this.id = id;
8 | this.type = type;
9 | this.activity = activity;
10 | this.broker = broker;
11 | this.logger = environment.Logger(type.toLowerCase());
12 | }
13 |
14 | TerminateEventDefinition.prototype.execute = function execute(executeMessage) {
15 | const executeContent = executeMessage.content;
16 |
17 | const throwContent = cloneContent(executeContent, {
18 | state: 'terminate',
19 | });
20 | throwContent.parent = shiftParent(executeContent.parent);
21 |
22 | this.logger.debug(`<${executeContent.executionId} (${executeContent.id})> terminate`);
23 | const broker = this.broker;
24 | broker.publish('event', 'process.terminate', throwContent, { type: 'terminate' });
25 | broker.publish('execution', 'execute.completed', cloneContent(executeContent));
26 | };
27 |
--------------------------------------------------------------------------------
/src/eventDefinitions/index.js:
--------------------------------------------------------------------------------
1 | import CancelEventDefinition from './CancelEventDefinition.js';
2 | import CompensateEventDefinition from './CompensateEventDefinition.js';
3 | import ConditionalEventDefinition from './ConditionalEventDefinition.js';
4 | import ErrorEventDefinition from './ErrorEventDefinition.js';
5 | import EscalationEventDefinition from './EscalationEventDefinition.js';
6 | import LinkEventDefinition from './LinkEventDefinition.js';
7 | import MessageEventDefinition from './MessageEventDefinition.js';
8 | import SignalEventDefinition from './SignalEventDefinition.js';
9 | import TerminateEventDefinition from './TerminateEventDefinition.js';
10 | import TimerEventDefinition from './TimerEventDefinition.js';
11 |
12 | export {
13 | CancelEventDefinition,
14 | CompensateEventDefinition,
15 | ConditionalEventDefinition,
16 | ErrorEventDefinition,
17 | EscalationEventDefinition,
18 | LinkEventDefinition,
19 | MessageEventDefinition,
20 | SignalEventDefinition,
21 | TerminateEventDefinition,
22 | TimerEventDefinition,
23 | };
24 |
--------------------------------------------------------------------------------
/src/events/EndEvent.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import EventDefinitionExecution from '../eventDefinitions/EventDefinitionExecution.js';
3 | import { cloneContent } from '../messageHelper.js';
4 |
5 | const kExecution = Symbol.for('execution');
6 |
7 | export default function EndEvent(activityDef, context) {
8 | return new Activity(EndEventBehaviour, { ...activityDef, isThrowing: true }, context);
9 | }
10 |
11 | export function EndEventBehaviour(activity) {
12 | this.id = activity.id;
13 | this.type = activity.type;
14 | this.broker = activity.broker;
15 | this[kExecution] = activity.eventDefinitions && new EventDefinitionExecution(activity, activity.eventDefinitions);
16 | }
17 |
18 | EndEventBehaviour.prototype.execute = function execute(executeMessage) {
19 | const execution = this[kExecution];
20 | if (execution) {
21 | return execution.execute(executeMessage);
22 | }
23 |
24 | return this.broker.publish('execution', 'execute.completed', cloneContent(executeMessage.content));
25 | };
26 |
--------------------------------------------------------------------------------
/src/events/IntermediateCatchEvent.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import EventDefinitionExecution from '../eventDefinitions/EventDefinitionExecution.js';
3 | import { cloneContent } from '../messageHelper.js';
4 |
5 | const kExecution = Symbol.for('execution');
6 |
7 | export default function IntermediateCatchEvent(activityDef, context) {
8 | return new Activity(IntermediateCatchEventBehaviour, activityDef, context);
9 | }
10 |
11 | export function IntermediateCatchEventBehaviour(activity) {
12 | this.id = activity.id;
13 | this.type = activity.type;
14 | this.broker = activity.broker;
15 | this[kExecution] = activity.eventDefinitions && new EventDefinitionExecution(activity, activity.eventDefinitions);
16 | }
17 |
18 | IntermediateCatchEventBehaviour.prototype.execute = function execute(executeMessage) {
19 | const execution = this[kExecution];
20 | if (execution) {
21 | return execution.execute(executeMessage);
22 | }
23 |
24 | const executeContent = executeMessage.content;
25 | const executionId = executeContent.executionId;
26 | const broker = this.broker;
27 | broker.subscribeTmp('api', `activity.#.${executionId}`, this._onApiMessage.bind(this, executeMessage), {
28 | noAck: true,
29 | consumerTag: '_api-behaviour-execution',
30 | });
31 |
32 | return broker.publish('event', 'activity.wait', cloneContent(executeContent));
33 | };
34 |
35 | IntermediateCatchEventBehaviour.prototype._onApiMessage = function onApiMessage(executeMessage, routingKey, message) {
36 | switch (message.properties.type) {
37 | case 'message':
38 | case 'signal': {
39 | const broker = this.broker;
40 | broker.cancel('_api-behaviour-execution');
41 | return broker.publish(
42 | 'execution',
43 | 'execute.completed',
44 | cloneContent(executeMessage.content, {
45 | output: message.content.message,
46 | })
47 | );
48 | }
49 | case 'discard': {
50 | const broker = this.broker;
51 | broker.cancel('_api-behaviour-execution');
52 | return broker.publish('execution', 'execute.discard', cloneContent(executeMessage.content));
53 | }
54 | case 'stop': {
55 | return this.broker.cancel('_api-behaviour-execution');
56 | }
57 | }
58 | };
59 |
--------------------------------------------------------------------------------
/src/events/IntermediateThrowEvent.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import EventDefinitionExecution from '../eventDefinitions/EventDefinitionExecution.js';
3 | import { cloneContent } from '../messageHelper.js';
4 |
5 | const kExecution = Symbol.for('execution');
6 |
7 | export default function IntermediateThrowEvent(activityDef, context) {
8 | return new Activity(IntermediateThrowEventBehaviour, { ...activityDef, isThrowing: true }, context);
9 | }
10 |
11 | export function IntermediateThrowEventBehaviour(activity) {
12 | this.id = activity.id;
13 | this.type = activity.type;
14 | this.broker = activity.broker;
15 | this[kExecution] = activity.eventDefinitions && new EventDefinitionExecution(activity, activity.eventDefinitions);
16 | }
17 |
18 | IntermediateThrowEventBehaviour.prototype.execute = function execute(executeMessage) {
19 | const execution = this[kExecution];
20 | if (execution) {
21 | return execution.execute(executeMessage);
22 | }
23 |
24 | return this.broker.publish('execution', 'execute.completed', cloneContent(executeMessage.content));
25 | };
26 |
--------------------------------------------------------------------------------
/src/events/index.js:
--------------------------------------------------------------------------------
1 | import BoundaryEvent, { BoundaryEventBehaviour } from './BoundaryEvent.js';
2 | import EndEvent, { EndEventBehaviour } from './EndEvent.js';
3 | import IntermediateCatchEvent, { IntermediateCatchEventBehaviour } from './IntermediateCatchEvent.js';
4 | import IntermediateThrowEvent, { IntermediateThrowEventBehaviour } from './IntermediateThrowEvent.js';
5 | import StartEvent, { StartEventBehaviour } from './StartEvent.js';
6 |
7 | export {
8 | BoundaryEvent,
9 | BoundaryEventBehaviour,
10 | EndEvent,
11 | EndEventBehaviour,
12 | IntermediateCatchEvent,
13 | IntermediateCatchEventBehaviour,
14 | IntermediateThrowEvent,
15 | IntermediateThrowEventBehaviour,
16 | StartEvent,
17 | StartEventBehaviour,
18 | };
19 |
--------------------------------------------------------------------------------
/src/flows/Association.js:
--------------------------------------------------------------------------------
1 | import { cloneParent } from '../messageHelper.js';
2 | import { EventBroker } from '../EventBroker.js';
3 | import { Api } from '../Api.js';
4 | import { getUniqueId } from '../shared.js';
5 |
6 | const kCounters = Symbol.for('counters');
7 |
8 | export default function Association(associationDef, { environment }) {
9 | const { id, type = 'association', name, parent, targetId, sourceId, behaviour = {} } = associationDef;
10 |
11 | this.id = id;
12 | this.type = type;
13 | this.name = name;
14 | this.parent = cloneParent(parent);
15 | this.behaviour = behaviour;
16 | this.sourceId = sourceId;
17 | this.targetId = targetId;
18 | this.isAssociation = true;
19 | this.environment = environment;
20 | const logger = (this.logger = environment.Logger(type.toLowerCase()));
21 |
22 | this[kCounters] = {
23 | take: 0,
24 | discard: 0,
25 | };
26 |
27 | const { broker, on, once, waitFor } = new EventBroker(this, { prefix: 'association', durable: true, autoDelete: false });
28 | this.broker = broker;
29 | this.on = on;
30 | this.once = once;
31 | this.waitFor = waitFor;
32 |
33 | logger.debug(`<${id}> init, <${sourceId}> -> <${targetId}>`);
34 | }
35 |
36 | Object.defineProperty(Association.prototype, 'counters', {
37 | get() {
38 | return { ...this[kCounters] };
39 | },
40 | });
41 |
42 | Association.prototype.take = function take(content) {
43 | this.logger.debug(`<${this.id}> take target <${this.targetId}>`);
44 | ++this[kCounters].take;
45 |
46 | this._publishEvent('take', content);
47 |
48 | return true;
49 | };
50 |
51 | Association.prototype.discard = function discard(content) {
52 | this.logger.debug(`<${this.id}> discard target <${this.targetId}>`);
53 | ++this[kCounters].discard;
54 |
55 | this._publishEvent('discard', content);
56 |
57 | return true;
58 | };
59 |
60 | Association.prototype.getState = function getState() {
61 | const brokerState = this.broker.getState(true);
62 | if (!brokerState && this.environment.settings.disableTrackState) return;
63 |
64 | return {
65 | id: this.id,
66 | type: this.type,
67 | counters: this.counters,
68 | broker: brokerState,
69 | };
70 | };
71 |
72 | Association.prototype.recover = function recover(state) {
73 | Object.assign(this[kCounters], state.counters);
74 | this.broker.recover(state.broker);
75 | };
76 |
77 | Association.prototype.getApi = function getApi(message) {
78 | return new Api('association', this.broker, message || { content: this._createMessageContent() });
79 | };
80 |
81 | Association.prototype.stop = function stop() {
82 | this.broker.stop();
83 | };
84 |
85 | Association.prototype._publishEvent = function publishEvent(action, content) {
86 | const eventContent = this._createMessageContent({
87 | action,
88 | message: content,
89 | sequenceId: getUniqueId(this.id),
90 | });
91 |
92 | this.broker.publish('event', `association.${action}`, eventContent, { type: action });
93 | };
94 |
95 | Association.prototype._createMessageContent = function createMessageContent(override) {
96 | return {
97 | ...override,
98 | id: this.id,
99 | type: this.type,
100 | name: this.name,
101 | sourceId: this.sourceId,
102 | targetId: this.targetId,
103 | isAssociation: true,
104 | parent: cloneParent(this.parent),
105 | };
106 | };
107 |
--------------------------------------------------------------------------------
/src/flows/index.js:
--------------------------------------------------------------------------------
1 | import Association from './Association.js';
2 | import MessageFlow from './MessageFlow.js';
3 | import SequenceFlow from './SequenceFlow.js';
4 |
5 | export { Association, MessageFlow, SequenceFlow };
6 |
--------------------------------------------------------------------------------
/src/gateways/EventBasedGateway.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import { cloneContent } from '../messageHelper.js';
3 |
4 | const kCompleted = Symbol.for('completed');
5 | const kTargets = Symbol.for('targets');
6 |
7 | export default function EventBasedGateway(activityDef, context) {
8 | return new Activity(EventBasedGatewayBehaviour, activityDef, context);
9 | }
10 |
11 | export function EventBasedGatewayBehaviour(activity, context) {
12 | this.id = activity.id;
13 | this.type = activity.type;
14 | this.activity = activity;
15 | this.broker = activity.broker;
16 | this.context = context;
17 | this[kTargets] = new Set(activity.outbound.map((flow) => context.getActivityById(flow.targetId)));
18 | }
19 |
20 | EventBasedGatewayBehaviour.prototype.execute = function execute(executeMessage) {
21 | const executeContent = executeMessage.content;
22 | const { executionId, outbound = [], outboundTaken } = executeContent;
23 |
24 | const targets = this[kTargets];
25 | this[kCompleted] = false;
26 | if (!targets.size) return this._complete(executeContent);
27 |
28 | for (const flow of this.activity.outbound) {
29 | outbound.push({ id: flow.id, action: 'take' });
30 | }
31 |
32 | if (!this[kCompleted] && outboundTaken) return;
33 |
34 | const targetConsumerTag = `_gateway-listener-${this.id}`;
35 |
36 | const onTargetCompleted = this._onTargetCompleted.bind(this, executeMessage);
37 | for (const target of this[kTargets]) {
38 | target.broker.subscribeOnce('event', 'activity.end', onTargetCompleted, { consumerTag: targetConsumerTag });
39 | }
40 |
41 | const broker = this.activity.broker;
42 | broker.subscribeOnce('api', `activity.stop.${executionId}`, () => this._stop(), {
43 | consumerTag: '_api-stop-execution',
44 | });
45 |
46 | this[kCompleted] = false;
47 |
48 | if (!executeMessage.fields.redelivered) {
49 | return broker.publish('execution', 'execute.outbound.take', cloneContent(executeContent, { outboundTaken: true }));
50 | }
51 | };
52 |
53 | EventBasedGatewayBehaviour.prototype._onTargetCompleted = function onTargetCompleted(executeMessage, _, message, owner) {
54 | const { id: targetId, executionId: targetExecutionId } = message.content;
55 | const executeContent = executeMessage.content;
56 | const executionId = executeContent.executionId;
57 | this.activity.logger.debug(`<${executionId} (${this.id})> <${targetExecutionId}> completed run, discarding the rest`);
58 |
59 | this._stop();
60 | for (const target of this[kTargets]) {
61 | if (target === owner) continue;
62 | target.discard();
63 | }
64 |
65 | const completedContent = cloneContent(executeContent, {
66 | taken: {
67 | id: targetId,
68 | executionId: targetExecutionId,
69 | },
70 | ignoreOutbound: true,
71 | });
72 |
73 | this._complete(completedContent);
74 | };
75 |
76 | EventBasedGatewayBehaviour.prototype._complete = function complete(completedContent) {
77 | this[kCompleted] = true;
78 | this.broker.publish('execution', 'execute.completed', cloneContent(completedContent));
79 | };
80 |
81 | EventBasedGatewayBehaviour.prototype._stop = function stop() {
82 | const targetConsumerTag = `_gateway-listener-${this.id}`;
83 | for (const target of this[kTargets]) target.broker.cancel(targetConsumerTag);
84 | this.broker.cancel('_api-stop-execution');
85 | };
86 |
--------------------------------------------------------------------------------
/src/gateways/ExclusiveGateway.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import { cloneContent } from '../messageHelper.js';
3 |
4 | export default function ExclusiveGateway(activityDef, context) {
5 | return new Activity(ExclusiveGatewayBehaviour, activityDef, context);
6 | }
7 |
8 | export function ExclusiveGatewayBehaviour(activity) {
9 | const { id, type, broker } = activity;
10 | this.id = id;
11 | this.type = type;
12 | this.broker = broker;
13 | }
14 |
15 | ExclusiveGatewayBehaviour.prototype.execute = function execute({ content }) {
16 | this.broker.publish('execution', 'execute.completed', cloneContent(content, { outboundTakeOne: true }));
17 | };
18 |
--------------------------------------------------------------------------------
/src/gateways/InclusiveGateway.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import { cloneContent } from '../messageHelper.js';
3 |
4 | export default function InclusiveGateway(activityDef, context) {
5 | return new Activity(InclusiveGatewayBehaviour, activityDef, context);
6 | }
7 |
8 | export function InclusiveGatewayBehaviour(activity) {
9 | const { id, type, broker } = activity;
10 | this.id = id;
11 | this.type = type;
12 | this.broker = broker;
13 | }
14 |
15 | InclusiveGatewayBehaviour.prototype.execute = function execute({ content }) {
16 | this.broker.publish('execution', 'execute.completed', cloneContent(content));
17 | };
18 |
--------------------------------------------------------------------------------
/src/gateways/ParallelGateway.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import { cloneContent } from '../messageHelper.js';
3 |
4 | export default function ParallelGateway(activityDef, context) {
5 | return new Activity(ParallelGatewayBehaviour, { ...activityDef, isParallelGateway: true }, context);
6 | }
7 |
8 | export function ParallelGatewayBehaviour(activity) {
9 | const { id, type, broker } = activity;
10 | this.id = id;
11 | this.type = type;
12 | this.broker = broker;
13 | }
14 |
15 | ParallelGatewayBehaviour.prototype.execute = function execute({ content }) {
16 | this.broker.publish('execution', 'execute.completed', cloneContent(content));
17 | };
18 |
--------------------------------------------------------------------------------
/src/gateways/index.js:
--------------------------------------------------------------------------------
1 | import EventBasedGateway, { EventBasedGatewayBehaviour } from './EventBasedGateway.js';
2 | import ExclusiveGateway, { ExclusiveGatewayBehaviour } from './ExclusiveGateway.js';
3 | import InclusiveGateway, { InclusiveGatewayBehaviour } from './InclusiveGateway.js';
4 | import ParallelGateway, { ParallelGatewayBehaviour } from './ParallelGateway.js';
5 |
6 | export {
7 | EventBasedGateway,
8 | EventBasedGatewayBehaviour,
9 | ExclusiveGateway,
10 | ExclusiveGatewayBehaviour,
11 | InclusiveGateway,
12 | InclusiveGatewayBehaviour,
13 | ParallelGateway,
14 | ParallelGatewayBehaviour,
15 | };
16 |
--------------------------------------------------------------------------------
/src/io/BpmnIO.js:
--------------------------------------------------------------------------------
1 | export default function BpmnIO(activity, context) {
2 | this.activity = activity;
3 | this.context = context;
4 | this.type = 'bpmnio';
5 |
6 | const { ioSpecification: ioSpecificationDef, properties: propertiesDef } = activity.behaviour;
7 |
8 | this.specification = ioSpecificationDef && new ioSpecificationDef.Behaviour(activity, ioSpecificationDef, context);
9 | this.properties = propertiesDef && new propertiesDef.Behaviour(activity, propertiesDef, context);
10 | }
11 |
12 | Object.defineProperty(BpmnIO.prototype, 'hasIo', {
13 | get() {
14 | return this.specification || this.properties;
15 | },
16 | });
17 |
18 | BpmnIO.prototype.activate = function activate(message) {
19 | const properties = this.properties,
20 | specification = this.specification;
21 | if (properties) properties.activate(message);
22 | if (specification) specification.activate(message);
23 | };
24 |
25 | BpmnIO.prototype.deactivate = function deactivate(message) {
26 | const properties = this.properties,
27 | specification = this.specification;
28 | if (properties) properties.deactivate(message);
29 | if (specification) specification.deactivate(message);
30 | };
31 |
--------------------------------------------------------------------------------
/src/io/EnvironmentDataObject.js:
--------------------------------------------------------------------------------
1 | export default function EnvironmentDataObject(dataObjectDef, { environment }) {
2 | const { id, type, name, behaviour, parent } = dataObjectDef;
3 | this.id = id;
4 | this.type = type;
5 | this.name = name;
6 | this.behaviour = behaviour;
7 | this.parent = parent;
8 | this.environment = environment;
9 | }
10 |
11 | EnvironmentDataObject.prototype.read = function read(broker, exchange, routingKeyPrefix, messageProperties) {
12 | const environment = this.environment;
13 | const value = environment.variables._data?.[this.id];
14 | const content = this._createContent(value);
15 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
16 | };
17 |
18 | EnvironmentDataObject.prototype.write = function write(broker, exchange, routingKeyPrefix, value, messageProperties) {
19 | const environment = this.environment;
20 | environment.variables._data = environment.variables._data || {};
21 | environment.variables._data[this.id] = value;
22 | const content = this._createContent(value);
23 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
24 | };
25 |
26 | EnvironmentDataObject.prototype._createContent = function createContent(value) {
27 | return {
28 | id: this.id,
29 | type: this.type,
30 | name: this.name,
31 | value,
32 | };
33 | };
34 |
--------------------------------------------------------------------------------
/src/io/EnvironmentDataStore.js:
--------------------------------------------------------------------------------
1 | export default function EnvironmentDataStore(dataStoreDef, { environment }) {
2 | const { id, type, name, behaviour, parent } = dataStoreDef;
3 | this.id = id;
4 | this.type = type;
5 | this.name = name;
6 | this.behaviour = behaviour;
7 | this.parent = parent;
8 | this.environment = environment;
9 | }
10 |
11 | EnvironmentDataStore.prototype.read = function read(broker, exchange, routingKeyPrefix, messageProperties) {
12 | const environment = this.environment;
13 | const value = environment.variables._data?.[this.id];
14 | const content = this._createContent(value);
15 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
16 | };
17 |
18 | EnvironmentDataStore.prototype.write = function write(broker, exchange, routingKeyPrefix, value, messageProperties) {
19 | const environment = this.environment;
20 | environment.variables._data = environment.variables._data || {};
21 | environment.variables._data[this.id] = value;
22 | const content = this._createContent(value);
23 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
24 | };
25 |
26 | EnvironmentDataStore.prototype._createContent = function createContent(value) {
27 | return {
28 | id: this.id,
29 | type: this.type,
30 | name: this.name,
31 | value,
32 | };
33 | };
34 |
--------------------------------------------------------------------------------
/src/io/EnvironmentDataStoreReference.js:
--------------------------------------------------------------------------------
1 | export default function EnvironmentDataStoreReference(dataObjectDef, { environment }) {
2 | const { id, type, name, behaviour, parent } = dataObjectDef;
3 | this.id = id;
4 | this.type = type;
5 | this.name = name;
6 | this.behaviour = behaviour;
7 | this.parent = parent;
8 | this.environment = environment;
9 | }
10 |
11 | EnvironmentDataStoreReference.prototype.read = function read(broker, exchange, routingKeyPrefix, messageProperties) {
12 | const environment = this.environment;
13 | const value = environment.variables._data?.[this.id];
14 | const content = this._createContent(value);
15 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
16 | };
17 |
18 | EnvironmentDataStoreReference.prototype.write = function write(broker, exchange, routingKeyPrefix, value, messageProperties) {
19 | const environment = this.environment;
20 | environment.variables._data = environment.variables._data || {};
21 | environment.variables._data[this.id] = value;
22 | const content = this._createContent(value);
23 | return broker.publish(exchange, `${routingKeyPrefix}response`, content, messageProperties);
24 | };
25 |
26 | EnvironmentDataStoreReference.prototype._createContent = function createContent(value) {
27 | return {
28 | id: this.id,
29 | type: this.type,
30 | name: this.name,
31 | value,
32 | };
33 | };
34 |
--------------------------------------------------------------------------------
/src/messageHelper.js:
--------------------------------------------------------------------------------
1 | export function cloneContent(content, extend) {
2 | const { discardSequence, inbound, outbound, parent, sequence } = content;
3 |
4 | const clone = {
5 | ...content,
6 | ...extend,
7 | };
8 |
9 | if (parent) {
10 | clone.parent = cloneParent(parent);
11 | }
12 | if (discardSequence) {
13 | clone.discardSequence = discardSequence.slice();
14 | }
15 | if (inbound) {
16 | clone.inbound = inbound.map((c) => cloneContent(c));
17 | }
18 | if (outbound) {
19 | clone.outbound = outbound.map((c) => cloneContent(c));
20 | }
21 | if (Array.isArray(sequence)) {
22 | clone.sequence = sequence.map((c) => cloneContent(c));
23 | }
24 |
25 | return clone;
26 | }
27 |
28 | export function cloneMessage(message, overrideContent) {
29 | return {
30 | fields: { ...message.fields },
31 | content: cloneContent(message.content, overrideContent),
32 | properties: { ...message.properties },
33 | };
34 | }
35 |
36 | export function cloneParent(parent) {
37 | const { path } = parent;
38 | const clone = { ...parent };
39 | if (!path) return clone;
40 |
41 | clone.path = path.map((p) => {
42 | return { ...p };
43 | });
44 |
45 | return clone;
46 | }
47 |
48 | export function unshiftParent(parent, adoptingParent) {
49 | const { id, type, executionId } = adoptingParent;
50 | if (!parent) {
51 | return {
52 | id,
53 | type,
54 | executionId,
55 | };
56 | }
57 |
58 | const clone = cloneParent(parent);
59 | const { id: parentId, type: parentType, executionId: parentExecutionId } = parent;
60 | clone.id = id;
61 | clone.executionId = executionId;
62 | clone.type = type;
63 |
64 | const path = (clone.path = clone.path || []);
65 | path.unshift({ id: parentId, type: parentType, executionId: parentExecutionId });
66 |
67 | return clone;
68 | }
69 |
70 | export function shiftParent(parent) {
71 | if (!parent) return;
72 | if (!parent.path || !parent.path.length) return;
73 |
74 | const clone = cloneParent(parent);
75 | const { id, executionId, type } = clone.path.shift();
76 | clone.id = id;
77 | clone.executionId = executionId;
78 | clone.type = type;
79 | clone.path = clone.path.length ? clone.path : undefined;
80 | return clone;
81 | }
82 |
83 | export function pushParent(parent, ancestor) {
84 | const { id, type, executionId } = ancestor;
85 | if (!parent) return { id, type, executionId };
86 |
87 | const clone = cloneParent(parent);
88 | if (clone.id === id) {
89 | if (executionId) clone.executionId = executionId;
90 | return clone;
91 | }
92 | const path = (clone.path = clone.path || []);
93 |
94 | for (const p of path) {
95 | if (p.id === id) {
96 | if (executionId) p.executionId = executionId;
97 | return clone;
98 | }
99 | }
100 |
101 | path.push({ id, type, executionId });
102 | return clone;
103 | }
104 |
--------------------------------------------------------------------------------
/src/process/Lane.js:
--------------------------------------------------------------------------------
1 | const kProcess = Symbol.for('process');
2 |
3 | export default function Lane(process, laneDefinition) {
4 | const { broker, environment } = process;
5 | const { id, type, behaviour } = laneDefinition;
6 |
7 | this[kProcess] = process;
8 |
9 | this.id = id;
10 | this.type = type;
11 | this.name = behaviour.name;
12 | this.parent = {
13 | id: process.id,
14 | type: process.type,
15 | };
16 | this.behaviour = { ...behaviour };
17 | this.environment = environment;
18 | this.broker = broker;
19 | this.context = process.context;
20 | this.logger = environment.Logger(type.toLowerCase());
21 | }
22 |
23 | Object.defineProperty(Lane.prototype, 'process', {
24 | get() {
25 | return this[kProcess];
26 | },
27 | });
28 |
--------------------------------------------------------------------------------
/src/shared.js:
--------------------------------------------------------------------------------
1 | const safePattern = /[./\\#*:\s]/g;
2 |
3 | export function generateId() {
4 | return Math.random().toString(16).substring(2, 12);
5 | }
6 |
7 | export function brokerSafeId(id) {
8 | return id.replace(safePattern, '_');
9 | }
10 |
11 | export function getUniqueId(prefix) {
12 | return `${brokerSafeId(prefix)}_${generateId()}`;
13 | }
14 |
15 | export function getOptionsAndCallback(optionsOrCallback, callback) {
16 | let options;
17 | if (typeof optionsOrCallback === 'function') {
18 | callback = optionsOrCallback;
19 | } else {
20 | options = optionsOrCallback;
21 | }
22 |
23 | return [options, callback];
24 | }
25 |
--------------------------------------------------------------------------------
/src/tasks/ScriptTask.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import ExecutionScope from '../activity/ExecutionScope.js';
3 | import { ActivityError } from '../error/Errors.js';
4 | import { cloneContent, cloneMessage } from '../messageHelper.js';
5 |
6 | export default function ScriptTask(activityDef, context) {
7 | return new Activity(ScriptTaskBehaviour, activityDef, context);
8 | }
9 |
10 | export function ScriptTaskBehaviour(activity) {
11 | const { id, type, behaviour } = activity;
12 |
13 | this.id = id;
14 | this.type = type;
15 | this.scriptFormat = behaviour.scriptFormat;
16 |
17 | this.loopCharacteristics =
18 | behaviour.loopCharacteristics && new behaviour.loopCharacteristics.Behaviour(activity, behaviour.loopCharacteristics);
19 | this.activity = activity;
20 | const environment = (this.environment = activity.environment);
21 |
22 | environment.registerScript(activity);
23 | }
24 |
25 | ScriptTaskBehaviour.prototype.execute = function execute(executeMessage) {
26 | const executeContent = executeMessage.content;
27 | const loopCharacteristics = this.loopCharacteristics;
28 | if (loopCharacteristics && executeContent.isRootScope) {
29 | return loopCharacteristics.execute(executeMessage);
30 | }
31 |
32 | const activity = this.activity;
33 | const scriptFormat = this.scriptFormat;
34 | const script = this.environment.getScript(scriptFormat, activity, cloneMessage(executeMessage));
35 | if (!script) {
36 | return activity.emitFatal(
37 | new ActivityError(`Script format ${scriptFormat} is unsupported or was not registered for <${activity.id}>`, executeMessage),
38 | executeContent
39 | );
40 | }
41 |
42 | return script.execute(ExecutionScope(activity, executeMessage), scriptCallback);
43 |
44 | function scriptCallback(err, output) {
45 | if (err) {
46 | activity.logger.error(`<${executeContent.executionId} (${activity.id})>`, err);
47 | return activity.broker.publish(
48 | 'execution',
49 | 'execute.error',
50 | cloneContent(executeContent, { error: new ActivityError(err.message, executeMessage, err) }, { mandatory: true })
51 | );
52 | }
53 | return activity.broker.publish('execution', 'execute.completed', cloneContent(executeContent, { output }));
54 | }
55 | };
56 |
--------------------------------------------------------------------------------
/src/tasks/ServiceImplementation.js:
--------------------------------------------------------------------------------
1 | import ExecutionScope from '../activity/ExecutionScope.js';
2 |
3 | export default function ServiceImplementation(activity) {
4 | this.type = `${activity.type}:implementation`;
5 | this.implementation = activity.behaviour.implementation;
6 | this.activity = activity;
7 | }
8 |
9 | ServiceImplementation.prototype.execute = function execute(executionMessage, callback) {
10 | const activity = this.activity;
11 | const implementation = this.implementation;
12 | const serviceFn = activity.environment.resolveExpression(implementation, executionMessage);
13 |
14 | if (typeof serviceFn !== 'function') return callback(new Error(`Implementation ${implementation} did not resolve to a function`));
15 |
16 | serviceFn.call(activity, ExecutionScope(activity, executionMessage), (err, ...args) => {
17 | callback(err, args);
18 | });
19 | };
20 |
--------------------------------------------------------------------------------
/src/tasks/StandardLoopCharacteristics.js:
--------------------------------------------------------------------------------
1 | import LoopCharacteristics from './LoopCharacteristics.js';
2 |
3 | export default function StandardLoopCharacteristics(activity, loopCharacteristics) {
4 | let { behaviour } = loopCharacteristics;
5 | behaviour = { ...behaviour, isSequential: true };
6 | return new LoopCharacteristics(activity, { ...loopCharacteristics, behaviour });
7 | }
8 |
--------------------------------------------------------------------------------
/src/tasks/Task.js:
--------------------------------------------------------------------------------
1 | import Activity from '../activity/Activity.js';
2 | import { cloneContent } from '../messageHelper.js';
3 |
4 | export default function Task(activityDef, context) {
5 | return new Activity(TaskBehaviour, activityDef, context);
6 | }
7 |
8 | export function TaskBehaviour(activity) {
9 | const { id, type, behaviour, broker } = activity;
10 | this.id = id;
11 | this.type = type;
12 | this.loopCharacteristics =
13 | behaviour.loopCharacteristics && new behaviour.loopCharacteristics.Behaviour(activity, behaviour.loopCharacteristics);
14 | this.broker = broker;
15 | }
16 |
17 | TaskBehaviour.prototype.execute = function execute(executeMessage) {
18 | const executeContent = executeMessage.content;
19 | const loopCharacteristics = this.loopCharacteristics;
20 | if (loopCharacteristics && executeContent.isRootScope) {
21 | return loopCharacteristics.execute(executeMessage);
22 | }
23 |
24 | return this.broker.publish('execution', 'execute.completed', cloneContent(executeContent));
25 | };
26 |
--------------------------------------------------------------------------------
/src/tasks/Transaction.js:
--------------------------------------------------------------------------------
1 | import SubProcess from './SubProcess.js';
2 |
3 | export default function Transaction(activityDef, context) {
4 | const transaction = { type: 'transaction', ...activityDef, isTransaction: true };
5 | const activity = SubProcess(transaction, context);
6 | return activity;
7 | }
8 |
--------------------------------------------------------------------------------
/src/tasks/index.js:
--------------------------------------------------------------------------------
1 | import CallActivity, { CallActivityBehaviour } from './CallActivity.js';
2 | import ReceiveTask, { ReceiveTaskBehaviour } from './ReceiveTask.js';
3 | import ScriptTask, { ScriptTaskBehaviour } from './ScriptTask.js';
4 | import ServiceTask, { ServiceTaskBehaviour } from './ServiceTask.js';
5 | import SignalTask, { SignalTaskBehaviour } from './SignalTask.js';
6 | import SubProcess, { SubProcessBehaviour } from './SubProcess.js';
7 | import Task, { TaskBehaviour } from './Task.js';
8 | import Transaction from './Transaction.js';
9 |
10 | export {
11 | CallActivity,
12 | CallActivityBehaviour,
13 | ReceiveTask,
14 | ReceiveTaskBehaviour,
15 | ScriptTask,
16 | ScriptTaskBehaviour,
17 | ServiceTask,
18 | ServiceTaskBehaviour,
19 | SignalTask,
20 | SignalTaskBehaviour,
21 | SubProcess,
22 | SubProcessBehaviour,
23 | Task,
24 | TaskBehaviour,
25 | Transaction,
26 | };
27 |
--------------------------------------------------------------------------------
/tasks.d.ts:
--------------------------------------------------------------------------------
1 | import './types/index.js';
2 |
--------------------------------------------------------------------------------
/test/Api-test.js:
--------------------------------------------------------------------------------
1 | import { Broker } from 'smqp';
2 | import { ActivityApi } from '../src/Api.js';
3 | import Environment from '../src/Environment.js';
4 |
5 | describe('Api', () => {
6 | it('Api without message throws', () => {
7 | expect(() => {
8 | ActivityApi(new Broker(), null, new Environment());
9 | }).to.throw(Error);
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/test/Timers-test.js:
--------------------------------------------------------------------------------
1 | import ck from 'chronokinesis';
2 |
3 | import { Timers } from '../src/Timers.js';
4 |
5 | describe('Timers', () => {
6 | describe('setTimeout', () => {
7 | afterEach(ck.reset);
8 |
9 | it('returns home baked timer object', () => {
10 | ck.freeze('2023-05-25T10:00Z');
11 | const timers = new Timers({
12 | setTimeout() {
13 | return 'ref';
14 | },
15 | clearTimeout() {},
16 | });
17 |
18 | const callback = () => {};
19 | const timer = timers.setTimeout(callback, 60000, 1);
20 |
21 | expect(timer.callback).to.equal(callback);
22 | expect(timer.delay).to.equal(60000);
23 | expect(timer.args).to.deep.equal([1]);
24 | expect(timer.owner).to.be.null;
25 | expect(timer.timerId).to.be.a('string');
26 | expect(timer.expireAt).to.deep.equal(new Date('2023-05-25T10:01Z'));
27 | expect(timer.timerRef).to.equal('ref');
28 | });
29 |
30 | it('adds timer to list of executing timers', () => {
31 | const timers = new Timers({
32 | setTimeout() {
33 | return 'ref';
34 | },
35 | clearTimeout() {},
36 | });
37 |
38 | const callback = () => {};
39 | const timer = timers.setTimeout(callback, 60000, 1);
40 |
41 | expect(timers.executing).to.have.length(1);
42 | expect(timers.executing[0].timerId).to.be.ok.and.equal(timer.timerId);
43 | });
44 | });
45 |
46 | describe('clearTimeout', () => {
47 | it('resets timerRef on timer', () => {
48 | const timers = new Timers({
49 | setTimeout() {
50 | return 'ref';
51 | },
52 | clearTimeout() {},
53 | });
54 |
55 | const timer = timers.setTimeout(() => {}, 100);
56 |
57 | expect(timer.timerRef).to.equal('ref');
58 |
59 | timers.clearTimeout(timer);
60 |
61 | expect(timer.timerRef).to.be.undefined;
62 | });
63 | });
64 | });
65 |
--------------------------------------------------------------------------------
/test/activity/ExecutionScope-test.js:
--------------------------------------------------------------------------------
1 | import Environment from '../../src/Environment.js';
2 | import ExecutionScope from '../../src/activity/ExecutionScope.js';
3 | import { ActivityError, BpmnError } from '../../src/error/Errors.js';
4 |
5 | describe('ExecutionScope', () => {
6 | it('exposes environment, error classes, and passed message', () => {
7 | const activity = {
8 | id: 'task1',
9 | type: 'task',
10 | environment: new Environment(),
11 | logger: {},
12 | };
13 | const message = {
14 | fields: {
15 | routingKey: 'run.execute',
16 | },
17 | content: {
18 | id: 'task1',
19 | },
20 | properties: {
21 | messageId: 'm1',
22 | },
23 | };
24 |
25 | const scope = ExecutionScope(activity, message);
26 | expect(scope).to.have.property('id', 'task1');
27 | expect(scope).to.have.property('type', 'task');
28 | expect(scope).to.have.property('logger', activity.logger);
29 | expect(scope).to.have.property('environment', activity.environment);
30 | expect(scope).to.have.property('BpmnError', BpmnError);
31 | expect(scope).to.have.property('ActivityError', ActivityError);
32 | expect(scope)
33 | .to.have.property('fields')
34 | .that.eql({
35 | routingKey: 'run.execute',
36 | })
37 | .but.not.equal(message.fields);
38 | expect(scope)
39 | .to.have.property('content')
40 | .that.eql({
41 | id: 'task1',
42 | })
43 | .but.not.equal(message.content);
44 | expect(scope)
45 | .to.have.property('properties')
46 | .that.eql({
47 | messageId: 'm1',
48 | })
49 | .but.not.equal(message.properties);
50 | });
51 |
52 | it('exposes resolve expression', () => {
53 | const activity = {
54 | id: 'task1',
55 | type: 'task',
56 | environment: new Environment({
57 | variables: {
58 | input: 1,
59 | },
60 | }),
61 | logger: {},
62 | };
63 | const message = {
64 | fields: {
65 | routingKey: 'run.execute',
66 | },
67 | content: {
68 | id: 'task1',
69 | },
70 | properties: {
71 | messageId: 'm1',
72 | },
73 | };
74 |
75 | const scope = ExecutionScope(activity, message);
76 | expect(scope.resolveExpression('${logger}')).to.equal(activity.logger);
77 | expect(scope.resolveExpression('${environment.variables.input}')).to.equal(1);
78 | expect(scope.resolveExpression('${properties.messageId}')).to.equal('m1');
79 | });
80 | });
81 |
--------------------------------------------------------------------------------
/test/bpmn-elements-test.js:
--------------------------------------------------------------------------------
1 | import * as api from '../src/index.js';
2 |
3 | describe('bpmn-elemements module', () => {
4 | it('exports Timers', () => {
5 | expect(api).to.have.property('Timers').that.is.a('function');
6 | });
7 |
8 | it('exports Errors', () => {
9 | expect(api).to.have.property('ActivityError').that.is.a('function');
10 | expect(api).to.have.property('RunError').that.is.a('function');
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/test/error/BpmnError-test.js:
--------------------------------------------------------------------------------
1 | import BpmnErrorActivity from '../../src/error/BpmnError.js';
2 | import Environment from '../../src/Environment.js';
3 |
4 | describe('BpmnError', () => {
5 | it('returns BpmnError instanceof from error', () => {
6 | const bpmnError = BpmnErrorActivity(
7 | {
8 | id: 'Error_0',
9 | name: 'TestError',
10 | },
11 | { environment: new Environment() }
12 | );
13 |
14 | const err = bpmnError.resolve({}, new Error('Men'));
15 |
16 | expect(err).to.have.property('id', 'Error_0');
17 | expect(err).to.have.property('name', 'TestError');
18 | });
19 |
20 | it('resolves errorCode expression', () => {
21 | const bpmnError = BpmnErrorActivity(
22 | {
23 | id: 'Error_0',
24 | name: 'TestError',
25 | behaviour: {
26 | errorCode: 'EMES',
27 | },
28 | },
29 | { environment: new Environment() }
30 | );
31 |
32 | const err = bpmnError.resolve(
33 | {
34 | resolveExpression(errorCode) {
35 | return errorCode;
36 | },
37 | },
38 | new Error('Men')
39 | );
40 |
41 | expect(err).to.have.property('code', 'EMES');
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/test/eventDefinitions/TerminateEventDefinition-test.js:
--------------------------------------------------------------------------------
1 | import Environment from '../../src/Environment.js';
2 | import TerminateEventDefinition from '../../src/eventDefinitions/TerminateEventDefinition.js';
3 | import { ActivityBroker } from '../../src/EventBroker.js';
4 |
5 | describe('TerminateEventDefinition', () => {
6 | let event;
7 | beforeEach(() => {
8 | event = {
9 | environment: new Environment(),
10 | broker: ActivityBroker(this).broker,
11 | };
12 | });
13 |
14 | it('publishes process terminate on parent broker and completes', () => {
15 | const terminateDefinition = new TerminateEventDefinition(event, {});
16 |
17 | const messages = [];
18 | event.broker.subscribeTmp(
19 | 'event',
20 | 'process.*',
21 | (_, msg) => {
22 | messages.push(msg);
23 | },
24 | { noAck: true }
25 | );
26 | event.broker.subscribeTmp(
27 | 'execution',
28 | '#',
29 | (_, msg) => {
30 | messages.push(msg);
31 | },
32 | { noAck: true }
33 | );
34 |
35 | terminateDefinition.execute({
36 | content: {
37 | id: 'end',
38 | executionId: 'end_0_0',
39 | parent: {
40 | id: 'end',
41 | executionId: 'end_0',
42 | type: 'bpmn:EndEvent',
43 | path: [
44 | {
45 | id: 'theProcess',
46 | executionId: 'theProcess_0',
47 | },
48 | ],
49 | },
50 | },
51 | });
52 |
53 | expect(messages).to.have.length(2);
54 | expect(messages[0].fields).to.have.property('routingKey', 'process.terminate');
55 | expect(messages[0].content).to.have.property('state', 'terminate');
56 | expect(messages[0].content).to.have.property('parent');
57 | expect(messages[0].content.parent).to.have.property('id', 'theProcess');
58 | expect(messages[0].content.parent).to.have.property('executionId', 'theProcess_0');
59 |
60 | expect(messages[1].fields).to.have.property('routingKey', 'execute.completed');
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/test/feature/activity-feature.js:
--------------------------------------------------------------------------------
1 | import Definition from '../../src/definition/Definition.js';
2 | import factory from '../helpers/factory.js';
3 | import testHelpers from '../helpers/testHelpers.js';
4 |
5 | Feature('Activity', () => {
6 | Scenario('When a task is discarded by multiple flows', () => {
7 | let definition;
8 |
9 | Given('a process with several decisions all ending up in one manual task', async () => {
10 | const source = factory.resource('consumer_error.bpmn');
11 | const context = await testHelpers.context(source);
12 |
13 | definition = new Definition(context);
14 | });
15 |
16 | let wait;
17 | When('definition is run', () => {
18 | wait = definition.waitFor('activity.wait');
19 | definition.run();
20 | });
21 |
22 | Then('manual task eventually executes and waits', () => {
23 | return wait;
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/test/feature/ad-hoc-subprocess-feature.js:
--------------------------------------------------------------------------------
1 | import Definition from '../../src/definition/Definition.js';
2 | import testHelpers from '../helpers/testHelpers.js';
3 | import factory from '../helpers/factory.js';
4 |
5 | Feature('Ad-hoc subprocess', () => {
6 | Scenario('Running ad-hoc subprocess', () => {
7 | let context, definition;
8 | Given('a process mathching feature', async () => {
9 | const source = factory.resource('ad-hoc-subprocess.bpmn');
10 | context = await testHelpers.context(source);
11 | });
12 |
13 | let leave;
14 | const completedActivities = [];
15 | When('running definition', () => {
16 | definition = new Definition(context);
17 |
18 | definition.broker.subscribeTmp(
19 | 'event',
20 | 'activity.end',
21 | (_, msg) => {
22 | completedActivities.push({ id: msg.content.id, parent: msg.content.parent.id });
23 | },
24 | { noAck: true }
25 | );
26 |
27 | leave = definition.waitFor('leave');
28 |
29 | definition.run();
30 | });
31 |
32 | Then('definition completes', () => {
33 | return leave;
34 | });
35 |
36 | And('all ad-hoc subprocess activities were taken', () => {
37 | expect(completedActivities).to.deep.equal([
38 | { id: 'start', parent: 'process_0' },
39 | { id: 'task1', parent: 'adhoc' },
40 | { id: 'throw', parent: 'adhoc' },
41 | { id: 'task2', parent: 'adhoc' },
42 | { id: 'task3', parent: 'adhoc' },
43 | { id: 'adhoc', parent: 'process_0' },
44 | { id: 'end', parent: 'process_0' },
45 | ]);
46 | });
47 | });
48 | });
49 |
--------------------------------------------------------------------------------
/test/feature/backward-compatability-feature.js:
--------------------------------------------------------------------------------
1 | import { promises as fs } from 'fs';
2 | import Definition from '../../src/definition/Definition.js';
3 | import factory from '../helpers/factory.js';
4 | import testHelpers from '../helpers/testHelpers.js';
5 |
6 | const motherOfAllSource = factory.resource('mother-of-all.bpmn');
7 |
8 | Feature('Backward compatability 5.2', () => {
9 | Scenario('Slimmer state', () => {
10 | let context;
11 | before(async () => {
12 | context = await testHelpers.context(motherOfAllSource);
13 | });
14 |
15 | let definition, state;
16 | Given('a state from version 5', async () => {
17 | state = JSON.parse(await fs.readFile('./test/resources/mother-of-all-state-5.json'));
18 | });
19 |
20 | let leave;
21 | When('recovered and resumed with state from version 5', () => {
22 | definition = new Definition(context).recover(state);
23 | leave = definition.waitFor('leave');
24 | definition.resume();
25 | });
26 |
27 | And('waiting tasks are signaled', () => {
28 | definition.signal({ id: 'userTask1' });
29 | definition.signal({ id: 'subUserTask1' });
30 | });
31 |
32 | Then('run completes', () => {
33 | return leave;
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/feature/definition-output-feature.js:
--------------------------------------------------------------------------------
1 | import Definition from '../../src/definition/Definition.js';
2 | import testHelpers from '../helpers/testHelpers.js';
3 |
4 | Feature('Definition output', () => {
5 | Scenario('Process completes with output', () => {
6 | let definition;
7 | Given('a process with output', async () => {
8 | const source = `
9 |
10 |
11 |
12 |
17 |
18 |
19 | `;
20 |
21 | const context = await testHelpers.context(source);
22 | definition = new Definition(context);
23 | });
24 |
25 | let end;
26 | When('ran', () => {
27 | end = definition.waitFor('end');
28 | definition.run();
29 | });
30 |
31 | Then('run completes', () => {
32 | return end;
33 | });
34 |
35 | And('definition state contain hoisted process output', () => {
36 | expect(definition.getState().environment.output).to.deep.equal({ foo: 'bar' });
37 | });
38 | });
39 |
40 | Scenario('Process fails after writing output', () => {
41 | let definition;
42 | Given('a process with output', async () => {
43 | const source = `
44 |
45 |
46 |
47 |
55 |
56 |
57 | `;
58 |
59 | const context = await testHelpers.context(source);
60 | definition = new Definition(context);
61 | });
62 |
63 | let errored;
64 | When('ran', () => {
65 | errored = definition.waitFor('error');
66 | definition.run();
67 | });
68 |
69 | Then('run fails', () => {
70 | return errored;
71 | });
72 |
73 | And('definition state contain hoisted process output', () => {
74 | expect(definition.getState().environment.output).to.deep.equal({ foo: 'bar' });
75 | });
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/test/feature/dummy-feature.js:
--------------------------------------------------------------------------------
1 | import Definition from '../../src/definition/Definition.js';
2 | import factory from '../helpers/factory.js';
3 | import testHelpers from '../helpers/testHelpers.js';
4 |
5 | const groupsSource = factory.resource('groups.bpmn');
6 |
7 | Feature('Dummy', () => {
8 | Scenario('Group of elements with categories', () => {
9 | let context, definition;
10 |
11 | let ended;
12 | When('a source with groups is ran', async () => {
13 | context = await testHelpers.context(groupsSource);
14 | definition = new Definition(context);
15 | ended = definition.waitFor('end');
16 | definition.run();
17 | });
18 |
19 | Then('it runs to end', () => {
20 | return ended;
21 | });
22 | });
23 | });
24 |
--------------------------------------------------------------------------------
/test/feature/expression-feature.js:
--------------------------------------------------------------------------------
1 | import testHelpers from '../helpers/testHelpers.js';
2 | import Definition from '../../src/definition/Definition.js';
3 | import { resolveExpression } from '@aircall/expression-parser';
4 |
5 | Feature('expressions', () => {
6 | Scenario('@aircall/expression-parser', () => {
7 | let definition;
8 | Given('a process with faulty sequence flow condition expression syntax', async () => {
9 | const source = `
10 |
13 |
14 |
15 |
16 | \${true === "false'}
17 |
18 |
19 |
20 |
21 |
22 | `;
23 |
24 | const context = await testHelpers.context(source);
25 | definition = new Definition(context, {
26 | expressions: { resolveExpression },
27 | });
28 | });
29 |
30 | let errored;
31 | When('definition is ran', () => {
32 | errored = definition.waitFor('error');
33 | definition.run();
34 | });
35 |
36 | Then('run fails with error from expression parser', async () => {
37 | const err = await errored;
38 | expect(err.content.error).to.match(/syntax/i);
39 | });
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/test/feature/issues/exclusive-gateway-join-feature.js:
--------------------------------------------------------------------------------
1 | import { Definition } from '../../../src/index.js';
2 | import testHelpers from '../../helpers/testHelpers.js';
3 | import factory from '../../helpers/factory.js';
4 |
5 | const source = factory.resource('exclusive-gateway-as-join.bpmn');
6 |
7 | Feature('Exclusive gateway used for joining', () => {
8 | Scenario('a number of exclusive gateway join and split', () => {
9 | let context, definition, end;
10 | When('running a definition matching the scenario', async () => {
11 | context = await testHelpers.context(source);
12 |
13 | definition = new Definition(context);
14 | end = definition.waitFor('end');
15 | definition.run();
16 | });
17 |
18 | Then('run completes', () => {
19 | return end;
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/feature/script-feature.js:
--------------------------------------------------------------------------------
1 | import Definition from '../../src/definition/Definition.js';
2 | import testHelpers from '../helpers/testHelpers.js';
3 |
4 | class Scripts {
5 | register({ behaviour }) {
6 | if (!/^(javascript|js)$/i.test(behaviour.scriptFormat)) return;
7 | }
8 | compile() {}
9 | getScript() {}
10 | }
11 |
12 | Feature('Script', () => {
13 | Scenario('Register script fails', () => {
14 | let context, definition;
15 |
16 | Given('a process with a script task with unsupported script format', async () => {
17 | const source = `
18 |
19 |
20 |
21 |
26 |
27 |
28 | `;
29 |
30 | context = await testHelpers.context(source);
31 | });
32 |
33 | let err;
34 | When('definition run', () => {
35 | definition = new Definition(context, { scripts: new Scripts() });
36 | try {
37 | definition.run();
38 | } catch (e) {
39 | err = e;
40 | }
41 | });
42 |
43 | Then('error is thrown', () => {
44 | expect(err.message).to.equal('Script format python is unsupported or was not registered for ');
45 | definition.stop();
46 | });
47 |
48 | Given('async formatting is added', () => {
49 | definition = new Definition(context, { scripts: new Scripts() });
50 | const task = definition.getActivityById('task');
51 | task.on('enter', () => {
52 | task.broker
53 | .getQueue('format-run-q')
54 | .queueMessage({ routingKey: 'run.enter.format' }, { endRoutingKey: 'run.enter.complete' }, { persistent: false });
55 | setImmediate(() => {
56 | task.broker.publish('format', 'run.enter.complete', { data: 1 }, { persistent: false });
57 | });
58 | });
59 | });
60 |
61 | let waitError;
62 | When('definition run', () => {
63 | waitError = definition.waitFor('error');
64 | definition.run();
65 | });
66 |
67 | Then('error is thrown', async () => {
68 | err = await waitError;
69 | expect(err.content.error.message).to.equal('Script format python is unsupported or was not registered for ');
70 | });
71 | });
72 | });
73 |
--------------------------------------------------------------------------------
/test/feature/service-task-feature.js:
--------------------------------------------------------------------------------
1 | import Definition from '../../src/definition/Definition.js';
2 | import testHelpers from '../helpers/testHelpers.js';
3 |
4 | Feature('Service task', () => {
5 | Scenario('Recover and resume mid execution', () => {
6 | let context, definition;
7 |
8 | Given('a process with a service task', async () => {
9 | const source = `
10 |
11 |
12 |
13 |
14 | `;
15 |
16 | context = await testHelpers.context(source);
17 | });
18 |
19 | let called = 0;
20 | When('definition run', () => {
21 | definition = new Definition(context, {
22 | services: {
23 | foo() {
24 | ++called;
25 | },
26 | },
27 | });
28 | definition.run();
29 | });
30 |
31 | Then('service is called', () => {
32 | expect(called).to.equal(1);
33 | });
34 |
35 | let state;
36 | Given('state is saved', () => {
37 | state = definition.getState();
38 | });
39 |
40 | let end;
41 | When('definition recovered and resumed', () => {
42 | definition = new Definition(context.clone(), {
43 | services: {
44 | foo(...args) {
45 | args.pop()();
46 | },
47 | },
48 | });
49 |
50 | end = definition.waitFor('leave');
51 |
52 | definition.recover(state).resume();
53 | });
54 |
55 | Then('resumed definition completes', () => {
56 | return end;
57 | });
58 | });
59 | });
60 |
--------------------------------------------------------------------------------
/test/flows/Association-test.js:
--------------------------------------------------------------------------------
1 | import Environment from '../../src/Environment.js';
2 | import Association from '../../src/flows/Association.js';
3 | import { ActivityBroker } from '../../src/EventBroker.js';
4 |
5 | describe('Association', () => {
6 | it('stop() stops broker', () => {
7 | const activity = ActivityBroker();
8 | const context = {
9 | environment: new Environment(),
10 | getActivityById() {
11 | return activity;
12 | },
13 | };
14 |
15 | const flow = new Association(
16 | {
17 | id: 'association',
18 | type: 'bpmn:Association',
19 | parent: {},
20 | source: {
21 | id: 'task',
22 | },
23 | target: {
24 | id: 'task1',
25 | },
26 | },
27 | context
28 | );
29 |
30 | expect(flow.broker.getExchange('event').stopped).to.be.false;
31 | flow.stop();
32 | expect(flow.broker.getExchange('event').stopped).to.be.true;
33 | });
34 |
35 | it('getApi() returns message Api', () => {
36 | const activity = ActivityBroker();
37 | const context = {
38 | environment: new Environment(),
39 | getActivityById() {
40 | return activity;
41 | },
42 | };
43 |
44 | const flow = new Association(
45 | {
46 | id: 'association',
47 | type: 'bpmn:Association',
48 | parent: {},
49 | source: {
50 | id: 'task',
51 | },
52 | target: {
53 | id: 'task1',
54 | },
55 | },
56 | context
57 | );
58 |
59 | const api = flow.getApi();
60 | expect(api).to.have.property('id', 'association');
61 | });
62 |
63 | it('getApi(message) returns message Api', () => {
64 | const activity = ActivityBroker();
65 | const context = {
66 | environment: new Environment(),
67 | getActivityById() {
68 | return activity;
69 | },
70 | };
71 |
72 | const flow = new Association(
73 | {
74 | id: 'association',
75 | type: 'bpmn:Association',
76 | parent: {},
77 | source: {
78 | id: 'task',
79 | },
80 | target: {
81 | id: 'task1',
82 | },
83 | },
84 | context
85 | );
86 |
87 | const api = flow.getApi({ content: { id: 'foo' } });
88 | expect(api).to.have.property('id', 'foo');
89 | });
90 | });
91 |
--------------------------------------------------------------------------------
/test/helpers/JavaScripts.js:
--------------------------------------------------------------------------------
1 | import { Script } from 'vm';
2 |
3 | export function Scripts(enableDummy = false) {
4 | this.scripts = new Map();
5 | this.enableDummy = enableDummy;
6 | }
7 |
8 | Scripts.prototype.register = function register({ id, type, behaviour, logger, environment }) {
9 | let scriptBody, language;
10 |
11 | switch (type) {
12 | case 'bpmn:SequenceFlow': {
13 | if (!behaviour.conditionExpression) return;
14 | language = behaviour.conditionExpression.language;
15 | if (!language) return;
16 | scriptBody = behaviour.conditionExpression.body;
17 | break;
18 | }
19 | default: {
20 | language = behaviour.scriptFormat;
21 | scriptBody = behaviour.script;
22 | }
23 | }
24 |
25 | const filename = `${type}/${id}`;
26 | if (!language || !scriptBody) {
27 | if (this.enableDummy) return;
28 | const script = new DummyScript(language, filename, logger);
29 | this.scripts.set(id, script);
30 | return script;
31 | }
32 |
33 | if (!/^(javascript|js)$/i.test(language)) return;
34 |
35 | const script = new JavaScript(language, filename, scriptBody, environment);
36 | this.scripts.set(id, script);
37 |
38 | return script;
39 | };
40 |
41 | Scripts.prototype.getScript = function getScript(language, { id }) {
42 | return this.scripts.get(id);
43 | };
44 |
45 | Scripts.prototype.compile = function compile(language, filename, scriptBody) {
46 | return new Script(scriptBody, { filename });
47 | };
48 |
49 | function JavaScript(language, filename, scriptBody, environment) {
50 | this.id = filename;
51 | this.script = new Script(scriptBody, { filename });
52 | this.language = language;
53 | this.environment = environment;
54 | }
55 |
56 | JavaScript.prototype.execute = function execute(executionContext, callback) {
57 | const timers = this.environment.timers.register(executionContext);
58 | return this.script.runInNewContext({ ...executionContext, ...timers, next: callback, console });
59 | };
60 |
61 | function DummyScript(language, filename, logger) {
62 | this.id = filename;
63 | this.isDummy = true;
64 | this.language = language;
65 | this.logger = logger;
66 | }
67 |
68 | DummyScript.prototype.execute = function execute(executionContext, callback) {
69 | const { id, executionId } = executionContext.content;
70 | this.logger.debug(`<${executionId} (${id})> passthrough dummy script ${this.language || 'esperanto'}`);
71 | callback();
72 | };
73 |
--------------------------------------------------------------------------------
/test/helpers/setup.js:
--------------------------------------------------------------------------------
1 | import 'chai/register-expect.js';
2 |
3 | process.env.NODE_ENV = 'test';
4 | Error.stackTraceLimit = 20;
5 |
--------------------------------------------------------------------------------
/test/io/DataObject-test.js:
--------------------------------------------------------------------------------
1 | import DataObject from '../../src/io/EnvironmentDataObject.js';
2 | import Environment from '../../src/Environment.js';
3 | import { ActivityBroker } from '../../src/EventBroker.js';
4 |
5 | describe('DataObject', () => {
6 | describe('read', () => {
7 | it('publishes message on passed broker exchange when value was read', () => {
8 | const { broker } = ActivityBroker();
9 | const dataObject = new DataObject({ id: 'input' }, { environment: new Environment() });
10 |
11 | let message;
12 | broker.subscribeOnce('format', 'test.#', (_, msg) => {
13 | message = msg;
14 | });
15 |
16 | dataObject.read(broker, 'format', 'test.');
17 |
18 | expect(message).to.be.ok;
19 | expect(message.content).to.have.property('id', 'input');
20 | });
21 | });
22 |
23 | describe('write', () => {
24 | it('publishes message on passed broker exchange when value was written', () => {
25 | const { broker } = ActivityBroker();
26 | const dataObject = new DataObject({ id: 'input' }, { environment: new Environment() });
27 |
28 | let message;
29 | broker.subscribeOnce('format', 'test.#', (_, msg) => {
30 | message = msg;
31 | });
32 |
33 | dataObject.write(broker, 'format', 'test.');
34 |
35 | expect(message).to.be.ok;
36 | expect(message.content).to.have.property('id', 'input');
37 | });
38 | });
39 |
40 | describe('builtin', () => {
41 | it('saves dataObject value in environment variables _data', () => {
42 | const environment = new Environment();
43 | const { broker } = ActivityBroker();
44 | const dataObject = new DataObject({ id: 'info' }, { environment });
45 |
46 | dataObject.write(broker, 'format', 'test', 'me');
47 |
48 | expect(environment.variables._data).to.have.property('info', 'me');
49 | });
50 | });
51 | });
52 |
--------------------------------------------------------------------------------
/test/io/Properties-test.js:
--------------------------------------------------------------------------------
1 | import Environment from '../../src/Environment.js';
2 | import Properties from '../../src/io/Properties.js';
3 | import { ActivityBroker } from '../../src/EventBroker.js';
4 |
5 | describe('Properties', () => {
6 | it('activate twice has no effect', () => {
7 | const { broker } = ActivityBroker();
8 | const props = new Properties(
9 | {
10 | id: 'input',
11 | broker,
12 | environment: new Environment(),
13 | },
14 | {
15 | values: [],
16 | }
17 | );
18 | props.activate({
19 | fields: {},
20 | content: {},
21 | });
22 | props.activate({
23 | fields: {},
24 | content: {},
25 | });
26 | });
27 |
28 | it('deactivate twice has no effect', () => {
29 | const { broker } = ActivityBroker();
30 | const props = new Properties(
31 | {
32 | id: 'input',
33 | broker,
34 | environment: new Environment(),
35 | },
36 | {
37 | values: [],
38 | }
39 | );
40 | props.activate({
41 | fields: {},
42 | content: {},
43 | });
44 | props.deactivate();
45 | props.deactivate();
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/resources/README.md:
--------------------------------------------------------------------------------
1 | # Resources
2 |
3 | All modeled with [Camunda modeler](https://camunda.org/download/modeler/).
4 |
--------------------------------------------------------------------------------
/test/resources/call-activity-signal.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/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/consumer_error.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow0
6 |
7 |
8 | flow2end
9 |
10 |
11 |
12 | flow3
13 | flow2
14 | flow4
15 | flow2end
16 |
17 |
18 |
19 | flow1
20 | flow3
21 | flow4
22 |
23 |
24 | next(null, false)
25 |
26 |
27 | flow0
28 | flow1
29 | flow2
30 |
31 |
32 | next(null, false)
33 |
34 |
35 | next(null, true)
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/test/resources/escalation-start-event.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SequenceFlow_1a2bsh6
6 |
7 |
8 |
9 | SequenceFlow_1a2bsh6
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/test/resources/extensions/JsExtension.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import { brokerSafeId } from '../../../src/shared.js';
3 |
4 | const moddleOptions = JSON.parse(fs.readFileSync('./test/resources/js-bpmn-moddle.json'));
5 |
6 | export default {
7 | extension: Js,
8 | moddleOptions,
9 | };
10 |
11 | function Js(activity, context) {
12 | const resultVariable = ResultVariableIo(activity, context);
13 | const formKey = FormKey(activity, context);
14 |
15 | return {
16 | type: 'js:extension',
17 | extensions: { resultVariable, formKey },
18 | activate(msg) {
19 | if (resultVariable) resultVariable.activate(msg);
20 | if (formKey) formKey.activate(msg);
21 | },
22 | deactivate() {
23 | if (resultVariable) resultVariable.deactivate();
24 | if (formKey) formKey.deactivate();
25 | },
26 | };
27 | }
28 |
29 | function ResultVariableIo(activity) {
30 | const { result } = activity.behaviour;
31 | if (!result) return;
32 |
33 | const { id, logger, environment } = activity;
34 | const { broker } = activity;
35 |
36 | const type = 'js:resultvariable';
37 | let activityConsumer;
38 |
39 | return {
40 | type,
41 | activate,
42 | deactivate,
43 | };
44 |
45 | function deactivate() {
46 | if (activityConsumer) activityConsumer = activityConsumer.cancel();
47 | }
48 |
49 | function activate() {
50 | if (activityConsumer) return;
51 | activityConsumer = broker.subscribeTmp('event', 'activity.end', onActivityEnd, { noAck: true });
52 | }
53 |
54 | function onActivityEnd(_, message) {
55 | const resultName = environment.resolveExpression(result, message.content);
56 | logger.debug(`<${id}> js:extension save to "${resultName}"`);
57 |
58 | environment.output[resultName] = message.content.output;
59 | }
60 | }
61 |
62 | function FormKey(activity, context) {
63 | const { id, logger, behaviour } = activity;
64 | const { formKey } = behaviour;
65 | if (!formKey) return;
66 |
67 | const { broker } = activity;
68 | const { environment } = context;
69 |
70 | const type = 'js:formkey';
71 | const safeType = brokerSafeId(type).toLowerCase();
72 | let activityConsumer;
73 |
74 | return {
75 | type,
76 | activate,
77 | deactivate,
78 | };
79 |
80 | function deactivate() {
81 | if (activityConsumer) activityConsumer = activityConsumer.cancel();
82 | }
83 |
84 | function activate() {
85 | if (activityConsumer) return;
86 | activityConsumer = broker.subscribeTmp('event', 'activity.start', onActivityStart, { noAck: true, consumerTag: '_' });
87 | }
88 |
89 | function onActivityStart(_, message) {
90 | const formKeyValue = environment.resolveExpression(formKey, message);
91 | logger.debug(`<${id}> apply form`);
92 |
93 | broker.publish('format', `run.${safeType}.start`, {
94 | form: {
95 | type,
96 | key: formKeyValue,
97 | },
98 | });
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/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": "ProcessProperties",
11 | "isAbstract": true,
12 | "extends": ["bpmn:Process"],
13 | "properties": [
14 | {
15 | "name": "candidateStarterUsers",
16 | "isAttr": true,
17 | "type": "String"
18 | },
19 | {
20 | "name": "versionTag",
21 | "isAttr": true,
22 | "type": "String"
23 | }
24 | ]
25 | },
26 | {
27 | "name": "TaskResult",
28 | "isAbstract": true,
29 | "extends": ["bpmn:Task", "bpmn:SubProcess"],
30 | "properties": [
31 | {
32 | "name": "result",
33 | "isAttr": true,
34 | "type": "String"
35 | }
36 | ]
37 | },
38 | {
39 | "name": "Output",
40 | "superClass": ["Element"]
41 | },
42 | {
43 | "name": "Collectable",
44 | "isAbstract": true,
45 | "extends": ["bpmn:MultiInstanceLoopCharacteristics"],
46 | "properties": [
47 | {
48 | "name": "collection",
49 | "isAttr": true,
50 | "type": "String"
51 | },
52 | {
53 | "name": "elementVariable",
54 | "isAttr": true,
55 | "type": "String"
56 | }
57 | ]
58 | },
59 | {
60 | "name": "FormSupported",
61 | "isAbstract": true,
62 | "extends": ["bpmn:StartEvent", "bpmn:UserTask"],
63 | "properties": [
64 | {
65 | "name": "formKey",
66 | "type": "String",
67 | "isAttr": true
68 | }
69 | ]
70 | },
71 | {
72 | "name": "ScriptTask",
73 | "isAbstract": true,
74 | "extends": ["bpmn:ScriptTask"],
75 | "properties": [
76 | {
77 | "name": "resource",
78 | "isAttr": true,
79 | "type": "String"
80 | }
81 | ]
82 | },
83 | {
84 | "name": "FormalExpression",
85 | "isAbstract": true,
86 | "extends": ["bpmn:FormalExpression"],
87 | "properties": [
88 | {
89 | "name": "resource",
90 | "isAttr": true,
91 | "type": "String"
92 | }
93 | ]
94 | }
95 | ]
96 | }
97 |
--------------------------------------------------------------------------------
/test/resources/link-event.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | flow1
6 |
7 |
8 | flow1
9 | flow2
10 | environment.services.log("task1"); next()
11 |
12 |
13 | flow4
14 |
15 |
16 | flow3
17 |
18 |
19 |
20 | flow3
21 | flow4
22 | environment.services.log("task2"); next()
23 |
24 |
25 | flow2
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/test/resources/process-message.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/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/signal-after-signal.bpmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Flow_16dj1xw
6 |
7 |
8 |
9 | Flow_16dj1xw
10 | Flow_1skrjsj
11 |
12 |
13 |
14 |
15 | Flow_1skrjsj
16 | Flow_1bmd3eh
17 |
18 |
19 |
20 | Flow_1bmd3eh
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 |
--------------------------------------------------------------------------------
/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/shared-test.js:
--------------------------------------------------------------------------------
1 | import { generateId, brokerSafeId } from '../src/shared.js';
2 |
3 | describe('shared', () => {
4 | describe('brokerSafeId', () => {
5 | it('removes whitespace, dots, stars, hashes, and slashes from string', () => {
6 | expect(brokerSafeId(' my\n\rinput')).to.equal('__my__input');
7 | expect(brokerSafeId('a.b')).to.equal('a_b');
8 | expect(brokerSafeId('a.b.c')).to.equal('a_b_c');
9 | expect(brokerSafeId('a*b')).to.equal('a_b');
10 | expect(brokerSafeId('a*b*c')).to.equal('a_b_c');
11 | expect(brokerSafeId('a#b')).to.equal('a_b');
12 | expect(brokerSafeId('a#b#c')).to.equal('a_b_c');
13 | expect(brokerSafeId('a/b')).to.equal('a_b');
14 | expect(brokerSafeId('a/b/c')).to.equal('a_b_c');
15 | expect(brokerSafeId('a\\b')).to.equal('a_b');
16 | expect(brokerSafeId('a\\b\\c')).to.equal('a_b_c');
17 | expect(brokerSafeId('a.b*c#d/e\\f')).to.equal('a_b_c_d_e_f');
18 | });
19 | });
20 |
21 | describe('generateId', () => {
22 | it('generates at least 2000 unique ids', () => {
23 | const ids = [];
24 | for (let i = 0; i < 2000; i++) ids.push(generateId());
25 |
26 | expect(ids.filter((a, idx, self) => self.indexOf(a) === idx).length).to.equal(2000);
27 | });
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/test/tasks/BusinessRuleTask-test.js:
--------------------------------------------------------------------------------
1 | import testHelpers from '../helpers/testHelpers.js';
2 |
3 | describe('BusinessRuleTask', () => {
4 | it('behaves like service task', async () => {
5 | const source = `
6 |
7 |
8 |
9 |
10 |
11 | `;
12 |
13 | const context = await testHelpers.context(source);
14 | context.environment.addService('rule', (...args) => {
15 | args.pop()(null, true);
16 | });
17 |
18 | const task = context.getActivityById('task');
19 |
20 | task.activate();
21 |
22 | const leave = task.waitFor('leave');
23 |
24 | task.run();
25 |
26 | const api = await leave;
27 |
28 | expect(api.content.output).to.eql([true]);
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/tasks/CallActivity-test.js:
--------------------------------------------------------------------------------
1 | import testHelpers from '../helpers/testHelpers.js';
2 |
3 | describe('CallActivity', () => {
4 | const source = `
5 |
6 |
7 |
8 |
9 |
10 | `;
11 |
12 | let context;
13 | beforeEach(async () => {
14 | context = await testHelpers.context(source);
15 | });
16 |
17 | it('ignores delegate message without message', () => {
18 | const task = context.getActivityById('task');
19 |
20 | task.run();
21 |
22 | task.broker.publish('api', 'definition.signal.Def_1', {}, { delegate: true });
23 |
24 | expect(task.status).to.equal('executing');
25 | });
26 |
27 | it('ignores non-delegated api message', () => {
28 | const task = context.getActivityById('task');
29 |
30 | task.run();
31 |
32 | task.broker.publish('api', 'definition.signal.Def_1', { message: { id: 'task' } }, { type: 'cancel', delegate: false });
33 |
34 | expect(task.status).to.equal('executing');
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/tasks/ManualTask-test.js:
--------------------------------------------------------------------------------
1 | import testHelpers from '../helpers/testHelpers.js';
2 |
3 | describe('ManualTask', () => {
4 | const source = `
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | `;
15 |
16 | let context;
17 | beforeEach(async () => {
18 | context = await testHelpers.context(source);
19 | });
20 |
21 | it('keeps execute wait state until signaled', async () => {
22 | const task = context.getActivityById('task');
23 |
24 | const waiting = task.waitFor('wait');
25 | const left = task.waitFor('leave');
26 | task.run();
27 |
28 | const waitApi = await waiting;
29 |
30 | expect(task.broker.getQueue('run-q').messageCount, 'run queue').to.equal(1);
31 | expect(task.broker.getQueue('execute-q').messageCount, 'execute queue').to.equal(1);
32 |
33 | waitApi.signal();
34 |
35 | await left;
36 |
37 | expect(task.broker.getQueue('run-q').messageCount, 'run queue').to.equal(0);
38 | expect(task.broker.getQueue('execute-q').messageCount, 'execute queue').to.equal(0);
39 | });
40 |
41 | it('sets output and completes when signaled', async () => {
42 | const task = context.getActivityById('task');
43 |
44 | const waiting = task.waitFor('wait');
45 | const leave = task.waitFor('leave');
46 | task.activate();
47 | task.run();
48 |
49 | const taskApi = await waiting;
50 | taskApi.signal({ data: 1 });
51 |
52 | const api = await leave;
53 |
54 | expect(api.content.output).to.eql({ data: 1 });
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/test/tasks/SendTask-test.js:
--------------------------------------------------------------------------------
1 | import testHelpers from '../helpers/testHelpers.js';
2 |
3 | describe('SendTask', () => {
4 | it('behaves like service task', async () => {
5 | const source = `
6 |
7 |
8 |
9 |
10 |
11 | `;
12 |
13 | const context = await testHelpers.context(source);
14 | context.environment.addService('get', (...args) => {
15 | args.pop()(null, true);
16 | });
17 |
18 | const task = context.getActivityById('task');
19 |
20 | task.activate();
21 |
22 | const leave = task.waitFor('leave');
23 |
24 | task.run();
25 |
26 | const api = await leave;
27 |
28 | expect(api.content.output).to.eql([true]);
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/test/tasks/Task-test.js:
--------------------------------------------------------------------------------
1 | import testHelpers from '../helpers/testHelpers.js';
2 |
3 | describe('Task', () => {
4 | const source = `
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | `;
15 |
16 | let context;
17 | beforeEach(async () => {
18 | context = await testHelpers.context(source);
19 | });
20 |
21 | it('completes immediately when executed', async () => {
22 | const task = context.getActivityById('task');
23 |
24 | const left = task.waitFor('leave');
25 | task.run();
26 |
27 | await left;
28 |
29 | expect(task.broker.getQueue('run-q').messageCount, 'run queue').to.equal(0);
30 | expect(task.broker.getQueue('execute-q').messageCount, 'execute queue').to.equal(0);
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/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/types.js"]
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------