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

executable-stories-astro

v0.1.0

Published

Astro integration for executable-stories: a content loader that turns the test run JSON into a live, hot-reloading docs collection.

Readme

executable-stories-astro

Make Astro a first-class way to view living documentation. An Astro integration + content loaders that turn your test run JSON into a hot-reloading docs site — generated scenarios and your hand-authored docs, side by side, driven by one config object.

npx --package executable-stories-formatters executable-stories init-astro my-docs   # scaffold (recommended)

One config drives everything

Author the config once and hand the same object to both halves of the integration. This is the single source of truth for sources, scenario selection, categorisation, authored docs, navigation, and theme.

// executable-stories.config.mjs
import { defineExecutableStories } from 'executable-stories-astro';

export default defineExecutableStories({
  // ── Sources: what test output to include ──────────────────────────
  source: '../reports/raw-run.json',        // single suite (shorthand)
  // sources: [                              // …or several, grouped by suite
  //   { name: 'web', label: 'Web app', source: '../apps/web/reports/raw-run.json' },
  //   { name: 'api', label: 'API',     source: '../apps/api/reports/raw-run.json' },
  // ],

  // ── Selection: which scenarios to show ────────────────────────────
  include: { tags: ['security'] },          // allowlist (tags | status | features)
  exclude: { status: ['skipped'] },         // denylist, applied after include

  // ── Categorisation ────────────────────────────────────────────────
  groupBy: 'tag',                           // 'feature' | 'tag' | 'source' | 'status' | 'none'

  // ── Authored docs: bring existing markdown in ─────────────────────
  docs: [{ path: 'src/content/docs/runbooks', label: 'Runbooks', base: 'runbooks' }],

  // ── Routes & theme ────────────────────────────────────────────────
  routeBase: '/stories',                    // default
  explorerBase: '/explorer',                // default
  theme: { accent: '#0b7285' },
});
// astro.config.mjs
import { executableStories, storiesSidebar } from 'executable-stories-astro';
import esConfig from './executable-stories.config.mjs';

export default defineConfig({
  integrations: [
    executableStories(esConfig),            // injects /stories, /stories/<slug>, /explorer
    starlight({
      sidebar: [{ label: 'Home', slug: 'index' }, ...storiesSidebar(esConfig)],
    }),
  ],
});
// src/content.config.ts
import { storiesLoader, trajectoryLoader, authoredDocsLoader } from 'executable-stories-astro';
import esConfig from '../executable-stories.config.mjs';

export const collections = {
  docs: defineCollection({ loader: authoredDocsLoader({ path: 'src/content/docs' }), schema: docsSchema() }),
  stories: defineCollection({ loader: storiesLoader(esConfig) }),
  trajectory: defineCollection({ loader: trajectoryLoader(esConfig) }),
};

Config reference

| Field | Type | Default | What it does | |---|---|---|---| | source | string | — | One run JSON (shorthand for sources: [{ source }]). | | sources | StorySource[] | — | Several named suites. { name?, label?, source, inputType?, synthesize? }. Names are derived from the path when omitted. | | include | StoryFilter | — | Allowlist: { tags?, status?, features? }. A scenario must match. | | exclude | StoryFilter | — | Denylist, applied after include. | | groupBy | GroupBy | 'feature' | How the index/explorer categorise scenarios: feature, tag (a scenario appears under each tag), source (suite), status, or none. | | docs | AuthoredDocsSource[] | — | Authored markdown folders to surface in the nav: { path, label?, base? }. | | collection | string | 'stories' | Collection name the loader feeds. | | routeBase | string | '/stories' | Where the stories index + detail pages mount. | | explorerBase | string | '/explorer' | Where the searchable Scenario Explorer mounts. | | injectStoryRoute | boolean | true | Inject the stories index + detail routes. | | injectExplorer | boolean | true | Inject the Scenario Explorer. | | theme.accent | string | — | Accent colour for the standalone story pages. |

What you get

  • Injected routes — a stories index at routeBase, one detail page per scenario, and a searchable/filterable Explorer. All styled out of the box (you do not wire any CSS) and link-correct for any routeBase.
  • Hot reload — the loader watches the run JSON; a fresh test run updates the open page with no reload. Nothing is written to disk; tests stay the source of truth.
  • storiesSidebar(config) — builds Starlight sidebar entries from the config (Stories, Explorer, and a group per docs source) so you don't hand-wire nav.

Authored docs

authoredDocsLoader({ path }) is a drop-in for Starlight's docsLoader() that makes existing, GitHub-style docs work without edits:

  • Auto-title — fills a missing title from each file's first # H1 (the one field Starlight requires), so frontmatter-free markdown imports cleanly.
  • Cross-link rewriting — rewrites relative ./other.md links to their routes, so doc-to-doc links don't 404.
  • External folders — point path at a folder outside the site and set base to mount it under a URL prefix.

The cross-link rewriting happens inside the loader (it rewrites the markdown body before rendering), so it is independent of the markdown processor. This matters on Astro 7: the default processor is now Sätteri (Rust), which does not run remark plugins — markdown.remarkPlugins only applies if you install @astrojs/markdown-remark and opt into the unified() processor. mdLinkRewrite() is also exported as a standalone remark plugin for that unified pipeline, but with authoredDocsLoader you don't need it.

Emitting the run JSON

The loader reads the raw run JSON, which your reporter writes only when you set rawRunPath:

new StoryReporter({ formats: ['html'], rawRunPath: 'reports/raw-run.json' })

Then run your tests in watch mode in one terminal and astro dev in another.