@bpmnkit/core 0.0.24 → 0.0.25
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.
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { BpmnAssociation, BpmnDefinitions, BpmnFlowElement, BpmnSequenceFlow, BpmnTextAnnotation } from "./bpmn-model.js";
|
|
1
|
+
import type { BpmnAssociation, BpmnDefinitions, BpmnError, BpmnEscalation, BpmnFlowElement, BpmnMessage, BpmnSequenceFlow, BpmnSignal, BpmnTextAnnotation } from "./bpmn-model.js";
|
|
2
2
|
import type { RestConnectorConfig } from "./rest-connector.js";
|
|
3
3
|
/** Options shared by all element methods. */
|
|
4
4
|
export interface ElementOptions {
|
|
5
5
|
name?: string;
|
|
6
|
+
isForCompensation?: boolean;
|
|
6
7
|
}
|
|
7
8
|
/** Options for creating a start event. */
|
|
8
9
|
export interface StartEventOptions extends ElementOptions {
|
|
@@ -25,6 +26,11 @@ export interface StartEventOptions extends ElementOptions {
|
|
|
25
26
|
modelerTemplateVersion?: string;
|
|
26
27
|
/** Zeebe modeler template icon (data URI). */
|
|
27
28
|
modelerTemplateIcon?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Non-interrupting flag — only meaningful for start events inside event sub-processes.
|
|
31
|
+
* Pass `false` to emit `isInterrupting="false"`. Omit for the default interrupting behavior.
|
|
32
|
+
*/
|
|
33
|
+
isInterrupting?: boolean;
|
|
28
34
|
}
|
|
29
35
|
/** Options for creating a service task. */
|
|
30
36
|
export interface ServiceTaskOptions {
|
|
@@ -53,6 +59,8 @@ export interface ServiceTaskOptions {
|
|
|
53
59
|
modelerTemplateVersion?: string;
|
|
54
60
|
/** Zeebe modeler template icon (data URI). */
|
|
55
61
|
modelerTemplateIcon?: string;
|
|
62
|
+
/** Mark this task as a compensation handler. */
|
|
63
|
+
isForCompensation?: boolean;
|
|
56
64
|
}
|
|
57
65
|
/** Options for creating a script task. */
|
|
58
66
|
export interface ScriptTaskOptions {
|
|
@@ -62,6 +70,8 @@ export interface ScriptTaskOptions {
|
|
|
62
70
|
expression: string;
|
|
63
71
|
/** Variable name to store the result. */
|
|
64
72
|
resultVariable: string;
|
|
73
|
+
/** Mark this task as a compensation handler. */
|
|
74
|
+
isForCompensation?: boolean;
|
|
65
75
|
}
|
|
66
76
|
/** Options for creating a user task. */
|
|
67
77
|
export interface UserTaskOptions {
|
|
@@ -71,6 +81,8 @@ export interface UserTaskOptions {
|
|
|
71
81
|
formId?: string;
|
|
72
82
|
/** Emit <zeebe:userTask /> to mark as a Camunda 8 native user task. */
|
|
73
83
|
zeebeUserTask?: boolean;
|
|
84
|
+
/** Mark this task as a compensation handler. */
|
|
85
|
+
isForCompensation?: boolean;
|
|
74
86
|
}
|
|
75
87
|
/** Options for creating a call activity. */
|
|
76
88
|
export interface CallActivityOptions {
|
|
@@ -80,6 +92,8 @@ export interface CallActivityOptions {
|
|
|
80
92
|
processId: string;
|
|
81
93
|
/** Whether to propagate all child variables. */
|
|
82
94
|
propagateAllChildVariables?: boolean;
|
|
95
|
+
/** Mark this activity as a compensation handler. */
|
|
96
|
+
isForCompensation?: boolean;
|
|
83
97
|
}
|
|
84
98
|
/** Options for creating a business rule task. */
|
|
85
99
|
export interface BusinessRuleTaskOptions {
|
|
@@ -91,6 +105,8 @@ export interface BusinessRuleTaskOptions {
|
|
|
91
105
|
decisionId?: string;
|
|
92
106
|
/** Variable to store the result. */
|
|
93
107
|
resultVariable?: string;
|
|
108
|
+
/** Mark this task as a compensation handler. */
|
|
109
|
+
isForCompensation?: boolean;
|
|
94
110
|
}
|
|
95
111
|
/** Options for gateway elements. */
|
|
96
112
|
export interface GatewayOptions extends ElementOptions {
|
|
@@ -118,12 +134,16 @@ export interface IntermediateThrowEventOptions extends ElementOptions {
|
|
|
118
134
|
signalName?: string;
|
|
119
135
|
/** Escalation code — creates an escalation throw event (aspirational). */
|
|
120
136
|
escalationCode?: string;
|
|
137
|
+
/** Emit a compensateEventDefinition. */
|
|
138
|
+
compensation?: boolean;
|
|
139
|
+
/** Activity to compensate (activityRef attribute on compensateEventDefinition). */
|
|
140
|
+
activityRef?: string;
|
|
121
141
|
}
|
|
122
142
|
/** Options for an end event. */
|
|
123
143
|
export interface EndEventOptions extends ElementOptions {
|
|
124
144
|
/** Error code — creates an error end event. */
|
|
125
145
|
errorCode?: string;
|
|
126
|
-
/** Error
|
|
146
|
+
/** Error code — creates an error end event. Alias for errorCode. */
|
|
127
147
|
errorRef?: string;
|
|
128
148
|
/** Message name — creates a message end event. */
|
|
129
149
|
messageName?: string;
|
|
@@ -140,7 +160,7 @@ export interface BoundaryEventOptions extends ElementOptions {
|
|
|
140
160
|
cancelActivity?: boolean;
|
|
141
161
|
/** Error code — creates an error boundary event. */
|
|
142
162
|
errorCode?: string;
|
|
143
|
-
/** Error
|
|
163
|
+
/** Error code — creates an error boundary event. Alias for errorCode. */
|
|
144
164
|
errorRef?: string;
|
|
145
165
|
/** Timer duration — creates a timer boundary event (aspirational). */
|
|
146
166
|
timerDuration?: string;
|
|
@@ -152,6 +172,8 @@ export interface BoundaryEventOptions extends ElementOptions {
|
|
|
152
172
|
messageName?: string;
|
|
153
173
|
/** Signal name — creates a signal boundary event (aspirational). */
|
|
154
174
|
signalName?: string;
|
|
175
|
+
/** Creates a compensation boundary event. */
|
|
176
|
+
compensation?: boolean;
|
|
155
177
|
}
|
|
156
178
|
/** Multi-instance loop configuration. */
|
|
157
179
|
export interface MultiInstanceOptions {
|
|
@@ -231,8 +253,12 @@ export declare class BranchBuilder {
|
|
|
231
253
|
/** @internal */
|
|
232
254
|
readonly _associations: BpmnAssociation[];
|
|
233
255
|
private readonly _annCounters;
|
|
256
|
+
private readonly rootErrors;
|
|
257
|
+
private readonly rootMessages;
|
|
258
|
+
private readonly rootSignals;
|
|
259
|
+
private readonly rootEscalations;
|
|
234
260
|
/** @internal */
|
|
235
|
-
constructor(gatewayId: string, branchName: string);
|
|
261
|
+
constructor(gatewayId: string, branchName: string, rootErrors?: BpmnError[], rootMessages?: BpmnMessage[], rootSignals?: BpmnSignal[], rootEscalations?: BpmnEscalation[]);
|
|
236
262
|
/** Set a FEEL condition expression on this branch's outgoing sequence flow. */
|
|
237
263
|
condition(expression: string): this;
|
|
238
264
|
/** Mark this branch as the gateway's default (no-condition) flow. */
|
|
@@ -256,6 +282,8 @@ export declare class BranchBuilder {
|
|
|
256
282
|
receiveTask(id: string, options?: ElementOptions): this;
|
|
257
283
|
businessRuleTask(id: string, options?: BusinessRuleTaskOptions): this;
|
|
258
284
|
callActivity(id: string, options: CallActivityOptions): this;
|
|
285
|
+
/** Add an abstract task with no Zeebe extensions. */
|
|
286
|
+
task(id: string, options?: ElementOptions): this;
|
|
259
287
|
startEvent(id?: string, options?: StartEventOptions): this;
|
|
260
288
|
endEvent(id?: string, options?: EndEventOptions): this;
|
|
261
289
|
intermediateThrowEvent(id?: string, options?: IntermediateThrowEventOptions): this;
|
|
@@ -291,6 +319,8 @@ export declare class SubProcessContentBuilder {
|
|
|
291
319
|
callActivity(id: string, options: CallActivityOptions): this;
|
|
292
320
|
sendTask(id: string, options?: ElementOptions): this;
|
|
293
321
|
receiveTask(id: string, options?: ElementOptions): this;
|
|
322
|
+
/** Add an abstract task with no Zeebe extensions. */
|
|
323
|
+
task(id: string, options?: ElementOptions): this;
|
|
294
324
|
exclusiveGateway(id: string, options?: GatewayOptions): this;
|
|
295
325
|
parallelGateway(id: string, options?: ElementOptions): this;
|
|
296
326
|
inclusiveGateway(id: string, options?: GatewayOptions): this;
|
|
@@ -313,6 +343,8 @@ export declare class ProcessBuilder {
|
|
|
313
343
|
private readonly sequenceFlows;
|
|
314
344
|
private readonly rootErrors;
|
|
315
345
|
private readonly rootMessages;
|
|
346
|
+
private readonly rootSignals;
|
|
347
|
+
private readonly rootEscalations;
|
|
316
348
|
private readonly _textAnnotations;
|
|
317
349
|
private readonly _associations;
|
|
318
350
|
private readonly _annCounters;
|
|
@@ -322,6 +354,7 @@ export declare class ProcessBuilder {
|
|
|
322
354
|
private _autoLayout;
|
|
323
355
|
private _executionPlatformVersion;
|
|
324
356
|
private _serviceTaskDefaults;
|
|
357
|
+
private _savedMainFlowId;
|
|
325
358
|
constructor(processId: string);
|
|
326
359
|
/** Enable auto-layout: `build()` will run the layout engine and populate diagram interchange data. */
|
|
327
360
|
withAutoLayout(): this;
|
|
@@ -393,6 +426,8 @@ export declare class ProcessBuilder {
|
|
|
393
426
|
businessRuleTask(id: string, options?: BusinessRuleTaskOptions): this;
|
|
394
427
|
/** Add a call activity referencing another process. */
|
|
395
428
|
callActivity(id: string, options: CallActivityOptions): this;
|
|
429
|
+
/** Add an abstract task with no Zeebe extensions. */
|
|
430
|
+
task(id: string, options?: ElementOptions): this;
|
|
396
431
|
/** Add an exclusive gateway (XOR split/join). */
|
|
397
432
|
exclusiveGateway(id: string, options?: GatewayOptions): this;
|
|
398
433
|
/** Add a parallel gateway (AND split/join). */
|
|
@@ -432,7 +467,7 @@ export declare class ProcessBuilder {
|
|
|
432
467
|
adHocSubProcess(id: string, content: (b: SubProcessContentBuilder) => void, options?: AdHocSubProcessOptions): this;
|
|
433
468
|
/** Add a sub-process (aspirational). */
|
|
434
469
|
subProcess(id: string, content: (b: SubProcessContentBuilder) => void, options?: SubProcessOptions): this;
|
|
435
|
-
/** Add an event sub-process
|
|
470
|
+
/** Add an event sub-process. Triggered by its start event — no incoming or outgoing sequence flows. */
|
|
436
471
|
eventSubProcess(id: string, content: (b: SubProcessContentBuilder) => void, options?: ElementOptions): this;
|
|
437
472
|
/** Attach a text annotation to the element at the current cursor position. */
|
|
438
473
|
textAnnotation(text: string): this;
|
|
@@ -7,7 +7,7 @@ const EXPORTER_VERSION = "0.0.23";
|
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
8
8
|
// Internal helpers
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
|
-
function buildEventDefinitions(opts, rootErrors, rootMessages) {
|
|
10
|
+
function buildEventDefinitions(opts, rootErrors, rootMessages, rootSignals, rootEscalations) {
|
|
11
11
|
const defs = [];
|
|
12
12
|
if (opts.timerDuration || opts.timerDate || opts.timerCycle) {
|
|
13
13
|
defs.push({
|
|
@@ -18,28 +18,63 @@ function buildEventDefinitions(opts, rootErrors, rootMessages) {
|
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
20
|
if (opts.errorCode !== undefined || opts.errorRef !== undefined) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
rootErrors.
|
|
25
|
-
|
|
21
|
+
const codeOrRef = opts.errorCode ?? opts.errorRef;
|
|
22
|
+
let errorRef;
|
|
23
|
+
if (codeOrRef !== undefined && rootErrors) {
|
|
24
|
+
let existing = rootErrors.find((e) => e.errorCode === codeOrRef || e.name === codeOrRef);
|
|
25
|
+
if (!existing) {
|
|
26
|
+
existing = { id: generateId("Error"), name: codeOrRef, errorCode: codeOrRef };
|
|
27
|
+
rootErrors.push(existing);
|
|
28
|
+
}
|
|
29
|
+
errorRef = existing.id;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
errorRef = codeOrRef;
|
|
26
33
|
}
|
|
27
34
|
defs.push({ type: "error", errorRef });
|
|
28
35
|
}
|
|
29
36
|
if (opts.messageName !== undefined) {
|
|
30
37
|
let messageRef = opts.messageName;
|
|
31
38
|
if (rootMessages) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
let existing = rootMessages.find((m) => m.name === opts.messageName);
|
|
40
|
+
if (!existing) {
|
|
41
|
+
existing = { id: generateId("Message"), name: opts.messageName, unknownAttributes: {} };
|
|
42
|
+
rootMessages.push(existing);
|
|
43
|
+
}
|
|
44
|
+
messageRef = existing.id;
|
|
35
45
|
}
|
|
36
46
|
defs.push({ type: "message", messageRef });
|
|
37
47
|
}
|
|
38
48
|
if (opts.signalName !== undefined) {
|
|
39
|
-
|
|
49
|
+
let signalRef = opts.signalName;
|
|
50
|
+
if (rootSignals) {
|
|
51
|
+
let existing = rootSignals.find((s) => s.name === opts.signalName);
|
|
52
|
+
if (!existing) {
|
|
53
|
+
existing = { id: generateId("Signal"), name: opts.signalName };
|
|
54
|
+
rootSignals.push(existing);
|
|
55
|
+
}
|
|
56
|
+
signalRef = existing.id;
|
|
57
|
+
}
|
|
58
|
+
defs.push({ type: "signal", signalRef });
|
|
40
59
|
}
|
|
41
60
|
if (opts.escalationCode !== undefined) {
|
|
42
|
-
|
|
61
|
+
let escalationRef = opts.escalationCode;
|
|
62
|
+
if (rootEscalations) {
|
|
63
|
+
let existing = rootEscalations.find((e) => e.escalationCode === opts.escalationCode);
|
|
64
|
+
if (!existing) {
|
|
65
|
+
existing = {
|
|
66
|
+
id: generateId("Escalation"),
|
|
67
|
+
name: opts.escalationCode,
|
|
68
|
+
escalationCode: opts.escalationCode,
|
|
69
|
+
};
|
|
70
|
+
rootEscalations.push(existing);
|
|
71
|
+
}
|
|
72
|
+
escalationRef = existing.id;
|
|
73
|
+
}
|
|
74
|
+
defs.push({ type: "escalation", escalationRef });
|
|
75
|
+
}
|
|
76
|
+
if (opts.compensation) {
|
|
77
|
+
defs.push({ type: "compensate", activityRef: opts.activityRef });
|
|
43
78
|
}
|
|
44
79
|
return defs;
|
|
45
80
|
}
|
|
@@ -213,10 +248,12 @@ function makeServiceTaskEl(id, options) {
|
|
|
213
248
|
extensionElements: buildServiceTaskExtensions(options),
|
|
214
249
|
});
|
|
215
250
|
el.unknownAttributes = unknownAttributes;
|
|
251
|
+
if (options.isForCompensation)
|
|
252
|
+
el.isForCompensation = true;
|
|
216
253
|
return el;
|
|
217
254
|
}
|
|
218
255
|
function makeScriptTaskEl(id, options) {
|
|
219
|
-
|
|
256
|
+
const el = makeFlowElement(id, "scriptTask", {
|
|
220
257
|
name: options.name,
|
|
221
258
|
extensionElements: zeebeExtensionsToXmlElements({
|
|
222
259
|
unknownElements: [
|
|
@@ -228,13 +265,19 @@ function makeScriptTaskEl(id, options) {
|
|
|
228
265
|
],
|
|
229
266
|
}),
|
|
230
267
|
});
|
|
268
|
+
if (options.isForCompensation)
|
|
269
|
+
el.isForCompensation = true;
|
|
270
|
+
return el;
|
|
231
271
|
}
|
|
232
272
|
function makeUserTaskEl(id, options) {
|
|
233
273
|
const ext = zeebeExtensionsToXmlElements({
|
|
234
274
|
...(options?.zeebeUserTask ? { userTask: true } : {}),
|
|
235
275
|
...(options?.formId ? { formDefinition: { formId: options.formId } } : {}),
|
|
236
276
|
});
|
|
237
|
-
|
|
277
|
+
const el = makeFlowElement(id, "userTask", { name: options?.name, extensionElements: ext });
|
|
278
|
+
if (options?.isForCompensation)
|
|
279
|
+
el.isForCompensation = true;
|
|
280
|
+
return el;
|
|
238
281
|
}
|
|
239
282
|
function makeBusinessRuleTaskEl(id, options) {
|
|
240
283
|
const ext = [];
|
|
@@ -249,19 +292,28 @@ function makeBusinessRuleTaskEl(id, options) {
|
|
|
249
292
|
},
|
|
250
293
|
}));
|
|
251
294
|
}
|
|
252
|
-
|
|
295
|
+
const el = makeFlowElement(id, "businessRuleTask", {
|
|
296
|
+
name: options?.name,
|
|
297
|
+
extensionElements: ext,
|
|
298
|
+
});
|
|
299
|
+
if (options?.isForCompensation)
|
|
300
|
+
el.isForCompensation = true;
|
|
301
|
+
return el;
|
|
253
302
|
}
|
|
254
303
|
function makeCallActivityEl(id, options) {
|
|
255
304
|
const attrs = { processId: options.processId };
|
|
256
305
|
if (options.propagateAllChildVariables !== undefined) {
|
|
257
306
|
attrs.propagateAllChildVariables = String(options.propagateAllChildVariables);
|
|
258
307
|
}
|
|
259
|
-
|
|
308
|
+
const el = makeFlowElement(id, "callActivity", {
|
|
260
309
|
name: options.name,
|
|
261
310
|
extensionElements: zeebeExtensionsToXmlElements({
|
|
262
311
|
unknownElements: [{ name: "zeebe:calledElement", attributes: attrs, children: [] }],
|
|
263
312
|
}),
|
|
264
313
|
});
|
|
314
|
+
if (options.isForCompensation)
|
|
315
|
+
el.isForCompensation = true;
|
|
316
|
+
return el;
|
|
265
317
|
}
|
|
266
318
|
function makeExclusiveGatewayEl(id, options) {
|
|
267
319
|
const el = makeFlowElement(id, "exclusiveGateway", options);
|
|
@@ -332,15 +384,17 @@ function insertJoinGateways(elements, flows) {
|
|
|
332
384
|
const gwType = elementTypes.get(splitId);
|
|
333
385
|
if (!gwType)
|
|
334
386
|
continue;
|
|
387
|
+
// eventBasedGateway is split-only; converge through an XOR join instead
|
|
388
|
+
const joinType = gwType === "eventBasedGateway" ? "exclusiveGateway" : gwType;
|
|
335
389
|
const targetType = elementTypes.get(targetId);
|
|
336
|
-
if (targetType ===
|
|
390
|
+
if (targetType === joinType)
|
|
337
391
|
continue;
|
|
338
392
|
const joinId = `${splitId}_join`;
|
|
339
393
|
if (elementTypes.has(joinId))
|
|
340
394
|
continue;
|
|
341
|
-
const joinElement = makeFlowElement(joinId,
|
|
395
|
+
const joinElement = makeFlowElement(joinId, joinType, {});
|
|
342
396
|
elements.push(joinElement);
|
|
343
|
-
elementTypes.set(joinId,
|
|
397
|
+
elementTypes.set(joinId, joinType);
|
|
344
398
|
for (const flow of convergingFlows)
|
|
345
399
|
flow.targetRef = joinId;
|
|
346
400
|
flows.push({
|
|
@@ -401,11 +455,19 @@ export class BranchBuilder {
|
|
|
401
455
|
/** @internal */
|
|
402
456
|
_associations = [];
|
|
403
457
|
_annCounters = new Map();
|
|
458
|
+
rootErrors;
|
|
459
|
+
rootMessages;
|
|
460
|
+
rootSignals;
|
|
461
|
+
rootEscalations;
|
|
404
462
|
/** @internal */
|
|
405
|
-
constructor(gatewayId, branchName) {
|
|
463
|
+
constructor(gatewayId, branchName, rootErrors = [], rootMessages = [], rootSignals = [], rootEscalations = []) {
|
|
406
464
|
this.gatewayId = gatewayId;
|
|
407
465
|
this.branchName = branchName;
|
|
408
466
|
this.lastNodeId = gatewayId;
|
|
467
|
+
this.rootErrors = rootErrors;
|
|
468
|
+
this.rootMessages = rootMessages;
|
|
469
|
+
this.rootSignals = rootSignals;
|
|
470
|
+
this.rootEscalations = rootEscalations;
|
|
409
471
|
}
|
|
410
472
|
/** Set a FEEL condition expression on this branch's outgoing sequence flow. */
|
|
411
473
|
condition(expression) {
|
|
@@ -500,10 +562,16 @@ export class BranchBuilder {
|
|
|
500
562
|
return this.addElement(makeScriptTaskEl(id, options));
|
|
501
563
|
}
|
|
502
564
|
sendTask(id, options) {
|
|
503
|
-
|
|
565
|
+
const el = makeFlowElement(id, "sendTask", options);
|
|
566
|
+
if (options?.isForCompensation)
|
|
567
|
+
el.isForCompensation = true;
|
|
568
|
+
return this.addElement(el);
|
|
504
569
|
}
|
|
505
570
|
receiveTask(id, options) {
|
|
506
|
-
|
|
571
|
+
const el = makeFlowElement(id, "receiveTask", options);
|
|
572
|
+
if (options?.isForCompensation)
|
|
573
|
+
el.isForCompensation = true;
|
|
574
|
+
return this.addElement(el);
|
|
507
575
|
}
|
|
508
576
|
businessRuleTask(id, options) {
|
|
509
577
|
return this.addElement(makeBusinessRuleTaskEl(id, options));
|
|
@@ -511,32 +579,38 @@ export class BranchBuilder {
|
|
|
511
579
|
callActivity(id, options) {
|
|
512
580
|
return this.addElement(makeCallActivityEl(id, options));
|
|
513
581
|
}
|
|
582
|
+
/** Add an abstract task with no Zeebe extensions. */
|
|
583
|
+
task(id, options) {
|
|
584
|
+
const el = makeFlowElement(id, "task", options);
|
|
585
|
+
if (options?.isForCompensation)
|
|
586
|
+
el.isForCompensation = true;
|
|
587
|
+
return this.addElement(el);
|
|
588
|
+
}
|
|
514
589
|
startEvent(id, options) {
|
|
515
590
|
const el = makeFlowElement(id ?? generateId("StartEvent"), "startEvent", options);
|
|
516
|
-
if (el.type === "startEvent" &&
|
|
517
|
-
(options
|
|
518
|
-
el.eventDefinitions = buildEventDefinitions(options);
|
|
591
|
+
if (el.type === "startEvent" && options) {
|
|
592
|
+
el.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
519
593
|
}
|
|
520
594
|
return this.addElement(el);
|
|
521
595
|
}
|
|
522
596
|
endEvent(id, options) {
|
|
523
597
|
const el = makeFlowElement(id ?? generateId("EndEvent"), "endEvent", options);
|
|
524
598
|
if (el.type === "endEvent" && options) {
|
|
525
|
-
el.eventDefinitions = buildEventDefinitions(options);
|
|
599
|
+
el.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
526
600
|
}
|
|
527
601
|
return this.addElement(el);
|
|
528
602
|
}
|
|
529
603
|
intermediateThrowEvent(id, options) {
|
|
530
604
|
const el = makeFlowElement(id ?? generateId("IntermediateThrowEvent"), "intermediateThrowEvent", options);
|
|
531
605
|
if (el.type === "intermediateThrowEvent" && options) {
|
|
532
|
-
el.eventDefinitions = buildEventDefinitions(options);
|
|
606
|
+
el.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
533
607
|
}
|
|
534
608
|
return this.addElement(el);
|
|
535
609
|
}
|
|
536
610
|
intermediateCatchEvent(id, options) {
|
|
537
611
|
const el = makeFlowElement(id ?? generateId("IntermediateCatchEvent"), "intermediateCatchEvent", options);
|
|
538
612
|
if (el.type === "intermediateCatchEvent" && options) {
|
|
539
|
-
el.eventDefinitions = buildEventDefinitions(options);
|
|
613
|
+
el.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
540
614
|
}
|
|
541
615
|
return this.addElement(el);
|
|
542
616
|
}
|
|
@@ -600,8 +674,11 @@ export class SubProcessContentBuilder {
|
|
|
600
674
|
// ---- Events ----
|
|
601
675
|
startEvent(id, options) {
|
|
602
676
|
const el = makeFlowElement(id ?? generateId("StartEvent"), "startEvent", options);
|
|
603
|
-
if (el.type === "startEvent" && options)
|
|
677
|
+
if (el.type === "startEvent" && options) {
|
|
604
678
|
el.eventDefinitions = buildEventDefinitions(options);
|
|
679
|
+
if (options.isInterrupting === false)
|
|
680
|
+
el.isInterrupting = false;
|
|
681
|
+
}
|
|
605
682
|
return this.addElement(el);
|
|
606
683
|
}
|
|
607
684
|
endEvent(id, options) {
|
|
@@ -639,10 +716,23 @@ export class SubProcessContentBuilder {
|
|
|
639
716
|
return this.addElement(makeCallActivityEl(id, options));
|
|
640
717
|
}
|
|
641
718
|
sendTask(id, options) {
|
|
642
|
-
|
|
719
|
+
const el = makeFlowElement(id, "sendTask", options);
|
|
720
|
+
if (options?.isForCompensation)
|
|
721
|
+
el.isForCompensation = true;
|
|
722
|
+
return this.addElement(el);
|
|
643
723
|
}
|
|
644
724
|
receiveTask(id, options) {
|
|
645
|
-
|
|
725
|
+
const el = makeFlowElement(id, "receiveTask", options);
|
|
726
|
+
if (options?.isForCompensation)
|
|
727
|
+
el.isForCompensation = true;
|
|
728
|
+
return this.addElement(el);
|
|
729
|
+
}
|
|
730
|
+
/** Add an abstract task with no Zeebe extensions. */
|
|
731
|
+
task(id, options) {
|
|
732
|
+
const el = makeFlowElement(id, "task", options);
|
|
733
|
+
if (options?.isForCompensation)
|
|
734
|
+
el.isForCompensation = true;
|
|
735
|
+
return this.addElement(el);
|
|
646
736
|
}
|
|
647
737
|
// ---- Gateways ----
|
|
648
738
|
exclusiveGateway(id, options) {
|
|
@@ -753,6 +843,8 @@ export class ProcessBuilder {
|
|
|
753
843
|
sequenceFlows = [];
|
|
754
844
|
rootErrors = [];
|
|
755
845
|
rootMessages = [];
|
|
846
|
+
rootSignals = [];
|
|
847
|
+
rootEscalations = [];
|
|
756
848
|
_textAnnotations = [];
|
|
757
849
|
_associations = [];
|
|
758
850
|
_annCounters = new Map();
|
|
@@ -762,6 +854,7 @@ export class ProcessBuilder {
|
|
|
762
854
|
_autoLayout = false;
|
|
763
855
|
_executionPlatformVersion = "8.9.0";
|
|
764
856
|
_serviceTaskDefaults = {};
|
|
857
|
+
_savedMainFlowId = undefined;
|
|
765
858
|
constructor(processId) {
|
|
766
859
|
this.processId = processId;
|
|
767
860
|
}
|
|
@@ -811,7 +904,9 @@ export class ProcessBuilder {
|
|
|
811
904
|
extensionElements: extElements,
|
|
812
905
|
});
|
|
813
906
|
if (element.type === "startEvent" && options) {
|
|
814
|
-
element.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages);
|
|
907
|
+
element.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
908
|
+
if (options.isInterrupting === false)
|
|
909
|
+
element.isInterrupting = false;
|
|
815
910
|
}
|
|
816
911
|
if (options?.modelerTemplate) {
|
|
817
912
|
element.unknownAttributes["zeebe:modelerTemplate"] = options.modelerTemplate;
|
|
@@ -850,7 +945,7 @@ export class ProcessBuilder {
|
|
|
850
945
|
const nodeId = id ?? generateId("EndEvent");
|
|
851
946
|
const element = makeFlowElement(nodeId, "endEvent", options);
|
|
852
947
|
if (element.type === "endEvent" && options) {
|
|
853
|
-
element.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages);
|
|
948
|
+
element.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
854
949
|
}
|
|
855
950
|
this.addFlowElement(element);
|
|
856
951
|
return this;
|
|
@@ -860,7 +955,7 @@ export class ProcessBuilder {
|
|
|
860
955
|
const nodeId = id ?? generateId("IntermediateThrowEvent");
|
|
861
956
|
const element = makeFlowElement(nodeId, "intermediateThrowEvent", options);
|
|
862
957
|
if (element.type === "intermediateThrowEvent" && options) {
|
|
863
|
-
element.eventDefinitions = buildEventDefinitions(options);
|
|
958
|
+
element.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
864
959
|
}
|
|
865
960
|
this.addFlowElement(element);
|
|
866
961
|
return this;
|
|
@@ -870,7 +965,7 @@ export class ProcessBuilder {
|
|
|
870
965
|
const nodeId = id ?? generateId("IntermediateCatchEvent");
|
|
871
966
|
const element = makeFlowElement(nodeId, "intermediateCatchEvent", options);
|
|
872
967
|
if (element.type === "intermediateCatchEvent" && options) {
|
|
873
|
-
element.eventDefinitions = buildEventDefinitions(options);
|
|
968
|
+
element.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
874
969
|
}
|
|
875
970
|
this.addFlowElement(element);
|
|
876
971
|
return this;
|
|
@@ -886,12 +981,17 @@ export class ProcessBuilder {
|
|
|
886
981
|
if (element.type === "boundaryEvent") {
|
|
887
982
|
element.attachedToRef = options.attachedTo;
|
|
888
983
|
element.cancelActivity = options.cancelActivity;
|
|
889
|
-
element.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages);
|
|
984
|
+
element.eventDefinitions = buildEventDefinitions(options, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
890
985
|
}
|
|
891
986
|
// Boundary events never auto-connect — temporarily clear lastNodeId
|
|
892
987
|
const prevLast = this.lastNodeId;
|
|
893
988
|
this.lastNodeId = undefined;
|
|
894
989
|
this.addFlowElement(element);
|
|
990
|
+
// For compensation boundary events, save the main-flow cursor AFTER addFlowElement
|
|
991
|
+
// so subsequent normal elements don't accidentally clear it before it's consumed.
|
|
992
|
+
if (options.compensation) {
|
|
993
|
+
this._savedMainFlowId = prevLast;
|
|
994
|
+
}
|
|
895
995
|
// Don't restore prevLast — the builder now chains from the boundary event
|
|
896
996
|
void prevLast;
|
|
897
997
|
return this;
|
|
@@ -921,6 +1021,7 @@ export class ProcessBuilder {
|
|
|
921
1021
|
this.lastNodeId = savedLast;
|
|
922
1022
|
this.currentGatewayId = savedGateway;
|
|
923
1023
|
this.openBranchEnds = savedOpenEnds;
|
|
1024
|
+
this._savedMainFlowId = undefined;
|
|
924
1025
|
return this;
|
|
925
1026
|
}
|
|
926
1027
|
// ---- Tasks ----
|
|
@@ -971,12 +1072,18 @@ export class ProcessBuilder {
|
|
|
971
1072
|
}
|
|
972
1073
|
/** Add a send task (aspirational). */
|
|
973
1074
|
sendTask(id, options) {
|
|
974
|
-
|
|
1075
|
+
const el = makeFlowElement(id, "sendTask", options);
|
|
1076
|
+
if (options?.isForCompensation)
|
|
1077
|
+
el.isForCompensation = true;
|
|
1078
|
+
this.addFlowElement(el);
|
|
975
1079
|
return this;
|
|
976
1080
|
}
|
|
977
1081
|
/** Add a receive task (aspirational). */
|
|
978
1082
|
receiveTask(id, options) {
|
|
979
|
-
|
|
1083
|
+
const el = makeFlowElement(id, "receiveTask", options);
|
|
1084
|
+
if (options?.isForCompensation)
|
|
1085
|
+
el.isForCompensation = true;
|
|
1086
|
+
this.addFlowElement(el);
|
|
980
1087
|
return this;
|
|
981
1088
|
}
|
|
982
1089
|
/** Add a business rule task. */
|
|
@@ -989,6 +1096,14 @@ export class ProcessBuilder {
|
|
|
989
1096
|
this.addFlowElement(makeCallActivityEl(id, options));
|
|
990
1097
|
return this;
|
|
991
1098
|
}
|
|
1099
|
+
/** Add an abstract task with no Zeebe extensions. */
|
|
1100
|
+
task(id, options) {
|
|
1101
|
+
const el = makeFlowElement(id, "task", options);
|
|
1102
|
+
if (options?.isForCompensation)
|
|
1103
|
+
el.isForCompensation = true;
|
|
1104
|
+
this.addFlowElement(el);
|
|
1105
|
+
return this;
|
|
1106
|
+
}
|
|
992
1107
|
// ---- Gateways ----
|
|
993
1108
|
/** Add an exclusive gateway (XOR split/join). */
|
|
994
1109
|
exclusiveGateway(id, options) {
|
|
@@ -1036,7 +1151,7 @@ export class ProcessBuilder {
|
|
|
1036
1151
|
if (!this.currentGatewayId) {
|
|
1037
1152
|
throw new Error("branch() must be called after a gateway element");
|
|
1038
1153
|
}
|
|
1039
|
-
const b = new BranchBuilder(this.currentGatewayId, name);
|
|
1154
|
+
const b = new BranchBuilder(this.currentGatewayId, name, this.rootErrors, this.rootMessages, this.rootSignals, this.rootEscalations);
|
|
1040
1155
|
callback(b);
|
|
1041
1156
|
for (const el of b._elements) {
|
|
1042
1157
|
if (this.flowElements.some((n) => n.id === el.id)) {
|
|
@@ -1187,20 +1302,29 @@ export class ProcessBuilder {
|
|
|
1187
1302
|
this.addFlowElement(element);
|
|
1188
1303
|
return this;
|
|
1189
1304
|
}
|
|
1190
|
-
/** Add an event sub-process
|
|
1305
|
+
/** Add an event sub-process. Triggered by its start event — no incoming or outgoing sequence flows. */
|
|
1191
1306
|
eventSubProcess(id, content, options) {
|
|
1192
1307
|
const sub = new SubProcessContentBuilder();
|
|
1193
1308
|
content(sub);
|
|
1194
1309
|
insertJoinGateways(sub._elements, sub._flows);
|
|
1195
1310
|
recomputeIncomingOutgoing(sub._elements, sub._flows);
|
|
1196
|
-
const element = makeFlowElement(id, "
|
|
1197
|
-
if (element.type === "
|
|
1311
|
+
const element = makeFlowElement(id, "subProcess", options);
|
|
1312
|
+
if (element.type === "subProcess") {
|
|
1313
|
+
element.triggeredByEvent = true;
|
|
1198
1314
|
element.flowElements = sub._elements;
|
|
1199
1315
|
element.sequenceFlows = sub._flows;
|
|
1200
1316
|
element.textAnnotations = sub._textAnnotations;
|
|
1201
1317
|
element.associations = sub._associations;
|
|
1202
1318
|
}
|
|
1203
|
-
|
|
1319
|
+
// Event sub-processes have no incoming/outgoing sequence flows and must not
|
|
1320
|
+
// advance the flow cursor — the surrounding process wires around them.
|
|
1321
|
+
// openBranchEnds is intentionally NOT drained here; the next normal
|
|
1322
|
+
// addFlowElement call will drain it and connect branch ends to that element.
|
|
1323
|
+
if (this.flowElements.some((n) => n.id === element.id)) {
|
|
1324
|
+
throw new Error(`Duplicate element ID "${element.id}" in process "${this.processId}"`);
|
|
1325
|
+
}
|
|
1326
|
+
this._savedMainFlowId = undefined;
|
|
1327
|
+
this.flowElements.push(element);
|
|
1204
1328
|
return this;
|
|
1205
1329
|
}
|
|
1206
1330
|
// ---- Annotations ----
|
|
@@ -1283,9 +1407,9 @@ export class ProcessBuilder {
|
|
|
1283
1407
|
"modeler:executionPlatformVersion": this._executionPlatformVersion,
|
|
1284
1408
|
},
|
|
1285
1409
|
errors: this.rootErrors,
|
|
1286
|
-
escalations:
|
|
1410
|
+
escalations: this.rootEscalations,
|
|
1287
1411
|
messages: this.rootMessages,
|
|
1288
|
-
signals:
|
|
1412
|
+
signals: this.rootSignals,
|
|
1289
1413
|
collaborations: [],
|
|
1290
1414
|
processes: [process],
|
|
1291
1415
|
// Seed diagram stub so applyAutoLayout preserves process-specific IDs.
|
|
@@ -1323,6 +1447,24 @@ export class ProcessBuilder {
|
|
|
1323
1447
|
throw new Error(`Duplicate element ID "${element.id}" in process "${this.processId}"`);
|
|
1324
1448
|
}
|
|
1325
1449
|
this.flowElements.push(element);
|
|
1450
|
+
// Compensation handlers are outside the normal token flow: link via association
|
|
1451
|
+
// from the preceding compensation boundary event, then restore the main-flow cursor.
|
|
1452
|
+
if (element.isForCompensation) {
|
|
1453
|
+
if (this.lastNodeId) {
|
|
1454
|
+
this._associations.push({
|
|
1455
|
+
id: generateId("Association"),
|
|
1456
|
+
sourceRef: this.lastNodeId,
|
|
1457
|
+
targetRef: element.id,
|
|
1458
|
+
associationDirection: "One",
|
|
1459
|
+
unknownAttributes: {},
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
// Restore main-flow cursor (saved by boundaryEvent() when compensation: true)
|
|
1463
|
+
this.lastNodeId = this._savedMainFlowId;
|
|
1464
|
+
this._savedMainFlowId = undefined;
|
|
1465
|
+
// Do NOT connect open branch ends — handler is outside normal flow
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1326
1468
|
if (this.lastNodeId) {
|
|
1327
1469
|
const flowId = generateId("Flow");
|
|
1328
1470
|
this.sequenceFlows.push({
|
|
@@ -1345,6 +1487,9 @@ export class ProcessBuilder {
|
|
|
1345
1487
|
});
|
|
1346
1488
|
}
|
|
1347
1489
|
this.openBranchEnds = [];
|
|
1490
|
+
// Clear any saved compensation cursor — a normal element advancing the cursor
|
|
1491
|
+
// means the compensation boundary/handler pattern has been interrupted.
|
|
1492
|
+
this._savedMainFlowId = undefined;
|
|
1348
1493
|
this.lastNodeId = element.id;
|
|
1349
1494
|
}
|
|
1350
1495
|
}
|
|
@@ -105,10 +105,12 @@ interface BpmnFlowNodeBase {
|
|
|
105
105
|
documentation?: string;
|
|
106
106
|
extensionElements: XmlElement[];
|
|
107
107
|
unknownAttributes: Record<string, string>;
|
|
108
|
+
isForCompensation?: boolean;
|
|
108
109
|
}
|
|
109
110
|
export interface BpmnStartEvent extends BpmnFlowNodeBase {
|
|
110
111
|
type: "startEvent";
|
|
111
112
|
eventDefinitions: BpmnEventDefinition[];
|
|
113
|
+
isInterrupting?: boolean;
|
|
112
114
|
}
|
|
113
115
|
export interface BpmnEndEvent extends BpmnFlowNodeBase {
|
|
114
116
|
type: "endEvent";
|
package/dist/bpmn/bpmn-parser.js
CHANGED
|
@@ -42,6 +42,7 @@ const KNOWN_ATTRS = new Set([
|
|
|
42
42
|
"default",
|
|
43
43
|
"attachedToRef",
|
|
44
44
|
"cancelActivity",
|
|
45
|
+
"isForCompensation",
|
|
45
46
|
"sourceRef",
|
|
46
47
|
"targetRef",
|
|
47
48
|
"associationDirection",
|
|
@@ -275,7 +276,15 @@ function parseFlowElement(element) {
|
|
|
275
276
|
unknownAttributes: unknownAttrs(element),
|
|
276
277
|
};
|
|
277
278
|
switch (ln) {
|
|
278
|
-
case "startEvent":
|
|
279
|
+
case "startEvent": {
|
|
280
|
+
const isInterruptingAttr = attr(element, "isInterrupting");
|
|
281
|
+
return {
|
|
282
|
+
...base,
|
|
283
|
+
type: "startEvent",
|
|
284
|
+
eventDefinitions: parseEventDefinitions(element),
|
|
285
|
+
...(isInterruptingAttr === "false" ? { isInterrupting: false } : {}),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
279
288
|
case "endEvent":
|
|
280
289
|
case "intermediateCatchEvent":
|
|
281
290
|
case "intermediateThrowEvent":
|
|
@@ -299,7 +308,12 @@ function parseFlowElement(element) {
|
|
|
299
308
|
case "businessRuleTask":
|
|
300
309
|
case "manualTask":
|
|
301
310
|
case "callActivity":
|
|
302
|
-
return {
|
|
311
|
+
return {
|
|
312
|
+
...base,
|
|
313
|
+
type: ln,
|
|
314
|
+
loopCharacteristics: parseLoopCharacteristics(element),
|
|
315
|
+
isForCompensation: attr(element, "isForCompensation") === "true" ? true : undefined,
|
|
316
|
+
};
|
|
303
317
|
case "adHocSubProcess":
|
|
304
318
|
return {
|
|
305
319
|
...base,
|
|
@@ -157,6 +157,8 @@ function serializeFlowElement(fe, ns) {
|
|
|
157
157
|
const attrs = { id: fe.id, ...fe.unknownAttributes };
|
|
158
158
|
if (fe.name !== undefined)
|
|
159
159
|
attrs.name = fe.name;
|
|
160
|
+
if (fe.isForCompensation)
|
|
161
|
+
attrs.isForCompensation = "true";
|
|
160
162
|
const children = [];
|
|
161
163
|
// Documentation
|
|
162
164
|
if (fe.documentation !== undefined) {
|
|
@@ -169,6 +171,10 @@ function serializeFlowElement(fe, ns) {
|
|
|
169
171
|
children.push(...flowRefs(fe.outgoing, "outgoing", bp));
|
|
170
172
|
switch (fe.type) {
|
|
171
173
|
case "startEvent":
|
|
174
|
+
if (fe.isInterrupting === false)
|
|
175
|
+
attrs.isInterrupting = "false";
|
|
176
|
+
children.push(...serializeEventDefinitions(fe.eventDefinitions, bp));
|
|
177
|
+
break;
|
|
172
178
|
case "endEvent":
|
|
173
179
|
case "intermediateCatchEvent":
|
|
174
180
|
case "intermediateThrowEvent":
|