@bpmn-ld/bpmn-ld 0.0.0-stage → 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/LICENSE +201 -0
- package/README.md +125 -2
- package/contexts/combined.jsonld +3 -0
- package/contexts/core.jsonld +170 -0
- package/contexts/di.jsonld +37 -0
- package/dist/analyze/analyze.d.ts +3 -0
- package/dist/analyze/analyze.js +11 -0
- package/dist/analyze/findings.d.ts +8 -0
- package/dist/analyze/findings.js +1 -0
- package/dist/analyze/report.d.ts +3 -0
- package/dist/analyze/report.js +9 -0
- package/dist/analyze/rules/ad-hoc-completion-condition.d.ts +2 -0
- package/dist/analyze/rules/ad-hoc-completion-condition.js +7 -0
- package/dist/analyze/rules/cannot-reach-end.d.ts +2 -0
- package/dist/analyze/rules/cannot-reach-end.js +28 -0
- package/dist/analyze/rules/compensation-association.d.ts +2 -0
- package/dist/analyze/rules/compensation-association.js +13 -0
- package/dist/analyze/rules/dead-end.d.ts +2 -0
- package/dist/analyze/rules/dead-end.js +13 -0
- package/dist/analyze/rules/end-event.d.ts +2 -0
- package/dist/analyze/rules/end-event.js +8 -0
- package/dist/analyze/rules/error-catch.d.ts +2 -0
- package/dist/analyze/rules/error-catch.js +14 -0
- package/dist/analyze/rules/escalation-catch.d.ts +2 -0
- package/dist/analyze/rules/escalation-catch.js +14 -0
- package/dist/analyze/rules/gateway-cardinality.d.ts +3 -0
- package/dist/analyze/rules/gateway-cardinality.js +17 -0
- package/dist/analyze/rules/index.d.ts +4 -0
- package/dist/analyze/rules/index.js +21 -0
- package/dist/analyze/rules/instantiating-gateway.d.ts +2 -0
- package/dist/analyze/rules/instantiating-gateway.js +14 -0
- package/dist/analyze/rules/non-interrupting-start.d.ts +2 -0
- package/dist/analyze/rules/non-interrupting-start.js +14 -0
- package/dist/analyze/rules/reachability.d.ts +2 -0
- package/dist/analyze/rules/reachability.js +25 -0
- package/dist/analyze/rules/rule.d.ts +7 -0
- package/dist/analyze/rules/rule.js +1 -0
- package/dist/analyze/rules/signal-catch.d.ts +2 -0
- package/dist/analyze/rules/signal-catch.js +12 -0
- package/dist/analyze/rules/split-join-pairing.d.ts +3 -0
- package/dist/analyze/rules/split-join-pairing.js +50 -0
- package/dist/analyze/rules/start-event.d.ts +2 -0
- package/dist/analyze/rules/start-event.js +8 -0
- package/dist/analyze/rules/task-message-flow.d.ts +2 -0
- package/dist/analyze/rules/task-message-flow.js +19 -0
- package/dist/analyze/scope-graph.d.ts +56 -0
- package/dist/analyze/scope-graph.js +89 -0
- package/dist/analyze-cli.d.ts +1 -0
- package/dist/analyze-cli.js +5 -0
- package/dist/analyze-run.d.ts +12 -0
- package/dist/analyze-run.js +85 -0
- package/dist/bpmn-ld-cli.d.ts +2 -0
- package/dist/bpmn-ld-cli.js +97 -0
- package/dist/build-cli.d.ts +1 -0
- package/dist/build-cli.js +2 -0
- package/dist/build.d.ts +3 -0
- package/dist/build.js +38 -0
- package/dist/cheat-sheet-catalog.d.ts +10 -0
- package/dist/cheat-sheet-catalog.js +72 -0
- package/dist/cheat-sheet-cli.d.ts +1 -0
- package/dist/cheat-sheet-cli.js +7 -0
- package/dist/cheat-sheet-compose.d.ts +13 -0
- package/dist/cheat-sheet-compose.js +107 -0
- package/dist/cheat-sheet-run.d.ts +11 -0
- package/dist/cheat-sheet-run.js +65 -0
- package/dist/cheat-sheet-svg.d.ts +2 -0
- package/dist/cheat-sheet-svg.js +2 -0
- package/dist/cheat-sheet.d.ts +2 -0
- package/dist/cheat-sheet.js +2 -0
- package/dist/check-xml/check.d.ts +3 -0
- package/dist/check-xml/check.js +11 -0
- package/dist/check-xml/lint.d.ts +2 -0
- package/dist/check-xml/lint.js +47 -0
- package/dist/check-xml/moddle.d.ts +6 -0
- package/dist/check-xml/moddle.js +23 -0
- package/dist/check-xml/types.d.ts +20 -0
- package/dist/check-xml/types.js +1 -0
- package/dist/check-xml/xsd.d.ts +2 -0
- package/dist/check-xml/xsd.js +25 -0
- package/dist/check-xml-cli.d.ts +1 -0
- package/dist/check-xml-cli.js +5 -0
- package/dist/check-xml-run.d.ts +10 -0
- package/dist/check-xml-run.js +77 -0
- package/dist/convert/analyze-output.d.ts +3 -0
- package/dist/convert/analyze-output.js +8 -0
- package/dist/convert/convert.d.ts +19 -0
- package/dist/convert/convert.js +96 -0
- package/dist/convert/errors.d.ts +5 -0
- package/dist/convert/errors.js +8 -0
- package/dist/convert/extensions.d.ts +23 -0
- package/dist/convert/extensions.js +103 -0
- package/dist/convert/mapping-check.d.ts +15 -0
- package/dist/convert/mapping-check.js +66 -0
- package/dist/convert/mapping.d.ts +52 -0
- package/dist/convert/mapping.js +273 -0
- package/dist/convert/namespaces.d.ts +2 -0
- package/dist/convert/namespaces.js +19 -0
- package/dist/convert/validate-output.d.ts +2 -0
- package/dist/convert/validate-output.js +28 -0
- package/dist/convert/warnings.d.ts +9 -0
- package/dist/convert/warnings.js +67 -0
- package/dist/convert/xml-reader.d.ts +11 -0
- package/dist/convert/xml-reader.js +84 -0
- package/dist/convert-cli.d.ts +1 -0
- package/dist/convert-cli.js +5 -0
- package/dist/convert-run.d.ts +10 -0
- package/dist/convert-run.js +125 -0
- package/dist/coverage-cli.d.ts +1 -0
- package/dist/coverage-cli.js +8 -0
- package/dist/coverage-mappings.d.ts +3 -0
- package/dist/coverage-mappings.js +34 -0
- package/dist/coverage-scope.d.ts +11 -0
- package/dist/coverage-scope.js +187 -0
- package/dist/coverage-xsd.d.ts +15 -0
- package/dist/coverage-xsd.js +55 -0
- package/dist/coverage.d.ts +35 -0
- package/dist/coverage.js +152 -0
- package/dist/docs-cli.d.ts +1 -0
- package/dist/docs-cli.js +18 -0
- package/dist/docs.d.ts +8 -0
- package/dist/docs.js +136 -0
- package/dist/eval/checks.d.ts +19 -0
- package/dist/eval/checks.js +252 -0
- package/dist/eval/regenerate.d.ts +10 -0
- package/dist/eval/regenerate.js +48 -0
- package/dist/eval-regenerate-cli.d.ts +1 -0
- package/dist/eval-regenerate-cli.js +16 -0
- package/dist/export/data.d.ts +10 -0
- package/dist/export/data.js +127 -0
- package/dist/export/diagram.d.ts +8 -0
- package/dist/export/diagram.js +112 -0
- package/dist/export/element.d.ts +27 -0
- package/dist/export/element.js +175 -0
- package/dist/export/export.d.ts +22 -0
- package/dist/export/export.js +124 -0
- package/dist/export/extensions.d.ts +18 -0
- package/dist/export/extensions.js +122 -0
- package/dist/export/layout-export.d.ts +4 -0
- package/dist/export/layout-export.js +25 -0
- package/dist/export/mapping-check.d.ts +39 -0
- package/dist/export/mapping-check.js +175 -0
- package/dist/export/normalize.d.ts +3 -0
- package/dist/export/normalize.js +67 -0
- package/dist/export-cli.d.ts +1 -0
- package/dist/export-cli.js +5 -0
- package/dist/export-run.d.ts +10 -0
- package/dist/export-run.js +157 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/install-skill-run.d.ts +8 -0
- package/dist/install-skill-run.js +30 -0
- package/dist/install-skill.d.ts +23 -0
- package/dist/install-skill.js +76 -0
- package/dist/layout/collaboration.d.ts +36 -0
- package/dist/layout/collaboration.js +152 -0
- package/dist/layout/di.d.ts +15 -0
- package/dist/layout/di.js +52 -0
- package/dist/layout/extract.d.ts +2 -0
- package/dist/layout/extract.js +99 -0
- package/dist/layout/lanes.d.ts +30 -0
- package/dist/layout/lanes.js +112 -0
- package/dist/layout/layering.d.ts +7 -0
- package/dist/layout/layering.js +53 -0
- package/dist/layout/layout.d.ts +12 -0
- package/dist/layout/layout.js +299 -0
- package/dist/layout/model.d.ts +52 -0
- package/dist/layout/model.js +10 -0
- package/dist/layout/placement.d.ts +25 -0
- package/dist/layout/placement.js +256 -0
- package/dist/layout/standalone.d.ts +8 -0
- package/dist/layout/standalone.js +50 -0
- package/dist/layout-cli.d.ts +1 -0
- package/dist/layout-cli.js +5 -0
- package/dist/layout-run.d.ts +9 -0
- package/dist/layout-run.js +105 -0
- package/dist/loader.d.ts +5 -0
- package/dist/loader.js +7 -0
- package/dist/namespace.d.ts +5 -0
- package/dist/namespace.js +5 -0
- package/dist/ontology.d.ts +19 -0
- package/dist/ontology.js +94 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +2 -0
- package/dist/render/geometry.d.ts +40 -0
- package/dist/render/geometry.js +81 -0
- package/dist/render/renderer.d.ts +31 -0
- package/dist/render/renderer.js +369 -0
- package/dist/render/symbols.d.ts +10 -0
- package/dist/render/symbols.js +26 -0
- package/dist/render/text.d.ts +20 -0
- package/dist/render/text.js +28 -0
- package/dist/render-cli.d.ts +1 -0
- package/dist/render-cli.js +7 -0
- package/dist/render-run.d.ts +9 -0
- package/dist/render-run.js +161 -0
- package/dist/render.d.ts +4 -0
- package/dist/render.js +4 -0
- package/dist/schema-overrides.d.ts +14 -0
- package/dist/schema-overrides.js +36 -0
- package/dist/schema-validate.d.ts +5 -0
- package/dist/schema-validate.js +8 -0
- package/dist/schema.d.ts +6 -0
- package/dist/schema.js +114 -0
- package/dist/shacl.d.ts +15 -0
- package/dist/shacl.js +37 -0
- package/dist/validate-cli.d.ts +1 -0
- package/dist/validate-cli.js +5 -0
- package/dist/validate-run.d.ts +8 -0
- package/dist/validate-run.js +58 -0
- package/dist/validate.d.ts +11 -0
- package/dist/validate.js +94 -0
- package/dist/versioning-cli.d.ts +1 -0
- package/dist/versioning-cli.js +18 -0
- package/dist/versioning.d.ts +17 -0
- package/dist/versioning.js +45 -0
- package/ontology/core.ttl +993 -0
- package/ontology/di.ttl +167 -0
- package/package.json +77 -4
- package/reference/bpmn-xsd/BPMN20.xsd +37 -0
- package/reference/bpmn-xsd/BPMNDI.xsd +100 -0
- package/reference/bpmn-xsd/DC.xsd +29 -0
- package/reference/bpmn-xsd/DI.xsd +100 -0
- package/reference/bpmn-xsd/Semantic.xsd +1562 -0
- package/reference/rospocher/ATTRIBUTION.md +22 -0
- package/reference/rospocher/BPMNOntology.owl +8937 -0
- package/reference/rospocher-verified.json +221 -0
- package/schema/bpmn-ld.schema.json +15230 -0
- package/shapes/core.shapes.ttl +398 -0
- package/skills/bpmn-ld/SKILL.md +154 -0
- package/skills/bpmn-ld/evals/models/order-approval-di.jsonld +296 -0
- package/skills/bpmn-ld/evals/models/refund-di.bpmn +82 -0
- package/skills/bpmn-ld/evals/models/refund.bpmn +18 -0
- package/skills/bpmn-ld/evals/outputs/explain-collaboration.md +22 -0
- package/skills/bpmn-ld/evals/outputs/explain-order-approval.md +24 -0
- package/skills/bpmn-ld/evals/outputs/explain-refund.md +17 -0
- package/skills/bpmn-ld/evals/outputs/generate-activity-compensation.jsonld +39 -0
- package/skills/bpmn-ld/evals/outputs/generate-ad-hoc-sub-process.jsonld +52 -0
- package/skills/bpmn-ld/evals/outputs/generate-boundary-event.jsonld +27 -0
- package/skills/bpmn-ld/evals/outputs/generate-collaboration.jsonld +49 -0
- package/skills/bpmn-ld/evals/outputs/generate-conditions.jsonld +93 -0
- package/skills/bpmn-ld/evals/outputs/generate-diagram-properties.jsonld +340 -0
- package/skills/bpmn-ld/evals/outputs/generate-error.jsonld +217 -0
- package/skills/bpmn-ld/evals/outputs/generate-escalation.jsonld +231 -0
- package/skills/bpmn-ld/evals/outputs/generate-event-definition-ref.jsonld +67 -0
- package/skills/bpmn-ld/evals/outputs/generate-event-sub-process.jsonld +54 -0
- package/skills/bpmn-ld/evals/outputs/generate-exclusive-choice.jsonld +32 -0
- package/skills/bpmn-ld/evals/outputs/generate-instantiating-gateway.jsonld +24 -0
- package/skills/bpmn-ld/evals/outputs/generate-lane-partition.jsonld +46 -0
- package/skills/bpmn-ld/evals/outputs/generate-link-event.expect.json +11 -0
- package/skills/bpmn-ld/evals/outputs/generate-link-event.jsonld +57 -0
- package/skills/bpmn-ld/evals/outputs/generate-loop.jsonld +30 -0
- package/skills/bpmn-ld/evals/outputs/generate-message.jsonld +61 -0
- package/skills/bpmn-ld/evals/outputs/generate-parallel-branches.jsonld +25 -0
- package/skills/bpmn-ld/evals/outputs/generate-process-attributes.jsonld +49 -0
- package/skills/bpmn-ld/evals/outputs/generate-script-task.jsonld +18 -0
- package/skills/bpmn-ld/evals/outputs/generate-signal.jsonld +178 -0
- package/skills/bpmn-ld/evals/outputs/generate-sub-process.jsonld +31 -0
- package/skills/bpmn-ld/evals/outputs/generate-targeted-compensation.jsonld +40 -0
- package/skills/bpmn-ld/evals/outputs/generate-task-implementation.jsonld +18 -0
- package/skills/bpmn-ld/evals/outputs/generate-task-message-references.jsonld +49 -0
- package/skills/bpmn-ld/evals/outputs/generate-transaction.jsonld +48 -0
- package/skills/bpmn-ld/evals/outputs/modify-order-approval.jsonld +376 -0
- package/skills/bpmn-ld/evals/outputs/modify-refund.jsonld +373 -0
- package/skills/bpmn-ld/evals/outputs/order-approval.jsonld +29 -0
- package/skills/bpmn-ld/evals/outputs/refine-order-approval.jsonld +377 -0
- package/skills/bpmn-ld/evals/prompts/explain-collaboration.md +3 -0
- package/skills/bpmn-ld/evals/prompts/explain-order-approval.md +3 -0
- package/skills/bpmn-ld/evals/prompts/explain-refund.md +3 -0
- package/skills/bpmn-ld/evals/prompts/generate-activity-compensation.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-ad-hoc-sub-process.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-boundary-event.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-collaboration.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-conditions.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-diagram-properties.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-error.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-escalation.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-event-definition-ref.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-event-sub-process.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-exclusive-choice.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-instantiating-gateway.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-lane-partition.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-link-event.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-loop.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-message.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-parallel-branches.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-process-attributes.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-script-task.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-signal.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-sub-process.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-targeted-compensation.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-task-implementation.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-task-message-references.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-transaction.md +1 -0
- package/skills/bpmn-ld/evals/prompts/modify-order-approval.md +4 -0
- package/skills/bpmn-ld/evals/prompts/modify-refund.md +4 -0
- package/skills/bpmn-ld/evals/prompts/order-approval.md +1 -0
- package/skills/bpmn-ld/evals/prompts/refine-order-approval.md +8 -0
- package/skills/bpmn-ld/examples/positive/activity-compensation.jsonld +39 -0
- package/skills/bpmn-ld/examples/positive/ad-hoc-sub-process.jsonld +52 -0
- package/skills/bpmn-ld/examples/positive/boundary-event.jsonld +27 -0
- package/skills/bpmn-ld/examples/positive/collaboration.jsonld +49 -0
- package/skills/bpmn-ld/examples/positive/conditions.jsonld +93 -0
- package/skills/bpmn-ld/examples/positive/diagram-properties.jsonld +340 -0
- package/skills/bpmn-ld/examples/positive/error.jsonld +208 -0
- package/skills/bpmn-ld/examples/positive/escalation.jsonld +231 -0
- package/skills/bpmn-ld/examples/positive/event-definition-ref.jsonld +142 -0
- package/skills/bpmn-ld/examples/positive/event-sub-process.jsonld +54 -0
- package/skills/bpmn-ld/examples/positive/exclusive-choice.jsonld +32 -0
- package/skills/bpmn-ld/examples/positive/instantiating-gateway.jsonld +24 -0
- package/skills/bpmn-ld/examples/positive/lane-partition.jsonld +46 -0
- package/skills/bpmn-ld/examples/positive/link-event.expect.json +10 -0
- package/skills/bpmn-ld/examples/positive/link-event.jsonld +138 -0
- package/skills/bpmn-ld/examples/positive/loop.jsonld +30 -0
- package/skills/bpmn-ld/examples/positive/message.jsonld +60 -0
- package/skills/bpmn-ld/examples/positive/order-approval.jsonld +29 -0
- package/skills/bpmn-ld/examples/positive/parallel-branches.jsonld +25 -0
- package/skills/bpmn-ld/examples/positive/process-attributes.jsonld +49 -0
- package/skills/bpmn-ld/examples/positive/script-task.jsonld +18 -0
- package/skills/bpmn-ld/examples/positive/signal.jsonld +150 -0
- package/skills/bpmn-ld/examples/positive/sub-process.jsonld +31 -0
- package/skills/bpmn-ld/examples/positive/targeted-compensation.jsonld +40 -0
- package/skills/bpmn-ld/examples/positive/task-implementation.jsonld +18 -0
- package/skills/bpmn-ld/examples/positive/task-message-references.jsonld +151 -0
- package/skills/bpmn-ld/examples/positive/transaction.jsonld +48 -0
- package/skills/bpmn-ld/references/clarification-and-refinement.md +60 -0
- package/skills/bpmn-ld/references/common-mistakes.md +110 -0
- package/skills/bpmn-ld/references/explain-and-document.md +36 -0
- package/skills/bpmn-ld/references/modify-existing-model.md +49 -0
- package/skills/bpmn-ld/references/patterns.md +292 -0
- package/skills/bpmn-ld/references/render-output.md +1 -0
- package/skills/bpmn-ld/references/rule-catalogue.md +183 -0
- package/skills/bpmn-ld/references/vocabulary-summary.md +65 -0
- package/skills/bpmn-ld/template.jsonld +14 -0
- package/templates/combined.jsonld +3 -0
- package/templates/core.jsonld +170 -0
- package/templates/core.shapes.ttl +398 -0
- package/templates/core.ttl +993 -0
- package/templates/di.jsonld +37 -0
- package/templates/di.ttl +167 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { geometryDiagrams } from "./geometry.js";
|
|
2
|
+
import { escapeXml, wrapText } from "./text.js";
|
|
3
|
+
import { eventCircle, taskRect, gatewayDiamond, doubleRect, thickRect, poolRect, laneRect, dataObject, dataStore, } from "./symbols.js";
|
|
4
|
+
const unwarn = (shape, message, warnings) => {
|
|
5
|
+
warnings.push({ kind: "unsupported", code: "undrawable-element", construct: String(shape.element?.type ?? "unknown"), location: `/${shape.id}`, node: shape.bpmnElement, message });
|
|
6
|
+
};
|
|
7
|
+
const shapeType = (shape) => String(shape.element?.type ?? "");
|
|
8
|
+
const isCatchEvent = (t) => t === "StartEvent" || t === "BoundaryEvent" || t === "IntermediateCatchEvent" || t === "IntermediateEvent";
|
|
9
|
+
const isThrowEvent = (t) => t === "IntermediateThrowEvent" || t === "EndEvent";
|
|
10
|
+
const eventMarker = (element, cx, cy, r, isCatch) => {
|
|
11
|
+
const defs = Array.isArray(element?.eventDefinitions) ? element.eventDefinitions : [];
|
|
12
|
+
const def = defs.length > 0 ? String(defs[0]?.type ?? "") : "";
|
|
13
|
+
const fill = isCatch ? "none" : "black";
|
|
14
|
+
const parts = [];
|
|
15
|
+
const mr = r * 0.45;
|
|
16
|
+
switch (def) {
|
|
17
|
+
case "TimerEventDefinition": {
|
|
18
|
+
parts.push(`<circle cx="${cx}" cy="${cy}" r="${mr}" fill="none" stroke="black" stroke-width="1.2"/>`);
|
|
19
|
+
parts.push(`<line x1="${cx}" y1="${cy}" x2="${cx}" y2="${cy - mr + 3}" stroke="black" stroke-width="1.2"/>`);
|
|
20
|
+
parts.push(`<line x1="${cx}" y1="${cy}" x2="${cx + mr - 3}" y2="${cy}" stroke="black" stroke-width="1.2"/>`);
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
case "MessageEventDefinition": {
|
|
24
|
+
const w = mr * 1.6;
|
|
25
|
+
const h = mr * 1.1;
|
|
26
|
+
parts.push(`<rect x="${cx - w / 2}" y="${cy - h / 2}" width="${w}" height="${h}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
27
|
+
if (isCatch) {
|
|
28
|
+
parts.push(`<polyline points="${cx - w / 2},${cy - h / 2} ${cx},${cy + h / 4} ${cx + w / 2},${cy - h / 2}" fill="none" stroke="black" stroke-width="1.2"/>`);
|
|
29
|
+
}
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
case "ErrorEventDefinition": {
|
|
33
|
+
const size = mr;
|
|
34
|
+
parts.push(`<polygon points="${cx},${cy - size} ${cx + size * 0.6},${cy + size * 0.4} ${cx - size * 0.6},${cy + size * 0.4}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case "EscalationEventDefinition": {
|
|
38
|
+
const size = mr;
|
|
39
|
+
parts.push(`<polygon points="${cx},${cy - size} ${cx + size * 0.7},${cy + size} ${cx - size * 0.7},${cy + size}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case "SignalEventDefinition": {
|
|
43
|
+
const size = mr;
|
|
44
|
+
parts.push(`<polygon points="${cx},${cy - size} ${cx + size},${cy + size * 0.6} ${cx - size},${cy + size * 0.6}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case "ConditionalEventDefinition": {
|
|
48
|
+
parts.push(`<rect x="${cx - mr}" y="${cy - mr}" width="${mr * 2}" height="${mr * 2}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "LinkEventDefinition": {
|
|
52
|
+
parts.push(`<circle cx="${cx}" cy="${cy}" r="${mr}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
53
|
+
parts.push(`<polygon points="${cx + mr * 0.2},${cy - mr * 0.5} ${cx + mr * 0.8},${cy} ${cx + mr * 0.2},${cy + mr * 0.5}" fill="${isCatch ? "black" : "white"}" stroke="black" stroke-width="1"/>`);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case "CompensateEventDefinition": {
|
|
57
|
+
parts.push(`<polygon points="${cx - mr},${cy - mr * 0.6} ${cx},${cy - mr * 0.6} ${cx - mr * 0.5},${cy + mr * 0.6} ${cx + mr * 0.5},${cy + mr * 0.6}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case "CancelEventDefinition": {
|
|
61
|
+
parts.push(`<line x1="${cx - mr}" y1="${cy - mr}" x2="${cx + mr}" y2="${cy + mr}" stroke="black" stroke-width="1.5"/>`);
|
|
62
|
+
parts.push(`<line x1="${cx + mr}" y1="${cy - mr}" x2="${cx - mr}" y2="${cy + mr}" stroke="black" stroke-width="1.5"/>`);
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case "TerminateEventDefinition": {
|
|
66
|
+
parts.push(`<circle cx="${cx}" cy="${cy}" r="${mr}" fill="black" stroke="black" stroke-width="1.2"/>`);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "MultipleEventDefinition": {
|
|
70
|
+
parts.push(`<polygon points="${cx},${cy - mr} ${cx + mr * 0.95},${cy - mr * 0.3} ${cx + mr * 0.6},${cy + mr * 0.8} ${cx - mr * 0.6},${cy + mr * 0.8} ${cx - mr * 0.95},${cy - mr * 0.3}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
case "ParallelMultipleEventDefinition": {
|
|
74
|
+
parts.push(`<line x1="${cx - mr}" y1="${cy}" x2="${cx + mr}" y2="${cy}" stroke="black" stroke-width="1.2"/>`);
|
|
75
|
+
parts.push(`<line x1="${cx}" y1="${cy - mr}" x2="${cx}" y2="${cy + mr}" stroke="black" stroke-width="1.2"/>`);
|
|
76
|
+
parts.push(`<circle cx="${cx}" cy="${cy}" r="${mr * 0.35}" fill="${fill}" stroke="black" stroke-width="1.2"/>`);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
default: {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return parts.join("");
|
|
84
|
+
};
|
|
85
|
+
const drawEvent = (shape, b, warnings, isThrowOverride) => {
|
|
86
|
+
const t = shapeType(shape);
|
|
87
|
+
const el = shape.element;
|
|
88
|
+
const r = Math.min(b.width, b.height) / 2;
|
|
89
|
+
const cx = b.x + r;
|
|
90
|
+
const cy = b.y + r;
|
|
91
|
+
const isInterrupting = el?.cancelActivity !== false;
|
|
92
|
+
const dashed = (t === "BoundaryEvent" || t === "StartEvent" || t === "IntermediateCatchEvent") && el?.cancelActivity === false;
|
|
93
|
+
const isCatch = isCatchEvent(t) && !isThrowOverride;
|
|
94
|
+
const stroke = (t === "EndEvent") ? 3 : 1.5;
|
|
95
|
+
const parts = [eventCircle(cx, cy, r - (t === "IntermediateCatchEvent" || t === "IntermediateThrowEvent" ? 2 : 0), stroke, dashed)];
|
|
96
|
+
if (t === "IntermediateCatchEvent" || t === "IntermediateThrowEvent") {
|
|
97
|
+
parts.push(eventCircle(cx, cy, r - (t === "IntermediateThrowEvent" ? 6 : 6), 1.2, false));
|
|
98
|
+
}
|
|
99
|
+
parts.push(eventMarker(el, cx, cy, r - 6, isCatch));
|
|
100
|
+
return parts.join("");
|
|
101
|
+
};
|
|
102
|
+
const taskIcon = (type, x, y) => {
|
|
103
|
+
const s = 14;
|
|
104
|
+
switch (type) {
|
|
105
|
+
case "UserTask": return `<circle cx="${x + s / 2}" cy="${y + s / 2}" r="${s / 3}" fill="none" stroke="black" stroke-width="1.2"/><line x1="${x + s / 2}" y1="${y + s * 0.83}" x2="${x + s / 2}" y2="${y + s}" stroke="black" stroke-width="1.2"/>`;
|
|
106
|
+
case "ServiceTask": return `<path d="M ${x + s / 2} ${y} l 3 4 4 1 -4 1 -3 4 -3 -4 -4 -1 4 -1 z" fill="none" stroke="black" stroke-width="1.2"/>`;
|
|
107
|
+
case "ScriptTask": return `<rect x="${x}" y="${y + 2}" width="${s}" height="${s - 4}" fill="none" stroke="black" stroke-width="1.2"/><line x1="${x + 2}" y1="${y + 5}" x2="${x + s - 2}" y2="${y + 5}" stroke="black" stroke-width="1"/><line x1="${x + 2}" y1="${y + 8}" x2="${x + s - 2}" y2="${y + 8}" stroke="black" stroke-width="1"/>`;
|
|
108
|
+
case "SendTask": return `<polygon points="${x},${y + s / 2} ${x + s},${y} ${x + s},${y + s}" fill="none" stroke="black" stroke-width="1.2"/>`;
|
|
109
|
+
case "ReceiveTask": return `<polygon points="${x},${y} ${x + s},${y + s / 2} ${x},${y + s}" fill="none" stroke="black" stroke-width="1.2"/>`;
|
|
110
|
+
case "ManualTask": return `<path d="M ${x + 2} ${y + s} V ${y + s / 2} M ${x + 6} ${y + s} V ${y + s / 3} M ${x + 10} ${y + s} V ${y + s / 3}" stroke="black" stroke-width="1.2" fill="none"/>`;
|
|
111
|
+
case "BusinessRuleTask": return `<rect x="${x + 2}" y="${y + 2}" width="${s - 4}" height="${s - 4}" fill="none" stroke="black" stroke-width="1.2"/><line x1="${x + 2}" y1="${y + s / 2}" x2="${x + s - 2}" y2="${y + s / 2}" stroke="black" stroke-width="1"/><line x1="${x + s / 2}" y1="${y + 2}" x2="${x + s / 2}" y2="${y + s - 2}" stroke="black" stroke-width="1"/>`;
|
|
112
|
+
default: return "";
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const activityMarkers = (el, x, y) => {
|
|
116
|
+
const parts = [];
|
|
117
|
+
if (el?.standardLoopCharacteristics) {
|
|
118
|
+
parts.push(`<path d="M ${x + 18} ${y} a 6 6 0 1 1 -6 6" fill="none" stroke="black" stroke-width="1.2"/>`);
|
|
119
|
+
}
|
|
120
|
+
if (el?.multiInstanceLoopCharacteristics) {
|
|
121
|
+
const seq = el.multiInstanceLoopCharacteristics.isSequential === true;
|
|
122
|
+
if (seq) {
|
|
123
|
+
parts.push(`<line x1="${x + 10}" y1="${y - 4}" x2="${x + 10}" y2="${y + 4}" stroke="black" stroke-width="1.2"/>`);
|
|
124
|
+
parts.push(`<line x1="${x + 14}" y1="${y - 4}" x2="${x + 14}" y2="${y + 4}" stroke="black" stroke-width="1.2"/>`);
|
|
125
|
+
parts.push(`<line x1="${x + 18}" y1="${y - 4}" x2="${x + 18}" y2="${y + 4}" stroke="black" stroke-width="1.2"/>`);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
parts.push(`<line x1="${x + 10}" y1="${y}" x2="${x + 18}" y2="${y - 6}" stroke="black" stroke-width="1.2"/>`);
|
|
129
|
+
parts.push(`<line x1="${x + 10}" y1="${y}" x2="${x + 18}" y2="${y}" stroke="black" stroke-width="1.2"/>`);
|
|
130
|
+
parts.push(`<line x1="${x + 10}" y1="${y}" x2="${x + 18}" y2="${y + 6}" stroke="black" stroke-width="1.2"/>`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (el?.isForCompensation === true) {
|
|
134
|
+
parts.push(`<polygon points="${x + 6},${y} ${x + 2},${y + 4} ${x + 6},${y + 8}" fill="black" stroke="black" stroke-width="1"/>`);
|
|
135
|
+
parts.push(`<polygon points="${x + 12},${y} ${x + 8},${y + 4} ${x + 12},${y + 8}" fill="black" stroke="black" stroke-width="1"/>`);
|
|
136
|
+
}
|
|
137
|
+
return parts.join("");
|
|
138
|
+
};
|
|
139
|
+
const drawShape = (shape, warnings) => {
|
|
140
|
+
const t = shapeType(shape);
|
|
141
|
+
const b = shape.bounds;
|
|
142
|
+
const el = shape.element;
|
|
143
|
+
if (t.endsWith("Event")) {
|
|
144
|
+
return drawEvent(shape, b, warnings);
|
|
145
|
+
}
|
|
146
|
+
if (t.endsWith("Gateway")) {
|
|
147
|
+
const cx = b.x + b.width / 2;
|
|
148
|
+
const cy = b.y + b.height / 2;
|
|
149
|
+
const size = Math.min(b.width, b.height) / 2;
|
|
150
|
+
const core = gatewayDiamond(cx, cy, size);
|
|
151
|
+
const r = size * 0.35;
|
|
152
|
+
let marker = "";
|
|
153
|
+
if (t === "ExclusiveGateway")
|
|
154
|
+
marker = `<line x1="${cx - r}" y1="${cy - r}" x2="${cx + r}" y2="${cy + r}" stroke="black" stroke-width="1.5"/><line x1="${cx + r}" y1="${cy - r}" x2="${cx - r}" y2="${cy + r}" stroke="black" stroke-width="1.5"/>`;
|
|
155
|
+
else if (t === "InclusiveGateway")
|
|
156
|
+
marker = `<circle cx="${cx}" cy="${cy}" r="${r}" fill="none" stroke="black" stroke-width="1.5"/>`;
|
|
157
|
+
else if (t === "ParallelGateway")
|
|
158
|
+
marker = `<line x1="${cx - r}" y1="${cy}" x2="${cx + r}" y2="${cy}" stroke="black" stroke-width="1.5"/><line x1="${cx}" y1="${cy - r}" x2="${cx}" y2="${cy + r}" stroke="black" stroke-width="1.5"/>`;
|
|
159
|
+
else if (t === "EventBasedGateway")
|
|
160
|
+
marker = `<polygon points="${cx},${cy - r} ${cx + r},${cy} ${cx},${cy + r} ${cx - r},${cy}" fill="none" stroke="black" stroke-width="1.5"/>`;
|
|
161
|
+
else if (t === "ComplexGateway")
|
|
162
|
+
marker = `<text x="${cx}" y="${cy + 4}" text-anchor="middle" font-family="sans-serif" font-size="12" fill="black">*</text>`;
|
|
163
|
+
return core + marker;
|
|
164
|
+
}
|
|
165
|
+
if (t === "Participant") {
|
|
166
|
+
const process = !!el?.processRef;
|
|
167
|
+
const horizontal = shape.isHorizontal !== false;
|
|
168
|
+
return poolRect(b.x, b.y, b.width, b.height, horizontal, process);
|
|
169
|
+
}
|
|
170
|
+
if (t === "Lane") {
|
|
171
|
+
const horizontal = shape.isHorizontal !== false;
|
|
172
|
+
return laneRect(b.x, b.y, b.width, b.height, horizontal);
|
|
173
|
+
}
|
|
174
|
+
if (t === "DataObjectReference")
|
|
175
|
+
return dataObject(b.x, b.y, b.width, b.height);
|
|
176
|
+
if (t === "DataStoreReference")
|
|
177
|
+
return dataStore(b.x, b.y, b.width, b.height);
|
|
178
|
+
if (t === "Transaction" || t === "SubProcess" || t === "AdHocSubProcess" || t === "CallActivity") {
|
|
179
|
+
let core;
|
|
180
|
+
if (t === "Transaction")
|
|
181
|
+
core = doubleRect(b.x, b.y, b.width, b.height);
|
|
182
|
+
else if (t === "CallActivity")
|
|
183
|
+
core = thickRect(b.x, b.y, b.width, b.height);
|
|
184
|
+
else
|
|
185
|
+
core = taskRect(b.x, b.y, b.width, b.height);
|
|
186
|
+
const parts = [core];
|
|
187
|
+
if (t === "AdHocSubProcess")
|
|
188
|
+
parts.push(`<text x="${b.x + 10}" y="${b.y + 14}" font-family="sans-serif" font-size="14" fill="black">~</text>`);
|
|
189
|
+
if (shape.isExpanded !== true && t !== "CallActivity" && t !== "Transaction") {
|
|
190
|
+
const cx = b.x + b.width / 2;
|
|
191
|
+
const cy = b.y + b.height / 2;
|
|
192
|
+
parts.push(`<line x1="${cx - 8}" y1="${cy}" x2="${cx + 8}" y2="${cy}" stroke="black" stroke-width="1.5"/>`);
|
|
193
|
+
parts.push(`<line x1="${cx}" y1="${cy - 8}" x2="${cx}" y2="${cy + 8}" stroke="black" stroke-width="1.5"/>`);
|
|
194
|
+
}
|
|
195
|
+
const markers = activityMarkers(el, b.x + 12, b.y + b.height - 10);
|
|
196
|
+
if (markers)
|
|
197
|
+
parts.push(markers);
|
|
198
|
+
const icon = taskIcon(t === "SubProcess" ? "" : "", b.x + 8, b.y + 8);
|
|
199
|
+
if (icon)
|
|
200
|
+
parts.push(icon);
|
|
201
|
+
return parts.join("");
|
|
202
|
+
}
|
|
203
|
+
if (t.endsWith("Task")) {
|
|
204
|
+
const parts = [taskRect(b.x, b.y, b.width, b.height)];
|
|
205
|
+
const icon = taskIcon(t, b.x + 8, b.y + 8);
|
|
206
|
+
if (icon)
|
|
207
|
+
parts.push(icon);
|
|
208
|
+
const markers = activityMarkers(el, b.x + b.width / 2, b.y + b.height - 10);
|
|
209
|
+
if (markers)
|
|
210
|
+
parts.push(markers);
|
|
211
|
+
const comp = el?.isForCompensation === true ? activityMarkers({ isForCompensation: true }, b.x + b.width - 20, b.y + 8) : "";
|
|
212
|
+
if (comp)
|
|
213
|
+
parts.push(comp);
|
|
214
|
+
return parts.join("");
|
|
215
|
+
}
|
|
216
|
+
if (t === "TextAnnotation" || t === "Group") {
|
|
217
|
+
return `<rect x="${b.x}" y="${b.y}" width="${b.width}" height="${b.height}" fill="none" stroke="black" stroke-width="1" stroke-dasharray="4 3"/>`;
|
|
218
|
+
}
|
|
219
|
+
unwarn(shape, "element type not specifically supported", warnings);
|
|
220
|
+
return taskRect(b.x, b.y, b.width, b.height);
|
|
221
|
+
};
|
|
222
|
+
const drawLabel = (text, bounds) => {
|
|
223
|
+
const lines = wrapText(text, bounds.width || 80);
|
|
224
|
+
const x = bounds.x + (bounds.width || 0) / 2;
|
|
225
|
+
let y = bounds.y + 12;
|
|
226
|
+
return lines.map((line, index) => `<text x="${x}" y="${y + index * 12}" text-anchor="middle" font-family="sans-serif" font-size="11" fill="black">${escapeXml(line)}</text>`).join("");
|
|
227
|
+
};
|
|
228
|
+
const edgeStyle = (type) => {
|
|
229
|
+
if (type === "MessageFlow")
|
|
230
|
+
return { dashed: true, dotted: false, marker: "openarrow", startMarker: "circle" };
|
|
231
|
+
if (type === "Association" || type === "DataAssociation")
|
|
232
|
+
return { dashed: false, dotted: true, marker: (type === "Association" && false) ? "arrow" : "none" };
|
|
233
|
+
return { dashed: false, dotted: false, marker: "arrow" };
|
|
234
|
+
};
|
|
235
|
+
export const renderDiagramSvg = (diagram) => {
|
|
236
|
+
const warnings = [];
|
|
237
|
+
let minX = Infinity;
|
|
238
|
+
let minY = Infinity;
|
|
239
|
+
let maxX = -Infinity;
|
|
240
|
+
let maxY = -Infinity;
|
|
241
|
+
const touch = (x, y) => {
|
|
242
|
+
minX = Math.min(minX, x);
|
|
243
|
+
minY = Math.min(minY, y);
|
|
244
|
+
maxX = Math.max(maxX, x);
|
|
245
|
+
maxY = Math.max(maxY, y);
|
|
246
|
+
};
|
|
247
|
+
for (const shape of diagram.shapes) {
|
|
248
|
+
touch(shape.bounds.x, shape.bounds.y);
|
|
249
|
+
touch(shape.bounds.x + shape.bounds.width, shape.bounds.y + shape.bounds.height);
|
|
250
|
+
if (shape.label?.bounds) {
|
|
251
|
+
const lb = shape.label.bounds;
|
|
252
|
+
touch(lb.x, lb.y);
|
|
253
|
+
touch(lb.x + lb.width, lb.y + lb.height);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
touch(shape.bounds.x - 20, shape.bounds.y + shape.bounds.height + 2);
|
|
257
|
+
touch(shape.bounds.x + shape.bounds.width + 20, shape.bounds.y + shape.bounds.height + 14);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const edge of diagram.edges)
|
|
261
|
+
for (const point of edge.waypoints)
|
|
262
|
+
touch(point.x, point.y);
|
|
263
|
+
const hasDrawable = diagram.shapes.length > 0 || diagram.edges.length > 0;
|
|
264
|
+
const margin = 20;
|
|
265
|
+
if (!hasDrawable) {
|
|
266
|
+
minX = 0;
|
|
267
|
+
minY = 0;
|
|
268
|
+
maxX = 100;
|
|
269
|
+
maxY = 100;
|
|
270
|
+
}
|
|
271
|
+
const width = Math.ceil((maxX - minX + margin * 2));
|
|
272
|
+
const height = Math.ceil((maxY - minY + margin * 2));
|
|
273
|
+
const offX = -minX + margin;
|
|
274
|
+
const offY = -minY + margin;
|
|
275
|
+
const parts = [];
|
|
276
|
+
const shifted = (x, y) => `${x + offX},${y + offY}`;
|
|
277
|
+
parts.push(`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">`);
|
|
278
|
+
parts.push(`<defs><marker id="arrow" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polygon points="0 0, 10 3.5, 0 7" fill="black"/></marker><marker id="openarrow" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto"><polyline points="0 0, 10 3.5, 0 7" fill="none" stroke="black" stroke-width="1.2"/></marker></defs>`);
|
|
279
|
+
for (const edge of diagram.edges) {
|
|
280
|
+
const el = edge.element;
|
|
281
|
+
const t = String(el?.type ?? "SequenceFlow");
|
|
282
|
+
const style = edgeStyle(t);
|
|
283
|
+
const pts = edge.waypoints.map((p) => shifted(p.x, p.y)).join(" ");
|
|
284
|
+
const dash = style.dashed ? ' stroke-dasharray="6 4"' : style.dotted ? ' stroke-dasharray="2 2"' : "";
|
|
285
|
+
const markerEnd = style.marker === "arrow" ? ' marker-end="url(#arrow)"' : style.marker === "openarrow" ? ' marker-end="url(#openarrow)"' : "";
|
|
286
|
+
let edgeSvg = `<polyline points="${pts}" fill="none" stroke="black" stroke-width="1.5"${dash}${markerEnd}/>`;
|
|
287
|
+
if (style.startMarker === "circle") {
|
|
288
|
+
const sp = edge.waypoints[0];
|
|
289
|
+
edgeSvg = `<circle cx="${sp.x + offX}" cy="${sp.y + offY}" r="4" fill="white" stroke="black" stroke-width="1.2"/>` + edgeSvg;
|
|
290
|
+
}
|
|
291
|
+
if (t === "SequenceFlow" && el?.isDefault === true) {
|
|
292
|
+
const sp = edge.waypoints[0];
|
|
293
|
+
edgeSvg += `<line x1="${sp.x + offX - 6}" y1="${sp.y + offY + 6}" x2="${sp.x + offX + 6}" y2="${sp.y + offY - 6}" stroke="black" stroke-width="1.2"/>`;
|
|
294
|
+
}
|
|
295
|
+
if (t === "SequenceFlow" && el?.conditionExpression) {
|
|
296
|
+
const sp = edge.waypoints[0];
|
|
297
|
+
const d = 4;
|
|
298
|
+
edgeSvg += `<polygon points="${sp.x + offX},${sp.y + offY - d} ${sp.x + offX + d},${sp.y + offY} ${sp.x + offX},${sp.y + offY + d} ${sp.x + offX - d},${sp.y + offY}" fill="white" stroke="black" stroke-width="1.2"/>`;
|
|
299
|
+
}
|
|
300
|
+
parts.push(edgeSvg);
|
|
301
|
+
const name = String(el?.name ?? "");
|
|
302
|
+
if (name !== "" && edge.label?.bounds) {
|
|
303
|
+
const lb = edge.label.bounds;
|
|
304
|
+
const bnds = { x: lb.x + offX, y: lb.y + offY, width: lb.width, height: lb.height };
|
|
305
|
+
parts.push(drawLabel(name, bnds));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
const renderShape = (shape) => {
|
|
309
|
+
const b = shape.bounds;
|
|
310
|
+
const sx = b.x + offX;
|
|
311
|
+
const sy = b.y + offY;
|
|
312
|
+
const shiftedBounds = { x: sx, y: sy, width: b.width, height: b.height };
|
|
313
|
+
const drawn = drawShape({ ...shape, bounds: shiftedBounds }, warnings);
|
|
314
|
+
const res = [drawn];
|
|
315
|
+
const el = shape.element;
|
|
316
|
+
const t = shapeType(shape);
|
|
317
|
+
let labelBounds;
|
|
318
|
+
if (shape.label?.bounds) {
|
|
319
|
+
const lb = shape.label.bounds;
|
|
320
|
+
labelBounds = { x: lb.x + offX, y: lb.y + offY, width: lb.width, height: lb.height };
|
|
321
|
+
}
|
|
322
|
+
else if (t.endsWith("Task") || t === "SubProcess" || t === "Transaction" || t === "AdHocSubProcess" || t === "CallActivity" || t === "Participant" || t === "Lane" || t === "DataObjectReference" || t === "DataStoreReference") {
|
|
323
|
+
labelBounds = { x: shiftedBounds.x + 4, y: shiftedBounds.y + shiftedBounds.height / 2 - 6, width: shiftedBounds.width - 8, height: 12 };
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
labelBounds = { x: shiftedBounds.x - 20, y: shiftedBounds.y + shiftedBounds.height + 2, width: shiftedBounds.width + 40, height: 12 };
|
|
327
|
+
}
|
|
328
|
+
const name = String(el?.name ?? el?.id ?? "");
|
|
329
|
+
if (name !== "") {
|
|
330
|
+
if (t === "Participant" || t === "Lane") {
|
|
331
|
+
const horizontal = shape.isHorizontal !== false;
|
|
332
|
+
const headerBounds = horizontal
|
|
333
|
+
? { x: shiftedBounds.x, y: shiftedBounds.y, width: 30, height: shiftedBounds.height }
|
|
334
|
+
: { x: shiftedBounds.x, y: shiftedBounds.y, width: shiftedBounds.width, height: 30 };
|
|
335
|
+
const nameBounds = shape.label?.bounds ? labelBounds : { x: headerBounds.x, y: headerBounds.y, width: headerBounds.width, height: headerBounds.height };
|
|
336
|
+
res.push(drawLabel(name, nameBounds));
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
res.push(drawLabel(name, labelBounds));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return res.join("");
|
|
343
|
+
};
|
|
344
|
+
for (const shape of diagram.shapes) {
|
|
345
|
+
parts.push(renderShape(shape));
|
|
346
|
+
}
|
|
347
|
+
parts.push(`</svg>`);
|
|
348
|
+
return { svg: parts.join(""), warnings };
|
|
349
|
+
};
|
|
350
|
+
export const renderDocument = (document) => {
|
|
351
|
+
const diagrams = geometryDiagrams(document);
|
|
352
|
+
const rendered = [];
|
|
353
|
+
const warnings = [];
|
|
354
|
+
const errors = [];
|
|
355
|
+
const drawables = diagrams.flatMap((diagram) => (diagram.shapes.length > 0 || diagram.edges.length > 0 ? [diagram] : []));
|
|
356
|
+
for (const diagram of diagrams) {
|
|
357
|
+
const out = renderDiagramSvg(diagram);
|
|
358
|
+
rendered.push({ id: diagram.id, name: diagram.name, svg: out.svg, warnings: out.warnings, errors: [] });
|
|
359
|
+
warnings.push(...out.warnings.map((w) => ({ ...w, location: `/${diagram.id}${w.location}` })));
|
|
360
|
+
if (diagram.shapes.length === 0 && diagram.edges.length === 0) {
|
|
361
|
+
const warning = { kind: "unsupported", code: "undrawable-element", construct: "diagram", location: `/${diagram.id}`, node: diagram.bpmnElement, message: "diagram has nothing drawable" };
|
|
362
|
+
warnings.push(warning);
|
|
363
|
+
rendered[rendered.length - 1].warnings.push(warning);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (drawables.length === 0)
|
|
367
|
+
errors.push({ code: "nothing-drawable", message: "model has nothing drawable" });
|
|
368
|
+
return { diagrams: rendered, warnings, errors };
|
|
369
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const eventCircle: (cx: number, cy: number, r: number, strokeWidth?: number, dashed?: boolean) => string;
|
|
2
|
+
export declare const taskRect: (x: number, y: number, width: number, height: number, strokeWidth?: number) => string;
|
|
3
|
+
export declare const gatewayDiamond: (cx: number, cy: number, size: number) => string;
|
|
4
|
+
export declare const doubleRect: (x: number, y: number, width: number, height: number) => string;
|
|
5
|
+
export declare const thickRect: (x: number, y: number, width: number, height: number) => string;
|
|
6
|
+
export declare const poolRect: (x: number, y: number, width: number, height: number, horizontal: boolean, process: boolean) => string;
|
|
7
|
+
export declare const laneRect: (x: number, y: number, width: number, height: number, horizontal: boolean) => string;
|
|
8
|
+
export declare const dataObject: (x: number, y: number, width: number, height: number) => string;
|
|
9
|
+
export declare const dataStore: (x: number, y: number, width: number, height: number) => string;
|
|
10
|
+
export declare const modelName: (element: any) => string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { escapeXml } from "./text.js";
|
|
2
|
+
export const eventCircle = (cx, cy, r, strokeWidth = 1.5, dashed = false) => `<circle cx="${cx}" cy="${cy}" r="${r}" fill="white" stroke="black" stroke-width="${strokeWidth}"${dashed ? ' stroke-dasharray="4 3"' : ""}/>`;
|
|
3
|
+
export const taskRect = (x, y, width, height, strokeWidth = 1.5) => `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="10" ry="10" fill="white" stroke="black" stroke-width="${strokeWidth}"/>`;
|
|
4
|
+
export const gatewayDiamond = (cx, cy, size) => `<polygon points="${cx},${cy - size} ${cx + size},${cy} ${cx},${cy + size} ${cx - size},${cy}" fill="white" stroke="black" stroke-width="1.5"/>`;
|
|
5
|
+
export const doubleRect = (x, y, width, height) => `${taskRect(x, y, width, height)}<rect x="${x + 4}" y="${y + 4}" width="${width - 8}" height="${height - 8}" rx="8" ry="8" fill="none" stroke="black" stroke-width="1.5"/>`;
|
|
6
|
+
export const thickRect = (x, y, width, height) => taskRect(x, y, width, height, 3);
|
|
7
|
+
export const poolRect = (x, y, width, height, horizontal, process) => {
|
|
8
|
+
if (!process)
|
|
9
|
+
return `<rect x="${x}" y="${y}" width="${width}" height="${height}" fill="black" stroke="black" stroke-width="1.5"/>`;
|
|
10
|
+
if (horizontal) {
|
|
11
|
+
return `<rect x="${x}" y="${y}" width="${width}" height="${height}" fill="white" stroke="black" stroke-width="1.5"/><rect x="${x}" y="${y}" width="30" height="${height}" fill="white" stroke="black" stroke-width="1.5"/>`;
|
|
12
|
+
}
|
|
13
|
+
return `<rect x="${x}" y="${y}" width="${width}" height="${height}" fill="white" stroke="black" stroke-width="1.5"/><rect x="${x}" y="${y}" width="${width}" height="30" fill="white" stroke="black" stroke-width="1.5"/>`;
|
|
14
|
+
};
|
|
15
|
+
export const laneRect = (x, y, width, height, horizontal) => {
|
|
16
|
+
if (horizontal) {
|
|
17
|
+
return `<rect x="${x}" y="${y}" width="${width}" height="${height}" fill="white" stroke="black" stroke-width="1.5"/><rect x="${x}" y="${y}" width="30" height="${height}" fill="white" stroke="black" stroke-width="1.5"/>`;
|
|
18
|
+
}
|
|
19
|
+
return `<rect x="${x}" y="${y}" width="${width}" height="${height}" fill="white" stroke="black" stroke-width="1.5"/><rect x="${x}" y="${y}" width="${width}" height="30" fill="white" stroke="black" stroke-width="1.5"/>`;
|
|
20
|
+
};
|
|
21
|
+
export const dataObject = (x, y, width, height) => `<path d="M ${x + 6} ${y} L ${x + width - 6} ${y} L ${x + width} ${y + 6} L ${x + width} ${y + height} L ${x} ${y + height} L ${x} ${y} Z M ${x + width - 6} ${y} L ${x + width - 6} ${y + 6} L ${x + width} ${y + 6}" fill="white" stroke="black" stroke-width="1.5"/>`;
|
|
22
|
+
export const dataStore = (x, y, width, height) => `<ellipse cx="${x + width / 2}" cy="${y + 6}" rx="${width / 2}" ry="6" fill="white" stroke="black" stroke-width="1.5"/><rect x="${x}" y="${y + 6}" width="${width}" height="${height - 12}" fill="white" stroke="none"/><line x1="${x}" y1="${y + 6}" x2="${x}" y2="${y + height - 6}" stroke="black" stroke-width="1.5"/><line x1="${x + width}" y1="${y + 6}" x2="${x + width}" y2="${y + height - 6}" stroke="black" stroke-width="1.5"/><ellipse cx="${x + width / 2}" cy="${y + height - 6}" rx="${width / 2}" ry="6" fill="white" stroke="black" stroke-width="1.5"/>`;
|
|
23
|
+
export const modelName = (element) => {
|
|
24
|
+
const raw = element?.name ?? element?.id ?? "";
|
|
25
|
+
return escapeXml(raw);
|
|
26
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const escapeXml: (value: unknown) => string;
|
|
2
|
+
export declare const wrapText: (value: unknown, maxWidth: number) => string[];
|
|
3
|
+
export declare const labelPosition: (label: {
|
|
4
|
+
bounds?: {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
} | undefined, fallback: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
}) => {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const ESCAPE = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" };
|
|
2
|
+
export const escapeXml = (value) => String(value).replace(/[&<>"']/g, (char) => ESCAPE[char]);
|
|
3
|
+
export const wrapText = (value, maxWidth) => {
|
|
4
|
+
const text = String(value ?? "");
|
|
5
|
+
if (text === "")
|
|
6
|
+
return [];
|
|
7
|
+
const words = text.split(/\s+/);
|
|
8
|
+
const lines = [];
|
|
9
|
+
let current = "";
|
|
10
|
+
for (const word of words) {
|
|
11
|
+
const candidate = current === "" ? word : `${current} ${word}`;
|
|
12
|
+
if (current !== "" && candidate.length * 7 > maxWidth) {
|
|
13
|
+
lines.push(current);
|
|
14
|
+
current = word;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
current = candidate;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (current !== "")
|
|
21
|
+
lines.push(current);
|
|
22
|
+
return lines;
|
|
23
|
+
};
|
|
24
|
+
export const labelPosition = (label, fallback) => {
|
|
25
|
+
if (label?.bounds)
|
|
26
|
+
return label.bounds;
|
|
27
|
+
return { x: fallback.x, y: fallback.y + fallback.height + 2, width: fallback.width, height: 12 };
|
|
28
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const EXIT_OK = 0;
|
|
2
|
+
export declare const EXIT_BAD_INPUT = 2;
|
|
3
|
+
export interface CliResult {
|
|
4
|
+
exitCode: number;
|
|
5
|
+
stdout: string;
|
|
6
|
+
stderr: string;
|
|
7
|
+
}
|
|
8
|
+
export declare const USAGE = "Usage: render <input> [options]\n\nRenders a BPMN-LD document or BPMN XML file to SVG.\n\nOptions:\n -o, --output <file> Write SVG to <file> (one per diagram)\n --force Recompute all diagram layout\n --json Print { warnings, errors }\n -h, --help Show this help\n\nExit codes:\n 0 Rendered successfully\n 2 Usage error or unusable input\n";
|
|
9
|
+
export declare const runRender: (argv: string[]) => CliResult;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { layoutDocument, LayoutInputError } from "./layout/layout.js";
|
|
5
|
+
import { convert } from "./convert/convert.js";
|
|
6
|
+
import { ConversionError } from "./convert/errors.js";
|
|
7
|
+
import { renderDocument } from "./render/renderer.js";
|
|
8
|
+
export const EXIT_OK = 0;
|
|
9
|
+
export const EXIT_BAD_INPUT = 2;
|
|
10
|
+
export const USAGE = `Usage: render <input> [options]
|
|
11
|
+
|
|
12
|
+
Renders a BPMN-LD document or BPMN XML file to SVG.
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
-o, --output <file> Write SVG to <file> (one per diagram)
|
|
16
|
+
--force Recompute all diagram layout
|
|
17
|
+
--json Print { warnings, errors }
|
|
18
|
+
-h, --help Show this help
|
|
19
|
+
|
|
20
|
+
Exit codes:
|
|
21
|
+
0 Rendered successfully
|
|
22
|
+
2 Usage error or unusable input
|
|
23
|
+
`;
|
|
24
|
+
class UsageError extends Error {
|
|
25
|
+
}
|
|
26
|
+
const parseArgs = (argv) => {
|
|
27
|
+
let input;
|
|
28
|
+
const options = { input: "", force: false, json: false };
|
|
29
|
+
for (let i = 0; i < argv.length; i++) {
|
|
30
|
+
const arg = argv[i];
|
|
31
|
+
if (arg === "-h" || arg === "--help")
|
|
32
|
+
return "help";
|
|
33
|
+
else if (arg === "-o" || arg === "--output") {
|
|
34
|
+
const value = argv[++i];
|
|
35
|
+
if (value === undefined || value.startsWith("-"))
|
|
36
|
+
throw new UsageError(`${arg} requires a value`);
|
|
37
|
+
options.output = value;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--force")
|
|
40
|
+
options.force = true;
|
|
41
|
+
else if (arg === "--json")
|
|
42
|
+
options.json = true;
|
|
43
|
+
else if (arg.startsWith("-"))
|
|
44
|
+
throw new UsageError(`unknown option ${arg}`);
|
|
45
|
+
else if (input !== undefined)
|
|
46
|
+
throw new UsageError(`unexpected extra argument ${arg}`);
|
|
47
|
+
else
|
|
48
|
+
input = arg;
|
|
49
|
+
}
|
|
50
|
+
if (input === undefined)
|
|
51
|
+
throw new UsageError("missing input file");
|
|
52
|
+
options.input = input;
|
|
53
|
+
return options;
|
|
54
|
+
};
|
|
55
|
+
const failure = (stderr) => ({ exitCode: EXIT_BAD_INPUT, stdout: "", stderr });
|
|
56
|
+
const readText = (path) => {
|
|
57
|
+
try {
|
|
58
|
+
return readFileSync(path, "utf8");
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const reason = error.code === "ENOENT" ? "no such file" : (error.message ?? "cannot read");
|
|
62
|
+
throw new UsageError(`cannot read ${path}: ${reason}`);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const fileName = (path) => path.split(/[\\/]/).pop() ?? path;
|
|
66
|
+
const suffixPath = (output, index) => {
|
|
67
|
+
if (index === 0)
|
|
68
|
+
return output;
|
|
69
|
+
const dot = output.lastIndexOf(".");
|
|
70
|
+
if (dot > output.lastIndexOf("/") && dot > output.lastIndexOf("\\") && dot > 0) {
|
|
71
|
+
return `${output.slice(0, dot)}-${index + 1}${output.slice(dot)}`;
|
|
72
|
+
}
|
|
73
|
+
return `${output}-${index + 1}`;
|
|
74
|
+
};
|
|
75
|
+
export const runRender = (argv) => {
|
|
76
|
+
let options;
|
|
77
|
+
try {
|
|
78
|
+
options = parseArgs(argv);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error instanceof UsageError)
|
|
82
|
+
return failure(`error: ${error.message}\n\n${USAGE}`);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
if (options === "help")
|
|
86
|
+
return { exitCode: EXIT_OK, stdout: USAGE, stderr: "" };
|
|
87
|
+
let document;
|
|
88
|
+
try {
|
|
89
|
+
const text = readText(options.input);
|
|
90
|
+
let parsed;
|
|
91
|
+
let isJson = false;
|
|
92
|
+
try {
|
|
93
|
+
parsed = JSON.parse(text);
|
|
94
|
+
isJson = true;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
isJson = false;
|
|
98
|
+
}
|
|
99
|
+
if (isJson) {
|
|
100
|
+
document = parsed;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const result = convert(text, { fileName: options.input });
|
|
104
|
+
document = result.document;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof UsageError || error instanceof ConversionError) {
|
|
109
|
+
const message = error instanceof UsageError ? error.message : `${options.input}: ${error.message}`;
|
|
110
|
+
if (options.json)
|
|
111
|
+
return { exitCode: EXIT_BAD_INPUT, stdout: `${JSON.stringify({ warnings: [], errors: [{ code: "bad-input", message }] }, null, 2)}\n`, stderr: "" };
|
|
112
|
+
return failure(`error: ${message}\n`);
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
let rendered;
|
|
117
|
+
try {
|
|
118
|
+
rendered = renderDocument(layoutDocument(document, { force: options.force }).document);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (!(error instanceof LayoutInputError))
|
|
122
|
+
throw error;
|
|
123
|
+
const errors = error.errors.map((message) => ({ code: "invalid-input", message }));
|
|
124
|
+
if (options.json)
|
|
125
|
+
return { exitCode: EXIT_BAD_INPUT, stdout: `${JSON.stringify({ warnings: [], errors }, null, 2)}\n`, stderr: "" };
|
|
126
|
+
return failure(error.errors.map((message) => `error: ${options.input}: invalid BPMN-LD: ${message}`).join("\n") + "\n");
|
|
127
|
+
}
|
|
128
|
+
const warnings = rendered.warnings;
|
|
129
|
+
const errors = rendered.errors;
|
|
130
|
+
const aggregateErrors = errors;
|
|
131
|
+
if (options.output !== undefined) {
|
|
132
|
+
if (aggregateErrors.length > 0) {
|
|
133
|
+
if (options.json)
|
|
134
|
+
return { exitCode: EXIT_BAD_INPUT, stdout: `${JSON.stringify({ warnings, errors: aggregateErrors }, null, 2)}\n`, stderr: "" };
|
|
135
|
+
return failure([...warnings.map((w) => `warning: ${w.message ?? w.code}`), ...aggregateErrors.map((e) => `error: ${e.message}`)].join("\n") + "\n");
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
rendered.diagrams.forEach((diagram, index) => {
|
|
139
|
+
writeFileSync(suffixPath(options.output, index), diagram.svg);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
const message = `cannot write ${options.output}: ${error.message}`;
|
|
144
|
+
if (options.json)
|
|
145
|
+
return { exitCode: EXIT_BAD_INPUT, stdout: `${JSON.stringify({ warnings, errors: [...aggregateErrors, { code: "cannot-write", message }] }, null, 2)}\n`, stderr: "" };
|
|
146
|
+
return failure(`error: ${message}\n`);
|
|
147
|
+
}
|
|
148
|
+
if (options.json)
|
|
149
|
+
return { exitCode: EXIT_OK, stdout: `${JSON.stringify({ warnings, errors: [] }, null, 2)}\n`, stderr: "" };
|
|
150
|
+
return { exitCode: EXIT_OK, stdout: "", stderr: warnings.length ? `${warnings.map((w) => `warning: ${w.message ?? w.code}`).join("\n")}\n` : "" };
|
|
151
|
+
}
|
|
152
|
+
if (aggregateErrors.length > 0) {
|
|
153
|
+
if (options.json)
|
|
154
|
+
return { exitCode: EXIT_BAD_INPUT, stdout: `${JSON.stringify({ warnings, errors: aggregateErrors }, null, 2)}\n`, stderr: "" };
|
|
155
|
+
return failure([...warnings.map((w) => `warning: ${w.message ?? w.code}`), ...aggregateErrors.map((e) => `error: ${e.message}`)].join("\n") + "\n");
|
|
156
|
+
}
|
|
157
|
+
const stdout = rendered.diagrams.length === 1 ? `${rendered.diagrams[0].svg}\n` : `${rendered.diagrams.map((d) => d.svg).join("\n")}\n`;
|
|
158
|
+
if (options.json)
|
|
159
|
+
return { exitCode: EXIT_OK, stdout: `${JSON.stringify({ warnings, errors: [] }, null, 2)}\n`, stderr: "" };
|
|
160
|
+
return { exitCode: EXIT_OK, stdout, stderr: warnings.length ? `${warnings.map((w) => `warning: ${w.message ?? w.code}`).join("\n")}\n` : "" };
|
|
161
|
+
};
|