@bpmnkit/core 0.1.1 → 0.2.0
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 +32 -1
- 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 +265 -3
- package/dist/bpmn/bpmn-builder.js +603 -197
- package/dist/bpmn/bpmn-model.d.ts +114 -0
- package/dist/bpmn/bpmn-parser.js +1414 -521
- package/dist/bpmn/bpmn-serializer.js +107 -19
- package/dist/bpmn/compact.d.ts +17 -2
- package/dist/bpmn/compact.js +3 -3
- package/dist/bpmn/full-operations.d.ts +89 -0
- package/dist/bpmn/full-operations.js +478 -0
- package/dist/bpmn/index.d.ts +19 -0
- package/dist/bpmn/index.js +21 -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 +7 -4
- package/dist/bpmn/optimize/flow.js +22 -2
- package/dist/bpmn/optimize/index.js +20 -9
- package/dist/bpmn/optimize/patterns.js +23 -16
- package/dist/bpmn/optimize/tasks.js +30 -7
- package/dist/bpmn/optimize/types.d.ts +10 -1
- package/dist/bpmn/optimize/utils.js +2 -4
- package/dist/bpmn/optimize/variable-flow.js +58 -67
- package/dist/bpmn/semantic-hash.d.ts +93 -0
- package/dist/bpmn/semantic-hash.js +155 -0
- package/dist/bpmn/sha256.d.ts +17 -0
- package/dist/bpmn/sha256.js +95 -0
- package/dist/bpmn/zeebe-extensions.d.ts +83 -0
- package/dist/bpmn/zeebe-extensions.js +117 -0
- package/dist/bpmn/zeebe-placement.d.ts +12 -0
- package/dist/bpmn/zeebe-placement.js +140 -0
- package/dist/errors.d.ts +40 -1
- package/dist/errors.js +41 -0
- package/dist/index.d.ts +16 -5
- package/dist/index.js +9 -3
- 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 +37 -0
- package/dist/layout/semantic/graph.js +242 -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 +514 -0
- package/dist/layout/types.d.ts +17 -0
- package/dist/node/index.d.ts +10 -0
- package/dist/node/index.js +9 -0
- package/dist/node/write.d.ts +81 -0
- package/dist/node/write.js +167 -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/dist/types/id-generator.js +11 -3
- package/dist/xml/index.d.ts +3 -1
- package/dist/xml/index.js +2 -1
- package/dist/xml/xml-parser.d.ts +32 -0
- package/dist/xml/xml-parser.js +394 -143
- package/package.json +9 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ZEEBE_PLACEMENT } from "./zeebe-placement.js";
|
|
1
2
|
/** Convert Zeebe extensions to XmlElement array for the BPMN model. */
|
|
2
3
|
export function zeebeExtensionsToXmlElements(extensions) {
|
|
3
4
|
const elements = [];
|
|
@@ -91,9 +92,125 @@ export function zeebeExtensionsToXmlElements(extensions) {
|
|
|
91
92
|
children: [],
|
|
92
93
|
});
|
|
93
94
|
}
|
|
95
|
+
if (extensions.assignmentDefinition) {
|
|
96
|
+
const attrs = {};
|
|
97
|
+
const { assignee, candidateGroups, candidateUsers } = extensions.assignmentDefinition;
|
|
98
|
+
if (assignee)
|
|
99
|
+
attrs.assignee = assignee;
|
|
100
|
+
if (candidateGroups)
|
|
101
|
+
attrs.candidateGroups = candidateGroups;
|
|
102
|
+
if (candidateUsers)
|
|
103
|
+
attrs.candidateUsers = candidateUsers;
|
|
104
|
+
if (Object.keys(attrs).length > 0) {
|
|
105
|
+
elements.push({ name: "zeebe:assignmentDefinition", attributes: attrs, children: [] });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (extensions.taskSchedule) {
|
|
109
|
+
const attrs = {};
|
|
110
|
+
if (extensions.taskSchedule.dueDate)
|
|
111
|
+
attrs.dueDate = extensions.taskSchedule.dueDate;
|
|
112
|
+
if (extensions.taskSchedule.followUpDate) {
|
|
113
|
+
attrs.followUpDate = extensions.taskSchedule.followUpDate;
|
|
114
|
+
}
|
|
115
|
+
if (Object.keys(attrs).length > 0) {
|
|
116
|
+
elements.push({ name: "zeebe:taskSchedule", attributes: attrs, children: [] });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (extensions.priorityDefinition) {
|
|
120
|
+
elements.push({
|
|
121
|
+
name: "zeebe:priorityDefinition",
|
|
122
|
+
attributes: { priority: extensions.priorityDefinition.priority },
|
|
123
|
+
children: [],
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (extensions.subscription) {
|
|
127
|
+
elements.push({
|
|
128
|
+
name: "zeebe:subscription",
|
|
129
|
+
attributes: { correlationKey: extensions.subscription.correlationKey },
|
|
130
|
+
children: [],
|
|
131
|
+
});
|
|
132
|
+
}
|
|
94
133
|
if (extensions.unknownElements) {
|
|
95
134
|
elements.push(...extensions.unknownElements);
|
|
96
135
|
}
|
|
97
136
|
return elements;
|
|
98
137
|
}
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// Placement
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
/**
|
|
142
|
+
* Reports where a Zeebe extension may go, from the descriptor rather than from
|
|
143
|
+
* our own idea of the rules.
|
|
144
|
+
*
|
|
145
|
+
* `zeebe.json` records a `meta.allowedIn` list per extension type;
|
|
146
|
+
* `scripts/generate-zeebe-placement.ts` resolves those entries — many of which
|
|
147
|
+
* name abstract BPMN types or Zeebe aliases — into the concrete element names in
|
|
148
|
+
* `ZEEBE_PLACEMENT`. Writing `zeebe:calledDecision` onto a service task produces
|
|
149
|
+
* a file Camunda rejects at deploy time; catching it at the write is the point.
|
|
150
|
+
*
|
|
151
|
+
* **An extension the table does not mention is allowed.** The descriptor
|
|
152
|
+
* declares no owner for `zeebe:subscription` or `zeebe:properties`, so we do not
|
|
153
|
+
* know where they may go and must not invent a rule — this check rejects only
|
|
154
|
+
* what the descriptor positively forbids. Vendor extensions outside the `zeebe:`
|
|
155
|
+
* namespace are not this function's business and are likewise allowed.
|
|
156
|
+
*
|
|
157
|
+
* @param ownerElement - The owner's BPMN element name, e.g. `bpmn:serviceTask`.
|
|
158
|
+
* @param extension - The extension element name, e.g. `zeebe:taskDefinition`.
|
|
159
|
+
*/
|
|
160
|
+
export function isZeebePlacementAllowed(ownerElement, extension) {
|
|
161
|
+
const owners = ZEEBE_PLACEMENT[extension];
|
|
162
|
+
return owners === undefined || owners.includes(ownerElement);
|
|
163
|
+
}
|
|
164
|
+
/** Thrown when a Zeebe extension is written somewhere the descriptor forbids. */
|
|
165
|
+
export class ZeebePlacementError extends Error {
|
|
166
|
+
ownerElement;
|
|
167
|
+
extension;
|
|
168
|
+
allowedOn;
|
|
169
|
+
constructor(ownerElement, extension, allowedOn) {
|
|
170
|
+
super(`<${extension}> is not allowed on <${ownerElement}>. The Zeebe schema allows it on: ${allowedOn.join(", ")}.`);
|
|
171
|
+
this.ownerElement = ownerElement;
|
|
172
|
+
this.extension = extension;
|
|
173
|
+
this.allowedOn = allowedOn;
|
|
174
|
+
this.name = "ZeebePlacementError";
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Throws {@link ZeebePlacementError} if the placement is one the descriptor
|
|
179
|
+
* forbids. See {@link isZeebePlacementAllowed} for what "forbids" covers.
|
|
180
|
+
*/
|
|
181
|
+
export function assertZeebePlacement(ownerElement, extension) {
|
|
182
|
+
if (isZeebePlacementAllowed(ownerElement, extension))
|
|
183
|
+
return;
|
|
184
|
+
throw new ZeebePlacementError(ownerElement, extension, ZEEBE_PLACEMENT[extension] ?? []);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* The BPMN element name a flow element is written as.
|
|
188
|
+
*
|
|
189
|
+
* The model's `type` is the element's local name in every case but one:
|
|
190
|
+
* `eventSubProcess` is our name for a `bpmn:subProcess` carrying
|
|
191
|
+
* `triggeredByEvent`, and BPMN has no element of that name.
|
|
192
|
+
*/
|
|
193
|
+
export function bpmnElementName(flowElement) {
|
|
194
|
+
const local = flowElement.type === "eventSubProcess" ? "subProcess" : flowElement.type;
|
|
195
|
+
return `bpmn:${local}`;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Finds a Zeebe extension element on a flow element, creating it if absent, and
|
|
199
|
+
* refuses a placement the descriptor forbids.
|
|
200
|
+
*
|
|
201
|
+
* Use this rather than pushing onto `extensionElements` directly: the push
|
|
202
|
+
* cannot fail, so an extension on the wrong element becomes a deploy-time error
|
|
203
|
+
* in someone else's terminal instead of a throw here.
|
|
204
|
+
*
|
|
205
|
+
* @throws ZeebePlacementError
|
|
206
|
+
*/
|
|
207
|
+
export function ensureZeebeExtension(owner, extension) {
|
|
208
|
+
assertZeebePlacement(bpmnElementName(owner), extension);
|
|
209
|
+
const existing = owner.extensionElements.find((candidate) => candidate.name === extension);
|
|
210
|
+
if (existing)
|
|
211
|
+
return existing;
|
|
212
|
+
const created = { name: extension, attributes: {}, children: [] };
|
|
213
|
+
owner.extensionElements.push(created);
|
|
214
|
+
return created;
|
|
215
|
+
}
|
|
99
216
|
//# sourceMappingURL=zeebe-extensions.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where each Zeebe extension element may be placed, keyed by element name.
|
|
3
|
+
*
|
|
4
|
+
* The value is the set of element names that may own it. Owners are BPMN
|
|
5
|
+
* elements for extensions that sit in an `extensionElements` bag, and Zeebe
|
|
6
|
+
* elements for the few that nest inside another extension.
|
|
7
|
+
*
|
|
8
|
+
* An extension absent from this table is one the descriptor says nothing about;
|
|
9
|
+
* see `isZeebePlacementAllowed` for what that means.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ZEEBE_PLACEMENT: Readonly<Record<string, readonly string[]>>;
|
|
12
|
+
//# sourceMappingURL=zeebe-placement.d.ts.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Generated by scripts/generate-zeebe-placement.ts — do not edit.
|
|
2
|
+
// Source: descriptors/zeebe.json (zeebe-bpmn-moddle, MIT) resolved against
|
|
3
|
+
// descriptors/bpmn.json (bpmn-moddle, MIT). Regenerate with:
|
|
4
|
+
// pnpm --filter @bpmnkit/core generate:placement
|
|
5
|
+
/**
|
|
6
|
+
* Where each Zeebe extension element may be placed, keyed by element name.
|
|
7
|
+
*
|
|
8
|
+
* The value is the set of element names that may own it. Owners are BPMN
|
|
9
|
+
* elements for extensions that sit in an `extensionElements` bag, and Zeebe
|
|
10
|
+
* elements for the few that nest inside another extension.
|
|
11
|
+
*
|
|
12
|
+
* An extension absent from this table is one the descriptor says nothing about;
|
|
13
|
+
* see `isZeebePlacementAllowed` for what that means.
|
|
14
|
+
*/
|
|
15
|
+
export const ZEEBE_PLACEMENT = {
|
|
16
|
+
"zeebe:adHoc": ["bpmn:adHocSubProcess"],
|
|
17
|
+
"zeebe:agentDefinition": ["bpmn:adHocSubProcess", "bpmn:serviceTask"],
|
|
18
|
+
"zeebe:assignmentDefinition": ["bpmn:userTask"],
|
|
19
|
+
"zeebe:calledDecision": ["bpmn:businessRuleTask"],
|
|
20
|
+
"zeebe:calledElement": ["bpmn:callActivity"],
|
|
21
|
+
"zeebe:conditionalFilter": ["bpmn:conditionalEventDefinition"],
|
|
22
|
+
"zeebe:executionListener": ["zeebe:executionListeners"],
|
|
23
|
+
"zeebe:executionListeners": [
|
|
24
|
+
"bpmn:adHocSubProcess",
|
|
25
|
+
"bpmn:boundaryEvent",
|
|
26
|
+
"bpmn:businessRuleTask",
|
|
27
|
+
"bpmn:callActivity",
|
|
28
|
+
"bpmn:endEvent",
|
|
29
|
+
"bpmn:eventBasedGateway",
|
|
30
|
+
"bpmn:exclusiveGateway",
|
|
31
|
+
"bpmn:implicitThrowEvent",
|
|
32
|
+
"bpmn:inclusiveGateway",
|
|
33
|
+
"bpmn:intermediateCatchEvent",
|
|
34
|
+
"bpmn:intermediateThrowEvent",
|
|
35
|
+
"bpmn:manualTask",
|
|
36
|
+
"bpmn:parallelGateway",
|
|
37
|
+
"bpmn:process",
|
|
38
|
+
"bpmn:receiveTask",
|
|
39
|
+
"bpmn:scriptTask",
|
|
40
|
+
"bpmn:sendTask",
|
|
41
|
+
"bpmn:serviceTask",
|
|
42
|
+
"bpmn:startEvent",
|
|
43
|
+
"bpmn:subProcess",
|
|
44
|
+
"bpmn:task",
|
|
45
|
+
"bpmn:transaction",
|
|
46
|
+
"bpmn:userTask",
|
|
47
|
+
],
|
|
48
|
+
"zeebe:formDefinition": ["bpmn:startEvent", "bpmn:userTask"],
|
|
49
|
+
"zeebe:input": [
|
|
50
|
+
"bpmn:adHocSubProcess",
|
|
51
|
+
"bpmn:businessRuleTask",
|
|
52
|
+
"bpmn:callActivity",
|
|
53
|
+
"bpmn:endEvent",
|
|
54
|
+
"bpmn:intermediateThrowEvent",
|
|
55
|
+
"bpmn:scriptTask",
|
|
56
|
+
"bpmn:sendTask",
|
|
57
|
+
"bpmn:serviceTask",
|
|
58
|
+
"bpmn:subProcess",
|
|
59
|
+
"bpmn:transaction",
|
|
60
|
+
"bpmn:userTask",
|
|
61
|
+
],
|
|
62
|
+
"zeebe:ioMapping": [
|
|
63
|
+
"bpmn:adHocSubProcess",
|
|
64
|
+
"bpmn:boundaryEvent",
|
|
65
|
+
"bpmn:businessRuleTask",
|
|
66
|
+
"bpmn:callActivity",
|
|
67
|
+
"bpmn:endEvent",
|
|
68
|
+
"bpmn:implicitThrowEvent",
|
|
69
|
+
"bpmn:intermediateCatchEvent",
|
|
70
|
+
"bpmn:intermediateThrowEvent",
|
|
71
|
+
"bpmn:receiveTask",
|
|
72
|
+
"bpmn:scriptTask",
|
|
73
|
+
"bpmn:sendTask",
|
|
74
|
+
"bpmn:serviceTask",
|
|
75
|
+
"bpmn:startEvent",
|
|
76
|
+
"bpmn:subProcess",
|
|
77
|
+
"bpmn:transaction",
|
|
78
|
+
"bpmn:userTask",
|
|
79
|
+
],
|
|
80
|
+
"zeebe:jobPriorityDefinition": [
|
|
81
|
+
"bpmn:adHocSubProcess",
|
|
82
|
+
"bpmn:businessRuleTask",
|
|
83
|
+
"bpmn:endEvent",
|
|
84
|
+
"bpmn:intermediateThrowEvent",
|
|
85
|
+
"bpmn:process",
|
|
86
|
+
"bpmn:scriptTask",
|
|
87
|
+
"bpmn:sendTask",
|
|
88
|
+
"bpmn:serviceTask",
|
|
89
|
+
],
|
|
90
|
+
"zeebe:linkedResource": ["bpmn:serviceTask"],
|
|
91
|
+
"zeebe:linkedResources": ["bpmn:serviceTask"],
|
|
92
|
+
"zeebe:loopCharacteristics": ["bpmn:multiInstanceLoopCharacteristics"],
|
|
93
|
+
"zeebe:output": [
|
|
94
|
+
"bpmn:adHocSubProcess",
|
|
95
|
+
"bpmn:boundaryEvent",
|
|
96
|
+
"bpmn:businessRuleTask",
|
|
97
|
+
"bpmn:callActivity",
|
|
98
|
+
"bpmn:endEvent",
|
|
99
|
+
"bpmn:implicitThrowEvent",
|
|
100
|
+
"bpmn:intermediateCatchEvent",
|
|
101
|
+
"bpmn:intermediateThrowEvent",
|
|
102
|
+
"bpmn:receiveTask",
|
|
103
|
+
"bpmn:scriptTask",
|
|
104
|
+
"bpmn:sendTask",
|
|
105
|
+
"bpmn:serviceTask",
|
|
106
|
+
"bpmn:startEvent",
|
|
107
|
+
"bpmn:subProcess",
|
|
108
|
+
"bpmn:transaction",
|
|
109
|
+
"bpmn:userTask",
|
|
110
|
+
],
|
|
111
|
+
"zeebe:priorityDefinition": ["bpmn:userTask"],
|
|
112
|
+
"zeebe:script": ["bpmn:scriptTask"],
|
|
113
|
+
"zeebe:taskDefinition": [
|
|
114
|
+
"bpmn:adHocSubProcess",
|
|
115
|
+
"bpmn:businessRuleTask",
|
|
116
|
+
"bpmn:endEvent",
|
|
117
|
+
"bpmn:intermediateThrowEvent",
|
|
118
|
+
"bpmn:scriptTask",
|
|
119
|
+
"bpmn:sendTask",
|
|
120
|
+
"bpmn:serviceTask",
|
|
121
|
+
],
|
|
122
|
+
"zeebe:taskHeaders": [
|
|
123
|
+
"bpmn:adHocSubProcess",
|
|
124
|
+
"bpmn:businessRuleTask",
|
|
125
|
+
"bpmn:endEvent",
|
|
126
|
+
"bpmn:intermediateThrowEvent",
|
|
127
|
+
"bpmn:scriptTask",
|
|
128
|
+
"bpmn:sendTask",
|
|
129
|
+
"bpmn:serviceTask",
|
|
130
|
+
"bpmn:userTask",
|
|
131
|
+
"zeebe:executionListener",
|
|
132
|
+
],
|
|
133
|
+
"zeebe:taskListener": ["zeebe:taskListeners"],
|
|
134
|
+
"zeebe:taskListeners": ["bpmn:userTask"],
|
|
135
|
+
"zeebe:taskSchedule": ["bpmn:userTask"],
|
|
136
|
+
"zeebe:userTask": ["bpmn:userTask"],
|
|
137
|
+
"zeebe:userTaskForm": ["bpmn:process"],
|
|
138
|
+
"zeebe:versionTag": ["bpmn:process"],
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=zeebe-placement.js.map
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SemanticDiff } from "./bpmn/semantic-hash.js";
|
|
1
2
|
/**
|
|
2
3
|
* Typed error codes for all errors thrown by `@bpmnkit/core`.
|
|
3
4
|
*
|
|
@@ -18,7 +19,11 @@ export type ErrorCode =
|
|
|
18
19
|
/** XML could not be parsed or a required attribute was missing. */
|
|
19
20
|
"parse-error"
|
|
20
21
|
/** A builder received an invalid combination of options. */
|
|
21
|
-
| "validation-error"
|
|
22
|
+
| "validation-error"
|
|
23
|
+
/** Serialising a model and reading it back did not reproduce the model. */
|
|
24
|
+
| "write-verification-error"
|
|
25
|
+
/** The output file could not be written. */
|
|
26
|
+
| "write-error";
|
|
22
27
|
/**
|
|
23
28
|
* Base class for all errors thrown by `@bpmnkit/core`.
|
|
24
29
|
*
|
|
@@ -74,4 +79,38 @@ export declare class ParseError extends BpmnSdkError {
|
|
|
74
79
|
export declare class ValidationError extends BpmnSdkError {
|
|
75
80
|
constructor(message: string);
|
|
76
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Thrown when serialising a model and parsing the result back does not
|
|
84
|
+
* reproduce the model.
|
|
85
|
+
*
|
|
86
|
+
* This means the write would have put something on disk that no longer says
|
|
87
|
+
* what the model said, so nothing is written. {@link changes} names the
|
|
88
|
+
* elements that diverged.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* import { writeBpmn } from "@bpmnkit/core/node"
|
|
93
|
+
* import { WriteVerificationError } from "@bpmnkit/core"
|
|
94
|
+
*
|
|
95
|
+
* try {
|
|
96
|
+
* await writeBpmn(definitions, { output: "flow.bpmn" })
|
|
97
|
+
* } catch (err) {
|
|
98
|
+
* if (err instanceof WriteVerificationError) {
|
|
99
|
+
* console.error("would have lost:", err.changes.removed)
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare class WriteVerificationError extends BpmnSdkError {
|
|
105
|
+
/** What differed between the model and the model read back from the output. */
|
|
106
|
+
readonly changes: SemanticDiff;
|
|
107
|
+
constructor(message: string, changes: SemanticDiff);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Thrown when the output file cannot be written — it already exists and
|
|
111
|
+
* `force` was not given, or the filesystem refused the write.
|
|
112
|
+
*/
|
|
113
|
+
export declare class WriteError extends BpmnSdkError {
|
|
114
|
+
constructor(message: string);
|
|
115
|
+
}
|
|
77
116
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.js
CHANGED
|
@@ -63,4 +63,45 @@ export class ValidationError extends BpmnSdkError {
|
|
|
63
63
|
this.name = "ValidationError";
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Thrown when serialising a model and parsing the result back does not
|
|
68
|
+
* reproduce the model.
|
|
69
|
+
*
|
|
70
|
+
* This means the write would have put something on disk that no longer says
|
|
71
|
+
* what the model said, so nothing is written. {@link changes} names the
|
|
72
|
+
* elements that diverged.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* import { writeBpmn } from "@bpmnkit/core/node"
|
|
77
|
+
* import { WriteVerificationError } from "@bpmnkit/core"
|
|
78
|
+
*
|
|
79
|
+
* try {
|
|
80
|
+
* await writeBpmn(definitions, { output: "flow.bpmn" })
|
|
81
|
+
* } catch (err) {
|
|
82
|
+
* if (err instanceof WriteVerificationError) {
|
|
83
|
+
* console.error("would have lost:", err.changes.removed)
|
|
84
|
+
* }
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export class WriteVerificationError extends BpmnSdkError {
|
|
89
|
+
/** What differed between the model and the model read back from the output. */
|
|
90
|
+
changes;
|
|
91
|
+
constructor(message, changes) {
|
|
92
|
+
super(message, "write-verification-error");
|
|
93
|
+
this.name = "WriteVerificationError";
|
|
94
|
+
this.changes = changes;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Thrown when the output file cannot be written — it already exists and
|
|
99
|
+
* `force` was not given, or the filesystem refused the write.
|
|
100
|
+
*/
|
|
101
|
+
export class WriteError extends BpmnSdkError {
|
|
102
|
+
constructor(message) {
|
|
103
|
+
super(message, "write-error");
|
|
104
|
+
this.name = "WriteError";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
66
107
|
//# sourceMappingURL=errors.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
export { BpmnSdkError, ParseError, ValidationError } from "./errors.js";
|
|
1
|
+
export { BpmnSdkError, ParseError, ValidationError, WriteError, WriteVerificationError, } from "./errors.js";
|
|
2
2
|
export type { ErrorCode } from "./errors.js";
|
|
3
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
|
+
export { diffSemantics, projectSemantics, semanticHash } from "./bpmn/semantic-hash.js";
|
|
8
|
+
export type { JsonValue, SemanticDiff, SemanticProjection } from "./bpmn/semantic-hash.js";
|
|
9
|
+
export { sha256Hex } from "./bpmn/sha256.js";
|
|
7
10
|
export { checkDiCompleteness } from "./bpmn/di-check.js";
|
|
8
11
|
export type { DiCompleteness } from "./bpmn/di-check.js";
|
|
9
12
|
export { planeForElement, listPlaneElementIds } from "./bpmn/di-planes.js";
|
|
10
|
-
export { DiagramBuilder } from "./bpmn/bpmn-builder.js";
|
|
11
|
-
export type {
|
|
13
|
+
export { DiagramBuilder, ProcessBuilder } from "./bpmn/bpmn-builder.js";
|
|
14
|
+
export type { BranchBuilder, BuildOptions, MessageFlowOptions, ParticipantOptions, DiagramMessageOptions, SubProcessContentBuilder, ServiceTaskOptions, ScriptTaskOptions, UserTaskOptions, CallActivityOptions, BusinessRuleTaskOptions, ElementOptions, GatewayOptions, MultiInstanceOptions, SubProcessOptions, StartEventOptions, IntermediateCatchEventOptions, IntermediateThrowEventOptions, EndEventOptions, BoundaryEventOptions, AdHocSubProcessOptions, } from "./bpmn/bpmn-builder.js";
|
|
12
15
|
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";
|
|
13
16
|
export type { RestConnectorConfig, RestAuthentication, HttpMethod, } from "./bpmn/rest-connector.js";
|
|
14
|
-
export type { ZeebeExtensions, ZeebeTaskDefinition, ZeebeIoMapping, ZeebeIoMappingEntry, ZeebeTaskHeaders, ZeebeTaskHeaderEntry, ZeebeProperties, ZeebePropertyEntry, ZeebeFormDefinition, ZeebeCalledDecision, } from "./bpmn/zeebe-extensions.js";
|
|
15
|
-
export { zeebeExtensionsToXmlElements } from "./bpmn/zeebe-extensions.js";
|
|
17
|
+
export type { ZeebeExtensions, ZeebeTaskDefinition, ZeebeIoMapping, ZeebeIoMappingEntry, ZeebeTaskHeaders, ZeebeTaskHeaderEntry, ZeebeProperties, ZeebePropertyEntry, ZeebeFormDefinition, ZeebeCalledDecision, ZeebeAssignmentDefinition, ZeebeTaskSchedule, ZeebePriorityDefinition, ZeebeSubscription, } from "./bpmn/zeebe-extensions.js";
|
|
18
|
+
export { assertZeebePlacement, bpmnElementName, ensureZeebeExtension, isZeebePlacementAllowed, ZeebePlacementError, zeebeExtensionsToXmlElements, } from "./bpmn/zeebe-extensions.js";
|
|
19
|
+
export { ZEEBE_PLACEMENT } from "./bpmn/zeebe-placement.js";
|
|
20
|
+
export { buildAiAgentSubProcess, AI_AGENT_JOB_WORKER_TASK_TYPE, AI_AGENT_DEFAULT_OUTPUT_ELEMENT, } from "./bpmn/agentic.js";
|
|
21
|
+
export type { AiAgentOptions, AiAgentModelConfig, AiAgentToolSpec, AiAgentToolParam, AiAgentToolParamType, AiAgentBuild, } from "./bpmn/agentic.js";
|
|
16
22
|
export { Dmn, layoutDmn, benchmarkDmnLayout, compactifyDmn, expandDmn } from "./dmn/index.js";
|
|
17
23
|
export type { DmnBenchmarkResult, DmnElementPosition, CompactDmn, CompactDmnDecision, CompactDmnInput, CompactDmnOutput, CompactDmnRule, } from "./dmn/index.js";
|
|
18
24
|
export { Form, compactifyForm, expandForm } from "./form/index.js";
|
|
@@ -33,6 +39,7 @@ export type { StoryRenderOptions } from "./bpmn/story.js";
|
|
|
33
39
|
export { analyzeVariableFlow, extractFeelIdentifiers } from "./bpmn/optimize/variable-flow.js";
|
|
34
40
|
export type { OptimizationReport, OptimizationFinding, OptimizationSeverity, OptimizationCategory, ApplyFixResult, OptimizeOptions, } from "./bpmn/optimize/types.js";
|
|
35
41
|
export { layoutProcess, layoutFlowNodes } from "./layout/index.js";
|
|
42
|
+
export type { LayoutEngine } from "./layout/index.js";
|
|
36
43
|
export { benchmarkLayout, compareLayouts, formatBenchmarkResult, generateAutoLayout, parseReferenceLayout, } from "./layout/index.js";
|
|
37
44
|
export type { BenchmarkResult, BoundingBox, ElementComparison, ElementPosition, FlowOrderViolation, } from "./layout/index.js";
|
|
38
45
|
export type { Bounds, LayoutEdge, LayoutNode, LayoutResult, Waypoint } from "./layout/index.js";
|
|
@@ -40,9 +47,13 @@ export { ELEMENT_SIZES, GRID_CELL_HEIGHT, GRID_CELL_WIDTH } from "./layout/index
|
|
|
40
47
|
export { compactify, expand } from "./bpmn/compact.js";
|
|
41
48
|
export { applyOperations } from "./bpmn/operations.js";
|
|
42
49
|
export type { BpmnOperation } from "./bpmn/operations.js";
|
|
50
|
+
export { applyBpmnOperations, reconcileCompact, OperationError } from "./bpmn/full-operations.js";
|
|
51
|
+
export type { ApplyBpmnOperationsOptions, ApplyBpmnOperationsResult, OperationProblem, } from "./bpmn/full-operations.js";
|
|
43
52
|
export { buildValidationDmn, findValidationStructure, getValidationInputNames, insertValidationStructure, removeValidationStructure, validationDecisionId, } from "./bpmn/input-validation.js";
|
|
44
53
|
export type { InputVariableDef, ValidationStructure, ValidationVariableType, } from "./bpmn/input-validation.js";
|
|
45
54
|
export { exportSvg } from "./bpmn/svg.js";
|
|
46
55
|
export type { SvgExportOptions } from "./bpmn/svg.js";
|
|
47
56
|
export type { CompactDiagram, CompactElement, CompactFlow, CompactProcess, } from "./bpmn/compact.js";
|
|
57
|
+
export { compilePlan, extractPlan, mergePlan, slugify, uniqueId, } from "./plan/index.js";
|
|
58
|
+
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";
|
|
48
59
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
export { BpmnSdkError, ParseError, ValidationError } from "./errors.js";
|
|
1
|
+
export { BpmnSdkError, ParseError, ValidationError, WriteError, WriteVerificationError, } from "./errors.js";
|
|
2
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
|
+
export { diffSemantics, projectSemantics, semanticHash } from "./bpmn/semantic-hash.js";
|
|
7
|
+
export { sha256Hex } from "./bpmn/sha256.js";
|
|
6
8
|
export { checkDiCompleteness } from "./bpmn/di-check.js";
|
|
7
9
|
export { planeForElement, listPlaneElementIds } from "./bpmn/di-planes.js";
|
|
8
|
-
export { DiagramBuilder } from "./bpmn/bpmn-builder.js";
|
|
9
|
-
export { zeebeExtensionsToXmlElements } from "./bpmn/zeebe-extensions.js";
|
|
10
|
+
export { DiagramBuilder, ProcessBuilder } from "./bpmn/bpmn-builder.js";
|
|
11
|
+
export { assertZeebePlacement, bpmnElementName, ensureZeebeExtension, isZeebePlacementAllowed, ZeebePlacementError, zeebeExtensionsToXmlElements, } from "./bpmn/zeebe-extensions.js";
|
|
12
|
+
export { ZEEBE_PLACEMENT } from "./bpmn/zeebe-placement.js";
|
|
13
|
+
export { buildAiAgentSubProcess, AI_AGENT_JOB_WORKER_TASK_TYPE, AI_AGENT_DEFAULT_OUTPUT_ELEMENT, } from "./bpmn/agentic.js";
|
|
10
14
|
export { Dmn, layoutDmn, benchmarkDmnLayout, compactifyDmn, expandDmn } from "./dmn/index.js";
|
|
11
15
|
export { Form, compactifyForm, expandForm } from "./form/index.js";
|
|
12
16
|
export { FormBuilder, GroupBuilder } from "./form/form-builder.js";
|
|
@@ -21,6 +25,8 @@ export { benchmarkLayout, compareLayouts, formatBenchmarkResult, generateAutoLay
|
|
|
21
25
|
export { ELEMENT_SIZES, GRID_CELL_HEIGHT, GRID_CELL_WIDTH } from "./layout/index.js";
|
|
22
26
|
export { compactify, expand } from "./bpmn/compact.js";
|
|
23
27
|
export { applyOperations } from "./bpmn/operations.js";
|
|
28
|
+
export { applyBpmnOperations, reconcileCompact, OperationError } from "./bpmn/full-operations.js";
|
|
24
29
|
export { buildValidationDmn, findValidationStructure, getValidationInputNames, insertValidationStructure, removeValidationStructure, validationDecisionId, } from "./bpmn/input-validation.js";
|
|
25
30
|
export { exportSvg } from "./bpmn/svg.js";
|
|
31
|
+
export { compilePlan, extractPlan, mergePlan, slugify, uniqueId, } from "./plan/index.js";
|
|
26
32
|
//# 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
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vertical order of the pools in a collaboration.
|
|
3
|
+
*
|
|
4
|
+
* Message flows read best when the pools they connect sit next to each other,
|
|
5
|
+
* so the order is chosen to minimise how far messages travel vertically rather
|
|
6
|
+
* than left as declared. Small collaborations can afford an exhaustive search;
|
|
7
|
+
* larger ones use repeated remove-and-reinsert, which reaches the same answer on
|
|
8
|
+
* every realistic diagram and never depends on iteration order.
|
|
9
|
+
*/
|
|
10
|
+
/** A message-flow relationship between two pools, by index into the input order. */
|
|
11
|
+
export interface PoolLink {
|
|
12
|
+
from: number;
|
|
13
|
+
to: number;
|
|
14
|
+
weight: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Order pools by their message-flow relationships. Returns indices into the
|
|
18
|
+
* input order; an input with no message flows comes back unchanged.
|
|
19
|
+
*/
|
|
20
|
+
export declare function orderPools(count: number, links: readonly PoolLink[]): number[];
|
|
21
|
+
//# sourceMappingURL=ordering.d.ts.map
|