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

v7.6.5

Published

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

Downloads

1,020

Readme

executable-stories-astro

Make Astro a first-class way to view living documentation. An Astro integration + content loaders that turn canonical per-source reports or an intentional run snapshot 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/by-file',             // whole local suite (shorthand)
  // sources: [                              // …or several, grouped by suite
  //   { name: 'web', label: 'Web app', source: '../apps/web/reports/by-file' },
  //   { name: 'api', label: 'API',     source: '../apps/api/reports/by-file' },
  // ],

  // ── 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 per-file directory or run snapshot (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? }. | | views | PersonaView[] | — | Audience lenses: { base, label?, description?, include?, exclude?, groupBy? } — each mounts a filtered, re-grouped index at its own URL (e.g. /for/product) and joins the sidebar under "Audiences". | | journeysBase | string | '/journeys' | Where journey walkthroughs mount. Journeys derive from journey:<id>[:<order>] tags: ordered member scenarios rendered as full cards under one aggregate status. Embed with <StoryJourney id="..."/>. | | injectJourneys | boolean | true | Inject the journeys index + detail routes. | | statesBase | string | '/states' | Where the UI-state catalog mounts: a thumbnail grid of state:<name>-tagged scenarios (first storyboard frame per card; viewport:* variants side by side). | | injectStates | boolean | true | Inject the states grid route. | | driftBase | string | '/drift' | Where the environment-drift page mounts: every scenario's status per source side by side, mismatches first. | | injectDrift | boolean | ≥2 sources | Inject the drift page. Defaults to on only when there are two or more sources to compare. | | historyFile | string | — | Path to the store the CLI's --history-file maintains. Journey pages then show journey-level run history ("7/10 recent runs passed · flaky"). | | 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. | | agentEndpoints | boolean | true | Inject /llms.txt + a Markdown twin per story at <routeBase>/<slug>.md. | | 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 configured report source; a focused test run updates the open page without hiding untouched scenarios. Generated state is disposable; tests stay the source of truth. When a run changes the nav tree (scenario added/renamed/removed) the integration triggers a dev-server restart so the Starlight sidebar rebuilds too; status-only changes stay pure HMR.
  • storiesSidebar(config) — builds Starlight sidebar entries from the config (Stories, Explorer, and a group per docs source) so you don't hand-wire nav.
  • Agent endpoints/llms.txt (an llms.txt-format index of every scenario) and a plain-Markdown twin of each story page at <routeBase>/<slug>.md, prerendered as real files on static builds — so the deployed site is consumable by agents and curl, not just browsers.
  • Design contextstory.link() docs pointing at Figma/Zeplin/Sketch (or labelled "Design ...") render as a Design strip on story and journey pages, so the mockup sits next to the proof. No new API, no config.
  • Self-tuning Vite config — the integration pre-bundles React + the report components (optimizeDeps) and dedupes React itself; no vite block needed in your astro.config.mjs.

Embedding scenarios in authored pages

Hand-written MDX can pull scenarios in as live evidence, rendered from the same collection as the story pages:

import StoryScenario from 'executable-stories-astro/components/StoryScenario.astro';
import StoryStatus from 'executable-stories-astro/components/StoryStatus.astro';

We cap discounts at 30% — enforced end-to-end
(currently <StoryStatus id="checkout--caps-the-discount-at-30-percent" />):

<StoryScenario id="checkout--caps-the-discount-at-30-percent" />

<StoryScenario/> renders the full scenario card (steps, status, failure output, attached docs); <StoryStatus/> is an inline linked status pill. Both resolve id against the stable scenario id, the URL slug, or the exact title, and render a visible callout when the reference no longer matches, so embedded evidence never silently disappears. Pair with <VerifiedBy/> for page-level verifiedBy: frontmatter badges.

Both take the same optional props:

| Prop | Type | Default | What it does | |---|---|---|---| | id | string | — | The scenario to resolve (stable id, slug, or exact title). Required. | | collection | string | configured collection | Resolve against a different loader collection. | | title | boolean | true (StoryScenario) / false (StoryStatus) | Show the scenario title (the card's title row / the label beside the status pill). | | link | boolean | true | StoryScenario only: show the "View full scenario" link under the card. |

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 per-source report state

The reporter maintains one canonical report per test source in reports/by-file/, which is the recommended local site source. Keep rawRunPath when current-run CLI commands also need the execution event:

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

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