diagram-contracts 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 +21 -0
- package/README.md +360 -0
- package/dist/cli.mjs +5170 -0
- package/dist/diagram-contracts.browser.mjs +232404 -0
- package/dist/index.d.ts +1002 -0
- package/dist/index.mjs +4970 -0
- package/package.json +88 -0
- package/tokens/theme.yaml +35 -0
- package/tokens/tokens.yaml +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 foo-log-inc
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
# diagram-contracts
|
|
2
|
+
|
|
3
|
+
**A Universal Layout Runtime that compiles the Rule Catalog into a Constraint Graph and deterministically generates Artifacts.**
|
|
4
|
+
|
|
5
|
+
diagram-contracts is neither a shape DSL nor a layout DSL.
|
|
6
|
+
All layout — VStack, HStack, Table, Connector — is expressed as a **Rule Catalog** and executed by the same **Graph Runtime**.
|
|
7
|
+
AI can safely describe diagrams, layouts, and relationship structures; the engine converts them to SVG deterministically.
|
|
8
|
+
|
|
9
|
+
## Sample Output
|
|
10
|
+
|
|
11
|
+
### AWS System Architecture (Draw TSX → SVG)
|
|
12
|
+
|
|
13
|
+
Generated from `samples/system-architecture.draw.tsx`. A composite layout combining AWS icons, nested VPC, AlignConstraint, and Connectors.
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
### ER Diagram (Draw TSX → SVG)
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
Regenerate:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
diagram-contracts render samples/system-architecture.draw.tsx -o samples/system-architecture.svg
|
|
25
|
+
diagram-contracts render samples/showcase-tsx.draw.tsx -o samples/showcase-tsx.svg
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Input Formats
|
|
29
|
+
|
|
30
|
+
| Format | Notation | Use Case |
|
|
31
|
+
|--------|----------|----------|
|
|
32
|
+
| **Draw TSX** | Restricted TSX (`.draw.tsx`) | **Recommended.** Type-safe production definitions, componentization, complex layouts |
|
|
33
|
+
| **Layout IR** | JSON | Tool integration, normalized intermediate representation |
|
|
34
|
+
| **Draw Text** | Mermaid-style text | Quick input, documentation embedding, Mermaid import |
|
|
35
|
+
|
|
36
|
+
All three formats are converted to **Layout IR (JSON)**; the downstream layout engine is shared.
|
|
37
|
+
|
|
38
|
+
## Draw TSX (Recommended)
|
|
39
|
+
|
|
40
|
+
Define diagrams in restricted TSX in `.draw.tsx` files. TypeScript type checking, component decomposition, and IDE completion are available — suitable for production definitions.
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<Document id="arch" padding={20} fontFamily="Inter, sans-serif">
|
|
44
|
+
<HStack gap={60} align="center">
|
|
45
|
+
<Box id="client" shape="roundedRect" width={120} height={60}>
|
|
46
|
+
<Text textAlign="center">Client</Text>
|
|
47
|
+
</Box>
|
|
48
|
+
<Box id="api" shape="roundedRect" width={120} height={60}>
|
|
49
|
+
<Text textAlign="center">API Server</Text>
|
|
50
|
+
</Box>
|
|
51
|
+
</HStack>
|
|
52
|
+
<Connector from="client" to="api" endArrow="arrow" route="orthogonal" />
|
|
53
|
+
<AlignConstraint targets={["client", "api"]} axis="horizontal" align="center" />
|
|
54
|
+
</Document>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Component Definitions
|
|
58
|
+
|
|
59
|
+
Draw TSX supports defining and using components (functions). Reusable diagram elements can be split into files and `import`ed.
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
// components/aws.draw.tsx
|
|
63
|
+
export function AwsVpc({ label, children }: { label: string }) {
|
|
64
|
+
return (
|
|
65
|
+
<Box shape="rect" padding={0} stroke="#8c4fff" strokeWidth={2} fill="white">
|
|
66
|
+
<VStack gap={0}>
|
|
67
|
+
<Box shape="rect" fill="#8c4fff" stroke="#8c4fff" strokeWidth={0} padding={8}>
|
|
68
|
+
<HStack gap={6} align="center">
|
|
69
|
+
<Icon name="aws:vpc-group" width={18} height={18} />
|
|
70
|
+
<Text fontWeight="bold" fontSize={12} foreground="white">{label}</Text>
|
|
71
|
+
</HStack>
|
|
72
|
+
</Box>
|
|
73
|
+
{children}
|
|
74
|
+
</VStack>
|
|
75
|
+
</Box>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Geometry Roles
|
|
81
|
+
|
|
82
|
+
A mechanism for referencing internal child elements from outside a component. Specify `align` / `connect` roles via the `geometry` property so Connectors and AlignConstraints reference the correct element without breaking component internals.
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<VStack id="svc" geometry={{ align: "svc-icon", connect: "svc-icon" }}>
|
|
86
|
+
<Icon id="svc-icon" name="aws:ecs" width={48} height={48} />
|
|
87
|
+
<Text fontSize={11}>ECS</Text>
|
|
88
|
+
</VStack>
|
|
89
|
+
|
|
90
|
+
<Connector from="svc" to="db" /> {/* connects from svc-icon rect */}
|
|
91
|
+
<AlignConstraint targets={["svc", "db"]} /> {/* aligns on svc-icon rect */}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
See [docs/geometry-role.md](docs/geometry-role.md) for details.
|
|
95
|
+
|
|
96
|
+
### Property Validation
|
|
97
|
+
|
|
98
|
+
The compiler dynamically derives allowed property lists from the SSoT in `layout-rules.yaml` and reports unknown properties as errors.
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
[error] Box: Unknown property "paddingTop" on <Box>. Allowed: fill, height, opacity, padding, ...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Text Measurement
|
|
105
|
+
|
|
106
|
+
During CLI rendering, text width and height are computed accurately using opentype.js metrics from fonts specified by `fontFamily` (`@fontsource` packages). `fontWeight="bold"` is reflected as well.
|
|
107
|
+
|
|
108
|
+
## Layout IR
|
|
109
|
+
|
|
110
|
+
The JSON intermediate representation that all input formats converge on. Suitable for tool integration or external generation.
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"kind": "document",
|
|
115
|
+
"props": { "padding": 20 },
|
|
116
|
+
"children": [
|
|
117
|
+
{
|
|
118
|
+
"kind": "hstack",
|
|
119
|
+
"props": { "gap": 60, "align": "center" },
|
|
120
|
+
"children": [
|
|
121
|
+
{ "kind": "box", "id": "a", "props": { "width": 120, "height": 60 } }
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Draw Text (Quick Input / Mermaid Import)
|
|
129
|
+
|
|
130
|
+
A lightweight notation that uses Mermaid flowchart syntax as its input format. Suitable for documentation embedding, quick input, and importing existing Mermaid assets.
|
|
131
|
+
|
|
132
|
+
```draw
|
|
133
|
+
flowchart LR
|
|
134
|
+
client[Client]:::primary
|
|
135
|
+
api[API Server]:::surface
|
|
136
|
+
db[(Database)]
|
|
137
|
+
|
|
138
|
+
client -->|REST API| api
|
|
139
|
+
api -.->|SQL| db
|
|
140
|
+
client ~~~ api ~~~ db
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
diagram-contracts is not a Mermaid compatibility implementation. It adopts Mermaid as an **import format**; layout is determined by diagram-contracts' own Rule Catalog Driven approach.
|
|
144
|
+
|
|
145
|
+
See [docs/draw-text.md](docs/draw-text.md) for details.
|
|
146
|
+
|
|
147
|
+
### Web Embedding
|
|
148
|
+
|
|
149
|
+
Place Draw Text source in HTML and render with client-side JS.
|
|
150
|
+
|
|
151
|
+
```html
|
|
152
|
+
<pre class="diagram-contracts" data-theme="default">
|
|
153
|
+
flowchart LR
|
|
154
|
+
client[Client]:::primary
|
|
155
|
+
api[API Server]:::surface
|
|
156
|
+
db[(Database)]
|
|
157
|
+
|
|
158
|
+
client -->|REST API| api
|
|
159
|
+
api -.->|SQL| db
|
|
160
|
+
client ~~~ api ~~~ db
|
|
161
|
+
</pre>
|
|
162
|
+
|
|
163
|
+
<script type="module">
|
|
164
|
+
import { renderDrawText } from 'diagram-contracts/browser';
|
|
165
|
+
|
|
166
|
+
for (const el of document.querySelectorAll('.diagram-contracts')) {
|
|
167
|
+
renderDrawText(el.textContent, {
|
|
168
|
+
mount: el,
|
|
169
|
+
theme: el.dataset.theme,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
</script>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Architecture
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
Draw TSX ──→ compile ──┐
|
|
179
|
+
Draw Text ──→ parse ────┼→ Layout IR (JSON)
|
|
180
|
+
Layout IR ──────────────┘
|
|
181
|
+
↓ normalize (geometry role resolution)
|
|
182
|
+
↓ Rule Catalog lookup (kind → Fragment Factory)
|
|
183
|
+
↓ Graph Fragment generation (slots + operations + edges)
|
|
184
|
+
↓ merge → Constraint Graph
|
|
185
|
+
↓ evaluate (topological resolve)
|
|
186
|
+
Resolved Layout Artifact (JSON)
|
|
187
|
+
↓ render
|
|
188
|
+
SVG (inline) / Canvas / PNG
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Design Philosophy: Rule Catalog Driven Layout
|
|
192
|
+
|
|
193
|
+
> **Layout construct is not an executor. Layout construct is a Rule Fragment Generator.**
|
|
194
|
+
|
|
195
|
+
Layout constructs (VStack, HStack, Table, Connector, etc.) do not execute layout themselves.
|
|
196
|
+
Each construct is a **Rule Catalog entry** that only generates the corresponding **Graph Fragment** (slot + operation + candidate edge).
|
|
197
|
+
Actual layout computation is performed in bulk by the **Graph Runtime**.
|
|
198
|
+
|
|
199
|
+
- **Rule Catalog defines the language**: `layout-rules.yaml` and `constraint-rules.yaml` declaratively define all layout behavior
|
|
200
|
+
- **`style_props` is the property SSoT**: layout params + style_props drive compiler property validation
|
|
201
|
+
- **Table is a Rule Bundle**: no dedicated Table algorithm; expressed as a row×column constraint rule set
|
|
202
|
+
- **Connector is a first-class Graph element**: Node, Group, Table, and Connector all coexist on the same Graph; group connections and cell connections are supported
|
|
203
|
+
- **Geometry Roles**: safely reference internal component elements from outside
|
|
204
|
+
- Size calculation always uses **bounding rect**; `shape` is visual only
|
|
205
|
+
- Same input always produces the same SVG (deterministic)
|
|
206
|
+
- AI must not write SVG coordinates, paths, or transforms
|
|
207
|
+
|
|
208
|
+
## Performance
|
|
209
|
+
|
|
210
|
+
Pipeline stage timings *(Node.js v24, JIT-warmed, 200 iterations, median / p95 in parentheses)*:
|
|
211
|
+
|
|
212
|
+
| Stage | ER Diagram (298 nodes) | AWS Architecture (217 nodes) |
|
|
213
|
+
|-------|------------------------|------------------------------|
|
|
214
|
+
| compile (TSX→IR) | 1.7ms (4.5ms) | 2.1ms (3.4ms) |
|
|
215
|
+
| normalize | 0.7ms (0.9ms) | 0.7ms (1.0ms) |
|
|
216
|
+
| evaluate | 10.3ms (15.6ms) | 5.9ms (10.1ms) |
|
|
217
|
+
| renderSvg | 0.3ms (0.8ms) | 0.3ms (0.6ms) |
|
|
218
|
+
| **TOTAL** | **13.2ms** (19.9ms) | **9.1ms** (13.7ms) |
|
|
219
|
+
|
|
220
|
+
Cold start (first run, no JIT): ER Diagram 114ms / AWS 52ms. In browser Web embedding, only the first render is cold; JIT optimization applies thereafter.
|
|
221
|
+
|
|
222
|
+
Re-benchmark: `node scripts/bench.mjs --warmup 50 --iterations 200`
|
|
223
|
+
|
|
224
|
+
## CLI
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Draw TSX → SVG (recommended)
|
|
228
|
+
diagram-contracts render diagram.draw.tsx -o output.svg
|
|
229
|
+
|
|
230
|
+
# Layout IR (JSON) → SVG
|
|
231
|
+
diagram-contracts render input.json -o output.svg
|
|
232
|
+
|
|
233
|
+
# Draw Text → SVG
|
|
234
|
+
diagram-contracts render diagram.txt -o output.svg
|
|
235
|
+
|
|
236
|
+
# stdin → SVG
|
|
237
|
+
echo '{"kind":"document","props":{"padding":20},"children":[]}' | diagram-contracts render
|
|
238
|
+
|
|
239
|
+
# Validate IR
|
|
240
|
+
diagram-contracts validate input.json
|
|
241
|
+
|
|
242
|
+
# Output normalized IR / resolved artifact
|
|
243
|
+
diagram-contracts ir input.json --stage normalized
|
|
244
|
+
diagram-contracts ir input.json --stage artifact
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Auto-detects input format: JSON (`{`), Draw Text (`flowchart`/`graph`), Draw TSX (`<`).
|
|
248
|
+
|
|
249
|
+
## Node.js API
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import {
|
|
253
|
+
parseDrawText,
|
|
254
|
+
compileDrawTsx,
|
|
255
|
+
parseIr,
|
|
256
|
+
renderToSvg,
|
|
257
|
+
renderDrawTextToSvg,
|
|
258
|
+
renderDrawTsxToSvg,
|
|
259
|
+
layout,
|
|
260
|
+
} from "diagram-contracts";
|
|
261
|
+
|
|
262
|
+
// Draw TSX → SVG (recommended)
|
|
263
|
+
const svg = renderDrawTsxToSvg(`
|
|
264
|
+
<Document padding={20}>
|
|
265
|
+
<Box id="a" shape="roundedRect"><Text>Hello</Text></Box>
|
|
266
|
+
</Document>
|
|
267
|
+
`);
|
|
268
|
+
|
|
269
|
+
// Draw Text → SVG
|
|
270
|
+
const svg2 = renderDrawTextToSvg(`
|
|
271
|
+
flowchart LR
|
|
272
|
+
A[Start] --> B[End]
|
|
273
|
+
`);
|
|
274
|
+
|
|
275
|
+
// Step-by-step pipeline
|
|
276
|
+
const { ir } = parseDrawText(source);
|
|
277
|
+
const { artifact } = layout(ir, { tokens: {}, theme: { defaults: {}, variants: {} } });
|
|
278
|
+
const svgString = renderSvg(artifact);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Browser
|
|
282
|
+
|
|
283
|
+
```html
|
|
284
|
+
<!-- Auto-initialization (recommended) -->
|
|
285
|
+
<script type="module" src="diagram-contracts/browser"></script>
|
|
286
|
+
|
|
287
|
+
<pre class="diagram-contracts">
|
|
288
|
+
flowchart LR
|
|
289
|
+
A[Start] --> B[End]
|
|
290
|
+
</pre>
|
|
291
|
+
|
|
292
|
+
<!-- Manual API -->
|
|
293
|
+
<script type="module">
|
|
294
|
+
import { renderDrawText, renderIr } from "diagram-contracts/browser";
|
|
295
|
+
|
|
296
|
+
const svg = renderDrawText("flowchart LR\n A --> B");
|
|
297
|
+
document.getElementById("target").innerHTML = svg;
|
|
298
|
+
</script>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Elements with class `.diagram-contracts`, `[data-diagram-contracts]`, or `pre[class*="language-draw"]` are auto-rendered on page load. New elements are detected via MutationObserver.
|
|
302
|
+
|
|
303
|
+
## Mermaid Input Support
|
|
304
|
+
|
|
305
|
+
Draw Text can use **Mermaid flowchart syntax as an input format**. Existing Mermaid sources can be used as-is.
|
|
306
|
+
|
|
307
|
+
| Category | Support |
|
|
308
|
+
|----------|---------|
|
|
309
|
+
| `flowchart TD/TB/LR` | ✅ |
|
|
310
|
+
| `graph TD` | ✅ |
|
|
311
|
+
| Node brackets `[]`, `()`, `([])`, `{}`, `[()]`, `(())`, `[[]]` | ✅ |
|
|
312
|
+
| Visible edges `-->`, `---`, `-.->`, `==>` | ✅ |
|
|
313
|
+
| Edge labels `\|label\|` | ✅ |
|
|
314
|
+
| Invisible edges `~~~` | ✅ (→ alignConstraint) |
|
|
315
|
+
| `subgraph ... end` | ✅ |
|
|
316
|
+
| `:::className` | ✅ (→ variant) |
|
|
317
|
+
| `%% comment` | ✅ |
|
|
318
|
+
| `flowchart BT/RL` | ❌ not supported in v1 |
|
|
319
|
+
| `sequenceDiagram`, etc. | ❌ flowchart only |
|
|
320
|
+
|
|
321
|
+
## Why this approach?
|
|
322
|
+
|
|
323
|
+
Existing layout tools each use different algorithms:
|
|
324
|
+
|
|
325
|
+
| Tool | Approach | Limitation |
|
|
326
|
+
|------|----------|------------|
|
|
327
|
+
| **CSS Flexbox / Grid** | UI layout model | Cannot handle Connectors (arrows). Poor fit for diagram relationship structures |
|
|
328
|
+
| **Graphviz** | Automatic graph placement | Node/Edge focused. Weak at structural layouts like Table/Grid |
|
|
329
|
+
| **Mermaid** | Graphviz-based + per-type renderers | Separate algorithm per diagram type. Cannot compose declarative constraints |
|
|
330
|
+
| **D2** | dagre-based | Same as above. Hard to add custom constraints |
|
|
331
|
+
|
|
332
|
+
diagram-contracts solves this by **expressing everything as Rule Catalog entries and reducing them to the same Graph Runtime**:
|
|
333
|
+
|
|
334
|
+
- **VStack / HStack** → Rule Fragments that determine child position and size
|
|
335
|
+
- **Table** → Rule Bundle for row×column grid (no dedicated algorithm)
|
|
336
|
+
- **Connector** → Rule Fragment depending on source/target anchor slots
|
|
337
|
+
- **AlignConstraint** → Rule Fragment generating equivalence constraints between slots
|
|
338
|
+
|
|
339
|
+
All coexist on the same Constraint Graph, so consistent constraint resolution applies even when Nodes, Connectors, and Tables are mixed.
|
|
340
|
+
|
|
341
|
+
## Documentation
|
|
342
|
+
|
|
343
|
+
Specifications, notation, and architecture are collected in [docs/](docs/).
|
|
344
|
+
|
|
345
|
+
| File | Description |
|
|
346
|
+
|------|-------------|
|
|
347
|
+
| [docs/diagram-contracts.md](docs/diagram-contracts.md) | Language specification — IR, Rule Catalog, Graph Runtime |
|
|
348
|
+
| [docs/layout-algorithm.md](docs/layout-algorithm.md) | Layout algorithm via Rule Catalog + Graph Runtime |
|
|
349
|
+
| [docs/geometry-role.md](docs/geometry-role.md) | Geometry Roles — external references to internal component elements |
|
|
350
|
+
| [docs/diagram-vocabulary.md](docs/diagram-vocabulary.md) | Elements, properties, and IR kinds |
|
|
351
|
+
| [docs/draw-text.md](docs/draw-text.md) | Draw Text — Mermaid syntax as input format |
|
|
352
|
+
|
|
353
|
+
## Invariants
|
|
354
|
+
|
|
355
|
+
1. **Bounding is rect** — layout = rect, shape = decoration
|
|
356
|
+
2. **Connector is a first-class Graph element** — coexists on the same Constraint Graph as Node, Group, Table
|
|
357
|
+
3. **Layout IR is JSON** — convergence point for all input formats
|
|
358
|
+
4. **Rule Catalog is the language definition** — `layout-rules.yaml` / `constraint-rules.yaml` are the SSoT for all layout behavior
|
|
359
|
+
5. **`style_props` is the property SSoT** — compiler validation is dynamically derived from YAML
|
|
360
|
+
6. **`required` is a hard constraint** — fail-fast if unsatisfiable
|