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

@layerd/wasm

v0.1.0

Published

Blazing fast, low-memory layered graph layout for JavaScript.

Downloads

95

Readme

@layerd/wasm

WASM module | Root README

Sugiyama-style hierarchical graph layout — Rust port of Eclipse Layout Kernel's layered algorithm, compiled to WebAssembly.

Designed to be a drop-in alternative to elkjs for the common "nodes and edges" case, with identical layout quality and much faster execution (native Rust → WASM, no JS interpreter on the hot path).

Install

bun add @layerd/wasm

This package targets modern ESM bundlers and runtimes that can handle WebAssembly module imports, top-level await, and import.meta.url asset resolution. It is not intended to be imported directly by Node's bare ESM loader without a bundler.

Usage

import { layout } from '@layerd/wasm';

const result = layout({
  nodes: [
    { id: 'n1', width: 30, height: 30 },
    { id: 'n2', width: 30, height: 30 },
    { id: 'n3', width: 30, height: 30 },
  ],
  edges: [
    { id: 'e1', source: 'n1', target: 'n2' },
    { id: 'e2', source: 'n2', target: 'n3' },
  ],
});

console.log(result.nodes[0]);
// { id: 'n1', width: 30, height: 30, x: ..., y: ... }

elkjs-compatible edge form

edges: [
  { id: 'e1', sources: ['n1'], targets: ['n2'] },
]

Both { source, target } and { sources, targets } work. Only the first element of each array is used (v1 does not support hyperedges).

API

layout(graph): LayoutResult

Runs the layered layout algorithm with ELK defaults and returns a new graph object annotated with positions.

Input

  • graph.nodes (or graph.children for elkjs compatibility) — array of { id, width, height }
  • graph.edges — array of { id, source, target } or { id, sources, targets }

Output

  • result.nodes[i]{ id, width, height, x, y }
  • result.edges[i]{ id, source, target, bends: [{ x, y }, ...] }
  • result.width, result.height — overall graph bounding box

Throws on invalid input (missing node referenced by an edge) or on any error inside the Rust layout pipeline.

layoutFlat(graph): FlatLayoutResult

Runs layout and returns typed arrays instead of allocating one JavaScript object per laid-out node, edge, and bend point.

Output

  • nodeWireIds, nodeWidth, nodeHeight, nodeX, nodeY
  • edgeWireIds, edgeSourceWireIds, edgeTargetWireIds, edgeBendStart, edgeBendLength
  • bendX, bendY
  • width, height

layoutView(graph): Lrd1LayoutView

Runs layout and returns a zero-copy view over the LRD1 result bytes. The view keeps the output Uint8Array alive and reads fields on demand, avoiding both object-result allocation and flat typed-array copies.

layoutBytes(input): Uint8Array

Runs layout on pre-encoded LRD1 bytes and returns LRD1 result bytes. This is the performance-oriented entry point for callers that already produce LRD1 or can cache encoded graph bytes across repeated layouts.

layoutFlatBytes(input): FlatLayoutResult

Runs layout on pre-encoded LRD1 bytes and returns a flat typed-array result. This is the result-side counterpart to layoutBytes for callers that want to cache or produce LRD1 input bytes and avoid object-result allocation.

layoutViewBytes(input): Lrd1LayoutView

Runs layout on pre-encoded LRD1 bytes and returns a zero-copy result view. This is the lowest-allocation result consumption API in the JavaScript SDK.

encodeLrd1(graph): Uint8Array

Encodes the object API graph shape into LRD1 input bytes.

decodeLrd1(bytes, graph?): LayoutResult

Decodes LRD1 result bytes into the object API shape. Pass the original graph when string or number ids should be restored from positional LRD1 ids.

decodeFlatLrd1(bytes): FlatLayoutResult

Decodes LRD1 result bytes into the same flat typed-array shape as layoutFlatBytes.

decodeViewLrd1(bytes): Lrd1LayoutView

Decodes LRD1 result bytes into a zero-copy result view.

version(): number

Returns the internal wire format version.

Benchmark

Build the WebAssembly package first, then run the TypeScript benchmark with Bun. cargo xtask build-wasm creates the wasm-bindgen files under pkg/ and runs the Rslib build that emits the public SDK entry under dist/.

cargo xtask build-wasm
cargo xtask wasm-bench -- --nodes 256 --fanout 2 --iters 3 --warmups 0

For SDK-only checks:

cd wasm/sdk
bun install
bun run build
bun run lint

The benchmark entrypoint remains:

cargo xtask wasm-bench -- --nodes 256 --fanout 2 --iters 3 --warmups 0
cargo xtask wasm-bench -- --nodes 256 --fanout 2 --iters 50 --warmups 5
cargo xtask wasm-bench -- --input /path/to/graph.json --iters 50
cargo xtask wasm-bench -- --input /path/to/graph.json --iters 3 --warmups 1 --profile
cargo xtask wasm-bench -- --input /path/to/graph.json --iters 3 --warmups 0 --only public --skip-elkjs
cargo xtask wasm-bench -- --input /path/to/graph.json --iters 3 --warmups 0 --only bytes --skip-elkjs --memory
cargo xtask wasm-bench -- --input /path/to/graph.json --iters 3 --warmups 0 --only flat --skip-elkjs --memory
cargo xtask wasm-bench -- --input /path/to/graph.json --iters 3 --warmups 0 --only flat-bytes --skip-elkjs --memory
cargo xtask wasm-bench -- --input /path/to/graph.json --iters 3 --warmups 0 --only view --skip-elkjs --memory
cargo xtask wasm-bench -- --input /path/to/graph.json --iters 3 --warmups 0 --only view-bytes --skip-elkjs --memory

The default benchmark reports JavaScript LRD1 encoding, raw wasm byte-buffer layout, the internal TypeScript object path, and elkjs layered layout on the same graph. With --profile, it also reports JavaScript result decoding as a separate row. Use --only public or --only bytes for isolated public SDK object/bytes measurements; wasm-bindgen's shared glue state makes those paths unsafe to mix with raw wasm rows in the same process. The repo bench pins [email protected] and runs with Bun only. Use --only all|encode|raw|bytes|flat-bytes|view-bytes|decode|sdk|public|flat|view|elkjs when a timing or memory probe needs a single isolated path. Add --memory to include Bun process.memoryUsage() start/max/end columns for RSS, heap, external memory, and ArrayBuffers.

The JSON graph format is the same v1 surface as the public SDK:

{
  "nodes": [
    { "id": "n1", "width": 30, "height": 30 },
    { "id": "n2", "width": 30, "height": 30 }
  ],
  "edges": [{ "id": "e1", "source": "n1", "target": "n2" }]
}

ELKT fixtures can be converted to this JSON subset with cargo xtask ffi-json <input.elkt> <output.json>.

What v1 does not support

The v1 API is intentionally minimal. The following ELK features are not yet exposed and will be added in future versions:

  • Labels
  • Explicit ports with side constraints
  • Nested subgraphs (compound graphs)
  • layoutOptions — all options use ELK defaults

If you need any of these today, use elkjs instead.

License

MIT