@dskripchenko/flow 0.0.0-stage → 1.0.1
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/CHANGELOG.md +82 -0
- package/LICENSE +21 -0
- package/README.md +87 -2
- package/dist/core-DfJMfClV.js +4121 -0
- package/dist/core-dyjNI9N1.cjs +4522 -0
- package/dist/index.cjs +69 -0
- package/dist/index.d.ts +767 -0
- package/dist/index.mjs +1 -0
- package/dist/mermaid.cjs +222 -0
- package/dist/mermaid.d.ts +107 -0
- package/dist/mermaid.mjs +220 -0
- package/dist/standalone.css +2 -0
- package/dist/standalone.js +14 -0
- package/dist/style.css +1119 -0
- package/dist/vue.cjs +5836 -0
- package/dist/vue.d.ts +1041 -0
- package/dist/vue.mjs +5823 -0
- package/package.json +122 -4
- package/schema/graph.schema.json +95 -0
- package/schema/snapshot.schema.json +35 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { nt as BUILTIN_TYPES, v as CommandError, t as DEFAULT_LIMITS, N as DESCRIPTION_FONT, P as EDGE_FONT, ft as GRAPH_VERSION, ct as GraphError, _ as History, F as LABEL_FONT, $ as LayoutError, Z as MAX_EDGES, Q as MAX_NODES, T as STATUS_TONES, u as alignNodes, y as applyCommand, Y as approximateMeasurer, C as backoffDelay, H as bezierPoint, I as boundsOf, n as centreOn, r as clampZoom, d as copyFragment, w as createPoller, rt as createRegistry, it as defaultRegistry, b as describeCommand, U as distanceToPolyline, f as distributeNodes, lt as emptyGraph, E as emptyMonitorState, W as facingSide, p as findFreeSpot, i as fitView, L as frameAround, pt as isEnabled, m as isFragment, at as isLocked, D as isStale, a as isVisible, et as issuesByElement, h as layoutCommand, R as layoutGraph, z as measureEdgeLabel, B as measureNode, O as mergeSnapshot, G as midpoint, o as nearestInDirection, x as nextId, ut as parseGraph, g as pasteFragment, k as pathEdges, ot as portsOf, A as reduceMonitor, K as route, V as routeEdges, q as sideNormal, J as sidePoint, S as snap, j as statusSummary, dt as stringifyGraph, M as toEpoch, s as toGraph, c as toScreen, st as typeOf, tt as validateGraph, X as wrapText, l as zoomAt } from "./core-DfJMfClV.js";
|
package/dist/mermaid.cjs
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_core = require("./core-dyjNI9N1.cjs");
|
|
3
|
+
let _dskripchenko_diagrams = require("@dskripchenko/diagrams");
|
|
4
|
+
// src/mermaid/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* @dskripchenko/flow/mermaid — a model from Mermaid `stateDiagram-v2` or
|
|
7
|
+
* `flowchart` source, through the parser of @dskripchenko/diagrams (an
|
|
8
|
+
* optional peer dependency: only this entry imports it).
|
|
9
|
+
*
|
|
10
|
+
* import { fromMermaid } from '@dskripchenko/flow/mermaid'
|
|
11
|
+
* const graph = fromMermaid('stateDiagram-v2\n [*] --> Idle')
|
|
12
|
+
*
|
|
13
|
+
* Composite states become sub-process nodes with their own inline graph;
|
|
14
|
+
* flowchart subgraphs become groups.
|
|
15
|
+
*/
|
|
16
|
+
var flowDirection = (d) => d === "LR" || d === "RL" ? "LR" : "TB";
|
|
17
|
+
/** Whether a source is one this importer reads. */
|
|
18
|
+
function isMermaidFlow(source) {
|
|
19
|
+
const head = source.replace(/^\s*(%%[^\n]*\n\s*)*/, "").split(/\s/, 1)[0] ?? "";
|
|
20
|
+
return /^(stateDiagram(-v2)?|flowchart|graph)$/.test(head);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Converts Mermaid source to a model.
|
|
24
|
+
*
|
|
25
|
+
* @throws GraphError for other diagram types; the diagrams parser's own
|
|
26
|
+
* `DiagramError` (with line and column) for syntax errors.
|
|
27
|
+
*/
|
|
28
|
+
function fromMermaid(source, options = {}) {
|
|
29
|
+
const ast = (0, _dskripchenko_diagrams.parse)(source);
|
|
30
|
+
if (ast && ast.type === "state" && ast.states instanceof Map) return fromState(ast, options);
|
|
31
|
+
if (ast && ast.type === "flowchart" && ast.nodes instanceof Map) return fromFlowchart(ast, options);
|
|
32
|
+
throw new require_core.GraphError(`Only stateDiagram-v2 and flowchart can be imported, not "${ast?.type ?? "unknown"}"`);
|
|
33
|
+
}
|
|
34
|
+
/** Readable ids for pseudo states: `\0start:root` → `start`, `\0end:Billing` → `Billing-end`. */
|
|
35
|
+
function pseudoId(id) {
|
|
36
|
+
const m = /^\u0000(start|end):(.*)$/.exec(id);
|
|
37
|
+
if (!m) return id;
|
|
38
|
+
return m[2] === "root" ? m[1] : `${m[2]}-${m[1]}`;
|
|
39
|
+
}
|
|
40
|
+
function fromState(ast, options) {
|
|
41
|
+
const composites = ast.composites;
|
|
42
|
+
/** The graph a state belongs to: its nearest composite that is not a region (or the root). */
|
|
43
|
+
const scopeOf = (parent) => {
|
|
44
|
+
let p = parent;
|
|
45
|
+
while (p !== void 0) {
|
|
46
|
+
const c = composites.get(p);
|
|
47
|
+
if (!c) return "";
|
|
48
|
+
if (!c.region) return p;
|
|
49
|
+
p = c.parent;
|
|
50
|
+
}
|
|
51
|
+
return "";
|
|
52
|
+
};
|
|
53
|
+
/** The region (as a group) a state sits in directly, if any. */
|
|
54
|
+
const regionOf = (parent) => parent !== void 0 && composites.get(parent)?.region ? parent : void 0;
|
|
55
|
+
const parentOf = (id) => ast.states.get(id)?.parent ?? composites.get(id)?.parent;
|
|
56
|
+
/** The element that stands for `id` inside graph `scope`: itself, or its ancestor composite in that scope. */
|
|
57
|
+
const representative = (id, scope) => {
|
|
58
|
+
let cur = id;
|
|
59
|
+
for (let guard = 0; cur !== void 0 && guard < 1e3; guard++) {
|
|
60
|
+
if (!composites.get(cur)?.region && scopeOf(parentOf(cur)) === scope) return cur;
|
|
61
|
+
cur = parentOf(cur);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const graphs = /* @__PURE__ */ new Map();
|
|
65
|
+
const graphFor = (scope) => {
|
|
66
|
+
let g = graphs.get(scope);
|
|
67
|
+
if (!g) {
|
|
68
|
+
g = {
|
|
69
|
+
version: 1,
|
|
70
|
+
nodes: [],
|
|
71
|
+
edges: []
|
|
72
|
+
};
|
|
73
|
+
if (scope === "") g.meta = { direction: options.direction ?? flowDirection(ast.direction) };
|
|
74
|
+
else g.meta = {
|
|
75
|
+
title: composites.get(scope)?.name ?? scope,
|
|
76
|
+
direction: options.direction ?? flowDirection(ast.direction)
|
|
77
|
+
};
|
|
78
|
+
graphs.set(scope, g);
|
|
79
|
+
}
|
|
80
|
+
return g;
|
|
81
|
+
};
|
|
82
|
+
graphFor("");
|
|
83
|
+
const typeFor = {
|
|
84
|
+
state: "state",
|
|
85
|
+
start: "start",
|
|
86
|
+
end: "end",
|
|
87
|
+
choice: "decision",
|
|
88
|
+
fork: "state",
|
|
89
|
+
join: "state"
|
|
90
|
+
};
|
|
91
|
+
for (const s of ast.states.values()) {
|
|
92
|
+
const g = graphFor(scopeOf(s.parent));
|
|
93
|
+
const n = {
|
|
94
|
+
id: pseudoId(s.id),
|
|
95
|
+
type: typeFor[s.kind]
|
|
96
|
+
};
|
|
97
|
+
if (s.kind === "state" || s.kind === "choice") n.label = s.name;
|
|
98
|
+
if (s.kind === "fork" || s.kind === "join") {
|
|
99
|
+
n.label = s.name || s.kind;
|
|
100
|
+
n.data = { pseudo: s.kind };
|
|
101
|
+
}
|
|
102
|
+
if (s.descriptions.length) n.description = s.descriptions.join(" ");
|
|
103
|
+
const region = regionOf(s.parent);
|
|
104
|
+
if (region) n.group = region;
|
|
105
|
+
g.nodes.push(n);
|
|
106
|
+
}
|
|
107
|
+
for (const c of composites.values()) {
|
|
108
|
+
if (c.region) {
|
|
109
|
+
const g = graphFor(scopeOf(c.parent));
|
|
110
|
+
const group = { id: c.id };
|
|
111
|
+
const parentRegion = regionOf(c.parent);
|
|
112
|
+
if (parentRegion) group.parent = parentRegion;
|
|
113
|
+
(g.groups ??= []).push(group);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const g = graphFor(scopeOf(c.parent));
|
|
117
|
+
const n = {
|
|
118
|
+
id: c.id,
|
|
119
|
+
type: "subprocess",
|
|
120
|
+
label: c.name,
|
|
121
|
+
subgraph: graphFor(c.id)
|
|
122
|
+
};
|
|
123
|
+
const region = regionOf(c.parent);
|
|
124
|
+
if (region) n.group = region;
|
|
125
|
+
g.nodes.push(n);
|
|
126
|
+
}
|
|
127
|
+
let edgeNo = 0;
|
|
128
|
+
for (const t of ast.transitions) {
|
|
129
|
+
let scope = scopeOf(parentOf(t.from));
|
|
130
|
+
let from;
|
|
131
|
+
let to;
|
|
132
|
+
for (let guard = 0; scope !== void 0 && guard < 1e3; guard++) {
|
|
133
|
+
from = representative(t.from, scope);
|
|
134
|
+
to = representative(t.to, scope);
|
|
135
|
+
if (from !== void 0 && to !== void 0) break;
|
|
136
|
+
scope = scope === "" ? void 0 : scopeOf(composites.get(scope)?.parent);
|
|
137
|
+
}
|
|
138
|
+
if (scope === void 0 || from === void 0 || to === void 0) continue;
|
|
139
|
+
const e = {
|
|
140
|
+
id: `t${++edgeNo}`,
|
|
141
|
+
from: pseudoId(from),
|
|
142
|
+
to: pseudoId(to)
|
|
143
|
+
};
|
|
144
|
+
if (t.label) e.label = t.label;
|
|
145
|
+
graphFor(scope).edges.push(e);
|
|
146
|
+
}
|
|
147
|
+
let noteNo = 0;
|
|
148
|
+
for (const note of ast.notes) {
|
|
149
|
+
const g = graphFor(scopeOf(parentOf(note.target)));
|
|
150
|
+
const id = `note-${++noteNo}`;
|
|
151
|
+
g.nodes.push({
|
|
152
|
+
id,
|
|
153
|
+
type: "note",
|
|
154
|
+
label: note.text
|
|
155
|
+
});
|
|
156
|
+
g.edges.push({
|
|
157
|
+
id: `${id}-link`,
|
|
158
|
+
from: id,
|
|
159
|
+
to: pseudoId(note.target),
|
|
160
|
+
kind: "association"
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return graphFor("");
|
|
164
|
+
}
|
|
165
|
+
function fromFlowchart(ast, options) {
|
|
166
|
+
const incoming = new Set(ast.edges.map((e) => e.to));
|
|
167
|
+
const outgoing = new Set(ast.edges.map((e) => e.from));
|
|
168
|
+
const typeFor = (id, shape) => {
|
|
169
|
+
switch (shape) {
|
|
170
|
+
case "rhombus":
|
|
171
|
+
case "choice":
|
|
172
|
+
case "hexagon": return "decision";
|
|
173
|
+
case "subroutine": return "subprocess";
|
|
174
|
+
case "doublecircle":
|
|
175
|
+
case "end": return "end";
|
|
176
|
+
case "start": return "start";
|
|
177
|
+
case "circle": return !incoming.has(id) ? "start" : !outgoing.has(id) ? "end" : "state";
|
|
178
|
+
case "round":
|
|
179
|
+
case "stadium": return "state";
|
|
180
|
+
case "asymmetric": return "note";
|
|
181
|
+
default: return "task";
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
const g = {
|
|
185
|
+
version: 1,
|
|
186
|
+
nodes: [],
|
|
187
|
+
edges: [],
|
|
188
|
+
meta: { direction: options.direction ?? flowDirection(ast.direction) }
|
|
189
|
+
};
|
|
190
|
+
for (const n of ast.nodes.values()) {
|
|
191
|
+
const node = {
|
|
192
|
+
id: n.id,
|
|
193
|
+
type: typeFor(n.id, n.shape)
|
|
194
|
+
};
|
|
195
|
+
if (n.text && !(node.type === "start" && n.text === n.id) && !(node.type === "end" && n.text === n.id)) node.label = n.text;
|
|
196
|
+
if (n.subgraph) node.group = n.subgraph;
|
|
197
|
+
g.nodes.push(node);
|
|
198
|
+
}
|
|
199
|
+
if (ast.subgraphs.size) g.groups = [...ast.subgraphs.values()].map((s) => {
|
|
200
|
+
const group = { id: s.id };
|
|
201
|
+
if (s.title) group.label = s.title;
|
|
202
|
+
if (s.parent) group.parent = s.parent;
|
|
203
|
+
return group;
|
|
204
|
+
});
|
|
205
|
+
let i = 0;
|
|
206
|
+
for (const e of ast.edges) {
|
|
207
|
+
if (e.stroke === "invisible") continue;
|
|
208
|
+
const edge = {
|
|
209
|
+
id: `e${++i}`,
|
|
210
|
+
from: e.from,
|
|
211
|
+
to: e.to
|
|
212
|
+
};
|
|
213
|
+
if (e.text) edge.label = e.text;
|
|
214
|
+
if (e.end === "none" && e.stroke === "dotted") edge.kind = "association";
|
|
215
|
+
g.edges.push(edge);
|
|
216
|
+
}
|
|
217
|
+
return g;
|
|
218
|
+
}
|
|
219
|
+
exports.fromMermaid = fromMermaid;
|
|
220
|
+
exports.isMermaidFlow = isMermaidFlow;
|
|
221
|
+
|
|
222
|
+
//# sourceMappingURL=mermaid.cjs.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/** Built-in node types; any other string is a custom type from a registry. */
|
|
2
|
+
declare type BuiltinNodeType = 'start' | 'end' | 'state' | 'task' | 'decision' | 'subprocess' | 'note';
|
|
3
|
+
|
|
4
|
+
/** Data values a node or an edge carries for the host: shown as key/value. */
|
|
5
|
+
declare type DataValue = string | number | boolean | null | DataValue[] | {
|
|
6
|
+
[key: string]: DataValue;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
declare interface FlowEdge {
|
|
10
|
+
id: string;
|
|
11
|
+
from: string;
|
|
12
|
+
to: string;
|
|
13
|
+
fromPort?: string;
|
|
14
|
+
toPort?: string;
|
|
15
|
+
label?: string;
|
|
16
|
+
/** A guard or condition, shown in the details and, when there is no label, on the edge. */
|
|
17
|
+
condition?: string;
|
|
18
|
+
enabled?: boolean;
|
|
19
|
+
/** `association` is a dashed line without an arrow (a note to its node). */
|
|
20
|
+
kind?: 'transition' | 'association';
|
|
21
|
+
data?: Record<string, DataValue>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare interface FlowGroup {
|
|
25
|
+
id: string;
|
|
26
|
+
label?: string;
|
|
27
|
+
/** The enclosing group. */
|
|
28
|
+
parent?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare interface FlowNode {
|
|
32
|
+
id: string;
|
|
33
|
+
type: NodeType;
|
|
34
|
+
label?: string;
|
|
35
|
+
/** The node's centre. Nodes without one are placed by the auto layout. */
|
|
36
|
+
position?: Point;
|
|
37
|
+
/** The group (frame) the node sits in. */
|
|
38
|
+
group?: string;
|
|
39
|
+
data?: Record<string, DataValue>;
|
|
40
|
+
/** `false` disables the node: drawn dimmed and struck through. Enabled by default. */
|
|
41
|
+
enabled?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* The node's own process: an inline graph, or a reference the host resolves
|
|
44
|
+
* (`subgraphs` prop or `loadSubgraph`). Makes the node drillable.
|
|
45
|
+
*/
|
|
46
|
+
subgraph?: string | Graph;
|
|
47
|
+
/** Overrides the type's ports. */
|
|
48
|
+
ports?: Port[];
|
|
49
|
+
/** A short secondary line under the label. */
|
|
50
|
+
description?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Converts Mermaid source to a model.
|
|
55
|
+
*
|
|
56
|
+
* @throws GraphError for other diagram types; the diagrams parser's own
|
|
57
|
+
* `DiagramError` (with line and column) for syntax errors.
|
|
58
|
+
*/
|
|
59
|
+
export declare function fromMermaid(source: string, options?: MermaidImportOptions): Graph;
|
|
60
|
+
|
|
61
|
+
declare interface Graph {
|
|
62
|
+
version: typeof GRAPH_VERSION;
|
|
63
|
+
nodes: FlowNode[];
|
|
64
|
+
edges: FlowEdge[];
|
|
65
|
+
groups?: FlowGroup[];
|
|
66
|
+
meta?: GraphMeta;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The model format this package reads and writes. */
|
|
70
|
+
declare const GRAPH_VERSION = 1;
|
|
71
|
+
|
|
72
|
+
declare interface GraphMeta {
|
|
73
|
+
title?: string;
|
|
74
|
+
/** Auto-layout direction; `LR` by default. */
|
|
75
|
+
direction?: 'TB' | 'LR';
|
|
76
|
+
[key: string]: DataValue | undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Whether a source is one this importer reads. */
|
|
80
|
+
export declare function isMermaidFlow(source: string): boolean;
|
|
81
|
+
|
|
82
|
+
export declare interface MermaidImportOptions {
|
|
83
|
+
/** Overrides the diagram's direction (TB/BT → TB, LR/RL → LR). */
|
|
84
|
+
direction?: 'TB' | 'LR';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
declare type NodeType = BuiltinNodeType | (string & {});
|
|
88
|
+
|
|
89
|
+
declare interface Point {
|
|
90
|
+
x: number;
|
|
91
|
+
y: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A named connection point on a node's side. */
|
|
95
|
+
declare interface Port {
|
|
96
|
+
id: string;
|
|
97
|
+
side: PortSide;
|
|
98
|
+
/** Position along the side, 0…1 from its start (left or top); 0.5 by default. */
|
|
99
|
+
offset?: number;
|
|
100
|
+
/** `in` takes incoming edges only, `out` outgoing only; both by default. */
|
|
101
|
+
kind?: 'in' | 'out' | 'both';
|
|
102
|
+
label?: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
declare type PortSide = 'top' | 'right' | 'bottom' | 'left';
|
|
106
|
+
|
|
107
|
+
export { }
|
package/dist/mermaid.mjs
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { ct as GraphError } from "./core-DfJMfClV.js";
|
|
2
|
+
import { parse } from "@dskripchenko/diagrams";
|
|
3
|
+
// src/mermaid/index.ts
|
|
4
|
+
/**
|
|
5
|
+
* @dskripchenko/flow/mermaid — a model from Mermaid `stateDiagram-v2` or
|
|
6
|
+
* `flowchart` source, through the parser of @dskripchenko/diagrams (an
|
|
7
|
+
* optional peer dependency: only this entry imports it).
|
|
8
|
+
*
|
|
9
|
+
* import { fromMermaid } from '@dskripchenko/flow/mermaid'
|
|
10
|
+
* const graph = fromMermaid('stateDiagram-v2\n [*] --> Idle')
|
|
11
|
+
*
|
|
12
|
+
* Composite states become sub-process nodes with their own inline graph;
|
|
13
|
+
* flowchart subgraphs become groups.
|
|
14
|
+
*/
|
|
15
|
+
var flowDirection = (d) => d === "LR" || d === "RL" ? "LR" : "TB";
|
|
16
|
+
/** Whether a source is one this importer reads. */
|
|
17
|
+
function isMermaidFlow(source) {
|
|
18
|
+
const head = source.replace(/^\s*(%%[^\n]*\n\s*)*/, "").split(/\s/, 1)[0] ?? "";
|
|
19
|
+
return /^(stateDiagram(-v2)?|flowchart|graph)$/.test(head);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Converts Mermaid source to a model.
|
|
23
|
+
*
|
|
24
|
+
* @throws GraphError for other diagram types; the diagrams parser's own
|
|
25
|
+
* `DiagramError` (with line and column) for syntax errors.
|
|
26
|
+
*/
|
|
27
|
+
function fromMermaid(source, options = {}) {
|
|
28
|
+
const ast = parse(source);
|
|
29
|
+
if (ast && ast.type === "state" && ast.states instanceof Map) return fromState(ast, options);
|
|
30
|
+
if (ast && ast.type === "flowchart" && ast.nodes instanceof Map) return fromFlowchart(ast, options);
|
|
31
|
+
throw new GraphError(`Only stateDiagram-v2 and flowchart can be imported, not "${ast?.type ?? "unknown"}"`);
|
|
32
|
+
}
|
|
33
|
+
/** Readable ids for pseudo states: `\0start:root` → `start`, `\0end:Billing` → `Billing-end`. */
|
|
34
|
+
function pseudoId(id) {
|
|
35
|
+
const m = /^\u0000(start|end):(.*)$/.exec(id);
|
|
36
|
+
if (!m) return id;
|
|
37
|
+
return m[2] === "root" ? m[1] : `${m[2]}-${m[1]}`;
|
|
38
|
+
}
|
|
39
|
+
function fromState(ast, options) {
|
|
40
|
+
const composites = ast.composites;
|
|
41
|
+
/** The graph a state belongs to: its nearest composite that is not a region (or the root). */
|
|
42
|
+
const scopeOf = (parent) => {
|
|
43
|
+
let p = parent;
|
|
44
|
+
while (p !== void 0) {
|
|
45
|
+
const c = composites.get(p);
|
|
46
|
+
if (!c) return "";
|
|
47
|
+
if (!c.region) return p;
|
|
48
|
+
p = c.parent;
|
|
49
|
+
}
|
|
50
|
+
return "";
|
|
51
|
+
};
|
|
52
|
+
/** The region (as a group) a state sits in directly, if any. */
|
|
53
|
+
const regionOf = (parent) => parent !== void 0 && composites.get(parent)?.region ? parent : void 0;
|
|
54
|
+
const parentOf = (id) => ast.states.get(id)?.parent ?? composites.get(id)?.parent;
|
|
55
|
+
/** The element that stands for `id` inside graph `scope`: itself, or its ancestor composite in that scope. */
|
|
56
|
+
const representative = (id, scope) => {
|
|
57
|
+
let cur = id;
|
|
58
|
+
for (let guard = 0; cur !== void 0 && guard < 1e3; guard++) {
|
|
59
|
+
if (!composites.get(cur)?.region && scopeOf(parentOf(cur)) === scope) return cur;
|
|
60
|
+
cur = parentOf(cur);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const graphs = /* @__PURE__ */ new Map();
|
|
64
|
+
const graphFor = (scope) => {
|
|
65
|
+
let g = graphs.get(scope);
|
|
66
|
+
if (!g) {
|
|
67
|
+
g = {
|
|
68
|
+
version: 1,
|
|
69
|
+
nodes: [],
|
|
70
|
+
edges: []
|
|
71
|
+
};
|
|
72
|
+
if (scope === "") g.meta = { direction: options.direction ?? flowDirection(ast.direction) };
|
|
73
|
+
else g.meta = {
|
|
74
|
+
title: composites.get(scope)?.name ?? scope,
|
|
75
|
+
direction: options.direction ?? flowDirection(ast.direction)
|
|
76
|
+
};
|
|
77
|
+
graphs.set(scope, g);
|
|
78
|
+
}
|
|
79
|
+
return g;
|
|
80
|
+
};
|
|
81
|
+
graphFor("");
|
|
82
|
+
const typeFor = {
|
|
83
|
+
state: "state",
|
|
84
|
+
start: "start",
|
|
85
|
+
end: "end",
|
|
86
|
+
choice: "decision",
|
|
87
|
+
fork: "state",
|
|
88
|
+
join: "state"
|
|
89
|
+
};
|
|
90
|
+
for (const s of ast.states.values()) {
|
|
91
|
+
const g = graphFor(scopeOf(s.parent));
|
|
92
|
+
const n = {
|
|
93
|
+
id: pseudoId(s.id),
|
|
94
|
+
type: typeFor[s.kind]
|
|
95
|
+
};
|
|
96
|
+
if (s.kind === "state" || s.kind === "choice") n.label = s.name;
|
|
97
|
+
if (s.kind === "fork" || s.kind === "join") {
|
|
98
|
+
n.label = s.name || s.kind;
|
|
99
|
+
n.data = { pseudo: s.kind };
|
|
100
|
+
}
|
|
101
|
+
if (s.descriptions.length) n.description = s.descriptions.join(" ");
|
|
102
|
+
const region = regionOf(s.parent);
|
|
103
|
+
if (region) n.group = region;
|
|
104
|
+
g.nodes.push(n);
|
|
105
|
+
}
|
|
106
|
+
for (const c of composites.values()) {
|
|
107
|
+
if (c.region) {
|
|
108
|
+
const g = graphFor(scopeOf(c.parent));
|
|
109
|
+
const group = { id: c.id };
|
|
110
|
+
const parentRegion = regionOf(c.parent);
|
|
111
|
+
if (parentRegion) group.parent = parentRegion;
|
|
112
|
+
(g.groups ??= []).push(group);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const g = graphFor(scopeOf(c.parent));
|
|
116
|
+
const n = {
|
|
117
|
+
id: c.id,
|
|
118
|
+
type: "subprocess",
|
|
119
|
+
label: c.name,
|
|
120
|
+
subgraph: graphFor(c.id)
|
|
121
|
+
};
|
|
122
|
+
const region = regionOf(c.parent);
|
|
123
|
+
if (region) n.group = region;
|
|
124
|
+
g.nodes.push(n);
|
|
125
|
+
}
|
|
126
|
+
let edgeNo = 0;
|
|
127
|
+
for (const t of ast.transitions) {
|
|
128
|
+
let scope = scopeOf(parentOf(t.from));
|
|
129
|
+
let from;
|
|
130
|
+
let to;
|
|
131
|
+
for (let guard = 0; scope !== void 0 && guard < 1e3; guard++) {
|
|
132
|
+
from = representative(t.from, scope);
|
|
133
|
+
to = representative(t.to, scope);
|
|
134
|
+
if (from !== void 0 && to !== void 0) break;
|
|
135
|
+
scope = scope === "" ? void 0 : scopeOf(composites.get(scope)?.parent);
|
|
136
|
+
}
|
|
137
|
+
if (scope === void 0 || from === void 0 || to === void 0) continue;
|
|
138
|
+
const e = {
|
|
139
|
+
id: `t${++edgeNo}`,
|
|
140
|
+
from: pseudoId(from),
|
|
141
|
+
to: pseudoId(to)
|
|
142
|
+
};
|
|
143
|
+
if (t.label) e.label = t.label;
|
|
144
|
+
graphFor(scope).edges.push(e);
|
|
145
|
+
}
|
|
146
|
+
let noteNo = 0;
|
|
147
|
+
for (const note of ast.notes) {
|
|
148
|
+
const g = graphFor(scopeOf(parentOf(note.target)));
|
|
149
|
+
const id = `note-${++noteNo}`;
|
|
150
|
+
g.nodes.push({
|
|
151
|
+
id,
|
|
152
|
+
type: "note",
|
|
153
|
+
label: note.text
|
|
154
|
+
});
|
|
155
|
+
g.edges.push({
|
|
156
|
+
id: `${id}-link`,
|
|
157
|
+
from: id,
|
|
158
|
+
to: pseudoId(note.target),
|
|
159
|
+
kind: "association"
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return graphFor("");
|
|
163
|
+
}
|
|
164
|
+
function fromFlowchart(ast, options) {
|
|
165
|
+
const incoming = new Set(ast.edges.map((e) => e.to));
|
|
166
|
+
const outgoing = new Set(ast.edges.map((e) => e.from));
|
|
167
|
+
const typeFor = (id, shape) => {
|
|
168
|
+
switch (shape) {
|
|
169
|
+
case "rhombus":
|
|
170
|
+
case "choice":
|
|
171
|
+
case "hexagon": return "decision";
|
|
172
|
+
case "subroutine": return "subprocess";
|
|
173
|
+
case "doublecircle":
|
|
174
|
+
case "end": return "end";
|
|
175
|
+
case "start": return "start";
|
|
176
|
+
case "circle": return !incoming.has(id) ? "start" : !outgoing.has(id) ? "end" : "state";
|
|
177
|
+
case "round":
|
|
178
|
+
case "stadium": return "state";
|
|
179
|
+
case "asymmetric": return "note";
|
|
180
|
+
default: return "task";
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const g = {
|
|
184
|
+
version: 1,
|
|
185
|
+
nodes: [],
|
|
186
|
+
edges: [],
|
|
187
|
+
meta: { direction: options.direction ?? flowDirection(ast.direction) }
|
|
188
|
+
};
|
|
189
|
+
for (const n of ast.nodes.values()) {
|
|
190
|
+
const node = {
|
|
191
|
+
id: n.id,
|
|
192
|
+
type: typeFor(n.id, n.shape)
|
|
193
|
+
};
|
|
194
|
+
if (n.text && !(node.type === "start" && n.text === n.id) && !(node.type === "end" && n.text === n.id)) node.label = n.text;
|
|
195
|
+
if (n.subgraph) node.group = n.subgraph;
|
|
196
|
+
g.nodes.push(node);
|
|
197
|
+
}
|
|
198
|
+
if (ast.subgraphs.size) g.groups = [...ast.subgraphs.values()].map((s) => {
|
|
199
|
+
const group = { id: s.id };
|
|
200
|
+
if (s.title) group.label = s.title;
|
|
201
|
+
if (s.parent) group.parent = s.parent;
|
|
202
|
+
return group;
|
|
203
|
+
});
|
|
204
|
+
let i = 0;
|
|
205
|
+
for (const e of ast.edges) {
|
|
206
|
+
if (e.stroke === "invisible") continue;
|
|
207
|
+
const edge = {
|
|
208
|
+
id: `e${++i}`,
|
|
209
|
+
from: e.from,
|
|
210
|
+
to: e.to
|
|
211
|
+
};
|
|
212
|
+
if (e.text) edge.label = e.text;
|
|
213
|
+
if (e.end === "none" && e.stroke === "dotted") edge.kind = "association";
|
|
214
|
+
g.edges.push(edge);
|
|
215
|
+
}
|
|
216
|
+
return g;
|
|
217
|
+
}
|
|
218
|
+
export { fromMermaid, isMermaidFlow };
|
|
219
|
+
|
|
220
|
+
//# sourceMappingURL=mermaid.mjs.map
|