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

@prnt/dagr-graph

v0.1.4

Published

Typed directed graph model for Dagr: stable node identity, zero dependencies.

Readme

@prnt/dagr-graph

The typed directed graph model everything else in Dagr is built on: stable node identity, adjacency, attributes, ports, patches, traversal and serialization, with zero runtime dependencies.

pnpm add @prnt/dagr-graph
import { Graph, NodeNotFoundError } from '@prnt/dagr-graph';

const graph = new Graph();

graph.addNode({ id: 'ingest', attrs: { label: 'Ingest' } });
graph.addNode({ id: 'parse', ports: [{ id: 'in', direction: 'in' }] });
graph.addNode('layout'); // shorthand for { id: 'layout' }
const generated = graph.addNode(); // id 'n1'

graph.addEdge({ source: 'ingest', target: 'parse', id: 'first', targetPort: 'in' });
graph.addEdge('parse', 'layout'); // shorthand for { source, target }

graph.successors('parse'); // ['layout']
graph.removeNode('parse'); // takes its incident edges with it

try {
  graph.successors('parse');
} catch (error) {
  if (error instanceof NodeNotFoundError) {
    // error.code === 'NODE_NOT_FOUND'
  }
}

Read this first: Graph is nominally typed

Graph carries #private fields, so two copies of @prnt/dagr-graph in one dependency tree are not interchangeable. Passing one copy's Graph where the other's is expected does not merely behave oddly, it fails to compile with separate declarations of a private property '#nodes'.

That is why @prnt/dagr-layout and @prnt/dagr-vdsl declare this package as a peerDependency rather than a dependency: a caret range on a 0.x package does not cross a minor, so ^0.1.0 and ^0.2.0 would resolve to two installed copies and every call that hands a graph across the boundary would stop compiling. If you see that error, run your package manager's dedupe and check you have one @prnt/dagr-graph, not a version mismatch in your own code.

Mutations are observable, and that is the point

Every mutation emits a patch, and subscribe returns the function that stops watching, which is the shape useSyncExternalStore wants:

const stop = graph.subscribe((patch) => {
  // A Patch IS the frozen list of ops, in the order they happened.
  for (const op of patch) {
    switch (op.op) {
      case 'add-node':
        break;
      default:
        break; // PatchOp is an OPEN union: always take a default arm
    }
  }
});

graph.batch(() => {
  graph.addNode('a');
  graph.addNode('b');
}); // one patch, at the close

PatchOp is an open union by declaration. New op kinds arrive in minor releases, so write default: rather than a never exhaustiveness check, or a future member breaks your build. batch bodies must be synchronous: the patch is emitted when the callback returns.

apply and invert are exported beside them, which is what makes undo a property of the model rather than something a caller reimplements.

Containment

A node may carry parent, at most one, and containment is acyclic. @prnt/dagr-layout currently ignores it: the field is the model, not the layout. Inline compound layout is M7 on the roadmap.

Documentation

The full model, every error code, and the serialization format are on the graph model page.

MIT © prnt.design