schematex 0.1.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/LICENSE +661 -0
- package/README.md +379 -0
- package/dist/chunk-2MQWZ2XY.cjs +453 -0
- package/dist/chunk-2MQWZ2XY.cjs.map +1 -0
- package/dist/chunk-2UKC6ZCY.cjs +1803 -0
- package/dist/chunk-2UKC6ZCY.cjs.map +1 -0
- package/dist/chunk-34X3ZJ6E.cjs +783 -0
- package/dist/chunk-34X3ZJ6E.cjs.map +1 -0
- package/dist/chunk-3FTUWAXK.cjs +1220 -0
- package/dist/chunk-3FTUWAXK.cjs.map +1 -0
- package/dist/chunk-3J7TFUOC.js +745 -0
- package/dist/chunk-3J7TFUOC.js.map +1 -0
- package/dist/chunk-47ZC6EMJ.js +1009 -0
- package/dist/chunk-47ZC6EMJ.js.map +1 -0
- package/dist/chunk-4DBRNOPA.cjs +750 -0
- package/dist/chunk-4DBRNOPA.cjs.map +1 -0
- package/dist/chunk-4G7ZIBHN.js +778 -0
- package/dist/chunk-4G7ZIBHN.js.map +1 -0
- package/dist/chunk-5C7DPDHQ.js +1321 -0
- package/dist/chunk-5C7DPDHQ.js.map +1 -0
- package/dist/chunk-ADOXGKAK.js +1251 -0
- package/dist/chunk-ADOXGKAK.js.map +1 -0
- package/dist/chunk-BE5HNDA5.cjs +874 -0
- package/dist/chunk-BE5HNDA5.cjs.map +1 -0
- package/dist/chunk-CZRM7LT7.js +889 -0
- package/dist/chunk-CZRM7LT7.js.map +1 -0
- package/dist/chunk-D4JTSPOL.js +1795 -0
- package/dist/chunk-D4JTSPOL.js.map +1 -0
- package/dist/chunk-DS47NTWZ.cjs +1034 -0
- package/dist/chunk-DS47NTWZ.cjs.map +1 -0
- package/dist/chunk-FDLZEKEB.js +449 -0
- package/dist/chunk-FDLZEKEB.js.map +1 -0
- package/dist/chunk-FGPTCDUT.cjs +1851 -0
- package/dist/chunk-FGPTCDUT.cjs.map +1 -0
- package/dist/chunk-HDKDQAEQ.cjs +86 -0
- package/dist/chunk-HDKDQAEQ.cjs.map +1 -0
- package/dist/chunk-IX554O5K.js +346 -0
- package/dist/chunk-IX554O5K.js.map +1 -0
- package/dist/chunk-KLJEK547.js +71 -0
- package/dist/chunk-KLJEK547.js.map +1 -0
- package/dist/chunk-LMFSHK45.js +1028 -0
- package/dist/chunk-LMFSHK45.js.map +1 -0
- package/dist/chunk-MDICUK6F.cjs +1258 -0
- package/dist/chunk-MDICUK6F.cjs.map +1 -0
- package/dist/chunk-N7KOXOMX.cjs +363 -0
- package/dist/chunk-N7KOXOMX.cjs.map +1 -0
- package/dist/chunk-NYCIK4SU.cjs +775 -0
- package/dist/chunk-NYCIK4SU.cjs.map +1 -0
- package/dist/chunk-PDPHRZZT.js +770 -0
- package/dist/chunk-PDPHRZZT.js.map +1 -0
- package/dist/chunk-ROFLJ74T.js +1212 -0
- package/dist/chunk-ROFLJ74T.js.map +1 -0
- package/dist/chunk-S6BK5DB6.cjs +845 -0
- package/dist/chunk-S6BK5DB6.cjs.map +1 -0
- package/dist/chunk-U4I37IBN.js +874 -0
- package/dist/chunk-U4I37IBN.js.map +1 -0
- package/dist/chunk-U5GGE6PJ.js +839 -0
- package/dist/chunk-U5GGE6PJ.js.map +1 -0
- package/dist/chunk-UHLYS3W5.cjs +1015 -0
- package/dist/chunk-UHLYS3W5.cjs.map +1 -0
- package/dist/chunk-URSKIHSY.cjs +881 -0
- package/dist/chunk-URSKIHSY.cjs.map +1 -0
- package/dist/chunk-V6WO7RK7.cjs +1056 -0
- package/dist/chunk-V6WO7RK7.cjs.map +1 -0
- package/dist/chunk-VFQCTXOX.js +869 -0
- package/dist/chunk-VFQCTXOX.js.map +1 -0
- package/dist/chunk-XQ52ICHU.cjs +895 -0
- package/dist/chunk-XQ52ICHU.cjs.map +1 -0
- package/dist/chunk-XX4BKS7Y.js +1051 -0
- package/dist/chunk-XX4BKS7Y.js.map +1 -0
- package/dist/chunk-XXU36667.js +1844 -0
- package/dist/chunk-XXU36667.js.map +1 -0
- package/dist/chunk-ZX7QKZK2.cjs +1326 -0
- package/dist/chunk-ZX7QKZK2.cjs.map +1 -0
- package/dist/diagrams/blockdiagram/index.cjs +25 -0
- package/dist/diagrams/blockdiagram/index.cjs.map +1 -0
- package/dist/diagrams/blockdiagram/index.d.cts +67 -0
- package/dist/diagrams/blockdiagram/index.d.ts +67 -0
- package/dist/diagrams/blockdiagram/index.js +4 -0
- package/dist/diagrams/blockdiagram/index.js.map +1 -0
- package/dist/diagrams/circuit/index.cjs +34 -0
- package/dist/diagrams/circuit/index.cjs.map +1 -0
- package/dist/diagrams/circuit/index.d.cts +138 -0
- package/dist/diagrams/circuit/index.d.ts +138 -0
- package/dist/diagrams/circuit/index.js +5 -0
- package/dist/diagrams/circuit/index.js.map +1 -0
- package/dist/diagrams/ecomap/index.cjs +30 -0
- package/dist/diagrams/ecomap/index.cjs.map +1 -0
- package/dist/diagrams/ecomap/index.d.cts +15 -0
- package/dist/diagrams/ecomap/index.d.ts +15 -0
- package/dist/diagrams/ecomap/index.js +5 -0
- package/dist/diagrams/ecomap/index.js.map +1 -0
- package/dist/diagrams/entity/index.cjs +26 -0
- package/dist/diagrams/entity/index.cjs.map +1 -0
- package/dist/diagrams/entity/index.d.cts +54 -0
- package/dist/diagrams/entity/index.d.ts +54 -0
- package/dist/diagrams/entity/index.js +5 -0
- package/dist/diagrams/entity/index.js.map +1 -0
- package/dist/diagrams/fishbone/index.cjs +34 -0
- package/dist/diagrams/fishbone/index.cjs.map +1 -0
- package/dist/diagrams/fishbone/index.d.cts +185 -0
- package/dist/diagrams/fishbone/index.d.ts +185 -0
- package/dist/diagrams/fishbone/index.js +5 -0
- package/dist/diagrams/fishbone/index.js.map +1 -0
- package/dist/diagrams/flowchart/index.cjs +34 -0
- package/dist/diagrams/flowchart/index.cjs.map +1 -0
- package/dist/diagrams/flowchart/index.d.cts +2 -0
- package/dist/diagrams/flowchart/index.d.ts +2 -0
- package/dist/diagrams/flowchart/index.js +5 -0
- package/dist/diagrams/flowchart/index.js.map +1 -0
- package/dist/diagrams/genogram/index.cjs +38 -0
- package/dist/diagrams/genogram/index.cjs.map +1 -0
- package/dist/diagrams/genogram/index.d.cts +20 -0
- package/dist/diagrams/genogram/index.d.ts +20 -0
- package/dist/diagrams/genogram/index.js +5 -0
- package/dist/diagrams/genogram/index.js.map +1 -0
- package/dist/diagrams/ladder/index.cjs +26 -0
- package/dist/diagrams/ladder/index.cjs.map +1 -0
- package/dist/diagrams/ladder/index.d.cts +49 -0
- package/dist/diagrams/ladder/index.d.ts +49 -0
- package/dist/diagrams/ladder/index.js +5 -0
- package/dist/diagrams/ladder/index.js.map +1 -0
- package/dist/diagrams/logic/index.cjs +26 -0
- package/dist/diagrams/logic/index.cjs.map +1 -0
- package/dist/diagrams/logic/index.d.cts +73 -0
- package/dist/diagrams/logic/index.d.ts +73 -0
- package/dist/diagrams/logic/index.js +5 -0
- package/dist/diagrams/logic/index.js.map +1 -0
- package/dist/diagrams/orgchart/index.cjs +30 -0
- package/dist/diagrams/orgchart/index.cjs.map +1 -0
- package/dist/diagrams/orgchart/index.d.cts +100 -0
- package/dist/diagrams/orgchart/index.d.ts +100 -0
- package/dist/diagrams/orgchart/index.js +5 -0
- package/dist/diagrams/orgchart/index.js.map +1 -0
- package/dist/diagrams/pedigree/index.cjs +30 -0
- package/dist/diagrams/pedigree/index.cjs.map +1 -0
- package/dist/diagrams/pedigree/index.d.cts +15 -0
- package/dist/diagrams/pedigree/index.d.ts +15 -0
- package/dist/diagrams/pedigree/index.js +5 -0
- package/dist/diagrams/pedigree/index.js.map +1 -0
- package/dist/diagrams/phylo/index.cjs +30 -0
- package/dist/diagrams/phylo/index.cjs.map +1 -0
- package/dist/diagrams/phylo/index.d.cts +32 -0
- package/dist/diagrams/phylo/index.d.ts +32 -0
- package/dist/diagrams/phylo/index.js +5 -0
- package/dist/diagrams/phylo/index.js.map +1 -0
- package/dist/diagrams/sld/index.cjs +26 -0
- package/dist/diagrams/sld/index.cjs.map +1 -0
- package/dist/diagrams/sld/index.d.cts +58 -0
- package/dist/diagrams/sld/index.d.ts +58 -0
- package/dist/diagrams/sld/index.js +5 -0
- package/dist/diagrams/sld/index.js.map +1 -0
- package/dist/diagrams/sociogram/index.cjs +26 -0
- package/dist/diagrams/sociogram/index.cjs.map +1 -0
- package/dist/diagrams/sociogram/index.d.cts +76 -0
- package/dist/diagrams/sociogram/index.d.ts +76 -0
- package/dist/diagrams/sociogram/index.js +5 -0
- package/dist/diagrams/sociogram/index.js.map +1 -0
- package/dist/diagrams/timing/index.cjs +21 -0
- package/dist/diagrams/timing/index.cjs.map +1 -0
- package/dist/diagrams/timing/index.d.cts +9 -0
- package/dist/diagrams/timing/index.d.ts +9 -0
- package/dist/diagrams/timing/index.js +4 -0
- package/dist/diagrams/timing/index.js.map +1 -0
- package/dist/diagrams/venn/index.cjs +38 -0
- package/dist/diagrams/venn/index.cjs.map +1 -0
- package/dist/diagrams/venn/index.d.cts +69 -0
- package/dist/diagrams/venn/index.d.ts +69 -0
- package/dist/diagrams/venn/index.js +5 -0
- package/dist/diagrams/venn/index.js.map +1 -0
- package/dist/index-BSlza1YY.d.ts +150 -0
- package/dist/index-BXefHVce.d.cts +150 -0
- package/dist/index.cjs +2033 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +1944 -0
- package/dist/index.js.map +1 -0
- package/dist/types-DqfcYkcY.d.cts +741 -0
- package/dist/types-DqfcYkcY.d.ts +741 -0
- package/package.json +163 -0
|
@@ -0,0 +1,1051 @@
|
|
|
1
|
+
import { resolveBaseTheme } from './chunk-IX554O5K.js';
|
|
2
|
+
import { title, desc, el, defs, text, rect, path, group, svgRoot, escapeXml, polygon, circle } from './chunk-KLJEK547.js';
|
|
3
|
+
|
|
4
|
+
// src/diagrams/entity/parser.ts
|
|
5
|
+
var EntityParseError = class extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "EntityParseError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var ENTITY_TYPE_ALIAS = {
|
|
12
|
+
corp: "corp",
|
|
13
|
+
corporation: "corp",
|
|
14
|
+
inc: "corp",
|
|
15
|
+
llc: "llc",
|
|
16
|
+
llp: "llc",
|
|
17
|
+
gmbh: "llc",
|
|
18
|
+
bv: "llc",
|
|
19
|
+
lp: "lp",
|
|
20
|
+
lllp: "lp",
|
|
21
|
+
fund: "lp",
|
|
22
|
+
trust: "trust",
|
|
23
|
+
individual: "individual",
|
|
24
|
+
person: "individual",
|
|
25
|
+
foundation: "foundation",
|
|
26
|
+
npo: "foundation",
|
|
27
|
+
disregarded: "disregarded",
|
|
28
|
+
branch: "disregarded",
|
|
29
|
+
pool: "pool",
|
|
30
|
+
placeholder: "placeholder",
|
|
31
|
+
tbf: "placeholder"
|
|
32
|
+
};
|
|
33
|
+
function stripComment(s) {
|
|
34
|
+
let out = "";
|
|
35
|
+
let inQuote = false;
|
|
36
|
+
for (const ch of s) {
|
|
37
|
+
if (ch === '"') inQuote = !inQuote;
|
|
38
|
+
if (ch === "#" && !inQuote) break;
|
|
39
|
+
out += ch;
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
function stripQuotes(v) {
|
|
44
|
+
const t = v.trim();
|
|
45
|
+
if (t.length >= 2 && t.startsWith('"') && t.endsWith('"')) return t.slice(1, -1);
|
|
46
|
+
return t;
|
|
47
|
+
}
|
|
48
|
+
function splitTopLevelCommas(inside) {
|
|
49
|
+
const parts = [];
|
|
50
|
+
let cur = "";
|
|
51
|
+
let inQuote = false;
|
|
52
|
+
let depth = 0;
|
|
53
|
+
for (const ch of inside) {
|
|
54
|
+
if (ch === '"') inQuote = !inQuote;
|
|
55
|
+
if (!inQuote) {
|
|
56
|
+
if (ch === "[") depth++;
|
|
57
|
+
else if (ch === "]") depth--;
|
|
58
|
+
}
|
|
59
|
+
if (ch === "," && !inQuote && depth === 0) {
|
|
60
|
+
parts.push(cur);
|
|
61
|
+
cur = "";
|
|
62
|
+
} else {
|
|
63
|
+
cur += ch;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (cur.trim()) parts.push(cur);
|
|
67
|
+
return parts;
|
|
68
|
+
}
|
|
69
|
+
function parseProps(inside) {
|
|
70
|
+
const out = {};
|
|
71
|
+
for (const p of splitTopLevelCommas(inside)) {
|
|
72
|
+
const s = p.trim();
|
|
73
|
+
if (!s) continue;
|
|
74
|
+
const colon = s.indexOf(":");
|
|
75
|
+
if (colon < 0) throw new EntityParseError(`Invalid property "${s}" (missing ":")`);
|
|
76
|
+
const k = s.slice(0, colon).trim().toLowerCase();
|
|
77
|
+
const v = s.slice(colon + 1).trim();
|
|
78
|
+
out[k] = v;
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
function parseIdList(v) {
|
|
83
|
+
const t = v.trim();
|
|
84
|
+
const inner = t.startsWith("[") && t.endsWith("]") ? t.slice(1, -1) : t;
|
|
85
|
+
return inner.split(",").map((s) => s.trim()).filter(Boolean);
|
|
86
|
+
}
|
|
87
|
+
function joinBrackets(lines) {
|
|
88
|
+
const out = [];
|
|
89
|
+
let buf = "";
|
|
90
|
+
let depth = 0;
|
|
91
|
+
for (const raw of lines) {
|
|
92
|
+
const line = stripComment(raw);
|
|
93
|
+
if (!line.trim() && depth === 0) {
|
|
94
|
+
if (buf) {
|
|
95
|
+
out.push(buf);
|
|
96
|
+
buf = "";
|
|
97
|
+
}
|
|
98
|
+
out.push("");
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const ch of line) {
|
|
102
|
+
if (ch === "[") depth++;
|
|
103
|
+
else if (ch === "]") depth = Math.max(0, depth - 1);
|
|
104
|
+
}
|
|
105
|
+
buf = buf ? buf + " " + line.trim() : line;
|
|
106
|
+
if (depth === 0) {
|
|
107
|
+
out.push(buf);
|
|
108
|
+
buf = "";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (buf) out.push(buf);
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
var EDGE_OPS = [
|
|
115
|
+
// Order matters: longer tokens first
|
|
116
|
+
{ token: "==>", op: "voting" },
|
|
117
|
+
{ token: "-.->", op: "pool" },
|
|
118
|
+
{ token: "-~->", op: "license" },
|
|
119
|
+
{ token: "-->", op: "distribution" },
|
|
120
|
+
{ token: "->", op: "ownership" }
|
|
121
|
+
];
|
|
122
|
+
function matchEdgeOp(line) {
|
|
123
|
+
for (const { token, op } of EDGE_OPS) {
|
|
124
|
+
const idx = findEdgeToken(line, token);
|
|
125
|
+
if (idx >= 0) return { op, idx, len: token.length };
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
function findEdgeToken(line, token) {
|
|
130
|
+
let inQuote = false;
|
|
131
|
+
let depth = 0;
|
|
132
|
+
for (let i = 0; i < line.length; i++) {
|
|
133
|
+
const ch = line[i];
|
|
134
|
+
if (ch === '"') inQuote = !inQuote;
|
|
135
|
+
if (!inQuote) {
|
|
136
|
+
if (ch === "[") depth++;
|
|
137
|
+
else if (ch === "]") depth = Math.max(0, depth - 1);
|
|
138
|
+
}
|
|
139
|
+
if (!inQuote && depth === 0 && line.startsWith(token, i)) {
|
|
140
|
+
return i;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return -1;
|
|
144
|
+
}
|
|
145
|
+
function parseEntityDSL(text2) {
|
|
146
|
+
const rawLines = text2.split("\n").map((l) => l.replace(/\r$/, ""));
|
|
147
|
+
const lines = joinBrackets(rawLines);
|
|
148
|
+
let title2;
|
|
149
|
+
const entities = [];
|
|
150
|
+
const entityMap = /* @__PURE__ */ new Map();
|
|
151
|
+
const edges = [];
|
|
152
|
+
const jurisdictions = [];
|
|
153
|
+
const jurisdictionMap = /* @__PURE__ */ new Map();
|
|
154
|
+
const clusters = [];
|
|
155
|
+
let clusterCounter = 0;
|
|
156
|
+
for (const raw of lines) {
|
|
157
|
+
const line = raw.trim();
|
|
158
|
+
if (!line) continue;
|
|
159
|
+
if (/^entity-structure\b/i.test(line)) {
|
|
160
|
+
const m = line.match(/"([^"]*)"/);
|
|
161
|
+
if (m) title2 = m[1];
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const jurMatch = line.match(
|
|
165
|
+
/^jurisdiction\s+([A-Za-z]{2,3})\s+"([^"]*)"(?:\s*\[([^\]]*)\])?\s*$/i
|
|
166
|
+
);
|
|
167
|
+
if (jurMatch) {
|
|
168
|
+
const code = jurMatch[1].toUpperCase();
|
|
169
|
+
const name = jurMatch[2];
|
|
170
|
+
const props = jurMatch[3] ? parseProps(jurMatch[3]) : {};
|
|
171
|
+
const def = { code, name };
|
|
172
|
+
if (props.color) def.color = stripQuotes(props.color);
|
|
173
|
+
jurisdictions.push(def);
|
|
174
|
+
jurisdictionMap.set(code, def);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const clusterMatch = line.match(/^cluster\s+"([^"]*)"(?:\s*\[([^\]]*)\])?\s*$/i);
|
|
178
|
+
if (clusterMatch) {
|
|
179
|
+
const label = clusterMatch[1];
|
|
180
|
+
const props = clusterMatch[2] ? parseProps(clusterMatch[2]) : {};
|
|
181
|
+
const def = {
|
|
182
|
+
id: `cluster-${++clusterCounter}`,
|
|
183
|
+
label,
|
|
184
|
+
members: props.members ? parseIdList(props.members) : []
|
|
185
|
+
};
|
|
186
|
+
if (props.color) def.color = stripQuotes(props.color);
|
|
187
|
+
clusters.push(def);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const entityMatch = line.match(
|
|
191
|
+
/^entity\s+([A-Za-z][A-Za-z0-9_-]*)\s+"([^"]*)"\s+([a-zA-Z]+)(?:@([A-Za-z]{2,3}))?(?:\s*\[([^\]]*)\])?\s*$/
|
|
192
|
+
);
|
|
193
|
+
if (entityMatch) {
|
|
194
|
+
const id = entityMatch[1];
|
|
195
|
+
const name = entityMatch[2];
|
|
196
|
+
const typeStr = entityMatch[3].toLowerCase();
|
|
197
|
+
const jurisdictionCode = entityMatch[4]?.toUpperCase();
|
|
198
|
+
const propsStr = entityMatch[5];
|
|
199
|
+
const entityType = ENTITY_TYPE_ALIAS[typeStr];
|
|
200
|
+
if (!entityType) {
|
|
201
|
+
throw new EntityParseError(`Unknown entity type "${typeStr}" for "${id}"`);
|
|
202
|
+
}
|
|
203
|
+
if (entityMap.has(id)) {
|
|
204
|
+
throw new EntityParseError(`Duplicate entity id "${id}"`);
|
|
205
|
+
}
|
|
206
|
+
const node = { id, name, entityType };
|
|
207
|
+
if (jurisdictionCode) node.jurisdiction = jurisdictionCode;
|
|
208
|
+
if (propsStr) {
|
|
209
|
+
const props = parseProps(propsStr);
|
|
210
|
+
if (props.status) node.status = stripQuotes(props.status);
|
|
211
|
+
if (props.tax) node.taxClass = stripQuotes(props.tax);
|
|
212
|
+
if (props.role) node.role = stripQuotes(props.role);
|
|
213
|
+
if (props.note) node.note = stripQuotes(props.note);
|
|
214
|
+
if (props.est) node.formationDate = stripQuotes(props.est);
|
|
215
|
+
const reserved = /* @__PURE__ */ new Set(["status", "tax", "role", "note", "est"]);
|
|
216
|
+
const extras = {};
|
|
217
|
+
for (const k of Object.keys(props)) {
|
|
218
|
+
if (!reserved.has(k)) extras[k] = stripQuotes(props[k]);
|
|
219
|
+
}
|
|
220
|
+
if (Object.keys(extras).length) node.properties = extras;
|
|
221
|
+
}
|
|
222
|
+
entities.push(node);
|
|
223
|
+
entityMap.set(id, node);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const edgeInfo = matchEdgeOp(line);
|
|
227
|
+
if (edgeInfo) {
|
|
228
|
+
const left = line.slice(0, edgeInfo.idx).trim();
|
|
229
|
+
const rest = line.slice(edgeInfo.idx + edgeInfo.len).trim();
|
|
230
|
+
if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(left)) {
|
|
231
|
+
throw new EntityParseError(`Invalid edge source on line: ${line}`);
|
|
232
|
+
}
|
|
233
|
+
const restMatch = rest.match(
|
|
234
|
+
/^([A-Za-z][A-Za-z0-9_-]*)(?:\s*:\s*([^\[]+?))?(?:\s*\[([^\]]*)\])?\s*$/
|
|
235
|
+
);
|
|
236
|
+
if (!restMatch) {
|
|
237
|
+
throw new EntityParseError(`Cannot parse edge: ${line}`);
|
|
238
|
+
}
|
|
239
|
+
const to = restMatch[1];
|
|
240
|
+
const pct = restMatch[2]?.trim();
|
|
241
|
+
const propsStr = restMatch[3];
|
|
242
|
+
const edge = { from: left, to, op: edgeInfo.op };
|
|
243
|
+
if (pct) edge.percentage = pct;
|
|
244
|
+
if (propsStr) {
|
|
245
|
+
const props = parseProps(propsStr);
|
|
246
|
+
if (props.class) edge.shareClass = stripQuotes(props.class);
|
|
247
|
+
if (props.label) edge.label = stripQuotes(props.label);
|
|
248
|
+
}
|
|
249
|
+
edges.push(edge);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
throw new EntityParseError(`Cannot parse line: ${line}`);
|
|
253
|
+
}
|
|
254
|
+
for (const e of edges) {
|
|
255
|
+
if (!entityMap.has(e.from)) {
|
|
256
|
+
throw new EntityParseError(`Edge references unknown entity "${e.from}"`);
|
|
257
|
+
}
|
|
258
|
+
if (!entityMap.has(e.to)) {
|
|
259
|
+
throw new EntityParseError(`Edge references unknown entity "${e.to}"`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (entities.length === 0) {
|
|
263
|
+
throw new EntityParseError("Entity structure diagram has no entities");
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
type: "entity",
|
|
267
|
+
title: title2,
|
|
268
|
+
entities,
|
|
269
|
+
edges,
|
|
270
|
+
jurisdictions,
|
|
271
|
+
clusters
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// src/diagrams/entity/layout.ts
|
|
276
|
+
var TIER_GAP = 170;
|
|
277
|
+
var H_GAP = 70;
|
|
278
|
+
var PADDING = 40;
|
|
279
|
+
var CLUSTER_PADDING = 26;
|
|
280
|
+
var CLUSTER_V_GAP = 16;
|
|
281
|
+
function geometryFor(type) {
|
|
282
|
+
switch (type) {
|
|
283
|
+
case "trust":
|
|
284
|
+
return { width: 200, height: 80 };
|
|
285
|
+
// ellipse bbox
|
|
286
|
+
case "individual":
|
|
287
|
+
return { width: 80, height: 80 };
|
|
288
|
+
// circle bbox; label rendered below
|
|
289
|
+
case "foundation":
|
|
290
|
+
return { width: 150, height: 80 };
|
|
291
|
+
case "pool":
|
|
292
|
+
case "disregarded":
|
|
293
|
+
case "placeholder":
|
|
294
|
+
case "corp":
|
|
295
|
+
case "llc":
|
|
296
|
+
case "lp":
|
|
297
|
+
default:
|
|
298
|
+
return { width: 170, height: 62 };
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function computeTiers(ast) {
|
|
302
|
+
const inEdges = /* @__PURE__ */ new Map();
|
|
303
|
+
const outEdges = /* @__PURE__ */ new Map();
|
|
304
|
+
for (const n of ast.entities) {
|
|
305
|
+
inEdges.set(n.id, []);
|
|
306
|
+
outEdges.set(n.id, []);
|
|
307
|
+
}
|
|
308
|
+
for (const e of ast.edges) {
|
|
309
|
+
inEdges.get(e.to)?.push(e);
|
|
310
|
+
outEdges.get(e.from)?.push(e);
|
|
311
|
+
}
|
|
312
|
+
const isHierarchy = (e) => e.op === "ownership" || e.op === "voting" || e.op === "pool";
|
|
313
|
+
const tiers = /* @__PURE__ */ new Map();
|
|
314
|
+
for (const n of ast.entities) {
|
|
315
|
+
const hierIn = (inEdges.get(n.id) ?? []).filter(isHierarchy);
|
|
316
|
+
if (hierIn.length === 0) tiers.set(n.id, 0);
|
|
317
|
+
}
|
|
318
|
+
const maxIter = ast.entities.length + 4;
|
|
319
|
+
for (let iter = 0; iter < maxIter; iter++) {
|
|
320
|
+
let changed = false;
|
|
321
|
+
for (const e of ast.edges) {
|
|
322
|
+
if (!isHierarchy(e)) continue;
|
|
323
|
+
const fromT = tiers.get(e.from);
|
|
324
|
+
if (fromT === void 0) continue;
|
|
325
|
+
const want = fromT + 1;
|
|
326
|
+
const cur = tiers.get(e.to);
|
|
327
|
+
if (cur === void 0 || want > cur) {
|
|
328
|
+
tiers.set(e.to, want);
|
|
329
|
+
changed = true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (!changed) break;
|
|
333
|
+
}
|
|
334
|
+
for (const e of ast.edges) {
|
|
335
|
+
if (isHierarchy(e)) continue;
|
|
336
|
+
if (!tiers.has(e.to)) {
|
|
337
|
+
const src = tiers.get(e.from);
|
|
338
|
+
if (src !== void 0) tiers.set(e.to, src);
|
|
339
|
+
}
|
|
340
|
+
if (!tiers.has(e.from)) {
|
|
341
|
+
const dst = tiers.get(e.to);
|
|
342
|
+
if (dst !== void 0) tiers.set(e.from, dst);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
for (const n of ast.entities) {
|
|
346
|
+
if (!tiers.has(n.id)) tiers.set(n.id, 0);
|
|
347
|
+
}
|
|
348
|
+
return tiers;
|
|
349
|
+
}
|
|
350
|
+
function layoutEntity(ast) {
|
|
351
|
+
const tiers = computeTiers(ast);
|
|
352
|
+
const maxTier = Math.max(0, ...Array.from(tiers.values()));
|
|
353
|
+
const byTier = /* @__PURE__ */ new Map();
|
|
354
|
+
for (const n of ast.entities) {
|
|
355
|
+
const t = tiers.get(n.id) ?? 0;
|
|
356
|
+
if (!byTier.has(t)) byTier.set(t, []);
|
|
357
|
+
byTier.get(t).push(n);
|
|
358
|
+
}
|
|
359
|
+
const layoutNodes = [];
|
|
360
|
+
const byId = /* @__PURE__ */ new Map();
|
|
361
|
+
for (const n of ast.entities) {
|
|
362
|
+
const g = geometryFor(n.entityType);
|
|
363
|
+
const t = tiers.get(n.id) ?? 0;
|
|
364
|
+
const ln = {
|
|
365
|
+
node: n,
|
|
366
|
+
x: 0,
|
|
367
|
+
y: PADDING + t * TIER_GAP + g.height / 2,
|
|
368
|
+
tier: t,
|
|
369
|
+
width: g.width,
|
|
370
|
+
height: g.height,
|
|
371
|
+
topY: 0,
|
|
372
|
+
bottomY: 0
|
|
373
|
+
};
|
|
374
|
+
ln.topY = ln.y - g.height / 2;
|
|
375
|
+
ln.bottomY = ln.y + g.height / 2;
|
|
376
|
+
layoutNodes.push(ln);
|
|
377
|
+
byId.set(n.id, ln);
|
|
378
|
+
}
|
|
379
|
+
for (let t = 0; t <= maxTier; t++) {
|
|
380
|
+
const nodes = byTier.get(t) ?? [];
|
|
381
|
+
let cx = PADDING;
|
|
382
|
+
for (const n of nodes) {
|
|
383
|
+
const ln = byId.get(n.id);
|
|
384
|
+
cx += ln.width / 2;
|
|
385
|
+
ln.x = cx;
|
|
386
|
+
cx += ln.width / 2 + H_GAP;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
const children = /* @__PURE__ */ new Map();
|
|
390
|
+
const parents = /* @__PURE__ */ new Map();
|
|
391
|
+
for (const n of ast.entities) {
|
|
392
|
+
children.set(n.id, []);
|
|
393
|
+
parents.set(n.id, []);
|
|
394
|
+
}
|
|
395
|
+
for (const e of ast.edges) {
|
|
396
|
+
if (e.op === "ownership" || e.op === "voting" || e.op === "pool") {
|
|
397
|
+
children.get(e.from)?.push(e.to);
|
|
398
|
+
parents.get(e.to)?.push(e.from);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
for (let iter = 0; iter < 4; iter++) {
|
|
402
|
+
for (let t = maxTier - 1; t >= 0; t--) {
|
|
403
|
+
const tierNodes = byTier.get(t) ?? [];
|
|
404
|
+
for (const n of tierNodes) {
|
|
405
|
+
const ln = byId.get(n.id);
|
|
406
|
+
const childIds = children.get(n.id) ?? [];
|
|
407
|
+
if (childIds.length === 0) continue;
|
|
408
|
+
if (childIds.length === 1) {
|
|
409
|
+
const child = byId.get(childIds[0]);
|
|
410
|
+
if (!child) continue;
|
|
411
|
+
const siblingParents = (parents.get(child.node.id) ?? []).map((pid) => byId.get(pid)).filter((p) => !!p && p.tier === t);
|
|
412
|
+
if (siblingParents.length > 1) {
|
|
413
|
+
siblingParents.sort(
|
|
414
|
+
(a, b) => (byTier.get(t) ?? []).indexOf(a.node) - (byTier.get(t) ?? []).indexOf(b.node)
|
|
415
|
+
);
|
|
416
|
+
const totalWidth = siblingParents.reduce(
|
|
417
|
+
(sum, p) => sum + effectiveHalfWidth(p) * 2,
|
|
418
|
+
0
|
|
419
|
+
);
|
|
420
|
+
const spread = Math.max(
|
|
421
|
+
H_GAP + (effectiveHalfWidth(ln) + effectiveHalfWidth(siblingParents[0])) / 1,
|
|
422
|
+
totalWidth / Math.max(1, siblingParents.length - 1)
|
|
423
|
+
);
|
|
424
|
+
const idx = siblingParents.indexOf(ln);
|
|
425
|
+
const n2 = siblingParents.length;
|
|
426
|
+
ln.x = child.x + (idx - (n2 - 1) / 2) * spread;
|
|
427
|
+
} else {
|
|
428
|
+
ln.x = child.x;
|
|
429
|
+
}
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const xs = childIds.map((c) => byId.get(c)?.x).filter((v) => v !== void 0);
|
|
433
|
+
if (xs.length > 0) {
|
|
434
|
+
ln.x = (Math.min(...xs) + Math.max(...xs)) / 2;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
for (let t = 1; t <= maxTier; t++) {
|
|
439
|
+
const tierNodes = byTier.get(t) ?? [];
|
|
440
|
+
for (const n of tierNodes) {
|
|
441
|
+
const ln = byId.get(n.id);
|
|
442
|
+
const parentIds = parents.get(n.id) ?? [];
|
|
443
|
+
if (parentIds.length < 2) continue;
|
|
444
|
+
const xs = parentIds.map((p) => byId.get(p)?.x).filter((v) => v !== void 0);
|
|
445
|
+
if (xs.length > 0) {
|
|
446
|
+
ln.x = (Math.min(...xs) + Math.max(...xs)) / 2;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
for (let t = 0; t <= maxTier; t++) {
|
|
451
|
+
const tierNodes = (byTier.get(t) ?? []).map((n) => byId.get(n.id)).sort((a, b) => a.x - b.x);
|
|
452
|
+
for (let i = 1; i < tierNodes.length; i++) {
|
|
453
|
+
const prev = tierNodes[i - 1];
|
|
454
|
+
const cur = tierNodes[i];
|
|
455
|
+
const gap = effectiveHalfWidth(prev) + effectiveHalfWidth(cur) + H_GAP;
|
|
456
|
+
if (cur.x - prev.x < gap) cur.x = prev.x + gap;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
let minLeft = Infinity;
|
|
461
|
+
for (const ln of layoutNodes) {
|
|
462
|
+
const left = ln.x - ln.width / 2;
|
|
463
|
+
if (left < minLeft) minLeft = left;
|
|
464
|
+
}
|
|
465
|
+
const shift = PADDING - minLeft;
|
|
466
|
+
if (Math.abs(shift) > 0.5) {
|
|
467
|
+
for (const ln of layoutNodes) ln.x += shift;
|
|
468
|
+
}
|
|
469
|
+
const edges = [];
|
|
470
|
+
for (const e of ast.edges) {
|
|
471
|
+
const from = byId.get(e.from);
|
|
472
|
+
const to = byId.get(e.to);
|
|
473
|
+
if (!from || !to) continue;
|
|
474
|
+
const sameTier = from.tier === to.tier;
|
|
475
|
+
let path2;
|
|
476
|
+
let labelX;
|
|
477
|
+
let labelY;
|
|
478
|
+
if (sameTier) {
|
|
479
|
+
const leftFirst = from.x < to.x;
|
|
480
|
+
const sx = leftFirst ? from.x + from.width / 2 : from.x - from.width / 2;
|
|
481
|
+
const ex = leftFirst ? to.x - to.width / 2 : to.x + to.width / 2;
|
|
482
|
+
path2 = `M ${sx} ${from.y} L ${ex} ${to.y}`;
|
|
483
|
+
labelX = (sx + ex) / 2;
|
|
484
|
+
labelY = from.y - 6;
|
|
485
|
+
} else {
|
|
486
|
+
const sx = from.x;
|
|
487
|
+
const sy = from.bottomY;
|
|
488
|
+
const ex = to.x;
|
|
489
|
+
const ey = to.topY;
|
|
490
|
+
const straight = Math.abs(sx - ex) < 0.5;
|
|
491
|
+
if (straight) {
|
|
492
|
+
path2 = `M ${sx} ${sy} L ${ex} ${ey}`;
|
|
493
|
+
labelX = sx;
|
|
494
|
+
labelY = (sy + ey) / 2;
|
|
495
|
+
} else {
|
|
496
|
+
const midY = (sy + ey) / 2;
|
|
497
|
+
path2 = `M ${sx} ${sy} L ${sx} ${midY} L ${ex} ${midY} L ${ex} ${ey}`;
|
|
498
|
+
const isHier = e.op === "ownership" || e.op === "voting" || e.op === "pool";
|
|
499
|
+
const parentCount = isHier ? (parents.get(e.to) ?? []).length : 0;
|
|
500
|
+
const fanIn = parentCount > 1;
|
|
501
|
+
if (fanIn) {
|
|
502
|
+
labelX = sx;
|
|
503
|
+
labelY = midY;
|
|
504
|
+
} else {
|
|
505
|
+
labelX = ex;
|
|
506
|
+
labelY = midY;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
edges.push({ edge: e, path: path2, labelX, labelY });
|
|
511
|
+
}
|
|
512
|
+
const layoutClusters = [];
|
|
513
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
514
|
+
const LABEL_ROW = 22;
|
|
515
|
+
for (const c of ast.clusters) {
|
|
516
|
+
const members = c.members.map((id) => byId.get(id)).filter((n) => !!n);
|
|
517
|
+
if (members.length === 0) continue;
|
|
518
|
+
const bounds = boundingBox(members);
|
|
519
|
+
layoutClusters.push({
|
|
520
|
+
id: c.id,
|
|
521
|
+
label: c.label,
|
|
522
|
+
color: c.color,
|
|
523
|
+
x: bounds.minX - CLUSTER_PADDING,
|
|
524
|
+
y: bounds.minY - CLUSTER_PADDING - LABEL_ROW,
|
|
525
|
+
width: bounds.maxX - bounds.minX + CLUSTER_PADDING * 2,
|
|
526
|
+
height: bounds.maxY - bounds.minY + CLUSTER_PADDING * 2 + LABEL_ROW
|
|
527
|
+
});
|
|
528
|
+
for (const m of members) usedIds.add(m.node.id);
|
|
529
|
+
}
|
|
530
|
+
for (const j of ast.jurisdictions) {
|
|
531
|
+
const members = layoutNodes.filter(
|
|
532
|
+
(n) => n.node.jurisdiction === j.code && !usedIds.has(n.node.id)
|
|
533
|
+
);
|
|
534
|
+
if (members.length === 0) continue;
|
|
535
|
+
const bounds = boundingBox(members);
|
|
536
|
+
layoutClusters.push({
|
|
537
|
+
id: `jur-${j.code}`,
|
|
538
|
+
label: j.name,
|
|
539
|
+
color: j.color,
|
|
540
|
+
x: bounds.minX - CLUSTER_PADDING,
|
|
541
|
+
y: bounds.minY - CLUSTER_PADDING - LABEL_ROW,
|
|
542
|
+
width: bounds.maxX - bounds.minX + CLUSTER_PADDING * 2,
|
|
543
|
+
height: bounds.maxY - bounds.minY + CLUSTER_PADDING * 2 + LABEL_ROW
|
|
544
|
+
});
|
|
545
|
+
for (const m of members) usedIds.add(m.node.id);
|
|
546
|
+
}
|
|
547
|
+
for (let i = 0; i < layoutClusters.length; i++) {
|
|
548
|
+
for (let k = i + 1; k < layoutClusters.length; k++) {
|
|
549
|
+
const a = layoutClusters[i];
|
|
550
|
+
const b = layoutClusters[k];
|
|
551
|
+
const hOverlap = !(a.x + a.width < b.x || b.x + b.width < a.x);
|
|
552
|
+
const vOverlap = !(a.y + a.height < b.y || b.y + b.height < a.y);
|
|
553
|
+
if (!hOverlap || !vOverlap) continue;
|
|
554
|
+
const [top, bot] = a.y < b.y ? [a, b] : [b, a];
|
|
555
|
+
const want = bot.y - CLUSTER_V_GAP;
|
|
556
|
+
if (top.y + top.height > want) top.height = Math.max(20, want - top.y);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
let maxX = 0;
|
|
560
|
+
let maxY = 0;
|
|
561
|
+
for (const ln of layoutNodes) {
|
|
562
|
+
const right = ln.x + ln.width / 2;
|
|
563
|
+
const bottom = ln.bottomY + 40;
|
|
564
|
+
if (right > maxX) maxX = right;
|
|
565
|
+
if (bottom > maxY) maxY = bottom;
|
|
566
|
+
}
|
|
567
|
+
for (const c of layoutClusters) {
|
|
568
|
+
if (c.x + c.width > maxX) maxX = c.x + c.width;
|
|
569
|
+
if (c.y + c.height > maxY) maxY = c.y + c.height;
|
|
570
|
+
}
|
|
571
|
+
const width = Math.max(400, maxX + PADDING);
|
|
572
|
+
const height = Math.max(200, maxY + PADDING);
|
|
573
|
+
return {
|
|
574
|
+
width,
|
|
575
|
+
height,
|
|
576
|
+
nodes: layoutNodes,
|
|
577
|
+
nodeById: byId,
|
|
578
|
+
edges,
|
|
579
|
+
clusters: layoutClusters
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function effectiveHalfWidth(ln) {
|
|
583
|
+
const n = ln.node;
|
|
584
|
+
const shapeHalf = ln.width / 2;
|
|
585
|
+
const textLen = (s) => s ? s.length : 0;
|
|
586
|
+
if (n.entityType === "individual") {
|
|
587
|
+
const longest = Math.max(
|
|
588
|
+
textLen(n.name),
|
|
589
|
+
textLen(n.role),
|
|
590
|
+
textLen(n.note)
|
|
591
|
+
);
|
|
592
|
+
const textHalf2 = longest * 6.2 / 2 + 4;
|
|
593
|
+
return Math.max(shapeHalf, textHalf2);
|
|
594
|
+
}
|
|
595
|
+
const longestBelow = Math.max(textLen(n.note), textLen(n.role));
|
|
596
|
+
const textHalf = longestBelow * 6 / 2 + 4;
|
|
597
|
+
return Math.max(shapeHalf, textHalf);
|
|
598
|
+
}
|
|
599
|
+
function boundingBox(nodes) {
|
|
600
|
+
let minX = Infinity;
|
|
601
|
+
let maxX = -Infinity;
|
|
602
|
+
let minY = Infinity;
|
|
603
|
+
let maxY = -Infinity;
|
|
604
|
+
for (const n of nodes) {
|
|
605
|
+
minX = Math.min(minX, n.x - n.width / 2);
|
|
606
|
+
maxX = Math.max(maxX, n.x + n.width / 2);
|
|
607
|
+
minY = Math.min(minY, n.topY);
|
|
608
|
+
maxY = Math.max(maxY, n.bottomY + 26);
|
|
609
|
+
}
|
|
610
|
+
return { minX, maxX, minY, maxY };
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// src/diagrams/entity/renderer.ts
|
|
614
|
+
function buildCss(t) {
|
|
615
|
+
return `
|
|
616
|
+
.lt-entity { background: ${t.bg}; font-family: system-ui, -apple-system, sans-serif; }
|
|
617
|
+
.lt-entity-title { font: bold 16px sans-serif; fill: ${t.text}; }
|
|
618
|
+
.lt-entity-name { font: 600 12px sans-serif; fill: ${t.text}; text-anchor: middle; }
|
|
619
|
+
.lt-entity-type { font: 500 10px sans-serif; fill: ${t.textMuted}; text-anchor: middle; }
|
|
620
|
+
.lt-entity-role { font: italic 10px sans-serif; fill: ${t.textMuted}; text-anchor: middle; }
|
|
621
|
+
.lt-entity-note { font: 10px sans-serif; fill: ${t.textMuted}; text-anchor: middle; }
|
|
622
|
+
.lt-entity-badge-bg { fill: ${t.bg}; stroke: ${t.neutral}; stroke-width: 1; }
|
|
623
|
+
.lt-entity-badge-text { font: 600 9px sans-serif; fill: ${t.text}; text-anchor: middle; letter-spacing: 0.5px; }
|
|
624
|
+
.lt-entity-edge { stroke: ${t.stroke}; stroke-width: 1.5; fill: none; }
|
|
625
|
+
.lt-entity-edge-voting { stroke: ${t.stroke}; stroke-width: 1.2; fill: none; }
|
|
626
|
+
.lt-entity-edge-pool { stroke: ${t.neutral}; stroke-width: 1.5; fill: none; stroke-dasharray: 5,4; }
|
|
627
|
+
.lt-entity-edge-license { stroke: ${t.palette[3]}; stroke-width: 1.5; fill: none; stroke-dasharray: 5,4; }
|
|
628
|
+
.lt-entity-edge-distribution { stroke: ${t.positive}; stroke-width: 1.5; fill: none; stroke-dasharray: 5,4; }
|
|
629
|
+
.lt-entity-edge-voting-pref { stroke: ${t.accent}; stroke-width: 1.8; fill: none; }
|
|
630
|
+
.lt-entity-label-bg { fill: ${t.bg}; stroke: ${t.neutral}; stroke-width: 1; }
|
|
631
|
+
.lt-entity-label { font: 600 10px sans-serif; fill: ${t.text}; text-anchor: middle; }
|
|
632
|
+
.lt-entity-label-sub { font: 500 9px sans-serif; fill: ${t.textMuted}; text-anchor: middle; }
|
|
633
|
+
.lt-entity-cluster { fill: none; stroke-dasharray: 6,4; stroke-width: 1.2; }
|
|
634
|
+
.lt-entity-cluster-label-bg { fill: ${t.bg}; }
|
|
635
|
+
.lt-entity-cluster-label { font: 600 11px sans-serif; letter-spacing: 0.5px; }
|
|
636
|
+
.lt-entity-status-new { stroke: ${t.positive}; stroke-width: 2.2; }
|
|
637
|
+
.lt-entity-status-eliminated { stroke: ${t.negative}; stroke-width: 2.2; }
|
|
638
|
+
.lt-entity-status-modified { stroke: ${t.warn}; stroke-width: 2.2; }
|
|
639
|
+
.lt-entity-status-tag { font: 700 8px sans-serif; text-anchor: middle; letter-spacing: 0.5px; }
|
|
640
|
+
`.trim();
|
|
641
|
+
}
|
|
642
|
+
var FILL = {
|
|
643
|
+
corp: "#dbeafe",
|
|
644
|
+
llc: "#dcfce7",
|
|
645
|
+
lp: "#fef9c3",
|
|
646
|
+
trust: "#ede9fe",
|
|
647
|
+
individual: "#fed7aa",
|
|
648
|
+
foundation: "#fef9c3",
|
|
649
|
+
disregarded: "#f5f5f5",
|
|
650
|
+
pool: "#f1f5f9",
|
|
651
|
+
placeholder: "#f9fafb"
|
|
652
|
+
};
|
|
653
|
+
var TYPE_LABEL = {
|
|
654
|
+
corp: "Corporation",
|
|
655
|
+
llc: "LLC",
|
|
656
|
+
lp: "LP / Fund",
|
|
657
|
+
trust: "Trust",
|
|
658
|
+
individual: "Individual",
|
|
659
|
+
foundation: "Foundation",
|
|
660
|
+
disregarded: "Disregarded Entity",
|
|
661
|
+
pool: "Reserved Pool",
|
|
662
|
+
placeholder: "To Be Formed"
|
|
663
|
+
};
|
|
664
|
+
function statusClass(node) {
|
|
665
|
+
switch (node.status) {
|
|
666
|
+
case "new":
|
|
667
|
+
return "lt-entity-status-new";
|
|
668
|
+
case "eliminated":
|
|
669
|
+
return "lt-entity-status-eliminated";
|
|
670
|
+
case "modified":
|
|
671
|
+
return "lt-entity-status-modified";
|
|
672
|
+
default:
|
|
673
|
+
return void 0;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
function renderShape(ln, t) {
|
|
677
|
+
const n = ln.node;
|
|
678
|
+
const fill = FILL[n.entityType] ?? t.bg;
|
|
679
|
+
const sc = statusClass(n);
|
|
680
|
+
const commonAttrs = {
|
|
681
|
+
fill,
|
|
682
|
+
stroke: t.stroke,
|
|
683
|
+
"stroke-width": 1.4
|
|
684
|
+
};
|
|
685
|
+
if (sc) {
|
|
686
|
+
commonAttrs.class = sc;
|
|
687
|
+
}
|
|
688
|
+
const dashed = n.entityType === "disregarded" || n.entityType === "placeholder" || n.entityType === "pool";
|
|
689
|
+
const dashAttr = dashed ? { "stroke-dasharray": "5,3" } : {};
|
|
690
|
+
const opacity = n.entityType === "placeholder" ? { opacity: 0.75 } : {};
|
|
691
|
+
const x = -ln.width / 2;
|
|
692
|
+
const y = -ln.height / 2;
|
|
693
|
+
switch (n.entityType) {
|
|
694
|
+
case "corp":
|
|
695
|
+
return rect({
|
|
696
|
+
x,
|
|
697
|
+
y,
|
|
698
|
+
width: ln.width,
|
|
699
|
+
height: ln.height,
|
|
700
|
+
rx: 0,
|
|
701
|
+
...commonAttrs,
|
|
702
|
+
...dashAttr
|
|
703
|
+
});
|
|
704
|
+
case "llc":
|
|
705
|
+
return rect({
|
|
706
|
+
x,
|
|
707
|
+
y,
|
|
708
|
+
width: ln.width,
|
|
709
|
+
height: ln.height,
|
|
710
|
+
rx: 10,
|
|
711
|
+
ry: 10,
|
|
712
|
+
...commonAttrs
|
|
713
|
+
});
|
|
714
|
+
case "lp": {
|
|
715
|
+
const w = ln.width;
|
|
716
|
+
const h = ln.height;
|
|
717
|
+
const notch = 12;
|
|
718
|
+
const pts = [
|
|
719
|
+
[x + notch, y],
|
|
720
|
+
[x + w - notch, y],
|
|
721
|
+
[x + w, y + notch],
|
|
722
|
+
[x + w, y + h],
|
|
723
|
+
[x, y + h],
|
|
724
|
+
[x, y + notch]
|
|
725
|
+
].map((p) => p.join(",")).join(" ");
|
|
726
|
+
return polygon({ points: pts, ...commonAttrs });
|
|
727
|
+
}
|
|
728
|
+
case "trust":
|
|
729
|
+
return el("ellipse", {
|
|
730
|
+
cx: 0,
|
|
731
|
+
cy: 0,
|
|
732
|
+
rx: ln.width / 2,
|
|
733
|
+
ry: ln.height / 2,
|
|
734
|
+
...commonAttrs
|
|
735
|
+
});
|
|
736
|
+
case "individual":
|
|
737
|
+
return circle({
|
|
738
|
+
cx: 0,
|
|
739
|
+
cy: 0,
|
|
740
|
+
r: Math.min(ln.width, ln.height) / 2 - 4,
|
|
741
|
+
...commonAttrs
|
|
742
|
+
});
|
|
743
|
+
case "foundation": {
|
|
744
|
+
const w = ln.width;
|
|
745
|
+
const h = ln.height;
|
|
746
|
+
const tip = 14;
|
|
747
|
+
const pts = [
|
|
748
|
+
[x + w / 2, y - tip],
|
|
749
|
+
[x + w, y],
|
|
750
|
+
[x + w, y + h],
|
|
751
|
+
[x, y + h],
|
|
752
|
+
[x, y]
|
|
753
|
+
].map((p) => p.join(",")).join(" ");
|
|
754
|
+
return polygon({ points: pts, ...commonAttrs });
|
|
755
|
+
}
|
|
756
|
+
case "disregarded":
|
|
757
|
+
return rect({
|
|
758
|
+
x,
|
|
759
|
+
y,
|
|
760
|
+
width: ln.width,
|
|
761
|
+
height: ln.height,
|
|
762
|
+
...commonAttrs,
|
|
763
|
+
...dashAttr
|
|
764
|
+
});
|
|
765
|
+
case "pool":
|
|
766
|
+
return rect({
|
|
767
|
+
x,
|
|
768
|
+
y,
|
|
769
|
+
width: ln.width,
|
|
770
|
+
height: ln.height,
|
|
771
|
+
rx: 8,
|
|
772
|
+
ry: 8,
|
|
773
|
+
...commonAttrs,
|
|
774
|
+
...dashAttr
|
|
775
|
+
});
|
|
776
|
+
case "placeholder":
|
|
777
|
+
return rect({
|
|
778
|
+
x,
|
|
779
|
+
y,
|
|
780
|
+
width: ln.width,
|
|
781
|
+
height: ln.height,
|
|
782
|
+
rx: 4,
|
|
783
|
+
ry: 4,
|
|
784
|
+
...commonAttrs,
|
|
785
|
+
...dashAttr,
|
|
786
|
+
...opacity
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function renderBadge(ln) {
|
|
791
|
+
const j = ln.node.jurisdiction;
|
|
792
|
+
if (!j) return void 0;
|
|
793
|
+
const bw = j.length >= 3 ? 26 : 20;
|
|
794
|
+
const bh = 13;
|
|
795
|
+
const bx = ln.width / 2 - bw - 4;
|
|
796
|
+
const by = -ln.height / 2 + 4;
|
|
797
|
+
return group({}, [
|
|
798
|
+
rect({ x: bx, y: by, width: bw, height: bh, rx: 2, class: "lt-entity-badge-bg" }),
|
|
799
|
+
text(
|
|
800
|
+
{ x: bx + bw / 2, y: by + bh - 3, class: "lt-entity-badge-text" },
|
|
801
|
+
j
|
|
802
|
+
)
|
|
803
|
+
]);
|
|
804
|
+
}
|
|
805
|
+
function renderStatusTag(ln, t) {
|
|
806
|
+
const n = ln.node;
|
|
807
|
+
if (!n.status || n.status === "normal") return void 0;
|
|
808
|
+
const map = {
|
|
809
|
+
new: { label: "NEW", color: t.positive },
|
|
810
|
+
eliminated: { label: "ELIMINATED", color: t.negative },
|
|
811
|
+
modified: { label: "MODIFIED", color: t.warn }
|
|
812
|
+
};
|
|
813
|
+
const info = map[n.status];
|
|
814
|
+
if (!info) return void 0;
|
|
815
|
+
const w = info.label.length * 6 + 8;
|
|
816
|
+
const h = 12;
|
|
817
|
+
const x = -ln.width / 2 - 2;
|
|
818
|
+
const y = -ln.height / 2 - h - 2;
|
|
819
|
+
return group({}, [
|
|
820
|
+
rect({ x, y, width: w, height: h, rx: 2, fill: info.color, stroke: "none" }),
|
|
821
|
+
text(
|
|
822
|
+
{ x: x + w / 2, y: y + h - 3, class: "lt-entity-status-tag", fill: t.bg },
|
|
823
|
+
info.label
|
|
824
|
+
)
|
|
825
|
+
]);
|
|
826
|
+
}
|
|
827
|
+
function renderNodeLabels(ln) {
|
|
828
|
+
const n = ln.node;
|
|
829
|
+
const pieces = [];
|
|
830
|
+
const isCircle = n.entityType === "individual";
|
|
831
|
+
if (isCircle) {
|
|
832
|
+
let y = ln.height / 2 + 14;
|
|
833
|
+
pieces.push(text({ x: 0, y, class: "lt-entity-name" }, n.name));
|
|
834
|
+
y += 13;
|
|
835
|
+
if (n.role) {
|
|
836
|
+
pieces.push(text({ x: 0, y, class: "lt-entity-role" }, n.role));
|
|
837
|
+
y += 12;
|
|
838
|
+
}
|
|
839
|
+
if (n.note) {
|
|
840
|
+
pieces.push(text({ x: 0, y, class: "lt-entity-note" }, n.note));
|
|
841
|
+
}
|
|
842
|
+
return pieces;
|
|
843
|
+
}
|
|
844
|
+
const topY = -6;
|
|
845
|
+
pieces.push(text({ x: 0, y: topY, class: "lt-entity-name" }, n.name));
|
|
846
|
+
pieces.push(text({ x: 0, y: topY + 14, class: "lt-entity-type" }, TYPE_LABEL[n.entityType] ?? n.entityType));
|
|
847
|
+
let belowY = ln.height / 2 + 14;
|
|
848
|
+
if (n.role) {
|
|
849
|
+
pieces.push(text({ x: 0, y: belowY, class: "lt-entity-role" }, n.role));
|
|
850
|
+
belowY += 12;
|
|
851
|
+
}
|
|
852
|
+
if (n.note) {
|
|
853
|
+
pieces.push(text({ x: 0, y: belowY, class: "lt-entity-note" }, n.note));
|
|
854
|
+
belowY += 12;
|
|
855
|
+
}
|
|
856
|
+
if (n.formationDate) {
|
|
857
|
+
pieces.push(
|
|
858
|
+
text({ x: 0, y: belowY, class: "lt-entity-note" }, `est. ${n.formationDate}`)
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
return pieces;
|
|
862
|
+
}
|
|
863
|
+
function edgeClass(edge) {
|
|
864
|
+
switch (edge.op) {
|
|
865
|
+
case "pool":
|
|
866
|
+
return "lt-entity-edge-pool";
|
|
867
|
+
case "license":
|
|
868
|
+
return "lt-entity-edge-license";
|
|
869
|
+
case "distribution":
|
|
870
|
+
return "lt-entity-edge-distribution";
|
|
871
|
+
case "voting":
|
|
872
|
+
return "lt-entity-edge-voting-pref";
|
|
873
|
+
case "ownership":
|
|
874
|
+
default:
|
|
875
|
+
if (edge.shareClass && /pref|series/i.test(edge.shareClass)) {
|
|
876
|
+
return "lt-entity-edge-voting-pref";
|
|
877
|
+
}
|
|
878
|
+
return "lt-entity-edge";
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function renderEdgeLabel(le) {
|
|
882
|
+
const e = le.edge;
|
|
883
|
+
const lines = [];
|
|
884
|
+
if (e.percentage) lines.push(e.percentage);
|
|
885
|
+
if (e.label) lines.push(e.label);
|
|
886
|
+
const hasClass = !!e.shareClass;
|
|
887
|
+
if (!lines.length && !hasClass) return void 0;
|
|
888
|
+
const main = lines[0];
|
|
889
|
+
const sub = lines[1];
|
|
890
|
+
const classLabel = hasClass ? e.shareClass : void 0;
|
|
891
|
+
const maxText = Math.max(
|
|
892
|
+
main ? main.length : 0,
|
|
893
|
+
sub ? sub.length : 0,
|
|
894
|
+
classLabel ? classLabel.length : 0
|
|
895
|
+
);
|
|
896
|
+
const w = Math.max(30, maxText * 6 + 10);
|
|
897
|
+
const rows = (main ? 1 : 0) + (sub ? 1 : 0) + (classLabel ? 1 : 0);
|
|
898
|
+
const h = 4 + rows * 12;
|
|
899
|
+
const x = le.labelX - w / 2;
|
|
900
|
+
const y = le.labelY - h / 2;
|
|
901
|
+
const pieces = [];
|
|
902
|
+
pieces.push(rect({ x, y, width: w, height: h, rx: 3, class: "lt-entity-label-bg" }));
|
|
903
|
+
let ty = y + 12;
|
|
904
|
+
if (main) {
|
|
905
|
+
pieces.push(text({ x: le.labelX, y: ty, class: "lt-entity-label" }, main));
|
|
906
|
+
ty += 12;
|
|
907
|
+
}
|
|
908
|
+
if (sub) {
|
|
909
|
+
pieces.push(text({ x: le.labelX, y: ty, class: "lt-entity-label-sub" }, sub));
|
|
910
|
+
ty += 12;
|
|
911
|
+
}
|
|
912
|
+
if (classLabel) {
|
|
913
|
+
pieces.push(text({ x: le.labelX, y: ty, class: "lt-entity-label-sub" }, classLabel));
|
|
914
|
+
}
|
|
915
|
+
return group({}, pieces);
|
|
916
|
+
}
|
|
917
|
+
function renderEntity(ast, config) {
|
|
918
|
+
const layout = layoutEntity(ast);
|
|
919
|
+
const t = resolveBaseTheme(config?.theme ?? "default");
|
|
920
|
+
const titleOffset = ast.title ? 34 : 12;
|
|
921
|
+
const width = Math.ceil(layout.width);
|
|
922
|
+
const height = Math.ceil(layout.height + titleOffset);
|
|
923
|
+
const children = [];
|
|
924
|
+
children.push(title(ast.title ?? "Entity Structure Diagram"));
|
|
925
|
+
children.push(
|
|
926
|
+
desc(
|
|
927
|
+
`Entity structure diagram with ${ast.entities.length} entities and ${ast.edges.length} relationships`
|
|
928
|
+
)
|
|
929
|
+
);
|
|
930
|
+
children.push(el("style", {}, buildCss(t)));
|
|
931
|
+
children.push(
|
|
932
|
+
defs([
|
|
933
|
+
arrowMarker("lt-entity-arrow", t.stroke),
|
|
934
|
+
arrowMarker("lt-entity-arrow-purple", t.palette[3] ?? t.stroke),
|
|
935
|
+
arrowMarker("lt-entity-arrow-green", t.positive),
|
|
936
|
+
arrowMarker("lt-entity-arrow-grey", t.neutral),
|
|
937
|
+
arrowMarker("lt-entity-arrow-blue", t.accent)
|
|
938
|
+
])
|
|
939
|
+
);
|
|
940
|
+
if (ast.title) {
|
|
941
|
+
children.push(text({ x: 20, y: 22, class: "lt-entity-title" }, ast.title));
|
|
942
|
+
}
|
|
943
|
+
const inner = [];
|
|
944
|
+
for (const c of layout.clusters) {
|
|
945
|
+
const color = c.color ?? t.neutral;
|
|
946
|
+
inner.push(
|
|
947
|
+
rect({
|
|
948
|
+
x: c.x,
|
|
949
|
+
y: c.y,
|
|
950
|
+
width: c.width,
|
|
951
|
+
height: c.height,
|
|
952
|
+
rx: 8,
|
|
953
|
+
stroke: color,
|
|
954
|
+
class: "lt-entity-cluster"
|
|
955
|
+
})
|
|
956
|
+
);
|
|
957
|
+
inner.push(
|
|
958
|
+
text(
|
|
959
|
+
{ x: c.x + 12, y: c.y + 16, class: "lt-entity-cluster-label", fill: color },
|
|
960
|
+
c.label
|
|
961
|
+
)
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
for (const le of layout.edges) {
|
|
965
|
+
const cls = edgeClass(le.edge);
|
|
966
|
+
const markerId = pickMarker(le.edge, cls);
|
|
967
|
+
inner.push(
|
|
968
|
+
path({
|
|
969
|
+
d: le.path,
|
|
970
|
+
class: cls,
|
|
971
|
+
"marker-end": `url(#${markerId})`
|
|
972
|
+
})
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
for (const le of layout.edges) {
|
|
976
|
+
const piece = renderEdgeLabel(le);
|
|
977
|
+
if (piece) inner.push(piece);
|
|
978
|
+
}
|
|
979
|
+
for (const ln of layout.nodes) {
|
|
980
|
+
const parts = [];
|
|
981
|
+
parts.push(renderShape(ln, t));
|
|
982
|
+
const badge = renderBadge(ln);
|
|
983
|
+
if (badge) parts.push(badge);
|
|
984
|
+
const status = renderStatusTag(ln, t);
|
|
985
|
+
if (status) parts.push(status);
|
|
986
|
+
for (const p of renderNodeLabels(ln)) parts.push(p);
|
|
987
|
+
inner.push(
|
|
988
|
+
group(
|
|
989
|
+
{
|
|
990
|
+
transform: `translate(${ln.x}, ${ln.y})`,
|
|
991
|
+
"data-entity-id": ln.node.id,
|
|
992
|
+
"data-entity-type": ln.node.entityType,
|
|
993
|
+
"data-jurisdiction": ln.node.jurisdiction ?? ""
|
|
994
|
+
},
|
|
995
|
+
parts
|
|
996
|
+
)
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
const wrap = group({ transform: `translate(0, ${titleOffset})` }, inner);
|
|
1000
|
+
children.push(wrap);
|
|
1001
|
+
return svgRoot(
|
|
1002
|
+
{
|
|
1003
|
+
class: "lt-entity",
|
|
1004
|
+
role: "img",
|
|
1005
|
+
"aria-label": escapeXml(ast.title ?? "Entity structure diagram"),
|
|
1006
|
+
width,
|
|
1007
|
+
height,
|
|
1008
|
+
viewBox: `0 0 ${width} ${height}`
|
|
1009
|
+
},
|
|
1010
|
+
children
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
function arrowMarker(id, color) {
|
|
1014
|
+
return el(
|
|
1015
|
+
"marker",
|
|
1016
|
+
{
|
|
1017
|
+
id,
|
|
1018
|
+
markerWidth: 9,
|
|
1019
|
+
markerHeight: 9,
|
|
1020
|
+
refX: 7,
|
|
1021
|
+
refY: 3,
|
|
1022
|
+
orient: "auto",
|
|
1023
|
+
markerUnits: "strokeWidth"
|
|
1024
|
+
},
|
|
1025
|
+
[el("path", { d: "M 0 0 L 7 3 L 0 6 z", fill: color })]
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
function pickMarker(_edge, cls) {
|
|
1029
|
+
if (cls === "lt-entity-edge-license") return "lt-entity-arrow-purple";
|
|
1030
|
+
if (cls === "lt-entity-edge-distribution") return "lt-entity-arrow-green";
|
|
1031
|
+
if (cls === "lt-entity-edge-pool") return "lt-entity-arrow-grey";
|
|
1032
|
+
if (cls === "lt-entity-edge-voting-pref") return "lt-entity-arrow-blue";
|
|
1033
|
+
return "lt-entity-arrow";
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// src/diagrams/entity/index.ts
|
|
1037
|
+
var entity = {
|
|
1038
|
+
type: "entity",
|
|
1039
|
+
detect(text2) {
|
|
1040
|
+
const first = text2.trim().split("\n")[0]?.trim().toLowerCase() ?? "";
|
|
1041
|
+
return first.startsWith("entity-structure");
|
|
1042
|
+
},
|
|
1043
|
+
render(text2, config) {
|
|
1044
|
+
const ast = parseEntityDSL(text2);
|
|
1045
|
+
return renderEntity(ast, config);
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
export { entity, layoutEntity, parseEntityDSL, renderEntity };
|
|
1050
|
+
//# sourceMappingURL=chunk-XX4BKS7Y.js.map
|
|
1051
|
+
//# sourceMappingURL=chunk-XX4BKS7Y.js.map
|