AphrodyBun GitHub

Runtime docs · Runtime · Interop & Tooling

Native code graph

Build, query and store code graphs of Rust, TypeScript, JavaScript and Markdown sources with bun:graph

bun:graph-native extracts a code graph from source files and runs graph algorithms on it in a worker thread. bun:graph stores graphs, runs and artifacts in a SQLite registry and queries them.

Build and query a graph

executeGraph() takes one request and resolves with its result. Each request has a scope (a graph:-prefixed source name and a profile) and an op.

graph.ts
import { executeGraph } from "bun:graph-native";

const scope = { source: "graph:demo", profile: "bun" } as const;

const built = await executeGraph({
  op: "build",
  scope,
  files: [{ path: "a.ts", content: "export function a() { return b(); }\nfunction b() { return 1; }\n" }],
});

console.log(built.result.graph.nodes.length, built.result.stats.calls);

const path = await executeGraph({ op: "path", scope, graph: built.result.graph, from: "a", to: "b" });
console.log(path.result.hops);

build extracts nodes and links from Rust (.rs), JavaScript and TypeScript (.js, .jsx, .mjs, .cjs, .ts, .tsx, .mts, .cts) and Markdown (.md, .mdx) files. Its result has the graph, call resolution statistics and the files that failed to parse.

opInputResult
buildfiles ({ path, content }), packages?graph, stats, parse_errors
querygraph, question, depth?, budget_tokens?The best-matching nodes and their neighbourhood as text
pathgraph, from, to, directed?The hops of the shortest path, or null
explaingraph, nodeThe node with its incoming and outgoing connections
analyzegraph, top?Communities, modularity and the most connected nodes
exportgraphThe normalized graph

limits bounds a request: max_files, max_file_bytes, max_source_bytes, max_nodes, max_edges, max_ast_depth and max_output_bytes. Pass an AbortSignal as { signal } to cancel a request. At most four requests run at once; further requests reject until one finishes.

Store graphs

BunPython (also exported as PyJS) opens a SQLite registry. With no argument it uses bun_python.sqlite in the current directory (registryPath).

registry.ts
import { BunPython } from "bun:graph";
import { executeGraph } from "bun:graph-native";

using registry = new BunPython("graph.sqlite");
const repository = registry.repository("my-app", "git", process.cwd());

const { result } = await executeGraph({
  op: "build",
  scope: { source: "graph:my-app", profile: "bun" },
  files: [{ path: "a.ts", content: "export function a() {}\n" }],
});

const hash = new Bun.CryptoHasher("sha256").update(JSON.stringify(result.graph)).digest("hex");
await registry.importGraph(repository, result.graph, hash);

console.log(registry.queryNodes({ repositoryId: repository, search: "a" }));
console.log(registry.counts());

A registry holds repositories, files, nodes, edges, graph snapshots, runs, samples, fixes, events and artifacts. Its main methods:

MethodDescription
repository(name, kind, root, revision?)Create or update a repository and return its id
importGraph(repositoryId, graph, sha256)Import a graph as a new snapshot
queryNodes({ repositoryId, kind?, label?, search? })Find nodes, paginated with limit and offset
neighbors(id), incoming(id), outgoing(id)Adjacent nodes with their edges
traverse(id, { direction?, maxDepth? })Walk the graph from a node, with node and work limits
snapshots(repositoryId), getSnapshot(repositoryId)Snapshots of a repository
coverage({ repositoryId, snapshotId? })Node and edge counts by provenance, and unresolved nodes
startRun(), finishRun(), sample(), event()Record command runs, benchmark samples and events
export(path?)Write the registry as JSON, next to the database by default

bun:graphx adds a Valkey/Redis result cache (GraphRedisCache) and helpers that render a registry as Markdown or HTML and stream bounded JSON. packages/bun-types/graph.d.ts declares every type.

The bun mcp graph tools and bun lsp use the same extraction to answer questions about a workspace.