@diagrammo/dgmo 0.8.2 → 0.8.4

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 (120) hide show
  1. package/.claude/commands/dgmo-diagram-this.md +60 -0
  2. package/.claude/commands/dgmo-document-project.md +128 -0
  3. package/.claude/commands/dgmo.md +185 -50
  4. package/.cursorrules +32 -37
  5. package/.github/copilot-instructions.md +35 -44
  6. package/.windsurfrules +32 -37
  7. package/README.md +4 -4
  8. package/dist/cli.cjs +189 -194
  9. package/dist/editor.cjs +336 -0
  10. package/dist/editor.cjs.map +1 -0
  11. package/dist/editor.d.cts +27 -0
  12. package/dist/editor.d.ts +27 -0
  13. package/dist/editor.js +305 -0
  14. package/dist/editor.js.map +1 -0
  15. package/dist/index.cjs +3699 -1564
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +7 -6
  18. package/dist/index.d.ts +7 -6
  19. package/dist/index.js +3699 -1564
  20. package/dist/index.js.map +1 -1
  21. package/docs/language-reference.md +822 -1060
  22. package/gallery/fixtures/arc.dgmo +18 -0
  23. package/gallery/fixtures/area.dgmo +19 -0
  24. package/gallery/fixtures/bar-stacked.dgmo +10 -0
  25. package/gallery/fixtures/bar.dgmo +10 -0
  26. package/gallery/fixtures/c4-full.dgmo +52 -0
  27. package/gallery/fixtures/c4.dgmo +17 -0
  28. package/gallery/fixtures/chord.dgmo +12 -0
  29. package/gallery/fixtures/class-basic.dgmo +14 -0
  30. package/gallery/fixtures/class-full.dgmo +43 -0
  31. package/gallery/fixtures/doughnut.dgmo +8 -0
  32. package/gallery/fixtures/flowchart-basic.dgmo +3 -0
  33. package/gallery/fixtures/flowchart-colors.dgmo +5 -0
  34. package/gallery/fixtures/flowchart-complex.dgmo +17 -0
  35. package/gallery/fixtures/flowchart-decision.dgmo +5 -0
  36. package/gallery/fixtures/flowchart-full.dgmo +13 -0
  37. package/gallery/fixtures/flowchart-groups.dgmo +10 -0
  38. package/gallery/fixtures/flowchart-loop.dgmo +7 -0
  39. package/gallery/fixtures/flowchart-nested.dgmo +7 -0
  40. package/gallery/fixtures/flowchart-shapes.dgmo +5 -0
  41. package/gallery/fixtures/function.dgmo +8 -0
  42. package/gallery/fixtures/funnel.dgmo +7 -0
  43. package/gallery/fixtures/gantt-full.dgmo +49 -0
  44. package/gallery/fixtures/gantt.dgmo +42 -0
  45. package/gallery/fixtures/heatmap.dgmo +8 -0
  46. package/gallery/fixtures/infra-full.dgmo +78 -0
  47. package/gallery/fixtures/infra-overload.dgmo +25 -0
  48. package/gallery/fixtures/infra.dgmo +47 -0
  49. package/gallery/fixtures/initiative-status-full.dgmo +46 -0
  50. package/gallery/fixtures/initiative-status-phases.dgmo +29 -0
  51. package/gallery/fixtures/initiative-status.dgmo +9 -0
  52. package/gallery/fixtures/line.dgmo +19 -0
  53. package/gallery/fixtures/multi-line.dgmo +11 -0
  54. package/gallery/fixtures/org-basic.dgmo +16 -0
  55. package/gallery/fixtures/org-full.dgmo +69 -0
  56. package/gallery/fixtures/org-teams.dgmo +25 -0
  57. package/gallery/fixtures/pie.dgmo +9 -0
  58. package/gallery/fixtures/polar-area.dgmo +8 -0
  59. package/gallery/fixtures/quadrant.dgmo +18 -0
  60. package/gallery/fixtures/radar.dgmo +8 -0
  61. package/gallery/fixtures/sankey.dgmo +31 -0
  62. package/gallery/fixtures/scatter.dgmo +21 -0
  63. package/gallery/fixtures/sequence-tags-protocols.dgmo +45 -0
  64. package/gallery/fixtures/sequence-tags.dgmo +41 -0
  65. package/gallery/fixtures/sequence.dgmo +35 -0
  66. package/gallery/fixtures/sitemap-basic.dgmo +12 -0
  67. package/gallery/fixtures/sitemap-full.dgmo +156 -0
  68. package/gallery/fixtures/slope.dgmo +8 -0
  69. package/gallery/fixtures/spr-eras.dgmo +62 -0
  70. package/gallery/fixtures/state.dgmo +30 -0
  71. package/gallery/fixtures/timeline-intraday.dgmo +14 -0
  72. package/gallery/fixtures/timeline.dgmo +32 -0
  73. package/gallery/fixtures/venn.dgmo +10 -0
  74. package/gallery/fixtures/wordcloud.dgmo +24 -0
  75. package/package.json +51 -2
  76. package/src/c4/layout.ts +372 -90
  77. package/src/c4/parser.ts +113 -62
  78. package/src/chart.ts +149 -64
  79. package/src/class/parser.ts +84 -28
  80. package/src/class/renderer.ts +2 -2
  81. package/src/cli.ts +179 -77
  82. package/src/completion.ts +381 -182
  83. package/src/d3.ts +1026 -428
  84. package/src/dgmo-mermaid.ts +16 -13
  85. package/src/dgmo-router.ts +70 -24
  86. package/src/echarts.ts +682 -169
  87. package/src/editor/dgmo.grammar +69 -0
  88. package/src/editor/dgmo.grammar.d.ts +2 -0
  89. package/src/editor/dgmo.grammar.js +18 -0
  90. package/src/editor/dgmo.grammar.terms.d.ts +5 -0
  91. package/src/editor/dgmo.grammar.terms.js +35 -0
  92. package/src/editor/highlight.ts +36 -0
  93. package/src/editor/index.ts +28 -0
  94. package/src/editor/keywords.ts +220 -0
  95. package/src/editor/tokens.ts +30 -0
  96. package/src/er/parser.ts +55 -29
  97. package/src/er/renderer.ts +112 -53
  98. package/src/gantt/calculator.ts +91 -29
  99. package/src/gantt/parser.ts +291 -97
  100. package/src/gantt/renderer.ts +1120 -350
  101. package/src/graph/flowchart-parser.ts +48 -75
  102. package/src/graph/state-parser.ts +54 -27
  103. package/src/infra/parser.ts +161 -177
  104. package/src/infra/renderer.ts +723 -271
  105. package/src/infra/types.ts +0 -1
  106. package/src/initiative-status/parser.ts +144 -56
  107. package/src/kanban/parser.ts +27 -19
  108. package/src/org/layout.ts +111 -44
  109. package/src/org/parser.ts +71 -27
  110. package/src/org/resolver.ts +3 -3
  111. package/src/palettes/index.ts +3 -2
  112. package/src/render.ts +1 -2
  113. package/src/sequence/parser.ts +209 -100
  114. package/src/sitemap/parser.ts +73 -44
  115. package/src/utils/arrows.ts +2 -22
  116. package/src/utils/duration.ts +39 -21
  117. package/src/utils/legend-constants.ts +0 -2
  118. package/src/utils/parsing.ts +82 -72
  119. package/src/utils/tag-groups.ts +4 -41
  120. package/src/infra/serialize.ts +0 -67
@@ -0,0 +1,69 @@
1
+ @top Document { (newline | Comment | ContentLine)* }
2
+
3
+ ContentLine { contentPart+ newline }
4
+
5
+ contentPart {
6
+ SyncArrow | AsyncArrow |
7
+ Duration | DateLiteral | Percentage | Number |
8
+ SectionMarker |
9
+ Url |
10
+ OpenBracket | CloseBracket | OpenParen | CloseParen | OpenAngle | CloseAngle |
11
+ ColorAnnotation |
12
+ Pipe | Colon | Comma | Plus | Dash | Tilde | Star | Question |
13
+ ChartType |
14
+ TagKeyword | DirectiveKeyword | ControlKeyword | ModifierKeyword |
15
+ Identifier |
16
+ Punct
17
+ }
18
+
19
+ @skip { spaces }
20
+
21
+ @tokens {
22
+ spaces { $[ \t]+ }
23
+ newline { "\n" }
24
+
25
+ Comment { "//" ![\n]* }
26
+
27
+ SyncArrow { "->" }
28
+ AsyncArrow { "~>" }
29
+
30
+ Duration { $[0-9]+ ("." $[0-9]+)? ("min" | "bd" | "h" | "d" | "w" | "m" | "q" | "y") "?"? }
31
+ DateLiteral { $[0-9] $[0-9] $[0-9] $[0-9] "-" $[0-9] $[0-9] ("-" $[0-9] $[0-9])? }
32
+ Percentage { $[0-9]+ ("." $[0-9]+)? "%" }
33
+ Number { $[0-9]+ ("." $[0-9]+)? }
34
+
35
+ SectionMarker { "==" }
36
+ Url { "http" "s"? "://" ![ \t\n|,)\]>]+ }
37
+ ColorAnnotation { "(" $[a-z\-]+ ")" }
38
+
39
+ Pipe { "|" }
40
+ Colon { ":" }
41
+ Comma { "," }
42
+ Plus { "+" }
43
+ Dash { "-" }
44
+ Tilde { "~" }
45
+ Star { "*" }
46
+ Question { "?" }
47
+ OpenBracket { "[" }
48
+ CloseBracket { "]" }
49
+ OpenParen { "(" }
50
+ CloseParen { ")" }
51
+ OpenAngle { "<" }
52
+ CloseAngle { ">" }
53
+
54
+ Identifier { $[a-zA-Z_] ($[a-zA-Z0-9_./&?=@#!'+] | "-" $[a-zA-Z_./&?=@#!'+])* }
55
+
56
+ Punct { ![$[ \t\n] }
57
+
58
+ @precedence {
59
+ Comment, SyncArrow, AsyncArrow,
60
+ Duration, DateLiteral, Percentage, Number, SectionMarker, Url, ColorAnnotation,
61
+ Pipe, Colon, Comma, Plus, Dash, Tilde, Star, Question,
62
+ OpenBracket, CloseBracket, OpenParen, CloseParen, OpenAngle, CloseAngle,
63
+ Identifier, Punct
64
+ }
65
+ }
66
+
67
+ @external specialize {Identifier} specializeKeyword from "./tokens" {
68
+ ChartType, TagKeyword, DirectiveKeyword, ControlKeyword, ModifierKeyword
69
+ }
@@ -0,0 +1,2 @@
1
+ import { LRParser } from '@lezer/lr';
2
+ export declare const parser: LRParser;
@@ -0,0 +1,18 @@
1
+ // This file was generated by lezer-generator. You probably shouldn't edit it.
2
+ import {LRParser} from "@lezer/lr"
3
+ import {specializeKeyword} from "./tokens"
4
+ export const parser = LRParser.deserialize({
5
+ version: 14,
6
+ states: "!WQVQPOOOOQO'#DU'#DUOOQO'#DP'#DPO%]QPO'#CdOOQO'#DO'#DOQVQPOOOOQO-E6}-E6}OOQO,59O,59OOOQO-E6|-E6|",
7
+ stateData: "&Q~OvOS~OPPOQPORPOSPOTPOVSOXPOYPOZPO[PO]PO^PO_PO`POaPObPOcPOdPOePOfPOgPOhPOiPOjPOkPOlPOmPOnPOoPOpPOqPOwSO~OPPOQPORPOSPOTPOXPOYPOZPO[PO]PO^PO_PO`POaPObPOcPOdPOePOfPOgPOhPOiPOjPOkPOlPOmPOnPOoPOpPOqPO~OwVO~P#]OVXYZ[]^_`ghijklmnoabcdefpqk~",
8
+ goto: "!byPPPPPPPPzPPPPPPPPPPPPPPPPPPPPPPPPP!O!UPPPP!]TSOTQTORWTSROTRURVQORT",
9
+ nodeNames: "⚠ ChartType TagKeyword DirectiveKeyword ControlKeyword ModifierKeyword Document Comment ContentLine SyncArrow AsyncArrow Duration DateLiteral Percentage Number SectionMarker Url OpenBracket CloseBracket OpenParen CloseParen OpenAngle CloseAngle ColorAnnotation Pipe Colon Comma Plus Dash Tilde Star Question Identifier Punct",
10
+ maxTerm: 40,
11
+ skippedNodes: [0],
12
+ repeatNodeCount: 2,
13
+ tokenData: "9q~RxOX#oXY#tYZ$PZp#opq#tqt#oux#oxy$Uyz$tz{${{|%S|}%Z}!O%b!O!P#o!P!Q%q!Q![&b![!],v!]!^#o!^!_,}!_!`-U!`!a-c!a!b-j!b!c#o!c!}-q!}#O0w#O#P#o#P#Q0|#Q#R#o#R#S-q#S#T#o#T#[-q#[#]1T#]#o-q#o#p#o#p#q9T#q#r#o#r#s9[#s;'S#o;'S;=`9k<%lO#o~#tOq~~#yQv~XY#tpq#t~$UOw~~$]Qc~q~}!O$c#T#o$c~$fRyz$o}!O$c#T#o$c~$tOg~~${Od~q~~%SOn~q~~%ZOk~q~~%bOj~q~~%iPl~q~!`!a%l~%qOX~~%vPq~!P!Q%y~&OSV~OY%yZ;'S%y;'S;=`&[<%lO%y~&_P;=`<%l%y~&iY^~q~uv'X!O!P'^!Q![(z#U#V(U#W#X([#[#]([#a#b(i#e#f([#k#l([#m#n([~'^O]~~'aP!Q!['d~'iX^~uv'X!Q!['d#U#V(U#W#X([#[#]([#a#b(i#e#f([#k#l([#m#n([~(XP#W#X([~(aPZ~!a!b(d~(iOZ~~(nQZ~!a!b(d#]#^(t~(wP#b#c([~)PY^~uv'X!O!P'^!Q![)o#U#V(U#W#X([#[#]([#a#b(i#e#f([#k#l([#m#n([~)tY^~uv'X!O!P'^!Q![*d#U#V(U#W#X([#[#]([#a#b(i#e#f([#k#l([#m#n([~*iZ^~uv'X}!O+[!O!P'^!Q![,R#U#V(U#W#X([#[#]([#a#b(i#e#f([#k#l([#m#n([~+_P!Q![+b~+eP!Q![+h~+mP[~}!O+p~+sP!Q![+v~+yP!Q![+|~,RO[~~,WY^~uv'X!O!P'^!Q![,R#U#V(U#W#X([#[#]([#a#b(i#e#f([#k#l([#m#n([~,}Oi~q~~-UOe~q~~-ZPq~!_!`-^~-cO_~~-jOf~q~~-qOo~q~~-x_p~q~qr.wst.wvw.wwx.w{|.w}!O/{!O!P.w!P!Q.w!Q![.w!_!`.w!a!b.w!b!c.w!c!}.w#R#S.w#T#o.w~.|_p~qr.wst.wvw.wwx.w{|.w}!O/{!O!P.w!P!Q.w!Q![.w!_!`.w!a!b.w!b!c.w!c!}.w#R#S.w#T#o.w~0O]qr.wst.wvw.wwx.w{|.w!O!P.w!P!Q.w!_!`.w!a!b.w!b!c.w!c!}.w#R#S.w#T#o.w~0|Oa~~1TOb~q~~1[ap~q~qr.wst.wvw.wwx.w{|.w}!O/{!O!P.w!P!Q.w!Q![.w!_!`.w!a!b.w!b!c.w!c!}.w#R#S.w#T#h.w#h#i2a#i#o.w~2fap~qr.wst.wvw.wwx.w{|.w}!O/{!O!P.w!P!Q.w!Q![.w!_!`.w!a!b.w!b!c.w!c!}.w#R#S.w#T#h.w#h#i3k#i#o.w~3pap~qr.wst.wvw.wwx.w{|.w}!O/{!O!P.w!P!Q.w!Q![.w!_!`.w!a!b.w!b!c.w!c!}.w#R#S.w#T#d.w#d#e4u#e#o.w~4zbp~qr.wst.wvw.wwx.w{|.w}!O/{!O!P.w!P!Q.w!Q![.w![!]6S!_!`.w!a!b.w!b!c.w!c!}.w#R#S.w#T#g.w#g#h7|#h#o.w~6VP!P!Q6Y~6]P!P!Q6`~6cYOX7RZp7Rqy7Rz|7R}!`7R!a#P7R#Q#p7R#q;'S7R;'S;=`7v<%lO7R~7WY`~OX7RZp7Rqy7Rz|7R}!`7R!a#P7R#Q#p7R#q;'S7R;'S;=`7v<%lO7R~7yP;=`<%l7R~8R`p~qr.wst.wvw.wwx.w{|.w}!O/{!O!P.w!P!Q.w!Q![.w![!]6S!_!`.w!a!b.w!b!c.w!c!}.w#R#S.w#T#o.w~9[Oh~q~~9cPm~q~!`!a9f~9kOY~~9nP;=`<%l#o",
14
+ tokenizers: [0],
15
+ topRules: {"Document":[0,6]},
16
+ specialized: [{term: 32, get: (value, stack) => (specializeKeyword(value, stack) << 1), external: specializeKeyword}],
17
+ tokenPrec: 204
18
+ })
@@ -0,0 +1,5 @@
1
+ export declare const ChartType: number;
2
+ export declare const TagKeyword: number;
3
+ export declare const DirectiveKeyword: number;
4
+ export declare const ControlKeyword: number;
5
+ export declare const ModifierKeyword: number;
@@ -0,0 +1,35 @@
1
+ // This file was generated by lezer-generator. You probably shouldn't edit it.
2
+ export const
3
+ ChartType = 1,
4
+ TagKeyword = 2,
5
+ DirectiveKeyword = 3,
6
+ ControlKeyword = 4,
7
+ ModifierKeyword = 5,
8
+ Document = 6,
9
+ Comment = 7,
10
+ ContentLine = 8,
11
+ SyncArrow = 9,
12
+ AsyncArrow = 10,
13
+ Duration = 11,
14
+ DateLiteral = 12,
15
+ Percentage = 13,
16
+ Number = 14,
17
+ SectionMarker = 15,
18
+ Url = 16,
19
+ OpenBracket = 17,
20
+ CloseBracket = 18,
21
+ OpenParen = 19,
22
+ CloseParen = 20,
23
+ OpenAngle = 21,
24
+ CloseAngle = 22,
25
+ ColorAnnotation = 23,
26
+ Pipe = 24,
27
+ Colon = 25,
28
+ Comma = 26,
29
+ Plus = 27,
30
+ Dash = 28,
31
+ Tilde = 29,
32
+ Star = 30,
33
+ Question = 31,
34
+ Identifier = 32,
35
+ Punct = 33
@@ -0,0 +1,36 @@
1
+ import type { NodePropSource } from '@lezer/common';
2
+ import { styleTags, tags as t } from '@lezer/highlight';
3
+
4
+ /** Maps grammar node names to semantic highlight tags. */
5
+ export const dgmoHighlighting: NodePropSource = styleTags({
6
+ Comment: t.lineComment,
7
+ ChartType: t.typeName,
8
+ TagKeyword: t.definitionKeyword,
9
+ DirectiveKeyword: t.keyword,
10
+ ControlKeyword: t.controlKeyword,
11
+ ModifierKeyword: t.modifier,
12
+ SyncArrow: t.operator,
13
+ AsyncArrow: t.operator,
14
+ Duration: t.number,
15
+ DateLiteral: t.number,
16
+ Number: t.number,
17
+ Percentage: t.number,
18
+ SectionMarker: t.heading,
19
+ OpenBracket: t.squareBracket,
20
+ CloseBracket: t.squareBracket,
21
+ OpenParen: t.paren,
22
+ CloseParen: t.paren,
23
+ OpenAngle: t.angleBracket,
24
+ CloseAngle: t.angleBracket,
25
+ Url: t.url,
26
+ ColorAnnotation: t.atom,
27
+ Pipe: t.separator,
28
+ Colon: t.separator,
29
+ Plus: t.separator,
30
+ Comma: t.punctuation,
31
+ Dash: t.operator,
32
+ Tilde: t.operator,
33
+ Star: t.operator,
34
+ Question: t.operator,
35
+ Punct: t.punctuation,
36
+ });
@@ -0,0 +1,28 @@
1
+ import { LRLanguage, LanguageSupport } from '@codemirror/language';
2
+ import type { Extension } from '@codemirror/state';
3
+ import { parser } from './dgmo.grammar.js';
4
+ import { dgmoHighlighting } from './highlight';
5
+
6
+ export { CHART_TYPES, METADATA_KEYS } from './keywords';
7
+ export { dgmoHighlighting } from './highlight';
8
+
9
+ /** The raw Lezer parser for DGMO. */
10
+ export const dgmoParser = parser.configure({
11
+ props: [dgmoHighlighting],
12
+ });
13
+
14
+ /** LRLanguage wrapper for CodeMirror. */
15
+ export const dgmoLanguage = LRLanguage.define({
16
+ name: 'dgmo',
17
+ parser: dgmoParser,
18
+ });
19
+
20
+ /** Full language support (language + extensions). */
21
+ export const dgmoLanguageSupport = new LanguageSupport(dgmoLanguage);
22
+
23
+ /**
24
+ * Drop-in replacement for the old dgmoExtension.
25
+ * Consumers should add indentationMarkers() separately if desired
26
+ * (from @replit/codemirror-indentation-markers).
27
+ */
28
+ export const dgmoExtension: Extension = dgmoLanguageSupport;
@@ -0,0 +1,220 @@
1
+ /** All supported DGMO chart types. */
2
+ export const CHART_TYPES = new Set([
3
+ // Diagram types
4
+ 'sequence',
5
+ 'flowchart',
6
+ 'class',
7
+ 'er',
8
+ 'org',
9
+ 'kanban',
10
+ 'c4',
11
+ 'initiative-status',
12
+ 'state',
13
+ 'sitemap',
14
+ 'infra',
15
+ 'gantt',
16
+ // Data chart types
17
+ 'bar',
18
+ 'line',
19
+ 'pie',
20
+ 'doughnut',
21
+ 'area',
22
+ 'polar-area',
23
+ 'radar',
24
+ 'bar-stacked',
25
+ 'multi-line',
26
+ 'scatter',
27
+ 'sankey',
28
+ 'chord',
29
+ 'function',
30
+ 'heatmap',
31
+ 'funnel',
32
+ // Visualization types
33
+ 'slope',
34
+ 'wordcloud',
35
+ 'arc',
36
+ 'timeline',
37
+ 'venn',
38
+ 'quadrant',
39
+ ]);
40
+
41
+ /** Metadata keys recognized across chart types. */
42
+ export const METADATA_KEYS = new Set([
43
+ 'title',
44
+ 'series',
45
+ 'orientation',
46
+ 'x-label',
47
+ 'y-label',
48
+ 'size-label',
49
+ 'x',
50
+ 'columns',
51
+ 'rows',
52
+ 'labels',
53
+ 'rotate',
54
+ 'max',
55
+ 'size',
56
+ 'order',
57
+ 'sort',
58
+ 'scale',
59
+ 'values',
60
+ 'notation',
61
+ 'x-axis',
62
+ 'y-axis',
63
+ 'top-right',
64
+ 'top-left',
65
+ 'bottom-right',
66
+ 'bottom-left',
67
+ ]);
68
+
69
+ /** Tag declaration keyword. */
70
+ export const TAG_KEYWORD = 'tag';
71
+
72
+ /** Directive keywords — commands that configure chart behavior. */
73
+ export const DIRECTIVE_KEYWORDS = new Set([
74
+ // Gantt
75
+ 'start',
76
+ 'era',
77
+ 'marker',
78
+ 'holiday',
79
+ 'workweek',
80
+ 'today-marker',
81
+ 'critical-path',
82
+ 'no-dependencies',
83
+ 'sort',
84
+ // Tags
85
+ 'tags',
86
+ 'import',
87
+ 'active-tag',
88
+ 'hide',
89
+ // ER
90
+ 'notation',
91
+ // Class
92
+ 'extends',
93
+ 'implements',
94
+ 'abstract',
95
+ 'interface',
96
+ 'enum',
97
+ // C4
98
+ 'containers',
99
+ 'components',
100
+ 'deployment',
101
+ // Infra directives
102
+ 'sub-node-label',
103
+ 'show-sub-node-count',
104
+ 'no-auto-color',
105
+ // Infra node properties
106
+ 'description',
107
+ 'instances',
108
+ 'max-rps',
109
+ 'latency-ms',
110
+ 'uptime',
111
+ 'firewall-block',
112
+ 'ratelimit-rps',
113
+ 'cb-error-threshold',
114
+ 'cb-latency-threshold-ms',
115
+ 'buffer',
116
+ 'drain-rate',
117
+ 'retention-hours',
118
+ 'partitions',
119
+ 'split',
120
+ 'slo-p90-latency-ms',
121
+ 'slo-availability',
122
+ 'cache-hit',
123
+ 'concurrency',
124
+ 'duration-ms',
125
+ 'cold-start-ms',
126
+ 'rps',
127
+ // Sequence
128
+ 'activations',
129
+ 'no-activations',
130
+ 'collapse-notes',
131
+ 'no-collapse-notes',
132
+ // Data charts
133
+ 'stacked',
134
+ 'no-label-name',
135
+ 'no-label-value',
136
+ 'no-label-percent',
137
+ // Quadrant
138
+ 'x-axis',
139
+ 'y-axis',
140
+ 'top-right',
141
+ 'top-left',
142
+ 'bottom-right',
143
+ 'bottom-left',
144
+ // Layout
145
+ 'direction-tb',
146
+ 'direction-lr',
147
+ // Initiative-status
148
+ 'contains',
149
+ // Data chart metadata
150
+ 'title',
151
+ 'series',
152
+ 'orientation',
153
+ 'x-label',
154
+ 'y-label',
155
+ 'size-label',
156
+ 'columns',
157
+ 'rows',
158
+ 'labels',
159
+ 'rotate',
160
+ 'scale',
161
+ 'values',
162
+ ]);
163
+
164
+ /** Control flow keywords — structural blocks. */
165
+ export const CONTROL_KEYWORDS = new Set([
166
+ 'if',
167
+ 'else',
168
+ 'loop',
169
+ 'parallel',
170
+ 'note',
171
+ ]);
172
+
173
+ /** Status keywords — initiative-status, kanban. */
174
+ export const STATUS_KEYWORDS = new Set([
175
+ 'na',
176
+ 'todo',
177
+ 'wip',
178
+ 'done',
179
+ 'blocked',
180
+ 'in-progress',
181
+ 'backlog',
182
+ 'ready',
183
+ ]);
184
+
185
+ /** Modifier keywords — adjust declarations. */
186
+ export const MODIFIER_KEYWORDS = new Set([
187
+ 'alias',
188
+ 'aka',
189
+ 'position',
190
+ 'default',
191
+ // Sequence participant types
192
+ 'actor',
193
+ 'service',
194
+ 'database',
195
+ 'queue',
196
+ 'cache',
197
+ 'gateway',
198
+ 'external',
199
+ 'networking',
200
+ 'frontend',
201
+ // C4 element types
202
+ 'system',
203
+ 'person',
204
+ 'container',
205
+ 'component',
206
+ // ER column modifiers
207
+ 'pk',
208
+ 'fk',
209
+ 'nullable',
210
+ 'unique',
211
+ // ER data types
212
+ 'int',
213
+ 'varchar',
214
+ 'text',
215
+ 'boolean',
216
+ 'date',
217
+ 'timestamp',
218
+ 'float',
219
+ 'decimal',
220
+ ]);
@@ -0,0 +1,30 @@
1
+ import {
2
+ ChartType,
3
+ TagKeyword,
4
+ DirectiveKeyword,
5
+ ControlKeyword,
6
+ ModifierKeyword,
7
+ } from './dgmo.grammar.terms.js';
8
+ import {
9
+ CHART_TYPES,
10
+ TAG_KEYWORD,
11
+ DIRECTIVE_KEYWORDS,
12
+ CONTROL_KEYWORDS,
13
+ STATUS_KEYWORDS,
14
+ MODIFIER_KEYWORDS,
15
+ } from './keywords';
16
+
17
+ /**
18
+ * Keyword specializer for the Lezer grammar.
19
+ * Called on every Identifier token — returns a specialized term ID
20
+ * or -1 to keep it as a plain Identifier.
21
+ */
22
+ export function specializeKeyword(value: string): number {
23
+ if (CHART_TYPES.has(value)) return ChartType;
24
+ if (value === TAG_KEYWORD) return TagKeyword;
25
+ if (DIRECTIVE_KEYWORDS.has(value)) return DirectiveKeyword;
26
+ if (CONTROL_KEYWORDS.has(value)) return ControlKeyword;
27
+ if (STATUS_KEYWORDS.has(value)) return ModifierKeyword;
28
+ if (MODIFIER_KEYWORDS.has(value)) return ModifierKeyword;
29
+ return -1;
30
+ }
package/src/er/parser.ts CHANGED
@@ -1,7 +1,13 @@
1
1
  import { resolveColor } from '../colors';
2
2
  import type { PaletteColors } from '../palettes';
3
3
  import { makeDgmoError, formatDgmoError, suggest } from '../diagnostics';
4
- import { measureIndent, extractColor, parsePipeMetadata, parseFirstLine, OPTION_NOCOLON_RE } from '../utils/parsing';
4
+ import {
5
+ measureIndent,
6
+ extractColor,
7
+ parsePipeMetadata,
8
+ parseFirstLine,
9
+ OPTION_NOCOLON_RE,
10
+ } from '../utils/parsing';
5
11
  import { matchTagBlockHeading, validateTagValues } from '../utils/tag-groups';
6
12
  import type { TagGroup } from '../utils/tag-groups';
7
13
  import type {
@@ -82,7 +88,7 @@ const KEYWORD_TO_SYMBOL: Record<string, string> = {
82
88
  function parseRelationship(
83
89
  trimmed: string,
84
90
  lineNumber: number,
85
- pushError: (line: number, message: string) => void,
91
+ pushError: (line: number, message: string) => void
86
92
  ): {
87
93
  source: string;
88
94
  target: string;
@@ -113,7 +119,7 @@ function parseRelationship(
113
119
  const toSym = KEYWORD_TO_SYMBOL[kw[3].toLowerCase()] ?? kw[3];
114
120
  pushError(
115
121
  lineNumber,
116
- `Use symbolic cardinality (1--*, ?--1, *--*) instead of "${kw[2]}-to-${kw[3]}". Example: ${kw[1]} ${fromSym}--${toSym} ${kw[4]}`,
122
+ `Use symbolic cardinality (1--*, ?--1, *--*) instead of "${kw[2]}-to-${kw[3]}". Example: ${kw[1]} ${fromSym}--${toSym} ${kw[4]}`
117
123
  );
118
124
  return null;
119
125
  }
@@ -176,7 +182,8 @@ export function parseERDiagram(
176
182
  error: null,
177
183
  };
178
184
 
179
- const fail = (line: number, message: string): ParsedERDiagram => {
185
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
186
+ const _fail = (line: number, message: string): ParsedERDiagram => {
180
187
  const diag = makeDgmoError(line, message);
181
188
  result.diagnostics.push(diag);
182
189
  result.error = formatDgmoError(diag);
@@ -243,15 +250,10 @@ export function parseERDiagram(
243
250
  firstLineParsed = true;
244
251
  }
245
252
 
246
- // Tag group heading — `tag Name` or deprecated `## Name`
253
+ // Tag group heading — `tag Name`
247
254
  if (!contentStarted && indent === 0) {
248
255
  const tagBlockMatch = matchTagBlockHeading(trimmed);
249
256
  if (tagBlockMatch) {
250
- if (tagBlockMatch.deprecated) {
251
- result.diagnostics.push(makeDgmoError(lineNumber,
252
- `'## ${tagBlockMatch.name}' is no longer supported — use 'tag: ${tagBlockMatch.name}' instead`));
253
- continue;
254
- }
255
257
  currentTagGroup = {
256
258
  name: tagBlockMatch.name,
257
259
  alias: tagBlockMatch.alias,
@@ -259,7 +261,10 @@ export function parseERDiagram(
259
261
  lineNumber,
260
262
  };
261
263
  if (tagBlockMatch.alias) {
262
- aliasMap.set(tagBlockMatch.alias.toLowerCase(), tagBlockMatch.name.toLowerCase());
264
+ aliasMap.set(
265
+ tagBlockMatch.alias.toLowerCase(),
266
+ tagBlockMatch.name.toLowerCase()
267
+ );
263
268
  }
264
269
  result.tagGroups.push(currentTagGroup);
265
270
  continue;
@@ -270,8 +275,13 @@ export function parseERDiagram(
270
275
  if (currentTagGroup && !contentStarted && indent > 0) {
271
276
  const { label, color } = extractColor(trimmed, palette);
272
277
  if (!color) {
273
- result.diagnostics.push(makeDgmoError(lineNumber,
274
- `Expected 'Value(color)' in tag group '${currentTagGroup.name}'`, 'warning'));
278
+ result.diagnostics.push(
279
+ makeDgmoError(
280
+ lineNumber,
281
+ `Expected 'Value(color)' in tag group '${currentTagGroup.name}'`,
282
+ 'warning'
283
+ )
284
+ );
275
285
  continue;
276
286
  }
277
287
  // First entry becomes the default
@@ -337,19 +347,16 @@ export function parseERDiagram(
337
347
  currentTable = null;
338
348
  contentStarted = true;
339
349
 
340
- // Try relationship
350
+ // Reject top-level relationships — must be indented under source table
341
351
  const rel = parseRelationship(trimmed, lineNumber, pushError);
342
352
  if (rel) {
343
- getOrCreateTable(rel.source, lineNumber);
344
- getOrCreateTable(rel.target, lineNumber);
345
-
346
- result.relationships.push({
347
- source: tableId(rel.source),
348
- target: tableId(rel.target),
349
- cardinality: { from: rel.from, to: rel.to },
350
- ...(rel.label && { label: rel.label }),
351
- lineNumber,
352
- });
353
+ result.diagnostics.push(
354
+ makeDgmoError(
355
+ lineNumber,
356
+ `Relationship "${rel.source} → ${rel.target}" must be indented under the source table "${rel.source}"`,
357
+ 'warning'
358
+ )
359
+ );
353
360
  continue;
354
361
  }
355
362
 
@@ -375,11 +382,19 @@ export function parseERDiagram(
375
382
  currentTable = table;
376
383
  continue;
377
384
  }
385
+
386
+ // Catch-all: nothing matched this line
387
+ result.diagnostics.push(
388
+ makeDgmoError(lineNumber, `Unexpected line: '${trimmed}'.`, 'warning')
389
+ );
378
390
  }
379
391
 
380
392
  // Validation
381
393
  if (result.tables.length === 0 && !result.error) {
382
- const diag = makeDgmoError(1, 'No tables found. Add table declarations like "users" or "orders (blue)".');
394
+ const diag = makeDgmoError(
395
+ 1,
396
+ 'No tables found. Add table declarations like "users" or "orders (blue)".'
397
+ );
383
398
  result.diagnostics.push(diag);
384
399
  result.error = formatDgmoError(diag);
385
400
  }
@@ -393,8 +408,9 @@ export function parseERDiagram(
393
408
  validateTagValues(
394
409
  tagEntities,
395
410
  result.tagGroups,
396
- (line, msg) => result.diagnostics.push(makeDgmoError(line, msg, 'warning')),
397
- suggest,
411
+ (line, msg) =>
412
+ result.diagnostics.push(makeDgmoError(line, msg, 'warning')),
413
+ suggest
398
414
  );
399
415
 
400
416
  // Inject defaults for tables without explicit tags
@@ -410,7 +426,11 @@ export function parseERDiagram(
410
426
  }
411
427
 
412
428
  // Warn about isolated tables (not in any relationship)
413
- if (result.tables.length >= 2 && result.relationships.length >= 1 && !result.error) {
429
+ if (
430
+ result.tables.length >= 2 &&
431
+ result.relationships.length >= 1 &&
432
+ !result.error
433
+ ) {
414
434
  const connectedIds = new Set<string>();
415
435
  for (const rel of result.relationships) {
416
436
  connectedIds.add(rel.source);
@@ -418,7 +438,13 @@ export function parseERDiagram(
418
438
  }
419
439
  for (const table of result.tables) {
420
440
  if (!connectedIds.has(table.id)) {
421
- result.diagnostics.push(makeDgmoError(table.lineNumber, `Table "${table.name}" is not connected to any other table`, 'warning'));
441
+ result.diagnostics.push(
442
+ makeDgmoError(
443
+ table.lineNumber,
444
+ `Table "${table.name}" is not connected to any other table`,
445
+ 'warning'
446
+ )
447
+ );
422
448
  }
423
449
  }
424
450
  }