@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
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { parseExpression } from "@bpmnkit/feel";
|
|
2
|
+
import { readZeebeIoMapping } from "./utils.js";
|
|
3
|
+
/** Recursively yields every flow element in a process, including inside sub-processes/ad-hoc sub-processes. */
|
|
4
|
+
function* walkElements(elements) {
|
|
5
|
+
for (const el of elements) {
|
|
6
|
+
yield el;
|
|
7
|
+
if (el.type === "subProcess" ||
|
|
8
|
+
el.type === "adHocSubProcess" ||
|
|
9
|
+
el.type === "eventSubProcess" ||
|
|
10
|
+
el.type === "transaction") {
|
|
11
|
+
yield* walkElements(el.flowElements);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Recursively yields every sequence flow, including inside sub-processes/ad-hoc sub-processes. */
|
|
16
|
+
function* walkFlows(elements, topLevel) {
|
|
17
|
+
yield* topLevel;
|
|
18
|
+
for (const el of elements) {
|
|
19
|
+
if (el.type === "subProcess" ||
|
|
20
|
+
el.type === "adHocSubProcess" ||
|
|
21
|
+
el.type === "eventSubProcess" ||
|
|
22
|
+
el.type === "transaction") {
|
|
23
|
+
yield* walkFlows(el.flowElements, el.sequenceFlows);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function checkFeel(text, elementId, processId, surface, findings) {
|
|
28
|
+
const trimmed = text.trim();
|
|
29
|
+
if (!trimmed.startsWith("="))
|
|
30
|
+
return;
|
|
31
|
+
const { errors } = parseExpression(trimmed.slice(1));
|
|
32
|
+
for (const err of errors) {
|
|
33
|
+
findings.push({
|
|
34
|
+
id: "feel-syntax/parse-error",
|
|
35
|
+
category: "feel-syntax",
|
|
36
|
+
severity: "error",
|
|
37
|
+
message: `Invalid FEEL expression on ${surface} of "${elementId}": ${err.message}`,
|
|
38
|
+
suggestion: "Fix the FEEL syntax — see the error position for the offending token.",
|
|
39
|
+
processId,
|
|
40
|
+
elementIds: [elementId],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Parse-validates every FEEL-looking expression (leading "=") in the process:
|
|
46
|
+
* sequence-flow conditions, zeebe:input/output sources, script task
|
|
47
|
+
* expressions, and ad-hoc sub-process completion conditions/outputElement —
|
|
48
|
+
* including inside nested sub-processes. Unlike `feel.ts` (heuristic
|
|
49
|
+
* complexity scoring), this uses the real `@bpmnkit/feel` parser and reports
|
|
50
|
+
* genuine syntax errors, not style suggestions.
|
|
51
|
+
*/
|
|
52
|
+
export function analyzeFeelSyntax(p) {
|
|
53
|
+
const findings = [];
|
|
54
|
+
const processId = p.id;
|
|
55
|
+
for (const flow of walkFlows(p.flowElements, p.sequenceFlows)) {
|
|
56
|
+
if (flow.conditionExpression?.text) {
|
|
57
|
+
checkFeel(flow.conditionExpression.text, flow.id, processId, "condition", findings);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const el of walkElements(p.flowElements)) {
|
|
61
|
+
const io = readZeebeIoMapping(el.extensionElements);
|
|
62
|
+
if (io) {
|
|
63
|
+
for (const input of io.inputs)
|
|
64
|
+
checkFeel(input.source, el.id, processId, `input "${input.target}"`, findings);
|
|
65
|
+
for (const output of io.outputs)
|
|
66
|
+
checkFeel(output.source, el.id, processId, `output "${output.target}"`, findings);
|
|
67
|
+
}
|
|
68
|
+
const scriptExt = el.extensionElements.find((e) => e.name === "zeebe:script");
|
|
69
|
+
if (scriptExt?.attributes.expression) {
|
|
70
|
+
checkFeel(scriptExt.attributes.expression, el.id, processId, "script expression", findings);
|
|
71
|
+
}
|
|
72
|
+
if (el.type === "adHocSubProcess") {
|
|
73
|
+
if (el.completionCondition?.text) {
|
|
74
|
+
checkFeel(el.completionCondition.text, el.id, processId, "completion condition", findings);
|
|
75
|
+
}
|
|
76
|
+
const adHocExt = el.extensionElements.find((e) => e.name === "zeebe:adHoc");
|
|
77
|
+
if (adHocExt?.attributes.outputElement) {
|
|
78
|
+
checkFeel(adHocExt.attributes.outputElement, el.id, processId, "outputElement", findings);
|
|
79
|
+
}
|
|
80
|
+
if (adHocExt?.attributes.activeElementsCollection) {
|
|
81
|
+
checkFeel(adHocExt.attributes.activeElementsCollection, el.id, processId, "activeElementsCollection", findings);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return findings;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=feel-syntax.js.map
|
|
@@ -63,10 +63,13 @@ export function analyzeFeel(p, opts) {
|
|
|
63
63
|
// Track FEEL expressions across all sequence flows for duplicate detection
|
|
64
64
|
const exprToFlowIds = new Map();
|
|
65
65
|
for (const flow of p.sequenceFlows) {
|
|
66
|
-
// Determine if source is an exclusive/inclusive gateway
|
|
66
|
+
// Determine if source is an exclusive/inclusive gateway with more than one
|
|
67
|
+
// outgoing flow — a join (single outgoing flow) makes no decision, so its
|
|
68
|
+
// one outgoing flow needs neither a condition nor a default marker.
|
|
67
69
|
const srcEl = p.flowElements.find((e) => e.id === flow.sourceRef);
|
|
68
70
|
const srcIsDecisionGateway = srcEl !== undefined &&
|
|
69
|
-
(srcEl.type === "exclusiveGateway" || srcEl.type === "inclusiveGateway")
|
|
71
|
+
(srcEl.type === "exclusiveGateway" || srcEl.type === "inclusiveGateway") &&
|
|
72
|
+
(bySource.get(srcEl.id)?.length ?? 0) > 1;
|
|
70
73
|
if (srcIsDecisionGateway && srcEl !== undefined) {
|
|
71
74
|
const defaultFlowId = srcEl.type === "exclusiveGateway" || srcEl.type === "inclusiveGateway"
|
|
72
75
|
? srcEl.default
|
|
@@ -32,8 +32,28 @@ export function analyzeFlow(p, _opts) {
|
|
|
32
32
|
elementIds: [],
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
-
// BFS reachability from start events
|
|
36
|
-
|
|
35
|
+
// BFS reachability from start events. Boundary events have no incoming
|
|
36
|
+
// sequence flow by design (they attach via `attachedToRef`), so add a
|
|
37
|
+
// synthetic edge from each host element to its boundary event(s) —
|
|
38
|
+
// otherwise every boundary event (and everything chained after it) would
|
|
39
|
+
// always be flagged as unreachable.
|
|
40
|
+
const reachabilityEdges = new Map(bySource);
|
|
41
|
+
for (const el of p.flowElements) {
|
|
42
|
+
if (el.type !== "boundaryEvent")
|
|
43
|
+
continue;
|
|
44
|
+
const hostEdges = reachabilityEdges.get(el.attachedToRef) ?? [];
|
|
45
|
+
reachabilityEdges.set(el.attachedToRef, [
|
|
46
|
+
...hostEdges,
|
|
47
|
+
{
|
|
48
|
+
id: `synthetic-attachment-${el.id}`,
|
|
49
|
+
sourceRef: el.attachedToRef,
|
|
50
|
+
targetRef: el.id,
|
|
51
|
+
extensionElements: [],
|
|
52
|
+
unknownAttributes: {},
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
const reachable = reachableFrom(startIds, reachabilityEdges);
|
|
37
57
|
for (const el of p.flowElements) {
|
|
38
58
|
// flow/unreachable
|
|
39
59
|
if (!reachable.has(el.id)) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { analyzeAgentic } from "./agentic.js";
|
|
2
|
+
import { analyzeDeploy } from "./deploy.js";
|
|
3
|
+
import { analyzeFeelSyntax } from "./feel-syntax.js";
|
|
1
4
|
import { analyzeFeel } from "./feel.js";
|
|
2
5
|
import { analyzeFlow } from "./flow.js";
|
|
3
6
|
import { analyzeNaming } from "./naming.js";
|
|
@@ -6,12 +9,16 @@ import { analyzeTasks } from "./tasks.js";
|
|
|
6
9
|
import { analyzeVariableFlow } from "./variable-flow.js";
|
|
7
10
|
const ALL_CATEGORIES = [
|
|
8
11
|
"feel",
|
|
12
|
+
"feel-syntax",
|
|
9
13
|
"flow",
|
|
10
14
|
"naming",
|
|
11
15
|
"task-reuse",
|
|
12
16
|
"extract",
|
|
13
17
|
"pattern",
|
|
14
18
|
"data-flow",
|
|
19
|
+
"deploy",
|
|
20
|
+
"agentic",
|
|
21
|
+
"connector",
|
|
15
22
|
];
|
|
16
23
|
function resolveOptions(opts) {
|
|
17
24
|
return {
|
|
@@ -21,6 +28,7 @@ function resolveOptions(opts) {
|
|
|
21
28
|
feelVariableThreshold: opts?.feelVariableThreshold ?? 4,
|
|
22
29
|
reuseThreshold: opts?.reuseThreshold ?? 2,
|
|
23
30
|
categories: opts?.categories ?? [...ALL_CATEGORIES],
|
|
31
|
+
resolveConnectorRequirements: opts?.resolveConnectorRequirements,
|
|
24
32
|
};
|
|
25
33
|
}
|
|
26
34
|
/** Run static analysis on a BPMN definitions object. */
|
|
@@ -31,6 +39,9 @@ export function optimize(defs, options) {
|
|
|
31
39
|
if (resolved.categories.includes("feel")) {
|
|
32
40
|
findings.push(...analyzeFeel(process, resolved));
|
|
33
41
|
}
|
|
42
|
+
if (resolved.categories.includes("feel-syntax")) {
|
|
43
|
+
findings.push(...analyzeFeelSyntax(process));
|
|
44
|
+
}
|
|
34
45
|
if (resolved.categories.includes("flow")) {
|
|
35
46
|
findings.push(...analyzeFlow(process, resolved));
|
|
36
47
|
}
|
|
@@ -46,16 +57,16 @@ export function optimize(defs, options) {
|
|
|
46
57
|
if (resolved.categories.includes("data-flow")) {
|
|
47
58
|
findings.push(...analyzeVariableFlow(process));
|
|
48
59
|
}
|
|
60
|
+
if (resolved.categories.includes("deploy") || resolved.categories.includes("connector")) {
|
|
61
|
+
const deployFindings = analyzeDeploy(process, resolved.resolveConnectorRequirements);
|
|
62
|
+
findings.push(...deployFindings.filter((f) => (f.category === "deploy" && resolved.categories.includes("deploy")) ||
|
|
63
|
+
(f.category === "connector" && resolved.categories.includes("connector"))));
|
|
64
|
+
}
|
|
65
|
+
if (resolved.categories.includes("agentic")) {
|
|
66
|
+
findings.push(...analyzeAgentic(process));
|
|
67
|
+
}
|
|
49
68
|
}
|
|
50
|
-
const byCategory = Object.fromEntries([
|
|
51
|
-
"feel",
|
|
52
|
-
"flow",
|
|
53
|
-
"naming",
|
|
54
|
-
"task-reuse",
|
|
55
|
-
"extract",
|
|
56
|
-
"pattern",
|
|
57
|
-
"data-flow",
|
|
58
|
-
].map((c) => [c, findings.filter((f) => f.category === c).length]));
|
|
69
|
+
const byCategory = Object.fromEntries(ALL_CATEGORIES.map((c) => [c, findings.filter((f) => f.category === c).length]));
|
|
59
70
|
const bySeverity = Object.fromEntries(["info", "warning", "error"].map((s) => [
|
|
60
71
|
s,
|
|
61
72
|
findings.filter((f) => f.severity === s).length,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BpmnDefinitions } from "../bpmn-model.js";
|
|
2
2
|
export type OptimizationSeverity = "info" | "warning" | "error";
|
|
3
|
-
export type OptimizationCategory = "feel" | "flow" | "naming" | "task-reuse" | "extract" | "pattern" | "data-flow";
|
|
3
|
+
export type OptimizationCategory = "feel" | "feel-syntax" | "flow" | "naming" | "task-reuse" | "extract" | "pattern" | "data-flow" | "deploy" | "agentic" | "connector";
|
|
4
4
|
export interface ApplyFixResult {
|
|
5
5
|
description: string;
|
|
6
6
|
/** New BpmnDefinitions generated by the fix (e.g. extracted reusable sub-process). */
|
|
@@ -36,6 +36,14 @@ export interface OptimizeOptions {
|
|
|
36
36
|
feelVariableThreshold?: number;
|
|
37
37
|
reuseThreshold?: number;
|
|
38
38
|
categories?: OptimizationCategory[];
|
|
39
|
+
/**
|
|
40
|
+
* Resolves a bundled connector template's missing required value keys —
|
|
41
|
+
* pass `(templateId, boundKeys) => applyConnectorTemplate(templateId,
|
|
42
|
+
* Object.fromEntries(boundKeys.map(k => [k, "x"]))).problems...` or an
|
|
43
|
+
* equivalent from `@bpmnkit/connectors`. Without it, `connector/*` findings
|
|
44
|
+
* are skipped (core has no dependency on the connector catalog).
|
|
45
|
+
*/
|
|
46
|
+
resolveConnectorRequirements?: (templateId: string, boundKeys: string[]) => string[];
|
|
39
47
|
}
|
|
40
48
|
export interface ResolvedOptions {
|
|
41
49
|
feelLengthThreshold: number;
|
|
@@ -44,5 +52,6 @@ export interface ResolvedOptions {
|
|
|
44
52
|
feelVariableThreshold: number;
|
|
45
53
|
reuseThreshold: number;
|
|
46
54
|
categories: OptimizationCategory[];
|
|
55
|
+
resolveConnectorRequirements?: (templateId: string, boundKeys: string[]) => string[];
|
|
47
56
|
}
|
|
48
57
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/bpmn/svg.js
CHANGED
|
@@ -155,7 +155,18 @@ export function exportSvg(defs, options) {
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
else {
|
|
158
|
-
|
|
158
|
+
// An expanded sub-process/transaction draws an opaque body rect that must
|
|
159
|
+
// render before (under) its own internal edges and child shapes, the same
|
|
160
|
+
// reason pools/lanes are treated as containers above — otherwise the body
|
|
161
|
+
// paints over everything nested inside it.
|
|
162
|
+
const isExpandedContainer = (type === "subProcess" ||
|
|
163
|
+
type === "adHocSubProcess" ||
|
|
164
|
+
type === "eventSubProcess" ||
|
|
165
|
+
type === "transaction") &&
|
|
166
|
+
shape.isExpanded === true;
|
|
167
|
+
inner = renderTask(el, width, height, t, { expanded: isExpandedContainer });
|
|
168
|
+
if (isExpandedContainer)
|
|
169
|
+
isContainer = true;
|
|
159
170
|
}
|
|
160
171
|
if (inner) {
|
|
161
172
|
const g = `<g transform="translate(${x} ${y})">${inner}</g>`;
|
|
@@ -442,7 +453,7 @@ function renderEvent(el, width, height, t) {
|
|
|
442
453
|
}
|
|
443
454
|
return out;
|
|
444
455
|
}
|
|
445
|
-
function renderTask(el, width, height, t) {
|
|
456
|
+
function renderTask(el, width, height, t, options) {
|
|
446
457
|
const type = el?.type ?? "";
|
|
447
458
|
let sw = 1.5;
|
|
448
459
|
let dash;
|
|
@@ -461,7 +472,15 @@ function renderTask(el, width, height, t) {
|
|
|
461
472
|
out += iconGroup(icon, 4, 4, t);
|
|
462
473
|
}
|
|
463
474
|
if (el?.name) {
|
|
464
|
-
|
|
475
|
+
// An expanded container's name sits in a top label, like a modeler's pool/
|
|
476
|
+
// subprocess header, so it doesn't collide with the child shapes drawn
|
|
477
|
+
// inside it (a centered label would land right where children are placed).
|
|
478
|
+
if (options?.expanded) {
|
|
479
|
+
out += labelSvg(el.name, width / 2, 14, width - 16, t, true);
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
out += labelSvg(el.name, width / 2, height / 2, width - 16, t);
|
|
483
|
+
}
|
|
465
484
|
}
|
|
466
485
|
if (type === "subProcess" ||
|
|
467
486
|
type === "adHocSubProcess" ||
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BpmnAdHocSubProcess, BpmnBoundaryEvent, BpmnBusinessRuleTask, BpmnCallActivity, BpmnComplexGateway, BpmnEndEvent, BpmnEventBasedGateway, BpmnEventSubProcess, BpmnExclusiveGateway, BpmnFlowElement, BpmnInclusiveGateway, BpmnIntermediateCatchEvent, BpmnIntermediateThrowEvent, BpmnManualTask, BpmnParallelGateway, BpmnReceiveTask, BpmnScriptTask, BpmnSendTask, BpmnServiceTask, BpmnStartEvent, BpmnSubProcess, BpmnTask, BpmnTransaction, BpmnUserTask } from "./bpmn-model.js";
|
|
1
|
+
import type { BpmnAdHocSubProcess, BpmnBoundaryEvent, BpmnBusinessRuleTask, BpmnCallActivity, BpmnComplexGateway, BpmnDataObject, BpmnDataObjectReference, BpmnDataStoreReference, BpmnEndEvent, BpmnEventBasedGateway, BpmnEventSubProcess, BpmnExclusiveGateway, BpmnFlowElement, BpmnInclusiveGateway, BpmnIntermediateCatchEvent, BpmnIntermediateThrowEvent, BpmnManualTask, BpmnParallelGateway, BpmnReceiveTask, BpmnScriptTask, BpmnSendTask, BpmnServiceTask, BpmnStartEvent, BpmnSubProcess, BpmnTask, BpmnTransaction, BpmnUserTask } from "./bpmn-model.js";
|
|
2
2
|
/**
|
|
3
3
|
* Narrows a flow element to {@link BpmnStartEvent}.
|
|
4
4
|
*
|
|
@@ -133,4 +133,10 @@ export declare function isBpmnComplexGateway(el: BpmnFlowElement): el is BpmnCom
|
|
|
133
133
|
* ```
|
|
134
134
|
*/
|
|
135
135
|
export declare function isBpmnGateway(el: BpmnFlowElement): el is BpmnExclusiveGateway | BpmnParallelGateway | BpmnInclusiveGateway | BpmnEventBasedGateway | BpmnComplexGateway;
|
|
136
|
+
/** Narrows a flow element to {@link BpmnDataObject}. */
|
|
137
|
+
export declare function isBpmnDataObject(el: BpmnFlowElement): el is BpmnDataObject;
|
|
138
|
+
/** Narrows a flow element to {@link BpmnDataObjectReference}. */
|
|
139
|
+
export declare function isBpmnDataObjectReference(el: BpmnFlowElement): el is BpmnDataObjectReference;
|
|
140
|
+
/** Narrows a flow element to {@link BpmnDataStoreReference}. */
|
|
141
|
+
export declare function isBpmnDataStoreReference(el: BpmnFlowElement): el is BpmnDataStoreReference;
|
|
136
142
|
//# sourceMappingURL=type-guards.d.ts.map
|
package/dist/bpmn/type-guards.js
CHANGED
|
@@ -208,4 +208,17 @@ export function isBpmnGateway(el) {
|
|
|
208
208
|
el.type === "eventBasedGateway" ||
|
|
209
209
|
el.type === "complexGateway");
|
|
210
210
|
}
|
|
211
|
+
// ── Data ──────────────────────────────────────────────────────────────────────
|
|
212
|
+
/** Narrows a flow element to {@link BpmnDataObject}. */
|
|
213
|
+
export function isBpmnDataObject(el) {
|
|
214
|
+
return el.type === "dataObject";
|
|
215
|
+
}
|
|
216
|
+
/** Narrows a flow element to {@link BpmnDataObjectReference}. */
|
|
217
|
+
export function isBpmnDataObjectReference(el) {
|
|
218
|
+
return el.type === "dataObjectReference";
|
|
219
|
+
}
|
|
220
|
+
/** Narrows a flow element to {@link BpmnDataStoreReference}. */
|
|
221
|
+
export function isBpmnDataStoreReference(el) {
|
|
222
|
+
return el.type === "dataStoreReference";
|
|
223
|
+
}
|
|
211
224
|
//# sourceMappingURL=type-guards.js.map
|
|
@@ -53,6 +53,25 @@ export interface ZeebeCalledDecision {
|
|
|
53
53
|
/** Process variable that receives the decision result. */
|
|
54
54
|
resultVariable: string;
|
|
55
55
|
}
|
|
56
|
+
/** Zeebe user task assignment (assignee / candidate groups / candidate users). */
|
|
57
|
+
export interface ZeebeAssignmentDefinition {
|
|
58
|
+
assignee?: string;
|
|
59
|
+
candidateGroups?: string;
|
|
60
|
+
candidateUsers?: string;
|
|
61
|
+
}
|
|
62
|
+
/** Zeebe user task scheduling (due date / follow-up date). */
|
|
63
|
+
export interface ZeebeTaskSchedule {
|
|
64
|
+
dueDate?: string;
|
|
65
|
+
followUpDate?: string;
|
|
66
|
+
}
|
|
67
|
+
/** Zeebe user task priority (0-100, default 50). */
|
|
68
|
+
export interface ZeebePriorityDefinition {
|
|
69
|
+
priority: string;
|
|
70
|
+
}
|
|
71
|
+
/** Zeebe message correlation key for a message catch/boundary/receive element. */
|
|
72
|
+
export interface ZeebeSubscription {
|
|
73
|
+
correlationKey: string;
|
|
74
|
+
}
|
|
56
75
|
/** Collected Zeebe extensions on a service task. */
|
|
57
76
|
export interface ZeebeExtensions {
|
|
58
77
|
taskDefinition?: ZeebeTaskDefinition;
|
|
@@ -66,6 +85,14 @@ export interface ZeebeExtensions {
|
|
|
66
85
|
calledDecision?: ZeebeCalledDecision;
|
|
67
86
|
/** Marks this as a Camunda 8 native user task (zeebe:userTask). */
|
|
68
87
|
userTask?: true;
|
|
88
|
+
/** User task assignee/candidates (zeebe:assignmentDefinition). */
|
|
89
|
+
assignmentDefinition?: ZeebeAssignmentDefinition;
|
|
90
|
+
/** User task due/follow-up dates (zeebe:taskSchedule). */
|
|
91
|
+
taskSchedule?: ZeebeTaskSchedule;
|
|
92
|
+
/** User task priority (zeebe:priorityDefinition). */
|
|
93
|
+
priorityDefinition?: ZeebePriorityDefinition;
|
|
94
|
+
/** Message correlation key for a message catch/boundary/receive element (zeebe:subscription). */
|
|
95
|
+
subscription?: ZeebeSubscription;
|
|
69
96
|
/** Unrecognized extension elements preserved for roundtrip. */
|
|
70
97
|
unknownElements?: XmlElement[];
|
|
71
98
|
}
|
|
@@ -91,6 +91,44 @@ export function zeebeExtensionsToXmlElements(extensions) {
|
|
|
91
91
|
children: [],
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
+
if (extensions.assignmentDefinition) {
|
|
95
|
+
const attrs = {};
|
|
96
|
+
const { assignee, candidateGroups, candidateUsers } = extensions.assignmentDefinition;
|
|
97
|
+
if (assignee)
|
|
98
|
+
attrs.assignee = assignee;
|
|
99
|
+
if (candidateGroups)
|
|
100
|
+
attrs.candidateGroups = candidateGroups;
|
|
101
|
+
if (candidateUsers)
|
|
102
|
+
attrs.candidateUsers = candidateUsers;
|
|
103
|
+
if (Object.keys(attrs).length > 0) {
|
|
104
|
+
elements.push({ name: "zeebe:assignmentDefinition", attributes: attrs, children: [] });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (extensions.taskSchedule) {
|
|
108
|
+
const attrs = {};
|
|
109
|
+
if (extensions.taskSchedule.dueDate)
|
|
110
|
+
attrs.dueDate = extensions.taskSchedule.dueDate;
|
|
111
|
+
if (extensions.taskSchedule.followUpDate) {
|
|
112
|
+
attrs.followUpDate = extensions.taskSchedule.followUpDate;
|
|
113
|
+
}
|
|
114
|
+
if (Object.keys(attrs).length > 0) {
|
|
115
|
+
elements.push({ name: "zeebe:taskSchedule", attributes: attrs, children: [] });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (extensions.priorityDefinition) {
|
|
119
|
+
elements.push({
|
|
120
|
+
name: "zeebe:priorityDefinition",
|
|
121
|
+
attributes: { priority: extensions.priorityDefinition.priority },
|
|
122
|
+
children: [],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (extensions.subscription) {
|
|
126
|
+
elements.push({
|
|
127
|
+
name: "zeebe:subscription",
|
|
128
|
+
attributes: { correlationKey: extensions.subscription.correlationKey },
|
|
129
|
+
children: [],
|
|
130
|
+
});
|
|
131
|
+
}
|
|
94
132
|
if (extensions.unknownElements) {
|
|
95
133
|
elements.push(...extensions.unknownElements);
|
|
96
134
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
export { BpmnSdkError, ParseError, ValidationError } from "./errors.js";
|
|
2
2
|
export type { ErrorCode } from "./errors.js";
|
|
3
|
-
export { isBpmnActivity, isBpmnAdHocSubProcess, isBpmnBoundaryEvent, isBpmnBusinessRuleTask, isBpmnCallActivity, isBpmnComplexGateway, isBpmnEndEvent, isBpmnEvent, isBpmnEventBasedGateway, isBpmnEventSubProcess, isBpmnExclusiveGateway, isBpmnGateway, isBpmnInclusiveGateway, isBpmnIntermediateCatchEvent, isBpmnIntermediateThrowEvent, isBpmnManualTask, isBpmnParallelGateway, isBpmnReceiveTask, isBpmnScriptTask, isBpmnSendTask, isBpmnServiceTask, isBpmnStartEvent, isBpmnSubProcess, isBpmnTask, isBpmnTransaction, isBpmnUserTask, } from "./bpmn/type-guards.js";
|
|
3
|
+
export { isBpmnActivity, isBpmnAdHocSubProcess, isBpmnBoundaryEvent, isBpmnBusinessRuleTask, isBpmnCallActivity, isBpmnComplexGateway, isBpmnDataObject, isBpmnDataObjectReference, isBpmnDataStoreReference, isBpmnEndEvent, isBpmnEvent, isBpmnEventBasedGateway, isBpmnEventSubProcess, isBpmnExclusiveGateway, isBpmnGateway, isBpmnInclusiveGateway, isBpmnIntermediateCatchEvent, isBpmnIntermediateThrowEvent, isBpmnManualTask, isBpmnParallelGateway, isBpmnReceiveTask, isBpmnScriptTask, isBpmnSendTask, isBpmnServiceTask, isBpmnStartEvent, isBpmnSubProcess, isBpmnTask, isBpmnTransaction, isBpmnUserTask, } from "./bpmn/type-guards.js";
|
|
4
4
|
export { findElement, findElementInProcess, findProcess, findSequenceFlow, getAllElements, getElementType, getZeebeExtensions, } from "./bpmn/utils.js";
|
|
5
5
|
export { Bpmn, SAMPLE_BPMN_XML } from "./bpmn/index.js";
|
|
6
6
|
export { applyAutoLayout } from "./bpmn/auto-layout.js";
|
|
7
7
|
export { checkDiCompleteness } from "./bpmn/di-check.js";
|
|
8
8
|
export type { DiCompleteness } from "./bpmn/di-check.js";
|
|
9
|
+
export { planeForElement, listPlaneElementIds } from "./bpmn/di-planes.js";
|
|
9
10
|
export { DiagramBuilder } from "./bpmn/bpmn-builder.js";
|
|
10
11
|
export type { ProcessBuilder, BranchBuilder, SubProcessContentBuilder, ServiceTaskOptions, ScriptTaskOptions, UserTaskOptions, CallActivityOptions, BusinessRuleTaskOptions, ElementOptions, GatewayOptions, MultiInstanceOptions, SubProcessOptions, StartEventOptions, IntermediateCatchEventOptions, IntermediateThrowEventOptions, EndEventOptions, BoundaryEventOptions, AdHocSubProcessOptions, } from "./bpmn/bpmn-builder.js";
|
|
11
|
-
export type { BpmnDefinitions, BpmnProcess, BpmnFlowNode, BpmnFlowElement, BpmnSequenceFlow, BpmnBoundaryEvent, BpmnElementType, BpmnStartEvent, BpmnEndEvent, BpmnIntermediateCatchEvent, BpmnIntermediateThrowEvent, BpmnTask, BpmnServiceTask, BpmnScriptTask, BpmnUserTask, BpmnSendTask, BpmnReceiveTask, BpmnBusinessRuleTask, BpmnManualTask, BpmnCallActivity, BpmnSubProcess, BpmnAdHocSubProcess, BpmnEventSubProcess, BpmnTransaction, BpmnExclusiveGateway, BpmnParallelGateway, BpmnInclusiveGateway, BpmnEventBasedGateway, BpmnComplexGateway, BpmnCollaboration, BpmnParticipant, BpmnMessageFlow, BpmnLane, BpmnLaneSet, BpmnError, BpmnEscalation, BpmnMessage, BpmnSignal, BpmnTextAnnotation, BpmnAssociation, BpmnConditionExpression, BpmnEventDefinition, BpmnTimerEventDefinition, BpmnErrorEventDefinition, BpmnEscalationEventDefinition, BpmnMessageEventDefinition, BpmnSignalEventDefinition, BpmnConditionalEventDefinition, BpmnLinkEventDefinition, BpmnCancelEventDefinition, BpmnTerminateEventDefinition, BpmnCompensateEventDefinition, BpmnMultiInstanceLoopCharacteristics, BpmnDiagram, BpmnDiPlane, BpmnDiShape, BpmnDiEdge, BpmnDiLabel, BpmnBounds, BpmnWaypoint, } from "./bpmn/bpmn-model.js";
|
|
12
|
+
export type { BpmnDefinitions, BpmnProcess, BpmnFlowNode, BpmnFlowElement, BpmnSequenceFlow, BpmnBoundaryEvent, BpmnElementType, BpmnStartEvent, BpmnEndEvent, BpmnIntermediateCatchEvent, BpmnIntermediateThrowEvent, BpmnTask, BpmnServiceTask, BpmnScriptTask, BpmnUserTask, BpmnSendTask, BpmnReceiveTask, BpmnBusinessRuleTask, BpmnManualTask, BpmnCallActivity, BpmnSubProcess, BpmnAdHocSubProcess, BpmnEventSubProcess, BpmnTransaction, BpmnExclusiveGateway, BpmnParallelGateway, BpmnInclusiveGateway, BpmnEventBasedGateway, BpmnComplexGateway, BpmnCollaboration, BpmnParticipant, BpmnMessageFlow, BpmnLane, BpmnLaneSet, BpmnError, BpmnEscalation, BpmnMessage, BpmnSignal, BpmnTextAnnotation, BpmnAssociation, BpmnGroup, BpmnDataObject, BpmnDataObjectReference, BpmnDataStoreReference, BpmnConditionExpression, BpmnEventDefinition, BpmnTimerEventDefinition, BpmnErrorEventDefinition, BpmnEscalationEventDefinition, BpmnMessageEventDefinition, BpmnSignalEventDefinition, BpmnConditionalEventDefinition, BpmnLinkEventDefinition, BpmnCancelEventDefinition, BpmnTerminateEventDefinition, BpmnCompensateEventDefinition, BpmnMultiInstanceLoopCharacteristics, BpmnDiagram, BpmnDiPlane, BpmnDiShape, BpmnDiEdge, BpmnDiLabel, BpmnBounds, BpmnWaypoint, } from "./bpmn/bpmn-model.js";
|
|
12
13
|
export type { RestConnectorConfig, RestAuthentication, HttpMethod, } from "./bpmn/rest-connector.js";
|
|
13
|
-
export type { ZeebeExtensions, ZeebeTaskDefinition, ZeebeIoMapping, ZeebeIoMappingEntry, ZeebeTaskHeaders, ZeebeTaskHeaderEntry, ZeebeProperties, ZeebePropertyEntry, ZeebeFormDefinition, ZeebeCalledDecision, } from "./bpmn/zeebe-extensions.js";
|
|
14
|
+
export type { ZeebeExtensions, ZeebeTaskDefinition, ZeebeIoMapping, ZeebeIoMappingEntry, ZeebeTaskHeaders, ZeebeTaskHeaderEntry, ZeebeProperties, ZeebePropertyEntry, ZeebeFormDefinition, ZeebeCalledDecision, ZeebeAssignmentDefinition, ZeebeTaskSchedule, ZeebePriorityDefinition, ZeebeSubscription, } from "./bpmn/zeebe-extensions.js";
|
|
14
15
|
export { zeebeExtensionsToXmlElements } from "./bpmn/zeebe-extensions.js";
|
|
16
|
+
export { buildAiAgentSubProcess, AI_AGENT_JOB_WORKER_TASK_TYPE, AI_AGENT_DEFAULT_OUTPUT_ELEMENT, } from "./bpmn/agentic.js";
|
|
17
|
+
export type { AiAgentOptions, AiAgentModelConfig, AiAgentToolSpec, AiAgentToolParam, AiAgentToolParamType, AiAgentBuild, } from "./bpmn/agentic.js";
|
|
15
18
|
export { Dmn, layoutDmn, benchmarkDmnLayout, compactifyDmn, expandDmn } from "./dmn/index.js";
|
|
16
19
|
export type { DmnBenchmarkResult, DmnElementPosition, CompactDmn, CompactDmnDecision, CompactDmnInput, CompactDmnOutput, CompactDmnRule, } from "./dmn/index.js";
|
|
17
20
|
export { Form, compactifyForm, expandForm } from "./form/index.js";
|
|
@@ -32,6 +35,7 @@ export type { StoryRenderOptions } from "./bpmn/story.js";
|
|
|
32
35
|
export { analyzeVariableFlow, extractFeelIdentifiers } from "./bpmn/optimize/variable-flow.js";
|
|
33
36
|
export type { OptimizationReport, OptimizationFinding, OptimizationSeverity, OptimizationCategory, ApplyFixResult, OptimizeOptions, } from "./bpmn/optimize/types.js";
|
|
34
37
|
export { layoutProcess, layoutFlowNodes } from "./layout/index.js";
|
|
38
|
+
export type { LayoutEngine } from "./layout/index.js";
|
|
35
39
|
export { benchmarkLayout, compareLayouts, formatBenchmarkResult, generateAutoLayout, parseReferenceLayout, } from "./layout/index.js";
|
|
36
40
|
export type { BenchmarkResult, BoundingBox, ElementComparison, ElementPosition, FlowOrderViolation, } from "./layout/index.js";
|
|
37
41
|
export type { Bounds, LayoutEdge, LayoutNode, LayoutResult, Waypoint } from "./layout/index.js";
|
|
@@ -44,4 +48,6 @@ export type { InputVariableDef, ValidationStructure, ValidationVariableType, } f
|
|
|
44
48
|
export { exportSvg } from "./bpmn/svg.js";
|
|
45
49
|
export type { SvgExportOptions } from "./bpmn/svg.js";
|
|
46
50
|
export type { CompactDiagram, CompactElement, CompactFlow, CompactProcess, } from "./bpmn/compact.js";
|
|
51
|
+
export { compilePlan, extractPlan, mergePlan, slugify, uniqueId, } from "./plan/index.js";
|
|
52
|
+
export type { CompilePlanOptions, ConnectorApplyResult, ConnectorResolver, ExtractResult, PlanAgentTool, PlanAiAgentStep, PlanBranch, PlanBusinessRuleTaskStep, PlanCallActivityStep, PlanConnectorRef, PlanConnectorStep, PlanEndStep, PlanErrorBoundary, PlanGatewayStep, PlanInputVariable, PlanProblem, PlanRawStep, PlanReceiveTaskStep, PlanScenario, PlanScriptTaskStep, PlanSendTaskStep, PlanServiceTaskStep, PlanStartStep, PlanStep, PlanSubProcessStep, PlanTimerBoundary, PlanUserTaskStep, PlanWaitStep, ProcessPlan, SynthResult, UnsupportedElement, } from "./plan/index.js";
|
|
47
53
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export { BpmnSdkError, ParseError, ValidationError } from "./errors.js";
|
|
2
|
-
export { isBpmnActivity, isBpmnAdHocSubProcess, isBpmnBoundaryEvent, isBpmnBusinessRuleTask, isBpmnCallActivity, isBpmnComplexGateway, isBpmnEndEvent, isBpmnEvent, isBpmnEventBasedGateway, isBpmnEventSubProcess, isBpmnExclusiveGateway, isBpmnGateway, isBpmnInclusiveGateway, isBpmnIntermediateCatchEvent, isBpmnIntermediateThrowEvent, isBpmnManualTask, isBpmnParallelGateway, isBpmnReceiveTask, isBpmnScriptTask, isBpmnSendTask, isBpmnServiceTask, isBpmnStartEvent, isBpmnSubProcess, isBpmnTask, isBpmnTransaction, isBpmnUserTask, } from "./bpmn/type-guards.js";
|
|
2
|
+
export { isBpmnActivity, isBpmnAdHocSubProcess, isBpmnBoundaryEvent, isBpmnBusinessRuleTask, isBpmnCallActivity, isBpmnComplexGateway, isBpmnDataObject, isBpmnDataObjectReference, isBpmnDataStoreReference, isBpmnEndEvent, isBpmnEvent, isBpmnEventBasedGateway, isBpmnEventSubProcess, isBpmnExclusiveGateway, isBpmnGateway, isBpmnInclusiveGateway, isBpmnIntermediateCatchEvent, isBpmnIntermediateThrowEvent, isBpmnManualTask, isBpmnParallelGateway, isBpmnReceiveTask, isBpmnScriptTask, isBpmnSendTask, isBpmnServiceTask, isBpmnStartEvent, isBpmnSubProcess, isBpmnTask, isBpmnTransaction, isBpmnUserTask, } from "./bpmn/type-guards.js";
|
|
3
3
|
export { findElement, findElementInProcess, findProcess, findSequenceFlow, getAllElements, getElementType, getZeebeExtensions, } from "./bpmn/utils.js";
|
|
4
4
|
export { Bpmn, SAMPLE_BPMN_XML } from "./bpmn/index.js";
|
|
5
5
|
export { applyAutoLayout } from "./bpmn/auto-layout.js";
|
|
6
6
|
export { checkDiCompleteness } from "./bpmn/di-check.js";
|
|
7
|
+
export { planeForElement, listPlaneElementIds } from "./bpmn/di-planes.js";
|
|
7
8
|
export { DiagramBuilder } from "./bpmn/bpmn-builder.js";
|
|
8
9
|
export { zeebeExtensionsToXmlElements } from "./bpmn/zeebe-extensions.js";
|
|
10
|
+
export { buildAiAgentSubProcess, AI_AGENT_JOB_WORKER_TASK_TYPE, AI_AGENT_DEFAULT_OUTPUT_ELEMENT, } from "./bpmn/agentic.js";
|
|
9
11
|
export { Dmn, layoutDmn, benchmarkDmnLayout, compactifyDmn, expandDmn } from "./dmn/index.js";
|
|
10
12
|
export { Form, compactifyForm, expandForm } from "./form/index.js";
|
|
11
13
|
export { FormBuilder, GroupBuilder } from "./form/form-builder.js";
|
|
@@ -22,4 +24,5 @@ export { compactify, expand } from "./bpmn/compact.js";
|
|
|
22
24
|
export { applyOperations } from "./bpmn/operations.js";
|
|
23
25
|
export { buildValidationDmn, findValidationStructure, getValidationInputNames, insertValidationStructure, removeValidationStructure, validationDecisionId, } from "./bpmn/input-validation.js";
|
|
24
26
|
export { exportSvg } from "./bpmn/svg.js";
|
|
27
|
+
export { compilePlan, extractPlan, mergePlan, slugify, uniqueId, } from "./plan/index.js";
|
|
25
28
|
//# sourceMappingURL=index.js.map
|
|
@@ -8,6 +8,8 @@ const ELEMENT_GAP = 30; // min gap between annotation and a non-annotation shape
|
|
|
8
8
|
const PREFERRED_OFFSET = 50; // preferred gap to associated element
|
|
9
9
|
const MIN_HEIGHT = 30;
|
|
10
10
|
const HORIZONTAL_SHIFTS = [0, 60, -60, 120, -120, 180, -180, 240, -240];
|
|
11
|
+
/** Cost added to a candidate whose association line would cross a shape. */
|
|
12
|
+
const BLOCKED_LINE_COST = 10_000;
|
|
11
13
|
function computeHeight(text, width) {
|
|
12
14
|
if (!text || !text.trim())
|
|
13
15
|
return MIN_HEIGHT;
|
|
@@ -189,7 +191,17 @@ export function packAnnotations(process, layoutNodes) {
|
|
|
189
191
|
}
|
|
190
192
|
}
|
|
191
193
|
}
|
|
192
|
-
|
|
194
|
+
// A clear box is not enough: the association line drawn back to the
|
|
195
|
+
// element must not cut through anything either.
|
|
196
|
+
const candidate = {
|
|
197
|
+
x: candidateX,
|
|
198
|
+
y,
|
|
199
|
+
width: item.bounds.width,
|
|
200
|
+
height: item.bounds.height,
|
|
201
|
+
};
|
|
202
|
+
const { pElem, pAnn } = associationWaypoints(linked.bounds, candidate);
|
|
203
|
+
const crosses = obstacles.some((sh) => sh !== linked.bounds && segmentHitsBox(pElem, pAnn, sh));
|
|
204
|
+
const cost = Math.hypot(candidateX - naturalX, y - naturalY) + (crosses ? BLOCKED_LINE_COST : 0);
|
|
193
205
|
if (cost < best.cost)
|
|
194
206
|
best = { x: candidateX, y, cost };
|
|
195
207
|
}
|
|
@@ -218,6 +230,29 @@ export function packAnnotations(process, layoutNodes) {
|
|
|
218
230
|
}
|
|
219
231
|
return result;
|
|
220
232
|
}
|
|
233
|
+
/** Whether a straight segment passes through a box. */
|
|
234
|
+
function segmentHitsBox(a, b, box) {
|
|
235
|
+
const minX = Math.min(a.x, b.x);
|
|
236
|
+
const maxX = Math.max(a.x, b.x);
|
|
237
|
+
const minY = Math.min(a.y, b.y);
|
|
238
|
+
const maxY = Math.max(a.y, b.y);
|
|
239
|
+
if (maxX <= box.x || box.x + box.width <= minX)
|
|
240
|
+
return false;
|
|
241
|
+
if (maxY <= box.y || box.y + box.height <= minY)
|
|
242
|
+
return false;
|
|
243
|
+
if (a.x === b.x || a.y === b.y)
|
|
244
|
+
return true;
|
|
245
|
+
// Diagonal: the box is hit unless all four corners fall on one side of it.
|
|
246
|
+
const side = (p) => Math.sign((b.x - a.x) * (p.y - a.y) - (b.y - a.y) * (p.x - a.x));
|
|
247
|
+
const corners = [
|
|
248
|
+
{ x: box.x, y: box.y },
|
|
249
|
+
{ x: box.x + box.width, y: box.y },
|
|
250
|
+
{ x: box.x + box.width, y: box.y + box.height },
|
|
251
|
+
{ x: box.x, y: box.y + box.height },
|
|
252
|
+
];
|
|
253
|
+
const first = side(corners[0] ?? { x: 0, y: 0 });
|
|
254
|
+
return corners.some((corner) => side(corner) !== first);
|
|
255
|
+
}
|
|
221
256
|
/**
|
|
222
257
|
* Edge-to-edge, clamped association waypoints between a linked element and
|
|
223
258
|
* its annotation. Port of `chooseWaypoints` (tmp/01-annotation-layouting.cjs:365-389).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Horizontal alignment between the pools of a collaboration.
|
|
3
|
+
*
|
|
4
|
+
* Each process is laid out on its own, so two elements that exchange a message
|
|
5
|
+
* usually end up at unrelated x positions and the message has to travel sideways
|
|
6
|
+
* to reach its partner. Sliding a whole process sideways costs nothing — the
|
|
7
|
+
* pool grows with it — and a message that leaves straight down crosses far less
|
|
8
|
+
* than one that wanders across two pools first.
|
|
9
|
+
*/
|
|
10
|
+
/** One message flow, as the two element centres it connects. */
|
|
11
|
+
export interface MessageLink {
|
|
12
|
+
fromPool: number;
|
|
13
|
+
toPool: number;
|
|
14
|
+
/** x centre of the source element within its pool's content. */
|
|
15
|
+
fromX: number;
|
|
16
|
+
/** x centre of the target element within its pool's content. */
|
|
17
|
+
toX: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Choose a horizontal offset per pool. The widest pool anchors the diagram and
|
|
21
|
+
* the rest slide to meet it, largest first, so the biggest process never moves
|
|
22
|
+
* to chase a small one. Offsets are normalised to keep every pool at or right of
|
|
23
|
+
* the origin.
|
|
24
|
+
*/
|
|
25
|
+
export declare function alignPools(count: number, widths: readonly number[], links: readonly MessageLink[]): number[];
|
|
26
|
+
//# sourceMappingURL=alignment.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Horizontal alignment between the pools of a collaboration.
|
|
3
|
+
*
|
|
4
|
+
* Each process is laid out on its own, so two elements that exchange a message
|
|
5
|
+
* usually end up at unrelated x positions and the message has to travel sideways
|
|
6
|
+
* to reach its partner. Sliding a whole process sideways costs nothing — the
|
|
7
|
+
* pool grows with it — and a message that leaves straight down crosses far less
|
|
8
|
+
* than one that wanders across two pools first.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Choose a horizontal offset per pool. The widest pool anchors the diagram and
|
|
12
|
+
* the rest slide to meet it, largest first, so the biggest process never moves
|
|
13
|
+
* to chase a small one. Offsets are normalised to keep every pool at or right of
|
|
14
|
+
* the origin.
|
|
15
|
+
*/
|
|
16
|
+
export function alignPools(count, widths, links) {
|
|
17
|
+
const offsets = new Array(count).fill(0);
|
|
18
|
+
if (count < 2 || links.length === 0)
|
|
19
|
+
return offsets;
|
|
20
|
+
const order = Array.from({ length: count }, (_, i) => i).sort((a, b) => (widths[b] ?? 0) - (widths[a] ?? 0) || a - b);
|
|
21
|
+
const placed = new Set();
|
|
22
|
+
const anchor = order[0];
|
|
23
|
+
if (anchor === undefined)
|
|
24
|
+
return offsets;
|
|
25
|
+
placed.add(anchor);
|
|
26
|
+
for (let i = 1; i < order.length; i++) {
|
|
27
|
+
const pool = order[i];
|
|
28
|
+
if (pool === undefined)
|
|
29
|
+
continue;
|
|
30
|
+
const connected = links.filter((link) => (link.fromPool === pool && placed.has(link.toPool)) ||
|
|
31
|
+
(link.toPool === pool && placed.has(link.fromPool)));
|
|
32
|
+
if (connected.length === 0) {
|
|
33
|
+
placed.add(pool);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
// Every connected message suggests the shift that would make it vertical;
|
|
37
|
+
// staying put is always in the running.
|
|
38
|
+
const candidates = new Set([0]);
|
|
39
|
+
for (const link of connected) {
|
|
40
|
+
const mine = link.fromPool === pool ? link.fromX : link.toX;
|
|
41
|
+
const theirs = link.fromPool === pool ? link.toX : link.fromX;
|
|
42
|
+
const otherPool = link.fromPool === pool ? link.toPool : link.fromPool;
|
|
43
|
+
candidates.add(theirs + (offsets[otherPool] ?? 0) - mine);
|
|
44
|
+
}
|
|
45
|
+
let best = 0;
|
|
46
|
+
let bestCost = Number.POSITIVE_INFINITY;
|
|
47
|
+
for (const candidate of [...candidates].sort((a, b) => Math.abs(a) - Math.abs(b) || a - b)) {
|
|
48
|
+
let cost = 0;
|
|
49
|
+
for (const link of connected) {
|
|
50
|
+
const mine = link.fromPool === pool ? link.fromX : link.toX;
|
|
51
|
+
const theirs = link.fromPool === pool ? link.toX : link.fromX;
|
|
52
|
+
const otherPool = link.fromPool === pool ? link.toPool : link.fromPool;
|
|
53
|
+
cost += Math.abs(mine + candidate - (theirs + (offsets[otherPool] ?? 0)));
|
|
54
|
+
}
|
|
55
|
+
if (cost < bestCost) {
|
|
56
|
+
bestCost = cost;
|
|
57
|
+
best = candidate;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
offsets[pool] = Math.round(best);
|
|
61
|
+
placed.add(pool);
|
|
62
|
+
}
|
|
63
|
+
const min = Math.min(...offsets);
|
|
64
|
+
return min === 0 ? offsets : offsets.map((offset) => offset - min);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=alignment.js.map
|