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

@pagenary/publisher

v2026.9.1

Published

Multi-tenant documentation static site generator with Markdown, search, SEO snapshots, and docs-as-code publishing.

Downloads

7,513

Readme

Pagenary Publisher

Where documentation takes shape.

@pagenary/publisher is the static site generator behind Pagenary — it turns one shared template catalog into many branded, tenant-specific documentation sites. Self-hosted static output by default, hash-based routing, full-text search, and a Git-aware build pipeline. Install it as a dev dependency and drive it with the pagenary CLI.

Built with AIWG, the multi-agent AI framework used to plan, audit, and ship this project.

npm install --save-dev @pagenary/publisher   # add Pagenary to your project
npx pagenary build my-docs           # build your docs tenant
npx pagenary serve                           # serve on http://localhost:5173

npm version npm downloads Docs License: AGPL v3 Node Version Search: @fortemi/core Built with AIWG

Docs Site · Quick Start · Features · Tenant Workflow · Documentation


What It Is

The publisher takes a catalog of shared section templates plus per-tenant content and configuration and produces a self-contained documentation bundle for each tenant. Each bundle is a static single-page app — hash-based routing (#page-id), no application server, with self-hosted browser assets by default — that you build once and host anywhere that serves files. A per-tenant <base> resolves asset and module URLs to the tenant root, so the same bundle serves correctly at a domain root or under a subpath mount. Tenants share the template catalog but keep isolated content, branding, navigation, and domains — each with ranked client-side search and SEO-ready output — so one repository can publish a dozen distinct sites.


Quick Start

Install the package and drive it with the pagenary CLI — no clone required. New here? Follow the Getting Started guide.

npm install --save-dev @pagenary/publisher

npx pagenary build my-docs   # build your tenant (see Tenant Registry below)
npx pagenary serve                   # preview on http://localhost:5173

Commands: build, serve, tenants list, check, new (run npx pagenary --help for the full list). The package also ships a compiled reference site under site/ — the Pagenary docs, built by Pagenary itself.

Building from source (contributors / modifying Pagenary):

npm install
npm run dev         # build + serve with watch mode
npm run build       # build default bundle to dist/

Pagenary development uses AIWG. On this host, maintainers can inspect, build, or run the AIWG project from ~/dev/aiwg.


Optional writable runtimes

The optional @pagenary/react adapter exposes record and PGlite runtimes, reviewed published-source refresh, local dataset imports, and scoped lexical search. These capabilities require explicit tenant/application integration; the default publisher search remains static and read-only. See the runtime API and boundaries and dependency tiers.

For repository tests, use Node 24.12.0 and run npm run test:setup from the repository root before npm test. Browser layout tests are part of the publisher suite. See Testing Pagenary for the complete prerequisites and qualification scope.

Features

Content Authoring

  • Markdown — write in .md files with full CommonMark support
  • HTML — direct markup control with .html files
  • JavaScript Modules — dynamic content with .js files returning { html, afterRender? }
  • Nested Directories — organize content in subdirectories (content/guides/setup.md)
  • React Brochureware — load a validated canonical content module while the SPA owns presentation; emit semantic route HTML, search, sitemap, JSON/text, and LLM-readable artifacts (guide)

Rich Content

  • Inline LaTeX — full KaTeX-supported math syntax in inline/display contexts, rendered to local HTML + MathML with bounded macros and expansion
  • LaTeX Pages — convert complete .tex pages to semantic HTML + MathML through an explicitly selected local converter profile
  • Mermaid Diagrams — local pinned Mermaid 11.16.0 runtime by default, with optional sanitized build-time SVG
  • PlantUML Diagrams — local-only build rendering with the PlantUML SANDBOX security profile and contained includes
  • Rich Content Guide — syntax, configuration, prerequisites, security boundaries, and failure behavior (guide)
  • Interactive Images — opt-in bounded zoom, pan, and reset for SVG, PNG, and JPEG/JPG with accessible text and keyboard/touch controls
  • Markdown Delivery — optionally emit per-route .md representations and serve them from the same canonical URL when Accept: text/markdown is preferred
  • Syntax Highlighting — Prism.js with 10+ language support
  • Markdown Tables — full table syntax with alignment support
  • HTML Components — spec tables, layer stacks, box diagrams, cards
  • Internal Links — auto-resolved #section-id links in Markdown

External Links

  • Navigation Links — add external URLs directly in the manifest with a url property
  • Smart Link Handling — external links open in a new tab with security headers (rel="noopener noreferrer")
  • Visual Indicators — a subtle ↗ icon marks external destinations
  • CTA Styling — button-like external-cta class for prominent external links

Navigation & Search

  • Command Palette — Ctrl/Cmd+K or / opens a global finder
  • Fortemi-backed full-text search — ranked results with snippets over a static chunked index emitted at build time; lazy chunk fetch (precache) and offset paging for infinite scroll, with a clean in-browser fallback. No server, no WASM. The engine is vendored (@fortemi/core) — see docs/VENDORING.md for the process, docs/ARCHITECTURE.md, and .aiwg/architecture/adr/ADR-015-fortemi-core-search-adapter.md.
  • Docs Map — opt-in static graph view over the same Fortemi corpus, with weighted relationships, zoom/pan controls, neighbor highlighting, and node metadata popups.
  • Manifest-Driven Nav — declarative navigation structure
  • Keyboard Navigation — arrow keys, Enter to select

Theming & Branding

  • Custom Colors — accentColor and surfaceColor per tenant
  • Brand Identity — logo text, tagline, copyright
  • Typography — IBM Plex Sans/Mono defaults, customizable

SEO (built in)

  • Metadata-driven titles — the shell <title> derives from the default page's metadata title ("<page title> · <brand>"), and each section sets its own title/description at runtime; the generic brand is only a fallback
  • Absolute URLs — declare a domain (or seo.siteUrl) and the sitemap, canonical, og:url, and robots URLs become fully-qualified
  • Static snapshots — crawler-friendly /pages/<id>.html for every section, self-canonical (the SPA hash route isn't crawlable)
  • Root HTML fallback — seo.rootHtmlFallback defaults on, embedding the default page in index.html so no-JS readers do not see an empty shell
  • sitemap.xml, robots.txt, llms.txt — generated automatically
  • JSON-LD + Open Graph — TechArticle/BreadcrumbList per page, optional Organization data, and og:image/twitter:image via seo.ogImage
  • Subpath-safe assets — a per-tenant <base> keeps stylesheet/script/snapshot URLs valid whether served at a domain root or a subpath

Export & Sharing

  • Export Options — Current Page or Entire Site
  • Branded Exports — tenant logo, brand name, and tagline in the export header
  • Document Export — one-click HTML export with a table of contents, print-optimized for PDF

Tenant Content Workflow

Directory Structure

my-tenant/
├── config.json           # Branding, theme, and SEO settings
├── manifest.json         # Navigation structure (optional)
├── content/              # Content files
│   ├── welcome.md        # Root-level content
│   ├── guides/           # Nested directory
│   │   ├── _manifest.json  # Section manifest
│   │   ├── getting-started.md
│   │   └── advanced.md
│   └── api/
│       ├── _manifest.json
│       └── reference.md
└── overrides/            # Post-build file replacements (optional)

Content Types

Markdown (.md)

# Getting Started

Welcome to the docs. Here's a code example:

\`\`\`javascript
console.log('Hello, Pagenary!');
\`\`\`

And a Mermaid diagram:

\`\`\`mermaid
graph LR
    A[Start] --> B[Build]
    B --> C[Deploy]
\`\`\`

Markdown also supports build-time inline LaTeX with \(...\), \[...\], and $$...$$; single-dollar math is opt-in. See the rich technical content guide for syntax and bounds.

LaTeX (.tex)

\documentclass{article}
\usepackage{amsmath}
\begin{document}
\section{A semantic LaTeX page}
\[ E = mc^2 \]
\end{document}

Complete .tex pages require the local converter selected by latexPage.profile. Pagenary does not install converters or TeX packages during the build.

HTML (.html)

<section class="section doc">
  <h1>Custom HTML Section</h1>
  <table class="spec-table">
    <tr><th>Feature</th><th>Status</th></tr>
    <tr><td>Search</td><td>Ready</td></tr>
  </table>
</section>

JavaScript (.js)

export async function load() {
  const data = await fetch('/api/metrics.json').then(r => r.json());
  return {
    html: `<section><h1>Metrics: ${data.count}</h1></section>`,
    afterRender(container) {
      // DOM manipulation after render
    }
  };
}

Manifest Configuration

Root manifest.json (optional — auto-generated from content/ if omitted):

[
  { "id": "welcome", "title": "Welcome", "file": "welcome.md" },
  {
    "id": "guides",
    "title": "Guides",
    "subsections": [
      { "id": "guides/getting-started", "title": "Getting Started", "file": "guides/getting-started.md" },
      { "id": "guides/advanced", "title": "Advanced Usage", "file": "guides/advanced.md" }
    ]
  }
]

Section _manifest.json (in content subdirectories):

{
  "title": "API Reference",
  "sections": [
    { "id": "overview", "title": "Overview", "file": "overview.md" },
    { "id": "endpoints", "title": "Endpoints", "file": "endpoints.md" }
  ]
}

External links in the manifest (use url instead of file):

[
  { "id": "welcome", "title": "Welcome", "file": "welcome.md" },
  { "title": "Support Portal", "url": "https://support.example.com" }
]

Branding & SEO Configuration

config.json:

{
  "title": "My Documentation",
  "description": "Comprehensive guide to our platform",
  "brandMark": "ACME",
  "brandSub": "Docs",
  "tagline": "Build better, faster",
  "copyright": "ACME Corp",
  "accentColor": "#6366F1",
  "surfaceColor": "#F7FAFC",
  "domain": "docs.acme.com",
  "seo": {
    "siteUrl": "https://docs.acme.com",
    "ogImage": "/assets/og-card.png",
    "structuredData": { "organizationName": "ACME Corporation" }
  }
}

| Property | Description | Default | |----------|-------------|---------| | title | Browser tab title | "Documentation" | | description | Meta description for SEO | - | | brandMark | Primary brand text (bold) | "DOCS" | | brandSub | Secondary brand text (light) | "TOOLKIT" | | tagline | Subtitle under brand | - | | copyright | Footer copyright text | - | | accentColor | Links, buttons, highlights | #111111 | | surfaceColor | Background color (hex) | #ffffff | | domain | Canonical domain; also the SEO base URL when seo.siteUrl is unset | - | | seo | SEO block — see Tenant Configuration | - |


Build Commands

With the package installed (the default):

npx pagenary build                    # build the default bundle to dist/
npx pagenary build --all              # build all enabled tenants
npx pagenary build my-tenant          # build a specific tenant
npx pagenary tenants list             # list configured tenants
npx pagenary serve                    # serve dist/ on localhost:5173

From source (clone — adds dev/utility scripts):

npm run build:incremental my-tenant  # git-aware incremental rebuild
npm run dev                          # build + serve with watch
npm run lint:content                 # check trailing whitespace/tabs
npm run check:seo                    # verify SEO metadata
npm run check                        # run all checks
npm test                             # run test suite
npm run test:browser                 # optional real-browser smoke (see below)

Browser smoke test (optional)

npm run test:browser runs scripts/smoke-browser.mjs, a real-browser check for things the jest/node suite can't cover: <base href> resolution under a subpath mount, asset + section loading, the runtime <title> brand, and Fortemi command-palette search. It builds + serves the pagenary tenant and drives a headless Chromium, capturing a screenshot for review.

Playwright is not a dependency (keeps the install lean); the script skips with instructions when it's absent. Enable it once:

npm i -D playwright && npx playwright install chromium
SMOKE_REQUIRE=1 npm run test:browser   # fail (not skip) if Playwright is missing — use in CI

Tenant Registry

Register tenants in a tenants.json at your project root (validated by the bundled tenants.schema.json):

{
  "tenants": [
    {
      "id": "my-docs",
      "source": { "type": "local", "path": "./docs" },
      "strictLinks": true
    },
    {
      "id": "client-portal",
      "source": { "type": "git", "url": "https://github.com/org/client-docs.git", "ref": "main" },
      "domains": ["docs.client.com"]
    }
  ]
}

Source types:

  • Local: { "type": "local", "path": "./relative/or/abs/path" }
  • Git: { "type": "git", "url": "https://…", "ref": "main", "path": "subdir" }

Per-tenant options include enabled (default true), strictLinks (default true — fail the build on broken internal links), and domains (for the multi-tenant Caddy router). See Tenant Configuration.


Docker Caddy Workflow

For multi-tenant domain testing:

# Add to /etc/hosts:
# 127.0.0.1 my-docs.local client-portal.local

npm run build:tenants   # build tenants
npm run caddy:up        # start Caddy
# Visit http://my-docs.local or http://client-portal.local

npm run caddy:logs      # tail logs
npm run caddy:reload    # reload config without restart
npm run caddy:restart   # full restart
npm run caddy:down      # stop container

Use a non-privileged port: DOCS_TOOLKIT_PORT=5173 npm run caddy:up


Repository Layout

apps/publisher/
├── src/
│   ├── index.html          # SPA shell
│   ├── app.js              # Router and core logic
│   ├── styles.css          # All styling
│   ├── manifest.js         # Default navigation
│   ├── seo.js              # Runtime meta tag management
│   ├── mermaid-init.js     # Diagram rendering
│   ├── syntax-highlight.js # Code highlighting
│   └── lib/                # search, router, export
├── scripts/
│   ├── build.js            # Core build script
│   ├── build-tenants.js    # Multi-tenant builder
│   ├── serve.js            # Dev server
│   └── lib/seo-generator.js # Sitemap, robots, snapshots, JSON-LD
├── tenants/                # Built-in example tenants
├── docs/                   # Documentation
└── Caddyfile, docker-compose.yml  # Multi-tenant routing

Documentation

The full documentation site is published at docs.pagenary.com — built by this publisher from the source below. Read it online, or browse the source:


License

GNU Affero General Public License v3.0 — strong copyleft. You may use, modify, and distribute Pagenary, but if you run a modified version to provide a network service, you must make the modified source available to its users. See LICENSE.


Back to Top

Made with care by Joseph Magly