@0dep/bpmn-extensions 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/dist/index.js +159 -45
- package/package.json +12 -4
- package/src/ElementExtensions.js +51 -15
- package/src/Expressions.js +2 -5
- package/src/ProcessExtensions.js +7 -0
- package/src/SubProcessExtensions.js +5 -5
- package/src/extensions/FeelScripts.js +10 -2
- package/src/extensions/Subscription.js +30 -0
- package/src/extensions/TaskDefinition.js +6 -0
- package/src/extensions/formatters.js +14 -1
- package/src/feel.js +3 -3
- package/src/getExtensions.js +32 -13
- package/src/index.js +2 -2
- package/types/index.d.ts +99 -47
- package/types/index.d.ts.map +6 -3
package/README.md
CHANGED
|
@@ -40,6 +40,10 @@ npm install @0dep/bpmn-extensions bpmn-elements
|
|
|
40
40
|
- **`zeebe:calledElement`** — call activity target process id.
|
|
41
41
|
- **`zeebe:loopCharacteristics`** — collection-based multi-instance (`inputCollection` / `inputElement`),
|
|
42
42
|
sequential or parallel, with `outputElement` aggregated into the `outputCollection` array in input order.
|
|
43
|
+
- **`zeebe:subscription`** — the referenced message's `correlationKey` is resolved when the catching
|
|
44
|
+
element (receive task, message event) starts waiting, and exposed on its content as
|
|
45
|
+
`subscription: { message, correlationKey }` — route an incoming message by matching it against the
|
|
46
|
+
waiting elements (`getPostponed()`) and signalling the right one.
|
|
43
47
|
|
|
44
48
|
## API
|
|
45
49
|
|
|
@@ -128,8 +132,12 @@ in `test/helpers/factory.js`, then run on a real bpmn-elements `Definition`. The
|
|
|
128
132
|
the engine's `Logger` to the [`debug`](https://www.npmjs.com/package/debug) package, so
|
|
129
133
|
`DEBUG=bpmn-extensions:* npm test` traces the engine and extensions (`:error:*` for errors only).
|
|
130
134
|
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
# Ecosystem
|
|
136
|
+
|
|
137
|
+
- [bpmn-engine](https://github.com/paed01/bpmn-engine) — BPMN 2.0 execution engine wrapping `bpmn-elements`; the batteries-included way to run, stop, resume, and recover flows.
|
|
138
|
+
- [bpmn-middleware](https://github.com/zerodep/bpmn-middleware) — Express middleware exposing the engine over HTTP, with pluggable state storage.
|
|
139
|
+
- [@onify/flow-extensions](https://github.com/onify/flow-extensions) — Onify Flow extensions for `bpmn-elements`.
|
|
140
|
+
- [dmn-elements](https://github.com/zerodep/dmn-elements) — Executable DMN 1.3 decision elements; back a Business Rule Task with it (see [Conformance](/docs/Conformance.md#business-rule-task-and-dmn)).
|
|
133
141
|
|
|
134
142
|
## License
|
|
135
143
|
|
package/dist/index.js
CHANGED
|
@@ -16203,11 +16203,11 @@ function evaluateFeel(expression, context) {
|
|
|
16203
16203
|
* @param {string} expression FEEL unary test
|
|
16204
16204
|
* @param {any} input The value being tested, available as `?` in the test
|
|
16205
16205
|
* @param {Record<string, any>} [context] Additional variables in scope
|
|
16206
|
-
* @returns {boolean}
|
|
16206
|
+
* @returns {boolean} true only when the test is satisfied — an undecidable (null) test is false
|
|
16207
16207
|
*/
|
|
16208
16208
|
function evaluateFeelUnaryTest(expression, input, context) {
|
|
16209
|
-
const { value } = unaryTest(stripFeel(expression), { ...
|
|
16210
|
-
return value;
|
|
16209
|
+
const { value } = unaryTest(stripFeel(expression), { ...context, '?': input });
|
|
16210
|
+
return value === true;
|
|
16211
16211
|
}
|
|
16212
16212
|
|
|
16213
16213
|
/**
|
|
@@ -16227,10 +16227,17 @@ function resolveValue(value, context) {
|
|
|
16227
16227
|
* for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups).
|
|
16228
16228
|
*/
|
|
16229
16229
|
class FormatActivity {
|
|
16230
|
+
/**
|
|
16231
|
+
* @param {import('bpmn-elements').Activity} activity
|
|
16232
|
+
* @param {any} assignmentDefinition
|
|
16233
|
+
*/
|
|
16230
16234
|
constructor(activity, assignmentDefinition) {
|
|
16231
16235
|
this.activity = activity;
|
|
16232
16236
|
this.assignmentDefinition = assignmentDefinition;
|
|
16233
16237
|
}
|
|
16238
|
+
/**
|
|
16239
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
16240
|
+
*/
|
|
16234
16241
|
resolve(elementApi) {
|
|
16235
16242
|
const scope = elementApi.environment.variables;
|
|
16236
16243
|
const result = {};
|
|
@@ -16259,9 +16266,15 @@ class FormatActivity {
|
|
|
16259
16266
|
* Format a process on enter: documentation.
|
|
16260
16267
|
*/
|
|
16261
16268
|
class FormatProcess {
|
|
16269
|
+
/**
|
|
16270
|
+
* @param {import('bpmn-elements').Process} bp
|
|
16271
|
+
*/
|
|
16262
16272
|
constructor(bp) {
|
|
16263
16273
|
this.process = bp;
|
|
16264
16274
|
}
|
|
16275
|
+
/**
|
|
16276
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Process>} elementApi
|
|
16277
|
+
*/
|
|
16265
16278
|
resolve(elementApi) {
|
|
16266
16279
|
const result = {};
|
|
16267
16280
|
const documentation = this.process.behaviour.documentation;
|
|
@@ -16280,7 +16293,7 @@ class FormatProcess {
|
|
|
16280
16293
|
function resolveList(value, scope) {
|
|
16281
16294
|
if (!value) return undefined;
|
|
16282
16295
|
const resolved = resolveValue(value, scope);
|
|
16283
|
-
if (Array.isArray(resolved)) return resolved.filter(
|
|
16296
|
+
if (Array.isArray(resolved)) return resolved.filter(Boolean);
|
|
16284
16297
|
if (typeof resolved !== 'string') return undefined;
|
|
16285
16298
|
return resolved
|
|
16286
16299
|
.split(',')
|
|
@@ -16558,6 +16571,12 @@ function JobService(taskType, activity) {
|
|
|
16558
16571
|
this.activity = activity;
|
|
16559
16572
|
}
|
|
16560
16573
|
|
|
16574
|
+
/**
|
|
16575
|
+
* Execute
|
|
16576
|
+
* @param {import('bpmn-elements').ElementBrokerMessage} executionMessage
|
|
16577
|
+
* @param {CallableFunction} callback
|
|
16578
|
+
* @returns {any}
|
|
16579
|
+
*/
|
|
16561
16580
|
JobService.prototype.execute = function execute(executionMessage, callback) {
|
|
16562
16581
|
const activity = this.activity;
|
|
16563
16582
|
const environment = activity.environment;
|
|
@@ -16575,23 +16594,58 @@ JobService.prototype.execute = function execute(executionMessage, callback) {
|
|
|
16575
16594
|
return serviceFn.call(activity, executionMessage, (err, result) => callback(err, result));
|
|
16576
16595
|
};
|
|
16577
16596
|
|
|
16597
|
+
/**
|
|
16598
|
+
* `zeebe:subscription` — rides on the referenced `bpmn:Message`, not on the catching element.
|
|
16599
|
+
*
|
|
16600
|
+
* Resolves the correlation key in the activity's variable scope on enter and exposes it on the
|
|
16601
|
+
* element content as `subscription`. Correlation itself is the embedding application's job:
|
|
16602
|
+
* read the resolved key off the waiting activity (`getPostponed()` → `content.subscription`)
|
|
16603
|
+
* and signal the matching one (`api.signal(...)`).
|
|
16604
|
+
*/
|
|
16605
|
+
class Subscription {
|
|
16606
|
+
/**
|
|
16607
|
+
* @param {{ id: string, name?: string }} messageRef The serialized message reference
|
|
16608
|
+
* @param {{ correlationKey: string }} subscription The `zeebe:Subscription` behaviour
|
|
16609
|
+
*/
|
|
16610
|
+
constructor(messageRef, subscription) {
|
|
16611
|
+
this.message = { id: messageRef.id, name: messageRef.name };
|
|
16612
|
+
this.correlationKey = subscription.correlationKey;
|
|
16613
|
+
}
|
|
16614
|
+
/**
|
|
16615
|
+
* @param {import('bpmn-elements').IApi<any>} elementApi
|
|
16616
|
+
* @returns {{ message: { id: string, name?: string }, correlationKey: any }}
|
|
16617
|
+
*/
|
|
16618
|
+
resolve(elementApi) {
|
|
16619
|
+
return {
|
|
16620
|
+
message: this.message,
|
|
16621
|
+
correlationKey: resolveValue(this.correlationKey, elementApi.environment.variables),
|
|
16622
|
+
};
|
|
16623
|
+
}
|
|
16624
|
+
}
|
|
16625
|
+
|
|
16626
|
+
/**
|
|
16627
|
+
* The extension handlers assembled for one element.
|
|
16628
|
+
* @typedef {object} ExtensionHandlers
|
|
16629
|
+
* @property {FormatActivity | FormatProcess} format
|
|
16630
|
+
* @property {IoMapping} [io]
|
|
16631
|
+
* @property {TaskHeaders} [headers]
|
|
16632
|
+
* @property {Properties} [properties]
|
|
16633
|
+
* @property {ExecutionListeners} [listeners]
|
|
16634
|
+
* @property {Form} [form]
|
|
16635
|
+
* @property {LoopCharacteristics} [loop]
|
|
16636
|
+
* @property {any} [script]
|
|
16637
|
+
* @property {any} [calledDecision]
|
|
16638
|
+
* @property {Function} [Service]
|
|
16639
|
+
* @property {Subscription} [subscription]
|
|
16640
|
+
*/
|
|
16641
|
+
|
|
16578
16642
|
/**
|
|
16579
16643
|
* Inspect an element's extension elements and assemble the extension handlers it needs.
|
|
16580
16644
|
* @param {import('bpmn-elements').Activity | import('bpmn-elements').Process} element
|
|
16581
|
-
* @
|
|
16582
|
-
*
|
|
16583
|
-
* io?: IoMapping,
|
|
16584
|
-
* headers?: TaskHeaders,
|
|
16585
|
-
* properties?: Properties,
|
|
16586
|
-
* listeners?: ExecutionListeners,
|
|
16587
|
-
* form?: Form,
|
|
16588
|
-
* loop?: LoopCharacteristics,
|
|
16589
|
-
* script?: any,
|
|
16590
|
-
* calledDecision?: any,
|
|
16591
|
-
* Service?: Function,
|
|
16592
|
-
* }}
|
|
16645
|
+
* @param {import('bpmn-elements').ContextInstance} [context]
|
|
16646
|
+
* @returns {ExtensionHandlers}
|
|
16593
16647
|
*/
|
|
16594
|
-
function getExtensions(element) {
|
|
16648
|
+
function getExtensions(element, context) {
|
|
16595
16649
|
const result = {};
|
|
16596
16650
|
const isProcess = element.type === 'bpmn:Process';
|
|
16597
16651
|
|
|
@@ -16635,6 +16689,18 @@ function getExtensions(element) {
|
|
|
16635
16689
|
}
|
|
16636
16690
|
}
|
|
16637
16691
|
|
|
16692
|
+
// A message catch (receive task / message event) may reference a bpmn:Message carrying a
|
|
16693
|
+
// zeebe:subscription with the correlation key. The subscription lives on the message element,
|
|
16694
|
+
// which the instantiated bpmn-elements Message strips — read it off the serialized context.
|
|
16695
|
+
const messageRef =
|
|
16696
|
+
element.behaviour.messageRef ?? element.behaviour.eventDefinitions?.find((ed) => ed.behaviour?.messageRef)?.behaviour.messageRef;
|
|
16697
|
+
if (messageRef?.id && context) {
|
|
16698
|
+
const subscription = context.definitionContext
|
|
16699
|
+
.getActivityById(messageRef.id)
|
|
16700
|
+
?.behaviour.extensionElements?.values?.find((v) => v.$type === 'zeebe:Subscription');
|
|
16701
|
+
if (subscription?.correlationKey) result.subscription = new Subscription(messageRef, subscription);
|
|
16702
|
+
}
|
|
16703
|
+
|
|
16638
16704
|
// Output side of a multi-instance `zeebe:loopCharacteristics` (the input side is lifted by extendFn).
|
|
16639
16705
|
const loopExtension = element.behaviour.loopCharacteristics?.behaviour?.extensionElements?.values?.find(
|
|
16640
16706
|
(v) => v.$type === 'zeebe:LoopCharacteristics'
|
|
@@ -16648,6 +16714,13 @@ function getExtensions(element) {
|
|
|
16648
16714
|
|
|
16649
16715
|
const SUBPROCESS_TYPES = new Set(['bpmn:SubProcess', 'bpmn:AdHocSubProcess', 'bpmn:Transaction']);
|
|
16650
16716
|
|
|
16717
|
+
// Consumer tags shared between activate (here and in the SubProcessExtensions override) and
|
|
16718
|
+
// deactivate — deactivate cancels by tag, so both must subscribe with these exact tags.
|
|
16719
|
+
const ON_ENTER_TAG = '0dep-bpmn-extensions:on-enter';
|
|
16720
|
+
const ON_EXECUTED_TAG = '0dep-bpmn-extensions:on-executed';
|
|
16721
|
+
const ON_START_TAG = '0dep-bpmn-extensions:on-start';
|
|
16722
|
+
const ON_END_TAG = '0dep-bpmn-extensions:on-end';
|
|
16723
|
+
|
|
16651
16724
|
/**
|
|
16652
16725
|
* Activity-level extensions.
|
|
16653
16726
|
*
|
|
@@ -16656,43 +16729,55 @@ const SUBPROCESS_TYPES = new Set(['bpmn:SubProcess', 'bpmn:AdHocSubProcess', 'bp
|
|
|
16656
16729
|
* proceeding — the same mechanism bpmn-elements uses for extension formatting.
|
|
16657
16730
|
*/
|
|
16658
16731
|
class ElementExtensions {
|
|
16659
|
-
|
|
16732
|
+
/**
|
|
16733
|
+
* @param {import('bpmn-elements').Activity} activity
|
|
16734
|
+
* @param {import('bpmn-elements').ContextInstance} context
|
|
16735
|
+
*/
|
|
16736
|
+
constructor(activity, context) {
|
|
16660
16737
|
this.activity = activity;
|
|
16661
16738
|
this.formatQ = activity.broker.getQueue('format-run-q');
|
|
16662
16739
|
|
|
16663
|
-
|
|
16740
|
+
/** @type {import('./getExtensions.js').ExtensionHandlers} */
|
|
16741
|
+
this.extensions = getExtensions(activity, context);
|
|
16664
16742
|
const Service = this.extensions.Service;
|
|
16665
16743
|
if (Service) activity.behaviour.Service = Service;
|
|
16666
16744
|
}
|
|
16745
|
+
/**
|
|
16746
|
+
* Activate extensions
|
|
16747
|
+
* @param {import('bpmn-elements').ElementBrokerMessage} message
|
|
16748
|
+
*/
|
|
16667
16749
|
activate(message) {
|
|
16668
16750
|
const activity = this.activity;
|
|
16669
16751
|
const listeners = this.extensions.listeners;
|
|
16670
16752
|
|
|
16671
16753
|
if (message.fields.redelivered && message.fields.routingKey === 'run.start') {
|
|
16672
|
-
activity.on('start', (api) => this._onEnter(api), { consumerTag:
|
|
16754
|
+
activity.on('start', (api) => this._onEnter(api), { consumerTag: ON_ENTER_TAG });
|
|
16673
16755
|
} else {
|
|
16674
|
-
activity.on('enter', (api) => this._onEnter(api), { consumerTag:
|
|
16756
|
+
activity.on('enter', (api) => this._onEnter(api), { consumerTag: ON_ENTER_TAG });
|
|
16675
16757
|
}
|
|
16676
16758
|
|
|
16677
16759
|
activity.on('activity.execution.completed', (api) => this._onExecuted(api), {
|
|
16678
|
-
consumerTag:
|
|
16760
|
+
consumerTag: ON_EXECUTED_TAG,
|
|
16679
16761
|
});
|
|
16680
16762
|
|
|
16681
16763
|
if (listeners?.onStart) {
|
|
16682
|
-
activity.on('start', (api) => this._onListener('start', api), { consumerTag:
|
|
16764
|
+
activity.on('start', (api) => this._onListener('start', api), { consumerTag: ON_START_TAG });
|
|
16683
16765
|
}
|
|
16684
16766
|
if (listeners?.onEnd) {
|
|
16685
|
-
activity.on('end', (api) => this._onListener('end', api), { consumerTag:
|
|
16767
|
+
activity.on('end', (api) => this._onListener('end', api), { consumerTag: ON_END_TAG });
|
|
16686
16768
|
}
|
|
16687
16769
|
}
|
|
16688
16770
|
deactivate() {
|
|
16689
16771
|
const broker = this.activity.broker;
|
|
16690
|
-
broker.cancel(
|
|
16691
|
-
broker.cancel(
|
|
16692
|
-
broker.cancel(
|
|
16693
|
-
broker.cancel(
|
|
16772
|
+
broker.cancel(ON_ENTER_TAG);
|
|
16773
|
+
broker.cancel(ON_EXECUTED_TAG);
|
|
16774
|
+
broker.cancel(ON_START_TAG);
|
|
16775
|
+
broker.cancel(ON_END_TAG);
|
|
16694
16776
|
}
|
|
16695
|
-
/**
|
|
16777
|
+
/**
|
|
16778
|
+
* @internal Shared with SubProcessExtensions.
|
|
16779
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
16780
|
+
*/
|
|
16696
16781
|
async _onEnter(elementApi) {
|
|
16697
16782
|
this.formatQ.queueMessage({ routingKey: 'run.enter.format' }, { endRoutingKey: 'run.enter.complete' }, { persistent: false });
|
|
16698
16783
|
try {
|
|
@@ -16702,7 +16787,10 @@ class ElementExtensions {
|
|
|
16702
16787
|
elementApi.broker.publish('format', 'run.enter.error', { error: err }, { persistent: false });
|
|
16703
16788
|
}
|
|
16704
16789
|
}
|
|
16705
|
-
/**
|
|
16790
|
+
/**
|
|
16791
|
+
* @internal Shared with SubProcessExtensions.
|
|
16792
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
16793
|
+
*/
|
|
16706
16794
|
async _onExecuted(elementApi) {
|
|
16707
16795
|
this.formatQ.queueMessage({ routingKey: 'run.end.format' }, { endRoutingKey: 'run.end.complete' }, { persistent: false });
|
|
16708
16796
|
try {
|
|
@@ -16712,7 +16800,10 @@ class ElementExtensions {
|
|
|
16712
16800
|
elementApi.broker.publish('format', 'run.end.error', { error: err }, { persistent: false });
|
|
16713
16801
|
}
|
|
16714
16802
|
}
|
|
16715
|
-
/**
|
|
16803
|
+
/**
|
|
16804
|
+
* @internal Shared with SubProcessExtensions.
|
|
16805
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
16806
|
+
*/
|
|
16716
16807
|
async _onListener(eventType, elementApi) {
|
|
16717
16808
|
const routingKey = `run.listener.${eventType}`;
|
|
16718
16809
|
this.formatQ.queueMessage({ routingKey }, { endRoutingKey: `${routingKey}.complete` }, { persistent: false });
|
|
@@ -16724,13 +16815,17 @@ class ElementExtensions {
|
|
|
16724
16815
|
elementApi.broker.publish('format', `${routingKey}.error`, { error: err }, { persistent: false });
|
|
16725
16816
|
}
|
|
16726
16817
|
}
|
|
16818
|
+
/**
|
|
16819
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
16820
|
+
*/
|
|
16727
16821
|
#formatOnEnter(elementApi) {
|
|
16728
|
-
const { format, io, headers, properties, form } = this.extensions;
|
|
16822
|
+
const { format, io, headers, properties, form, subscription } = this.extensions;
|
|
16729
16823
|
const result = { ...format.resolve(elementApi) };
|
|
16730
16824
|
|
|
16731
16825
|
if (headers) result.headers = headers.resolve();
|
|
16732
16826
|
if (properties) result.properties = properties.resolve(elementApi);
|
|
16733
16827
|
if (form) result.form = form.resolve(elementApi);
|
|
16828
|
+
if (subscription) result.subscription = subscription.resolve(elementApi);
|
|
16734
16829
|
if (io?.hasInput) {
|
|
16735
16830
|
result.input = io.getInput(elementApi);
|
|
16736
16831
|
// A (sub) process input mapping creates local variables visible to its children; a job
|
|
@@ -16742,6 +16837,9 @@ class ElementExtensions {
|
|
|
16742
16837
|
Object.assign(elementApi.content, result);
|
|
16743
16838
|
return result;
|
|
16744
16839
|
}
|
|
16840
|
+
/**
|
|
16841
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
16842
|
+
*/
|
|
16745
16843
|
#formatOnExecuted(elementApi) {
|
|
16746
16844
|
const { io, script, calledDecision, loop } = this.extensions;
|
|
16747
16845
|
let jobResult = elementApi.content.output;
|
|
@@ -16778,6 +16876,10 @@ class ElementExtensions {
|
|
|
16778
16876
|
}
|
|
16779
16877
|
return {};
|
|
16780
16878
|
}
|
|
16879
|
+
/**
|
|
16880
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
16881
|
+
* @param {any} output
|
|
16882
|
+
*/
|
|
16781
16883
|
#assignOutput(elementApi, output) {
|
|
16782
16884
|
const environment = this.activity.environment;
|
|
16783
16885
|
// Visible to downstream FEEL within the process, and surfaced as process output
|
|
@@ -16793,8 +16895,12 @@ class ElementExtensions {
|
|
|
16793
16895
|
* the result to the process variables.
|
|
16794
16896
|
*/
|
|
16795
16897
|
class ProcessExtensions {
|
|
16898
|
+
/**
|
|
16899
|
+
* @param {import('bpmn-elements').Process} bp
|
|
16900
|
+
*/
|
|
16796
16901
|
constructor(bp) {
|
|
16797
16902
|
this.process = bp;
|
|
16903
|
+
/** @type {import('./getExtensions.js').ExtensionHandlers} */
|
|
16798
16904
|
this.extensions = getExtensions(bp);
|
|
16799
16905
|
}
|
|
16800
16906
|
activate() {
|
|
@@ -16808,6 +16914,9 @@ class ProcessExtensions {
|
|
|
16808
16914
|
deactivate() {
|
|
16809
16915
|
this.process.broker.cancel('0dep-bpmn-extensions:on-enter');
|
|
16810
16916
|
}
|
|
16917
|
+
/**
|
|
16918
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Process>} elementApi
|
|
16919
|
+
*/
|
|
16811
16920
|
#onEnter(elementApi) {
|
|
16812
16921
|
try {
|
|
16813
16922
|
// bpmn-elements (>=18.0.4) forwards a call activity's formatted input — its io mapping plus,
|
|
@@ -16849,14 +16958,14 @@ class SubProcessExtensions extends ElementExtensions {
|
|
|
16849
16958
|
const listeners = this.extensions.listeners;
|
|
16850
16959
|
const enterKey = message.fields.redelivered && message.fields.routingKey === 'run.start' ? 'activity.start' : 'activity.enter';
|
|
16851
16960
|
|
|
16852
|
-
this.#subscribe(enterKey, (api) => this._onEnter(api),
|
|
16853
|
-
this.#subscribe('activity.execution.completed', (api) => this._onExecuted(api),
|
|
16961
|
+
this.#subscribe(enterKey, (api) => this._onEnter(api), ON_ENTER_TAG);
|
|
16962
|
+
this.#subscribe('activity.execution.completed', (api) => this._onExecuted(api), ON_EXECUTED_TAG);
|
|
16854
16963
|
|
|
16855
16964
|
if (listeners?.onStart) {
|
|
16856
|
-
this.#subscribe('activity.start', (api) => this._onListener('start', api),
|
|
16965
|
+
this.#subscribe('activity.start', (api) => this._onListener('start', api), ON_START_TAG);
|
|
16857
16966
|
}
|
|
16858
16967
|
if (listeners?.onEnd) {
|
|
16859
|
-
this.#subscribe('activity.end', (api) => this._onListener('end', api),
|
|
16968
|
+
this.#subscribe('activity.end', (api) => this._onListener('end', api), ON_END_TAG);
|
|
16860
16969
|
}
|
|
16861
16970
|
}
|
|
16862
16971
|
#subscribe(routingKey, callback, consumerTag) {
|
|
@@ -16880,7 +16989,7 @@ class SubProcessExtensions extends ElementExtensions {
|
|
|
16880
16989
|
* definition resolves FEEL expressions (`= ...`) instead of the default
|
|
16881
16990
|
* `${...}` template expressions.
|
|
16882
16991
|
*
|
|
16883
|
-
* @returns {import('bpmn-elements').IExpressions}
|
|
16992
|
+
* @returns {import('bpmn-elements').IExpressions & {isExpression: (text:string) => boolean, hasExpression: (text:string) => boolean}}
|
|
16884
16993
|
*/
|
|
16885
16994
|
function FeelExpressions() {
|
|
16886
16995
|
return {
|
|
@@ -16915,10 +17024,7 @@ function resolveExpression(templatedString, context) {
|
|
|
16915
17024
|
* (e.g. a multi-instance item).
|
|
16916
17025
|
*/
|
|
16917
17026
|
function getScope(context) {
|
|
16918
|
-
|
|
16919
|
-
const variables = environment ? environment.variables : {};
|
|
16920
|
-
const local = context && context.content && context.content.variables;
|
|
16921
|
-
return { ...variables, ...local };
|
|
17027
|
+
return { ...context?.environment?.variables, ...context?.content?.variables };
|
|
16922
17028
|
}
|
|
16923
17029
|
|
|
16924
17030
|
/**
|
|
@@ -16937,10 +17043,18 @@ function FeelScripts() {
|
|
|
16937
17043
|
}
|
|
16938
17044
|
|
|
16939
17045
|
class FeelScriptRegistry {
|
|
16940
|
-
register() {
|
|
17046
|
+
register() {
|
|
17047
|
+
return undefined;
|
|
17048
|
+
}
|
|
17049
|
+
/**
|
|
17050
|
+
* @param {string} _scriptFormat
|
|
17051
|
+
* @param {{ id: string, [x: string]: any }} activity
|
|
17052
|
+
* @returns {import('bpmn-elements').Script} the FEEL script, or undefined when the activity
|
|
17053
|
+
* has no `zeebe:script` — bpmn-elements handles that, but `IScripts` declares a bare `Script`
|
|
17054
|
+
*/
|
|
16941
17055
|
getScript(_scriptFormat, activity) {
|
|
16942
17056
|
const expression = getScriptExpression(activity);
|
|
16943
|
-
if (expression === undefined) return undefined;
|
|
17057
|
+
if (expression === undefined) return /** @type {any} */ (undefined);
|
|
16944
17058
|
return new FeelScript(expression);
|
|
16945
17059
|
}
|
|
16946
17060
|
}
|
|
@@ -16972,8 +17086,8 @@ function getScriptExpression(activity) {
|
|
|
16972
17086
|
/**
|
|
16973
17087
|
* A flow extension activated by bpmn-elements around an element's run.
|
|
16974
17088
|
* @typedef {object} FlowExtension
|
|
16975
|
-
* @property {(message:
|
|
16976
|
-
* @property {(message?:
|
|
17089
|
+
* @property {(message: import('bpmn-elements').ElementBrokerMessage) => void} activate
|
|
17090
|
+
* @property {(message?: import('bpmn-elements').ElementBrokerMessage) => void} deactivate
|
|
16977
17091
|
*/
|
|
16978
17092
|
|
|
16979
17093
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0dep/bpmn-extensions",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Flow extensions for bpmn-elements: FEEL expressions and the zeebe-namespace BPMN extension elements",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -51,18 +51,26 @@
|
|
|
51
51
|
"access": "public"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"bpmn-elements": ">=18.0.
|
|
54
|
+
"bpmn-elements": ">=18.0.12"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"feelin": "^7.0.1"
|
|
58
58
|
},
|
|
59
|
+
"overrides": {
|
|
60
|
+
"mocha": {
|
|
61
|
+
"yargs": "^18.1.0"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
59
64
|
"devDependencies": {
|
|
60
65
|
"@eslint/js": "^10.0.1",
|
|
61
66
|
"@rollup/plugin-commonjs": "^29.0.3",
|
|
62
67
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
63
|
-
"
|
|
68
|
+
"@types/chai": "^5.2.3",
|
|
69
|
+
"@types/mocha": "^10.0.10",
|
|
70
|
+
"@types/node": "^26.1.1",
|
|
71
|
+
"bpmn-elements": "^18.0.12",
|
|
64
72
|
"bpmn-moddle": "^9.0.1",
|
|
65
|
-
"c8": "^
|
|
73
|
+
"c8": "^12.0.0",
|
|
66
74
|
"chai": "^6.2.2",
|
|
67
75
|
"debug": "^4.4.3",
|
|
68
76
|
"dts-buddy": "^0.8.2",
|
package/src/ElementExtensions.js
CHANGED
|
@@ -2,6 +2,13 @@ import { getExtensions } from './getExtensions.js';
|
|
|
2
2
|
|
|
3
3
|
const SUBPROCESS_TYPES = new Set(['bpmn:SubProcess', 'bpmn:AdHocSubProcess', 'bpmn:Transaction']);
|
|
4
4
|
|
|
5
|
+
// Consumer tags shared between activate (here and in the SubProcessExtensions override) and
|
|
6
|
+
// deactivate — deactivate cancels by tag, so both must subscribe with these exact tags.
|
|
7
|
+
export const ON_ENTER_TAG = '0dep-bpmn-extensions:on-enter';
|
|
8
|
+
export const ON_EXECUTED_TAG = '0dep-bpmn-extensions:on-executed';
|
|
9
|
+
export const ON_START_TAG = '0dep-bpmn-extensions:on-start';
|
|
10
|
+
export const ON_END_TAG = '0dep-bpmn-extensions:on-end';
|
|
11
|
+
|
|
5
12
|
/**
|
|
6
13
|
* Activity-level extensions.
|
|
7
14
|
*
|
|
@@ -10,43 +17,55 @@ const SUBPROCESS_TYPES = new Set(['bpmn:SubProcess', 'bpmn:AdHocSubProcess', 'bp
|
|
|
10
17
|
* proceeding — the same mechanism bpmn-elements uses for extension formatting.
|
|
11
18
|
*/
|
|
12
19
|
export class ElementExtensions {
|
|
13
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @param {import('bpmn-elements').Activity} activity
|
|
22
|
+
* @param {import('bpmn-elements').ContextInstance} context
|
|
23
|
+
*/
|
|
24
|
+
constructor(activity, context) {
|
|
14
25
|
this.activity = activity;
|
|
15
26
|
this.formatQ = activity.broker.getQueue('format-run-q');
|
|
16
27
|
|
|
17
|
-
|
|
28
|
+
/** @type {import('./getExtensions.js').ExtensionHandlers} */
|
|
29
|
+
this.extensions = getExtensions(activity, context);
|
|
18
30
|
const Service = this.extensions.Service;
|
|
19
31
|
if (Service) activity.behaviour.Service = Service;
|
|
20
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Activate extensions
|
|
35
|
+
* @param {import('bpmn-elements').ElementBrokerMessage} message
|
|
36
|
+
*/
|
|
21
37
|
activate(message) {
|
|
22
38
|
const activity = this.activity;
|
|
23
39
|
const listeners = this.extensions.listeners;
|
|
24
40
|
|
|
25
41
|
if (message.fields.redelivered && message.fields.routingKey === 'run.start') {
|
|
26
|
-
activity.on('start', (api) => this._onEnter(api), { consumerTag:
|
|
42
|
+
activity.on('start', (api) => this._onEnter(api), { consumerTag: ON_ENTER_TAG });
|
|
27
43
|
} else {
|
|
28
|
-
activity.on('enter', (api) => this._onEnter(api), { consumerTag:
|
|
44
|
+
activity.on('enter', (api) => this._onEnter(api), { consumerTag: ON_ENTER_TAG });
|
|
29
45
|
}
|
|
30
46
|
|
|
31
47
|
activity.on('activity.execution.completed', (api) => this._onExecuted(api), {
|
|
32
|
-
consumerTag:
|
|
48
|
+
consumerTag: ON_EXECUTED_TAG,
|
|
33
49
|
});
|
|
34
50
|
|
|
35
51
|
if (listeners?.onStart) {
|
|
36
|
-
activity.on('start', (api) => this._onListener('start', api), { consumerTag:
|
|
52
|
+
activity.on('start', (api) => this._onListener('start', api), { consumerTag: ON_START_TAG });
|
|
37
53
|
}
|
|
38
54
|
if (listeners?.onEnd) {
|
|
39
|
-
activity.on('end', (api) => this._onListener('end', api), { consumerTag:
|
|
55
|
+
activity.on('end', (api) => this._onListener('end', api), { consumerTag: ON_END_TAG });
|
|
40
56
|
}
|
|
41
57
|
}
|
|
42
58
|
deactivate() {
|
|
43
59
|
const broker = this.activity.broker;
|
|
44
|
-
broker.cancel(
|
|
45
|
-
broker.cancel(
|
|
46
|
-
broker.cancel(
|
|
47
|
-
broker.cancel(
|
|
60
|
+
broker.cancel(ON_ENTER_TAG);
|
|
61
|
+
broker.cancel(ON_EXECUTED_TAG);
|
|
62
|
+
broker.cancel(ON_START_TAG);
|
|
63
|
+
broker.cancel(ON_END_TAG);
|
|
48
64
|
}
|
|
49
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* @internal Shared with SubProcessExtensions.
|
|
67
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
68
|
+
*/
|
|
50
69
|
async _onEnter(elementApi) {
|
|
51
70
|
this.formatQ.queueMessage({ routingKey: 'run.enter.format' }, { endRoutingKey: 'run.enter.complete' }, { persistent: false });
|
|
52
71
|
try {
|
|
@@ -56,7 +75,10 @@ export class ElementExtensions {
|
|
|
56
75
|
elementApi.broker.publish('format', 'run.enter.error', { error: err }, { persistent: false });
|
|
57
76
|
}
|
|
58
77
|
}
|
|
59
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* @internal Shared with SubProcessExtensions.
|
|
80
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
81
|
+
*/
|
|
60
82
|
async _onExecuted(elementApi) {
|
|
61
83
|
this.formatQ.queueMessage({ routingKey: 'run.end.format' }, { endRoutingKey: 'run.end.complete' }, { persistent: false });
|
|
62
84
|
try {
|
|
@@ -66,7 +88,10 @@ export class ElementExtensions {
|
|
|
66
88
|
elementApi.broker.publish('format', 'run.end.error', { error: err }, { persistent: false });
|
|
67
89
|
}
|
|
68
90
|
}
|
|
69
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* @internal Shared with SubProcessExtensions.
|
|
93
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
94
|
+
*/
|
|
70
95
|
async _onListener(eventType, elementApi) {
|
|
71
96
|
const routingKey = `run.listener.${eventType}`;
|
|
72
97
|
this.formatQ.queueMessage({ routingKey }, { endRoutingKey: `${routingKey}.complete` }, { persistent: false });
|
|
@@ -78,13 +103,17 @@ export class ElementExtensions {
|
|
|
78
103
|
elementApi.broker.publish('format', `${routingKey}.error`, { error: err }, { persistent: false });
|
|
79
104
|
}
|
|
80
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
108
|
+
*/
|
|
81
109
|
#formatOnEnter(elementApi) {
|
|
82
|
-
const { format, io, headers, properties, form } = this.extensions;
|
|
110
|
+
const { format, io, headers, properties, form, subscription } = this.extensions;
|
|
83
111
|
const result = { ...format.resolve(elementApi) };
|
|
84
112
|
|
|
85
113
|
if (headers) result.headers = headers.resolve();
|
|
86
114
|
if (properties) result.properties = properties.resolve(elementApi);
|
|
87
115
|
if (form) result.form = form.resolve(elementApi);
|
|
116
|
+
if (subscription) result.subscription = subscription.resolve(elementApi);
|
|
88
117
|
if (io?.hasInput) {
|
|
89
118
|
result.input = io.getInput(elementApi);
|
|
90
119
|
// A (sub) process input mapping creates local variables visible to its children; a job
|
|
@@ -96,6 +125,9 @@ export class ElementExtensions {
|
|
|
96
125
|
Object.assign(elementApi.content, result);
|
|
97
126
|
return result;
|
|
98
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
130
|
+
*/
|
|
99
131
|
#formatOnExecuted(elementApi) {
|
|
100
132
|
const { io, script, calledDecision, loop } = this.extensions;
|
|
101
133
|
let jobResult = elementApi.content.output;
|
|
@@ -132,6 +164,10 @@ export class ElementExtensions {
|
|
|
132
164
|
}
|
|
133
165
|
return {};
|
|
134
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
169
|
+
* @param {any} output
|
|
170
|
+
*/
|
|
135
171
|
#assignOutput(elementApi, output) {
|
|
136
172
|
const environment = this.activity.environment;
|
|
137
173
|
// Visible to downstream FEEL within the process, and surfaced as process output
|
package/src/Expressions.js
CHANGED
|
@@ -7,7 +7,7 @@ import { isFeelExpression, evaluateFeel } from './feel.js';
|
|
|
7
7
|
* definition resolves FEEL expressions (`= ...`) instead of the default
|
|
8
8
|
* `${...}` template expressions.
|
|
9
9
|
*
|
|
10
|
-
* @returns {import('bpmn-elements').IExpressions}
|
|
10
|
+
* @returns {import('bpmn-elements').IExpressions & {isExpression: (text:string) => boolean, hasExpression: (text:string) => boolean}}
|
|
11
11
|
*/
|
|
12
12
|
export function FeelExpressions() {
|
|
13
13
|
return {
|
|
@@ -42,8 +42,5 @@ function resolveExpression(templatedString, context) {
|
|
|
42
42
|
* (e.g. a multi-instance item).
|
|
43
43
|
*/
|
|
44
44
|
function getScope(context) {
|
|
45
|
-
|
|
46
|
-
const variables = environment ? environment.variables : {};
|
|
47
|
-
const local = context && context.content && context.content.variables;
|
|
48
|
-
return { ...variables, ...local };
|
|
45
|
+
return { ...context?.environment?.variables, ...context?.content?.variables };
|
|
49
46
|
}
|
package/src/ProcessExtensions.js
CHANGED
|
@@ -6,8 +6,12 @@ import { getExtensions } from './getExtensions.js';
|
|
|
6
6
|
* the result to the process variables.
|
|
7
7
|
*/
|
|
8
8
|
export class ProcessExtensions {
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('bpmn-elements').Process} bp
|
|
11
|
+
*/
|
|
9
12
|
constructor(bp) {
|
|
10
13
|
this.process = bp;
|
|
14
|
+
/** @type {import('./getExtensions.js').ExtensionHandlers} */
|
|
11
15
|
this.extensions = getExtensions(bp);
|
|
12
16
|
}
|
|
13
17
|
activate() {
|
|
@@ -21,6 +25,9 @@ export class ProcessExtensions {
|
|
|
21
25
|
deactivate() {
|
|
22
26
|
this.process.broker.cancel('0dep-bpmn-extensions:on-enter');
|
|
23
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Process>} elementApi
|
|
30
|
+
*/
|
|
24
31
|
#onEnter(elementApi) {
|
|
25
32
|
try {
|
|
26
33
|
// bpmn-elements (>=18.0.4) forwards a call activity's formatted input — its io mapping plus,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ElementExtensions } from './ElementExtensions.js';
|
|
1
|
+
import { ElementExtensions, ON_ENTER_TAG, ON_EXECUTED_TAG, ON_START_TAG, ON_END_TAG } from './ElementExtensions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Sub-process extensions.
|
|
@@ -14,14 +14,14 @@ export class SubProcessExtensions extends ElementExtensions {
|
|
|
14
14
|
const listeners = this.extensions.listeners;
|
|
15
15
|
const enterKey = message.fields.redelivered && message.fields.routingKey === 'run.start' ? 'activity.start' : 'activity.enter';
|
|
16
16
|
|
|
17
|
-
this.#subscribe(enterKey, (api) => this._onEnter(api),
|
|
18
|
-
this.#subscribe('activity.execution.completed', (api) => this._onExecuted(api),
|
|
17
|
+
this.#subscribe(enterKey, (api) => this._onEnter(api), ON_ENTER_TAG);
|
|
18
|
+
this.#subscribe('activity.execution.completed', (api) => this._onExecuted(api), ON_EXECUTED_TAG);
|
|
19
19
|
|
|
20
20
|
if (listeners?.onStart) {
|
|
21
|
-
this.#subscribe('activity.start', (api) => this._onListener('start', api),
|
|
21
|
+
this.#subscribe('activity.start', (api) => this._onListener('start', api), ON_START_TAG);
|
|
22
22
|
}
|
|
23
23
|
if (listeners?.onEnd) {
|
|
24
|
-
this.#subscribe('activity.end', (api) => this._onListener('end', api),
|
|
24
|
+
this.#subscribe('activity.end', (api) => this._onListener('end', api), ON_END_TAG);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
#subscribe(routingKey, callback, consumerTag) {
|
|
@@ -16,10 +16,18 @@ export function FeelScripts() {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
class FeelScriptRegistry {
|
|
19
|
-
register() {
|
|
19
|
+
register() {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} _scriptFormat
|
|
24
|
+
* @param {{ id: string, [x: string]: any }} activity
|
|
25
|
+
* @returns {import('bpmn-elements').Script} the FEEL script, or undefined when the activity
|
|
26
|
+
* has no `zeebe:script` — bpmn-elements handles that, but `IScripts` declares a bare `Script`
|
|
27
|
+
*/
|
|
20
28
|
getScript(_scriptFormat, activity) {
|
|
21
29
|
const expression = getScriptExpression(activity);
|
|
22
|
-
if (expression === undefined) return undefined;
|
|
30
|
+
if (expression === undefined) return /** @type {any} */ (undefined);
|
|
23
31
|
return new FeelScript(expression);
|
|
24
32
|
}
|
|
25
33
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { resolveValue } from '../feel.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `zeebe:subscription` — rides on the referenced `bpmn:Message`, not on the catching element.
|
|
5
|
+
*
|
|
6
|
+
* Resolves the correlation key in the activity's variable scope on enter and exposes it on the
|
|
7
|
+
* element content as `subscription`. Correlation itself is the embedding application's job:
|
|
8
|
+
* read the resolved key off the waiting activity (`getPostponed()` → `content.subscription`)
|
|
9
|
+
* and signal the matching one (`api.signal(...)`).
|
|
10
|
+
*/
|
|
11
|
+
export class Subscription {
|
|
12
|
+
/**
|
|
13
|
+
* @param {{ id: string, name?: string }} messageRef The serialized message reference
|
|
14
|
+
* @param {{ correlationKey: string }} subscription The `zeebe:Subscription` behaviour
|
|
15
|
+
*/
|
|
16
|
+
constructor(messageRef, subscription) {
|
|
17
|
+
this.message = { id: messageRef.id, name: messageRef.name };
|
|
18
|
+
this.correlationKey = subscription.correlationKey;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @param {import('bpmn-elements').IApi<any>} elementApi
|
|
22
|
+
* @returns {{ message: { id: string, name?: string }, correlationKey: any }}
|
|
23
|
+
*/
|
|
24
|
+
resolve(elementApi) {
|
|
25
|
+
return {
|
|
26
|
+
message: this.message,
|
|
27
|
+
correlationKey: resolveValue(this.correlationKey, elementApi.environment.variables),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -22,6 +22,12 @@ export function JobService(taskType, activity) {
|
|
|
22
22
|
this.activity = activity;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Execute
|
|
27
|
+
* @param {import('bpmn-elements').ElementBrokerMessage} executionMessage
|
|
28
|
+
* @param {CallableFunction} callback
|
|
29
|
+
* @returns {any}
|
|
30
|
+
*/
|
|
25
31
|
JobService.prototype.execute = function execute(executionMessage, callback) {
|
|
26
32
|
const activity = this.activity;
|
|
27
33
|
const environment = activity.environment;
|
|
@@ -5,10 +5,17 @@ import { resolveValue } from '../feel.js';
|
|
|
5
5
|
* for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups).
|
|
6
6
|
*/
|
|
7
7
|
export class FormatActivity {
|
|
8
|
+
/**
|
|
9
|
+
* @param {import('bpmn-elements').Activity} activity
|
|
10
|
+
* @param {any} assignmentDefinition
|
|
11
|
+
*/
|
|
8
12
|
constructor(activity, assignmentDefinition) {
|
|
9
13
|
this.activity = activity;
|
|
10
14
|
this.assignmentDefinition = assignmentDefinition;
|
|
11
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
18
|
+
*/
|
|
12
19
|
resolve(elementApi) {
|
|
13
20
|
const scope = elementApi.environment.variables;
|
|
14
21
|
const result = {};
|
|
@@ -37,9 +44,15 @@ export class FormatActivity {
|
|
|
37
44
|
* Format a process on enter: documentation.
|
|
38
45
|
*/
|
|
39
46
|
export class FormatProcess {
|
|
47
|
+
/**
|
|
48
|
+
* @param {import('bpmn-elements').Process} bp
|
|
49
|
+
*/
|
|
40
50
|
constructor(bp) {
|
|
41
51
|
this.process = bp;
|
|
42
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Process>} elementApi
|
|
55
|
+
*/
|
|
43
56
|
resolve(elementApi) {
|
|
44
57
|
const result = {};
|
|
45
58
|
const documentation = this.process.behaviour.documentation;
|
|
@@ -58,7 +71,7 @@ export class FormatProcess {
|
|
|
58
71
|
function resolveList(value, scope) {
|
|
59
72
|
if (!value) return undefined;
|
|
60
73
|
const resolved = resolveValue(value, scope);
|
|
61
|
-
if (Array.isArray(resolved)) return resolved.filter(
|
|
74
|
+
if (Array.isArray(resolved)) return resolved.filter(Boolean);
|
|
62
75
|
if (typeof resolved !== 'string') return undefined;
|
|
63
76
|
return resolved
|
|
64
77
|
.split(',')
|
package/src/feel.js
CHANGED
|
@@ -40,11 +40,11 @@ export function evaluateFeel(expression, context) {
|
|
|
40
40
|
* @param {string} expression FEEL unary test
|
|
41
41
|
* @param {any} input The value being tested, available as `?` in the test
|
|
42
42
|
* @param {Record<string, any>} [context] Additional variables in scope
|
|
43
|
-
* @returns {boolean}
|
|
43
|
+
* @returns {boolean} true only when the test is satisfied — an undecidable (null) test is false
|
|
44
44
|
*/
|
|
45
45
|
export function evaluateFeelUnaryTest(expression, input, context) {
|
|
46
|
-
const { value } = unaryTest(stripFeel(expression), { ...
|
|
47
|
-
return value;
|
|
46
|
+
const { value } = unaryTest(stripFeel(expression), { ...context, '?': input });
|
|
47
|
+
return value === true;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
package/src/getExtensions.js
CHANGED
|
@@ -6,24 +6,31 @@ import { ExecutionListeners } from './extensions/ExecutionListeners.js';
|
|
|
6
6
|
import { Form } from './extensions/Form.js';
|
|
7
7
|
import { LoopCharacteristics } from './extensions/LoopCharacteristics.js';
|
|
8
8
|
import { JobService } from './extensions/TaskDefinition.js';
|
|
9
|
+
import { Subscription } from './extensions/Subscription.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The extension handlers assembled for one element.
|
|
13
|
+
* @typedef {object} ExtensionHandlers
|
|
14
|
+
* @property {FormatActivity | FormatProcess} format
|
|
15
|
+
* @property {IoMapping} [io]
|
|
16
|
+
* @property {TaskHeaders} [headers]
|
|
17
|
+
* @property {Properties} [properties]
|
|
18
|
+
* @property {ExecutionListeners} [listeners]
|
|
19
|
+
* @property {Form} [form]
|
|
20
|
+
* @property {LoopCharacteristics} [loop]
|
|
21
|
+
* @property {any} [script]
|
|
22
|
+
* @property {any} [calledDecision]
|
|
23
|
+
* @property {Function} [Service]
|
|
24
|
+
* @property {Subscription} [subscription]
|
|
25
|
+
*/
|
|
9
26
|
|
|
10
27
|
/**
|
|
11
28
|
* Inspect an element's extension elements and assemble the extension handlers it needs.
|
|
12
29
|
* @param {import('bpmn-elements').Activity | import('bpmn-elements').Process} element
|
|
13
|
-
* @
|
|
14
|
-
*
|
|
15
|
-
* io?: IoMapping,
|
|
16
|
-
* headers?: TaskHeaders,
|
|
17
|
-
* properties?: Properties,
|
|
18
|
-
* listeners?: ExecutionListeners,
|
|
19
|
-
* form?: Form,
|
|
20
|
-
* loop?: LoopCharacteristics,
|
|
21
|
-
* script?: any,
|
|
22
|
-
* calledDecision?: any,
|
|
23
|
-
* Service?: Function,
|
|
24
|
-
* }}
|
|
30
|
+
* @param {import('bpmn-elements').ContextInstance} [context]
|
|
31
|
+
* @returns {ExtensionHandlers}
|
|
25
32
|
*/
|
|
26
|
-
export function getExtensions(element) {
|
|
33
|
+
export function getExtensions(element, context) {
|
|
27
34
|
const result = {};
|
|
28
35
|
const isProcess = element.type === 'bpmn:Process';
|
|
29
36
|
|
|
@@ -67,6 +74,18 @@ export function getExtensions(element) {
|
|
|
67
74
|
}
|
|
68
75
|
}
|
|
69
76
|
|
|
77
|
+
// A message catch (receive task / message event) may reference a bpmn:Message carrying a
|
|
78
|
+
// zeebe:subscription with the correlation key. The subscription lives on the message element,
|
|
79
|
+
// which the instantiated bpmn-elements Message strips — read it off the serialized context.
|
|
80
|
+
const messageRef =
|
|
81
|
+
element.behaviour.messageRef ?? element.behaviour.eventDefinitions?.find((ed) => ed.behaviour?.messageRef)?.behaviour.messageRef;
|
|
82
|
+
if (messageRef?.id && context) {
|
|
83
|
+
const subscription = context.definitionContext
|
|
84
|
+
.getActivityById(messageRef.id)
|
|
85
|
+
?.behaviour.extensionElements?.values?.find((v) => v.$type === 'zeebe:Subscription');
|
|
86
|
+
if (subscription?.correlationKey) result.subscription = new Subscription(messageRef, subscription);
|
|
87
|
+
}
|
|
88
|
+
|
|
70
89
|
// Output side of a multi-instance `zeebe:loopCharacteristics` (the input side is lifted by extendFn).
|
|
71
90
|
const loopExtension = element.behaviour.loopCharacteristics?.behaviour?.extensionElements?.values?.find(
|
|
72
91
|
(v) => v.$type === 'zeebe:LoopCharacteristics'
|
package/src/index.js
CHANGED
|
@@ -12,8 +12,8 @@ export { ServiceError, FormatError } from './Errors.js';
|
|
|
12
12
|
/**
|
|
13
13
|
* A flow extension activated by bpmn-elements around an element's run.
|
|
14
14
|
* @typedef {object} FlowExtension
|
|
15
|
-
* @property {(message:
|
|
16
|
-
* @property {(message?:
|
|
15
|
+
* @property {(message: import('bpmn-elements').ElementBrokerMessage) => void} activate
|
|
16
|
+
* @property {(message?: import('bpmn-elements').ElementBrokerMessage) => void} deactivate
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
/**
|
package/types/index.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
18
18
|
* A flow extension activated by bpmn-elements around an element's run.
|
|
19
19
|
*/
|
|
20
20
|
export type FlowExtension = {
|
|
21
|
-
activate: (message:
|
|
22
|
-
deactivate: (message?:
|
|
21
|
+
activate: (message: import("bpmn-elements").ElementBrokerMessage) => void;
|
|
22
|
+
deactivate: (message?: import("bpmn-elements").ElementBrokerMessage) => void;
|
|
23
23
|
};
|
|
24
24
|
/**
|
|
25
25
|
* FEEL-aware expressions implementation for bpmn-elements.
|
|
@@ -29,7 +29,10 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
29
29
|
* `${...}` template expressions.
|
|
30
30
|
*
|
|
31
31
|
* */
|
|
32
|
-
export function FeelExpressions(): import("bpmn-elements").IExpressions
|
|
32
|
+
export function FeelExpressions(): import("bpmn-elements").IExpressions & {
|
|
33
|
+
isExpression: (text: string) => boolean;
|
|
34
|
+
hasExpression: (text: string) => boolean;
|
|
35
|
+
};
|
|
33
36
|
/**
|
|
34
37
|
* A bpmn-elements `scripts` implementation for script tasks.
|
|
35
38
|
*
|
|
@@ -75,7 +78,10 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
75
78
|
type: string | undefined;
|
|
76
79
|
taskType: any;
|
|
77
80
|
activity: any;
|
|
78
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Execute
|
|
83
|
+
* */
|
|
84
|
+
execute(executionMessage: import("bpmn-elements").ElementBrokerMessage, callback: CallableFunction): any;
|
|
79
85
|
}
|
|
80
86
|
/**
|
|
81
87
|
* Activity-level extensions.
|
|
@@ -85,29 +91,29 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
85
91
|
* proceeding — the same mechanism bpmn-elements uses for extension formatting.
|
|
86
92
|
*/
|
|
87
93
|
export class ElementExtensions {
|
|
88
|
-
|
|
89
|
-
activity:
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
loop?: LoopCharacteristics;
|
|
99
|
-
script?: any;
|
|
100
|
-
calledDecision?: any;
|
|
101
|
-
Service?: Function;
|
|
102
|
-
};
|
|
103
|
-
activate(message: any): void;
|
|
94
|
+
|
|
95
|
+
constructor(activity: import("bpmn-elements").Activity, context: import("bpmn-elements").ContextInstance);
|
|
96
|
+
activity: import("bpmn-elements").Activity;
|
|
97
|
+
formatQ: import("smqp").Queue | undefined;
|
|
98
|
+
|
|
99
|
+
extensions: ExtensionHandlers;
|
|
100
|
+
/**
|
|
101
|
+
* Activate extensions
|
|
102
|
+
* */
|
|
103
|
+
activate(message: import("bpmn-elements").ElementBrokerMessage): void;
|
|
104
104
|
deactivate(): void;
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
|
|
105
|
+
/**
|
|
106
|
+
* @internal Shared with SubProcessExtensions.
|
|
107
|
+
* */
|
|
108
|
+
_onEnter(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Activity>): Promise<void>;
|
|
109
|
+
/**
|
|
110
|
+
* @internal Shared with SubProcessExtensions.
|
|
111
|
+
* */
|
|
112
|
+
_onExecuted(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Activity>): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* @internal Shared with SubProcessExtensions.
|
|
115
|
+
* */
|
|
116
|
+
_onListener(eventType: any, elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Activity>): Promise<void>;
|
|
111
117
|
#private;
|
|
112
118
|
}
|
|
113
119
|
/**
|
|
@@ -115,20 +121,11 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
115
121
|
* the result to the process variables.
|
|
116
122
|
*/
|
|
117
123
|
export class ProcessExtensions {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
headers?: TaskHeaders;
|
|
124
|
-
properties?: Properties;
|
|
125
|
-
listeners?: ExecutionListeners;
|
|
126
|
-
form?: Form;
|
|
127
|
-
loop?: LoopCharacteristics;
|
|
128
|
-
script?: any;
|
|
129
|
-
calledDecision?: any;
|
|
130
|
-
Service?: Function;
|
|
131
|
-
};
|
|
124
|
+
|
|
125
|
+
constructor(bp: import("bpmn-elements").Process);
|
|
126
|
+
process: import("bpmn-elements").Process;
|
|
127
|
+
|
|
128
|
+
extensions: ExtensionHandlers;
|
|
132
129
|
activate(): void;
|
|
133
130
|
deactivate(): void;
|
|
134
131
|
#private;
|
|
@@ -143,6 +140,7 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
143
140
|
* sub-process's own id.
|
|
144
141
|
*/
|
|
145
142
|
export class SubProcessExtensions extends ElementExtensions {
|
|
143
|
+
activate(message: any): void;
|
|
146
144
|
#private;
|
|
147
145
|
}
|
|
148
146
|
/**
|
|
@@ -164,7 +162,8 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
164
162
|
* @param expression FEEL unary test
|
|
165
163
|
* @param input The value being tested, available as `?` in the test
|
|
166
164
|
* @param context Additional variables in scope
|
|
167
|
-
*
|
|
165
|
+
* @returns true only when the test is satisfied — an undecidable (null) test is false
|
|
166
|
+
*/
|
|
168
167
|
export function evaluateFeelUnaryTest(expression: string, input: any, context?: Record<string, any>): boolean;
|
|
169
168
|
/**
|
|
170
169
|
* Resolve a value: evaluate it as FEEL when it starts with `=`, otherwise
|
|
@@ -186,15 +185,33 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
186
185
|
statusCode: number;
|
|
187
186
|
};
|
|
188
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* The extension handlers assembled for one element.
|
|
190
|
+
*/
|
|
191
|
+
type ExtensionHandlers = {
|
|
192
|
+
format: FormatActivity | FormatProcess;
|
|
193
|
+
io?: IoMapping | undefined;
|
|
194
|
+
headers?: TaskHeaders | undefined;
|
|
195
|
+
properties?: Properties | undefined;
|
|
196
|
+
listeners?: ExecutionListeners | undefined;
|
|
197
|
+
form?: Form | undefined;
|
|
198
|
+
loop?: LoopCharacteristics | undefined;
|
|
199
|
+
script?: any;
|
|
200
|
+
calledDecision?: any;
|
|
201
|
+
Service?: Function | undefined;
|
|
202
|
+
subscription?: Subscription | undefined;
|
|
203
|
+
};
|
|
189
204
|
/**
|
|
190
205
|
* Format an activity on enter from its behaviour and extensions: documentation and,
|
|
191
206
|
* for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups).
|
|
192
207
|
*/
|
|
193
208
|
class FormatActivity {
|
|
194
|
-
|
|
195
|
-
activity: any;
|
|
209
|
+
|
|
210
|
+
constructor(activity: import("bpmn-elements").Activity, assignmentDefinition: any);
|
|
211
|
+
activity: import("bpmn-elements").Activity;
|
|
196
212
|
assignmentDefinition: any;
|
|
197
|
-
|
|
213
|
+
|
|
214
|
+
resolve(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Activity>): {
|
|
198
215
|
description: any;
|
|
199
216
|
assignee: any;
|
|
200
217
|
candidateUsers: any[];
|
|
@@ -205,9 +222,11 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
205
222
|
* Format a process on enter: documentation.
|
|
206
223
|
*/
|
|
207
224
|
class FormatProcess {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
225
|
+
|
|
226
|
+
constructor(bp: import("bpmn-elements").Process);
|
|
227
|
+
process: import("bpmn-elements").Process;
|
|
228
|
+
|
|
229
|
+
resolve(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Process>): {
|
|
211
230
|
description: any;
|
|
212
231
|
};
|
|
213
232
|
}
|
|
@@ -323,6 +342,39 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
323
342
|
* */
|
|
324
343
|
aggregate(indexedOutput: Record<string, any> | undefined, baseScope: Record<string, any>): any[];
|
|
325
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* `zeebe:subscription` — rides on the referenced `bpmn:Message`, not on the catching element.
|
|
347
|
+
*
|
|
348
|
+
* Resolves the correlation key in the activity's variable scope on enter and exposes it on the
|
|
349
|
+
* element content as `subscription`. Correlation itself is the embedding application's job:
|
|
350
|
+
* read the resolved key off the waiting activity (`getPostponed()` → `content.subscription`)
|
|
351
|
+
* and signal the matching one (`api.signal(...)`).
|
|
352
|
+
*/
|
|
353
|
+
class Subscription {
|
|
354
|
+
/**
|
|
355
|
+
* @param messageRef The serialized message reference
|
|
356
|
+
* @param subscription The `zeebe:Subscription` behaviour
|
|
357
|
+
*/
|
|
358
|
+
constructor(messageRef: {
|
|
359
|
+
id: string;
|
|
360
|
+
name?: string;
|
|
361
|
+
}, subscription: {
|
|
362
|
+
correlationKey: string;
|
|
363
|
+
});
|
|
364
|
+
message: {
|
|
365
|
+
id: string;
|
|
366
|
+
name: string | undefined;
|
|
367
|
+
};
|
|
368
|
+
correlationKey: string;
|
|
369
|
+
|
|
370
|
+
resolve(elementApi: import("bpmn-elements").IApi<any>): {
|
|
371
|
+
message: {
|
|
372
|
+
id: string;
|
|
373
|
+
name?: string;
|
|
374
|
+
};
|
|
375
|
+
correlationKey: any;
|
|
376
|
+
};
|
|
377
|
+
}
|
|
326
378
|
|
|
327
379
|
export {};
|
|
328
380
|
}
|
package/types/index.d.ts.map
CHANGED
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"Properties",
|
|
25
25
|
"ExecutionListeners",
|
|
26
26
|
"Form",
|
|
27
|
-
"LoopCharacteristics"
|
|
27
|
+
"LoopCharacteristics",
|
|
28
|
+
"Subscription"
|
|
28
29
|
],
|
|
29
30
|
"sources": [
|
|
30
31
|
"../src/index.js",
|
|
@@ -42,7 +43,8 @@
|
|
|
42
43
|
"../src/extensions/Properties.js",
|
|
43
44
|
"../src/extensions/ExecutionListeners.js",
|
|
44
45
|
"../src/extensions/Form.js",
|
|
45
|
-
"../src/extensions/LoopCharacteristics.js"
|
|
46
|
+
"../src/extensions/LoopCharacteristics.js",
|
|
47
|
+
"../src/extensions/Subscription.js"
|
|
46
48
|
],
|
|
47
49
|
"sourcesContent": [
|
|
48
50
|
null,
|
|
@@ -60,8 +62,9 @@
|
|
|
60
62
|
null,
|
|
61
63
|
null,
|
|
62
64
|
null,
|
|
65
|
+
null,
|
|
63
66
|
null
|
|
64
67
|
],
|
|
65
|
-
"mappings": ";;;;;;;iBAwBgBA,UAAUA;;;;;;;;iBAqBVC,QAAQA;;;;;;;;;;;;;;;;iBClCRC,eAAeA
|
|
68
|
+
"mappings": ";;;;;;;iBAwBgBA,UAAUA;;;;;;;;iBAqBVC,QAAQA;;;;;;;;;;;;;;;;iBClCRC,eAAeA;;;;;;;;;;;;;;iBCEfC,WAAWA;;;;;;;;;;;;;;;iBCIXC,UAAUA;cAAVA,UAAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCCbC,iBAAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCXjBC,iBAAiBA;;;;;;;;;;;;;;;;;;;cCIjBC,oBAAoBA;;;;;;;iBCEjBC,gBAAgBA;;;;iBAShBC,SAASA;;;;;;iBAUTC,YAAYA;;;;;;;;iBAYZC,qBAAqBA;;;;;;iBAYrBC,YAAYA;cCxDfC,YAAYA;;;;;;;cAQZC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;OCFXC,cAAcA;;;;;;;;;;;;;;;;OAuCdC,aAAaA;;;;;;;;;;;;;;;;;;;;OChCbC,SAASA;;;;;;;;;;;;;;;;;;;;;;;OCPTC,WAAWA;;;;;;;;;;;OCEXC,UAAUA;;;;;;;;;;;;;;;OCIVC,kBAAkBA;;;;;;;;;;;;;;;;;;;OCFlBC,IAAIA;;;;;;;;;;;;;;;;;;;;;;OCGJC,mBAAmBA;;;;;;;;;;;;;;;;;;;OCHnBC,YAAYA",
|
|
66
69
|
"ignoreList": []
|
|
67
70
|
}
|