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

avoid-nodes-router

v0.3.1

Published

Abstract edge routing engine for avoid-nodes. Transport-agnostic — works with any protocol (REST, WebSocket, SSE, etc).

Readme

avoid-nodes-router

GitHub | How It Works

npm Sponsor

If this package saves you time, consider supporting its development:

USDC (Solana): 59FhVxK3uxABiJ9VzXtCoyCxqq4nhoZDBtUV3gEkiexo


Transport-agnostic, server-side orthogonal edge routing engine powered by libavoid-js (WASM). Route edges around nodes on the server — plug into any protocol: REST, WebSocket, SSE, Socket.IO, or IPC.

Using React Flow? Check out avoid-nodes-edge for client-side routing with a built-in Web Worker.

Using Svelte Flow? Check out avoid-nodes-edge-svelte.

Features

  • Orthogonal (right-angle) edge routing that avoids overlapping nodes
  • Runs in Node.js via WASM — no browser required
  • Each handler spawns its own Worker thread with isolated WASM memory
  • Transport-agnostic: wire handleMessage() to any protocol
  • Auto best side detection for optimal handle positions
  • Configurable spacing, rounding, and grid snapping
  • PersistentServerRouter for incremental updates without rebuilding
  • Clean teardown: destroy() terminates the worker and frees all WASM memory

Install

npm install avoid-nodes-router
yarn add avoid-nodes-router
pnpm add avoid-nodes-router

Peer Dependencies

| Package | Version | |---|---| | libavoid-js | 0.4.5 |

Quick Start

Express Example

A full Express API that loads WASM once at startup, computes ELK layout for node positions, expands group nodes, and routes edges server-side — returning everything ready to render:

import express from 'express';
import { loadAvoidWasm, routeAll } from 'avoid-nodes-router';
import type { FlowNode, FlowEdge } from 'avoid-nodes-router';

async function main() {
  // Load WASM once at startup
  await loadAvoidWasm();

  const app = express();
  app.use(express.json());

  app.get('/api/diagram', async (req, res) => {
    const nodes: FlowNode[] = [
      { id: '1', position: { x: 0, y: 0 }, width: 150, height: 50 },
      { id: '2', position: { x: 300, y: 0 }, width: 150, height: 50 },
      { id: '3', position: { x: 150, y: 150 }, width: 150, height: 50 },
    ];

    const edges: FlowEdge[] = [
      { id: 'e1-2', source: '1', target: '2', type: 'avoidNodes' },
      { id: 'e1-3', source: '1', target: '3', type: 'avoidNodes' },
    ];

    // Compute edge routing paths that avoid nodes
    const routes = routeAll(nodes, edges, {
      shapeBufferDistance: 12,
      idealNudgingDistance: 10,
      edgeRounding: 8,
      autoBestSideConnection: true,
      shouldSplitEdgesNearHandle: true,
    });

    // Return nodes, edges, and pre-computed routes
    res.json({ nodes, edges, routes });
  });

  app.listen(3001, () => {
    console.log('Server listening on http://localhost:3001');
  });
}

main().catch(console.error);

See the express-api-example for a complete implementation with ELK layout, group node expansion, and multiple diagram tabs.

Using the Routing Engine Directly

For simpler use cases, call routeAll() directly without the handler/worker:

import { loadAvoidWasm, routeAll } from 'avoid-nodes-router';

// Load WASM once at startup
await loadAvoidWasm();

// Route edges
const routes = routeAll(nodes, edges, {
  shapeBufferDistance: 12,
  edgeRounding: 8,
  autoBestSideConnection: true,
});

Using the Persistent Router

For incremental updates (e.g., dragging nodes), use PersistentServerRouter to avoid rebuilding the WASM router on every change:

import { loadAvoidWasm, PersistentServerRouter } from 'avoid-nodes-router';

await loadAvoidWasm();

const router = new PersistentServerRouter();

// Initial setup
const routes = router.reset(nodes, edges, options);

// Incremental update — only moves changed shapes
const updated = router.updateNodes([
  { id: '1', position: { x: 50, y: 50 }, width: 150, height: 50 },
]);

// Clean up
router.destroy();

API Reference

createRoutingHandler()

Creates a routing handler with its own Worker thread and isolated WASM instance.

import { createRoutingHandler } from 'avoid-nodes-router';

const handler = createRoutingHandler();

handler.handleMessage(msg)

Process a routing request and return a response. Async because routing runs in a worker thread.

handler.destroy()

Terminate the worker and free all WASM memory. Call on client disconnect.

Request Types

| Command | Description | |---|---| | reset | Send full graph (nodes, edges, options) for re-routing | | updateNodes | Send changed nodes for incremental routing | | addNode | Add a single node | | removeNode | Remove a node by ID | | addEdge | Add a single edge | | removeEdge | Remove an edge by ID | | route | One-shot route (doesn't update internal state) |

Response Types

| Command | Description | |---|---| | routed | Success — contains routes: Record<string, AvoidRoute> | | error | Failure — contains message: string |

routeAll(nodes, edges, options?)

Stateless one-shot routing function. Creates and destroys a WASM router per call.

loadAvoidWasm()

Load the libavoid-js WASM binary. Must be called before routeAll() or PersistentServerRouter.

getAvoidLib()

Returns the loaded WASM library instance. Throws if loadAvoidWasm() hasn't been called.

PersistentServerRouter

Keeps a WASM Router alive across requests for incremental updates.

| Method | Description | |---|---| | reset(nodes, edges, options?) | Full rebuild with new graph data | | updateNodes(nodes) | Move existing shapes without rebuilding | | getState() | Returns current nodes, edges, and options | | destroy() | Free all WASM memory |

Router Options

| Option | Type | Default | Description | |---|---|---|---| | shapeBufferDistance | number | 8 | Buffer distance (px) between edges and node boundaries | | idealNudgingDistance | number | 10 | Distance (px) between parallel edge segments | | handleNudgingDistance | number | same as idealNudgingDistance | Distance (px) for handle-end nudging | | edgeRounding | number | 0 | Corner radius (px) for rounded orthogonal bends | | diagramGridSize | number | 0 | Snap edge waypoints to a grid (0 = no grid) | | shouldSplitEdgesNearHandle | boolean | false | When true, edges spread out along the node border | | autoBestSideConnection | boolean | false | Auto-detect optimal handle side based on node positions |

Types

import type {
  FlowNode,
  FlowEdge,
  AvoidRoute,
  AvoidRouterOptions,
  RoutingHandler,
  RoutingRequest,
  RoutingResponse,
} from 'avoid-nodes-router';

Architecture

Client (any transport)          Server (Node.js)
──────────────────────          ────────────────
                                createRoutingHandler()
WebSocket / HTTP / SSE ──────►    spawns Worker thread
  sends RoutingRequest            loads libavoid WASM
                                  runs routing
  receives RoutingResponse ◄──    posts results back

  on disconnect ──────────────►  handler.destroy()
                                  terminates worker
                                  frees WASM memory

Each handler gets its own Worker thread and WASM instance, so multiple clients route independently without interference.

Sponsor

If this package saves you time, consider supporting its development:

Sponsor

USDC (Solana): 59FhVxK3uxABiJ9VzXtCoyCxqq4nhoZDBtUV3gEkiexo

License

SEE LICENSE IN LICENSE