@diagrammo/dgmo 0.47.0 → 0.48.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/dist/advanced.js +11 -11
- package/dist/auto.js +16 -16
- package/dist/auto.mjs +41 -12
- package/dist/block.js +4 -4
- package/dist/{chunk-FNB3PNGF.js → chunk-7DTWEFUE.js} +3 -3
- package/dist/{chunk-2DXMZRWJ.js → chunk-B65G5DNL.js} +8 -1
- package/dist/{chunk-RZCF2VVM.js → chunk-B7JUCQUA.js} +3 -1
- package/dist/{chunk-DU7W62N5.js → chunk-FANX6UJR.js} +3 -3
- package/dist/{chunk-6FZYCKXB.js → chunk-HSH73DP6.js} +2 -2
- package/dist/{chunk-P4RH4OSP.js → chunk-ILKORZ3H.js} +2 -2
- package/dist/{chunk-Y5QGVTVC.js → chunk-JTSMEYIZ.js} +7 -3
- package/dist/{chunk-RCERWOJR.js → chunk-ODZVHU3R.js} +1 -2
- package/dist/{chunk-DZAQBW35.js → chunk-SGZYXELT.js} +3 -7
- package/dist/{chunk-CUVHQHZ7.js → chunk-THXCFRTU.js} +3 -3
- package/dist/{chunk-IH6ZMVQ5.js → chunk-UC7TFHUM.js} +29 -11
- package/dist/{chunk-EMS2SK26.js → chunk-ZBWMSD5N.js} +3 -1
- package/dist/cli.cjs +15 -15
- package/dist/{dgmo-router-DZCIDUO5.js → dgmo-router-X35VLZLM.js} +3 -3
- package/dist/element.js +16 -16
- package/dist/element.mjs +41 -12
- package/dist/{flowchart-renderer-ZAEOL4DX.js → flowchart-renderer-SNDSII4D.js} +2 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -4
- package/dist/{parser-FUSKFGZN.js → parser-2EWECDSA.js} +2 -2
- package/dist/{renderer-UWA5XS74.js → renderer-4JHRUBE6.js} +3 -3
- package/dist/{renderer-WZRREQ2P.js → renderer-5MYBMBYO.js} +2 -2
- package/dist/{renderer-TOCOVLHS.js → renderer-B2OMQIQG.js} +3 -3
- package/dist/{renderer-2XQQQ6QR.js → renderer-N43WR5GV.js} +3 -3
- package/dist/{renderer-T7YKC5VP.js → renderer-ZRNG54CW.js} +3 -3
- package/dist/{state-renderer-RFYHSP4K.js → state-renderer-B4DQBM6M.js} +2 -2
- package/package.json +23 -71
- package/dist/advanced.cjs +0 -71648
- package/dist/advanced.d.cts +0 -5719
- package/dist/auto.cjs +0 -70648
- package/dist/auto.d.cts +0 -54
- package/dist/block.cjs +0 -70208
- package/dist/block.d.cts +0 -115
- package/dist/editor.cjs +0 -526
- package/dist/editor.d.cts +0 -26
- package/dist/element.cjs +0 -70530
- package/dist/element.d.cts +0 -62
- package/dist/highlight.cjs +0 -852
- package/dist/highlight.d.cts +0 -49
- package/dist/index.cjs +0 -70491
- package/dist/index.d.cts +0 -160
- package/dist/pert.cjs +0 -325
- package/dist/pert.d.cts +0 -559
- package/dist/themes-1-CuKpeH.d.cts +0 -908
- package/src/advanced.ts +0 -873
- package/src/arc/parser.ts +0 -15
- package/src/arc/renderer.ts +0 -633
- package/src/auto/index.ts +0 -621
- package/src/auto/shared.ts +0 -370
- package/src/auto/styles.ts +0 -73
- package/src/block/layout.ts +0 -198
- package/src/block/parser.ts +0 -427
- package/src/block/renderer.ts +0 -484
- package/src/block/types.ts +0 -67
- package/src/boxes-and-lines/collapse.ts +0 -96
- package/src/boxes-and-lines/focus.ts +0 -257
- package/src/boxes-and-lines/label-placement.ts +0 -339
- package/src/boxes-and-lines/layout-grouped.ts +0 -912
- package/src/boxes-and-lines/layout-layered.ts +0 -722
- package/src/boxes-and-lines/layout-search.ts +0 -1678
- package/src/boxes-and-lines/layout.ts +0 -398
- package/src/boxes-and-lines/parser.ts +0 -1181
- package/src/boxes-and-lines/renderer.ts +0 -1465
- package/src/boxes-and-lines/types.ts +0 -66
- package/src/c4/layout.ts +0 -2441
- package/src/c4/parser.ts +0 -1158
- package/src/c4/renderer.ts +0 -2008
- package/src/c4/types.ts +0 -89
- package/src/chart-type-registry.ts +0 -529
- package/src/chart-types.ts +0 -225
- package/src/chart.ts +0 -685
- package/src/charts-d3/bar.ts +0 -338
- package/src/charts-d3/chord.ts +0 -140
- package/src/charts-d3/function.ts +0 -147
- package/src/charts-d3/funnel.ts +0 -104
- package/src/charts-d3/heatmap.ts +0 -145
- package/src/charts-d3/index.ts +0 -337
- package/src/charts-d3/interactions.ts +0 -477
- package/src/charts-d3/line.ts +0 -344
- package/src/charts-d3/mount.ts +0 -151
- package/src/charts-d3/pie.ts +0 -117
- package/src/charts-d3/polar.ts +0 -89
- package/src/charts-d3/radar.ts +0 -0
- package/src/charts-d3/sankey.ts +0 -0
- package/src/charts-d3/scatter.ts +0 -185
- package/src/charts-d3/shared.ts +0 -208
- package/src/class/layout.ts +0 -326
- package/src/class/parser.ts +0 -635
- package/src/class/renderer.ts +0 -793
- package/src/class/types.ts +0 -58
- package/src/cli-banner.ts +0 -106
- package/src/cli.ts +0 -1080
- package/src/colors-diagnostics.ts +0 -35
- package/src/colors.ts +0 -379
- package/src/completion-types.ts +0 -27
- package/src/completion.ts +0 -2668
- package/src/cycle/layout.ts +0 -1026
- package/src/cycle/parser.ts +0 -387
- package/src/cycle/renderer.ts +0 -653
- package/src/cycle/types.ts +0 -101
- package/src/d3.ts +0 -1434
- package/src/data-chart-parser.ts +0 -1007
- package/src/dgmo-router.ts +0 -296
- package/src/diagnostics-registry.ts +0 -108
- package/src/diagnostics.ts +0 -0
- package/src/dimensions.ts +0 -20
- package/src/directives-registry.ts +0 -396
- package/src/editor/dgmo.grammar +0 -76
- package/src/editor/dgmo.grammar.d.ts +0 -2
- package/src/editor/dgmo.grammar.js +0 -18
- package/src/editor/dgmo.grammar.terms.d.ts +0 -5
- package/src/editor/dgmo.grammar.terms.js +0 -35
- package/src/editor/highlight-api.ts +0 -596
- package/src/editor/highlight.ts +0 -36
- package/src/editor/index.ts +0 -27
- package/src/editor/keywords.ts +0 -128
- package/src/editor/tokens.ts +0 -30
- package/src/element/index.ts +0 -432
- package/src/embed/css.ts +0 -312
- package/src/embed/escape.ts +0 -15
- package/src/embed/index.ts +0 -280
- package/src/er/classify.ts +0 -228
- package/src/er/layout.ts +0 -530
- package/src/er/parser.ts +0 -750
- package/src/er/renderer.ts +0 -725
- package/src/er/types.ts +0 -52
- package/src/error-card.ts +0 -269
- package/src/event-line/diagnostics.ts +0 -117
- package/src/event-line/parser.ts +0 -643
- package/src/event-line/renderer.ts +0 -1742
- package/src/event-line/types.ts +0 -74
- package/src/fonts.ts +0 -3
- package/src/gantt/calculator.ts +0 -979
- package/src/gantt/parser.ts +0 -1641
- package/src/gantt/renderer.ts +0 -3787
- package/src/gantt/resolver.ts +0 -172
- package/src/gantt/types.ts +0 -206
- package/src/graph/diagnostics.ts +0 -64
- package/src/graph/edge-spline.ts +0 -29
- package/src/graph/flowchart-parser.ts +0 -759
- package/src/graph/flowchart-renderer.ts +0 -817
- package/src/graph/layout.ts +0 -472
- package/src/graph/notes.ts +0 -21
- package/src/graph/state-collapse.ts +0 -78
- package/src/graph/state-parser.ts +0 -574
- package/src/graph/state-renderer.ts +0 -653
- package/src/graph/types.ts +0 -65
- package/src/index.ts +0 -261
- package/src/infra/compute.ts +0 -1385
- package/src/infra/layout.ts +0 -888
- package/src/infra/parser.ts +0 -1234
- package/src/infra/renderer.ts +0 -2534
- package/src/infra/roles.ts +0 -75
- package/src/infra/types.ts +0 -216
- package/src/infra/validation.ts +0 -201
- package/src/journey-map/layout.ts +0 -490
- package/src/journey-map/parser.ts +0 -557
- package/src/journey-map/renderer.ts +0 -1580
- package/src/journey-map/types.ts +0 -47
- package/src/kanban/mutations.ts +0 -175
- package/src/kanban/parser.ts +0 -509
- package/src/kanban/renderer.ts +0 -1449
- package/src/kanban/types.ts +0 -39
- package/src/label-layout.ts +0 -368
- package/src/map/colorize.ts +0 -54
- package/src/map/completion.ts +0 -316
- package/src/map/context-labels.ts +0 -609
- package/src/map/data/PROVENANCE.json +0 -1
- package/src/map/data/README.md +0 -40
- package/src/map/data/airport-collisions.json +0 -1
- package/src/map/data/airports.json +0 -1
- package/src/map/data/gazetteer.json +0 -1
- package/src/map/data/lakes.json +0 -1
- package/src/map/data/mountain-ranges.json +0 -1
- package/src/map/data/na-lakes.json +0 -1
- package/src/map/data/na-land.json +0 -1
- package/src/map/data/region-names.json +0 -1
- package/src/map/data/rivers.json +0 -1
- package/src/map/data/types.ts +0 -121
- package/src/map/data/us-states.json +0 -1
- package/src/map/data/water-bodies.json +0 -1
- package/src/map/data/world-coarse.json +0 -1
- package/src/map/data/world-detail.json +0 -1
- package/src/map/diagnostics.ts +0 -134
- package/src/map/dimensions.ts +0 -150
- package/src/map/geo-query.ts +0 -295
- package/src/map/geo.ts +0 -438
- package/src/map/invert.ts +0 -111
- package/src/map/layout.ts +0 -5069
- package/src/map/legend-band.ts +0 -127
- package/src/map/load-data.ts +0 -186
- package/src/map/parser.ts +0 -783
- package/src/map/renderer.ts +0 -1445
- package/src/map/resolved-types.ts +0 -160
- package/src/map/resolver.ts +0 -1146
- package/src/map/types.ts +0 -194
- package/src/mindmap/collapse.ts +0 -67
- package/src/mindmap/layout.ts +0 -649
- package/src/mindmap/parser.ts +0 -430
- package/src/mindmap/renderer.ts +0 -637
- package/src/mindmap/text-wrap.ts +0 -218
- package/src/mindmap/types.ts +0 -56
- package/src/org/collapse.ts +0 -151
- package/src/org/layout.ts +0 -1262
- package/src/org/parser.ts +0 -519
- package/src/org/renderer.ts +0 -866
- package/src/org/resolver.ts +0 -636
- package/src/palettes/atlas.ts +0 -77
- package/src/palettes/blueprint.ts +0 -73
- package/src/palettes/catppuccin.ts +0 -84
- package/src/palettes/color-utils.ts +0 -443
- package/src/palettes/index.ts +0 -67
- package/src/palettes/nord.ts +0 -67
- package/src/palettes/registry.ts +0 -114
- package/src/palettes/slate.ts +0 -73
- package/src/palettes/tidewater.ts +0 -73
- package/src/palettes/tokyo-night.ts +0 -86
- package/src/palettes/types.ts +0 -88
- package/src/pert/analyzer.ts +0 -1183
- package/src/pert/diagnostics.ts +0 -110
- package/src/pert/index.ts +0 -43
- package/src/pert/internal.ts +0 -252
- package/src/pert/layout.ts +0 -971
- package/src/pert/monte-carlo.ts +0 -446
- package/src/pert/parser.ts +0 -1606
- package/src/pert/renderer.ts +0 -4009
- package/src/pert/share-normalize.ts +0 -62
- package/src/pert/types.ts +0 -604
- package/src/pyramid/parser.ts +0 -185
- package/src/pyramid/renderer.ts +0 -768
- package/src/pyramid/types.ts +0 -28
- package/src/quadrant/parser.ts +0 -18
- package/src/quadrant/renderer.ts +0 -638
- package/src/raci/diagnostics.ts +0 -213
- package/src/raci/index.ts +0 -23
- package/src/raci/mutations.ts +0 -321
- package/src/raci/parser.ts +0 -699
- package/src/raci/renderer.ts +0 -1627
- package/src/raci/types.ts +0 -82
- package/src/raci/variants.ts +0 -320
- package/src/render.ts +0 -268
- package/src/ring/parser.ts +0 -212
- package/src/ring/renderer.ts +0 -490
- package/src/ring/types.ts +0 -27
- package/src/sequence/collapse.ts +0 -174
- package/src/sequence/parser.ts +0 -1596
- package/src/sequence/participant-inference.ts +0 -152
- package/src/sequence/renderer.ts +0 -3168
- package/src/sequence/tag-resolution.ts +0 -164
- package/src/sharing.ts +0 -211
- package/src/sitemap/collapse.ts +0 -100
- package/src/sitemap/layout.ts +0 -925
- package/src/sitemap/parser.ts +0 -755
- package/src/sitemap/renderer.ts +0 -828
- package/src/sitemap/types.ts +0 -42
- package/src/slope/parser.ts +0 -18
- package/src/slope/renderer.ts +0 -378
- package/src/swimlane/diagnostics.ts +0 -94
- package/src/swimlane/layout.ts +0 -597
- package/src/swimlane/parser.ts +0 -613
- package/src/swimlane/renderer.ts +0 -681
- package/src/swimlane/types.ts +0 -151
- package/src/tech-radar/interactive.ts +0 -1129
- package/src/tech-radar/layout.ts +0 -197
- package/src/tech-radar/parser.ts +0 -436
- package/src/tech-radar/renderer.ts +0 -1239
- package/src/tech-radar/shared.ts +0 -151
- package/src/tech-radar/types.ts +0 -86
- package/src/themes.ts +0 -22
- package/src/timeline/parser.ts +0 -472
- package/src/timeline/renderer.ts +0 -3024
- package/src/timeline/types.ts +0 -33
- package/src/timeline/viz-parser.ts +0 -21
- package/src/treemap/diagnostics.ts +0 -62
- package/src/treemap/layout-radial.ts +0 -233
- package/src/treemap/layout.ts +0 -189
- package/src/treemap/parser.ts +0 -463
- package/src/treemap/renderer-radial.ts +0 -438
- package/src/treemap/renderer.ts +0 -572
- package/src/treemap/treemap-shared.ts +0 -309
- package/src/treemap/types.ts +0 -60
- package/src/utils/arrow-markers.ts +0 -51
- package/src/utils/arrows-diagnostics.ts +0 -62
- package/src/utils/arrows.ts +0 -227
- package/src/utils/brand.ts +0 -61
- package/src/utils/card.ts +0 -183
- package/src/utils/collapse-engine/tree-edges.ts +0 -115
- package/src/utils/collapse-engine/tree.ts +0 -117
- package/src/utils/d3-helpers.ts +0 -219
- package/src/utils/d3-types.ts +0 -9
- package/src/utils/description-helpers.ts +0 -38
- package/src/utils/duration.ts +0 -312
- package/src/utils/export-container.ts +0 -41
- package/src/utils/expr-eval.ts +0 -27
- package/src/utils/fit-canvas.ts +0 -64
- package/src/utils/hover-styles.ts +0 -673
- package/src/utils/inline-markdown.ts +0 -96
- package/src/utils/legend-constants.ts +0 -48
- package/src/utils/legend-d3.ts +0 -618
- package/src/utils/legend-integration.ts +0 -107
- package/src/utils/legend-layout.ts +0 -776
- package/src/utils/legend-svg.ts +0 -214
- package/src/utils/legend-types.ts +0 -272
- package/src/utils/name-normalize.ts +0 -163
- package/src/utils/note-box/constants.ts +0 -25
- package/src/utils/note-box/index.ts +0 -11
- package/src/utils/note-box/metrics.ts +0 -90
- package/src/utils/note-box/svg.ts +0 -331
- package/src/utils/notes/bounds.ts +0 -30
- package/src/utils/notes/build.ts +0 -131
- package/src/utils/notes/index.ts +0 -18
- package/src/utils/notes/model.ts +0 -19
- package/src/utils/notes/parse.ts +0 -131
- package/src/utils/notes/place.ts +0 -177
- package/src/utils/notes/resolve.ts +0 -88
- package/src/utils/number-format.ts +0 -36
- package/src/utils/parsing.ts +0 -953
- package/src/utils/reserved-key-registry.ts +0 -239
- package/src/utils/safe-href.ts +0 -38
- package/src/utils/scaling.ts +0 -97
- package/src/utils/svg-embed.ts +0 -251
- package/src/utils/tag-groups.ts +0 -607
- package/src/utils/text-measure.ts +0 -122
- package/src/utils/time-ticks.ts +0 -253
- package/src/utils/title-constants.ts +0 -24
- package/src/utils/visual-conventions.ts +0 -61
- package/src/utils/wrapped-desc.ts +0 -87
- package/src/venn/parser.ts +0 -18
- package/src/venn/renderer.ts +0 -1047
- package/src/version-control/diagnostics.ts +0 -67
- package/src/version-control/parser.ts +0 -544
- package/src/version-control/renderer.ts +0 -724
- package/src/version-control/types.ts +0 -100
- package/src/visualizations/parse.ts +0 -1560
- package/src/visualizations/types.ts +0 -262
- package/src/wireframe/layout.ts +0 -479
- package/src/wireframe/parser.ts +0 -1114
- package/src/wireframe/renderer.ts +0 -1318
- package/src/wireframe/types.ts +0 -110
- package/src/wordcloud/parser.ts +0 -18
- package/src/wordcloud/renderer.ts +0 -357
package/src/swimlane/parser.ts
DELETED
|
@@ -1,613 +0,0 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// Swimlane Diagram — Parser
|
|
3
|
-
// ============================================================
|
|
4
|
-
//
|
|
5
|
-
// Grammar (syntax source of truth: `docs/swimlane-syntax.html`):
|
|
6
|
-
// swimlane [Title]
|
|
7
|
-
// direction LR|TB // optional, default LR
|
|
8
|
-
// lane <Name> [color] // row declarations (order + color)
|
|
9
|
-
// tag <Name> as <a> … // optional tag groups (§1.5)
|
|
10
|
-
// [Phase] // optional phase columns (3-deep)
|
|
11
|
-
// <Lane ref> // bare line matching a declared lane
|
|
12
|
-
// <node> // bare task / <gateway> / (terminal) / [[subprocess]]
|
|
13
|
-
// <Lane ref> // 2-deep (no phases): lane ▸ node
|
|
14
|
-
// <node>
|
|
15
|
-
// A -label-> B -> C // flow block (last); in-arrow labels;
|
|
16
|
-
// <Source> // a bare source header groups indented
|
|
17
|
-
// -label-> Target // arrows beneath it
|
|
18
|
-
//
|
|
19
|
-
// Detection is via `ALL_CHART_TYPES` (utils/parsing.ts), matched by
|
|
20
|
-
// `parseFirstLine` — NOT the registry. Lanes are pre-declared with the `lane`
|
|
21
|
-
// keyword, which lets the main pass disambiguate a lane-context line (matches a
|
|
22
|
-
// declared lane) from a node declaration (anything else) from a flow
|
|
23
|
-
// source-group header (bare, indent-0, not a lane).
|
|
24
|
-
|
|
25
|
-
import { emit, type DgmoError } from '../diagnostics';
|
|
26
|
-
import { SWIMLANE_DX } from './diagnostics';
|
|
27
|
-
import { measureIndent, extractColor, parseFirstLine } from '../utils/parsing';
|
|
28
|
-
import {
|
|
29
|
-
matchTagBlockHeading,
|
|
30
|
-
stripDefaultModifier,
|
|
31
|
-
finalizeAutoTagColors,
|
|
32
|
-
AUTO_TAG_COLOR_SENTINEL,
|
|
33
|
-
tagAttrKey,
|
|
34
|
-
type TagGroup,
|
|
35
|
-
} from '../utils/tag-groups';
|
|
36
|
-
import type { Writable } from '../utils/brand';
|
|
37
|
-
import type { PaletteColors } from '../palettes';
|
|
38
|
-
import type {
|
|
39
|
-
ParsedSwimlane,
|
|
40
|
-
SwimDirection,
|
|
41
|
-
SwimEdge,
|
|
42
|
-
SwimEvent,
|
|
43
|
-
SwimLane,
|
|
44
|
-
SwimNode,
|
|
45
|
-
SwimPhase,
|
|
46
|
-
SwimShape,
|
|
47
|
-
} from './types';
|
|
48
|
-
|
|
49
|
-
/** Collapse internal whitespace + lowercase for a lookup key. */
|
|
50
|
-
function normKey(label: string): string {
|
|
51
|
-
return label.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface NodeTokenResult {
|
|
55
|
-
shape: SwimShape;
|
|
56
|
-
event: SwimEvent;
|
|
57
|
-
label: string;
|
|
58
|
-
/** Set when the token is a deferred/fast-follow construct (UNSUPPORTED). */
|
|
59
|
-
unsupported?: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Parse a node token (shape + event + label) from a structural / flow
|
|
64
|
-
* reference. Delimiters are optional sugar — `Validate` ≡ `<Validate>`.
|
|
65
|
-
*/
|
|
66
|
-
function parseNodeToken(raw: string): NodeTokenResult {
|
|
67
|
-
const t = raw.trim();
|
|
68
|
-
|
|
69
|
-
// Gateways: <…>, <+ …>, and the fast-follow <o …> / <* …>.
|
|
70
|
-
if (t.startsWith('<') && t.endsWith('>') && t.length >= 2) {
|
|
71
|
-
const inner = t.slice(1, -1).trim();
|
|
72
|
-
if (inner.startsWith('+')) {
|
|
73
|
-
return {
|
|
74
|
-
shape: 'parallel',
|
|
75
|
-
event: 'none',
|
|
76
|
-
label: inner.slice(1).trim(),
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
if (inner.startsWith('o ') || inner === 'o') {
|
|
80
|
-
return {
|
|
81
|
-
shape: 'exclusive',
|
|
82
|
-
event: 'none',
|
|
83
|
-
label: inner.replace(/^o\s*/, '').trim(),
|
|
84
|
-
unsupported: 'inclusive gateway (<o …>) is fast-follow',
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
if (inner.startsWith('* ') || inner === '*') {
|
|
88
|
-
return {
|
|
89
|
-
shape: 'exclusive',
|
|
90
|
-
event: 'none',
|
|
91
|
-
label: inner.replace(/^\*\s*/, '').trim(),
|
|
92
|
-
unsupported: 'event-based gateway (<* …>) is fast-follow',
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
return { shape: 'exclusive', event: 'none', label: inner };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Subprocess: [[ … ]] (check before single-bracket / phase forms).
|
|
99
|
-
if (t.startsWith('[[') && t.endsWith(']]') && t.length >= 4) {
|
|
100
|
-
return { shape: 'subprocess', event: 'none', label: t.slice(2, -2).trim() };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Terminal: ( … ) with optional `!` error prefix + optional trailing type
|
|
104
|
-
// word (`(Paid) success`). The trailing word lives OUTSIDE the parens.
|
|
105
|
-
if (t.startsWith('(')) {
|
|
106
|
-
const close = t.indexOf(')');
|
|
107
|
-
if (close > 0) {
|
|
108
|
-
let inner = t.slice(1, close).trim();
|
|
109
|
-
let event: SwimEvent = 'none';
|
|
110
|
-
let unsupported: string | undefined;
|
|
111
|
-
if (inner.startsWith('!')) {
|
|
112
|
-
event = 'error';
|
|
113
|
-
inner = inner.slice(1).trim();
|
|
114
|
-
}
|
|
115
|
-
const trailing = t
|
|
116
|
-
.slice(close + 1)
|
|
117
|
-
.trim()
|
|
118
|
-
.toLowerCase();
|
|
119
|
-
if (trailing) {
|
|
120
|
-
if (trailing === 'success') event = 'success';
|
|
121
|
-
else if (trailing === 'terminate') event = 'terminate';
|
|
122
|
-
else if (trailing === 'error') event = 'error';
|
|
123
|
-
else if (trailing === 'timer')
|
|
124
|
-
unsupported = 'timer events are fast-follow';
|
|
125
|
-
else if (trailing === 'message')
|
|
126
|
-
unsupported = 'message events are fast-follow';
|
|
127
|
-
else if (trailing === 'signal')
|
|
128
|
-
unsupported = 'signal events are fast-follow';
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
shape: 'terminal',
|
|
132
|
-
event,
|
|
133
|
-
label: inner,
|
|
134
|
-
...(unsupported !== undefined && { unsupported }),
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Bare task (delimiters were optional in a flow reference, so a bare
|
|
140
|
-
// `Validate` still resolves by name to its declared shape downstream).
|
|
141
|
-
return { shape: 'task', event: 'none', label: t };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Arrow token: a plain `->`, or a labeled `-label->` whose leading `-` sits at a
|
|
146
|
-
* token boundary (start-of-line or after whitespace). The boundary requirement
|
|
147
|
-
* keeps a hyphen *inside* a node name (`Read-Write -> Done`) from being mistaken
|
|
148
|
-
* for the start of a label — only a boundary-flanked `-…->` splits as a labeled
|
|
149
|
-
* arrow, so a bare ` -> ` (no label chars before the `->`) falls through to the
|
|
150
|
-
* plain alternative. The label itself may contain spaces (`-no ack->`); it just
|
|
151
|
-
* can't contain `>`. The labeled alternative is tried first so `-changes->` wins
|
|
152
|
-
* over the bare `->` nested inside it.
|
|
153
|
-
*/
|
|
154
|
-
const ARROW_RE = /(?:^|\s)-([^>]+?)->|->/g;
|
|
155
|
-
|
|
156
|
-
interface ChainPart {
|
|
157
|
-
text: string;
|
|
158
|
-
/** Label of the arrow that FOLLOWS this part (undefined for the last). */
|
|
159
|
-
labelAfter?: string;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/** Split a flow line into node references + in-arrow labels. */
|
|
163
|
-
function splitChain(line: string): ChainPart[] {
|
|
164
|
-
const parts: ChainPart[] = [];
|
|
165
|
-
ARROW_RE.lastIndex = 0;
|
|
166
|
-
let lastIndex = 0;
|
|
167
|
-
let m: RegExpExecArray | null;
|
|
168
|
-
const rawLabels: (string | undefined)[] = [];
|
|
169
|
-
const segments: string[] = [];
|
|
170
|
-
while ((m = ARROW_RE.exec(line)) !== null) {
|
|
171
|
-
segments.push(line.slice(lastIndex, m.index));
|
|
172
|
-
let lbl = m[1]?.trim();
|
|
173
|
-
if (lbl === '') lbl = undefined; // `-->` (empty in-arrow label) → no label
|
|
174
|
-
rawLabels.push(lbl);
|
|
175
|
-
lastIndex = m.index + m[0].length;
|
|
176
|
-
}
|
|
177
|
-
segments.push(line.slice(lastIndex));
|
|
178
|
-
for (let i = 0; i < segments.length; i++) {
|
|
179
|
-
parts.push({
|
|
180
|
-
text: segments[i]!.trim(),
|
|
181
|
-
...(rawLabels[i] !== undefined && { labelAfter: rawLabels[i]! }),
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
return parts;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/** Split a node declaration line into its token region + `key: value` meta. */
|
|
188
|
-
function splitNodeMeta(line: string): {
|
|
189
|
-
token: string;
|
|
190
|
-
meta: Record<string, string>;
|
|
191
|
-
} {
|
|
192
|
-
// First `<ident>:` occurrence (with optional space) marks the meta region.
|
|
193
|
-
const m = line.match(/(^|\s)([A-Za-z][\w-]*)\s*:/);
|
|
194
|
-
if (m?.index === undefined) {
|
|
195
|
-
return { token: line.trim(), meta: {} };
|
|
196
|
-
}
|
|
197
|
-
const cut = m.index + (m[1] ? m[1].length : 0);
|
|
198
|
-
const token = line.slice(0, cut).trim();
|
|
199
|
-
const region = line.slice(cut);
|
|
200
|
-
const meta: Record<string, string> = {};
|
|
201
|
-
for (const pair of region.split(',')) {
|
|
202
|
-
const idx = pair.indexOf(':');
|
|
203
|
-
if (idx <= 0) continue;
|
|
204
|
-
const key = pair.slice(0, idx).trim().toLowerCase();
|
|
205
|
-
const value = pair.slice(idx + 1).trim();
|
|
206
|
-
if (!key) continue;
|
|
207
|
-
meta[key] = value;
|
|
208
|
-
}
|
|
209
|
-
return { token, meta };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export function parseSwimlane(
|
|
213
|
-
content: string,
|
|
214
|
-
palette?: PaletteColors
|
|
215
|
-
): ParsedSwimlane {
|
|
216
|
-
const diagnostics: DgmoError[] = [];
|
|
217
|
-
const lanes: SwimLane[] = [];
|
|
218
|
-
const phases: SwimPhase[] = [];
|
|
219
|
-
const nodes: SwimNode[] = [];
|
|
220
|
-
const edges: SwimEdge[] = [];
|
|
221
|
-
const tagGroups: Writable<TagGroup>[] = [];
|
|
222
|
-
|
|
223
|
-
const laneByKey = new Map<string, SwimLane>();
|
|
224
|
-
const phaseByKey = new Map<string, SwimPhase>();
|
|
225
|
-
const nodeByKey = new Map<string, SwimNode>();
|
|
226
|
-
const metaAliasMap = new Map<string, string>();
|
|
227
|
-
const options: Record<string, string> = {};
|
|
228
|
-
let direction: SwimDirection = 'LR';
|
|
229
|
-
|
|
230
|
-
const rawLines = content.split('\n');
|
|
231
|
-
|
|
232
|
-
// ── First line: chart type + title ──────────────────────────
|
|
233
|
-
let title: string | undefined;
|
|
234
|
-
let titleLineNumber: number | undefined;
|
|
235
|
-
let startIdx = 0;
|
|
236
|
-
for (let i = 0; i < rawLines.length; i++) {
|
|
237
|
-
const t = rawLines[i]!.trim();
|
|
238
|
-
if (!t || t.startsWith('//')) continue;
|
|
239
|
-
const fl = parseFirstLine(rawLines[i]!);
|
|
240
|
-
if (fl?.chartType === 'swimlane') {
|
|
241
|
-
title = fl.title;
|
|
242
|
-
titleLineNumber = i + 1;
|
|
243
|
-
}
|
|
244
|
-
startIdx = i + 1;
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// ── Pass 1: lanes / tag groups / options (top-level declarations) ──
|
|
249
|
-
// Done first so the main pass can disambiguate lane-context lines.
|
|
250
|
-
let currentTagGroup: Writable<TagGroup> | null = null;
|
|
251
|
-
let sawContent = false;
|
|
252
|
-
for (let i = startIdx; i < rawLines.length; i++) {
|
|
253
|
-
const raw = rawLines[i]!;
|
|
254
|
-
const lineNum = i + 1;
|
|
255
|
-
const trimmed = raw.trim();
|
|
256
|
-
if (!trimmed || trimmed.startsWith('//')) continue;
|
|
257
|
-
const indent = measureIndent(raw);
|
|
258
|
-
|
|
259
|
-
// Tag group heading.
|
|
260
|
-
const tagMatch = matchTagBlockHeading(trimmed);
|
|
261
|
-
if (tagMatch && indent === 0) {
|
|
262
|
-
const newGroup: Writable<TagGroup> = {
|
|
263
|
-
name: tagMatch.name,
|
|
264
|
-
...(tagMatch.alias !== undefined && { alias: tagMatch.alias }),
|
|
265
|
-
entries: [],
|
|
266
|
-
lineNumber: lineNum,
|
|
267
|
-
};
|
|
268
|
-
currentTagGroup = newGroup;
|
|
269
|
-
if (tagMatch.alias) {
|
|
270
|
-
metaAliasMap.set(normKey(tagMatch.alias), tagAttrKey(tagMatch.name));
|
|
271
|
-
}
|
|
272
|
-
metaAliasMap.set(normKey(tagMatch.name), tagAttrKey(tagMatch.name));
|
|
273
|
-
if (tagMatch.inlineValues) {
|
|
274
|
-
for (const rawVal of tagMatch.inlineValues) {
|
|
275
|
-
const { text, isDefault } = stripDefaultModifier(rawVal);
|
|
276
|
-
const { label, color } = extractColor(
|
|
277
|
-
text,
|
|
278
|
-
palette,
|
|
279
|
-
diagnostics,
|
|
280
|
-
lineNum
|
|
281
|
-
);
|
|
282
|
-
newGroup.entries.push({
|
|
283
|
-
value: label,
|
|
284
|
-
color: color ?? AUTO_TAG_COLOR_SENTINEL,
|
|
285
|
-
lineNumber: lineNum,
|
|
286
|
-
});
|
|
287
|
-
if (isDefault) newGroup.defaultValue = label;
|
|
288
|
-
}
|
|
289
|
-
if (!newGroup.defaultValue && newGroup.entries.length > 0) {
|
|
290
|
-
newGroup.defaultValue = newGroup.entries[0]!.value;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
tagGroups.push(newGroup);
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
// Tag entries (indented under a heading, before any content).
|
|
297
|
-
if (currentTagGroup && indent > 0 && !sawContent) {
|
|
298
|
-
const { text, isDefault } = stripDefaultModifier(trimmed);
|
|
299
|
-
const { label, color } = extractColor(
|
|
300
|
-
text,
|
|
301
|
-
palette,
|
|
302
|
-
diagnostics,
|
|
303
|
-
lineNum
|
|
304
|
-
);
|
|
305
|
-
currentTagGroup.entries.push({
|
|
306
|
-
value: label,
|
|
307
|
-
color: color ?? AUTO_TAG_COLOR_SENTINEL,
|
|
308
|
-
lineNumber: lineNum,
|
|
309
|
-
});
|
|
310
|
-
if (isDefault || currentTagGroup.entries.length === 1) {
|
|
311
|
-
currentTagGroup.defaultValue = label;
|
|
312
|
-
}
|
|
313
|
-
continue;
|
|
314
|
-
}
|
|
315
|
-
if (indent === 0) currentTagGroup = null;
|
|
316
|
-
|
|
317
|
-
// `solid-fill` bare directive (full-saturation node fills).
|
|
318
|
-
if (indent === 0 && /^solid-fill\s*$/i.test(trimmed)) {
|
|
319
|
-
options['solid-fill'] = 'on';
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// `direction LR|TB`.
|
|
324
|
-
if (indent === 0 && /^direction\s+/i.test(trimmed)) {
|
|
325
|
-
const val = trimmed
|
|
326
|
-
.replace(/^direction\s+/i, '')
|
|
327
|
-
.trim()
|
|
328
|
-
.toUpperCase();
|
|
329
|
-
if (val === 'LR' || val === 'TB') {
|
|
330
|
-
direction = val;
|
|
331
|
-
options['direction'] = val;
|
|
332
|
-
}
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// `lane <Name> [color]`.
|
|
337
|
-
if (indent === 0 && /^lane\s+/i.test(trimmed)) {
|
|
338
|
-
const rest = trimmed.replace(/^lane\s+/i, '').trim();
|
|
339
|
-
const { label, color } = extractColor(
|
|
340
|
-
rest,
|
|
341
|
-
palette,
|
|
342
|
-
diagnostics,
|
|
343
|
-
lineNum
|
|
344
|
-
);
|
|
345
|
-
const key = normKey(label);
|
|
346
|
-
if (laneByKey.has(key)) continue; // ignore re-declaration
|
|
347
|
-
const lane: SwimLane = {
|
|
348
|
-
id: label,
|
|
349
|
-
label,
|
|
350
|
-
...(color !== undefined && { color }),
|
|
351
|
-
lineNumber: lineNum,
|
|
352
|
-
};
|
|
353
|
-
lanes.push(lane);
|
|
354
|
-
laneByKey.set(key, lane);
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Anything else at indent 0 that is not a phase/flow marks content start
|
|
359
|
-
// (so trailing tag entries don't leak). Phases and content handled in pass 2.
|
|
360
|
-
if (indent === 0 && !trimmed.startsWith('tag ')) sawContent = true;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Lane colors: resolve raw names — keep raw name in the model; the renderer
|
|
364
|
-
// resolves to hex. (extractColor already stored a hex; but we stored the raw
|
|
365
|
-
// word above via extractColor's label/color — color IS a resolved hex string.)
|
|
366
|
-
|
|
367
|
-
// ── Pass 2: phases, lane context, nodes, flow ───────────────
|
|
368
|
-
let currentPhase: SwimPhase | null = null;
|
|
369
|
-
let currentLane: SwimLane | null = null;
|
|
370
|
-
let laneIndent = -1; // indent at which the current lane context was opened
|
|
371
|
-
let currentFlowSource: string | null = null;
|
|
372
|
-
|
|
373
|
-
const declareNode = (
|
|
374
|
-
token: string,
|
|
375
|
-
meta: Record<string, string>,
|
|
376
|
-
lineNum: number
|
|
377
|
-
): void => {
|
|
378
|
-
const parsed = parseNodeToken(token);
|
|
379
|
-
if (parsed.unsupported) {
|
|
380
|
-
diagnostics.push(
|
|
381
|
-
emit(SWIMLANE_DX.UNSUPPORTED, lineNum, { reason: parsed.unsupported })
|
|
382
|
-
);
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
if (!parsed.label) return;
|
|
386
|
-
if (!currentLane) {
|
|
387
|
-
diagnostics.push(
|
|
388
|
-
emit(SWIMLANE_DX.UNKNOWN_LANE, lineNum, { node: parsed.label })
|
|
389
|
-
);
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
// Strip the same-line trailing color token BEFORE computing the identity
|
|
393
|
-
// key — the flow block references the node by its clean name (`resolveRef`
|
|
394
|
-
// keys off the post-color label), so dedup + lookup must use `cleanLabel`
|
|
395
|
-
// too, else a colored node is unreferenceable and duplicates slip the gate.
|
|
396
|
-
const { label: cleanLabel, color } = extractColor(
|
|
397
|
-
parsed.label,
|
|
398
|
-
palette,
|
|
399
|
-
diagnostics,
|
|
400
|
-
lineNum
|
|
401
|
-
);
|
|
402
|
-
const key = normKey(cleanLabel);
|
|
403
|
-
if (nodeByKey.has(key)) {
|
|
404
|
-
diagnostics.push(
|
|
405
|
-
emit(SWIMLANE_DX.DUPLICATE_NODE, lineNum, { name: cleanLabel })
|
|
406
|
-
);
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
// Tag-meta: reject the deferred reserved keys.
|
|
410
|
-
const tags: Record<string, string> = {};
|
|
411
|
-
for (const [k, v] of Object.entries(meta)) {
|
|
412
|
-
if (k === 'note') {
|
|
413
|
-
diagnostics.push(
|
|
414
|
-
emit(SWIMLANE_DX.UNSUPPORTED, lineNum, {
|
|
415
|
-
reason: 'notes are deferred past v1',
|
|
416
|
-
})
|
|
417
|
-
);
|
|
418
|
-
continue;
|
|
419
|
-
}
|
|
420
|
-
if (k === 'data') {
|
|
421
|
-
diagnostics.push(
|
|
422
|
-
emit(SWIMLANE_DX.UNSUPPORTED, lineNum, {
|
|
423
|
-
reason: 'data objects are deferred past v1',
|
|
424
|
-
})
|
|
425
|
-
);
|
|
426
|
-
continue;
|
|
427
|
-
}
|
|
428
|
-
if (k === 'timer' || k === 'message' || k === 'signal') {
|
|
429
|
-
diagnostics.push(
|
|
430
|
-
emit(SWIMLANE_DX.UNSUPPORTED, lineNum, {
|
|
431
|
-
reason: `${k} events are fast-follow`,
|
|
432
|
-
})
|
|
433
|
-
);
|
|
434
|
-
continue;
|
|
435
|
-
}
|
|
436
|
-
const canonical = metaAliasMap.get(k) ?? k;
|
|
437
|
-
tags[canonical] = v;
|
|
438
|
-
}
|
|
439
|
-
const node: SwimNode = {
|
|
440
|
-
id: cleanLabel,
|
|
441
|
-
label: cleanLabel,
|
|
442
|
-
shape: parsed.shape,
|
|
443
|
-
event: parsed.event,
|
|
444
|
-
lane: currentLane.id,
|
|
445
|
-
...(currentPhase && { phase: currentPhase.id }),
|
|
446
|
-
...(color !== undefined && { color }),
|
|
447
|
-
tags,
|
|
448
|
-
lineNumber: lineNum,
|
|
449
|
-
};
|
|
450
|
-
nodes.push(node);
|
|
451
|
-
nodeByKey.set(key, node);
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
const resolveRef = (token: string, lineNum: number): string | null => {
|
|
455
|
-
const parsed = parseNodeToken(token);
|
|
456
|
-
if (parsed.unsupported) {
|
|
457
|
-
diagnostics.push(
|
|
458
|
-
emit(SWIMLANE_DX.UNSUPPORTED, lineNum, { reason: parsed.unsupported })
|
|
459
|
-
);
|
|
460
|
-
return null;
|
|
461
|
-
}
|
|
462
|
-
const { label } = extractColor(parsed.label, palette);
|
|
463
|
-
const key = normKey(label);
|
|
464
|
-
const node = nodeByKey.get(key);
|
|
465
|
-
if (!node) {
|
|
466
|
-
diagnostics.push(
|
|
467
|
-
emit(SWIMLANE_DX.UNKNOWN_NODE, lineNum, { node: parsed.label })
|
|
468
|
-
);
|
|
469
|
-
return null;
|
|
470
|
-
}
|
|
471
|
-
return node.id;
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
const addChain = (line: string, lineNum: number): void => {
|
|
475
|
-
if (line.includes('~>')) {
|
|
476
|
-
diagnostics.push(
|
|
477
|
-
emit(SWIMLANE_DX.UNSUPPORTED, lineNum, {
|
|
478
|
-
reason: 'message flow (~>) is fast-follow',
|
|
479
|
-
})
|
|
480
|
-
);
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
const parts = splitChain(line);
|
|
484
|
-
if (parts.length < 2) return;
|
|
485
|
-
// A leading empty part means the chain starts with `-label->`, so its source
|
|
486
|
-
// is the group header (`currentFlowSource`). Every indented arrow under one
|
|
487
|
-
// header fans out from the SAME header — we never chain off a prior line's
|
|
488
|
-
// tail — so the group source is read but not reassigned here.
|
|
489
|
-
const resolved: (string | null)[] = [];
|
|
490
|
-
for (let p = 0; p < parts.length; p++) {
|
|
491
|
-
const text = parts[p]!.text;
|
|
492
|
-
if (p === 0 && text === '') {
|
|
493
|
-
resolved.push(currentFlowSource);
|
|
494
|
-
continue;
|
|
495
|
-
}
|
|
496
|
-
resolved.push(resolveRef(text, lineNum));
|
|
497
|
-
}
|
|
498
|
-
for (let p = 0; p < parts.length - 1; p++) {
|
|
499
|
-
const src = resolved[p];
|
|
500
|
-
const tgt = resolved[p + 1];
|
|
501
|
-
const label = parts[p]!.labelAfter;
|
|
502
|
-
if (!src || !tgt) continue;
|
|
503
|
-
edges.push({
|
|
504
|
-
source: src,
|
|
505
|
-
target: tgt,
|
|
506
|
-
...(label !== undefined && { label }),
|
|
507
|
-
lineNumber: lineNum,
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
let inTagBlock = false; // skip a `tag` heading's indented entries (pass-1 owns them)
|
|
513
|
-
for (let i = startIdx; i < rawLines.length; i++) {
|
|
514
|
-
const raw = rawLines[i]!;
|
|
515
|
-
const lineNum = i + 1;
|
|
516
|
-
let trimmed = raw.trim();
|
|
517
|
-
if (!trimmed || trimmed.startsWith('//')) continue;
|
|
518
|
-
// Strip trailing inline comments.
|
|
519
|
-
const cIdx = trimmed.indexOf('//');
|
|
520
|
-
if (cIdx > 0) trimmed = trimmed.slice(0, cIdx).trim();
|
|
521
|
-
if (!trimmed) continue;
|
|
522
|
-
const indent = measureIndent(raw);
|
|
523
|
-
|
|
524
|
-
// Skip the declarations pass-1 already consumed.
|
|
525
|
-
if (indent === 0 && /^(lane|direction|tag)\s+/i.test(trimmed)) {
|
|
526
|
-
inTagBlock = /^tag\s+/i.test(trimmed);
|
|
527
|
-
continue;
|
|
528
|
-
}
|
|
529
|
-
// Bare diagram-level directives consumed in pass 1.
|
|
530
|
-
if (indent === 0 && /^solid-fill\s*$/i.test(trimmed)) continue;
|
|
531
|
-
// Indented tag entries (already consumed in pass 1).
|
|
532
|
-
if (matchTagBlockHeading(trimmed) && indent === 0) {
|
|
533
|
-
inTagBlock = true;
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
if (indent === 0) inTagBlock = false;
|
|
537
|
-
if (inTagBlock && indent > 0) continue;
|
|
538
|
-
|
|
539
|
-
// `[Phase]` header (bracketed, not `[[subprocess]]`).
|
|
540
|
-
if (
|
|
541
|
-
indent === 0 &&
|
|
542
|
-
trimmed.startsWith('[') &&
|
|
543
|
-
trimmed.endsWith(']') &&
|
|
544
|
-
!trimmed.startsWith('[[')
|
|
545
|
-
) {
|
|
546
|
-
const label = trimmed.slice(1, -1).trim();
|
|
547
|
-
if (lanes.length === 0) {
|
|
548
|
-
diagnostics.push(emit(SWIMLANE_DX.NO_LANES, lineNum, { phase: label }));
|
|
549
|
-
}
|
|
550
|
-
const key = normKey(label);
|
|
551
|
-
let phase = phaseByKey.get(key);
|
|
552
|
-
if (!phase) {
|
|
553
|
-
phase = { id: label, label, lineNumber: lineNum };
|
|
554
|
-
phases.push(phase);
|
|
555
|
-
phaseByKey.set(key, phase);
|
|
556
|
-
}
|
|
557
|
-
currentPhase = phase;
|
|
558
|
-
currentLane = null;
|
|
559
|
-
laneIndent = -1;
|
|
560
|
-
currentFlowSource = null;
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Flow edge (contains an arrow). Node names can't contain `->`, so a bare
|
|
565
|
-
// `->`/`~>` presence unambiguously marks a flow line.
|
|
566
|
-
if (trimmed.includes('->') || trimmed.includes('~>')) {
|
|
567
|
-
addChain(trimmed, lineNum);
|
|
568
|
-
continue;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// A bare line matching a declared lane opens that lane's context — but ONLY
|
|
572
|
-
// at the lane indent level (or shallower). A line DEEPER than the current
|
|
573
|
-
// lane is a node even when it shares a lane's name (lanes and nodes are
|
|
574
|
-
// separate namespaces; the indent disambiguates 3-deep phase ▸ lane ▸ node).
|
|
575
|
-
const laneRef = laneByKey.get(normKey(trimmed));
|
|
576
|
-
if (laneRef && (laneIndent < 0 || indent <= laneIndent)) {
|
|
577
|
-
currentLane = laneRef;
|
|
578
|
-
laneIndent = indent;
|
|
579
|
-
currentFlowSource = null;
|
|
580
|
-
continue;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
// A bare indent-0 line that is NOT a lane → flow source-group header.
|
|
584
|
-
if (indent === 0) {
|
|
585
|
-
const ref = resolveRef(trimmed, lineNum);
|
|
586
|
-
if (ref) currentFlowSource = ref;
|
|
587
|
-
continue;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Indented bare line → node declaration under the current lane.
|
|
591
|
-
const { token, meta } = splitNodeMeta(trimmed);
|
|
592
|
-
declareNode(token, meta, lineNum);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
finalizeAutoTagColors(tagGroups, palette);
|
|
596
|
-
|
|
597
|
-
// Per-element diagnostics (duplicate/unknown/unsupported) are non-fatal — the
|
|
598
|
-
// rest of the diagram still renders best-effort. `error` stays null so the
|
|
599
|
-
// export guard only bails on a genuinely empty diagram (nodes.length === 0).
|
|
600
|
-
return {
|
|
601
|
-
...(title !== undefined && { title }),
|
|
602
|
-
...(titleLineNumber !== undefined && { titleLineNumber }),
|
|
603
|
-
direction,
|
|
604
|
-
lanes,
|
|
605
|
-
phases,
|
|
606
|
-
nodes,
|
|
607
|
-
edges,
|
|
608
|
-
tagGroups,
|
|
609
|
-
options,
|
|
610
|
-
diagnostics,
|
|
611
|
-
error: null,
|
|
612
|
-
};
|
|
613
|
-
}
|