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 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
+ ![AWS System Architecture](samples/system-architecture.svg)
16
+
17
+ ### ER Diagram (Draw TSX → SVG)
18
+
19
+ ![ER Diagram](samples/showcase-tsx.svg)
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