@jarenjs/mermaid 0.34.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.
Files changed (64) hide show
  1. package/README.md +282 -0
  2. package/dist/types/ast.d.ts +210 -0
  3. package/dist/types/component/index.d.ts +81 -0
  4. package/dist/types/errors.d.ts +27 -0
  5. package/dist/types/index.d.ts +97 -0
  6. package/dist/types/interactive.d.ts +36 -0
  7. package/dist/types/layout/flowchart.d.ts +12 -0
  8. package/dist/types/layout/sequence.d.ts +12 -0
  9. package/dist/types/layout/state.d.ts +19 -0
  10. package/dist/types/parser/class.d.ts +11 -0
  11. package/dist/types/parser/config.d.ts +61 -0
  12. package/dist/types/parser/er.d.ts +11 -0
  13. package/dist/types/parser/flowchart.d.ts +26 -0
  14. package/dist/types/parser/gantt.d.ts +11 -0
  15. package/dist/types/parser/index.d.ts +22 -0
  16. package/dist/types/parser/pie.d.ts +11 -0
  17. package/dist/types/parser/sequence.d.ts +16 -0
  18. package/dist/types/parser/state.d.ts +22 -0
  19. package/dist/types/plugin.d.ts +50 -0
  20. package/dist/types/render/error.d.ts +23 -0
  21. package/dist/types/render/flowchart.d.ts +18 -0
  22. package/dist/types/render/index.d.ts +23 -0
  23. package/dist/types/render/misc.d.ts +54 -0
  24. package/dist/types/render/sequence.d.ts +16 -0
  25. package/dist/types/styles.d.ts +81 -0
  26. package/dist/types/theme.d.ts +47 -0
  27. package/dist/types/to-mermaid.d.ts +20 -0
  28. package/dist/types/utils.d.ts +47 -0
  29. package/docs/MERMAID-FORMAT.md +242 -0
  30. package/package.json +84 -0
  31. package/schemas/jaren-mermaid-ast.schema.json +78 -0
  32. package/schemas/jaren-workflow.schema.json +28 -0
  33. package/src/ast.js +252 -0
  34. package/src/component/index.js +109 -0
  35. package/src/errors.js +35 -0
  36. package/src/index.js +155 -0
  37. package/src/interactive.js +244 -0
  38. package/src/layout/flowchart.js +352 -0
  39. package/src/layout/sequence.js +178 -0
  40. package/src/layout/state.js +65 -0
  41. package/src/parser/class.js +90 -0
  42. package/src/parser/config.js +215 -0
  43. package/src/parser/er.js +86 -0
  44. package/src/parser/flowchart.js +413 -0
  45. package/src/parser/gantt.js +49 -0
  46. package/src/parser/index.js +122 -0
  47. package/src/parser/pie.js +32 -0
  48. package/src/parser/sequence.js +156 -0
  49. package/src/parser/state.js +137 -0
  50. package/src/plugin.js +76 -0
  51. package/src/render/error.js +55 -0
  52. package/src/render/flowchart.js +249 -0
  53. package/src/render/index.js +93 -0
  54. package/src/render/misc.js +135 -0
  55. package/src/render/sequence.js +152 -0
  56. package/src/styles.js +181 -0
  57. package/src/theme.js +180 -0
  58. package/src/to-mermaid.js +317 -0
  59. package/src/utils.js +64 -0
  60. package/styles/mermaid.css +115 -0
  61. package/stylesheets/dag-to-flowchart.jslt.json +62 -0
  62. package/stylesheets/flowchart-to-dag.jslt.json +29 -0
  63. package/stylesheets/state-to-workflow.jslt.json +26 -0
  64. package/stylesheets/workflow-to-state.jslt.json +41 -0
package/src/ast.js ADDED
@@ -0,0 +1,252 @@
1
+ //@ts-check
2
+ /**
3
+ * @file The Mermaid AST vocabulary: node constructors and walkers.
4
+ *
5
+ * Every node is a plain JSON object born from a constructor so that all
6
+ * nodes of a type share one hidden class — property access stays
7
+ * monomorphic and the structural hash is deterministic (MERMAID-FORMAT
8
+ * §4). The AST is deliberately **geometry-free**:
9
+ * layout is a separate pass, so the AST is a faithful, lossless
10
+ * semantic model of the diagram's *meaning* — a directed graph
11
+ * (flowchart), an ordered interaction (sequence), a finite state machine
12
+ * (state) — that other engines project via JSLT without ever seeing a
13
+ * coordinate.
14
+ *
15
+ * Member order is fixed per constructor; keep it stable so the FNV-1a
16
+ * hash of `JSON.stringify` is reproducible and the round-trip printer
17
+ * (`to-mermaid.js`) is a fixed point.
18
+ */
19
+
20
+ /** The diagram-document format version this package produces. */
21
+ export const MERMAID_VERSION = '0.1';
22
+
23
+ //#region envelope --------------------------------------------------
24
+
25
+ /**
26
+ * @typedef {object} DiagramDocument
27
+ * @property {string} $mermaid format version
28
+ * @property {string} diagram diagram type (`flowchart`, `sequence`, …)
29
+ * @property {any} config plain-JSON config (theme, per-type options)
30
+ * @property {any} ast the type-specific, geometry-free AST
31
+ * @property {{ hash: string, direction: string|null, title: string|null }} meta
32
+ */
33
+
34
+ /**
35
+ * Assemble the shared DiagramDocument envelope. Fresh per parse,
36
+ * never mutated after return — share-friendly for JSLT/patch.
37
+ * @param {string} diagram
38
+ * @param {any} config
39
+ * @param {any} ast
40
+ * @param {{ hash: string, direction: string|null, title: string|null }} meta
41
+ * @returns {DiagramDocument}
42
+ */
43
+ export function diagramDocument(diagram, config, ast, meta) {
44
+ return { $mermaid: MERMAID_VERSION, diagram, config, ast, meta };
45
+ }
46
+
47
+ //#endregion
48
+ //#region flowchart -------------------------------------------------
49
+
50
+ /**
51
+ * @typedef {'rect'|'round'|'stadium'|'subroutine'|'cylinder'|'circle'
52
+ * |'doublecircle'|'diamond'|'hexagon'|'parallelogram'|'parallelogram_alt'
53
+ * |'trapezoid'|'trapezoid_alt'|'asymmetric'} FlowShape
54
+ */
55
+
56
+ /**
57
+ * A flowchart vertex.
58
+ * @param {string} id
59
+ * @param {string} label
60
+ * @param {FlowShape} shape
61
+ * @returns {{ id: string, label: string, shape: FlowShape }}
62
+ */
63
+ export function flowNode(id, label, shape) {
64
+ return { id, label, shape };
65
+ }
66
+
67
+ /**
68
+ * A flowchart edge (link). `stroke` is the line style, `head`/`tail`
69
+ * the endpoint markers, `length` the dash count (preserved so the
70
+ * canonical printer is a fixed point), `label` the optional edge text.
71
+ * @param {string} from
72
+ * @param {string} to
73
+ * @param {'solid'|'thick'|'dotted'} stroke
74
+ * @param {'none'|'arrow'|'circle'|'cross'} head
75
+ * @param {'none'|'arrow'|'circle'|'cross'} tail
76
+ * @param {number} length
77
+ * @param {string|null} label
78
+ * @returns {object}
79
+ */
80
+ export function flowEdge(from, to, stroke, head, tail, length, label) {
81
+ return { from, to, stroke, head, tail, length, label };
82
+ }
83
+
84
+ /**
85
+ * A subgraph grouping.
86
+ * @param {string} id
87
+ * @param {string} label
88
+ * @param {string|null} direction
89
+ * @param {string[]} nodes ids of member nodes
90
+ * @returns {object}
91
+ */
92
+ export function flowSubgraph(id, label, direction, nodes) {
93
+ return { id, label, direction, nodes };
94
+ }
95
+
96
+ /**
97
+ * A `classDef` style class.
98
+ * @param {string} name
99
+ * @param {string} styles semicolon-separated CSS declarations
100
+ * @returns {object}
101
+ */
102
+ export function flowClassDef(name, styles) {
103
+ return { name, styles };
104
+ }
105
+
106
+ /**
107
+ * A `class`/`:::` assignment of a style class to a node.
108
+ * @param {string} node
109
+ * @param {string} name
110
+ * @returns {object}
111
+ */
112
+ export function flowClass(node, name) {
113
+ return { node, name };
114
+ }
115
+
116
+ /**
117
+ * A `style` inline-style assignment to a node.
118
+ * @param {string} node
119
+ * @param {string} styles
120
+ * @returns {object}
121
+ */
122
+ export function flowStyle(node, styles) {
123
+ return { node, styles };
124
+ }
125
+
126
+ /**
127
+ * The flowchart AST root.
128
+ * @param {string} direction
129
+ * @param {object[]} nodes
130
+ * @param {object[]} edges
131
+ * @param {object[]} subgraphs
132
+ * @param {object[]} classDefs
133
+ * @param {object[]} classes
134
+ * @param {object[]} styles
135
+ * @returns {object}
136
+ */
137
+ export function flowchartAst(direction, nodes, edges, subgraphs, classDefs, classes, styles) {
138
+ return { direction, nodes, edges, subgraphs, classDefs, classes, styles };
139
+ }
140
+
141
+ //#endregion
142
+ //#region sequence --------------------------------------------------
143
+
144
+ /**
145
+ * A declared participant/actor.
146
+ * @param {string} id
147
+ * @param {string} label
148
+ * @param {'participant'|'actor'} kind
149
+ * @returns {object}
150
+ */
151
+ export function seqParticipant(id, label, kind) {
152
+ return { id, label, kind };
153
+ }
154
+
155
+ /**
156
+ * A message statement.
157
+ * @param {string} from
158
+ * @param {string} to
159
+ * @param {string} text
160
+ * @param {'solid'|'dotted'} line
161
+ * @param {'arrow'|'open'|'cross'|'point'} head
162
+ * @param {'activate'|'deactivate'|null} activation `+`/`-` shorthand
163
+ * @returns {object}
164
+ */
165
+ export function seqMessage(from, to, text, line, head, activation) {
166
+ return { kind: 'message', from, to, text, line, head, activation };
167
+ }
168
+
169
+ /**
170
+ * A note statement.
171
+ * @param {'left of'|'right of'|'over'} placement
172
+ * @param {string[]} actors
173
+ * @param {string} text
174
+ * @returns {object}
175
+ */
176
+ export function seqNote(placement, actors, text) {
177
+ return { kind: 'note', placement, actors, text };
178
+ }
179
+
180
+ /**
181
+ * An explicit activate/deactivate statement.
182
+ * @param {'activate'|'deactivate'} kind
183
+ * @param {string} actor
184
+ * @returns {object}
185
+ */
186
+ export function seqActivation(kind, actor) {
187
+ return { kind, actor };
188
+ }
189
+
190
+ /**
191
+ * A block statement (loop/opt/alt/par/critical/break). Single-branch
192
+ * blocks (loop/opt/critical/break) carry one branch; alt/par carry one
193
+ * per `else`/`and` section. `branches` is always present so the shape
194
+ * is monomorphic.
195
+ * @param {'loop'|'opt'|'alt'|'par'|'critical'|'break'} blockType
196
+ * @param {{ label: string, statements: object[] }[]} branches
197
+ * @returns {object}
198
+ */
199
+ export function seqBlock(blockType, branches) {
200
+ return { kind: 'block', blockType, branches };
201
+ }
202
+
203
+ /**
204
+ * The sequence AST root.
205
+ * @param {object[]} participants explicitly declared participants
206
+ * @param {object[]} statements ordered interaction statements
207
+ * @param {boolean} autonumber
208
+ * @returns {object}
209
+ */
210
+ export function sequenceAst(participants, statements, autonumber) {
211
+ return { participants, statements, autonumber };
212
+ }
213
+
214
+ //#endregion
215
+ //#region generic AST for secondary/placeholder diagrams ------------
216
+
217
+ /**
218
+ * A geometry-free "lines" AST for diagram types that parse-accept but
219
+ * are not yet laid out (the secondary types). Preserves the raw body
220
+ * lines so `toMermaid` round-trips and the coverage scorecard can be
221
+ * honest.
222
+ * @param {string} diagram
223
+ * @param {string[]} lines
224
+ * @returns {object}
225
+ */
226
+ export function rawAst(diagram, lines) {
227
+ return { diagram, lines };
228
+ }
229
+
230
+ //#endregion
231
+ //#region walkers ---------------------------------------------------
232
+
233
+ /**
234
+ * Walk the ordered statements of a sequence AST (descending into block
235
+ * branches), calling `visitor(stmt)` pre-order.
236
+ * @param {object[]} statements
237
+ * @param {(stmt: any) => void} visitor
238
+ */
239
+ export function walkSequence(statements, visitor) {
240
+ for (let i = 0; i < statements.length; i++) {
241
+ const stmt = statements[i];
242
+ visitor(stmt);
243
+ if (/** @type {any} */ (stmt).kind === 'block') {
244
+ const branches = /** @type {any} */ (stmt).branches;
245
+ for (let b = 0; b < branches.length; b++) {
246
+ walkSequence(branches[b].statements, visitor);
247
+ }
248
+ }
249
+ }
250
+ }
251
+
252
+ //#endregion
@@ -0,0 +1,109 @@
1
+ //@ts-check
2
+ /**
3
+ * @file The Mermaid VISUAL COMPONENT — part two of the package.
4
+ *
5
+ * Everything below this line is presentation glue; the engine
6
+ * (`@jarenjs/mermaid`) neither knows nor needs any of it. Mirrors
7
+ * `@jarenjs/md`'s `createMdComponent` field-for-field:
8
+ *
9
+ * - `createMermaidComponent()` — a memoized `view()` projection for
10
+ * `@jarenjs/app` viewModels (reference-stable, so an unchanged source
11
+ * patches in O(1) — the O(change) contract), `effects` entries for
12
+ * the app effect registry (`mermaid-render`, `mermaid-load`), and a
13
+ * `hydrate()` pass kept for API symmetry (a no-op in v1: the render is
14
+ * already complete);
15
+ * - `styles/mermaid.css` — the component stylesheet.
16
+ *
17
+ * The boundary is one-way: the component imports the engine, never the
18
+ * reverse.
19
+ */
20
+
21
+ import { createProjectionMemo } from '@jarenjs/view/helpers';
22
+ import { compileMermaid, diagramToVnode } from '../index.js';
23
+
24
+ /**
25
+ * @typedef {object} MermaidComponentOptions
26
+ * @property {any} [theme] theme name or override object
27
+ * @property {number} [memoLimit] LRU size for the source-string memo (default 32)
28
+ * @property {string | URL} [base] base URL for `mermaid-load`
29
+ * @property {typeof globalThis.fetch} [fetch] fetch implementation for `mermaid-load`
30
+ * @property {(err: any) => void} [onHydrateError]
31
+ */
32
+ /**
33
+ * @typedef {object} MermaidComponent
34
+ * @property {(source: string) => any} compile memoized compile
35
+ * @property {(sourceOrDoc: any) => any} view memoized vnode projection
36
+ * @property {Record<string, (props: any, dispatch: any) => any>} effects
37
+ * @property {(container: any) => void} hydrate no-op in v1
38
+ */
39
+
40
+ /**
41
+ * Create the Mermaid component.
42
+ *
43
+ * @example
44
+ * const mermaid = createMermaidComponent();
45
+ * createApp(appDoc, {
46
+ * effects: { ...mermaid.effects },
47
+ * viewModel: (state) => ({ ...state, diagram: mermaid.view(state.source) }),
48
+ * });
49
+ *
50
+ * @param {MermaidComponentOptions} [options]
51
+ * @returns {MermaidComponent}
52
+ */
53
+ export function createMermaidComponent(options = {}) {
54
+ const compileOptions = { theme: options.theme };
55
+
56
+ const { compile, view } = createProjectionMemo({
57
+ memoLimit: options.memoLimit ?? 32,
58
+ compile: (source) => compileMermaid(source, compileOptions),
59
+ toVnode: (compiled) => compiled.toVnode(),
60
+ docToVnode: (doc) => diagramToVnode(doc, compileOptions),
61
+ });
62
+
63
+ return {
64
+ compile,
65
+
66
+ view,
67
+
68
+ effects: {
69
+ /**
70
+ * Render an in-state source to an SVG vnode and dispatch it:
71
+ * `{ run: 'mermaid-render', with: { source, done } }`.
72
+ */
73
+ 'mermaid-render': (props, dispatch) => {
74
+ dispatch(props.done, compile(String(props.source ?? '')).toVnode());
75
+ },
76
+
77
+ /**
78
+ * Fetch a `.mmd`/`.mermaid` URL and dispatch the DiagramDocument:
79
+ * `{ run: 'mermaid-load', with: { url, done, error? } }`.
80
+ */
81
+ 'mermaid-load': (props, dispatch) => {
82
+ const f = options.fetch ?? globalThis.fetch;
83
+ const url = options.base ? new URL(String(props.url), options.base).href : String(props.url);
84
+ return f(url).then(
85
+ (res) => {
86
+ if (!res.ok) throw new Error(`HTTP ${res.status} for ${url}`);
87
+ return res.text();
88
+ },
89
+ ).then(
90
+ (text) => dispatch(props.done, compile(text).doc),
91
+ (err) => {
92
+ if (props.error !== undefined) {
93
+ dispatch(props.error, { url, message: String(err?.message ?? err) });
94
+ }
95
+ else {
96
+ throw err;
97
+ }
98
+ },
99
+ );
100
+ },
101
+ },
102
+
103
+ // Render is complete; hydrate is reserved for optional
104
+ // client-only enhancements (pan/zoom) that are out of scope for v1.
105
+ hydrate() {
106
+ /* no-op */
107
+ },
108
+ };
109
+ }
package/src/errors.js ADDED
@@ -0,0 +1,35 @@
1
+ //@ts-check
2
+ /**
3
+ * @file The engine's one error type. Parsers raise it with a 1-based
4
+ * `line`/`column`; the render path (`diagramToVnode`/`renderMermaid`)
5
+ * catches it and emits a clear error vnode instead of throwing, so
6
+ * rendering is always total.
7
+ */
8
+
9
+ export class MermaidParseError extends Error {
10
+ /**
11
+ * @param {string} message
12
+ * @param {number} [line] 1-based line number
13
+ * @param {number} [column] 1-based column number
14
+ */
15
+ constructor(message, line = 0, column = 0) {
16
+ super(message);
17
+ this.name = 'MermaidParseError';
18
+ /** @type {number} */
19
+ this.line = line;
20
+ /** @type {number} */
21
+ this.column = column;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * The `fail(message, position)` idiom from `packages/json/src/path.js`,
27
+ * adapted to line/column. Throws; never returns.
28
+ * @param {string} message
29
+ * @param {number} line
30
+ * @param {number} [column]
31
+ * @returns {never}
32
+ */
33
+ export function fail(message, line, column = 0) {
34
+ throw new MermaidParseError(message, line, column);
35
+ }
package/src/index.js ADDED
@@ -0,0 +1,155 @@
1
+ //@ts-check
2
+ /**
3
+ * @file `@jarenjs/mermaid` — a native, headless Mermaid clone. This is
4
+ * the **engine** (part one): pure functions over data — text ⇄ AST ⇄
5
+ * pure-vnode SVG — that know only the `@jarenjs/view` vnode shape. It
6
+ * imports nothing from the component, `@jarenjs/app`, the DOM or
7
+ * `@jarenjs/md` (the two-layer rule).
8
+ *
9
+ * The pipeline mirrors `@jarenjs/md`:
10
+ *
11
+ * source ──parseMermaid──▶ DiagramDocument (geometry-free JSON AST)
12
+ * │
13
+ * ┌─────────────────┼───────────────────┐
14
+ * ▼ ▼ ▼
15
+ * toMermaid() layoutDiagram() JSLT / query
16
+ * (canonical → diagramToVnode() (the AST is an
17
+ * round-trip) (pure-vnode SVG) ordinary document)
18
+ */
19
+
20
+ import { renderToString, createDomRenderer } from '@jarenjs/view';
21
+ import { parseMermaid } from './parser/index.js';
22
+ import { toMermaid } from './to-mermaid.js';
23
+ import { diagramToVnode, layoutDiagram } from './render/index.js';
24
+ import { hashContent } from './utils.js';
25
+
26
+ export { parseMermaid } from './parser/index.js';
27
+ export { toMermaid } from './to-mermaid.js';
28
+ export { parseMermaidConfig } from './parser/config.js';
29
+ export { layoutDiagram, diagramToVnode } from './render/index.js';
30
+ export { createTheme, THEMES, HOST_VARS } from './theme.js';
31
+ export { sanitizeHref } from '@jarenjs/view/helpers';
32
+ export { MermaidParseError } from './errors.js';
33
+ export { hashContent } from './utils.js';
34
+ export {
35
+ MERMAID_VERSION, diagramDocument, walkSequence,
36
+ flowNode, flowEdge, sequenceAst, flowchartAst,
37
+ } from './ast.js';
38
+
39
+ /**
40
+ * Convenience: parse → layout → render, error-safe, in one call.
41
+ * @param {string} source
42
+ * @param {{ theme?: any }} [options]
43
+ * @returns {any} an SVG vnode
44
+ */
45
+ export function renderMermaid(source, options = {}) {
46
+ return diagramToVnode(source, options);
47
+ }
48
+
49
+ /**
50
+ * @typedef {object} CompiledMermaid
51
+ * @property {import('./ast.js').DiagramDocument} doc the parsed document
52
+ * @property {() => any} toVnode cached pure-vnode SVG
53
+ * @property {() => string} toSvgString cached standalone SVG string (SSR)
54
+ * @property {() => string} toText canonical Mermaid text (`toMermaid`)
55
+ * @property {(visitor: (stmt: any) => void) => void} walk walk the AST
56
+ */
57
+
58
+ /**
59
+ * Parse once and return a bundle of cached projections.
60
+ * `toVnode`/`toSvgString`/`toText` each compute at most once; the
61
+ * vnode is returned by reference on repeat calls, so an unchanged
62
+ * document patches in O(1) through the view reconciler.
63
+ *
64
+ * @param {string} source
65
+ * @param {{ theme?: any, [k: string]: any }} [options]
66
+ * @returns {CompiledMermaid}
67
+ */
68
+ export function compileMermaid(source, options = {}) {
69
+ let doc = null;
70
+ let parseError = null;
71
+ try {
72
+ doc = parseMermaid(source, options);
73
+ }
74
+ catch (err) {
75
+ parseError = err;
76
+ }
77
+ let vnode;
78
+ let svg;
79
+ let text;
80
+ let layout;
81
+ return {
82
+ doc,
83
+ parseError,
84
+ toVnode() {
85
+ // Error-safe: a parse failure renders the error vnode, which
86
+ // `diagramToVnode` produces when handed the original source.
87
+ if (vnode === undefined) vnode = diagramToVnode(doc ?? source, options);
88
+ return vnode;
89
+ },
90
+ toSvgString() {
91
+ if (svg === undefined) svg = renderToString(this.toVnode());
92
+ return svg;
93
+ },
94
+ toText() {
95
+ if (text === undefined) text = doc !== null ? toMermaid(doc) : source;
96
+ return text;
97
+ },
98
+ toLayout() {
99
+ // The editor's hit-testing substrate: the pure PositionedDiagram
100
+ // (nodes with x/y/w/h, edges with routed points), cached like the
101
+ // other projections — same compiled document, reference-equal
102
+ // scene. Null for types without a geometric layout, and for
103
+ // documents that failed to parse.
104
+ if (layout === undefined) layout = doc !== null ? layoutDiagram(doc) : null;
105
+ return layout;
106
+ },
107
+ walk(visitor) {
108
+ if (doc !== null) walkDoc(doc, visitor);
109
+ },
110
+ };
111
+ }
112
+
113
+ /**
114
+ * A host-DOM renderer (mirrors `@jarenjs/md`'s `createMdRenderer`).
115
+ * Owns a `@jarenjs/view` DOM renderer over `container` and patches the
116
+ * rendered SVG on each `render(docOrSource)` call. `createDomRenderer`
117
+ * touches no DOM until it is handed a container, so importing it keeps
118
+ * the engine host-agnostic.
119
+ *
120
+ * @param {{ container: any, document?: any, theme?: any, [k: string]: any }} config
121
+ * @returns {(docOrSource: any) => void}
122
+ */
123
+ export function createMermaidRenderer(config) {
124
+ const render = createDomRenderer(config.container, { document: config.document });
125
+ return (docOrSource) => {
126
+ render(diagramToVnode(docOrSource, config));
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Walk a document's AST statements (sequence blocks descend).
132
+ * @param {import('./ast.js').DiagramDocument} doc
133
+ * @param {(stmt: any) => void} visitor
134
+ */
135
+ function walkDoc(doc, visitor) {
136
+ const ast = doc.ast;
137
+ if (doc.diagram === 'sequence') {
138
+ const walk = (stmts) => {
139
+ for (const s of stmts) {
140
+ visitor(s);
141
+ if (s.kind === 'block') for (const b of s.branches) walk(b.statements);
142
+ }
143
+ };
144
+ walk(ast.statements);
145
+ }
146
+ else if (doc.diagram === 'flowchart') {
147
+ for (const n of ast.nodes) visitor(n);
148
+ for (const e of ast.edges) visitor(e);
149
+ }
150
+ else {
151
+ visitor(ast);
152
+ }
153
+ }
154
+
155
+ export { hashContent as contentHash };