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

@unlocksaas/seo

v0.1.1

Published

Honesty-first JSON-LD, llms.txt, and verification primitives for AI-discoverable websites. Extracted from unlocksaas.com.

Downloads

128

Readme

@unlocksaas/seo

Honesty-first JSON-LD, llms.txt, and verification primitives for AI-discoverable websites.

npm types license

Extracted from the production codebase at unlocksaas.com — the post-launch playbook for non-engineer founders shipping with AI tools. The Brunson Hard-Rule discipline this package enforces is documented at unlocksaas.com/editorial-policy.


Why another SEO package?

Most JSON-LD libraries ship every possible field. This one refuses to ship fabricated fields.

The three failure modes that demote a site from Google AI Overviews, Perplexity citations, and Bing Copilot answers are:

  1. Fabricated aggregateRating — a star rating with reviewCount: 0 (or no count at all)
  2. Drift between JSON-LD and rendered HTML — schema says one price, the page shows another
  3. Stale or missing date stampsdatePublished: "soon" is the canonical example

@unlocksaas/seo enforces all three at build time, and ships a validate-claims CLI that audits a deployed page in one shot.


Install

npm install @unlocksaas/seo

CLI (no install required):

npx @unlocksaas/seo validate-claims https://yoursite.com/pricing

The killer feature: validate-claims

Point it at a URL (or a local HTML file). It extracts every <script type="application/ld+json"> block, every <meta>, and the visible HTML. Then it diffs.

$ npx @unlocksaas/seo validate-claims https://yoursite.com/pricing

validate-claims: https://yoursite.com/pricing
  fetched: 2026-05-18T19:12:32Z (HTTP 200), 24,118 bytes

Schema graph:
  Article, BreadcrumbList, FAQPage, Organization, SoftwareApplication, WebSite

Honesty violations:
  aggregateRating.requires-nonzero-count  [3].aggregateRating
    aggregateRating declares 0 reviews. Omit aggregateRating until at least one verified review exists.
  datePublished.must-be-iso-date  [1].datePublished
    datePublished must be a valid YYYY-MM-DD ISO date; got "soon".
  sameAs.must-be-https  [0].sameAs[2]
    sameAs[2] is not an absolute https URL: "twitter.com/yourhandle".

Schema-to-rendered drift:
  jsonLd[3](SoftwareApplication).offers[1].price
    Offer.price "$29/mo" is not visible on the page. Schema-to-rendered drift.
  jsonLd[4](FAQPage).mainEntity[1].name
    FAQ question "..." is not present in visible HTML.

Recommendations:
  → Missing BreadcrumbList. Without it, Google falls back to the raw URL in the SERP — lower CTR.
  → Missing og:image. Twitter/LinkedIn previews will be text-only.

FAIL

Exit code is non-zero on any violation. Wire it into CI and your pricing page can never silently drift from your Stripe checkout again.


The Brunson Hard-Rule, in code

Every builder in this package is a honesty-gated function. The most consequential examples:

aggregateRating is dropped if reviewCount is 0

import { buildProduct } from "@unlocksaas/seo/jsonld";

const product = buildProduct({
  name: "My SaaS",
  url: "https://example.com",
  offers: [{ price: 49, priceCurrency: "USD" }],
  aggregateRating: { ratingValue: 5, reviewCount: 0 },  // silently dropped
});

// Output omits the aggregateRating block entirely — no demotion trigger.

The validate-claims CLI flags any deployed page that ships an aggregateRating with reviewCount === 0 so a teammate cannot bypass the rule by writing raw JSON-LD.

sameAs filters non-https URLs

buildOrganization({
  name: "Demo",
  url: "https://demo.example",
  sameAs: [
    "http://insecure.example",        // dropped
    "https://github.com/demo",        // kept
    "twitter.com/demo",               // dropped (no scheme)
    "not-a-url",                      // dropped (unparseable)
  ],
});

ISO 8601 dates are enforced

buildArticle({
  url: "https://demo.example/post",
  headline: "...",
  datePublished: "soon",   // throws at build time
  dateModified: "2026-05-17",
});

Derived ratings, not invented ones

import { deriveComparisonRatings } from "@unlocksaas/seo/review";

const ratings = deriveComparisonRatings([
  { name: "Pricing", winner: "A" },
  { name: "Speed",   winner: "B" },
  { name: "Support", winner: "tie" },
  { name: "Free tier", winner: "different" },  // shrinks the denominator
]);
// → { aRating: 2.5, bRating: 2.5, aWins: 1, bWins: 1, ties: 1, differents: 1, total: 4 }

The reader can reproduce the math from the dimensions you render on the same page. No invented star ratings.


llms.txt + llms-feed.json in one config file

@unlocksaas/seo ships a typed SiteDescriptor shape and two renderers — renderLlmsTxt(site) and renderLlmsFeed(site). Both read from the same source, so the markdown and the JSON sibling cannot drift on freshness.

npx @unlocksaas/seo init ./site.config.json
# edit site.config.json
npx @unlocksaas/seo generate-llms-txt --config ./site.config.json --out ./public
# → ./public/llms.txt
# → ./public/llms-feed.json

Both files carry Last verified and Next review dates so a retrieval-augmented model that cached your content weeks ago can tell its snapshot is stale.


Next.js App Router adapter

If you're on Next.js 14+:

// app/layout.tsx
import type { Metadata } from "next";
import { buildVerification } from "@unlocksaas/seo/verification";
import { pageAlternates } from "@unlocksaas/seo/next";

export const metadata: Metadata = {
  metadataBase: new URL("https://yoursite.com"),
  title: { default: "Your Site", template: "%s — Your Site" },
  verification: buildVerification(),  // env-driven, empty until you paste codes into Vercel
  alternates: pageAlternates({ canonical: "/" }),
};
// app/page.tsx
import { jsonLdScriptProps } from "@unlocksaas/seo/next";
import { buildOrganization, buildWebSite, buildIds } from "@unlocksaas/seo/jsonld";

const ids = buildIds("https://yoursite.com");

const jsonLd = [
  buildOrganization({ id: ids.organization, name: "Your Co", url: "https://yoursite.com" }),
  buildWebSite({
    id: ids.website,
    name: "Your Co",
    url: "https://yoursite.com",
    publisher: { "@id": ids.organization },
    potentialAction: [{
      type: "SearchAction",
      target: "https://yoursite.com/search?q={search_term_string}",
      queryInput: "search_term_string",
    }],
  }),
];

export default function Page() {
  return (
    <>
      <script {...jsonLdScriptProps(jsonLd)} />
      <h1>...</h1>
    </>
  );
}

This package has zero runtime dependencies — including no React peer dep. The Next.js adapter exposes plain functions (pageAlternates, markdownAlternate, serializeJsonLd, jsonLdScriptProps) that work in any React-shaped framework. The core JSON-LD / llms / honesty modules are framework-free.


Module map

| Entry | What it gives you | |---|---| | @unlocksaas/seo | Re-exports the most-used primitives | | @unlocksaas/seo/jsonld | buildOrganization, buildPerson, buildWebSite, buildArticle, buildFaqPage, buildBreadcrumbList, buildHowTo, buildProduct, buildReview, speakableFromClass, buildIds | | @unlocksaas/seo/llms | renderLlmsTxt, renderLlmsFeed, types | | @unlocksaas/seo/honesty | auditJsonLd, omitEmpty, isIsoDate, formatVerifiedDate, addDaysIso | | @unlocksaas/seo/verification | buildVerification (Google/Bing/Yandex/Pinterest/Facebook/Naver env slots) | | @unlocksaas/seo/freshness | createFreshness, renderActivationLog | | @unlocksaas/seo/review | deriveComparisonRatings | | @unlocksaas/seo/next | pageAlternates, markdownAlternate, serializeJsonLd, jsonLdScriptProps |


CLI reference

unlocksaas-seo validate-claims <url|file>  # audit a deployed page
  --json                                   # machine-readable output
  --strict                                 # treat drift as errors
  --timeout=15000                          # fetch timeout in ms

unlocksaas-seo init [./site.config.json]   # scaffold a SiteDescriptor
unlocksaas-seo generate-llms-txt --config FILE --out DIR
unlocksaas-seo help

Exit codes: 0 clean · 1 violations · 2 invalid args · 3 fetch failed.

Add to your CI:

- run: npx @unlocksaas/seo validate-claims https://${{ secrets.PREVIEW_URL }}/pricing --strict

Who built this

Unlock SaaS — a playbook that turns an already-shipped product into a verified paying customer in 60 days. Built for non-engineer founders who shipped with Lovable, Claude, Replit, v0, or Cursor and now have a flat Stripe line.

The discipline this package enforces is documented at:

If you use this package and want to credit it:

<a href="https://unlocksaas.com" rel="external">Powered by @unlocksaas/seo</a>

License

MIT — use freely. Attribution to unlocksaas.com appreciated but not required.

Contributing

Issues and PRs welcome at github.com/kindrat86/unlocksaas — the package lives at packages/seo/ in the Unlock SaaS monorepo. A standalone mirror at github.com/unlocksaas/seo is planned but the monorepo is the canonical source.

The single non-negotiable rule: no fabricated examples in tests or docs. Every code sample in this README runs against the actual published API, end-to-end. If you submit a PR, the example you add must pass npx @unlocksaas/seo validate-claims against itself.