@bpmnkit/core 0.1.0 → 0.1.2
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 +2 -0
- package/dist/bpmn/agentic.d.ts +121 -0
- package/dist/bpmn/agentic.js +97 -0
- package/dist/bpmn/auto-layout.d.ts +5 -5
- package/dist/bpmn/auto-layout.js +592 -36
- package/dist/bpmn/bpmn-builder.d.ts +56 -0
- package/dist/bpmn/bpmn-builder.js +155 -182
- package/dist/bpmn/bpmn-model.d.ts +39 -2
- package/dist/bpmn/bpmn-parser.js +48 -2
- package/dist/bpmn/bpmn-serializer.js +33 -0
- package/dist/bpmn/compact.js +11 -1
- package/dist/bpmn/di-planes.d.ts +13 -0
- package/dist/bpmn/di-planes.js +20 -0
- package/dist/bpmn/optimize/agentic.d.ts +10 -0
- package/dist/bpmn/optimize/agentic.js +88 -0
- package/dist/bpmn/optimize/deploy.d.ts +16 -0
- package/dist/bpmn/optimize/deploy.js +143 -0
- package/dist/bpmn/optimize/feel-syntax.d.ts +12 -0
- package/dist/bpmn/optimize/feel-syntax.js +87 -0
- package/dist/bpmn/optimize/feel.js +5 -2
- package/dist/bpmn/optimize/flow.js +22 -2
- package/dist/bpmn/optimize/index.js +20 -9
- package/dist/bpmn/optimize/tasks.js +1 -0
- package/dist/bpmn/optimize/types.d.ts +10 -1
- package/dist/bpmn/svg.js +22 -3
- package/dist/bpmn/type-guards.d.ts +7 -1
- package/dist/bpmn/type-guards.js +13 -0
- package/dist/bpmn/zeebe-extensions.d.ts +27 -0
- package/dist/bpmn/zeebe-extensions.js +38 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.js +4 -1
- package/dist/layout/annotations.js +36 -1
- package/dist/layout/collaboration/alignment.d.ts +26 -0
- package/dist/layout/collaboration/alignment.js +66 -0
- package/dist/layout/collaboration/ordering.d.ts +21 -0
- package/dist/layout/collaboration/ordering.js +102 -0
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/layout-engine.d.ts +13 -3
- package/dist/layout/layout-engine.js +9 -4
- package/dist/layout/semantic/bands.d.ts +19 -0
- package/dist/layout/semantic/bands.js +324 -0
- package/dist/layout/semantic/graph.d.ts +29 -0
- package/dist/layout/semantic/graph.js +217 -0
- package/dist/layout/semantic/index.d.ts +13 -0
- package/dist/layout/semantic/index.js +181 -0
- package/dist/layout/semantic/place.d.ts +40 -0
- package/dist/layout/semantic/place.js +271 -0
- package/dist/layout/semantic/route.d.ts +14 -0
- package/dist/layout/semantic/route.js +454 -0
- package/dist/layout/types.d.ts +17 -0
- package/dist/plan/compile.d.ts +39 -0
- package/dist/plan/compile.js +380 -0
- package/dist/plan/extract.d.ts +31 -0
- package/dist/plan/extract.js +248 -0
- package/dist/plan/index.d.ts +6 -0
- package/dist/plan/index.js +5 -0
- package/dist/plan/merge.d.ts +13 -0
- package/dist/plan/merge.js +80 -0
- package/dist/plan/slug.d.ts +5 -0
- package/dist/plan/slug.js +22 -0
- package/dist/plan/types.d.ts +225 -0
- package/dist/plan/types.js +13 -0
- package/package.json +2 -2
|
@@ -5,7 +5,7 @@ import type { XmlElement } from "../types/xml-element.js";
|
|
|
5
5
|
* exhaustive `switch` statements and type-narrowing with the type guard helpers
|
|
6
6
|
* exported from `@bpmnkit/core`.
|
|
7
7
|
*/
|
|
8
|
-
export type BpmnElementType = "startEvent" | "endEvent" | "intermediateThrowEvent" | "intermediateCatchEvent" | "boundaryEvent" | "task" | "serviceTask" | "scriptTask" | "userTask" | "sendTask" | "receiveTask" | "businessRuleTask" | "manualTask" | "callActivity" | "exclusiveGateway" | "parallelGateway" | "inclusiveGateway" | "eventBasedGateway" | "complexGateway" | "subProcess" | "adHocSubProcess" | "eventSubProcess" | "transaction";
|
|
8
|
+
export type BpmnElementType = "startEvent" | "endEvent" | "intermediateThrowEvent" | "intermediateCatchEvent" | "boundaryEvent" | "task" | "serviceTask" | "scriptTask" | "userTask" | "sendTask" | "receiveTask" | "businessRuleTask" | "manualTask" | "callActivity" | "exclusiveGateway" | "parallelGateway" | "inclusiveGateway" | "eventBasedGateway" | "complexGateway" | "subProcess" | "adHocSubProcess" | "eventSubProcess" | "transaction" | "dataObject" | "dataObjectReference" | "dataStoreReference";
|
|
9
9
|
/** Axis-aligned bounding box used by BPMN diagram interchange (BPMNDi). */
|
|
10
10
|
export interface BpmnBounds {
|
|
11
11
|
x: number;
|
|
@@ -150,6 +150,26 @@ export interface BpmnCallActivity extends BpmnFlowNodeBase {
|
|
|
150
150
|
type: "callActivity";
|
|
151
151
|
loopCharacteristics?: BpmnMultiInstanceLoopCharacteristics;
|
|
152
152
|
}
|
|
153
|
+
/** A data object — a piece of information flowing through the process. */
|
|
154
|
+
export interface BpmnDataObject extends BpmnFlowNodeBase {
|
|
155
|
+
type: "dataObject";
|
|
156
|
+
/** When true, represents a collection of items. */
|
|
157
|
+
isCollection?: boolean;
|
|
158
|
+
}
|
|
159
|
+
/** A visual reference to a {@link BpmnDataObject} (the shape drawn on the canvas). */
|
|
160
|
+
export interface BpmnDataObjectReference extends BpmnFlowNodeBase {
|
|
161
|
+
type: "dataObjectReference";
|
|
162
|
+
/** Id of the referenced `dataObject`. */
|
|
163
|
+
dataObjectRef?: string;
|
|
164
|
+
/** When true, drawn with the collection (parallel-bars) marker. */
|
|
165
|
+
isCollection?: boolean;
|
|
166
|
+
}
|
|
167
|
+
/** A visual reference to a data store (drawn as a cylinder). */
|
|
168
|
+
export interface BpmnDataStoreReference extends BpmnFlowNodeBase {
|
|
169
|
+
type: "dataStoreReference";
|
|
170
|
+
/** Id of the referenced `dataStore`. */
|
|
171
|
+
dataStoreRef?: string;
|
|
172
|
+
}
|
|
153
173
|
export interface BpmnSendTask extends BpmnFlowNodeBase {
|
|
154
174
|
type: "sendTask";
|
|
155
175
|
messageRef?: string;
|
|
@@ -163,10 +183,15 @@ export interface BpmnReceiveTask extends BpmnFlowNodeBase {
|
|
|
163
183
|
export interface BpmnAdHocSubProcess extends BpmnFlowNodeBase {
|
|
164
184
|
type: "adHocSubProcess";
|
|
165
185
|
loopCharacteristics?: BpmnMultiInstanceLoopCharacteristics;
|
|
186
|
+
/** FEEL expression evaluated after each tool activity completes; ends the ad-hoc scope when true. */
|
|
187
|
+
completionCondition?: BpmnConditionExpression;
|
|
188
|
+
/** Whether still-running inner activity instances are cancelled once the completion condition is met (default true). */
|
|
189
|
+
cancelRemainingInstances?: boolean;
|
|
166
190
|
flowElements: BpmnFlowElement[];
|
|
167
191
|
sequenceFlows: BpmnSequenceFlow[];
|
|
168
192
|
textAnnotations: BpmnTextAnnotation[];
|
|
169
193
|
associations: BpmnAssociation[];
|
|
194
|
+
groups: BpmnGroup[];
|
|
170
195
|
}
|
|
171
196
|
export interface BpmnSubProcess extends BpmnFlowNodeBase {
|
|
172
197
|
type: "subProcess";
|
|
@@ -176,6 +201,7 @@ export interface BpmnSubProcess extends BpmnFlowNodeBase {
|
|
|
176
201
|
sequenceFlows: BpmnSequenceFlow[];
|
|
177
202
|
textAnnotations: BpmnTextAnnotation[];
|
|
178
203
|
associations: BpmnAssociation[];
|
|
204
|
+
groups: BpmnGroup[];
|
|
179
205
|
}
|
|
180
206
|
export interface BpmnEventSubProcess extends BpmnFlowNodeBase {
|
|
181
207
|
type: "eventSubProcess";
|
|
@@ -183,6 +209,7 @@ export interface BpmnEventSubProcess extends BpmnFlowNodeBase {
|
|
|
183
209
|
sequenceFlows: BpmnSequenceFlow[];
|
|
184
210
|
textAnnotations: BpmnTextAnnotation[];
|
|
185
211
|
associations: BpmnAssociation[];
|
|
212
|
+
groups: BpmnGroup[];
|
|
186
213
|
}
|
|
187
214
|
export interface BpmnExclusiveGateway extends BpmnFlowNodeBase {
|
|
188
215
|
type: "exclusiveGateway";
|
|
@@ -217,6 +244,7 @@ export interface BpmnTransaction extends BpmnFlowNodeBase {
|
|
|
217
244
|
sequenceFlows: BpmnSequenceFlow[];
|
|
218
245
|
textAnnotations: BpmnTextAnnotation[];
|
|
219
246
|
associations: BpmnAssociation[];
|
|
247
|
+
groups: BpmnGroup[];
|
|
220
248
|
}
|
|
221
249
|
/**
|
|
222
250
|
* Discriminated union of every BPMN flow node that can appear inside a
|
|
@@ -233,7 +261,7 @@ export interface BpmnTransaction extends BpmnFlowNodeBase {
|
|
|
233
261
|
* }
|
|
234
262
|
* ```
|
|
235
263
|
*/
|
|
236
|
-
export type BpmnFlowElement = BpmnStartEvent | BpmnEndEvent | BpmnIntermediateCatchEvent | BpmnIntermediateThrowEvent | BpmnBoundaryEvent | BpmnTask | BpmnServiceTask | BpmnScriptTask | BpmnUserTask | BpmnSendTask | BpmnReceiveTask | BpmnBusinessRuleTask | BpmnManualTask | BpmnCallActivity | BpmnSubProcess | BpmnAdHocSubProcess | BpmnEventSubProcess | BpmnTransaction | BpmnExclusiveGateway | BpmnParallelGateway | BpmnInclusiveGateway | BpmnEventBasedGateway | BpmnComplexGateway;
|
|
264
|
+
export type BpmnFlowElement = BpmnStartEvent | BpmnEndEvent | BpmnIntermediateCatchEvent | BpmnIntermediateThrowEvent | BpmnBoundaryEvent | BpmnTask | BpmnServiceTask | BpmnScriptTask | BpmnUserTask | BpmnSendTask | BpmnReceiveTask | BpmnBusinessRuleTask | BpmnManualTask | BpmnCallActivity | BpmnSubProcess | BpmnAdHocSubProcess | BpmnEventSubProcess | BpmnTransaction | BpmnExclusiveGateway | BpmnParallelGateway | BpmnInclusiveGateway | BpmnEventBasedGateway | BpmnComplexGateway | BpmnDataObject | BpmnDataObjectReference | BpmnDataStoreReference;
|
|
237
265
|
/** Backward-compat alias used by the layout module. */
|
|
238
266
|
export type BpmnFlowNode = BpmnFlowElement;
|
|
239
267
|
/**
|
|
@@ -264,6 +292,13 @@ export interface BpmnAssociation {
|
|
|
264
292
|
associationDirection?: string;
|
|
265
293
|
unknownAttributes: Record<string, string>;
|
|
266
294
|
}
|
|
295
|
+
/** A group artifact — a dashed rounded rectangle visually grouping elements. */
|
|
296
|
+
export interface BpmnGroup {
|
|
297
|
+
id: string;
|
|
298
|
+
/** Id of the `categoryValue` supplying the group's label, if any. */
|
|
299
|
+
categoryValueRef?: string;
|
|
300
|
+
unknownAttributes: Record<string, string>;
|
|
301
|
+
}
|
|
267
302
|
/** A swim lane within a pool (organises flow nodes visually). */
|
|
268
303
|
export interface BpmnLane {
|
|
269
304
|
id: string;
|
|
@@ -294,6 +329,7 @@ export interface BpmnProcess {
|
|
|
294
329
|
sequenceFlows: BpmnSequenceFlow[];
|
|
295
330
|
textAnnotations: BpmnTextAnnotation[];
|
|
296
331
|
associations: BpmnAssociation[];
|
|
332
|
+
groups: BpmnGroup[];
|
|
297
333
|
laneSet?: BpmnLaneSet;
|
|
298
334
|
unknownAttributes: Record<string, string>;
|
|
299
335
|
}
|
|
@@ -319,6 +355,7 @@ export interface BpmnCollaboration {
|
|
|
319
355
|
messageFlows: BpmnMessageFlow[];
|
|
320
356
|
textAnnotations: BpmnTextAnnotation[];
|
|
321
357
|
associations: BpmnAssociation[];
|
|
358
|
+
groups: BpmnGroup[];
|
|
322
359
|
extensionElements: XmlElement[];
|
|
323
360
|
unknownAttributes: Record<string, string>;
|
|
324
361
|
}
|
package/dist/bpmn/bpmn-parser.js
CHANGED
|
@@ -59,6 +59,10 @@ const KNOWN_ATTRS = new Set([
|
|
|
59
59
|
"exporter",
|
|
60
60
|
"exporterVersion",
|
|
61
61
|
"processRef",
|
|
62
|
+
"dataObjectRef",
|
|
63
|
+
"dataStoreRef",
|
|
64
|
+
"isCollection",
|
|
65
|
+
"categoryValueRef",
|
|
62
66
|
]);
|
|
63
67
|
/** Extract unknown (namespace-qualified) attributes from an element. */
|
|
64
68
|
function unknownAttrs(element) {
|
|
@@ -261,6 +265,9 @@ const FLOW_ELEMENT_TYPES = new Set([
|
|
|
261
265
|
"inclusiveGateway",
|
|
262
266
|
"eventBasedGateway",
|
|
263
267
|
"complexGateway",
|
|
268
|
+
"dataObject",
|
|
269
|
+
"dataObjectReference",
|
|
270
|
+
"dataStoreReference",
|
|
264
271
|
]);
|
|
265
272
|
function parseFlowElement(element) {
|
|
266
273
|
const ln = localName(element.name);
|
|
@@ -321,13 +328,21 @@ function parseFlowElement(element) {
|
|
|
321
328
|
loopCharacteristics: parseLoopCharacteristics(element),
|
|
322
329
|
isForCompensation: attr(element, "isForCompensation") === "true" ? true : undefined,
|
|
323
330
|
};
|
|
324
|
-
case "adHocSubProcess":
|
|
331
|
+
case "adHocSubProcess": {
|
|
332
|
+
const completionEl = findChild(element, "completionCondition");
|
|
325
333
|
return {
|
|
326
334
|
...base,
|
|
327
335
|
type: "adHocSubProcess",
|
|
328
336
|
loopCharacteristics: parseLoopCharacteristics(element),
|
|
337
|
+
completionCondition: completionEl
|
|
338
|
+
? { text: completionEl.text ?? "", attributes: { ...completionEl.attributes } }
|
|
339
|
+
: undefined,
|
|
340
|
+
cancelRemainingInstances: attr(element, "cancelRemainingInstances") !== undefined
|
|
341
|
+
? attr(element, "cancelRemainingInstances") === "true"
|
|
342
|
+
: undefined,
|
|
329
343
|
...parseProcessContents(element),
|
|
330
344
|
};
|
|
345
|
+
}
|
|
331
346
|
case "subProcess":
|
|
332
347
|
return {
|
|
333
348
|
...base,
|
|
@@ -361,6 +376,25 @@ function parseFlowElement(element) {
|
|
|
361
376
|
return { ...base, type: "eventBasedGateway" };
|
|
362
377
|
case "complexGateway":
|
|
363
378
|
return { ...base, type: "complexGateway", default: attr(element, "default") };
|
|
379
|
+
case "dataObject":
|
|
380
|
+
return {
|
|
381
|
+
...base,
|
|
382
|
+
type: "dataObject",
|
|
383
|
+
...(attr(element, "isCollection") === "true" ? { isCollection: true } : {}),
|
|
384
|
+
};
|
|
385
|
+
case "dataObjectReference":
|
|
386
|
+
return {
|
|
387
|
+
...base,
|
|
388
|
+
type: "dataObjectReference",
|
|
389
|
+
dataObjectRef: attr(element, "dataObjectRef"),
|
|
390
|
+
...(attr(element, "isCollection") === "true" ? { isCollection: true } : {}),
|
|
391
|
+
};
|
|
392
|
+
case "dataStoreReference":
|
|
393
|
+
return {
|
|
394
|
+
...base,
|
|
395
|
+
type: "dataStoreReference",
|
|
396
|
+
dataStoreRef: attr(element, "dataStoreRef"),
|
|
397
|
+
};
|
|
364
398
|
default:
|
|
365
399
|
return undefined;
|
|
366
400
|
}
|
|
@@ -407,6 +441,13 @@ function parseAssociation(element) {
|
|
|
407
441
|
unknownAttributes: unknownAttrs(element),
|
|
408
442
|
};
|
|
409
443
|
}
|
|
444
|
+
function parseGroup(element) {
|
|
445
|
+
return {
|
|
446
|
+
id: requiredAttr(element, "id"),
|
|
447
|
+
categoryValueRef: attr(element, "categoryValueRef"),
|
|
448
|
+
unknownAttributes: unknownAttrs(element),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
410
451
|
// ---------------------------------------------------------------------------
|
|
411
452
|
// Process contents (shared between process and adHocSubProcess)
|
|
412
453
|
// ---------------------------------------------------------------------------
|
|
@@ -415,6 +456,7 @@ function parseProcessContents(element) {
|
|
|
415
456
|
const sequenceFlows = [];
|
|
416
457
|
const textAnnotations = [];
|
|
417
458
|
const associations = [];
|
|
459
|
+
const groups = [];
|
|
418
460
|
for (const child of element.children) {
|
|
419
461
|
const ln = localName(child.name);
|
|
420
462
|
if (FLOW_ELEMENT_TYPES.has(ln)) {
|
|
@@ -431,8 +473,11 @@ function parseProcessContents(element) {
|
|
|
431
473
|
else if (ln === "association") {
|
|
432
474
|
associations.push(parseAssociation(child));
|
|
433
475
|
}
|
|
476
|
+
else if (ln === "group") {
|
|
477
|
+
groups.push(parseGroup(child));
|
|
478
|
+
}
|
|
434
479
|
}
|
|
435
|
-
return { flowElements, sequenceFlows, textAnnotations, associations };
|
|
480
|
+
return { flowElements, sequenceFlows, textAnnotations, associations, groups };
|
|
436
481
|
}
|
|
437
482
|
// ---------------------------------------------------------------------------
|
|
438
483
|
// Lanes
|
|
@@ -498,6 +543,7 @@ function parseCollaboration(element) {
|
|
|
498
543
|
messageFlows: findChildren(element, "messageFlow").map(parseMessageFlow),
|
|
499
544
|
textAnnotations: findChildren(element, "textAnnotation").map(parseTextAnnotation),
|
|
500
545
|
associations: findChildren(element, "association").map(parseAssociation),
|
|
546
|
+
groups: findChildren(element, "group").map(parseGroup),
|
|
501
547
|
extensionElements: parseExtensionElements(element),
|
|
502
548
|
unknownAttributes: unknownAttrs(element),
|
|
503
549
|
};
|
|
@@ -202,8 +202,14 @@ function serializeFlowElement(fe, ns) {
|
|
|
202
202
|
children.push(...serializeLoopCharacteristics(fe.loopCharacteristics, bp));
|
|
203
203
|
break;
|
|
204
204
|
case "adHocSubProcess":
|
|
205
|
+
if (fe.cancelRemainingInstances !== undefined) {
|
|
206
|
+
attrs.cancelRemainingInstances = String(fe.cancelRemainingInstances);
|
|
207
|
+
}
|
|
205
208
|
children.push(...serializeLoopCharacteristics(fe.loopCharacteristics, bp));
|
|
206
209
|
children.push(...serializeProcessContents(fe, ns));
|
|
210
|
+
if (fe.completionCondition) {
|
|
211
|
+
children.push(el(`${bp}:completionCondition`, fe.completionCondition.attributes, [], fe.completionCondition.text));
|
|
212
|
+
}
|
|
207
213
|
break;
|
|
208
214
|
case "subProcess":
|
|
209
215
|
if (fe.triggeredByEvent !== undefined)
|
|
@@ -233,6 +239,20 @@ function serializeFlowElement(fe, ns) {
|
|
|
233
239
|
case "parallelGateway":
|
|
234
240
|
case "eventBasedGateway":
|
|
235
241
|
break;
|
|
242
|
+
case "dataObject":
|
|
243
|
+
if (fe.isCollection)
|
|
244
|
+
attrs.isCollection = "true";
|
|
245
|
+
break;
|
|
246
|
+
case "dataObjectReference":
|
|
247
|
+
if (fe.dataObjectRef !== undefined)
|
|
248
|
+
attrs.dataObjectRef = fe.dataObjectRef;
|
|
249
|
+
if (fe.isCollection)
|
|
250
|
+
attrs.isCollection = "true";
|
|
251
|
+
break;
|
|
252
|
+
case "dataStoreReference":
|
|
253
|
+
if (fe.dataStoreRef !== undefined)
|
|
254
|
+
attrs.dataStoreRef = fe.dataStoreRef;
|
|
255
|
+
break;
|
|
236
256
|
}
|
|
237
257
|
return el(`${bp}:${fe.type}`, attrs, children);
|
|
238
258
|
}
|
|
@@ -276,6 +296,12 @@ function serializeAssociation(a, bp) {
|
|
|
276
296
|
attrs.associationDirection = a.associationDirection;
|
|
277
297
|
return el(`${bp}:association`, attrs, []);
|
|
278
298
|
}
|
|
299
|
+
function serializeGroup(g, bp) {
|
|
300
|
+
const attrs = { id: g.id, ...g.unknownAttributes };
|
|
301
|
+
if (g.categoryValueRef !== undefined)
|
|
302
|
+
attrs.categoryValueRef = g.categoryValueRef;
|
|
303
|
+
return el(`${bp}:group`, attrs, []);
|
|
304
|
+
}
|
|
279
305
|
// ---------------------------------------------------------------------------
|
|
280
306
|
// Process contents
|
|
281
307
|
// ---------------------------------------------------------------------------
|
|
@@ -294,6 +320,10 @@ function serializeProcessContents(p, ns) {
|
|
|
294
320
|
for (const a of p.associations) {
|
|
295
321
|
children.push(serializeAssociation(a, bp));
|
|
296
322
|
}
|
|
323
|
+
// `groups` may be absent on hand-constructed partial models.
|
|
324
|
+
for (const g of p.groups ?? []) {
|
|
325
|
+
children.push(serializeGroup(g, bp));
|
|
326
|
+
}
|
|
297
327
|
return children;
|
|
298
328
|
}
|
|
299
329
|
// ---------------------------------------------------------------------------
|
|
@@ -370,6 +400,9 @@ function serializeCollaboration(c, ns) {
|
|
|
370
400
|
for (const a of c.associations) {
|
|
371
401
|
children.push(serializeAssociation(a, bp));
|
|
372
402
|
}
|
|
403
|
+
for (const g of c.groups ?? []) {
|
|
404
|
+
children.push(serializeGroup(g, bp));
|
|
405
|
+
}
|
|
373
406
|
return el(`${bp}:collaboration`, { id: c.id, ...c.unknownAttributes }, children);
|
|
374
407
|
}
|
|
375
408
|
// ---------------------------------------------------------------------------
|
package/dist/bpmn/compact.js
CHANGED
|
@@ -192,7 +192,13 @@ function makeExtensions(el) {
|
|
|
192
192
|
}
|
|
193
193
|
function buildSubContent(children) {
|
|
194
194
|
if (!children || children.elements.length === 0) {
|
|
195
|
-
return {
|
|
195
|
+
return {
|
|
196
|
+
flowElements: [],
|
|
197
|
+
sequenceFlows: [],
|
|
198
|
+
textAnnotations: [],
|
|
199
|
+
associations: [],
|
|
200
|
+
groups: [],
|
|
201
|
+
};
|
|
196
202
|
}
|
|
197
203
|
const inc = new Map();
|
|
198
204
|
const out = new Map();
|
|
@@ -217,6 +223,7 @@ function buildSubContent(children) {
|
|
|
217
223
|
})),
|
|
218
224
|
textAnnotations: [],
|
|
219
225
|
associations: [],
|
|
226
|
+
groups: [],
|
|
220
227
|
};
|
|
221
228
|
}
|
|
222
229
|
function buildFlowElement(el, incoming, outgoing) {
|
|
@@ -284,6 +291,8 @@ function buildFlowElement(el, incoming, outgoing) {
|
|
|
284
291
|
return { ...base, type: "eventBasedGateway" };
|
|
285
292
|
case "complexGateway":
|
|
286
293
|
return { ...base, type: "complexGateway" };
|
|
294
|
+
default:
|
|
295
|
+
return { ...base, type: "task" };
|
|
287
296
|
}
|
|
288
297
|
}
|
|
289
298
|
function buildDiagram(processId, process) {
|
|
@@ -342,6 +351,7 @@ function expandProcess(compact) {
|
|
|
342
351
|
sequenceFlows,
|
|
343
352
|
textAnnotations: [],
|
|
344
353
|
associations: [],
|
|
354
|
+
groups: [],
|
|
345
355
|
unknownAttributes: {},
|
|
346
356
|
};
|
|
347
357
|
return { process, diagram: buildDiagram(compact.id, process) };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BpmnDefinitions, BpmnDiPlane } from "./bpmn-model.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the DI plane whose `bpmnElement` matches `elementId`, if any.
|
|
4
|
+
*
|
|
5
|
+
* The primary plane's `bpmnElement` is a process or collaboration id; a
|
|
6
|
+
* collapsed sub-process that carries its own layout has a separate
|
|
7
|
+
* `BPMNDiagram` whose plane `bpmnElement` is the sub-process id. This resolves
|
|
8
|
+
* either.
|
|
9
|
+
*/
|
|
10
|
+
export declare function planeForElement(defs: BpmnDefinitions, elementId: string): BpmnDiPlane | undefined;
|
|
11
|
+
/** Lists every DI plane's `bpmnElement` id, in document order. */
|
|
12
|
+
export declare function listPlaneElementIds(defs: BpmnDefinitions): string[];
|
|
13
|
+
//# sourceMappingURL=di-planes.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the DI plane whose `bpmnElement` matches `elementId`, if any.
|
|
3
|
+
*
|
|
4
|
+
* The primary plane's `bpmnElement` is a process or collaboration id; a
|
|
5
|
+
* collapsed sub-process that carries its own layout has a separate
|
|
6
|
+
* `BPMNDiagram` whose plane `bpmnElement` is the sub-process id. This resolves
|
|
7
|
+
* either.
|
|
8
|
+
*/
|
|
9
|
+
export function planeForElement(defs, elementId) {
|
|
10
|
+
for (const diagram of defs.diagrams) {
|
|
11
|
+
if (diagram.plane.bpmnElement === elementId)
|
|
12
|
+
return diagram.plane;
|
|
13
|
+
}
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
/** Lists every DI plane's `bpmnElement` id, in document order. */
|
|
17
|
+
export function listPlaneElementIds(defs) {
|
|
18
|
+
return defs.diagrams.map((d) => d.plane.bpmnElement);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=di-planes.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BpmnProcess } from "../bpmn-model.js";
|
|
2
|
+
import type { OptimizationFinding } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Agentic-specific checks for the Camunda 8 AI Agent Sub-process pattern:
|
|
5
|
+
* tools must be root nodes with a description the LLM can read, every
|
|
6
|
+
* `fromAi()` call must reference `toolCall.*`, and the agent should aggregate
|
|
7
|
+
* tool results and cap its model-call budget.
|
|
8
|
+
*/
|
|
9
|
+
export declare function analyzeAgentic(p: BpmnProcess): OptimizationFinding[];
|
|
10
|
+
//# sourceMappingURL=agentic.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { AI_AGENT_JOB_WORKER_TASK_TYPE } from "../agentic.js";
|
|
2
|
+
import { readZeebeIoMapping, readZeebeTaskType } from "./utils.js";
|
|
3
|
+
const FROM_AI_CALL = /fromAi\(\s*([^,)]+)/g;
|
|
4
|
+
function isAiAgentSubProcess(el) {
|
|
5
|
+
return readZeebeTaskType(el.extensionElements) === AI_AGENT_JOB_WORKER_TASK_TYPE;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Agentic-specific checks for the Camunda 8 AI Agent Sub-process pattern:
|
|
9
|
+
* tools must be root nodes with a description the LLM can read, every
|
|
10
|
+
* `fromAi()` call must reference `toolCall.*`, and the agent should aggregate
|
|
11
|
+
* tool results and cap its model-call budget.
|
|
12
|
+
*/
|
|
13
|
+
export function analyzeAgentic(p) {
|
|
14
|
+
const findings = [];
|
|
15
|
+
const processId = p.id;
|
|
16
|
+
for (const el of p.flowElements) {
|
|
17
|
+
if (el.type !== "adHocSubProcess" || !isAiAgentSubProcess(el))
|
|
18
|
+
continue;
|
|
19
|
+
const adHocExt = el.extensionElements.find((e) => e.name === "zeebe:adHoc");
|
|
20
|
+
if (!adHocExt?.attributes.outputCollection) {
|
|
21
|
+
findings.push({
|
|
22
|
+
id: "agentic/no-output-collection",
|
|
23
|
+
category: "agentic",
|
|
24
|
+
severity: "warning",
|
|
25
|
+
message: `AI Agent "${el.name ?? el.id}" has no outputCollection — tool call results won't be aggregated.`,
|
|
26
|
+
suggestion: 'Set zeebe:adHoc outputCollection (e.g. "toolCallResults").',
|
|
27
|
+
processId,
|
|
28
|
+
elementIds: [el.id],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const io = readZeebeIoMapping(el.extensionElements);
|
|
32
|
+
const hasLimit = io?.inputs.some((i) => i.target === "data.limits.maxModelCalls") ?? false;
|
|
33
|
+
if (!hasLimit) {
|
|
34
|
+
findings.push({
|
|
35
|
+
id: "agentic/limits-missing",
|
|
36
|
+
category: "agentic",
|
|
37
|
+
severity: "info",
|
|
38
|
+
message: `AI Agent "${el.name ?? el.id}" has no data.limits.maxModelCalls binding.`,
|
|
39
|
+
suggestion: "Set a model-call limit as a safety net against infinite tool loops.",
|
|
40
|
+
processId,
|
|
41
|
+
elementIds: [el.id],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
for (const tool of el.flowElements) {
|
|
45
|
+
if (tool.incoming.length > 0) {
|
|
46
|
+
findings.push({
|
|
47
|
+
id: "agentic/tool-not-root",
|
|
48
|
+
category: "agentic",
|
|
49
|
+
severity: "error",
|
|
50
|
+
message: `"${tool.name ?? tool.id}" inside AI Agent "${el.name ?? el.id}" has an incoming sequence flow — the connector only discovers tools with no incoming flow.`,
|
|
51
|
+
suggestion: "Remove the incoming sequence flow; tools must be root nodes.",
|
|
52
|
+
processId,
|
|
53
|
+
elementIds: [tool.id],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (!tool.documentation?.trim()) {
|
|
57
|
+
findings.push({
|
|
58
|
+
id: "agentic/tool-no-description",
|
|
59
|
+
category: "agentic",
|
|
60
|
+
severity: "warning",
|
|
61
|
+
message: `Tool "${tool.name ?? tool.id}" has no documentation — the LLM sees no description for this tool.`,
|
|
62
|
+
suggestion: "Set <bpmn:documentation> describing what this tool does.",
|
|
63
|
+
processId,
|
|
64
|
+
elementIds: [tool.id],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const toolIo = readZeebeIoMapping(tool.extensionElements);
|
|
68
|
+
for (const input of toolIo?.inputs ?? []) {
|
|
69
|
+
for (const match of input.source.matchAll(FROM_AI_CALL)) {
|
|
70
|
+
const firstArg = match[1]?.trim();
|
|
71
|
+
if (firstArg && !firstArg.startsWith("toolCall.")) {
|
|
72
|
+
findings.push({
|
|
73
|
+
id: "agentic/fromai-bad-ref",
|
|
74
|
+
category: "agentic",
|
|
75
|
+
severity: "error",
|
|
76
|
+
message: `fromAi() on "${tool.name ?? tool.id}" input "${input.target}" references "${firstArg}", not a toolCall.* field.`,
|
|
77
|
+
suggestion: 'fromAi()\'s first argument must reference "toolCall.<param>".',
|
|
78
|
+
processId,
|
|
79
|
+
elementIds: [tool.id],
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return findings;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=agentic.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BpmnProcess } from "../bpmn-model.js";
|
|
2
|
+
import type { OptimizationFinding } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Given a bundled connector template id and the set of value keys bound on
|
|
5
|
+
* the element (io-mapping targets, task-header keys, zeebe:property names),
|
|
6
|
+
* returns the required keys that are missing. Inject `applyConnectorTemplate`-
|
|
7
|
+
* backed logic from `@bpmnkit/connectors` here; core stays dependency-free.
|
|
8
|
+
*/
|
|
9
|
+
export type ConnectorRequirementsResolver = (templateId: string, boundKeys: string[]) => string[];
|
|
10
|
+
/**
|
|
11
|
+
* Zeebe/Reebe deploy-parity checks — mirrors what Camunda 8 rejects at
|
|
12
|
+
* deployment time (`apps/reebe/crates/reebe-bpmn/src/validator.rs`), so a
|
|
13
|
+
* process that passes this profile deploys without a validation error.
|
|
14
|
+
*/
|
|
15
|
+
export declare function analyzeDeploy(p: BpmnProcess, resolveConnectorRequirements?: ConnectorRequirementsResolver): OptimizationFinding[];
|
|
16
|
+
//# sourceMappingURL=deploy.d.ts.map
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { readZeebeIoMapping, readZeebeTaskHeaders, readZeebeTaskType } from "./utils.js";
|
|
2
|
+
function findExt(el, name) {
|
|
3
|
+
return el.extensionElements.find((e) => e.name === name);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Zeebe/Reebe deploy-parity checks — mirrors what Camunda 8 rejects at
|
|
7
|
+
* deployment time (`apps/reebe/crates/reebe-bpmn/src/validator.rs`), so a
|
|
8
|
+
* process that passes this profile deploys without a validation error.
|
|
9
|
+
*/
|
|
10
|
+
export function analyzeDeploy(p, resolveConnectorRequirements) {
|
|
11
|
+
const findings = [];
|
|
12
|
+
const processId = p.id;
|
|
13
|
+
if (p.isExecutable !== true) {
|
|
14
|
+
findings.push({
|
|
15
|
+
id: "deploy/process-not-executable",
|
|
16
|
+
category: "deploy",
|
|
17
|
+
severity: "error",
|
|
18
|
+
message: `Process "${processId}" is not marked executable.`,
|
|
19
|
+
suggestion: 'Set isExecutable="true" — Camunda 8 refuses to deploy a non-executable process.',
|
|
20
|
+
processId,
|
|
21
|
+
elementIds: [],
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
for (const el of p.flowElements) {
|
|
25
|
+
if (el.type === "serviceTask" || el.type === "sendTask") {
|
|
26
|
+
const type = readZeebeTaskType(el.extensionElements);
|
|
27
|
+
if (!type) {
|
|
28
|
+
findings.push({
|
|
29
|
+
id: "deploy/service-task-no-type",
|
|
30
|
+
category: "deploy",
|
|
31
|
+
severity: "error",
|
|
32
|
+
message: `"${el.name ?? el.id}" (${el.type}) has no zeebe:taskDefinition type.`,
|
|
33
|
+
suggestion: "Set a job type — Camunda 8 refuses to deploy a task with no task definition.",
|
|
34
|
+
processId,
|
|
35
|
+
elementIds: [el.id],
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (el.type === "businessRuleTask") {
|
|
40
|
+
const hasType = readZeebeTaskType(el.extensionElements) !== null;
|
|
41
|
+
const hasDecision = findExt(el, "zeebe:calledDecision") !== undefined;
|
|
42
|
+
if (!hasType && !hasDecision) {
|
|
43
|
+
findings.push({
|
|
44
|
+
id: "deploy/service-task-no-type",
|
|
45
|
+
category: "deploy",
|
|
46
|
+
severity: "error",
|
|
47
|
+
message: `"${el.name ?? el.id}" (businessRuleTask) has neither a zeebe:taskDefinition type nor a zeebe:calledDecision.`,
|
|
48
|
+
suggestion: "Set a decisionId or a job type.",
|
|
49
|
+
processId,
|
|
50
|
+
elementIds: [el.id],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (el.type === "callActivity") {
|
|
55
|
+
const called = findExt(el, "zeebe:calledElement");
|
|
56
|
+
if (!called?.attributes.processId) {
|
|
57
|
+
findings.push({
|
|
58
|
+
id: "deploy/call-activity-no-process",
|
|
59
|
+
category: "deploy",
|
|
60
|
+
severity: "error",
|
|
61
|
+
message: `Call activity "${el.name ?? el.id}" has no zeebe:calledElement processId.`,
|
|
62
|
+
suggestion: "Set the process id to call.",
|
|
63
|
+
processId,
|
|
64
|
+
elementIds: [el.id],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (el.type === "startEvent") {
|
|
69
|
+
const messageDef = el.eventDefinitions.find((d) => d.type === "message");
|
|
70
|
+
if (messageDef && !messageDef.messageRef) {
|
|
71
|
+
findings.push({
|
|
72
|
+
id: "deploy/message-start-no-name",
|
|
73
|
+
category: "deploy",
|
|
74
|
+
severity: "error",
|
|
75
|
+
message: `Message start event "${el.name ?? el.id}" has no message name.`,
|
|
76
|
+
suggestion: "Set a message name — Camunda 8 refuses to deploy an unnamed message reference.",
|
|
77
|
+
processId,
|
|
78
|
+
elementIds: [el.id],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (el.type === "intermediateCatchEvent" ||
|
|
83
|
+
el.type === "boundaryEvent" ||
|
|
84
|
+
el.type === "receiveTask") {
|
|
85
|
+
const messageDef = el.type === "receiveTask"
|
|
86
|
+
? undefined
|
|
87
|
+
: el.eventDefinitions.find((d) => d.type === "message");
|
|
88
|
+
const isMessageCatch = el.type === "receiveTask" ? el.messageRef !== undefined : messageDef !== undefined;
|
|
89
|
+
if (isMessageCatch) {
|
|
90
|
+
const subscription = findExt(el, "zeebe:subscription");
|
|
91
|
+
if (!subscription?.attributes.correlationKey) {
|
|
92
|
+
findings.push({
|
|
93
|
+
id: "deploy/message-catch-no-correlation",
|
|
94
|
+
category: "deploy",
|
|
95
|
+
severity: "error",
|
|
96
|
+
message: `Message catch "${el.name ?? el.id}" (${el.type}) has no zeebe:subscription correlationKey.`,
|
|
97
|
+
suggestion: "Set a correlation key — required for every message catch in Camunda 8.",
|
|
98
|
+
processId,
|
|
99
|
+
elementIds: [el.id],
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (resolveConnectorRequirements) {
|
|
105
|
+
const templateId = el.unknownAttributes["zeebe:modelerTemplate"];
|
|
106
|
+
if (templateId) {
|
|
107
|
+
const boundKeys = new Set();
|
|
108
|
+
const io = readZeebeIoMapping(el.extensionElements);
|
|
109
|
+
if (io) {
|
|
110
|
+
for (const i of io.inputs)
|
|
111
|
+
boundKeys.add(i.target);
|
|
112
|
+
for (const o of io.outputs)
|
|
113
|
+
boundKeys.add(o.target);
|
|
114
|
+
}
|
|
115
|
+
const headers = readZeebeTaskHeaders(el.extensionElements);
|
|
116
|
+
if (headers)
|
|
117
|
+
for (const h of headers.headers)
|
|
118
|
+
boundKeys.add(h.key);
|
|
119
|
+
const propsExt = findExt(el, "zeebe:properties");
|
|
120
|
+
if (propsExt) {
|
|
121
|
+
for (const child of propsExt.children ?? []) {
|
|
122
|
+
if (child.attributes.name)
|
|
123
|
+
boundKeys.add(child.attributes.name);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const missing = resolveConnectorRequirements(templateId, [...boundKeys]);
|
|
127
|
+
for (const key of missing) {
|
|
128
|
+
findings.push({
|
|
129
|
+
id: "connector/missing-required",
|
|
130
|
+
category: "connector",
|
|
131
|
+
severity: "error",
|
|
132
|
+
message: `"${el.name ?? el.id}" is missing required connector value "${key}" for template "${templateId}".`,
|
|
133
|
+
suggestion: `Set a value for "${key}".`,
|
|
134
|
+
processId,
|
|
135
|
+
elementIds: [el.id],
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return findings;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BpmnProcess } from "../bpmn-model.js";
|
|
2
|
+
import type { OptimizationFinding } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Parse-validates every FEEL-looking expression (leading "=") in the process:
|
|
5
|
+
* sequence-flow conditions, zeebe:input/output sources, script task
|
|
6
|
+
* expressions, and ad-hoc sub-process completion conditions/outputElement —
|
|
7
|
+
* including inside nested sub-processes. Unlike `feel.ts` (heuristic
|
|
8
|
+
* complexity scoring), this uses the real `@bpmnkit/feel` parser and reports
|
|
9
|
+
* genuine syntax errors, not style suggestions.
|
|
10
|
+
*/
|
|
11
|
+
export declare function analyzeFeelSyntax(p: BpmnProcess): OptimizationFinding[];
|
|
12
|
+
//# sourceMappingURL=feel-syntax.d.ts.map
|