@0dep/bpmn-extensions 0.0.1 → 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
@@ -24,6 +24,8 @@ npm install @0dep/bpmn-extensions bpmn-elements
24
24
  - **`zeebe:script`** — a `FeelScripts()` adapter runs a script task's FEEL `expression` and assigns
25
25
  the result to its `resultVariable`.
26
26
  - **`zeebe:taskDefinition`** — maps a service task's job `type` to an environment service (job worker).
27
+ - **`zeebe:calledDecision`** — a business rule task resolves its `decisionId` via an environment
28
+ service (you bring the decision; we don't evaluate DMN) and assigns the result to `resultVariable`.
27
29
  - **`zeebe:ioMapping`** — input mapping on entry (parent scope) and output mapping on completion
28
30
  (job-result scope), with dotted `target` paths. Propagates across boundaries: a call activity's
29
31
  input reaches the called process and its output maps the called process result back; a sub
@@ -38,6 +40,10 @@ npm install @0dep/bpmn-extensions bpmn-elements
38
40
  - **`zeebe:calledElement`** — call activity target process id.
39
41
  - **`zeebe:loopCharacteristics`** — collection-based multi-instance (`inputCollection` / `inputElement`),
40
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.
41
47
 
42
48
  ## API
43
49
 
@@ -126,8 +132,12 @@ in `test/helpers/factory.js`, then run on a real bpmn-elements `Definition`. The
126
132
  the engine's `Logger` to the [`debug`](https://www.npmjs.com/package/debug) package, so
127
133
  `DEBUG=bpmn-extensions:* npm test` traces the engine and extensions (`:error:*` for errors only).
128
134
 
129
- See the `camunda8-bpmn-schemas` skill (`.claude/skills/`) for a reference of the `zeebe:*`
130
- 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)).
131
141
 
132
142
  ## License
133
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,21 +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
- * Service?: Function,
16590
- * }}
16645
+ * @param {import('bpmn-elements').ContextInstance} [context]
16646
+ * @returns {ExtensionHandlers}
16591
16647
  */
16592
- function getExtensions(element) {
16648
+ function getExtensions(element, context) {
16593
16649
  const result = {};
16594
16650
  const isProcess = element.type === 'bpmn:Process';
16595
16651
 
@@ -16619,6 +16675,13 @@ function getExtensions(element) {
16619
16675
  case 'zeebe:Script':
16620
16676
  result.script = ext;
16621
16677
  break;
16678
+ case 'zeebe:CalledDecision':
16679
+ // A business rule task runs as a service task (bpmn-elements maps it to ServiceTask). The
16680
+ // decision is resolved by an environment service named by the decision id; its result is
16681
+ // named by `resultVariable`.
16682
+ result.calledDecision = ext;
16683
+ result.Service = JobService.bind(JobService, ext.decisionId);
16684
+ break;
16622
16685
  case 'zeebe:FormDefinition':
16623
16686
  result.form = new Form(ext);
16624
16687
  break;
@@ -16626,6 +16689,18 @@ function getExtensions(element) {
16626
16689
  }
16627
16690
  }
16628
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
+
16629
16704
  // Output side of a multi-instance `zeebe:loopCharacteristics` (the input side is lifted by extendFn).
16630
16705
  const loopExtension = element.behaviour.loopCharacteristics?.behaviour?.extensionElements?.values?.find(
16631
16706
  (v) => v.$type === 'zeebe:LoopCharacteristics'
@@ -16639,6 +16714,13 @@ function getExtensions(element) {
16639
16714
 
16640
16715
  const SUBPROCESS_TYPES = new Set(['bpmn:SubProcess', 'bpmn:AdHocSubProcess', 'bpmn:Transaction']);
16641
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
+
16642
16724
  /**
16643
16725
  * Activity-level extensions.
16644
16726
  *
@@ -16647,43 +16729,55 @@ const SUBPROCESS_TYPES = new Set(['bpmn:SubProcess', 'bpmn:AdHocSubProcess', 'bp
16647
16729
  * proceeding — the same mechanism bpmn-elements uses for extension formatting.
16648
16730
  */
16649
16731
  class ElementExtensions {
16650
- constructor(activity) {
16732
+ /**
16733
+ * @param {import('bpmn-elements').Activity} activity
16734
+ * @param {import('bpmn-elements').ContextInstance} context
16735
+ */
16736
+ constructor(activity, context) {
16651
16737
  this.activity = activity;
16652
16738
  this.formatQ = activity.broker.getQueue('format-run-q');
16653
16739
 
16654
- this.extensions = getExtensions(activity);
16740
+ /** @type {import('./getExtensions.js').ExtensionHandlers} */
16741
+ this.extensions = getExtensions(activity, context);
16655
16742
  const Service = this.extensions.Service;
16656
16743
  if (Service) activity.behaviour.Service = Service;
16657
16744
  }
16745
+ /**
16746
+ * Activate extensions
16747
+ * @param {import('bpmn-elements').ElementBrokerMessage} message
16748
+ */
16658
16749
  activate(message) {
16659
16750
  const activity = this.activity;
16660
16751
  const listeners = this.extensions.listeners;
16661
16752
 
16662
16753
  if (message.fields.redelivered && message.fields.routingKey === 'run.start') {
16663
- 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 });
16664
16755
  } else {
16665
- 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 });
16666
16757
  }
16667
16758
 
16668
16759
  activity.on('activity.execution.completed', (api) => this._onExecuted(api), {
16669
- consumerTag: '0dep-bpmn-extensions:on-executed',
16760
+ consumerTag: ON_EXECUTED_TAG,
16670
16761
  });
16671
16762
 
16672
16763
  if (listeners?.onStart) {
16673
- 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 });
16674
16765
  }
16675
16766
  if (listeners?.onEnd) {
16676
- 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 });
16677
16768
  }
16678
16769
  }
16679
16770
  deactivate() {
16680
16771
  const broker = this.activity.broker;
16681
- broker.cancel('0dep-bpmn-extensions:on-enter');
16682
- broker.cancel('0dep-bpmn-extensions:on-executed');
16683
- broker.cancel('0dep-bpmn-extensions:on-start');
16684
- 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);
16685
16776
  }
16686
- /** @internal Shared with SubProcessExtensions. */
16777
+ /**
16778
+ * @internal Shared with SubProcessExtensions.
16779
+ * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
16780
+ */
16687
16781
  async _onEnter(elementApi) {
16688
16782
  this.formatQ.queueMessage({ routingKey: 'run.enter.format' }, { endRoutingKey: 'run.enter.complete' }, { persistent: false });
16689
16783
  try {
@@ -16693,7 +16787,10 @@ class ElementExtensions {
16693
16787
  elementApi.broker.publish('format', 'run.enter.error', { error: err }, { persistent: false });
16694
16788
  }
16695
16789
  }
16696
- /** @internal Shared with SubProcessExtensions. */
16790
+ /**
16791
+ * @internal Shared with SubProcessExtensions.
16792
+ * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
16793
+ */
16697
16794
  async _onExecuted(elementApi) {
16698
16795
  this.formatQ.queueMessage({ routingKey: 'run.end.format' }, { endRoutingKey: 'run.end.complete' }, { persistent: false });
16699
16796
  try {
@@ -16703,7 +16800,10 @@ class ElementExtensions {
16703
16800
  elementApi.broker.publish('format', 'run.end.error', { error: err }, { persistent: false });
16704
16801
  }
16705
16802
  }
16706
- /** @internal Shared with SubProcessExtensions. */
16803
+ /**
16804
+ * @internal Shared with SubProcessExtensions.
16805
+ * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
16806
+ */
16707
16807
  async _onListener(eventType, elementApi) {
16708
16808
  const routingKey = `run.listener.${eventType}`;
16709
16809
  this.formatQ.queueMessage({ routingKey }, { endRoutingKey: `${routingKey}.complete` }, { persistent: false });
@@ -16715,13 +16815,17 @@ class ElementExtensions {
16715
16815
  elementApi.broker.publish('format', `${routingKey}.error`, { error: err }, { persistent: false });
16716
16816
  }
16717
16817
  }
16818
+ /**
16819
+ * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
16820
+ */
16718
16821
  #formatOnEnter(elementApi) {
16719
- const { format, io, headers, properties, form } = this.extensions;
16822
+ const { format, io, headers, properties, form, subscription } = this.extensions;
16720
16823
  const result = { ...format.resolve(elementApi) };
16721
16824
 
16722
16825
  if (headers) result.headers = headers.resolve();
16723
16826
  if (properties) result.properties = properties.resolve(elementApi);
16724
16827
  if (form) result.form = form.resolve(elementApi);
16828
+ if (subscription) result.subscription = subscription.resolve(elementApi);
16725
16829
  if (io?.hasInput) {
16726
16830
  result.input = io.getInput(elementApi);
16727
16831
  // A (sub) process input mapping creates local variables visible to its children; a job
@@ -16733,8 +16837,11 @@ class ElementExtensions {
16733
16837
  Object.assign(elementApi.content, result);
16734
16838
  return result;
16735
16839
  }
16840
+ /**
16841
+ * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
16842
+ */
16736
16843
  #formatOnExecuted(elementApi) {
16737
- const { io, script, loop } = this.extensions;
16844
+ const { io, script, calledDecision, loop } = this.extensions;
16738
16845
  let jobResult = elementApi.content.output;
16739
16846
 
16740
16847
  // Multi-instance: aggregate the per-instance outputs into the `outputCollection` array.
@@ -16750,9 +16857,10 @@ class ElementExtensions {
16750
16857
  jobResult = jobResult.output;
16751
16858
  }
16752
16859
 
16753
- // A script task's FEEL result is named by its `resultVariable`; a job's result is already
16754
- // an object of variables.
16755
- const resultObject = script ? (script.resultVariable ? { [script.resultVariable]: jobResult } : undefined) : jobResult;
16860
+ // A script task (`zeebe:script`) or business rule task (`zeebe:calledDecision`) names its single
16861
+ // result with `resultVariable`. A plain job's result is already an object of variables.
16862
+ const named = script || calledDecision;
16863
+ const resultObject = named ? (named.resultVariable ? { [named.resultVariable]: jobResult } : undefined) : jobResult;
16756
16864
 
16757
16865
  let output;
16758
16866
  if (io?.hasOutput) {
@@ -16768,6 +16876,10 @@ class ElementExtensions {
16768
16876
  }
16769
16877
  return {};
16770
16878
  }
16879
+ /**
16880
+ * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
16881
+ * @param {any} output
16882
+ */
16771
16883
  #assignOutput(elementApi, output) {
16772
16884
  const environment = this.activity.environment;
16773
16885
  // Visible to downstream FEEL within the process, and surfaced as process output
@@ -16783,8 +16895,12 @@ class ElementExtensions {
16783
16895
  * the result to the process variables.
16784
16896
  */
16785
16897
  class ProcessExtensions {
16898
+ /**
16899
+ * @param {import('bpmn-elements').Process} bp
16900
+ */
16786
16901
  constructor(bp) {
16787
16902
  this.process = bp;
16903
+ /** @type {import('./getExtensions.js').ExtensionHandlers} */
16788
16904
  this.extensions = getExtensions(bp);
16789
16905
  }
16790
16906
  activate() {
@@ -16798,6 +16914,9 @@ class ProcessExtensions {
16798
16914
  deactivate() {
16799
16915
  this.process.broker.cancel('0dep-bpmn-extensions:on-enter');
16800
16916
  }
16917
+ /**
16918
+ * @param {import('bpmn-elements').IApi<import('bpmn-elements').Process>} elementApi
16919
+ */
16801
16920
  #onEnter(elementApi) {
16802
16921
  try {
16803
16922
  // bpmn-elements (>=18.0.4) forwards a call activity's formatted input — its io mapping plus,
@@ -16839,14 +16958,14 @@ class SubProcessExtensions extends ElementExtensions {
16839
16958
  const listeners = this.extensions.listeners;
16840
16959
  const enterKey = message.fields.redelivered && message.fields.routingKey === 'run.start' ? 'activity.start' : 'activity.enter';
16841
16960
 
16842
- this.#subscribe(enterKey, (api) => this._onEnter(api), '0dep-bpmn-extensions:on-enter');
16843
- 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);
16844
16963
 
16845
16964
  if (listeners?.onStart) {
16846
- 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);
16847
16966
  }
16848
16967
  if (listeners?.onEnd) {
16849
- 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);
16850
16969
  }
16851
16970
  }
16852
16971
  #subscribe(routingKey, callback, consumerTag) {
@@ -16870,7 +16989,7 @@ class SubProcessExtensions extends ElementExtensions {
16870
16989
  * definition resolves FEEL expressions (`= ...`) instead of the default
16871
16990
  * `${...}` template expressions.
16872
16991
  *
16873
- * @returns {import('bpmn-elements').IExpressions}
16992
+ * @returns {import('bpmn-elements').IExpressions & {isExpression: (text:string) => boolean, hasExpression: (text:string) => boolean}}
16874
16993
  */
16875
16994
  function FeelExpressions() {
16876
16995
  return {
@@ -16905,10 +17024,7 @@ function resolveExpression(templatedString, context) {
16905
17024
  * (e.g. a multi-instance item).
16906
17025
  */
16907
17026
  function getScope(context) {
16908
- const environment = context && context.environment;
16909
- const variables = environment ? environment.variables : {};
16910
- const local = context && context.content && context.content.variables;
16911
- return { ...variables, ...local };
17027
+ return { ...context?.environment?.variables, ...context?.content?.variables };
16912
17028
  }
16913
17029
 
16914
17030
  /**
@@ -16927,10 +17043,18 @@ function FeelScripts() {
16927
17043
  }
16928
17044
 
16929
17045
  class FeelScriptRegistry {
16930
- 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
+ */
16931
17055
  getScript(_scriptFormat, activity) {
16932
17056
  const expression = getScriptExpression(activity);
16933
- if (expression === undefined) return undefined;
17057
+ if (expression === undefined) return /** @type {any} */ (undefined);
16934
17058
  return new FeelScript(expression);
16935
17059
  }
16936
17060
  }
@@ -16962,8 +17086,8 @@ function getScriptExpression(activity) {
16962
17086
  /**
16963
17087
  * A flow extension activated by bpmn-elements around an element's run.
16964
17088
  * @typedef {object} FlowExtension
16965
- * @property {(message: any) => void} activate
16966
- * @property {(message?: any) => void} deactivate
17089
+ * @property {(message: import('bpmn-elements').ElementBrokerMessage) => void} activate
17090
+ * @property {(message?: import('bpmn-elements').ElementBrokerMessage) => void} deactivate
16967
17091
  */
16968
17092
 
16969
17093
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0dep/bpmn-extensions",
3
- "version": "0.0.1",
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,8 +125,11 @@ 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
- const { io, script, loop } = this.extensions;
132
+ const { io, script, calledDecision, loop } = this.extensions;
101
133
  let jobResult = elementApi.content.output;
102
134
 
103
135
  // Multi-instance: aggregate the per-instance outputs into the `outputCollection` array.
@@ -113,9 +145,10 @@ export class ElementExtensions {
113
145
  jobResult = jobResult.output;
114
146
  }
115
147
 
116
- // A script task's FEEL result is named by its `resultVariable`; a job's result is already
117
- // an object of variables.
118
- const resultObject = script ? (script.resultVariable ? { [script.resultVariable]: jobResult } : undefined) : jobResult;
148
+ // A script task (`zeebe:script`) or business rule task (`zeebe:calledDecision`) names its single
149
+ // result with `resultVariable`. A plain job's result is already an object of variables.
150
+ const named = script || calledDecision;
151
+ const resultObject = named ? (named.resultVariable ? { [named.resultVariable]: jobResult } : undefined) : jobResult;
119
152
 
120
153
  let output;
121
154
  if (io?.hasOutput) {
@@ -131,6 +164,10 @@ export class ElementExtensions {
131
164
  }
132
165
  return {};
133
166
  }
167
+ /**
168
+ * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
169
+ * @param {any} output
170
+ */
134
171
  #assignOutput(elementApi, output) {
135
172
  const environment = this.activity.environment;
136
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,22 +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
- * Service?: Function,
22
- * }}
30
+ * @param {import('bpmn-elements').ContextInstance} [context]
31
+ * @returns {ExtensionHandlers}
23
32
  */
24
- export function getExtensions(element) {
33
+ export function getExtensions(element, context) {
25
34
  const result = {};
26
35
  const isProcess = element.type === 'bpmn:Process';
27
36
 
@@ -51,6 +60,13 @@ export function getExtensions(element) {
51
60
  case 'zeebe:Script':
52
61
  result.script = ext;
53
62
  break;
63
+ case 'zeebe:CalledDecision':
64
+ // A business rule task runs as a service task (bpmn-elements maps it to ServiceTask). The
65
+ // decision is resolved by an environment service named by the decision id; its result is
66
+ // named by `resultVariable`.
67
+ result.calledDecision = ext;
68
+ result.Service = JobService.bind(JobService, ext.decisionId);
69
+ break;
54
70
  case 'zeebe:FormDefinition':
55
71
  result.form = new Form(ext);
56
72
  break;
@@ -58,6 +74,18 @@ export function getExtensions(element) {
58
74
  }
59
75
  }
60
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
+
61
89
  // Output side of a multi-instance `zeebe:loopCharacteristics` (the input side is lifted by extendFn).
62
90
  const loopExtension = element.behaviour.loopCharacteristics?.behaviour?.extensionElements?.values?.find(
63
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,27 +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
- Service?: Function;
100
- };
101
- 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;
102
104
  deactivate(): void;
103
- /** @internal Shared with SubProcessExtensions. */
104
- _onEnter(elementApi: any): Promise<void>;
105
- /** @internal Shared with SubProcessExtensions. */
106
- _onExecuted(elementApi: any): Promise<void>;
107
- /** @internal Shared with SubProcessExtensions. */
108
- _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>;
109
117
  #private;
110
118
  }
111
119
  /**
@@ -113,18 +121,11 @@ declare module '@0dep/bpmn-extensions' {
113
121
  * the result to the process variables.
114
122
  */
115
123
  export class ProcessExtensions {
116
- constructor(bp: any);
117
- process: any;
118
- extensions: {
119
- format: FormatActivity | FormatProcess;
120
- io?: IoMapping;
121
- headers?: TaskHeaders;
122
- properties?: Properties;
123
- listeners?: ExecutionListeners;
124
- form?: Form;
125
- loop?: LoopCharacteristics;
126
- Service?: Function;
127
- };
124
+
125
+ constructor(bp: import("bpmn-elements").Process);
126
+ process: import("bpmn-elements").Process;
127
+
128
+ extensions: ExtensionHandlers;
128
129
  activate(): void;
129
130
  deactivate(): void;
130
131
  #private;
@@ -139,6 +140,7 @@ declare module '@0dep/bpmn-extensions' {
139
140
  * sub-process's own id.
140
141
  */
141
142
  export class SubProcessExtensions extends ElementExtensions {
143
+ activate(message: any): void;
142
144
  #private;
143
145
  }
144
146
  /**
@@ -160,7 +162,8 @@ declare module '@0dep/bpmn-extensions' {
160
162
  * @param expression FEEL unary test
161
163
  * @param input The value being tested, available as `?` in the test
162
164
  * @param context Additional variables in scope
163
- * */
165
+ * @returns true only when the test is satisfied — an undecidable (null) test is false
166
+ */
164
167
  export function evaluateFeelUnaryTest(expression: string, input: any, context?: Record<string, any>): boolean;
165
168
  /**
166
169
  * Resolve a value: evaluate it as FEEL when it starts with `=`, otherwise
@@ -182,15 +185,33 @@ declare module '@0dep/bpmn-extensions' {
182
185
  statusCode: number;
183
186
  };
184
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
+ };
185
204
  /**
186
205
  * Format an activity on enter from its behaviour and extensions: documentation and,
187
206
  * for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups).
188
207
  */
189
208
  class FormatActivity {
190
- constructor(activity: any, assignmentDefinition: any);
191
- activity: any;
209
+
210
+ constructor(activity: import("bpmn-elements").Activity, assignmentDefinition: any);
211
+ activity: import("bpmn-elements").Activity;
192
212
  assignmentDefinition: any;
193
- resolve(elementApi: any): {
213
+
214
+ resolve(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Activity>): {
194
215
  description: any;
195
216
  assignee: any;
196
217
  candidateUsers: any[];
@@ -201,9 +222,11 @@ declare module '@0dep/bpmn-extensions' {
201
222
  * Format a process on enter: documentation.
202
223
  */
203
224
  class FormatProcess {
204
- constructor(bp: any);
205
- process: any;
206
- 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>): {
207
230
  description: any;
208
231
  };
209
232
  }
@@ -319,6 +342,39 @@ declare module '@0dep/bpmn-extensions' {
319
342
  * */
320
343
  aggregate(indexedOutput: Record<string, any> | undefined, baseScope: Record<string, any>): any[];
321
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
+ }
322
378
 
323
379
  export {};
324
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
  }