@0dep/bpmn-extensions 0.0.4 → 0.0.6

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
@@ -1,6 +1,6 @@
1
1
  # @0dep/bpmn-extensions
2
2
 
3
- [![Built latest](https://github.com/zerodep/bpmn-extensions/actions/workflows/build-latest.yaml/badge.svg)](https://github.com/zerodep/bpmn-extensions/actions/workflows/build-latest.yaml)[![Coverage Status](https://coveralls.io/repos/github/zerodep/bpmn-extensions/badge.svg?branch=main)](https://coveralls.io/github/zerodep/bpmn-extensions?branch=main)[![npm version](https://img.shields.io/npm/v/@0dep/bpmn-extensions)](https://www.npmjs.com/package/@0dep/bpmn-extensions)
3
+ [![Built latest](https://github.com/zerodep/bpmn-extensions/actions/workflows/build-latest.yaml/badge.svg)](https://github.com/zerodep/bpmn-extensions/actions/workflows/build-latest.yaml)[![Coverage Status](https://coveralls.io/repos/github/zerodep/bpmn-extensions/badge.svg?branch=main)](https://coveralls.io/github/zerodep/bpmn-extensions?branch=main)
4
4
 
5
5
  Flow extensions for [bpmn-elements](https://github.com/paed01/bpmn-elements) with
6
6
  [FEEL](https://www.omg.org/dmn/) expression support.
@@ -12,10 +12,11 @@ which covers the older `camunda:*` extension elements.
12
12
  ## Install
13
13
 
14
14
  ```sh
15
- npm install @0dep/bpmn-extensions bpmn-elements
15
+ npm install @0dep/bpmn-extensions bpmn-elements croner
16
16
  ```
17
17
 
18
- `bpmn-elements` (>= 18) is a peer dependency. Requires Node.js >= 22.
18
+ `bpmn-elements` (>= 18) and [`croner`](https://www.npmjs.com/package/croner) (cron timer cycles) are
19
+ peer dependencies. Requires Node.js >= 22.
19
20
 
20
21
  ## What it does
21
22
 
@@ -38,6 +39,9 @@ npm install @0dep/bpmn-extensions bpmn-elements
38
39
  process's input becomes variables local to its children.
39
40
  - **`zeebe:taskHeaders`** / **`zeebe:properties`** — exposed on the element content.
40
41
  - **`zeebe:assignmentDefinition`** — assignee / candidate users / groups for user tasks.
42
+ - **`zeebe:priorityDefinition`** / **`zeebe:taskSchedule`** — a user task's `priority` and
43
+ `dueDate` / `followUpDate` resolved onto the element content for a task list to order and
44
+ remind by; FEEL date results are exposed as ISO 8601 strings.
41
45
  - **`zeebe:formDefinition`** — resolves a user task's form (`formId` / `formKey` / `externalReference`,
42
46
  `bindingType`) onto the element content for a task list to render.
43
47
  - **`zeebe:executionListeners`** — blocking start/end job workers around an element, called as
@@ -46,6 +50,10 @@ npm install @0dep/bpmn-extensions bpmn-elements
46
50
  - **`zeebe:calledElement`** — call activity target process id.
47
51
  - **`zeebe:loopCharacteristics`** — collection-based multi-instance (`inputCollection` / `inputElement`),
48
52
  sequential or parallel, with `outputElement` aggregated into the `outputCollection` array in input order.
53
+ - **Timer start events with cron** — a `TimerEventDefinition` that also parses a **cron** `timeCycle`
54
+ (`0 0 * * *`, as Camunda 8 schedules timer start events) besides ISO 8601 intervals. A top-level
55
+ timer start event's cycle is lifted onto its behaviour as `scheduledStart` by `extendFn` and exposed
56
+ on the start event content, so a scheduler can find the flows to start without running them.
49
57
  - **`zeebe:subscription`** — the referenced message's `correlationKey` is resolved when the catching
50
58
  element (receive task, message event) starts waiting, and exposed on its content as
51
59
  `subscription: { message, correlationKey }` — route an incoming message by matching it against the
@@ -54,8 +62,14 @@ npm install @0dep/bpmn-extensions bpmn-elements
54
62
  ## API
55
63
 
56
64
  - `extensions(element, context)` — the flow extensions factory; pass it as an environment extension.
65
+ Elements that carry no zeebe extension data (and nothing else to format) get no extension attached
66
+ at all and run untouched; call activities and processes are always attached, since they propagate
67
+ io across process boundaries.
57
68
  - `extendFn(behaviour)` — moddle-context-serializer behaviour extender (lifts the extension data
58
- bpmn-elements expects on the behaviour: call activity process id and multi-instance collection).
69
+ bpmn-elements expects on the behaviour: call activity process id, multi-instance collection, and a
70
+ timer start event's cycle as `scheduledStart`).
71
+ - `TimerEventDefinition` — a bpmn-elements timer event definition that accepts cron `timeCycle`s;
72
+ install it through the type resolver: `TypeResolver({ ...elements, TimerEventDefinition })`.
59
73
  - `FeelExpressions()` — a bpmn-elements `IExpressions` implementation backed by FEEL.
60
74
  - `FeelScripts()` — a bpmn-elements `scripts` implementation that runs `zeebe:script` FEEL expressions.
61
75
  - FEEL helpers: `isFeelExpression`, `stripFeel`, `evaluateFeel`, `evaluateFeelUnaryTest`, `resolveValue`,
@@ -69,7 +83,7 @@ Wire it into a bpmn-elements definition (see `test/helpers/testHelpers.js` for t
69
83
  ```javascript
70
84
  import { createRequire } from 'node:module';
71
85
  import { strict as assert } from 'node:assert';
72
- import BpmnModdle from 'bpmn-moddle';
86
+ import { BpmnModdle } from 'bpmn-moddle';
73
87
  import * as elements from 'bpmn-elements';
74
88
  import { Serializer, TypeResolver } from 'moddle-context-serializer';
75
89
  import { extensions, extendFn, FeelExpressions, FeelScripts } from '@0dep/bpmn-extensions';
@@ -124,6 +138,60 @@ A service task's `zeebe:taskDefinition type="charge-card"` is dispatched to the
124
138
  service named `charge-card`. Its callback result is the job's variables, which `zeebe:ioMapping`
125
139
  output parameters map back into the process.
126
140
 
141
+ ## Extract timers
142
+
143
+ List the timers of a definition without running it — e.g. to schedule flows by their timer start
144
+ event. The serializer collects every timer event definition; `extendFn` marks a timer start event's
145
+ cycle as `scheduledStart`, and the cron-capable `TimerEventDefinition` parses each one to its next
146
+ expiry:
147
+
148
+ ```javascript
149
+ import { createRequire } from 'node:module';
150
+ import { strict as assert } from 'node:assert';
151
+ import { BpmnModdle } from 'bpmn-moddle';
152
+ import * as elements from 'bpmn-elements';
153
+ import { Serializer, TypeResolver } from 'moddle-context-serializer';
154
+ import { extendFn, TimerEventDefinition } from '@0dep/bpmn-extensions';
155
+
156
+ const require = createRequire(import.meta.url);
157
+ const moddle = new BpmnModdle({ zeebe: require('zeebe-bpmn-moddle/resources/zeebe.json') });
158
+
159
+ const source = `<?xml version="1.0" encoding="UTF-8"?>
160
+ <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="def">
161
+ <process id="nightly" isExecutable="true">
162
+ <startEvent id="start">
163
+ <timerEventDefinition>
164
+ <timeCycle xsi:type="tFormalExpression">0 1 * * *</timeCycle>
165
+ </timerEventDefinition>
166
+ </startEvent>
167
+ <sequenceFlow id="to-task" sourceRef="start" targetRef="task" />
168
+ <userTask id="task" />
169
+ <boundaryEvent id="reminder" cancelActivity="false" attachedToRef="task">
170
+ <timerEventDefinition>
171
+ <timeDuration xsi:type="tFormalExpression">R3/PT1M</timeDuration>
172
+ </timerEventDefinition>
173
+ </boundaryEvent>
174
+ </process>
175
+ </definitions>`;
176
+
177
+ const moddleContext = await moddle.fromXML(source);
178
+ const serializer = Serializer(moddleContext, TypeResolver({ ...elements, TimerEventDefinition }), extendFn);
179
+
180
+ const dummyActivity = { id: 'dummy', broker: {}, environment: { Logger() {} } };
181
+ const timers = serializer.getTimers().map((t) => ({
182
+ ...t,
183
+ parsed: new TimerEventDefinition(dummyActivity, { type: t.timer.type, behaviour: t.timer }).parse(t.timer.timerType, t.timer.value),
184
+ }));
185
+
186
+ console.log(timers.map((t) => [t.parent.id, t.timer.timerType, t.timer.value, t.parsed.expireAt]));
187
+ assert.equal(timers.length, 2);
188
+ assert.equal(timers[0].parent.id, 'start');
189
+ assert.ok(timers[0].parsed.expireAt instanceof Date);
190
+
191
+ // A timer start event is a schedule: the cycle is lifted onto the behaviour
192
+ assert.equal(serializer.getActivityById('start').behaviour.scheduledStart, '0 1 * * *');
193
+ ```
194
+
127
195
  ## Development
128
196
 
129
197
  ```sh
@@ -141,6 +209,7 @@ the engine's `Logger` to the [`debug`](https://www.npmjs.com/package/debug) pack
141
209
 
142
210
  # Ecosystem
143
211
 
212
+ - [0dep.se/run](https://0dep.se/run) — Run a BPMN diagram in the browser: `bpmn-elements` wired with `@0dep/bpmn-extensions`, drawn with bpmn-js; step through, signal waiting tasks, and drop DMN files for business rule tasks.
144
213
  - [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.
145
214
  - [bpmn-middleware](https://github.com/zerodep/bpmn-middleware) — Express middleware exposing the engine over HTTP, with pluggable state storage.
146
215
  - [@onify/flow-extensions](https://github.com/onify/flow-extensions) — Onify Flow extensions for `bpmn-elements`.
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ var croner = require('croner');
4
+ var bpmnElements = require('bpmn-elements');
5
+
3
6
  // these aren't really private, but nor are they really useful to document
4
7
 
5
8
  /**
@@ -11609,9 +11612,9 @@ function getSpecializer(spec) {
11609
11612
  *
11610
11613
  * @return {T[]}
11611
11614
  */
11612
- const nativeHasOwnProperty$1 = Object.prototype.hasOwnProperty;
11615
+ const nativeHasOwnProperty = Object.prototype.hasOwnProperty;
11613
11616
 
11614
- function isNil$2(obj) {
11617
+ function isNil$1(obj) {
11615
11618
  return obj == null;
11616
11619
  }
11617
11620
 
@@ -11623,8 +11626,8 @@ function isNil$2(obj) {
11623
11626
  *
11624
11627
  * @return {Boolean}
11625
11628
  */
11626
- function has$1(target, key) {
11627
- return !isNil$2(target) && nativeHasOwnProperty$1.call(target, key);
11629
+ function has(target, key) {
11630
+ return !isNil$1(target) && nativeHasOwnProperty.call(target, key);
11628
11631
  }
11629
11632
 
11630
11633
  let nextTagID = 0;
@@ -13107,7 +13110,7 @@ class VariableContext {
13107
13110
  continue;
13108
13111
  }
13109
13112
 
13110
- if (has$1(merged, key)) {
13113
+ if (has(merged, key)) {
13111
13114
 
13112
13115
  // deep merge nested contexts
13113
13116
  merged[key] = this.__merge(merged[key], value);
@@ -13241,7 +13244,7 @@ class Variables {
13241
13244
  return null;
13242
13245
  }
13243
13246
 
13244
- if (!isNil$2(scope.value)) {
13247
+ if (!isNil$1(scope.value)) {
13245
13248
  return scope.value;
13246
13249
  }
13247
13250
  }
@@ -13871,33 +13874,6 @@ const parser = LRParser.deserialize({
13871
13874
  tokenPrec: 2660
13872
13875
  });
13873
13876
 
13874
- /**
13875
- * Flatten array, one level deep.
13876
- *
13877
- * @template T
13878
- *
13879
- * @param {T[][] | T[] | null} [arr]
13880
- *
13881
- * @return {T[]}
13882
- */
13883
- const nativeHasOwnProperty = Object.prototype.hasOwnProperty;
13884
-
13885
- function isNil$1(obj) {
13886
- return obj == null;
13887
- }
13888
-
13889
- /**
13890
- * Return true, if target owns a property with the given key.
13891
- *
13892
- * @param {Object} target
13893
- * @param {String} key
13894
- *
13895
- * @return {Boolean}
13896
- */
13897
- function has(target, key) {
13898
- return !isNil$1(target) && nativeHasOwnProperty.call(target, key);
13899
- }
13900
-
13901
13877
  function isNil(e) {
13902
13878
  return e === null || e === undefined;
13903
13879
  }
@@ -16238,17 +16214,22 @@ function getFeelScope(environment, localVariables) {
16238
16214
  }
16239
16215
 
16240
16216
  /**
16241
- * Format an activity on enter from its behaviour and extensions: documentation and,
16242
- * for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups).
16217
+ * Format an activity on enter from its behaviour and extensions: documentation, a top-level
16218
+ * timer start event's `scheduledStart` (lifted by `extendFn`) and, for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups),
16219
+ * `zeebe:priorityDefinition` (priority), and `zeebe:taskSchedule` (due/follow-up date).
16243
16220
  */
16244
16221
  class FormatActivity {
16245
16222
  /**
16246
16223
  * @param {import('bpmn-elements').Activity} activity
16247
16224
  * @param {any} assignmentDefinition
16225
+ * @param {any} [priorityDefinition]
16226
+ * @param {any} [taskSchedule]
16248
16227
  */
16249
- constructor(activity, assignmentDefinition) {
16228
+ constructor(activity, assignmentDefinition, priorityDefinition, taskSchedule) {
16250
16229
  this.activity = activity;
16251
16230
  this.assignmentDefinition = assignmentDefinition;
16231
+ this.priorityDefinition = priorityDefinition;
16232
+ this.taskSchedule = taskSchedule;
16252
16233
  }
16253
16234
  /**
16254
16235
  * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
@@ -16260,9 +16241,14 @@ class FormatActivity {
16260
16241
  const documentation = this.activity.behaviour.documentation;
16261
16242
  if (documentation && !elementApi.content.description) {
16262
16243
  const text = documentation[0]?.text;
16263
- if (text) result.description = resolveValue(text, scope);
16244
+ if (text) result.description = /** @type {string} */ (resolveValue(text, scope));
16264
16245
  }
16265
16246
 
16247
+ // A timer start event's cycle is only a schedule when it starts the process itself — a
16248
+ // sub-process start event runs whenever its parent does.
16249
+ const scheduledStart = this.activity.behaviour.scheduledStart;
16250
+ if (scheduledStart && this.activity.parent?.type === 'bpmn:Process') result.scheduledStart = scheduledStart;
16251
+
16266
16252
  const assignment = this.assignmentDefinition;
16267
16253
  if (assignment) {
16268
16254
  const { assignee, candidateUsers, candidateGroups } = assignment;
@@ -16273,6 +16259,22 @@ class FormatActivity {
16273
16259
  if (groups) result.candidateGroups = groups;
16274
16260
  }
16275
16261
 
16262
+ const priority = this.priorityDefinition?.priority;
16263
+ if (priority !== undefined) {
16264
+ const resolved = resolveValue(priority, scope);
16265
+ // A static attribute value is a string — expose a numeric one as a number (like retries).
16266
+ const num = Number(resolved);
16267
+ /** @type {Number | string | undefined} */
16268
+ result.priority = typeof resolved === 'string' && resolved.trim() !== '' && Number.isFinite(num) ? num : resolved;
16269
+ }
16270
+
16271
+ const schedule = this.taskSchedule;
16272
+ if (schedule) {
16273
+ const { dueDate, followUpDate } = schedule;
16274
+ if (dueDate !== undefined) result.dueDate = resolveDate(dueDate, scope);
16275
+ if (followUpDate !== undefined) result.followUpDate = resolveDate(followUpDate, scope);
16276
+ }
16277
+
16276
16278
  return result;
16277
16279
  }
16278
16280
  }
@@ -16295,15 +16297,33 @@ class FormatProcess {
16295
16297
  const documentation = this.process.behaviour.documentation;
16296
16298
  if (documentation && !elementApi.content.description) {
16297
16299
  const text = documentation[0]?.text;
16298
- if (text) result.description = resolveValue(text, getFeelScope(elementApi.environment));
16300
+ if (text) result.description = /** @type {string} */ (resolveValue(text, getFeelScope(elementApi.environment)));
16299
16301
  }
16300
16302
  return result;
16301
16303
  }
16302
16304
  }
16303
16305
 
16306
+ /**
16307
+ * Resolve a due/follow-up date to an ISO 8601 string. A FEEL temporal result (a luxon
16308
+ * DateTime) or a Date is ISO-stringified — content is JSON-serialized on `getState()`, so a
16309
+ * string is the shape that survives a recover round trip. Anything else passes through as-is.
16310
+ * @param {any} value
16311
+ * @param {any} scope
16312
+ * @returns {string | undefined} Date as iso string
16313
+ */
16314
+ function resolveDate(value, scope) {
16315
+ const resolved = resolveValue(value, scope);
16316
+ if (typeof resolved?.toISO === 'function') return resolved.toISO();
16317
+ if (resolved instanceof Date) return resolved.toISOString();
16318
+ return resolved;
16319
+ }
16320
+
16304
16321
  /**
16305
16322
  * Resolve a candidate users/groups value to a string array. A FEEL expression may yield an
16306
16323
  * array directly, otherwise a comma-separated string is split.
16324
+ * @param {any} value
16325
+ * @param {any} scope
16326
+ * @returns {string[] | undefined}
16307
16327
  */
16308
16328
  function resolveList(value, scope) {
16309
16329
  if (!value) return undefined;
@@ -16652,6 +16672,7 @@ class Subscription {
16652
16672
  * @property {any} [calledDecision]
16653
16673
  * @property {Function} [Service]
16654
16674
  * @property {Subscription} [subscription]
16675
+ * @property {boolean} [isEmpty] no zeebe extension data found and nothing to format — the element needs no extension
16655
16676
  */
16656
16677
 
16657
16678
  /**
@@ -16664,10 +16685,15 @@ function getExtensions(element, context) {
16664
16685
  const result = {};
16665
16686
  const isProcess = element.type === 'bpmn:Process';
16666
16687
 
16667
- let assignmentDefinition;
16688
+ let assignmentDefinition, priorityDefinition, taskSchedule;
16689
+ let foundZeebe = false;
16668
16690
  const values = element.behaviour.extensionElements?.values;
16669
16691
  if (values) {
16670
16692
  for (const ext of values) {
16693
+ // Any zeebe-namespaced extension element counts as found — including ones with no
16694
+ // handler (e.g. the bare `zeebe:userTask` marker), which keep the element extension
16695
+ // loaded so e.g. a native user task's completion payload is merged.
16696
+ if (ext.$type.startsWith('zeebe:')) foundZeebe = true;
16671
16697
  switch (ext.$type) {
16672
16698
  case 'zeebe:IoMapping':
16673
16699
  result.io = new IoMapping(element, ext);
@@ -16681,6 +16707,12 @@ function getExtensions(element, context) {
16681
16707
  case 'zeebe:AssignmentDefinition':
16682
16708
  assignmentDefinition = ext;
16683
16709
  break;
16710
+ case 'zeebe:PriorityDefinition':
16711
+ priorityDefinition = ext;
16712
+ break;
16713
+ case 'zeebe:TaskSchedule':
16714
+ taskSchedule = ext;
16715
+ break;
16684
16716
  case 'zeebe:ExecutionListeners':
16685
16717
  if (ext.listeners?.length) result.listeners = new ExecutionListeners(element, ext);
16686
16718
  break;
@@ -16722,7 +16754,22 @@ function getExtensions(element, context) {
16722
16754
  );
16723
16755
  if (loopExtension?.outputCollection) result.loop = new LoopCharacteristics(loopExtension);
16724
16756
 
16725
- result.format = isProcess ? new FormatProcess(element) : new FormatActivity(element, assignmentDefinition);
16757
+ // Nothing zeebe-flavoured and nothing to format (documentation, a process-level timer start
16758
+ // event's `scheduledStart`) — the element needs no extension. A call
16759
+ // activity is never inert: its output unwrap/merge is type-driven (the standard BPMN
16760
+ // `calledElement` attribute needs no zeebe elements). Ignored for processes — a called
16761
+ // process always needs its inbound input promoted, whatever it carries itself.
16762
+ result.isEmpty =
16763
+ !foundZeebe &&
16764
+ !result.subscription &&
16765
+ !loopExtension &&
16766
+ !element.behaviour.documentation &&
16767
+ !(element.behaviour.scheduledStart && element.parent?.type === 'bpmn:Process') &&
16768
+ element.type !== 'bpmn:CallActivity';
16769
+
16770
+ result.format = isProcess
16771
+ ? new FormatProcess(element)
16772
+ : new FormatActivity(element, assignmentDefinition, priorityDefinition, taskSchedule);
16726
16773
 
16727
16774
  return result;
16728
16775
  }
@@ -17090,6 +17137,50 @@ function getScriptExpression(activity) {
17090
17137
  return script?.expression;
17091
17138
  }
17092
17139
 
17140
+ /**
17141
+ * bpmn-elements timer event definition that also accepts a **cron** `timeCycle` (Camunda 8
17142
+ * timer start events schedule with cron, e.g. `0 0 * * *`). An ISO 8601 interval is tried
17143
+ * first; on failure the value is parsed as cron and the next run is the expiry.
17144
+ *
17145
+ * Install it via the type resolver so it replaces the bpmn-elements default:
17146
+ * `TypeResolver({ ...elements, TimerEventDefinition })`.
17147
+ */
17148
+ class TimerEventDefinition extends bpmnElements.TimerEventDefinition {
17149
+ /**
17150
+ * Supported timeCycle formats
17151
+ * @returns {string[]}
17152
+ */
17153
+ get supports() {
17154
+ return ['cron', 'iso8601'];
17155
+ }
17156
+ /**
17157
+ * @param {import('bpmn-elements').TimerType} timerType
17158
+ * @param {string} value
17159
+ * @returns {import('bpmn-elements').parsedTimer}
17160
+ */
17161
+ parse(timerType, value) {
17162
+ if (timerType !== 'timeCycle') return super.parse(timerType, value);
17163
+
17164
+ /** @type {any} */
17165
+ let isoError;
17166
+ try {
17167
+ return super.parse(timerType, value);
17168
+ } catch (err) {
17169
+ isoError = err;
17170
+ }
17171
+
17172
+ try {
17173
+ const expireAt = new croner.Cron(value).nextRun();
17174
+ if (!expireAt) throw new RangeError('cron has no next run');
17175
+ return { expireAt, delay: expireAt.getTime() - Date.now() };
17176
+ } catch (/** @type {any} */ err) {
17177
+ this.logger.error(`<${this.activity?.id}> failed to parse timeCycle: ${isoError.message}`);
17178
+ this.logger.error(`<${this.activity?.id}> failed to parse timeCycle as cron: ${err.message}`);
17179
+ throw new RangeError(`Failed to parse timeCycle <${value?.substring(0, 255)}> as ISO 8601 interval or cron`, { cause: err });
17180
+ }
17181
+ }
17182
+ }
17183
+
17093
17184
  /**
17094
17185
  * A flow extension activated by bpmn-elements around an element's run.
17095
17186
  * @typedef {object} FlowExtension
@@ -17099,20 +17190,32 @@ function getScriptExpression(activity) {
17099
17190
 
17100
17191
  /**
17101
17192
  * Flow extensions factory. Pass it to the engine via the environment `extensions` option.
17193
+ *
17194
+ * Returns undefined for an element that carries no zeebe extension data and nothing else to
17195
+ * format (no documentation, not a call activity) — bpmn-elements skips falsy extensions, so
17196
+ * plain elements run untouched, with no broker subscriptions or format-queue traffic.
17102
17197
  * @param {import('bpmn-elements').Activity | import('bpmn-elements').Process} element
17103
17198
  * @param {import('bpmn-elements').ContextInstance} context
17104
- * @returns {FlowExtension}
17199
+ * @returns {FlowExtension | undefined}
17105
17200
  */
17106
17201
  function extensions(element, context) {
17107
17202
  switch (element.type) {
17108
17203
  case 'bpmn:Process':
17204
+ // A process is always attached: a called process needs its inbound input promoted
17205
+ // even when it carries no zeebe extension data of its own.
17109
17206
  return new ProcessExtensions(element, context);
17110
17207
  case 'bpmn:SubProcess':
17111
17208
  case 'bpmn:AdHocSubProcess':
17112
- case 'bpmn:Transaction':
17113
- return new SubProcessExtensions(element, context);
17114
- default:
17115
- return new ElementExtensions(element, context);
17209
+ case 'bpmn:Transaction': {
17210
+ const subProcessExtensions = new SubProcessExtensions(element, context);
17211
+ if (subProcessExtensions.extensions.isEmpty) return undefined;
17212
+ return subProcessExtensions;
17213
+ }
17214
+ default: {
17215
+ const elementExtensions = new ElementExtensions(element, context);
17216
+ if (elementExtensions.extensions.isEmpty) return undefined;
17217
+ return elementExtensions;
17218
+ }
17116
17219
  }
17117
17220
  }
17118
17221
 
@@ -17121,10 +17224,17 @@ function extensions(element, context) {
17121
17224
  *
17122
17225
  * Lifts extension data onto the places bpmn-elements expects to find it on the element
17123
17226
  * behaviour: the call activity's called process id (`zeebe:calledElement`) and the multi-instance
17124
- * input collection/element (`zeebe:loopCharacteristics`).
17227
+ * input collection/element (`zeebe:loopCharacteristics`). A timer start event's `timeCycle`
17228
+ * (ISO 8601 or cron) is lifted as `scheduledStart`, so a scheduler can find it without running
17229
+ * the flow.
17125
17230
  * @param {any} behaviour
17126
17231
  */
17127
17232
  function extendFn(behaviour) {
17233
+ if (behaviour.$type === 'bpmn:StartEvent' && Array.isArray(behaviour.eventDefinitions)) {
17234
+ const timer = behaviour.eventDefinitions.find((ed) => ed?.type === 'bpmn:TimerEventDefinition' && ed.behaviour?.timeCycle);
17235
+ if (timer) behaviour.scheduledStart = timer.behaviour.timeCycle;
17236
+ }
17237
+
17128
17238
  const values = behaviour.extensionElements?.values;
17129
17239
  if (Array.isArray(values)) {
17130
17240
  for (const ext of values) {
@@ -17152,6 +17262,7 @@ exports.JobService = JobService;
17152
17262
  exports.ProcessExtensions = ProcessExtensions;
17153
17263
  exports.ServiceError = ServiceError;
17154
17264
  exports.SubProcessExtensions = SubProcessExtensions;
17265
+ exports.TimerEventDefinition = TimerEventDefinition;
17155
17266
  exports.evaluateFeel = evaluateFeel;
17156
17267
  exports.evaluateFeelUnaryTest = evaluateFeelUnaryTest;
17157
17268
  exports.extendFn = extendFn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0dep/bpmn-extensions",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
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,7 +51,8 @@
51
51
  "access": "public"
52
52
  },
53
53
  "peerDependencies": {
54
- "bpmn-elements": ">=18.0.12"
54
+ "bpmn-elements": ">=18.0.12",
55
+ "croner": ">=9"
55
56
  },
56
57
  "dependencies": {
57
58
  "feelin": "^7.0.1"
@@ -69,9 +70,10 @@
69
70
  "@types/mocha": "^10.0.10",
70
71
  "@types/node": "^26.1.1",
71
72
  "bpmn-elements": "^18.0.12",
72
- "bpmn-moddle": "^9.0.1",
73
+ "bpmn-moddle": "^10.1.0",
73
74
  "c8": "^12.0.0",
74
75
  "chai": "^6.2.2",
76
+ "croner": "^10.0.1",
75
77
  "debug": "^4.4.3",
76
78
  "dts-buddy": "^0.8.2",
77
79
  "eslint": "^10.6.0",
@@ -83,7 +85,7 @@
83
85
  "rollup": "^4.62.2",
84
86
  "texample": "^1.0.2",
85
87
  "typescript": "^6.0.3",
86
- "zeebe-bpmn-moddle": "^1.15.0"
88
+ "zeebe-bpmn-moddle": "^2.0.0"
87
89
  },
88
90
  "files": [
89
91
  "src",
@@ -0,0 +1,46 @@
1
+ import { Cron } from 'croner';
2
+ import { TimerEventDefinition as BaseTimerEventDefinition } from 'bpmn-elements';
3
+
4
+ /**
5
+ * bpmn-elements timer event definition that also accepts a **cron** `timeCycle` (Camunda 8
6
+ * timer start events schedule with cron, e.g. `0 0 * * *`). An ISO 8601 interval is tried
7
+ * first; on failure the value is parsed as cron and the next run is the expiry.
8
+ *
9
+ * Install it via the type resolver so it replaces the bpmn-elements default:
10
+ * `TypeResolver({ ...elements, TimerEventDefinition })`.
11
+ */
12
+ export class TimerEventDefinition extends BaseTimerEventDefinition {
13
+ /**
14
+ * Supported timeCycle formats
15
+ * @returns {string[]}
16
+ */
17
+ get supports() {
18
+ return ['cron', 'iso8601'];
19
+ }
20
+ /**
21
+ * @param {import('bpmn-elements').TimerType} timerType
22
+ * @param {string} value
23
+ * @returns {import('bpmn-elements').parsedTimer}
24
+ */
25
+ parse(timerType, value) {
26
+ if (timerType !== 'timeCycle') return super.parse(timerType, value);
27
+
28
+ /** @type {any} */
29
+ let isoError;
30
+ try {
31
+ return super.parse(timerType, value);
32
+ } catch (err) {
33
+ isoError = err;
34
+ }
35
+
36
+ try {
37
+ const expireAt = new Cron(value).nextRun();
38
+ if (!expireAt) throw new RangeError('cron has no next run');
39
+ return { expireAt, delay: expireAt.getTime() - Date.now() };
40
+ } catch (/** @type {any} */ err) {
41
+ this.logger.error(`<${this.activity?.id}> failed to parse timeCycle: ${isoError.message}`);
42
+ this.logger.error(`<${this.activity?.id}> failed to parse timeCycle as cron: ${err.message}`);
43
+ throw new RangeError(`Failed to parse timeCycle <${value?.substring(0, 255)}> as ISO 8601 interval or cron`, { cause: err });
44
+ }
45
+ }
46
+ }
@@ -1,17 +1,22 @@
1
1
  import { resolveValue, getFeelScope } from '../feel.js';
2
2
 
3
3
  /**
4
- * Format an activity on enter from its behaviour and extensions: documentation and,
5
- * for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups).
4
+ * Format an activity on enter from its behaviour and extensions: documentation, a top-level
5
+ * timer start event's `scheduledStart` (lifted by `extendFn`) and, for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups),
6
+ * `zeebe:priorityDefinition` (priority), and `zeebe:taskSchedule` (due/follow-up date).
6
7
  */
7
8
  export class FormatActivity {
8
9
  /**
9
10
  * @param {import('bpmn-elements').Activity} activity
10
11
  * @param {any} assignmentDefinition
12
+ * @param {any} [priorityDefinition]
13
+ * @param {any} [taskSchedule]
11
14
  */
12
- constructor(activity, assignmentDefinition) {
15
+ constructor(activity, assignmentDefinition, priorityDefinition, taskSchedule) {
13
16
  this.activity = activity;
14
17
  this.assignmentDefinition = assignmentDefinition;
18
+ this.priorityDefinition = priorityDefinition;
19
+ this.taskSchedule = taskSchedule;
15
20
  }
16
21
  /**
17
22
  * @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
@@ -23,9 +28,14 @@ export class FormatActivity {
23
28
  const documentation = this.activity.behaviour.documentation;
24
29
  if (documentation && !elementApi.content.description) {
25
30
  const text = documentation[0]?.text;
26
- if (text) result.description = resolveValue(text, scope);
31
+ if (text) result.description = /** @type {string} */ (resolveValue(text, scope));
27
32
  }
28
33
 
34
+ // A timer start event's cycle is only a schedule when it starts the process itself — a
35
+ // sub-process start event runs whenever its parent does.
36
+ const scheduledStart = this.activity.behaviour.scheduledStart;
37
+ if (scheduledStart && this.activity.parent?.type === 'bpmn:Process') result.scheduledStart = scheduledStart;
38
+
29
39
  const assignment = this.assignmentDefinition;
30
40
  if (assignment) {
31
41
  const { assignee, candidateUsers, candidateGroups } = assignment;
@@ -36,6 +46,22 @@ export class FormatActivity {
36
46
  if (groups) result.candidateGroups = groups;
37
47
  }
38
48
 
49
+ const priority = this.priorityDefinition?.priority;
50
+ if (priority !== undefined) {
51
+ const resolved = resolveValue(priority, scope);
52
+ // A static attribute value is a string — expose a numeric one as a number (like retries).
53
+ const num = Number(resolved);
54
+ /** @type {Number | string | undefined} */
55
+ result.priority = typeof resolved === 'string' && resolved.trim() !== '' && Number.isFinite(num) ? num : resolved;
56
+ }
57
+
58
+ const schedule = this.taskSchedule;
59
+ if (schedule) {
60
+ const { dueDate, followUpDate } = schedule;
61
+ if (dueDate !== undefined) result.dueDate = resolveDate(dueDate, scope);
62
+ if (followUpDate !== undefined) result.followUpDate = resolveDate(followUpDate, scope);
63
+ }
64
+
39
65
  return result;
40
66
  }
41
67
  }
@@ -58,15 +84,33 @@ export class FormatProcess {
58
84
  const documentation = this.process.behaviour.documentation;
59
85
  if (documentation && !elementApi.content.description) {
60
86
  const text = documentation[0]?.text;
61
- if (text) result.description = resolveValue(text, getFeelScope(elementApi.environment));
87
+ if (text) result.description = /** @type {string} */ (resolveValue(text, getFeelScope(elementApi.environment)));
62
88
  }
63
89
  return result;
64
90
  }
65
91
  }
66
92
 
93
+ /**
94
+ * Resolve a due/follow-up date to an ISO 8601 string. A FEEL temporal result (a luxon
95
+ * DateTime) or a Date is ISO-stringified — content is JSON-serialized on `getState()`, so a
96
+ * string is the shape that survives a recover round trip. Anything else passes through as-is.
97
+ * @param {any} value
98
+ * @param {any} scope
99
+ * @returns {string | undefined} Date as iso string
100
+ */
101
+ function resolveDate(value, scope) {
102
+ const resolved = resolveValue(value, scope);
103
+ if (typeof resolved?.toISO === 'function') return resolved.toISO();
104
+ if (resolved instanceof Date) return resolved.toISOString();
105
+ return resolved;
106
+ }
107
+
67
108
  /**
68
109
  * Resolve a candidate users/groups value to a string array. A FEEL expression may yield an
69
110
  * array directly, otherwise a comma-separated string is split.
111
+ * @param {any} value
112
+ * @param {any} scope
113
+ * @returns {string[] | undefined}
70
114
  */
71
115
  function resolveList(value, scope) {
72
116
  if (!value) return undefined;
@@ -22,6 +22,7 @@ import { Subscription } from './extensions/Subscription.js';
22
22
  * @property {any} [calledDecision]
23
23
  * @property {Function} [Service]
24
24
  * @property {Subscription} [subscription]
25
+ * @property {boolean} [isEmpty] no zeebe extension data found and nothing to format — the element needs no extension
25
26
  */
26
27
 
27
28
  /**
@@ -34,10 +35,15 @@ export function getExtensions(element, context) {
34
35
  const result = {};
35
36
  const isProcess = element.type === 'bpmn:Process';
36
37
 
37
- let assignmentDefinition;
38
+ let assignmentDefinition, priorityDefinition, taskSchedule;
39
+ let foundZeebe = false;
38
40
  const values = element.behaviour.extensionElements?.values;
39
41
  if (values) {
40
42
  for (const ext of values) {
43
+ // Any zeebe-namespaced extension element counts as found — including ones with no
44
+ // handler (e.g. the bare `zeebe:userTask` marker), which keep the element extension
45
+ // loaded so e.g. a native user task's completion payload is merged.
46
+ if (ext.$type.startsWith('zeebe:')) foundZeebe = true;
41
47
  switch (ext.$type) {
42
48
  case 'zeebe:IoMapping':
43
49
  result.io = new IoMapping(element, ext);
@@ -51,6 +57,12 @@ export function getExtensions(element, context) {
51
57
  case 'zeebe:AssignmentDefinition':
52
58
  assignmentDefinition = ext;
53
59
  break;
60
+ case 'zeebe:PriorityDefinition':
61
+ priorityDefinition = ext;
62
+ break;
63
+ case 'zeebe:TaskSchedule':
64
+ taskSchedule = ext;
65
+ break;
54
66
  case 'zeebe:ExecutionListeners':
55
67
  if (ext.listeners?.length) result.listeners = new ExecutionListeners(element, ext);
56
68
  break;
@@ -92,7 +104,22 @@ export function getExtensions(element, context) {
92
104
  );
93
105
  if (loopExtension?.outputCollection) result.loop = new LoopCharacteristics(loopExtension);
94
106
 
95
- result.format = isProcess ? new FormatProcess(element) : new FormatActivity(element, assignmentDefinition);
107
+ // Nothing zeebe-flavoured and nothing to format (documentation, a process-level timer start
108
+ // event's `scheduledStart`) — the element needs no extension. A call
109
+ // activity is never inert: its output unwrap/merge is type-driven (the standard BPMN
110
+ // `calledElement` attribute needs no zeebe elements). Ignored for processes — a called
111
+ // process always needs its inbound input promoted, whatever it carries itself.
112
+ result.isEmpty =
113
+ !foundZeebe &&
114
+ !result.subscription &&
115
+ !loopExtension &&
116
+ !element.behaviour.documentation &&
117
+ !(element.behaviour.scheduledStart && element.parent?.type === 'bpmn:Process') &&
118
+ element.type !== 'bpmn:CallActivity';
119
+
120
+ result.format = isProcess
121
+ ? new FormatProcess(element)
122
+ : new FormatActivity(element, assignmentDefinition, priorityDefinition, taskSchedule);
96
123
 
97
124
  return result;
98
125
  }
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ import { SubProcessExtensions } from './SubProcessExtensions.js';
5
5
  export { ElementExtensions, ProcessExtensions, SubProcessExtensions };
6
6
  export { FeelExpressions } from './Expressions.js';
7
7
  export { FeelScripts } from './extensions/FeelScripts.js';
8
+ export { TimerEventDefinition } from './TimerEventDefinition.js';
8
9
  export { isFeelExpression, stripFeel, evaluateFeel, evaluateFeelUnaryTest, resolveValue, getFeelScope } from './feel.js';
9
10
  export { JobService } from './extensions/TaskDefinition.js';
10
11
  export { ServiceError, FormatError } from './Errors.js';
@@ -18,20 +19,32 @@ export { ServiceError, FormatError } from './Errors.js';
18
19
 
19
20
  /**
20
21
  * Flow extensions factory. Pass it to the engine via the environment `extensions` option.
22
+ *
23
+ * Returns undefined for an element that carries no zeebe extension data and nothing else to
24
+ * format (no documentation, not a call activity) — bpmn-elements skips falsy extensions, so
25
+ * plain elements run untouched, with no broker subscriptions or format-queue traffic.
21
26
  * @param {import('bpmn-elements').Activity | import('bpmn-elements').Process} element
22
27
  * @param {import('bpmn-elements').ContextInstance} context
23
- * @returns {FlowExtension}
28
+ * @returns {FlowExtension | undefined}
24
29
  */
25
30
  export function extensions(element, context) {
26
31
  switch (element.type) {
27
32
  case 'bpmn:Process':
33
+ // A process is always attached: a called process needs its inbound input promoted
34
+ // even when it carries no zeebe extension data of its own.
28
35
  return new ProcessExtensions(element, context);
29
36
  case 'bpmn:SubProcess':
30
37
  case 'bpmn:AdHocSubProcess':
31
- case 'bpmn:Transaction':
32
- return new SubProcessExtensions(element, context);
33
- default:
34
- return new ElementExtensions(element, context);
38
+ case 'bpmn:Transaction': {
39
+ const subProcessExtensions = new SubProcessExtensions(element, context);
40
+ if (subProcessExtensions.extensions.isEmpty) return undefined;
41
+ return subProcessExtensions;
42
+ }
43
+ default: {
44
+ const elementExtensions = new ElementExtensions(element, context);
45
+ if (elementExtensions.extensions.isEmpty) return undefined;
46
+ return elementExtensions;
47
+ }
35
48
  }
36
49
  }
37
50
 
@@ -40,10 +53,17 @@ export function extensions(element, context) {
40
53
  *
41
54
  * Lifts extension data onto the places bpmn-elements expects to find it on the element
42
55
  * behaviour: the call activity's called process id (`zeebe:calledElement`) and the multi-instance
43
- * input collection/element (`zeebe:loopCharacteristics`).
56
+ * input collection/element (`zeebe:loopCharacteristics`). A timer start event's `timeCycle`
57
+ * (ISO 8601 or cron) is lifted as `scheduledStart`, so a scheduler can find it without running
58
+ * the flow.
44
59
  * @param {any} behaviour
45
60
  */
46
61
  export function extendFn(behaviour) {
62
+ if (behaviour.$type === 'bpmn:StartEvent' && Array.isArray(behaviour.eventDefinitions)) {
63
+ const timer = behaviour.eventDefinitions.find((ed) => ed?.type === 'bpmn:TimerEventDefinition' && ed.behaviour?.timeCycle);
64
+ if (timer) behaviour.scheduledStart = timer.behaviour.timeCycle;
65
+ }
66
+
47
67
  const values = behaviour.extensionElements?.values;
48
68
  if (Array.isArray(values)) {
49
69
  for (const ext of values) {
package/types/index.d.ts CHANGED
@@ -1,17 +1,24 @@
1
1
  declare module '@0dep/bpmn-extensions' {
2
+ import type { TimerEventDefinition as BaseTimerEventDefinition } from 'bpmn-elements';
2
3
  /**
3
4
  * A flow extension activated by bpmn-elements around an element's run.
4
5
  * */
5
6
  /**
6
7
  * Flow extensions factory. Pass it to the engine via the environment `extensions` option.
8
+ *
9
+ * Returns undefined for an element that carries no zeebe extension data and nothing else to
10
+ * format (no documentation, not a call activity) — bpmn-elements skips falsy extensions, so
11
+ * plain elements run untouched, with no broker subscriptions or format-queue traffic.
7
12
  * */
8
- export function extensions(element: import("bpmn-elements").Activity | import("bpmn-elements").Process, context: import("bpmn-elements").ContextInstance): FlowExtension;
13
+ export function extensions(element: import("bpmn-elements").Activity | import("bpmn-elements").Process, context: import("bpmn-elements").ContextInstance): FlowExtension | undefined;
9
14
  /**
10
15
  * Behaviour extend function for moddle-context-serializer.
11
16
  *
12
17
  * Lifts extension data onto the places bpmn-elements expects to find it on the element
13
18
  * behaviour: the call activity's called process id (`zeebe:calledElement`) and the multi-instance
14
- * input collection/element (`zeebe:loopCharacteristics`).
19
+ * input collection/element (`zeebe:loopCharacteristics`). A timer start event's `timeCycle`
20
+ * (ISO 8601 or cron) is lifted as `scheduledStart`, so a scheduler can find it without running
21
+ * the flow.
15
22
  * */
16
23
  export function extendFn(behaviour: any): void;
17
24
  /**
@@ -44,6 +51,20 @@ declare module '@0dep/bpmn-extensions' {
44
51
  *
45
52
  * */
46
53
  export function FeelScripts(): import("bpmn-elements").IScripts;
54
+ /**
55
+ * bpmn-elements timer event definition that also accepts a **cron** `timeCycle` (Camunda 8
56
+ * timer start events schedule with cron, e.g. `0 0 * * *`). An ISO 8601 interval is tried
57
+ * first; on failure the value is parsed as cron and the next run is the expiry.
58
+ *
59
+ * Install it via the type resolver so it replaces the bpmn-elements default:
60
+ * `TypeResolver({ ...elements, TimerEventDefinition })`.
61
+ */
62
+ export class TimerEventDefinition extends BaseTimerEventDefinition {
63
+ /**
64
+ * Supported timeCycle formats
65
+ * */
66
+ get supports(): string[];
67
+ }
47
68
  /**
48
69
  * `zeebe:taskDefinition`.
49
70
  *
@@ -210,22 +231,34 @@ declare module '@0dep/bpmn-extensions' {
210
231
  calledDecision?: any;
211
232
  Service?: Function | undefined;
212
233
  subscription?: Subscription | undefined;
234
+ /**
235
+ * no zeebe extension data found and nothing to format — the element needs no extension
236
+ */
237
+ isEmpty?: boolean | undefined;
213
238
  };
214
239
  /**
215
- * Format an activity on enter from its behaviour and extensions: documentation and,
216
- * for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups).
240
+ * Format an activity on enter from its behaviour and extensions: documentation, a top-level
241
+ * timer start event's `scheduledStart` (lifted by `extendFn`) and, for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups),
242
+ * `zeebe:priorityDefinition` (priority), and `zeebe:taskSchedule` (due/follow-up date).
217
243
  */
218
244
  class FormatActivity {
219
245
 
220
- constructor(activity: import("bpmn-elements").Activity, assignmentDefinition: any);
246
+ constructor(activity: import("bpmn-elements").Activity, assignmentDefinition: any, priorityDefinition?: any, taskSchedule?: any);
221
247
  activity: import("bpmn-elements").Activity;
222
248
  assignmentDefinition: any;
249
+ priorityDefinition: any;
250
+ taskSchedule: any;
223
251
 
224
252
  resolve(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Activity>): {
225
- description: any;
253
+ description: string;
254
+ scheduledStart: any;
226
255
  assignee: any;
227
- candidateUsers: any[];
228
- candidateGroups: any[];
256
+ candidateUsers: string[];
257
+ candidateGroups: string[];
258
+
259
+ priority: number | string | undefined;
260
+ dueDate: string | undefined;
261
+ followUpDate: string | undefined;
229
262
  };
230
263
  }
231
264
  /**
@@ -237,7 +270,7 @@ declare module '@0dep/bpmn-extensions' {
237
270
  process: import("bpmn-elements").Process;
238
271
 
239
272
  resolve(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Process>): {
240
- description: any;
273
+ description: string;
241
274
  };
242
275
  }
243
276
  /**
@@ -6,6 +6,7 @@
6
6
  "extendFn",
7
7
  "FeelExpressions",
8
8
  "FeelScripts",
9
+ "TimerEventDefinition",
9
10
  "JobService",
10
11
  "ElementExtensions",
11
12
  "ProcessExtensions",
@@ -32,6 +33,7 @@
32
33
  "../src/index.js",
33
34
  "../src/Expressions.js",
34
35
  "../src/extensions/FeelScripts.js",
36
+ "../src/TimerEventDefinition.js",
35
37
  "../src/extensions/TaskDefinition.js",
36
38
  "../src/ElementExtensions.js",
37
39
  "../src/ProcessExtensions.js",
@@ -64,8 +66,9 @@
64
66
  null,
65
67
  null,
66
68
  null,
69
+ null,
67
70
  null
68
71
  ],
69
- "mappings": ";;;;;;;iBAwBgBA,UAAUA;;;;;;;;iBAqBVC,QAAQA;;;;;;;;;;;;;;;;iBClCRC,eAAeA;;;;;;;;;;;;;;iBCEfC,WAAWA;;;;;;;;;;;;;;;iBCIXC,UAAUA;cAAVA,UAAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCEbC,iBAAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCZjBC,iBAAiBA;;;;;;;;;;;;;;;;;;;cCIjBC,oBAAoBA;;;;;;;iBCEjBC,gBAAgBA;;;;iBAShBC,SAASA;;;;;;iBAUTC,YAAYA;;;;;;;;iBAYZC,qBAAqBA;;;;;;iBAYrBC,YAAYA;;;;;;;;;;iBAgBZC,YAAYA;cCxEfC,YAAYA;;;;;;;cAQZC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;OCFXC,cAAcA;;;;;;;;;;;;;;;;OAuCdC,aAAaA;;;;;;;;;;;;;;;;;;;;OChCbC,SAASA;;;;;;;;;;;;;;;;;;;;;;;OCPTC,WAAWA;;;;;;;;;;;OCEXC,UAAUA;;;;;;;;;;;;;;;OCIVC,kBAAkBA;;;;;;;;;;;;;;;;;;;OCFlBC,IAAIA;;;;;;;;;;;;;;;;;;;;;;OCGJC,mBAAmBA;;;;;;;;;;;;;;;;;;;OCHnBC,YAAYA",
72
+ "mappings": ";;;;;;;;;;;;iBA6BgBA,UAAUA;;;;;;;;;;iBA+BVC,QAAQA;;;;;;;;;;;;;;;;iBCjDRC,eAAeA;;;;;;;;;;;;;;iBCEfC,WAAWA;;;;;;;;;cCFdC,oBAAoBA;;;;;;;;;;;;;;;;;;;;iBCMjBC,UAAUA;cAAVA,UAAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCEbC,iBAAiBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCZjBC,iBAAiBA;;;;;;;;;;;;;;;;;;;cCIjBC,oBAAoBA;;;;;;;iBCEjBC,gBAAgBA;;;;iBAShBC,SAASA;;;;;;iBAUTC,YAAYA;;;;;;;;iBAYZC,qBAAqBA;;;;;;iBAYrBC,YAAYA;;;;;;;;;;iBAgBZC,YAAYA;cCxEfC,YAAYA;;;;;;;cAQZC,WAAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OCDXC,cAAcA;;;;;;;;;;;;;;;;;;;;;;;OAgEdC,aAAaA;;;;;;;;;;;;;;;;;;;;OC1DbC,SAASA;;;;;;;;;;;;;;;;;;;;;;;OCPTC,WAAWA;;;;;;;;;;;OCEXC,UAAUA;;;;;;;;;;;;;;;OCIVC,kBAAkBA;;;;;;;;;;;;;;;;;;;OCFlBC,IAAIA;;;;;;;;;;;;;;;;;;;;;;OCGJC,mBAAmBA;;;;;;;;;;;;;;;;;;;OCHnBC,YAAYA",
70
73
  "ignoreList": []
71
74
  }