# `Choreo`
[🔗](https://github.com/code-shoily/choreo/blob/v0.14.1/lib/choreo.ex#L1)

A domain-specific layer on top of Yog for modeling system architectures.

Choreo lets you define infrastructure diagrams (databases, caches,
services, networks, etc.) as graphs and render them to DOT format or
Mermaid.js syntax with system-diagram theming. You can also run graph
algorithms such as MST and topological sort to analyze your architecture.

## When to use

Use `Choreo` when you need to document, communicate, or analyze the
infrastructure of a system — whether for architecture review, onboarding
docs, cost optimisation, or resilience planning.

## Quick Start

    system =
      Choreo.new()
      |> Choreo.add_database(:db, label: "Postgres", kind: :postgres)
      |> Choreo.add_cache(:cache, label: "Redis", kind: :redis)
      |> Choreo.add_service(:api, label: "API Gateway")
      |> Choreo.connect(:api, :cache, cost: 5)
      |> Choreo.connect(:api, :db, cost: 10)

    dot = Choreo.to_dot(system)
    mermaid = Choreo.to_mermaid(system)

## Algorithms

    # Minimum spanning tree for cost optimisation
    {:ok, mst} = Choreo.Analysis.mst(system)

    # Topological sort for data-flow ordering
    {:ok, order} = Choreo.Analysis.topological_sort(system)

## Diagram

<div class="graphviz">
    digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];

    cache [label="Redis", style="filled", fontcolor="white", fillcolor="#f59e0b", shape="octagon"];
    api [label="API Gateway", style="filled", fontcolor="white", fillcolor="#10b981", shape="box3d"];
    db [label="Postgres", style="filled", fontcolor="white", fillcolor="#3b82f6", shape="cylinder"];

    api -> cache [label="5", penwidth="1.0", color="#64748b"];
    api -> db [label="10", penwidth="1.0", color="#64748b", headport="n"];
  }
</div>

# `t`

```elixir
@type t() :: %Choreo{
  clusters: %{required(String.t()) =&gt; map()},
  edge_meta: %{optional(Yog.Multi.Graph.edge_id()) =&gt; map()},
  graph: Yog.Multi.Graph.t()
}
```

# `add_cache`

```elixir
@spec add_cache(t(), Yog.node_id(), keyword()) :: t()
```

Adds a cache node to the system.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_cache(:cache, kind: :redis)
    iex> Choreo.nodes(system)[:cache].node_type
    :cache

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];
    cache [label="Redis", style="filled", fontcolor="white", fillcolor="#f59e0b", shape="octagon"];
  }
</div>

# `add_cluster`

```elixir
@spec add_cluster(t(), String.t(), keyword()) :: t()
```

Defines a cluster (subgraph) for grouping nodes visually.

## Options

* `:parent` (`t:String.t/0`) - Parent cluster name for nesting.

* `:label` (`t:String.t/0`) - Display label for the cluster.

* `:cluster_type` (`t:atom/0`) - Semantic cluster type (e.g. `:vpc`, `:subnet_public`, `:subnet_private`).

* `:style` - Visual style (e.g. :filled, :rounded).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:color` (`t:String.t/0`) - Border color override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_cluster("vpc", label: "VPC")
    iex> Map.keys(Choreo.clusters(system))
    ["cluster_vpc"]

# `add_compute`

```elixir
@spec add_compute(t(), Yog.node_id(), keyword()) :: t()
```

Adds a compute workload runner node (EC2, ECS task, Kubernetes pod).

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_compute(:app)
    iex> Choreo.nodes(system)[:app].node_type
    :compute

# `add_database`

```elixir
@spec add_database(t(), Yog.node_id(), keyword()) :: t()
```

Adds a database node to the system.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_database(:db, kind: :postgres)
    iex> Choreo.nodes(system)[:db].node_type
    :database
    iex> Choreo.nodes(system)[:db].kind
    :postgres
    iex> Choreo.nodes(system)[:db].label
    "db"

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];
    db [label="db", style="filled", fontcolor="white", fillcolor="#3b82f6", shape="cylinder"];
  }
</div>

# `add_dataflow`

```elixir
@spec add_dataflow(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Adds a data-flow edge between two nodes.

This is semantically identical to `connect/4` but renders with a
different default style (dashed, data-flow colour).

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:a) |> Choreo.add_service(:b) |> Choreo.add_dataflow(:a, :b)
    iex> [{:a, :b, 1, meta}] = Choreo.edges_with_meta(system)
    iex> meta.type
    :dataflow

# `add_internet`

```elixir
@spec add_internet(t(), Yog.node_id(), keyword()) :: t()
```

Adds an internet gateway or public internet entrypoint node.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_internet(:gw)
    iex> Choreo.nodes(system)[:gw].node_type
    :internet

# `add_load_balancer`

```elixir
@spec add_load_balancer(t(), Yog.node_id(), keyword()) :: t()
```

Adds a load-balancer node to the system.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_load_balancer(:lb)
    iex> Choreo.nodes(system)[:lb].node_type
    :load_balancer

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];
    lb [label="lb", style="filled", fontcolor="white", fillcolor="#8b5cf6", shape="hexagon"];
  }
</div>

# `add_managed_db`

```elixir
@spec add_managed_db(t(), Yog.node_id(), keyword()) :: t()
```

Adds a managed database node (RDS, Aurora, Cloud SQL).

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_managed_db(:db)
    iex> Choreo.nodes(system)[:db].node_type
    :managed_db

# `add_network`

```elixir
@spec add_network(t(), Yog.node_id(), keyword()) :: t()
```

Adds a network / infrastructure boundary node.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_network(:vpc)
    iex> Choreo.nodes(system)[:vpc].node_type
    :network

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];

    cloudflare [label="cloudflare", style="filled", fontcolor="white", fillcolor="#6366f1", shape="cloud"];
  }
</div>

# `add_node`

```elixir
@spec add_node(t(), Yog.node_id(), keyword()) :: t()
```

Adds a generic node to the system.

Use this when none of the typed builders fit.

## Examples

    iex> system = Choreo.new() |> Choreo.add_node(:misc, label: "Custom")
    iex> Choreo.nodes(system)[:misc].node_type
    :generic
    iex> Choreo.nodes(system)[:misc].label
    "Custom"

# `add_queue`

```elixir
@spec add_queue(t(), Yog.node_id(), keyword()) :: t()
```

Adds a queue / messaging node to the system.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_queue(:q, kind: :kafka)
    iex> Choreo.nodes(system)[:q].node_type
    :queue

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];

    queue [label="queue", style="filled", fontcolor="white", fillcolor="#ec4899", shape="component"];
  }
</div>

# `add_service`

```elixir
@spec add_service(t(), Yog.node_id(), keyword()) :: t()
```

Adds a service / application node to the system.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:api, label: "Gateway")
    iex> Choreo.nodes(system)[:api].node_type
    :service
    iex> Choreo.nodes(system)[:api].label
    "Gateway"

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];
    auth [label="auth", style="filled", fontcolor="white", fillcolor="#10b981", shape="box3d"];
  }
</div>

# `add_storage`

```elixir
@spec add_storage(t(), Yog.node_id(), keyword()) :: t()
```

Adds a storage / blob node to the system.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_storage(:s3)
    iex> Choreo.nodes(system)[:s3].node_type
    :storage

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];
    storage [label="storage", style="filled", fontcolor="white", fillcolor="#14b8a6", shape="tab"];
  }
</div>

# `add_subnet_private`

```elixir
@spec add_subnet_private(t(), String.t(), keyword()) :: t()
```

Adds a private subnet cluster boundary.

## Options

* `:parent` (`t:String.t/0`) - Parent cluster name for nesting.

* `:label` (`t:String.t/0`) - Display label for the cluster.

* `:cluster_type` (`t:atom/0`) - Semantic cluster type (e.g. `:vpc`, `:subnet_public`, `:subnet_private`).

* `:style` - Visual style (e.g. :filled, :rounded).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:color` (`t:String.t/0`) - Border color override.

# `add_subnet_public`

```elixir
@spec add_subnet_public(t(), String.t(), keyword()) :: t()
```

Adds a public subnet cluster boundary.

## Options

* `:parent` (`t:String.t/0`) - Parent cluster name for nesting.

* `:label` (`t:String.t/0`) - Display label for the cluster.

* `:cluster_type` (`t:atom/0`) - Semantic cluster type (e.g. `:vpc`, `:subnet_public`, `:subnet_private`).

* `:style` - Visual style (e.g. :filled, :rounded).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:color` (`t:String.t/0`) - Border color override.

# `add_user`

```elixir
@spec add_user(t(), Yog.node_id(), keyword()) :: t()
```

Adds a user / external actor node to the system.

## Options

* `:kind` (`t:atom/0`) - Specific kind of node (e.g. `:postgres`, `:redis`).

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:description` (`t:String.t/0`) - Tooltip / annotation text.

* `:cluster` (`t:String.t/0`) - Cluster name for visual grouping.

* `:shape` (`t:atom/0`) - Shape override (e.g. `:box`, `:cylinder`).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:fontcolor` (`t:String.t/0`) - Font color override.

* `:style` (`t:String.t/0`) - Style override (e.g. `"filled"`).

* `:penwidth` - Border thickness override.

* `:image` (`t:String.t/0`) - Image/icon path or URL override.

## Examples

    iex> system = Choreo.new() |> Choreo.add_user(:client)
    iex> Choreo.nodes(system)[:client].node_type
    :user

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];
    user [label="user", style="filled", fontcolor="white", fillcolor="#ef4444", shape="doublecircle"];
  }
</div>

# `add_vpc`

```elixir
@spec add_vpc(t(), String.t(), keyword()) :: t()
```

Adds a VPC cluster network boundary.

## Options

* `:parent` (`t:String.t/0`) - Parent cluster name for nesting.

* `:label` (`t:String.t/0`) - Display label for the cluster.

* `:cluster_type` (`t:atom/0`) - Semantic cluster type (e.g. `:vpc`, `:subnet_public`, `:subnet_private`).

* `:style` - Visual style (e.g. :filled, :rounded).

* `:fillcolor` (`t:String.t/0`) - Background color override.

* `:color` (`t:String.t/0`) - Border color override.

# `clusters`

```elixir
@spec clusters(t()) :: %{required(String.t()) =&gt; map()}
```

Returns all cluster definitions in the system.

## Examples

    iex> system = Choreo.new() |> Choreo.add_cluster("vpc", label: "VPC")
    iex> Choreo.clusters(system)
    %{"cluster_vpc" => %{label: "VPC"}}

# `connect`

```elixir
@spec connect(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Connects two nodes with a weighted edge.

## Options

* `:cost` - Numeric weight used by MST and algorithms. The default value is `1`.

* `:label` (`t:String.t/0`) - Display label for the edge.

* `:protocol` - Communication protocol.

* `:type` (`t:atom/0`) - Internal semantic type.

* `:headport` (`t:String.t/0`) - Graphviz headport override.

* `:tailport` (`t:String.t/0`) - Graphviz tailport override.

* `:strict` (`t:boolean/0`) - Whether to raise an error if the source or target node does not exist. The default value is `false`.

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:a) |> Choreo.add_service(:b) |> Choreo.connect(:a, :b, cost: 5)
    iex> Choreo.edges(system)
    [{:a, :b, 5}]

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="lightblue", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];

    a [label="a", style="filled", fontcolor="white", fillcolor="#10b981", shape="box3d"];
    b [label="b", style="filled", fontcolor="white", fillcolor="#10b981", shape="box3d"];

    a -> b [label="5", penwidth="1.0", color="#64748b"];
  }
</div>

# `edges`

```elixir
@spec edges(t()) :: [{Yog.node_id(), Yog.node_id(), number()}]
```

Returns all edges in the system as a list of `{from, to, cost}` tuples.

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:a) |> Choreo.add_service(:b) |> Choreo.connect(:a, :b)
    iex> Choreo.edges(system)
    [{:a, :b, 1}]

# `edges_with_meta`

```elixir
@spec edges_with_meta(t()) :: [{Yog.node_id(), Yog.node_id(), number(), map()}]
```

Returns all edges with their metadata as `{from, to, cost, meta}` tuples.

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:a) |> Choreo.add_service(:b) |> Choreo.connect(:a, :b, protocol: :http)
    iex> [{:a, :b, 1, meta}] = Choreo.edges_with_meta(system)
    iex> meta.protocol
    :http

# `embed`

```elixir
@spec embed(t(), struct(), String.t(), keyword()) :: t()
```

Embeds another diagram inside a cluster of the current system.

## Options

* `:prefix` - Prefix to prevent ID collisions. The default value is `"sub_"`.

## Examples

    iex> system = Choreo.new() |> Choreo.add_cluster("vpc")
    iex> flow = Choreo.Dataflow.new() |> Choreo.Dataflow.add_source(:in)
    iex> system = Choreo.embed(system, flow, "vpc", prefix: "flow_")
    iex> Map.has_key?(Choreo.nodes(system), :flow_in)
    true

# `new`

```elixir
@spec new(keyword()) :: t()
```

Creates a new empty system diagram.

## Options

* `:directed` (`t:boolean/0`) - Whether the underlying graph is directed. The default value is `true`.

## Examples

    iex> system = Choreo.new()
    iex> map_size(system.edge_meta)
    0
    iex> map_size(system.clusters)
    0
    iex> system = Choreo.new(directed: false)
    iex> system.graph.kind
    :undirected

# `nodes`

```elixir
@spec nodes(t()) :: %{required(Yog.node_id()) =&gt; map()}
```

Returns all nodes in the system as a map of `id => data`.

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:api)
    iex> Map.keys(Choreo.nodes(system))
    [:api]

# `theme`

```elixir
@spec theme(atom(), keyword()) :: Choreo.Theme.t()
```

Returns a theme for `Choreo` infrastructure diagrams.

## Examples

    iex> theme = Choreo.theme(:default, graph_rankdir: :lr)
    iex> theme.graph_rankdir
    :lr

# `to_dot`

```elixir
@spec to_dot(any(), keyword()) :: String.t()
```

Renders the system diagram to DOT format.

## Options

  * `:theme` - `:default`, `:dark`, `:minimal`, `:warm`, `:forest`, or `:ocean`
  * `:highlighted_nodes` - list of node IDs to highlight
  * `:highlighted_edges` - list of edge IDs or `{from, to}` tuples to highlight
  * Any option accepted by `Yog.Render.DOT.to_dot/2`

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:api)
    iex> dot = Choreo.to_dot(system)
    iex> String.contains?(dot, "digraph")
    true
    iex> String.contains?(dot, "api")
    true

# `to_graph`

```elixir
@spec to_graph(t()) :: Yog.Multi.Graph.t()
```

Returns the raw `Yog.Multi.Graph` struct underpinning the system.

Useful when you want to drop down to the raw Yog Multi API.

## Examples

    iex> system = Choreo.new()
    iex> Choreo.to_graph(system) == system.graph
    true

# `to_mermaid`

```elixir
@spec to_mermaid(any(), keyword()) :: String.t()
```

Renders the system diagram to Mermaid.js syntax.

## Options

  * `:syntax` - `:flowchart` (default) or `:architecture` (native
    `architecture-beta` diagram type)
  * `:theme` - `:default`, `:dark`, `:minimal`, `:warm`, `:forest`, or `:ocean`
  * `:direction` - `:td`, `:lr`, `:bt`, or `:rl`
  * `:highlighted_nodes` - list of node IDs to highlight
  * `:highlighted_edges` - list of edge IDs or `{from, to}` tuples to highlight
  * Any option accepted by `Yog.Multi.Mermaid.to_mermaid/2`

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:api)
    iex> mermaid = Choreo.to_mermaid(system)
    iex> String.contains?(mermaid, "graph TD")
    true
    iex> String.contains?(mermaid, "api")
    true

    iex> system = Choreo.new() |> Choreo.add_service(:api)
    iex> mermaid = Choreo.to_mermaid(system, syntax: :architecture)
    iex> String.contains?(mermaid, "architecture-beta")
    true
    iex> String.contains?(mermaid, "api")
    true

# `to_simple_graph`

```elixir
@spec to_simple_graph(t(), keyword()) :: Yog.Graph.t()
```

Collapses parallel edges into a simple `Yog.Graph` for algorithm analysis.

## Options

  * `:combine` - function to combine weights of parallel edges
    (default: `&min/2`)

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:a) |> Choreo.add_service(:b)
    ...> |> Choreo.connect(:a, :b, cost: 10) |> Choreo.connect(:a, :b, cost: 5)
    iex> simple = Choreo.to_simple_graph(system)
    iex> Yog.all_edges(simple)
    [{:a, :b, 5}]

# `trace`

```elixir
@spec trace(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Declares a semantic trace (cross-diagram link) between two nodes in a composed system.

Trace links are kept separate from the visual layout by default and are only shown
when `:show_traces` is set to `true`.

## Options

* `:type` (`t:atom/0`) - Semantic trace type (e.g. :executes, :stores, :implements). The default value is `:executes`.

## Examples

    iex> system = Choreo.new() |> Choreo.add_service(:api) |> Choreo.add_database(:db)
    iex> system = Choreo.trace(system, :api, :db, type: :stores)
    iex> [{:api, :db, 1, meta}] = Choreo.edges_with_meta(system)
    iex> meta.edge_type
    :trace

---

*Consult [api-reference.md](api-reference.md) for complete listing*
