nodelink

package
v1.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package nodelink provides node-and-edge graph visualization using Graphviz.

This package implements a traditional graph visualization where nodes are represented as boxes and dependencies are shown as directed edges between them.

Architecture

Unlike the tower visualization which separates layout computation from rendering, nodelink uses Graphviz which handles both in a single step:

Tower:    DAG → layout.Build() → Layout → sink.RenderSVG() → SVG
Nodelink: DAG → ToDOT() → DOT → RenderSVG() → SVG

The DOT format serves as the intermediate representation (similar to tower's layout.json), enabling re-rendering without re-parsing the dependency graph.

Pipeline Integration

In the job system, nodelink follows the same three-stage pattern:

Parse:  manifest/package → graph.json (DAG)
Layout: graph.json → layout.dot (Graphviz DOT format)
Export: layout.dot → svg/png/pdf

Layout Engines

Graphviz provides several layout engines via the Engine option:

  • dot: Hierarchical (default) - best for dependency graphs
  • neato: Spring model - for undirected graphs
  • fdp: Force-directed - for clustering
  • circo: Circular - for cyclic structures
  • twopi: Radial - for tree-like graphs

Usage

Direct usage (bypassing the job system):

g, _ := resolver.Resolve(ctx, "requests", opts)
dot := nodelink.ToDOT(g, nodelink.Options{})
svg, _ := nodelink.RenderSVG(dot)

Via the API:

// Step 1: Parse
POST /api/v1/parse {language: "python", package: "requests"}
// → graph.json

// Step 2: Layout
POST /api/v1/layout {graph_path: "job-123/graph.json", viz_type: "nodelink"}
// → layout.dot

// Step 3: Export
POST /api/v1/export {layout_path: "job-456/layout.dot", formats: ["svg"]}
// → nodelink.svg

Subdividers

Subdivider nodes (created by dag/transform.Subdivide) are rendered with dashed outlines and grey fill to visually distinguish them from regular dependency nodes.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Export

func Export(dot string, g *dag.DAG, opts Options, width, height float64, style string) (graph.Layout, error)

Export creates a serializable nodelink layout from a DOT string.

Unlike tower layouts, nodelink layouts don't compute positions internally—Graphviz does that during rendering. This function packages the DOT string and graph metadata into the unified serialization format.

Use this when you need to:

  • Export nodelink layout to JSON file
  • Cache the layout for later rendering
  • Return layout data from an API

func Parse

func Parse(layout graph.Layout) (string, error)

Parse extracts the DOT string from a serialized nodelink layout.

Returns an error if the layout is not a nodelink type or is missing the DOT string.

func RenderPDF

func RenderPDF(dot string) ([]byte, error)

RenderPDF renders a DOT graph as PDF via SVG conversion. This is a convenience wrapper around RenderSVG and render.ToPDF.

Requires librsvg: brew install librsvg (macOS), apt install librsvg2-bin (Linux).

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/core/dag"
	"github.com/stacktower-io/stacktower/pkg/core/render/nodelink"
)

func main() {
	// Create a dependency graph
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{ID: "frontend", Row: 0})
	_ = g.AddNode(dag.Node{ID: "backend", Row: 1})
	_ = g.AddEdge(dag.Edge{From: "frontend", To: "backend"})

	// Convert to DOT
	dot := nodelink.ToDOT(g, nodelink.Options{})

	// Render to PDF (requires Graphviz and librsvg)
	pdf, err := nodelink.RenderPDF(dot)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Printf("Generated PDF (%d bytes)\n", len(pdf))
	// Output varies based on tool installation
}

func RenderPNG

func RenderPNG(dot string, scale float64) ([]byte, error)

RenderPNG renders a DOT graph as PNG via SVG conversion. This is a convenience wrapper around RenderSVG and render.ToPNG.

A scale of 2.0 produces a 2x resolution image suitable for high-DPI displays.

Requires librsvg: brew install librsvg (macOS), apt install librsvg2-bin (Linux).

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/core/dag"
	"github.com/stacktower-io/stacktower/pkg/core/render/nodelink"
)

func main() {
	// Create a graph
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{ID: "service", Row: 0})
	_ = g.AddNode(dag.Node{ID: "cache", Row: 1})
	_ = g.AddEdge(dag.Edge{From: "service", To: "cache"})

	// Convert to DOT
	dot := nodelink.ToDOT(g, nodelink.Options{})

	// Render to high-resolution PNG (requires Graphviz and librsvg)
	png, err := nodelink.RenderPNG(dot, 2.0) // 2x scale for retina displays
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Printf("Generated PNG (%d bytes)\n", len(png))
	// Output varies based on tool installation
}

func RenderSVG

func RenderSVG(dot string) ([]byte, error)

RenderSVG renders a DOT graph to SVG using Graphviz. Returns the SVG bytes ready for display or further conversion with render.ToPDF or render.ToPNG.

This function is serialized with a mutex because the go-graphviz WASM backend is not thread-safe and can produce memory access errors under concurrent use.

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/core/dag"
	"github.com/stacktower-io/stacktower/pkg/core/render/nodelink"
)

func main() {
	// Create a simple graph
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{ID: "web", Row: 0})
	_ = g.AddNode(dag.Node{ID: "api", Row: 1})
	_ = g.AddNode(dag.Node{ID: "db", Row: 2})
	_ = g.AddEdge(dag.Edge{From: "web", To: "api"})
	_ = g.AddEdge(dag.Edge{From: "api", To: "db"})

	// Convert to DOT
	dot := nodelink.ToDOT(g, nodelink.Options{})

	// Render to SVG (requires Graphviz)
	svg, err := nodelink.RenderSVG(dot)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Printf("Generated SVG (%d bytes)\n", len(svg))
	// Output varies based on Graphviz installation
}

func ToDOT

func ToDOT(g *dag.DAG, opts Options) string

ToDOT converts a DAG to Graphviz DOT format for node-link visualization. The resulting DOT string can be rendered using RenderSVG, RenderPDF, or RenderPNG.

Subdivider nodes (created by dag/transform.Subdivide) are rendered with dashed outlines and grey fill to distinguish them from regular nodes.

Example
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/core/dag"
	"github.com/stacktower-io/stacktower/pkg/core/render/nodelink"
)

func main() {
	// Create a simple dependency graph
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{ID: "app", Row: 0})
	_ = g.AddNode(dag.Node{ID: "database", Row: 1})
	_ = g.AddNode(dag.Node{ID: "auth", Row: 1})
	_ = g.AddEdge(dag.Edge{From: "app", To: "database"})
	_ = g.AddEdge(dag.Edge{From: "app", To: "auth"})

	// Convert to DOT format
	_ = nodelink.ToDOT(g, nodelink.Options{})

	// The DOT output can be rendered with Graphviz
	fmt.Println("Generated DOT format for visualization")
}
Output:
Generated DOT format for visualization
Example (Detailed)
package main

import (
	"fmt"

	"github.com/stacktower-io/stacktower/pkg/core/dag"
	"github.com/stacktower-io/stacktower/pkg/core/render/nodelink"
)

func main() {
	// Create a graph with metadata
	g := dag.New(nil)
	_ = g.AddNode(dag.Node{
		ID:   "fastapi",
		Row:  0,
		Meta: dag.Metadata{"version": "0.100.0"},
	})
	_ = g.AddNode(dag.Node{
		ID:   "pydantic",
		Row:  1,
		Meta: dag.Metadata{"version": "2.0.0"},
	})
	_ = g.AddEdge(dag.Edge{From: "fastapi", To: "pydantic"})

	// Use detailed mode to include metadata in labels
	_ = nodelink.ToDOT(g, nodelink.Options{Detailed: true})

	// The detailed DOT includes row numbers and metadata
	fmt.Println("Generated detailed DOT with metadata")
}
Output:
Generated detailed DOT with metadata

Types

type Options

type Options struct {
	// Detailed includes row numbers and metadata in node labels.
	// When false, only the node ID is shown.
	Detailed bool
}

Options configures node-link diagram rendering.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL