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

@domandigital/graph

v0.6.0

Published

Schema.org entity-graph builders: stable @id vocabulary, node builders, and a validator that catches unresolved refs and duplicate @ids before they ship.

Readme

@domandigital/graph

Schema.org entity-graph builders for Doman Digital client sites: a stable @id vocabulary, node builders for the common entity kinds (Organization, WebSite, Person, Place, Service, Article, FAQPage, BreadcrumbList, ItemList, OfferCatalog), and a validator that catches unresolved @id references and duplicate @ids before they ship.

Read PRINCIPLES.md before wiring this into a new repo or extending it. This README is the API reference; PRINCIPLES.md is the long-term contract that keeps the portfolio from drifting back into disconnected schema islands.

Why this exists

Across the portfolio, structured data was reimplemented per repo. The recurring defect: provider / worksFor / publisher nested as literal objects instead of {'@id': ...} references, so a page's Service and the sitewide Organization it belongs to are disconnected in the eyes of anything parsing the graph. This package makes the connected version the default, and adds a check (findGraphIssues) that fails loudly when a ref doesn't resolve or two nodes collide on one @id -- the two failure modes hand-written JSON-LD kept shipping.

Proven in production shape against two differently-structured sites before extraction: dd-templates (multi-niche template, per-author Person nodes, /service-areas/ + /blog/ routing) and RMP-Electrical (single business, one founder, /electrician/ + /guides/ routing). Both consume this package with zero routing configuration -- every @id is root-anchored (${siteUrl}/#kind-slug), not built from the entity's own page path.

Install

pnpm add @domandigital/graph

Public on npm, Apache-2.0, published with provenance from a tagged release. ESM and CJS builds ship together, each with its own types, so it works under both import and require. Requires Node 20 or newer.

Earlier versions were consumed as a git dependency pinned to a tag. If you're upgrading a repo that still does that, replace the github:Doman-Digital/dd-graph#vX.Y.Z specifier with a normal semver range. You can also drop resolve.preserveSymlinks: true from vitest.config.ts if it was added for this package: that worked around pnpm putting a literal # in the git dependency's virtual-store path, which Vite truncated as a URL fragment. Registry installs have no # in the path, so the workaround is dead weight now.

Usage

import { createGraphIds, buildSpine, buildService, buildGraph, findGraphIssues } from "@domandigital/graph";

const ids = createGraphIds(siteUrl);

const graph = buildGraph([
  ...buildSpine(
    { name: "Acme Electrical", description: "...", url: siteUrl },
    { name: "Acme Electrical", url: siteUrl },
    ids,
    "Electrician",
  ),
  buildService({ name: "Rewiring", slug: "rewiring", url: `${siteUrl}/services/rewiring` }, ids),
]);

// In CI or a test: fail the build if the graph doesn't resolve.
const issues = findGraphIssues(graph);
if (issues.length > 0) throw new Error(issues.join("\n"));

Render graph as a single <script type="application/ld+json"> per page. This package doesn't ship a React/Next component for that -- emission strategy (next/script vs a plain <script>) is a per-site choice -- but does export escapeJsonLdForScript(json) so CMS-authored strings (a testimonial quote, an FAQ answer) can't break out of the script tag:

import { escapeJsonLdForScript } from "@domandigital/graph";

export function JsonLdScript({ graph }: { graph: JsonLdGraph }) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: escapeJsonLdForScript(JSON.stringify(graph)) }}
    />
  );
}

What stays in the consumer

  • Niche/business-type mapping (e.g. "salon" → ['BeautySalon', 'HealthAndBeautyBusiness', 'LocalBusiness']).
  • CMS-shape adapters (mapping your Sanity/CMS document shape onto OrganizationInput etc).
  • aggregateRating provenance/gating logic -- whether you're allowed to emit a rating at all is a compliance decision, not a graph-shape one.
  • The actual <script> emission component.

Development

pnpm install
pnpm test         # vitest
pnpm typecheck    # tsc --noEmit
pnpm build        # tsup -> dist/

CI runs all three on every push and pull request, across Node 20, 22 and 24.

Versioning

Semver, published to npm on tag push. Bump version in package.json, commit, then tag (git tag vX.Y.Z && git push --tags). The release workflow checks the tag against package.json and refuses to publish if they disagree, then builds and publishes via npm trusted publishing, so no npm token is stored anywhere and every release carries a provenance attestation.

Consumers use a normal semver range. Because this is still 0.x, a minor bump can carry a type change that surfaces as a compile error in a consumer, which is the system working: the v0.1.0 to v0.1.1 null/undefined fix did exactly that within minutes of being wired up.