@0dep/bpmn-extensions 0.0.4 → 0.0.5
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 +8 -2
- package/dist/index.js +89 -44
- package/package.json +2 -2
- package/src/extensions/formatters.js +43 -4
- package/src/getExtensions.js +23 -2
- package/src/index.js +17 -5
- package/types/index.d.ts +22 -7
- package/types/index.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @0dep/bpmn-extensions
|
|
2
2
|
|
|
3
|
-
[](https://github.com/zerodep/bpmn-extensions/actions/workflows/build-latest.yaml)[](https://coveralls.io/github/zerodep/bpmn-extensions?branch=main)
|
|
3
|
+
[](https://github.com/zerodep/bpmn-extensions/actions/workflows/build-latest.yaml)[](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.
|
|
@@ -38,6 +38,9 @@ npm install @0dep/bpmn-extensions bpmn-elements
|
|
|
38
38
|
process's input becomes variables local to its children.
|
|
39
39
|
- **`zeebe:taskHeaders`** / **`zeebe:properties`** — exposed on the element content.
|
|
40
40
|
- **`zeebe:assignmentDefinition`** — assignee / candidate users / groups for user tasks.
|
|
41
|
+
- **`zeebe:priorityDefinition`** / **`zeebe:taskSchedule`** — a user task's `priority` and
|
|
42
|
+
`dueDate` / `followUpDate` resolved onto the element content for a task list to order and
|
|
43
|
+
remind by; FEEL date results are exposed as ISO 8601 strings.
|
|
41
44
|
- **`zeebe:formDefinition`** — resolves a user task's form (`formId` / `formKey` / `externalReference`,
|
|
42
45
|
`bindingType`) onto the element content for a task list to render.
|
|
43
46
|
- **`zeebe:executionListeners`** — blocking start/end job workers around an element, called as
|
|
@@ -54,6 +57,9 @@ npm install @0dep/bpmn-extensions bpmn-elements
|
|
|
54
57
|
## API
|
|
55
58
|
|
|
56
59
|
- `extensions(element, context)` — the flow extensions factory; pass it as an environment extension.
|
|
60
|
+
Elements that carry no zeebe extension data (and nothing else to format) get no extension attached
|
|
61
|
+
at all and run untouched; call activities and processes are always attached, since they propagate
|
|
62
|
+
io across process boundaries.
|
|
57
63
|
- `extendFn(behaviour)` — moddle-context-serializer behaviour extender (lifts the extension data
|
|
58
64
|
bpmn-elements expects on the behaviour: call activity process id and multi-instance collection).
|
|
59
65
|
- `FeelExpressions()` — a bpmn-elements `IExpressions` implementation backed by FEEL.
|
|
@@ -69,7 +75,7 @@ Wire it into a bpmn-elements definition (see `test/helpers/testHelpers.js` for t
|
|
|
69
75
|
```javascript
|
|
70
76
|
import { createRequire } from 'node:module';
|
|
71
77
|
import { strict as assert } from 'node:assert';
|
|
72
|
-
import BpmnModdle from 'bpmn-moddle';
|
|
78
|
+
import { BpmnModdle } from 'bpmn-moddle';
|
|
73
79
|
import * as elements from 'bpmn-elements';
|
|
74
80
|
import { Serializer, TypeResolver } from 'moddle-context-serializer';
|
|
75
81
|
import { extensions, extendFn, FeelExpressions, FeelScripts } from '@0dep/bpmn-extensions';
|
package/dist/index.js
CHANGED
|
@@ -11609,9 +11609,9 @@ function getSpecializer(spec) {
|
|
|
11609
11609
|
*
|
|
11610
11610
|
* @return {T[]}
|
|
11611
11611
|
*/
|
|
11612
|
-
const nativeHasOwnProperty
|
|
11612
|
+
const nativeHasOwnProperty = Object.prototype.hasOwnProperty;
|
|
11613
11613
|
|
|
11614
|
-
function isNil$
|
|
11614
|
+
function isNil$1(obj) {
|
|
11615
11615
|
return obj == null;
|
|
11616
11616
|
}
|
|
11617
11617
|
|
|
@@ -11623,8 +11623,8 @@ function isNil$2(obj) {
|
|
|
11623
11623
|
*
|
|
11624
11624
|
* @return {Boolean}
|
|
11625
11625
|
*/
|
|
11626
|
-
function has
|
|
11627
|
-
return !isNil$
|
|
11626
|
+
function has(target, key) {
|
|
11627
|
+
return !isNil$1(target) && nativeHasOwnProperty.call(target, key);
|
|
11628
11628
|
}
|
|
11629
11629
|
|
|
11630
11630
|
let nextTagID = 0;
|
|
@@ -13107,7 +13107,7 @@ class VariableContext {
|
|
|
13107
13107
|
continue;
|
|
13108
13108
|
}
|
|
13109
13109
|
|
|
13110
|
-
if (has
|
|
13110
|
+
if (has(merged, key)) {
|
|
13111
13111
|
|
|
13112
13112
|
// deep merge nested contexts
|
|
13113
13113
|
merged[key] = this.__merge(merged[key], value);
|
|
@@ -13241,7 +13241,7 @@ class Variables {
|
|
|
13241
13241
|
return null;
|
|
13242
13242
|
}
|
|
13243
13243
|
|
|
13244
|
-
if (!isNil$
|
|
13244
|
+
if (!isNil$1(scope.value)) {
|
|
13245
13245
|
return scope.value;
|
|
13246
13246
|
}
|
|
13247
13247
|
}
|
|
@@ -13871,33 +13871,6 @@ const parser = LRParser.deserialize({
|
|
|
13871
13871
|
tokenPrec: 2660
|
|
13872
13872
|
});
|
|
13873
13873
|
|
|
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
13874
|
function isNil(e) {
|
|
13902
13875
|
return e === null || e === undefined;
|
|
13903
13876
|
}
|
|
@@ -16239,16 +16212,21 @@ function getFeelScope(environment, localVariables) {
|
|
|
16239
16212
|
|
|
16240
16213
|
/**
|
|
16241
16214
|
* Format an activity on enter from its behaviour and extensions: documentation and,
|
|
16242
|
-
* for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups)
|
|
16215
|
+
* for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups),
|
|
16216
|
+
* `zeebe:priorityDefinition` (priority), and `zeebe:taskSchedule` (due/follow-up date).
|
|
16243
16217
|
*/
|
|
16244
16218
|
class FormatActivity {
|
|
16245
16219
|
/**
|
|
16246
16220
|
* @param {import('bpmn-elements').Activity} activity
|
|
16247
16221
|
* @param {any} assignmentDefinition
|
|
16222
|
+
* @param {any} [priorityDefinition]
|
|
16223
|
+
* @param {any} [taskSchedule]
|
|
16248
16224
|
*/
|
|
16249
|
-
constructor(activity, assignmentDefinition) {
|
|
16225
|
+
constructor(activity, assignmentDefinition, priorityDefinition, taskSchedule) {
|
|
16250
16226
|
this.activity = activity;
|
|
16251
16227
|
this.assignmentDefinition = assignmentDefinition;
|
|
16228
|
+
this.priorityDefinition = priorityDefinition;
|
|
16229
|
+
this.taskSchedule = taskSchedule;
|
|
16252
16230
|
}
|
|
16253
16231
|
/**
|
|
16254
16232
|
* @param {import('bpmn-elements').IApi<import('bpmn-elements').Activity>} elementApi
|
|
@@ -16260,7 +16238,7 @@ class FormatActivity {
|
|
|
16260
16238
|
const documentation = this.activity.behaviour.documentation;
|
|
16261
16239
|
if (documentation && !elementApi.content.description) {
|
|
16262
16240
|
const text = documentation[0]?.text;
|
|
16263
|
-
if (text) result.description = resolveValue(text, scope);
|
|
16241
|
+
if (text) result.description = /** @type {string} */ (resolveValue(text, scope));
|
|
16264
16242
|
}
|
|
16265
16243
|
|
|
16266
16244
|
const assignment = this.assignmentDefinition;
|
|
@@ -16273,6 +16251,22 @@ class FormatActivity {
|
|
|
16273
16251
|
if (groups) result.candidateGroups = groups;
|
|
16274
16252
|
}
|
|
16275
16253
|
|
|
16254
|
+
const priority = this.priorityDefinition?.priority;
|
|
16255
|
+
if (priority !== undefined) {
|
|
16256
|
+
const resolved = resolveValue(priority, scope);
|
|
16257
|
+
// A static attribute value is a string — expose a numeric one as a number (like retries).
|
|
16258
|
+
const num = Number(resolved);
|
|
16259
|
+
/** @type {Number | string | undefined} */
|
|
16260
|
+
result.priority = typeof resolved === 'string' && resolved.trim() !== '' && Number.isFinite(num) ? num : resolved;
|
|
16261
|
+
}
|
|
16262
|
+
|
|
16263
|
+
const schedule = this.taskSchedule;
|
|
16264
|
+
if (schedule) {
|
|
16265
|
+
const { dueDate, followUpDate } = schedule;
|
|
16266
|
+
if (dueDate !== undefined) result.dueDate = resolveDate(dueDate, scope);
|
|
16267
|
+
if (followUpDate !== undefined) result.followUpDate = resolveDate(followUpDate, scope);
|
|
16268
|
+
}
|
|
16269
|
+
|
|
16276
16270
|
return result;
|
|
16277
16271
|
}
|
|
16278
16272
|
}
|
|
@@ -16295,15 +16289,33 @@ class FormatProcess {
|
|
|
16295
16289
|
const documentation = this.process.behaviour.documentation;
|
|
16296
16290
|
if (documentation && !elementApi.content.description) {
|
|
16297
16291
|
const text = documentation[0]?.text;
|
|
16298
|
-
if (text) result.description = resolveValue(text, getFeelScope(elementApi.environment));
|
|
16292
|
+
if (text) result.description = /** @type {string} */ (resolveValue(text, getFeelScope(elementApi.environment)));
|
|
16299
16293
|
}
|
|
16300
16294
|
return result;
|
|
16301
16295
|
}
|
|
16302
16296
|
}
|
|
16303
16297
|
|
|
16298
|
+
/**
|
|
16299
|
+
* Resolve a due/follow-up date to an ISO 8601 string. A FEEL temporal result (a luxon
|
|
16300
|
+
* DateTime) or a Date is ISO-stringified — content is JSON-serialized on `getState()`, so a
|
|
16301
|
+
* string is the shape that survives a recover round trip. Anything else passes through as-is.
|
|
16302
|
+
* @param {any} value
|
|
16303
|
+
* @param {any} scope
|
|
16304
|
+
* @returns {string | undefined} Date as iso string
|
|
16305
|
+
*/
|
|
16306
|
+
function resolveDate(value, scope) {
|
|
16307
|
+
const resolved = resolveValue(value, scope);
|
|
16308
|
+
if (typeof resolved?.toISO === 'function') return resolved.toISO();
|
|
16309
|
+
if (resolved instanceof Date) return resolved.toISOString();
|
|
16310
|
+
return resolved;
|
|
16311
|
+
}
|
|
16312
|
+
|
|
16304
16313
|
/**
|
|
16305
16314
|
* Resolve a candidate users/groups value to a string array. A FEEL expression may yield an
|
|
16306
16315
|
* array directly, otherwise a comma-separated string is split.
|
|
16316
|
+
* @param {any} value
|
|
16317
|
+
* @param {any} scope
|
|
16318
|
+
* @returns {string[] | undefined}
|
|
16307
16319
|
*/
|
|
16308
16320
|
function resolveList(value, scope) {
|
|
16309
16321
|
if (!value) return undefined;
|
|
@@ -16652,6 +16664,7 @@ class Subscription {
|
|
|
16652
16664
|
* @property {any} [calledDecision]
|
|
16653
16665
|
* @property {Function} [Service]
|
|
16654
16666
|
* @property {Subscription} [subscription]
|
|
16667
|
+
* @property {boolean} [isEmpty] no zeebe extension data found and nothing to format — the element needs no extension
|
|
16655
16668
|
*/
|
|
16656
16669
|
|
|
16657
16670
|
/**
|
|
@@ -16664,10 +16677,15 @@ function getExtensions(element, context) {
|
|
|
16664
16677
|
const result = {};
|
|
16665
16678
|
const isProcess = element.type === 'bpmn:Process';
|
|
16666
16679
|
|
|
16667
|
-
let assignmentDefinition;
|
|
16680
|
+
let assignmentDefinition, priorityDefinition, taskSchedule;
|
|
16681
|
+
let foundZeebe = false;
|
|
16668
16682
|
const values = element.behaviour.extensionElements?.values;
|
|
16669
16683
|
if (values) {
|
|
16670
16684
|
for (const ext of values) {
|
|
16685
|
+
// Any zeebe-namespaced extension element counts as found — including ones with no
|
|
16686
|
+
// handler (e.g. the bare `zeebe:userTask` marker), which keep the element extension
|
|
16687
|
+
// loaded so e.g. a native user task's completion payload is merged.
|
|
16688
|
+
if (ext.$type.startsWith('zeebe:')) foundZeebe = true;
|
|
16671
16689
|
switch (ext.$type) {
|
|
16672
16690
|
case 'zeebe:IoMapping':
|
|
16673
16691
|
result.io = new IoMapping(element, ext);
|
|
@@ -16681,6 +16699,12 @@ function getExtensions(element, context) {
|
|
|
16681
16699
|
case 'zeebe:AssignmentDefinition':
|
|
16682
16700
|
assignmentDefinition = ext;
|
|
16683
16701
|
break;
|
|
16702
|
+
case 'zeebe:PriorityDefinition':
|
|
16703
|
+
priorityDefinition = ext;
|
|
16704
|
+
break;
|
|
16705
|
+
case 'zeebe:TaskSchedule':
|
|
16706
|
+
taskSchedule = ext;
|
|
16707
|
+
break;
|
|
16684
16708
|
case 'zeebe:ExecutionListeners':
|
|
16685
16709
|
if (ext.listeners?.length) result.listeners = new ExecutionListeners(element, ext);
|
|
16686
16710
|
break;
|
|
@@ -16722,7 +16746,16 @@ function getExtensions(element, context) {
|
|
|
16722
16746
|
);
|
|
16723
16747
|
if (loopExtension?.outputCollection) result.loop = new LoopCharacteristics(loopExtension);
|
|
16724
16748
|
|
|
16725
|
-
|
|
16749
|
+
// Nothing zeebe-flavoured and nothing to format — the element needs no extension. A call
|
|
16750
|
+
// activity is never inert: its output unwrap/merge is type-driven (the standard BPMN
|
|
16751
|
+
// `calledElement` attribute needs no zeebe elements). Ignored for processes — a called
|
|
16752
|
+
// process always needs its inbound input promoted, whatever it carries itself.
|
|
16753
|
+
result.isEmpty =
|
|
16754
|
+
!foundZeebe && !result.subscription && !loopExtension && !element.behaviour.documentation && element.type !== 'bpmn:CallActivity';
|
|
16755
|
+
|
|
16756
|
+
result.format = isProcess
|
|
16757
|
+
? new FormatProcess(element)
|
|
16758
|
+
: new FormatActivity(element, assignmentDefinition, priorityDefinition, taskSchedule);
|
|
16726
16759
|
|
|
16727
16760
|
return result;
|
|
16728
16761
|
}
|
|
@@ -17099,20 +17132,32 @@ function getScriptExpression(activity) {
|
|
|
17099
17132
|
|
|
17100
17133
|
/**
|
|
17101
17134
|
* Flow extensions factory. Pass it to the engine via the environment `extensions` option.
|
|
17135
|
+
*
|
|
17136
|
+
* Returns undefined for an element that carries no zeebe extension data and nothing else to
|
|
17137
|
+
* format (no documentation, not a call activity) — bpmn-elements skips falsy extensions, so
|
|
17138
|
+
* plain elements run untouched, with no broker subscriptions or format-queue traffic.
|
|
17102
17139
|
* @param {import('bpmn-elements').Activity | import('bpmn-elements').Process} element
|
|
17103
17140
|
* @param {import('bpmn-elements').ContextInstance} context
|
|
17104
|
-
* @returns {FlowExtension}
|
|
17141
|
+
* @returns {FlowExtension | undefined}
|
|
17105
17142
|
*/
|
|
17106
17143
|
function extensions(element, context) {
|
|
17107
17144
|
switch (element.type) {
|
|
17108
17145
|
case 'bpmn:Process':
|
|
17146
|
+
// A process is always attached: a called process needs its inbound input promoted
|
|
17147
|
+
// even when it carries no zeebe extension data of its own.
|
|
17109
17148
|
return new ProcessExtensions(element, context);
|
|
17110
17149
|
case 'bpmn:SubProcess':
|
|
17111
17150
|
case 'bpmn:AdHocSubProcess':
|
|
17112
|
-
case 'bpmn:Transaction':
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
return
|
|
17151
|
+
case 'bpmn:Transaction': {
|
|
17152
|
+
const subProcessExtensions = new SubProcessExtensions(element, context);
|
|
17153
|
+
if (subProcessExtensions.extensions.isEmpty) return undefined;
|
|
17154
|
+
return subProcessExtensions;
|
|
17155
|
+
}
|
|
17156
|
+
default: {
|
|
17157
|
+
const elementExtensions = new ElementExtensions(element, context);
|
|
17158
|
+
if (elementExtensions.extensions.isEmpty) return undefined;
|
|
17159
|
+
return elementExtensions;
|
|
17160
|
+
}
|
|
17116
17161
|
}
|
|
17117
17162
|
}
|
|
17118
17163
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0dep/bpmn-extensions",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
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",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@types/mocha": "^10.0.10",
|
|
70
70
|
"@types/node": "^26.1.1",
|
|
71
71
|
"bpmn-elements": "^18.0.12",
|
|
72
|
-
"bpmn-moddle": "^
|
|
72
|
+
"bpmn-moddle": "^10.1.0",
|
|
73
73
|
"c8": "^12.0.0",
|
|
74
74
|
"chai": "^6.2.2",
|
|
75
75
|
"debug": "^4.4.3",
|
|
@@ -2,16 +2,21 @@ import { resolveValue, getFeelScope } from '../feel.js';
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Format an activity on enter from its behaviour and extensions: documentation and,
|
|
5
|
-
* for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups)
|
|
5
|
+
* 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,7 +28,7 @@ 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
|
|
|
29
34
|
const assignment = this.assignmentDefinition;
|
|
@@ -36,6 +41,22 @@ export class FormatActivity {
|
|
|
36
41
|
if (groups) result.candidateGroups = groups;
|
|
37
42
|
}
|
|
38
43
|
|
|
44
|
+
const priority = this.priorityDefinition?.priority;
|
|
45
|
+
if (priority !== undefined) {
|
|
46
|
+
const resolved = resolveValue(priority, scope);
|
|
47
|
+
// A static attribute value is a string — expose a numeric one as a number (like retries).
|
|
48
|
+
const num = Number(resolved);
|
|
49
|
+
/** @type {Number | string | undefined} */
|
|
50
|
+
result.priority = typeof resolved === 'string' && resolved.trim() !== '' && Number.isFinite(num) ? num : resolved;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const schedule = this.taskSchedule;
|
|
54
|
+
if (schedule) {
|
|
55
|
+
const { dueDate, followUpDate } = schedule;
|
|
56
|
+
if (dueDate !== undefined) result.dueDate = resolveDate(dueDate, scope);
|
|
57
|
+
if (followUpDate !== undefined) result.followUpDate = resolveDate(followUpDate, scope);
|
|
58
|
+
}
|
|
59
|
+
|
|
39
60
|
return result;
|
|
40
61
|
}
|
|
41
62
|
}
|
|
@@ -58,15 +79,33 @@ export class FormatProcess {
|
|
|
58
79
|
const documentation = this.process.behaviour.documentation;
|
|
59
80
|
if (documentation && !elementApi.content.description) {
|
|
60
81
|
const text = documentation[0]?.text;
|
|
61
|
-
if (text) result.description = resolveValue(text, getFeelScope(elementApi.environment));
|
|
82
|
+
if (text) result.description = /** @type {string} */ (resolveValue(text, getFeelScope(elementApi.environment)));
|
|
62
83
|
}
|
|
63
84
|
return result;
|
|
64
85
|
}
|
|
65
86
|
}
|
|
66
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a due/follow-up date to an ISO 8601 string. A FEEL temporal result (a luxon
|
|
90
|
+
* DateTime) or a Date is ISO-stringified — content is JSON-serialized on `getState()`, so a
|
|
91
|
+
* string is the shape that survives a recover round trip. Anything else passes through as-is.
|
|
92
|
+
* @param {any} value
|
|
93
|
+
* @param {any} scope
|
|
94
|
+
* @returns {string | undefined} Date as iso string
|
|
95
|
+
*/
|
|
96
|
+
function resolveDate(value, scope) {
|
|
97
|
+
const resolved = resolveValue(value, scope);
|
|
98
|
+
if (typeof resolved?.toISO === 'function') return resolved.toISO();
|
|
99
|
+
if (resolved instanceof Date) return resolved.toISOString();
|
|
100
|
+
return resolved;
|
|
101
|
+
}
|
|
102
|
+
|
|
67
103
|
/**
|
|
68
104
|
* Resolve a candidate users/groups value to a string array. A FEEL expression may yield an
|
|
69
105
|
* array directly, otherwise a comma-separated string is split.
|
|
106
|
+
* @param {any} value
|
|
107
|
+
* @param {any} scope
|
|
108
|
+
* @returns {string[] | undefined}
|
|
70
109
|
*/
|
|
71
110
|
function resolveList(value, scope) {
|
|
72
111
|
if (!value) return undefined;
|
package/src/getExtensions.js
CHANGED
|
@@ -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,16 @@ export function getExtensions(element, context) {
|
|
|
92
104
|
);
|
|
93
105
|
if (loopExtension?.outputCollection) result.loop = new LoopCharacteristics(loopExtension);
|
|
94
106
|
|
|
95
|
-
|
|
107
|
+
// Nothing zeebe-flavoured and nothing to format — the element needs no extension. A call
|
|
108
|
+
// activity is never inert: its output unwrap/merge is type-driven (the standard BPMN
|
|
109
|
+
// `calledElement` attribute needs no zeebe elements). Ignored for processes — a called
|
|
110
|
+
// process always needs its inbound input promoted, whatever it carries itself.
|
|
111
|
+
result.isEmpty =
|
|
112
|
+
!foundZeebe && !result.subscription && !loopExtension && !element.behaviour.documentation && element.type !== 'bpmn:CallActivity';
|
|
113
|
+
|
|
114
|
+
result.format = isProcess
|
|
115
|
+
? new FormatProcess(element)
|
|
116
|
+
: new FormatActivity(element, assignmentDefinition, priorityDefinition, taskSchedule);
|
|
96
117
|
|
|
97
118
|
return result;
|
|
98
119
|
}
|
package/src/index.js
CHANGED
|
@@ -18,20 +18,32 @@ export { ServiceError, FormatError } from './Errors.js';
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Flow extensions factory. Pass it to the engine via the environment `extensions` option.
|
|
21
|
+
*
|
|
22
|
+
* Returns undefined for an element that carries no zeebe extension data and nothing else to
|
|
23
|
+
* format (no documentation, not a call activity) — bpmn-elements skips falsy extensions, so
|
|
24
|
+
* plain elements run untouched, with no broker subscriptions or format-queue traffic.
|
|
21
25
|
* @param {import('bpmn-elements').Activity | import('bpmn-elements').Process} element
|
|
22
26
|
* @param {import('bpmn-elements').ContextInstance} context
|
|
23
|
-
* @returns {FlowExtension}
|
|
27
|
+
* @returns {FlowExtension | undefined}
|
|
24
28
|
*/
|
|
25
29
|
export function extensions(element, context) {
|
|
26
30
|
switch (element.type) {
|
|
27
31
|
case 'bpmn:Process':
|
|
32
|
+
// A process is always attached: a called process needs its inbound input promoted
|
|
33
|
+
// even when it carries no zeebe extension data of its own.
|
|
28
34
|
return new ProcessExtensions(element, context);
|
|
29
35
|
case 'bpmn:SubProcess':
|
|
30
36
|
case 'bpmn:AdHocSubProcess':
|
|
31
|
-
case 'bpmn:Transaction':
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return
|
|
37
|
+
case 'bpmn:Transaction': {
|
|
38
|
+
const subProcessExtensions = new SubProcessExtensions(element, context);
|
|
39
|
+
if (subProcessExtensions.extensions.isEmpty) return undefined;
|
|
40
|
+
return subProcessExtensions;
|
|
41
|
+
}
|
|
42
|
+
default: {
|
|
43
|
+
const elementExtensions = new ElementExtensions(element, context);
|
|
44
|
+
if (elementExtensions.extensions.isEmpty) return undefined;
|
|
45
|
+
return elementExtensions;
|
|
46
|
+
}
|
|
35
47
|
}
|
|
36
48
|
}
|
|
37
49
|
|
package/types/index.d.ts
CHANGED
|
@@ -4,8 +4,12 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
4
4
|
* */
|
|
5
5
|
/**
|
|
6
6
|
* Flow extensions factory. Pass it to the engine via the environment `extensions` option.
|
|
7
|
+
*
|
|
8
|
+
* Returns undefined for an element that carries no zeebe extension data and nothing else to
|
|
9
|
+
* format (no documentation, not a call activity) — bpmn-elements skips falsy extensions, so
|
|
10
|
+
* plain elements run untouched, with no broker subscriptions or format-queue traffic.
|
|
7
11
|
* */
|
|
8
|
-
export function extensions(element: import("bpmn-elements").Activity | import("bpmn-elements").Process, context: import("bpmn-elements").ContextInstance): FlowExtension;
|
|
12
|
+
export function extensions(element: import("bpmn-elements").Activity | import("bpmn-elements").Process, context: import("bpmn-elements").ContextInstance): FlowExtension | undefined;
|
|
9
13
|
/**
|
|
10
14
|
* Behaviour extend function for moddle-context-serializer.
|
|
11
15
|
*
|
|
@@ -210,22 +214,33 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
210
214
|
calledDecision?: any;
|
|
211
215
|
Service?: Function | undefined;
|
|
212
216
|
subscription?: Subscription | undefined;
|
|
217
|
+
/**
|
|
218
|
+
* no zeebe extension data found and nothing to format — the element needs no extension
|
|
219
|
+
*/
|
|
220
|
+
isEmpty?: boolean | undefined;
|
|
213
221
|
};
|
|
214
222
|
/**
|
|
215
223
|
* Format an activity on enter from its behaviour and extensions: documentation and,
|
|
216
|
-
* for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups)
|
|
224
|
+
* for user tasks, the `zeebe:assignmentDefinition` (assignee, candidate users/groups),
|
|
225
|
+
* `zeebe:priorityDefinition` (priority), and `zeebe:taskSchedule` (due/follow-up date).
|
|
217
226
|
*/
|
|
218
227
|
class FormatActivity {
|
|
219
228
|
|
|
220
|
-
constructor(activity: import("bpmn-elements").Activity, assignmentDefinition: any);
|
|
229
|
+
constructor(activity: import("bpmn-elements").Activity, assignmentDefinition: any, priorityDefinition?: any, taskSchedule?: any);
|
|
221
230
|
activity: import("bpmn-elements").Activity;
|
|
222
231
|
assignmentDefinition: any;
|
|
232
|
+
priorityDefinition: any;
|
|
233
|
+
taskSchedule: any;
|
|
223
234
|
|
|
224
235
|
resolve(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Activity>): {
|
|
225
|
-
description:
|
|
236
|
+
description: string;
|
|
226
237
|
assignee: any;
|
|
227
|
-
candidateUsers:
|
|
228
|
-
candidateGroups:
|
|
238
|
+
candidateUsers: string[];
|
|
239
|
+
candidateGroups: string[];
|
|
240
|
+
|
|
241
|
+
priority: number | string | undefined;
|
|
242
|
+
dueDate: string | undefined;
|
|
243
|
+
followUpDate: string | undefined;
|
|
229
244
|
};
|
|
230
245
|
}
|
|
231
246
|
/**
|
|
@@ -237,7 +252,7 @@ declare module '@0dep/bpmn-extensions' {
|
|
|
237
252
|
process: import("bpmn-elements").Process;
|
|
238
253
|
|
|
239
254
|
resolve(elementApi: import("bpmn-elements").IApi<import("bpmn-elements").Process>): {
|
|
240
|
-
description:
|
|
255
|
+
description: string;
|
|
241
256
|
};
|
|
242
257
|
}
|
|
243
258
|
/**
|
package/types/index.d.ts.map
CHANGED
|
@@ -66,6 +66,6 @@
|
|
|
66
66
|
null,
|
|
67
67
|
null
|
|
68
68
|
],
|
|
69
|
-
"mappings": "
|
|
69
|
+
"mappings": ";;;;;;;;;;;iBA4BgBA,UAAUA;;;;;;;;iBA6BVC,QAAQA;;;;;;;;;;;;;;;;iBC9CRC,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OCDXC,cAAcA;;;;;;;;;;;;;;;;;;;;;;OA2DdC,aAAaA;;;;;;;;;;;;;;;;;;;;OCrDbC,SAASA;;;;;;;;;;;;;;;;;;;;;;;OCPTC,WAAWA;;;;;;;;;;;OCEXC,UAAUA;;;;;;;;;;;;;;;OCIVC,kBAAkBA;;;;;;;;;;;;;;;;;;;OCFlBC,IAAIA;;;;;;;;;;;;;;;;;;;;;;OCGJC,mBAAmBA;;;;;;;;;;;;;;;;;;;OCHnBC,YAAYA",
|
|
70
70
|
"ignoreList": []
|
|
71
71
|
}
|