@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 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
- See the `camunda8-bpmn-schemas` skill (`.claude/skills/`) for a reference of the `zeebe:*`
132
- extension elements and FEEL.
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), { ...(context || {}), '?': input });
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((v) => v !== null && v !== undefined);
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
- * @returns {{
16582
- * format: FormatActivity | FormatProcess,
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
- constructor(activity) {
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
- this.extensions = getExtensions(activity);
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: '0dep-bpmn-extensions:on-enter' });
16754
+ activity.on('start', (api) => this._onEnter(api), { consumerTag: ON_ENTER_TAG });
16673
16755
  } else {
16674
- activity.on('enter', (api) => this._onEnter(api), { consumerTag: '0dep-bpmn-extensions:on-enter' });
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: '0dep-bpmn-extensions:on-executed',
16760
+ consumerTag: ON_EXECUTED_TAG,
16679
16761
  });
16680
16762
 
16681
16763
  if (listeners?.onStart) {
16682
- activity.on('start', (api) => this._onListener('start', api), { consumerTag: '0dep-bpmn-extensions:on-start' });
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: '0dep-bpmn-extensions:on-end' });
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('0dep-bpmn-extensions:on-enter');
16691
- broker.cancel('0dep-bpmn-extensions:on-executed');
16692
- broker.cancel('0dep-bpmn-extensions:on-start');
16693
- broker.cancel('0dep-bpmn-extensions:on-end');
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
- /** @internal Shared with SubProcessExtensions. */
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
- /** @internal Shared with SubProcessExtensions. */
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
- /** @internal Shared with SubProcessExtensions. */
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), '0dep-bpmn-extensions:on-enter');
16853
- this.#subscribe('activity.execution.completed', (api) => this._onExecuted(api), '0dep-bpmn-extensions:on-executed');
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), '0dep-bpmn-extensions:on-start');
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), '0dep-bpmn-extensions:on-end');
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
- const environment = context && context.environment;
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: any) => void} activate
16976
- * @property {(message?: any) => void} deactivate
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.2",
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.4"
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
- "bpmn-elements": "^18.0.4",
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": "^11.0.0",
73
+ "c8": "^12.0.0",
66
74
  "chai": "^6.2.2",
67
75
  "debug": "^4.4.3",
68
76
  "dts-buddy": "^0.8.2",
@@ -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
- constructor(activity) {
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
- this.extensions = getExtensions(activity);
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: '0dep-bpmn-extensions:on-enter' });
42
+ activity.on('start', (api) => this._onEnter(api), { consumerTag: ON_ENTER_TAG });
27
43
  } else {
28
- activity.on('enter', (api) => this._onEnter(api), { consumerTag: '0dep-bpmn-extensions:on-enter' });
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: '0dep-bpmn-extensions:on-executed',
48
+ consumerTag: ON_EXECUTED_TAG,
33
49
  });
34
50
 
35
51
  if (listeners?.onStart) {
36
- activity.on('start', (api) => this._onListener('start', api), { consumerTag: '0dep-bpmn-extensions:on-start' });
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: '0dep-bpmn-extensions:on-end' });
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('0dep-bpmn-extensions:on-enter');
45
- broker.cancel('0dep-bpmn-extensions:on-executed');
46
- broker.cancel('0dep-bpmn-extensions:on-start');
47
- broker.cancel('0dep-bpmn-extensions:on-end');
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
- /** @internal Shared with SubProcessExtensions. */
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
- /** @internal Shared with SubProcessExtensions. */
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
- /** @internal Shared with SubProcessExtensions. */
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
@@ -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
- const environment = context && context.environment;
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
  }
@@ -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), '0dep-bpmn-extensions:on-enter');
18
- this.#subscribe('activity.execution.completed', (api) => this._onExecuted(api), '0dep-bpmn-extensions:on-executed');
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), '0dep-bpmn-extensions:on-start');
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), '0dep-bpmn-extensions:on-end');
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((v) => v !== null && v !== undefined);
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), { ...(context || {}), '?': input });
47
- return value;
46
+ const { value } = unaryTest(stripFeel(expression), { ...context, '?': input });
47
+ return value === true;
48
48
  }
49
49
 
50
50
  /**
@@ -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
- * @returns {{
14
- * format: FormatActivity | FormatProcess,
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: any) => void} activate
16
- * @property {(message?: any) => void} deactivate
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: any) => void;
22
- deactivate: (message?: any) => void;
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
- execute(executionMessage: any, callback: any): any;
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
- constructor(activity: any);
89
- activity: any;
90
- formatQ: any;
91
- extensions: {
92
- format: FormatActivity | FormatProcess;
93
- io?: IoMapping;
94
- headers?: TaskHeaders;
95
- properties?: Properties;
96
- listeners?: ExecutionListeners;
97
- form?: Form;
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
- /** @internal Shared with SubProcessExtensions. */
106
- _onEnter(elementApi: any): Promise<void>;
107
- /** @internal Shared with SubProcessExtensions. */
108
- _onExecuted(elementApi: any): Promise<void>;
109
- /** @internal Shared with SubProcessExtensions. */
110
- _onListener(eventType: any, elementApi: any): Promise<void>;
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
- constructor(bp: any);
119
- process: any;
120
- extensions: {
121
- format: FormatActivity | FormatProcess;
122
- io?: IoMapping;
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
- constructor(activity: any, assignmentDefinition: any);
195
- activity: any;
209
+
210
+ constructor(activity: import("bpmn-elements").Activity, assignmentDefinition: any);
211
+ activity: import("bpmn-elements").Activity;
196
212
  assignmentDefinition: any;
197
- resolve(elementApi: any): {
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
- constructor(bp: any);
209
- process: any;
210
- resolve(elementApi: any): {
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
  }
@@ -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;;;;;;;;;;;iBCEfC,WAAWA;;;;;;;;;;;;;;;iBCIXC,UAAUA;cAAVA,UAAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCNbC,iBAAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCJjBC,iBAAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCIjBC,oBAAoBA;;;;;;iBCEjBC,gBAAgBA;;;;iBAShBC,SAASA;;;;;;iBAUTC,YAAYA;;;;;;;iBAYZC,qBAAqBA;;;;;;iBAYrBC,YAAYA;cCxDfC,YAAYA;;;;;;;cAQZC,WAAWA;;;;;;;;;;;OCFXC,cAAcA;;;;;;;;;;;;;;OAgCdC,aAAaA;;;;;;;;;;;;;;;;;;OCzBbC,SAASA;;;;;;;;;;;;;;;;;;;;;;;OCPTC,WAAWA;;;;;;;;;;;OCEXC,UAAUA;;;;;;;;;;;;;;;OCIVC,kBAAkBA;;;;;;;;;;;;;;;;;;;OCFlBC,IAAIA;;;;;;;;;;;;;;;;;;;;;;OCGJC,mBAAmBA",
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
  }