@diagrammo/dgmo 0.45.0 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.cjs +31 -25
- package/dist/advanced.d.cts +3 -855
- package/dist/advanced.d.ts +3 -855
- package/dist/advanced.js +541 -70101
- package/dist/analyzer-UYKBXAKR.js +15 -0
- package/dist/auto.cjs +500 -341
- package/dist/auto.css +342 -159
- package/dist/auto.d.cts +10 -8
- package/dist/auto.d.ts +10 -8
- package/dist/auto.js +373 -265
- package/dist/auto.mjs +500 -341
- package/dist/block.cjs +31 -25
- package/dist/block.js +83 -69411
- package/dist/calculator-LDYQCDDY.js +11 -0
- package/dist/charts-d3-QOZHKTCT.js +25 -0
- package/dist/chunk-2DXMZRWJ.js +800 -0
- package/dist/chunk-2LACINWN.js +786 -0
- package/dist/chunk-33LCUH4H.js +893 -0
- package/dist/chunk-3CIOJ5YJ.js +35 -0
- package/dist/chunk-3HDBAXF5.js +86 -0
- package/dist/chunk-3JLKYGBQ.js +290 -0
- package/dist/chunk-3MQQNXPD.js +415 -0
- package/dist/chunk-3R5POCTZ.js +967 -0
- package/dist/chunk-3SMGIJDD.js +112 -0
- package/dist/chunk-44CM72SQ.js +409 -0
- package/dist/chunk-4GRI7MEB.js +1604 -0
- package/dist/chunk-4OHACBGX.js +606 -0
- package/dist/chunk-5DH3KYFH.js +21 -0
- package/dist/chunk-5MLYVTYM.js +76 -0
- package/dist/chunk-5QXL4NYS.js +553 -0
- package/dist/chunk-5WRI5ZAA.js +31 -0
- package/dist/chunk-6EK55ART.js +117 -0
- package/dist/chunk-6FZYCKXB.js +2065 -0
- package/dist/chunk-6HO5ZTXK.js +277 -0
- package/dist/chunk-6KFWC4CO.js +7 -0
- package/dist/chunk-6MTWIQVO.js +121 -0
- package/dist/chunk-6NYWYNBI.js +329 -0
- package/dist/chunk-6SM5NJQH.js +620 -0
- package/dist/chunk-725LDASD.js +422 -0
- package/dist/chunk-76OWZ3BA.js +130 -0
- package/dist/chunk-7JEGTRLD.js +338 -0
- package/dist/chunk-A44USUV7.js +923 -0
- package/dist/chunk-A4GOY3PE.js +25 -0
- package/dist/chunk-A4VFQH5G.js +251 -0
- package/dist/chunk-AELOBFW5.js +375 -0
- package/dist/chunk-AN2MOO5T.js +522 -0
- package/dist/chunk-BMR4CIEO.js +41 -0
- package/dist/chunk-BS7SV7C3.js +51 -0
- package/dist/chunk-BUPDIHML.js +613 -0
- package/dist/chunk-BUVJFRPY.js +887 -0
- package/dist/chunk-C3Z4DX3V.js +112 -0
- package/dist/chunk-CI2G3OEB.js +1818 -0
- package/dist/chunk-CKNVO24D.js +54 -0
- package/dist/chunk-CUVHQHZ7.js +391 -0
- package/dist/chunk-CXK4DZH3.js +114 -0
- package/dist/chunk-DKEPS5LH.js +315 -0
- package/dist/chunk-DU7W62N5.js +487 -0
- package/dist/chunk-DZAQBW35.js +751 -0
- package/dist/chunk-EB6FPQUP.js +316 -0
- package/dist/chunk-EMS2SK26.js +376 -0
- package/dist/chunk-ERLBPDGU.js +510 -0
- package/dist/chunk-EVCVDTXE.js +732 -0
- package/dist/chunk-FCIIHT24.js +384 -0
- package/dist/chunk-FFUN5D66.js +97 -0
- package/dist/chunk-FITOLXSX.js +714 -0
- package/dist/chunk-FMDSF3OV.js +68 -0
- package/dist/chunk-FNB3PNGF.js +410 -0
- package/dist/chunk-G2H74XJZ.js +2104 -0
- package/dist/chunk-GMR5MFHJ.js +339 -0
- package/dist/chunk-H6PI3KJT.js +305 -0
- package/dist/chunk-I46X5VUA.js +59 -0
- package/dist/chunk-I5IGB56C.js +255 -0
- package/dist/chunk-IH6ZMVQ5.js +3950 -0
- package/dist/chunk-IJZFN3HM.js +463 -0
- package/dist/chunk-IPWPWM6R.js +3079 -0
- package/dist/chunk-IYULMRBE.js +23 -0
- package/dist/chunk-IZMRON6C.js +167 -0
- package/dist/chunk-J5FIXQRT.js +813 -0
- package/dist/chunk-JED6ZDEE.js +73 -0
- package/dist/chunk-JEUJOX5X.js +136 -0
- package/dist/chunk-JM4LEPY3.js +436 -0
- package/dist/chunk-L3UHP6TD.js +101 -0
- package/dist/chunk-L66GEFB6.js +524 -0
- package/dist/chunk-LIOHW36Z.js +469 -0
- package/dist/chunk-LP7EC4MI.js +16 -0
- package/dist/chunk-MITI6NOL.js +548 -0
- package/dist/chunk-MO5YHHMQ.js +168 -0
- package/dist/chunk-MZSBFAPJ.js +885 -0
- package/dist/chunk-NNAAJVBU.js +9 -0
- package/dist/chunk-NOTA6TEI.js +47 -0
- package/dist/chunk-NURGB5Z4.js +335 -0
- package/dist/chunk-NX4EQS3U.js +1062 -0
- package/dist/chunk-NXMESY4J.js +1 -0
- package/dist/chunk-OJR3MQYV.js +1458 -0
- package/dist/chunk-OURHMODR.js +454 -0
- package/dist/chunk-P235SVAH.js +1057 -0
- package/dist/chunk-P4RH4OSP.js +1038 -0
- package/dist/chunk-P6T66CCZ.js +226 -0
- package/dist/chunk-PBAHEPLQ.js +223 -0
- package/dist/chunk-PBHE5UG6.js +1530 -0
- package/dist/chunk-PDKUANDZ.js +35 -0
- package/dist/chunk-PWIHZ6NJ.js +296 -0
- package/dist/chunk-QEBUY5NY.js +1685 -0
- package/dist/chunk-QLCCJEJI.js +253 -0
- package/dist/chunk-QOOUC2J5.js +52 -0
- package/dist/chunk-QYTPPDAO.js +402 -0
- package/dist/chunk-R3XI2VC7.js +801 -0
- package/dist/chunk-RCERWOJR.js +154 -0
- package/dist/chunk-RJE5JTVK.js +362 -0
- package/dist/chunk-RTLBSQ3G.js +1155 -0
- package/dist/chunk-RUNNI6VE.js +448 -0
- package/dist/chunk-RVMRZAPB.js +354 -0
- package/dist/chunk-RYEGFMUO.js +955 -0
- package/dist/chunk-RZCF2VVM.js +528 -0
- package/dist/chunk-SML67VPV.js +1025 -0
- package/dist/chunk-SQ7IY53Q.js +44 -0
- package/dist/chunk-SQAG3UXH.js +161 -0
- package/dist/chunk-SRTU6HMQ.js +1851 -0
- package/dist/chunk-T2OE3YQB.js +550 -0
- package/dist/chunk-T3UBLACL.js +299 -0
- package/dist/chunk-TLVIHFRO.js +296 -0
- package/dist/chunk-TYS6CSS5.js +52 -0
- package/dist/chunk-U6EQI3U6.js +130 -0
- package/dist/chunk-UJ4FJLBF.js +86 -0
- package/dist/chunk-ULOEATBZ.js +347 -0
- package/dist/chunk-UNCCZ3NU.js +127 -0
- package/dist/chunk-USZW2HHU.js +197 -0
- package/dist/chunk-UWN33VNX.js +1610 -0
- package/dist/chunk-UZ7EZW3B.js +40 -0
- package/dist/chunk-VOKGH627.js +37 -0
- package/dist/chunk-WBRZJOEC.js +204 -0
- package/dist/chunk-WCSOJFU3.js +380 -0
- package/dist/chunk-WCT3NYCV.js +160 -0
- package/dist/chunk-WVAJFAIA.js +529 -0
- package/dist/chunk-X5LCYCL5.js +394 -0
- package/dist/chunk-XAKEUMEJ.js +1256 -0
- package/dist/chunk-Y2ZE3MSG.js +590 -0
- package/dist/chunk-Y3J7VR52.js +646 -0
- package/dist/chunk-Y5QGVTVC.js +33 -0
- package/dist/chunk-YAA6IID3.js +548 -0
- package/dist/chunk-YGJTDXVR.js +692 -0
- package/dist/chunk-YRYKTYMQ.js +186 -0
- package/dist/chunk-YYVTBZZC.js +61 -0
- package/dist/chunk-Z5CGSFJB.js +647 -0
- package/dist/chunk-Z72XRXD4.js +787 -0
- package/dist/chunk-ZGR7ULFJ.js +978 -0
- package/dist/chunk-ZJEL4H2R.js +851 -0
- package/dist/chunk-ZO27FGJS.js +554 -0
- package/dist/cli.cjs +112 -112
- package/dist/collapse-CLPJD2RL.js +11 -0
- package/dist/collapse-SLINJQW6.js +9 -0
- package/dist/collapse-ZWUFFCK2.js +9 -0
- package/dist/compute-G55RG53N.js +8 -0
- package/dist/dgmo-router-DZCIDUO5.js +73 -0
- package/dist/dimensions-J3FHIMJ6.js +22 -0
- package/dist/element.cjs +492 -325
- package/dist/element.js +367 -259
- package/dist/element.mjs +492 -325
- package/dist/flowchart-parser-ZNSKIBVR.js +21 -0
- package/dist/flowchart-renderer-ZAEOL4DX.js +35 -0
- package/dist/index.cjs +34 -28
- package/dist/index.d.cts +3 -469
- package/dist/index.d.ts +3 -469
- package/dist/index.js +137 -69679
- package/dist/layout-46N4JJYK.js +17 -0
- package/dist/layout-5RA6QWR6.js +26 -0
- package/dist/layout-5T6RH5M6.js +14 -0
- package/dist/layout-7QXCNOVS.js +13 -0
- package/dist/layout-EE6GE46Y.js +17 -0
- package/dist/layout-G23F7LDH.js +9 -0
- package/dist/layout-HRBZPFCN.js +23 -0
- package/dist/layout-HWDSRX5E.js +20 -0
- package/dist/layout-HYZPWAK5.js +18 -0
- package/dist/layout-N76NR4ZO.js +19 -0
- package/dist/layout-OB3RI63S.js +14 -0
- package/dist/layout-S2QBYSK2.js +15 -0
- package/dist/layout-UM5F4DJB.js +15 -0
- package/dist/layout-search-OYV77HHS.js +1655 -0
- package/dist/load-data-EHDDZ5ZS.js +8 -0
- package/dist/palettes-PRJ2RI6D.js +56 -0
- package/dist/parser-2MFP2H2E.js +11 -0
- package/dist/parser-3STE62GB.js +21 -0
- package/dist/parser-4FL2CMDG.js +18 -0
- package/dist/parser-4OGOBJJ4.js +13 -0
- package/dist/parser-6BTRLKRZ.js +15 -0
- package/dist/parser-AQETEVRW.js +21 -0
- package/dist/parser-CFZD5IBL.js +12 -0
- package/dist/parser-FUSKFGZN.js +16 -0
- package/dist/parser-I6KXEYFK.js +13 -0
- package/dist/parser-IXSZZQHY.js +18 -0
- package/dist/parser-JAL2HD4I.js +13 -0
- package/dist/parser-KNSQSKQO.js +20 -0
- package/dist/parser-N5O4IHUN.js +13 -0
- package/dist/parser-NGU3ETO6.js +16 -0
- package/dist/parser-NRAI6O25.js +19 -0
- package/dist/parser-O5J4VBII.js +11 -0
- package/dist/parser-OV7NOBW6.js +21 -0
- package/dist/parser-PQ3POX45.js +23 -0
- package/dist/parser-TFZFBVWN.js +11 -0
- package/dist/parser-TRTHDFPP.js +18 -0
- package/dist/parser-VQP2UJ6W.js +11 -0
- package/dist/parser-XDVELHRK.js +13 -0
- package/dist/parser-XKRP7QKP.js +11 -0
- package/dist/parser-Y6WTBU76.js +14 -0
- package/dist/parser-ZSDPZYQP.js +12 -0
- package/dist/renderer-2XQQQ6QR.js +28 -0
- package/dist/renderer-5NDVNDAZ.js +26 -0
- package/dist/renderer-6EEZHMH4.js +26 -0
- package/dist/renderer-6W5462RB.js +43 -0
- package/dist/renderer-7MVLWWQD.js +22 -0
- package/dist/renderer-AWWAF6XD.js +33 -0
- package/dist/renderer-BYCNMBF7.js +34 -0
- package/dist/renderer-CLJHQPBJ.js +29 -0
- package/dist/renderer-GHPZMFU2.js +15 -0
- package/dist/renderer-JGBKUCZD.js +16 -0
- package/dist/renderer-KQB7UGDS.js +19 -0
- package/dist/renderer-PGX2TKVS.js +24 -0
- package/dist/renderer-QIWVHWZO.js +15 -0
- package/dist/renderer-RPOA5YPZ.js +35 -0
- package/dist/renderer-SMIBCRVB.js +24 -0
- package/dist/renderer-T7YKC5VP.js +28 -0
- package/dist/renderer-TOCOVLHS.js +37 -0
- package/dist/renderer-UWA5XS74.js +39 -0
- package/dist/renderer-VACVQYML.js +25 -0
- package/dist/renderer-W4M6QLIJ.js +37 -0
- package/dist/renderer-WZRREQ2P.js +23 -0
- package/dist/renderer-YY2OSF6E.js +27 -0
- package/dist/renderer-Z7I3W2PP.js +24 -0
- package/dist/renderer-ZGNTUWC4.js +22 -0
- package/dist/renderer-ZTR3DER3.js +19 -0
- package/dist/renderer-radial-C32GPJQ7.js +21 -0
- package/dist/resolver-NOAA7ID7.js +13 -0
- package/dist/state-collapse-HCGD6IVJ.js +8 -0
- package/dist/state-parser-Z2V23ATR.js +18 -0
- package/dist/state-renderer-RFYHSP4K.js +34 -0
- package/dist/themes-1-CuKpeH.d.cts +908 -0
- package/dist/themes-1-CuKpeH.d.ts +908 -0
- package/package.json +1 -1
- package/src/auto/index.ts +55 -57
- package/src/auto/shared.ts +142 -142
- package/src/auto/styles.ts +44 -199
- package/src/c4/renderer.ts +2 -2
- package/src/element/index.ts +29 -21
- package/src/infra/renderer.ts +1 -1
- package/src/render.ts +34 -14
- package/src/sitemap/renderer.ts +1 -1
- package/src/timeline/renderer.ts +1 -1
- package/src/utils/hover-styles.ts +6 -1
- package/src/utils/legend-d3.ts +1 -4
- package/src/utils/legend-svg.ts +1 -1
|
@@ -0,0 +1,800 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tryStripDescriptionKeyword
|
|
3
|
+
} from "./chunk-IYULMRBE.js";
|
|
4
|
+
import {
|
|
5
|
+
inferParticipantType
|
|
6
|
+
} from "./chunk-C3Z4DX3V.js";
|
|
7
|
+
import {
|
|
8
|
+
AUTO_TAG_COLOR_SENTINEL,
|
|
9
|
+
finalizeAutoTagColors,
|
|
10
|
+
matchTagBlockHeading,
|
|
11
|
+
stripDefaultModifier,
|
|
12
|
+
tagAttrKey,
|
|
13
|
+
validateTagGroupNames
|
|
14
|
+
} from "./chunk-QLCCJEJI.js";
|
|
15
|
+
import {
|
|
16
|
+
parseInArrowLabel
|
|
17
|
+
} from "./chunk-FFUN5D66.js";
|
|
18
|
+
import {
|
|
19
|
+
normalizeName
|
|
20
|
+
} from "./chunk-NOTA6TEI.js";
|
|
21
|
+
import {
|
|
22
|
+
C4_REGISTRY,
|
|
23
|
+
OPTION_NOCOLON_RE,
|
|
24
|
+
extractColor,
|
|
25
|
+
measureIndent,
|
|
26
|
+
parseFirstLine,
|
|
27
|
+
splitNameAndMeta,
|
|
28
|
+
warnUnknownMetaKeys,
|
|
29
|
+
withTagAliases
|
|
30
|
+
} from "./chunk-Y3J7VR52.js";
|
|
31
|
+
import {
|
|
32
|
+
formatDgmoError,
|
|
33
|
+
makeDgmoError,
|
|
34
|
+
makeFail,
|
|
35
|
+
suggest
|
|
36
|
+
} from "./chunk-CXK4DZH3.js";
|
|
37
|
+
|
|
38
|
+
// src/c4/parser.ts
|
|
39
|
+
var CONTAINER_RE = /^\[([^\]]+)\]$/;
|
|
40
|
+
var ELEMENT_RE = /^(person|system|container|component)\s+(.+)$/i;
|
|
41
|
+
var IS_A_RE = /\s+is\s+a(?:n)?\s+(\w+)\s*$/i;
|
|
42
|
+
var C4_IS_A_RE = /^([^:]+?)\s+is\s+an?\s+(person|system|container|component|external|database)\b(.*)$/i;
|
|
43
|
+
var RELATIONSHIP_RE = /^(<?-?>|<?~?>)\s*(.+)$/;
|
|
44
|
+
var C4_LABELED_SYNC_RE = /^-(.+?)->\s*(.+)$/;
|
|
45
|
+
var C4_LABELED_ASYNC_RE = /^~(.+?)~>\s*(.+)$/;
|
|
46
|
+
var C4_LABELED_BIDI_SYNC_RE = /^<-(.+?)->\s*(.+)$/;
|
|
47
|
+
var C4_LABELED_BIDI_ASYNC_RE = /^<~(.+?)~>\s*(.+)$/;
|
|
48
|
+
var SECTION_HEADER_RE = /^(containers|components|deployment)\s*$/i;
|
|
49
|
+
var CONTAINER_REF_RE = /^container\s+(.+)$/i;
|
|
50
|
+
var METADATA_RE = /^([a-z][a-z0-9-]*):\s+(.+)$/i;
|
|
51
|
+
var VALID_ELEMENT_TYPES = /* @__PURE__ */ new Set([
|
|
52
|
+
"person",
|
|
53
|
+
"system",
|
|
54
|
+
"container",
|
|
55
|
+
"component"
|
|
56
|
+
]);
|
|
57
|
+
var VALID_SHAPES = /* @__PURE__ */ new Set([
|
|
58
|
+
"default",
|
|
59
|
+
"database",
|
|
60
|
+
"cache",
|
|
61
|
+
"queue",
|
|
62
|
+
"cloud",
|
|
63
|
+
"external"
|
|
64
|
+
]);
|
|
65
|
+
var KNOWN_C4_OPTIONS = /* @__PURE__ */ new Set(["layout", "active-tag"]);
|
|
66
|
+
var KNOWN_C4_BOOLEANS = /* @__PURE__ */ new Set([
|
|
67
|
+
"direction-tb",
|
|
68
|
+
"solid-fill",
|
|
69
|
+
"no-title"
|
|
70
|
+
]);
|
|
71
|
+
var ALL_CHART_TYPES = [
|
|
72
|
+
"c4",
|
|
73
|
+
"org",
|
|
74
|
+
"class",
|
|
75
|
+
"flowchart",
|
|
76
|
+
"sequence",
|
|
77
|
+
"er",
|
|
78
|
+
"bar",
|
|
79
|
+
"line",
|
|
80
|
+
"pie",
|
|
81
|
+
"scatter",
|
|
82
|
+
"sankey",
|
|
83
|
+
"venn",
|
|
84
|
+
"timeline",
|
|
85
|
+
"arc",
|
|
86
|
+
"slope",
|
|
87
|
+
"kanban"
|
|
88
|
+
];
|
|
89
|
+
function participantTypeToC4Shape(pType) {
|
|
90
|
+
switch (pType) {
|
|
91
|
+
case "database":
|
|
92
|
+
return "database";
|
|
93
|
+
case "cache":
|
|
94
|
+
return "cache";
|
|
95
|
+
case "queue":
|
|
96
|
+
return "queue";
|
|
97
|
+
case "external":
|
|
98
|
+
return "external";
|
|
99
|
+
case "networking":
|
|
100
|
+
return "cloud";
|
|
101
|
+
default:
|
|
102
|
+
return "default";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function inferC4Shape(name, tech) {
|
|
106
|
+
if (tech) {
|
|
107
|
+
const techShape = participantTypeToC4Shape(inferParticipantType(tech));
|
|
108
|
+
if (techShape !== "default") return techShape;
|
|
109
|
+
}
|
|
110
|
+
return participantTypeToC4Shape(inferParticipantType(name));
|
|
111
|
+
}
|
|
112
|
+
function parseC4MetaTail(tail, metaAliasMap) {
|
|
113
|
+
const trimmed = tail.trim();
|
|
114
|
+
if (!trimmed) return {};
|
|
115
|
+
if (!trimmed.includes(":")) {
|
|
116
|
+
return {};
|
|
117
|
+
}
|
|
118
|
+
const registry = withTagAliases(C4_REGISTRY, new Set(metaAliasMap.keys()));
|
|
119
|
+
const split = splitNameAndMeta(
|
|
120
|
+
`color: __c4ph, ${trimmed}`,
|
|
121
|
+
registry,
|
|
122
|
+
metaAliasMap
|
|
123
|
+
);
|
|
124
|
+
const meta = split.meta;
|
|
125
|
+
if (meta["color"] === "__c4ph") delete meta["color"];
|
|
126
|
+
return meta;
|
|
127
|
+
}
|
|
128
|
+
function parseC4NameAndMeta(text, metaAliasMap) {
|
|
129
|
+
const registry = withTagAliases(C4_REGISTRY, new Set(metaAliasMap.keys()));
|
|
130
|
+
const split = splitNameAndMeta(text, registry, metaAliasMap);
|
|
131
|
+
return { name: split.name, metadata: split.meta };
|
|
132
|
+
}
|
|
133
|
+
function parseArrowType(arrow) {
|
|
134
|
+
switch (arrow) {
|
|
135
|
+
case "->":
|
|
136
|
+
return "sync";
|
|
137
|
+
case "~>":
|
|
138
|
+
return "async";
|
|
139
|
+
case "<->":
|
|
140
|
+
return "bidirectional";
|
|
141
|
+
case "<~>":
|
|
142
|
+
return "bidirectional-async";
|
|
143
|
+
default:
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function parseC4(content, palette) {
|
|
148
|
+
const options = {};
|
|
149
|
+
const result = {
|
|
150
|
+
title: null,
|
|
151
|
+
titleLineNumber: null,
|
|
152
|
+
options,
|
|
153
|
+
tagGroups: [],
|
|
154
|
+
elements: [],
|
|
155
|
+
relationships: [],
|
|
156
|
+
deployment: [],
|
|
157
|
+
diagnostics: [],
|
|
158
|
+
error: null
|
|
159
|
+
};
|
|
160
|
+
const pushError = (line, message, severity = "error") => {
|
|
161
|
+
const diag = makeDgmoError(line, message, severity);
|
|
162
|
+
result.diagnostics.push(diag);
|
|
163
|
+
if (!result.error && severity === "error")
|
|
164
|
+
result.error = formatDgmoError(diag);
|
|
165
|
+
};
|
|
166
|
+
const fail = makeFail(result);
|
|
167
|
+
if (!content?.trim()) {
|
|
168
|
+
return fail(0, "No content provided");
|
|
169
|
+
}
|
|
170
|
+
const lines = content.split("\n");
|
|
171
|
+
let contentStarted = false;
|
|
172
|
+
let sawChartType = false;
|
|
173
|
+
let inDeployment = false;
|
|
174
|
+
let currentTagGroup = null;
|
|
175
|
+
const metaAliasMap = /* @__PURE__ */ new Map();
|
|
176
|
+
const nameAliasMap = /* @__PURE__ */ new Map();
|
|
177
|
+
function resolveNameRef(rawName) {
|
|
178
|
+
return nameAliasMap.get(rawName.trim()) ?? rawName;
|
|
179
|
+
}
|
|
180
|
+
const knownNames = /* @__PURE__ */ new Map();
|
|
181
|
+
const elementMetadata = /* @__PURE__ */ new WeakMap();
|
|
182
|
+
const elementDescription = /* @__PURE__ */ new WeakMap();
|
|
183
|
+
const stack = [];
|
|
184
|
+
const deployStack = [];
|
|
185
|
+
for (let i = 0; i < lines.length; i++) {
|
|
186
|
+
const line = lines[i];
|
|
187
|
+
const lineNumber = i + 1;
|
|
188
|
+
const trimmed = line.trim();
|
|
189
|
+
if (!trimmed) {
|
|
190
|
+
if (currentTagGroup) currentTagGroup = null;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (trimmed.startsWith("//")) continue;
|
|
194
|
+
if (!contentStarted && !sawChartType) {
|
|
195
|
+
const firstLine = parseFirstLine(trimmed);
|
|
196
|
+
if (firstLine) {
|
|
197
|
+
if (firstLine.chartType !== "c4") {
|
|
198
|
+
let msg = `Expected chart type "c4", got "${firstLine.chartType}"`;
|
|
199
|
+
const hint = suggest(firstLine.chartType, ALL_CHART_TYPES);
|
|
200
|
+
if (hint) msg += `. ${hint}`;
|
|
201
|
+
return fail(lineNumber, msg);
|
|
202
|
+
}
|
|
203
|
+
sawChartType = true;
|
|
204
|
+
if (firstLine.title) {
|
|
205
|
+
result.title = firstLine.title;
|
|
206
|
+
result.titleLineNumber = lineNumber;
|
|
207
|
+
}
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const tagBlockMatch = matchTagBlockHeading(trimmed);
|
|
212
|
+
if (tagBlockMatch) {
|
|
213
|
+
if (contentStarted) {
|
|
214
|
+
pushError(lineNumber, "Tag groups must appear before content");
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
currentTagGroup = {
|
|
218
|
+
name: tagBlockMatch.name,
|
|
219
|
+
...tagBlockMatch.alias !== void 0 && {
|
|
220
|
+
alias: tagBlockMatch.alias
|
|
221
|
+
},
|
|
222
|
+
entries: [],
|
|
223
|
+
lineNumber
|
|
224
|
+
};
|
|
225
|
+
if (tagBlockMatch.alias) {
|
|
226
|
+
metaAliasMap.set(
|
|
227
|
+
normalizeName(tagBlockMatch.alias),
|
|
228
|
+
tagAttrKey(tagBlockMatch.name)
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
metaAliasMap.set(
|
|
232
|
+
normalizeName(tagBlockMatch.name),
|
|
233
|
+
tagAttrKey(tagBlockMatch.name)
|
|
234
|
+
);
|
|
235
|
+
result.tagGroups.push(currentTagGroup);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (!contentStarted && !currentTagGroup && measureIndent(line) === 0) {
|
|
239
|
+
if (KNOWN_C4_BOOLEANS.has(trimmed.toLowerCase())) {
|
|
240
|
+
options[trimmed.toLowerCase()] = "on";
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
const optMatch = trimmed.match(OPTION_NOCOLON_RE);
|
|
244
|
+
if (optMatch) {
|
|
245
|
+
const key = optMatch[1].trim().toLowerCase();
|
|
246
|
+
if (KNOWN_C4_OPTIONS.has(key)) {
|
|
247
|
+
options[key] = optMatch[2].trim();
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (currentTagGroup && !contentStarted) {
|
|
253
|
+
const indent2 = measureIndent(line);
|
|
254
|
+
if (indent2 > 0) {
|
|
255
|
+
const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
|
|
256
|
+
const { label, color } = extractColor(
|
|
257
|
+
cleanEntry,
|
|
258
|
+
palette,
|
|
259
|
+
result.diagnostics,
|
|
260
|
+
lineNumber
|
|
261
|
+
);
|
|
262
|
+
if (isDefault) {
|
|
263
|
+
currentTagGroup.defaultValue = label;
|
|
264
|
+
} else if (currentTagGroup.entries.length === 0) {
|
|
265
|
+
currentTagGroup.defaultValue = label;
|
|
266
|
+
}
|
|
267
|
+
currentTagGroup.entries.push({
|
|
268
|
+
value: label,
|
|
269
|
+
color: color ?? AUTO_TAG_COLOR_SENTINEL,
|
|
270
|
+
lineNumber
|
|
271
|
+
});
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
currentTagGroup = null;
|
|
275
|
+
}
|
|
276
|
+
contentStarted = true;
|
|
277
|
+
currentTagGroup = null;
|
|
278
|
+
if (!sawChartType) {
|
|
279
|
+
return fail(lineNumber, 'Missing "c4" header');
|
|
280
|
+
}
|
|
281
|
+
const indent = measureIndent(line);
|
|
282
|
+
if (inDeployment) {
|
|
283
|
+
while (deployStack.length > 0) {
|
|
284
|
+
const top = deployStack[deployStack.length - 1];
|
|
285
|
+
if (top.indent < indent) break;
|
|
286
|
+
deployStack.pop();
|
|
287
|
+
}
|
|
288
|
+
if (indent === 0 && (C4_IS_A_RE.test(trimmed) || ELEMENT_RE.test(trimmed))) {
|
|
289
|
+
inDeployment = false;
|
|
290
|
+
} else {
|
|
291
|
+
const refMatch = trimmed.match(CONTAINER_REF_RE);
|
|
292
|
+
if (refMatch) {
|
|
293
|
+
const refName = refMatch[1].trim();
|
|
294
|
+
if (deployStack.length > 0) {
|
|
295
|
+
deployStack[deployStack.length - 1].node.containerRefs.push(
|
|
296
|
+
refName
|
|
297
|
+
);
|
|
298
|
+
} else {
|
|
299
|
+
pushError(
|
|
300
|
+
lineNumber,
|
|
301
|
+
`"container ${refName}" must be indented under a deployment node (e.g. under 'Prod is a deployment-node')`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const { name: nodeName, metadata } = parseC4NameAndMeta(
|
|
307
|
+
trimmed,
|
|
308
|
+
metaAliasMap
|
|
309
|
+
);
|
|
310
|
+
warnUnknownMetaKeys(
|
|
311
|
+
metadata,
|
|
312
|
+
withTagAliases(C4_REGISTRY, new Set(metaAliasMap.keys())),
|
|
313
|
+
(msg) => pushError(lineNumber, msg, "warning"),
|
|
314
|
+
nodeName
|
|
315
|
+
);
|
|
316
|
+
const shape = inferC4Shape(
|
|
317
|
+
nodeName,
|
|
318
|
+
metadata["tech"] ?? metadata["technology"]
|
|
319
|
+
);
|
|
320
|
+
const dNode = {
|
|
321
|
+
name: nodeName,
|
|
322
|
+
metadata,
|
|
323
|
+
shape,
|
|
324
|
+
children: [],
|
|
325
|
+
containerRefs: [],
|
|
326
|
+
lineNumber
|
|
327
|
+
};
|
|
328
|
+
if (deployStack.length > 0) {
|
|
329
|
+
deployStack[deployStack.length - 1].node.children.push(dNode);
|
|
330
|
+
} else {
|
|
331
|
+
result.deployment.push(dNode);
|
|
332
|
+
}
|
|
333
|
+
deployStack.push({ node: dNode, indent });
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const sectionMatch = trimmed.match(SECTION_HEADER_RE);
|
|
338
|
+
if (sectionMatch) {
|
|
339
|
+
const sectionType = sectionMatch[1].toLowerCase();
|
|
340
|
+
if (sectionType === "deployment") {
|
|
341
|
+
inDeployment = true;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const parentEntry = findParentElement(indent, stack);
|
|
345
|
+
if (parentEntry) {
|
|
346
|
+
parentEntry.element.sectionHeader = sectionType;
|
|
347
|
+
parentEntry.element.sectionHeaderLineNumber = lineNumber;
|
|
348
|
+
stack.push({
|
|
349
|
+
kind: "section",
|
|
350
|
+
sectionType,
|
|
351
|
+
parentElement: parentEntry.element,
|
|
352
|
+
indent
|
|
353
|
+
});
|
|
354
|
+
} else {
|
|
355
|
+
pushError(
|
|
356
|
+
lineNumber,
|
|
357
|
+
`"${sectionType}" must be indented under an element (e.g. under 'Web App is a container')`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
while (stack.length > 0) {
|
|
363
|
+
const top = stack[stack.length - 1];
|
|
364
|
+
if (top.indent < indent) break;
|
|
365
|
+
stack.pop();
|
|
366
|
+
}
|
|
367
|
+
const containerMatch = trimmed.match(CONTAINER_RE);
|
|
368
|
+
if (containerMatch) {
|
|
369
|
+
const groupName = containerMatch[1].trim();
|
|
370
|
+
const parentEntry = findParentElement(indent, stack);
|
|
371
|
+
if (parentEntry) {
|
|
372
|
+
const group = {
|
|
373
|
+
name: groupName,
|
|
374
|
+
children: [],
|
|
375
|
+
lineNumber
|
|
376
|
+
};
|
|
377
|
+
parentEntry.element.groups.push(group);
|
|
378
|
+
stack.push({
|
|
379
|
+
kind: "group",
|
|
380
|
+
group,
|
|
381
|
+
parentElement: parentEntry.element,
|
|
382
|
+
indent
|
|
383
|
+
});
|
|
384
|
+
} else {
|
|
385
|
+
pushError(
|
|
386
|
+
lineNumber,
|
|
387
|
+
`Group [${groupName}] must be indented under an element (e.g. under 'Web App is a container')`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
{
|
|
393
|
+
const labeledPatterns = [
|
|
394
|
+
{ re: C4_LABELED_BIDI_SYNC_RE, arrowType: "bidirectional" },
|
|
395
|
+
{ re: C4_LABELED_BIDI_ASYNC_RE, arrowType: "bidirectional-async" },
|
|
396
|
+
{ re: C4_LABELED_SYNC_RE, arrowType: "sync" },
|
|
397
|
+
{ re: C4_LABELED_ASYNC_RE, arrowType: "async" }
|
|
398
|
+
];
|
|
399
|
+
let labeledHandled = false;
|
|
400
|
+
for (const { re, arrowType } of labeledPatterns) {
|
|
401
|
+
const m = trimmed.match(re);
|
|
402
|
+
if (!m) continue;
|
|
403
|
+
const rawLabel = m[1].trim();
|
|
404
|
+
const targetBody = m[2].trim();
|
|
405
|
+
if (!rawLabel) break;
|
|
406
|
+
if (arrowType === "bidirectional" || arrowType === "bidirectional-async") {
|
|
407
|
+
const source = findParentElement(indent, stack)?.element.name ?? "?";
|
|
408
|
+
pushError(
|
|
409
|
+
lineNumber,
|
|
410
|
+
`Bidirectional arrows are no longer supported. Replace with two separate arrows:
|
|
411
|
+
-${rawLabel}-> ${targetBody}
|
|
412
|
+
${targetBody} -${rawLabel}-> ${source}`
|
|
413
|
+
);
|
|
414
|
+
labeledHandled = true;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
const labelResult = parseInArrowLabel(rawLabel, lineNumber);
|
|
418
|
+
labelResult.diagnostics.forEach((d) => result.diagnostics.push(d));
|
|
419
|
+
const label = labelResult.label;
|
|
420
|
+
let technology;
|
|
421
|
+
const targetParsed = parseC4NameAndMeta(targetBody, metaAliasMap);
|
|
422
|
+
warnUnknownMetaKeys(
|
|
423
|
+
targetParsed.metadata,
|
|
424
|
+
withTagAliases(C4_REGISTRY, new Set(metaAliasMap.keys())),
|
|
425
|
+
(msg) => pushError(lineNumber, msg, "warning"),
|
|
426
|
+
targetParsed.name
|
|
427
|
+
);
|
|
428
|
+
const target = targetParsed.name;
|
|
429
|
+
if (targetParsed.metadata["tech"]) {
|
|
430
|
+
technology = targetParsed.metadata["tech"];
|
|
431
|
+
}
|
|
432
|
+
if (targetParsed.metadata["technology"]) {
|
|
433
|
+
technology = targetParsed.metadata["technology"];
|
|
434
|
+
}
|
|
435
|
+
const rel = {
|
|
436
|
+
target: resolveNameRef(target),
|
|
437
|
+
...label !== void 0 && { label },
|
|
438
|
+
...technology !== void 0 && { technology },
|
|
439
|
+
arrowType,
|
|
440
|
+
lineNumber
|
|
441
|
+
};
|
|
442
|
+
const parentEntry = findParentElement(indent, stack);
|
|
443
|
+
if (parentEntry) {
|
|
444
|
+
parentEntry.element.relationships.push(rel);
|
|
445
|
+
} else {
|
|
446
|
+
result.relationships.push(rel);
|
|
447
|
+
}
|
|
448
|
+
labeledHandled = true;
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
if (labeledHandled) continue;
|
|
452
|
+
}
|
|
453
|
+
const relMatch = trimmed.match(RELATIONSHIP_RE);
|
|
454
|
+
if (relMatch) {
|
|
455
|
+
const arrowType = parseArrowType(relMatch[1]);
|
|
456
|
+
if (arrowType) {
|
|
457
|
+
if (arrowType === "bidirectional" || arrowType === "bidirectional-async") {
|
|
458
|
+
const arrow = relMatch[1];
|
|
459
|
+
const target2 = relMatch[2].trim();
|
|
460
|
+
const source = findParentElement(indent, stack)?.element.name ?? "?";
|
|
461
|
+
pushError(
|
|
462
|
+
lineNumber,
|
|
463
|
+
`'${arrow}' bidirectional arrows are no longer supported. Replace with two separate arrows:
|
|
464
|
+
-> ${target2}
|
|
465
|
+
${target2} -> ${source}`
|
|
466
|
+
);
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
const target = relMatch[2].trim();
|
|
470
|
+
const rel = {
|
|
471
|
+
target: resolveNameRef(target),
|
|
472
|
+
arrowType,
|
|
473
|
+
lineNumber
|
|
474
|
+
};
|
|
475
|
+
const parentEntry = findParentElement(indent, stack);
|
|
476
|
+
if (parentEntry) {
|
|
477
|
+
parentEntry.element.relationships.push(rel);
|
|
478
|
+
} else {
|
|
479
|
+
result.relationships.push(rel);
|
|
480
|
+
}
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
const isATypeMatch = trimmed.match(C4_IS_A_RE);
|
|
485
|
+
if (isATypeMatch) {
|
|
486
|
+
let namePart = isATypeMatch[1].trim();
|
|
487
|
+
const rawType = isATypeMatch[2].toLowerCase();
|
|
488
|
+
let remainder = isATypeMatch[3];
|
|
489
|
+
const asPostfix = remainder.match(
|
|
490
|
+
/^(.*?)\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*(\|.*)?$/
|
|
491
|
+
);
|
|
492
|
+
if (asPostfix) {
|
|
493
|
+
nameAliasMap.set(asPostfix[2], namePart);
|
|
494
|
+
remainder = (asPostfix[1] + (asPostfix[3] ?? "")).trim();
|
|
495
|
+
} else {
|
|
496
|
+
const asInName = namePart.match(
|
|
497
|
+
/^(.*?)\s+as\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*$/
|
|
498
|
+
);
|
|
499
|
+
if (asInName) {
|
|
500
|
+
namePart = asInName[1].trim();
|
|
501
|
+
nameAliasMap.set(asInName[2], namePart);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
let elementType;
|
|
505
|
+
let explicitShape = null;
|
|
506
|
+
if (rawType === "external") {
|
|
507
|
+
elementType = "system";
|
|
508
|
+
explicitShape = "external";
|
|
509
|
+
} else if (rawType === "database") {
|
|
510
|
+
elementType = "container";
|
|
511
|
+
explicitShape = "database";
|
|
512
|
+
} else {
|
|
513
|
+
elementType = rawType;
|
|
514
|
+
}
|
|
515
|
+
const remainderTrimmed = remainder.trim();
|
|
516
|
+
let metaTail = remainderTrimmed;
|
|
517
|
+
const remainderIsA = remainderTrimmed.match(
|
|
518
|
+
/^\s*is\s+a(?:n)?\s+(\w+)\s*(.*)$/i
|
|
519
|
+
);
|
|
520
|
+
if (remainderIsA) {
|
|
521
|
+
const shapeName = remainderIsA[1].toLowerCase();
|
|
522
|
+
if (VALID_SHAPES.has(shapeName)) {
|
|
523
|
+
explicitShape = shapeName;
|
|
524
|
+
} else {
|
|
525
|
+
pushError(
|
|
526
|
+
lineNumber,
|
|
527
|
+
`Unknown shape "${remainderIsA[1]}". Valid shapes: ${[...VALID_SHAPES].join(", ")}`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
metaTail = remainderIsA[2].trim();
|
|
531
|
+
}
|
|
532
|
+
const nameIsAMatch = namePart.match(IS_A_RE);
|
|
533
|
+
if (nameIsAMatch) {
|
|
534
|
+
const shapeName = nameIsAMatch[1].toLowerCase();
|
|
535
|
+
if (VALID_SHAPES.has(shapeName)) {
|
|
536
|
+
explicitShape = shapeName;
|
|
537
|
+
} else {
|
|
538
|
+
pushError(
|
|
539
|
+
lineNumber,
|
|
540
|
+
`Unknown shape "${nameIsAMatch[1]}". Valid shapes: ${[...VALID_SHAPES].join(", ")}`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
namePart = namePart.substring(0, nameIsAMatch.index).trim();
|
|
544
|
+
}
|
|
545
|
+
const metadata = parseC4MetaTail(metaTail, metaAliasMap);
|
|
546
|
+
const shape = explicitShape ?? inferC4Shape(namePart, metadata["tech"] ?? metadata["technology"]);
|
|
547
|
+
let isADescription;
|
|
548
|
+
if ("description" in metadata) {
|
|
549
|
+
const descVal = metadata["description"].trim();
|
|
550
|
+
if (descVal) isADescription = [descVal];
|
|
551
|
+
delete metadata["description"];
|
|
552
|
+
}
|
|
553
|
+
const element = {
|
|
554
|
+
name: namePart,
|
|
555
|
+
type: elementType,
|
|
556
|
+
shape,
|
|
557
|
+
metadata,
|
|
558
|
+
...isADescription !== void 0 && { description: isADescription },
|
|
559
|
+
children: [],
|
|
560
|
+
groups: [],
|
|
561
|
+
relationships: [],
|
|
562
|
+
lineNumber
|
|
563
|
+
};
|
|
564
|
+
elementMetadata.set(element, metadata);
|
|
565
|
+
if (isADescription !== void 0)
|
|
566
|
+
elementDescription.set(element, isADescription);
|
|
567
|
+
const key = normalizeName(namePart);
|
|
568
|
+
const existingLine = knownNames.get(key);
|
|
569
|
+
if (existingLine !== void 0) {
|
|
570
|
+
pushError(
|
|
571
|
+
lineNumber,
|
|
572
|
+
`Duplicate element name "${namePart}" (first defined on line ${existingLine})`
|
|
573
|
+
);
|
|
574
|
+
} else {
|
|
575
|
+
knownNames.set(key, lineNumber);
|
|
576
|
+
}
|
|
577
|
+
attachElement(element, indent, stack, result);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
const elementMatch = trimmed.match(ELEMENT_RE);
|
|
581
|
+
if (elementMatch) {
|
|
582
|
+
const elementType = elementMatch[1].toLowerCase();
|
|
583
|
+
const nameAndRest = elementMatch[2];
|
|
584
|
+
const parsed = parseC4NameAndMeta(nameAndRest, metaAliasMap);
|
|
585
|
+
warnUnknownMetaKeys(
|
|
586
|
+
parsed.metadata,
|
|
587
|
+
withTagAliases(C4_REGISTRY, new Set(metaAliasMap.keys())),
|
|
588
|
+
(msg) => pushError(lineNumber, msg, "warning"),
|
|
589
|
+
parsed.name
|
|
590
|
+
);
|
|
591
|
+
let namePart = parsed.name;
|
|
592
|
+
const metadata = parsed.metadata;
|
|
593
|
+
let explicitShape = null;
|
|
594
|
+
const isAMatch = namePart.match(IS_A_RE);
|
|
595
|
+
if (isAMatch) {
|
|
596
|
+
const shapeName = isAMatch[1].toLowerCase();
|
|
597
|
+
if (VALID_SHAPES.has(shapeName)) {
|
|
598
|
+
explicitShape = shapeName;
|
|
599
|
+
} else {
|
|
600
|
+
pushError(
|
|
601
|
+
lineNumber,
|
|
602
|
+
`Unknown shape "${isAMatch[1]}". Valid shapes: ${[...VALID_SHAPES].join(", ")}`
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
namePart = namePart.substring(0, isAMatch.index).trim();
|
|
606
|
+
}
|
|
607
|
+
pushError(
|
|
608
|
+
lineNumber,
|
|
609
|
+
`'${elementMatch[1]} ${namePart}' prefix syntax is no longer supported \u2014 use '${namePart} is a ${elementType}' instead`
|
|
610
|
+
);
|
|
611
|
+
const shape = explicitShape ?? inferC4Shape(namePart, metadata["tech"] ?? metadata["technology"]);
|
|
612
|
+
let prefixDescription;
|
|
613
|
+
if ("description" in metadata) {
|
|
614
|
+
const descVal = metadata["description"].trim();
|
|
615
|
+
if (descVal) prefixDescription = [descVal];
|
|
616
|
+
delete metadata["description"];
|
|
617
|
+
}
|
|
618
|
+
const element = {
|
|
619
|
+
name: namePart,
|
|
620
|
+
type: elementType,
|
|
621
|
+
shape,
|
|
622
|
+
metadata,
|
|
623
|
+
...prefixDescription !== void 0 && {
|
|
624
|
+
description: prefixDescription
|
|
625
|
+
},
|
|
626
|
+
children: [],
|
|
627
|
+
groups: [],
|
|
628
|
+
relationships: [],
|
|
629
|
+
lineNumber
|
|
630
|
+
};
|
|
631
|
+
elementMetadata.set(element, metadata);
|
|
632
|
+
if (prefixDescription !== void 0)
|
|
633
|
+
elementDescription.set(element, prefixDescription);
|
|
634
|
+
const key = normalizeName(namePart);
|
|
635
|
+
const existingLine = knownNames.get(key);
|
|
636
|
+
if (existingLine !== void 0) {
|
|
637
|
+
pushError(
|
|
638
|
+
lineNumber,
|
|
639
|
+
`Duplicate element name "${namePart}" (first defined on line ${existingLine})`
|
|
640
|
+
);
|
|
641
|
+
} else {
|
|
642
|
+
knownNames.set(key, lineNumber);
|
|
643
|
+
}
|
|
644
|
+
attachElement(element, indent, stack, result);
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
const metadataMatch = trimmed.match(METADATA_RE);
|
|
648
|
+
if (metadataMatch && !ELEMENT_RE.test(trimmed)) {
|
|
649
|
+
const parentEntry = findParentElement(indent, stack);
|
|
650
|
+
if (parentEntry) {
|
|
651
|
+
const rawKey = metadataMatch[1].trim().toLowerCase();
|
|
652
|
+
if (rawKey === "import") {
|
|
653
|
+
parentEntry.element.importPath = metadataMatch[2].trim();
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
const key = metaAliasMap.get(rawKey) ?? rawKey;
|
|
657
|
+
const value = metadataMatch[2].trim();
|
|
658
|
+
if (key === "description") {
|
|
659
|
+
let desc = elementDescription.get(parentEntry.element);
|
|
660
|
+
if (!desc) {
|
|
661
|
+
desc = [];
|
|
662
|
+
elementDescription.set(parentEntry.element, desc);
|
|
663
|
+
parentEntry.element.description = desc;
|
|
664
|
+
}
|
|
665
|
+
desc.push(value);
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
const meta = elementMetadata.get(parentEntry.element);
|
|
669
|
+
if (meta) meta[key] = value;
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
const firstWord = trimmed.split(/\s+/)[0].toLowerCase();
|
|
674
|
+
if (firstWord.length > 3) {
|
|
675
|
+
const hint = suggest(firstWord, [...VALID_ELEMENT_TYPES]);
|
|
676
|
+
if (hint) {
|
|
677
|
+
pushError(lineNumber, `Unknown keyword "${firstWord}". ${hint}`);
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
const parent = findParentElement(indent, stack);
|
|
682
|
+
const descResult = tryStripDescriptionKeyword(trimmed);
|
|
683
|
+
if (parent && descResult.isKeyword && !descResult.needsColon) {
|
|
684
|
+
let desc = elementDescription.get(parent.element);
|
|
685
|
+
if (!desc) {
|
|
686
|
+
desc = [];
|
|
687
|
+
elementDescription.set(parent.element, desc);
|
|
688
|
+
parent.element.description = desc;
|
|
689
|
+
}
|
|
690
|
+
desc.push(descResult.text);
|
|
691
|
+
} else {
|
|
692
|
+
pushError(
|
|
693
|
+
lineNumber,
|
|
694
|
+
`Unexpected content: "${trimmed}". Expected an element ('Web App is a container'), a relationship ('-Uses-> Database'), indented metadata ('tech: React'), or a group ('[Backend]'). (\xA78)`
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
finalizeAutoTagColors(result.tagGroups, palette);
|
|
699
|
+
validateRelationshipTargets(result, knownNames, pushError);
|
|
700
|
+
validateDeploymentRefs(result, knownNames, pushError);
|
|
701
|
+
validateTagGroupNames(
|
|
702
|
+
result.tagGroups,
|
|
703
|
+
(line, msg) => pushError(line, msg, "warning")
|
|
704
|
+
);
|
|
705
|
+
return result;
|
|
706
|
+
}
|
|
707
|
+
function findParentElement(indent, stack) {
|
|
708
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
709
|
+
const entry = stack[i];
|
|
710
|
+
if (entry.indent >= indent) continue;
|
|
711
|
+
if (entry.kind === "element") return entry;
|
|
712
|
+
if (entry.kind === "group") {
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
if (entry.kind === "section") {
|
|
716
|
+
return {
|
|
717
|
+
kind: "element",
|
|
718
|
+
element: entry.parentElement,
|
|
719
|
+
indent: entry.indent
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
function attachElement(element, indent, stack, result) {
|
|
726
|
+
let attached = false;
|
|
727
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
728
|
+
const entry = stack[i];
|
|
729
|
+
if (entry.indent >= indent) continue;
|
|
730
|
+
if (entry.kind === "group") {
|
|
731
|
+
entry.group.children.push(element);
|
|
732
|
+
attached = true;
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
if (entry.kind === "section") {
|
|
736
|
+
entry.parentElement.children.push(element);
|
|
737
|
+
attached = true;
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
if (entry.kind === "element") {
|
|
741
|
+
entry.element.children.push(element);
|
|
742
|
+
attached = true;
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (!attached) {
|
|
747
|
+
result.elements.push(element);
|
|
748
|
+
}
|
|
749
|
+
stack.push({ kind: "element", element, indent });
|
|
750
|
+
}
|
|
751
|
+
function validateRelationshipTargets(result, knownNames, pushWarning) {
|
|
752
|
+
const allNames = Array.from(knownNames.keys());
|
|
753
|
+
function walkRels(elements) {
|
|
754
|
+
for (const el of elements) {
|
|
755
|
+
for (const rel of el.relationships) {
|
|
756
|
+
if (!knownNames.has(normalizeName(rel.target))) {
|
|
757
|
+
let msg = `Relationship target "${rel.target}" not found \u2014 declare it first (e.g. '${rel.target} is a container'), then reference it in the arrow`;
|
|
758
|
+
const hint = suggest(normalizeName(rel.target), allNames);
|
|
759
|
+
if (hint) msg += `. ${hint}`;
|
|
760
|
+
pushWarning(rel.lineNumber, msg, "warning");
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
walkRels(el.children);
|
|
764
|
+
for (const g of el.groups) {
|
|
765
|
+
walkRels(g.children);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
walkRels(result.elements);
|
|
770
|
+
for (const rel of result.relationships) {
|
|
771
|
+
if (!knownNames.has(normalizeName(rel.target))) {
|
|
772
|
+
let msg = `Relationship target "${rel.target}" not found`;
|
|
773
|
+
const hint = suggest(normalizeName(rel.target), allNames);
|
|
774
|
+
if (hint) msg += `. ${hint}`;
|
|
775
|
+
pushWarning(rel.lineNumber, msg, "warning");
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
function validateDeploymentRefs(result, knownNames, pushWarning) {
|
|
780
|
+
const allNames = Array.from(knownNames.keys());
|
|
781
|
+
function walkDeploy(nodes) {
|
|
782
|
+
for (const node of nodes) {
|
|
783
|
+
for (const ref of node.containerRefs) {
|
|
784
|
+
if (!knownNames.has(normalizeName(ref))) {
|
|
785
|
+
let msg = `Deployment reference "container ${ref}" not found \u2014 declare the container element first, then reference it under the deployment node`;
|
|
786
|
+
const hint = suggest(normalizeName(ref), allNames);
|
|
787
|
+
if (hint) msg += `. ${hint}`;
|
|
788
|
+
pushWarning(node.lineNumber, msg, "warning");
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
walkDeploy(node.children);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
walkDeploy(result.deployment);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
export {
|
|
798
|
+
parseC4
|
|
799
|
+
};
|
|
800
|
+
//# sourceMappingURL=chunk-2DXMZRWJ.js.map
|