npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

diagram-contracts

v0.2.1

Published

Rule Catalog driven layout runtime — compile diagrams to deterministic SVG from Draw TSX, Draw Text, or Layout IR

Readme

diagram-contracts

A Universal Layout Runtime that compiles the Rule Catalog into a Constraint Graph and deterministically generates Artifacts.

diagram-contracts is neither a shape DSL nor a layout DSL. All layout — VStack, HStack, Table, Connector — is expressed as a Rule Catalog and executed by the same Graph Runtime. AI can safely describe diagrams, layouts, and relationship structures; the engine converts them to SVG deterministically.

Sample Output

AWS System Architecture (Draw TSX → SVG)

AWS System Architecture

Source:

ER Diagram (Draw TSX → SVG)

ER Diagram

Source: assets/readme/showcase-tsx.draw.tsx

Regenerate:

diagram-contracts render assets/readme/system-architecture.draw.tsx -o assets/readme/system-architecture.svg
diagram-contracts render assets/readme/showcase-tsx.draw.tsx -o assets/readme/showcase-tsx.svg

Input Formats

| Format | Notation | Use Case | |--------|----------|----------| | Draw TSX | Restricted TSX (.draw.tsx) | Recommended. Type-safe production definitions, componentization, complex layouts | | Layout IR | JSON | Tool integration, normalized intermediate representation | | Draw Text | Mermaid-style text | Quick input, documentation embedding, Mermaid import |

All three formats are converted to Layout IR (JSON); the downstream layout engine is shared.

Draw TSX (Recommended)

Define diagrams in restricted TSX in .draw.tsx files. TypeScript type checking, component decomposition, and IDE completion are available — suitable for production definitions.

<Document id="arch" padding={20} fontFamily="Inter, sans-serif">
  <HStack gap={60} align="center">
    <Box id="client" shape="roundedRect" width={120} height={60}>
      <Text textAlign="center">Client</Text>
    </Box>
    <Box id="api" shape="roundedRect" width={120} height={60}>
      <Text textAlign="center">API Server</Text>
    </Box>
  </HStack>
  <Connector from="client" to="api" endArrow="arrow" route="orthogonal" />
  <AlignConstraint targets={["client", "api"]} axis="horizontal" align="center" />
</Document>

Component Definitions

Draw TSX supports defining and using components (functions). Reusable diagram elements can be split into files and imported.

// components/aws.draw.tsx
export function AwsVpc({ label, children }: { label: string }) {
  return (
    <Box shape="rect" padding={0} stroke="#8c4fff" strokeWidth={2} fill="white">
      <VStack gap={0}>
        <Box shape="rect" fill="#8c4fff" stroke="#8c4fff" strokeWidth={0} padding={8}>
          <HStack gap={6} align="center">
            <Icon name="aws:vpc-group" width={18} height={18} />
            <Text fontWeight="bold" fontSize={12} foreground="white">{label}</Text>
          </HStack>
        </Box>
        {children}
      </VStack>
    </Box>
  );
}

Component and built-in resolution follows the React convention: a defined or imported component takes precedence over a built-in element of the same name. Naming a component Cell, Divider, Row, etc. therefore uses your component (not the built-in), and the compiler emits a one-time warning noting the shadow so the precedence is never silent. Rename the component to reach the built-in element. Imported components are in scope both at the document tree and inside other component bodies (including transitively, when an imported file imports its own components).

Caution — structural names. Shadowing a structural built-in used by a composite element (Row / Cell inside Table) means every <Row> / <Cell> in the document — including those inside a <Table> — resolves to your component, which breaks table layout. Prefer names that do not collide with built-ins for reusable components.

Children

Element children may be other elements, components, the {children} slot (inside a component body), and {array.map(...)} over a literal array:

<VStack gap={5}>
  {["alpha", "beta", "gamma"].map((label, i) => <Item t={label} n={i} />)}
</VStack>

The array must be a literal so its length is known at compile time; the callback must be an inline function returning a single JSX element. Its parameter (and optional index) bind per element, while enclosing component props referenced in the body stay deferred until that component is expanded. Mapping over a runtime value (e.g. a component prop) or any other unsupported child expression is a compile error, never a silent drop. An unknown element name (neither a built-in nor a defined/imported component) is likewise a compile error.

Prop Expressions

Draw TSX is compiled statically — there is no JSX runtime. Prop expressions are therefore restricted to a grammar the compiler can constant-fold at compile time:

| Supported | Example | |-----------|---------| | Literals (number / string / boolean / template) | width={50}, fill="#f00", visible={false} | | Props references (inside a component) | height={h} | | Arithmetic + - * / % with standard precedence | height={h - 16}, width={2 + 3 * 4} (= 14) | | Parentheses and unary minus, arbitrarily nested | width={-(h + 2) * 3 + 100} | | String concatenation (string + string) | label={"REST " + "API"} | | Template literals with expression spans | id={`item-${i + 1}`} | | Arrays and object literals of the above | targets={["a", "b"]}, geometry={{ align: "x" }} |

Arithmetic over component props is folded when the component is expanded, so height={h - 16} with <Shell h={100} /> renders 84.

Everything else is a compile error naming the prop and the offending syntax — unsupported expressions are never silently dropped to element defaults:

  • Function calls (Math.max(…)), member access (obj.w), ternaries, logical operators (&&, ||), spread attributes ({...rest}), and any other syntax → error.
  • Arithmetic requires numbers; + also accepts two strings. Mixed string/number + is an error (no implicit coercion).
  • Division or modulo by zero — and any other non-finite result, including overflowing literals like 1e999 — → error at compile time; non-finite values never reach layout.
  • Referencing a prop that the caller did not provide inside an expression → error.
  • Exception: null / undefined as the entire prop value mean "prop not set" (standard JSX semantics) and fall back to the element's schema default without a diagnostic. Inside an expression ({5 + null}, {-null}, {`a${undefined}`}) they are a compile error.

Geometry Roles

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.

<VStack id="svc" geometry={{ align: "svc-icon", connect: "svc-icon" }}>
  <Icon id="svc-icon" name="aws:ecs" width={48} height={48} />
  <Text fontSize={11}>ECS</Text>
</VStack>

<Connector from="svc" to="db" />          {/* connects from svc-icon rect */}
<AlignConstraint targets={["svc", "db"]} /> {/* aligns on svc-icon rect */}

See docs/geometry-role.md for details.

Property Validation

The compiler dynamically derives allowed property lists from the SSoT in layout-rules.yaml and reports unknown properties as errors.

[error] Box: Unknown property "paddingTop" on <Box>. Allowed: fill, height, opacity, padding, ...

Text Measurement

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.

Layout IR

The JSON intermediate representation that all input formats converge on. Suitable for tool integration or external generation.

{
  "kind": "document",
  "props": { "padding": 20 },
  "children": [
    {
      "kind": "hstack",
      "props": { "gap": 60, "align": "center" },
      "children": [
        { "kind": "box", "id": "a", "props": { "width": 120, "height": 60 } }
      ]
    }
  ]
}

Draw Text (Quick Input / Mermaid Import)

A lightweight notation that uses Mermaid flowchart syntax as its input format. Suitable for documentation embedding, quick input, and importing existing Mermaid assets.

flowchart LR
  client[Client]:::primary
  api[API Server]:::surface
  db[(Database)]

  client -->|REST API| api
  api -.->|SQL| db
  client ~~~ api ~~~ db

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.

See docs/draw-text.md for details.

Web Embedding

Place Draw Text source in HTML and render with client-side JS.

<pre class="diagram-contracts" data-theme="default">
flowchart LR
  client[Client]:::primary
  api[API Server]:::surface
  db[(Database)]

  client -->|REST API| api
  api -.->|SQL| db
  client ~~~ api ~~~ db
</pre>

<script type="module">
  import { renderDrawText } from 'diagram-contracts/browser';

  for (const el of document.querySelectorAll('.diagram-contracts')) {
    renderDrawText(el.textContent, {
      mount: el,
      theme: el.dataset.theme,
    });
  }
</script>

Architecture

Draw TSX  ──→ compile ──┐
Draw Text ──→ parse ────┼→ Layout IR (JSON)
Layout IR ──────────────┘
  ↓ normalize (geometry role resolution)
  ↓ Rule Catalog lookup (kind → Fragment Factory)
  ↓ Graph Fragment generation (slots + operations + edges)
  ↓ merge → Constraint Graph
  ↓ evaluate (topological resolve)
Resolved Layout Artifact (JSON)
  ↓ render
SVG (inline) / Canvas / PNG

Design Philosophy: Rule Catalog Driven Layout

Layout construct is not an executor. Layout construct is a Rule Fragment Generator.

Layout constructs (VStack, HStack, Table, Connector, etc.) do not execute layout themselves. Each construct is a Rule Catalog entry that only generates the corresponding Graph Fragment (slot + operation + candidate edge). Actual layout computation is performed in bulk by the Graph Runtime.

  • Rule Catalog defines the language: layout-rules.yaml and constraint-rules.yaml declaratively define all layout behavior
  • style_props is the property SSoT: layout params + style_props drive compiler property validation
  • Table is a Rule Bundle: no dedicated Table algorithm; expressed as a row×column constraint rule set
  • 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
  • Geometry Roles: safely reference internal component elements from outside
  • Size calculation always uses bounding rect; shape is visual only
  • Same input always produces the same SVG (deterministic)
  • AI must not write SVG coordinates, paths, or transforms

Performance

Pipeline stage timings (Node.js v24, JIT-warmed, 200 iterations, median / p95 in parentheses):

| Stage | ER Diagram (298 nodes) | AWS Architecture (217 nodes) | |-------|------------------------|------------------------------| | compile (TSX→IR) | 1.7ms (4.5ms) | 2.1ms (3.4ms) | | normalize | 0.7ms (0.9ms) | 0.7ms (1.0ms) | | evaluate | 10.3ms (15.6ms) | 5.9ms (10.1ms) | | renderSvg | 0.3ms (0.8ms) | 0.3ms (0.6ms) | | TOTAL | 13.2ms (19.9ms) | 9.1ms (13.7ms) |

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.

Re-benchmark: node scripts/bench.mjs --warmup 50 --iterations 200

CLI

# Draw TSX → SVG (recommended)
diagram-contracts render diagram.draw.tsx -o output.svg

# Layout IR (JSON) → SVG
diagram-contracts render input.json -o output.svg

# Draw Text → SVG
diagram-contracts render diagram.txt -o output.svg

# stdin → SVG
echo '{"kind":"document","props":{"padding":20},"children":[]}' | diagram-contracts render

# Validate IR
diagram-contracts validate input.json

# Output normalized IR / resolved artifact
diagram-contracts ir input.json --stage normalized
diagram-contracts ir input.json --stage artifact

Auto-detects input format: JSON ({), Draw Text (flowchart/graph), Draw TSX (<).

Node.js API

import {
  parseDrawText,
  compileDrawTsx,
  parseIr,
  renderToSvg,
  renderDrawTextToSvg,
  renderDrawTsxToSvg,
  layout,
} from "diagram-contracts";

// Draw TSX → SVG (recommended)
const svg = renderDrawTsxToSvg(`
<Document padding={20}>
  <Box id="a" shape="roundedRect"><Text>Hello</Text></Box>
</Document>
`);

// Draw Text → SVG
const svg2 = renderDrawTextToSvg(`
flowchart LR
  A[Start] --> B[End]
`);

// Step-by-step pipeline
const { ir } = parseDrawText(source);
const { artifact } = layout(ir, { tokens: {}, theme: { defaults: {}, variants: {} } });
const svgString = renderSvg(artifact);

Browser

<!-- Auto-initialization (recommended) -->
<script type="module" src="diagram-contracts/browser"></script>

<pre class="diagram-contracts">
flowchart LR
  A[Start] --> B[End]
</pre>

<!-- Manual API -->
<script type="module">
  import { renderDrawText, renderIr } from "diagram-contracts/browser";

  const svg = renderDrawText("flowchart LR\n  A --> B");
  document.getElementById("target").innerHTML = svg;
</script>

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.

Mermaid Input Support

Draw Text can use Mermaid flowchart syntax as an input format. Existing Mermaid sources can be used as-is.

| Category | Support | |----------|---------| | flowchart TD/TB/LR | ✅ | | graph TD | ✅ | | Node brackets [], (), ([]), {}, [()], (()), [[]] | ✅ | | Visible edges -->, ---, -.->, ==> | ✅ | | Edge labels \|label\| | ✅ | | Invisible edges ~~~ | ✅ (→ alignConstraint) | | subgraph ... end | ✅ | | :::className | ✅ (→ variant) | | %% comment | ✅ | | flowchart BT/RL | ❌ not supported in v1 | | sequenceDiagram, etc. | ❌ flowchart only |

Why this approach?

Existing layout tools each use different algorithms:

| Tool | Approach | Limitation | |------|----------|------------| | CSS Flexbox / Grid | UI layout model | Cannot handle Connectors (arrows). Poor fit for diagram relationship structures | | Graphviz | Automatic graph placement | Node/Edge focused. Weak at structural layouts like Table/Grid | | Mermaid | Graphviz-based + per-type renderers | Separate algorithm per diagram type. Cannot compose declarative constraints | | D2 | dagre-based | Same as above. Hard to add custom constraints |

diagram-contracts solves this by expressing everything as Rule Catalog entries and reducing them to the same Graph Runtime:

  • VStack / HStack → Rule Fragments that determine child position and size
  • Table → Rule Bundle for row×column grid (no dedicated algorithm)
  • Connector → Rule Fragment depending on source/target anchor slots
  • AlignConstraint → Rule Fragment generating equivalence constraints between slots

All coexist on the same Constraint Graph, so consistent constraint resolution applies even when Nodes, Connectors, and Tables are mixed.

Documentation

Specifications, notation, and architecture are collected in docs/.

| File | Description | |------|-------------| | docs/diagram-contracts.md | Language specification — IR, Rule Catalog, Graph Runtime | | docs/layout-algorithm.md | Layout algorithm via Rule Catalog + Graph Runtime | | docs/geometry-role.md | Geometry Roles — external references to internal component elements | | docs/diagram-vocabulary.md | Elements, properties, and IR kinds | | docs/draw-text.md | Draw Text — Mermaid syntax as input format |

Invariants

  1. Bounding is rect — layout = rect, shape = decoration
  2. Connector is a first-class Graph element — coexists on the same Constraint Graph as Node, Group, Table
  3. Layout IR is JSON — convergence point for all input formats
  4. Rule Catalog is the language definitionlayout-rules.yaml / constraint-rules.yaml are the SSoT for all layout behavior
  5. style_props is the property SSoT — compiler validation is dynamically derived from YAML
  6. required is a hard constraint — fail-fast if unsatisfiable