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

opensitestack

v1.0.3

Published

A typed multisite platform core for Next.js.

Readme

opensitestack

The opensitestack package provides the typed, site-neutral core and Next.js request adapter used by OpenSiteStack applications.

The v1 API is stable under Semantic Versioning and is published for independent Next.js application repositories.

Host-only HSTS

createHostOnlyHstsHeader() from opensitestack/security returns the shared one-year browser policy Strict-Transport-Security: max-age=31536000. It intentionally excludes includeSubDomains and preload so a website cannot impose policy on sibling subdomains outside its operating boundary. Apply it to every concrete HTTPS website host and validate redirect-only aliases at the responsible edge.

Site registry

defineSiteRegistry validates production domains, optional development hosts, HTTPS canonical origins, and typed site metadata once at application startup. Every normalized host must belong to exactly one site. Invalid definitions use SiteRegistryError with stable error codes; malformed and unknown request hosts resolve to null.

Content sources

contentDocumentSchema defines IDs, route slugs, title, summary, lifecycle status, indexability, publication timestamps, tags, and body. Content is indexable by default and may opt out explicitly. The body may be empty for a metadata-only route whose presentation is supplied entirely by components or structured fields. Source-specific schemas may require a body when their content model needs one. defineContentSchema adds project-specific fields without allowing replacement of those base fields.

A ContentSourceAdapter returns raw values plus stable source references. validateContentSource validates every value, preserves provenance, and rejects duplicate IDs or slugs. The Node-only opensitestack/markdown entry point provides a recursive local Markdown/MDX adapter that ignores symbolic links. It also exports parseMarkdownDocument for applications that need the same YAML-frontmatter behavior without introducing a second parser.

Group inheritance

A site may map a named contentAreas entry to one groupId. Registry creation verifies that the group exists and that the site is an explicit member. The resolver always returns a complete site value first, otherwise the one group value, otherwise null; it never merges fields or follows another fallback.

const registry = defineSiteRegistry({
  sites: [
    {
      id: "alpha",
      name: "Alpha",
      domain: "alpha.example",
      canonicalOrigin: "https://alpha.example",
      metadata: {
        title: "Alpha",
        description: "Alpha website",
        locale: "en",
      },
      robots: { disallow: ["/api/"] },
      contentAreas: { legal: { groupId: "company-group" } },
      theme: "alpha",
    },
  ],
  groups: [
    { id: "company-group", name: "Company", siteIds: ["alpha"] },
  ],
});

Publication and SEO

createPublicationEntries is the canonical publication gate. It accepts route candidates for one resolved site and keeps only documents that are published, indexable, and whose optional publishedAt timestamp is due. Omitting publishedAt publishes timeless content immediately. It creates same-origin canonical URLs and rejects duplicate routes. Sitemap, search, feed, and page-level structured-data adapters consume only these entries, so they cannot silently disagree about publishability.

const entries = createPublicationEntries({
  site,
  candidates: documents.map((content) => ({
    content,
    pathname: `/articles/${content.slug}`,
  })),
});

const searchDocuments = projectPublicationEntries(entries, (entry) => ({
  id: entry.content.id,
  title: entry.content.title,
  url: entry.canonicalUrl,
}));

The package root exposes the pure createNextMetadata, createNextRobots, and createNextSitemap helpers. The opensitestack/next entry point additionally provides request-bound current-site resolution. All helpers use the already resolved site; content metadata and sitemap rows require a matching PublicationEntry. createNextRobots keeps the common allow-all policy and adds optional, validated site-level robots.disallow paths such as /api/. createWebsiteStructuredData and createWebPageStructuredData provide Schema.org objects, while serializeStructuredData escapes HTML delimiters before rendering JSON-LD in a native <script type="application/ld+json">.

Search and feed providers remain application choices. They should use projectPublicationEntries to transform the same filtered entries into their provider-specific records instead of implementing another publication filter.

Matrix discovery

The package root exposes builders for Matrix server and client discovery plus createMatrixWellKnownResponse. Applications keep their concrete homeserver, identity, authentication, and RTC values locally and use the shared helper in their /.well-known/matrix/server and /.well-known/matrix/client routes. Configured URLs must use HTTPS; responses receive interoperable CORS and cache headers. Sites without Matrix do not create these routes.

Consent, analytics, and forms

Sites opt into integrations with provider-neutral adapter IDs. A configured analytics adapter must name a purpose declared by the site's consent policy. Consent and analytics must use the same runtime.

The default application runtime validates consent state against the current policy version; malformed, stale, and undeclared grants fail closed. resolveAnalyticsScripts returns no scripts and does not call an adapter until the matching purpose is granted. It accepts only external HTTPS scripts with afterInteractive or lazyOnload strategies.

integrations: {
  consent: {
    adapterId: "site-consent",
    policyVersion: "2026-08",
    purposes: ["analytics"],
  },
  analytics: {
    adapterId: "site-analytics",
    consentPurpose: "analytics",
  },
  forms: {
    contact: { adapterId: "contact-delivery" },
  },
}
const consent = await resolveConsent({
  site,
  adapter: consentAdapter,
  input: consentCookie,
});

const scripts = await resolveAnalyticsScripts({
  site,
  consent,
  adapter: analyticsAdapter,
});

For an external consent manager that controls script activation, set both references to runtime: "consent-manager". Its adapter supplies validated HTTPS bootstrap descriptors through resolveConsentManagerScripts. Analytics adapters then supply inert external or inline descriptors tagged with the same manager ID and a manager-owned group. OpenSiteStack validates these descriptors but deliberately does not prescribe vendor-specific HTML attributes. Bootstrap descriptors default to afterInteractive so an external manager cannot mutate the document during React hydration; consumers may select beforeInteractive explicitly only when the provider is proven hydration-safe.

integrations: {
  consent: {
    adapterId: "consent-manager",
    policyVersion: "2026-08",
    purposes: ["analytics"],
    runtime: "consent-manager",
  },
  analytics: {
    adapterId: "site-analytics",
    consentPurpose: "analytics",
    runtime: "consent-manager",
  },
}

The consuming application maps those descriptors to the consent manager's documented inert-script markup. Inline content is trusted adapter code, never untrusted CMS or document content. The consent manager remains responsible for activation and durable consent state in this runtime.

The consuming application may map application-runtime descriptors to next/script; consent-manager descriptors require the manager-specific inert markup described above. The adapter implementation should be imported only when the site has the integration enabled. A site without analytics configuration always returns an empty list without invoking provider code.

Forms are mapped by a site-owned form ID to one adapter ID. A ServerFormAdapter owns a Zod schema and receives only the parsed value. Call submitSiteForm from a Server Action or Route Handler and select the site from the trusted request host, not from a submitted field.

const result = await submitSiteForm({
  site,
  formId: "contact",
  adapter: contactAdapter,
  input: {
    email: formData.get("email"),
    message: formData.get("message"),
  },
});

Schema validation does not replace authentication, authorization, CSRF/origin checks, abuse protection, rate limits, or provider credential handling. Those controls remain server-side responsibilities of the consuming application.

Themes and components

defineThemeRegistry validates site-owned design-token maps and can check every site's theme reference during application startup. Token names are arbitrary kebab-case identifiers; OpenSiteStack does not define colors, typography, spacing scales, or a shared visual language. createThemeStyle maps them to CSS Custom Properties without generating stylesheets or components.

const themes = defineThemeRegistry(
  {
    themes: [
      {
        id: "alpha",
        tokens: {
          "color-background": "#f4efe4",
          "font-display": "ui-serif, Georgia, serif",
          "content-width": "48rem",
        },
      },
    ],
  },
  siteRegistry.sites,
);

const theme = themes.getThemeForSite(site);
return <html data-theme={theme.id} style={createThemeStyle(theme)} />;

defineComponentSlots keeps shared route and data logic independent from brand-owned presentation. It freezes a typed default component map and accepts overrides only for known slots. Components are returned by identity without a wrapper, hook, or Client Component boundary, so Server Components remain the default and individual sites can replace complete layouts.

const homeSlots = defineComponentSlots({ Home: DefaultHome });
const betaSlots = homeSlots.resolve({ Home: BetaHome });

defineMdxComponents applies the same explicit allowlist to the component map passed to an MDX renderer. A site may override an allowed component but cannot add an undeclared one through the resolver. The allowlist does not sanitize MDX source or disable JavaScript expressions and imports; untrusted MDX still needs a restricted compiler pipeline.