# Pre-generated dumps

Your docs site is a Node project, and you don't want to add a Python toolchain to build it.
`griffe dump` writes the whole API surface of a package to one JSON file. The plugin can read that
file instead of running Griffe. The build machine then needs no `uv`, no interpreter, and no Python
at all. The Python project produces the file, and the docs site only reads it.

This method also works for a package with an environment that is hard to reproduce, for example one
with compiled extensions, private dependencies, or a pinned Python version. It is also how
[versioned documentation](/starlight-pydocs/guides/versioned-docs/) freezes the API surface of a
release.

## What a dump costs you

A dump is a snapshot. The docs build cannot refresh it. The pages describe the API as it was when
you wrote the file, until something writes a new one: a release workflow, a scheduled job, or you at
the keyboard. For a pinned release, that is exactly what you want. For a dump that tracks `main`, it
will go stale without warning.

Three things change at build time:

- Griffe never runs, so the options that configure it (`docstringStyle`, `docstringOptions`,
  `extensions`, and `forceInspection`) have no effect on a package with a `source`. Set them in the
  command that produces the dump instead. Member filters, source links, the sidebar, and search
  still apply, because these are rendering concerns.
- There are no Python files to watch, so `astro dev` does not reload when you regenerate a dump.
  Restart the dev server.
- `versions: { refs }` ([version annotations](/starlight-pydocs/guides/version-annotations/))
  extracts each ref from a git checkout, so you cannot combine it with `source`. The plugin throws an
  error telling you to document that release as its own `packages` entry.

## Generate, publish, point the config at it

1. Extract the API surface in the Python project, running from the repository root:

   ```sh /-f\b/ /-d\b/
   uvx --from griffe griffe dump -f -d google --search src mypkg -o api.json
   ```

   You need both `-f` and `-d`. Griffe does not warn you if you leave either one out. See
   [the command in detail](#the-command-in-detail).

2. Publish the file where the docs build can read it. Dumps are read-only build inputs and compress
   well, so a release asset is enough:

   ```yaml title=".github/workflows/api-dump.yml" {21}
   name: Publish API dump

   on:
     release:
       types: [published]

   permissions:
     contents: write

   jobs:
     dump:
       runs-on: ubuntu-latest
       steps:
         - uses: actions/checkout@v7
         - uses: astral-sh/setup-uv@v7
         - name: Extract the API surface
           run: uvx --from griffe griffe dump -f -d google --search src mypkg -o api.json
         - name: Attach it to the release
           env:
             GH_TOKEN: ${{ github.token }}
           run: gh release upload "${{ github.event.release.tag_name }}" api.json
   ```

3. Point the package entry at the published URL:

   ```js title="astro.config.mjs" {5}
   starlightPydocs({
     packages: [
       {
         name: 'mypkg',
         source: { url: 'https://github.com/you/mypkg/releases/download/v2.1.0/api.json' },
       },
     ],
   });
   ```

Point at a tag for a pinned version. Or upload to a stable location, such as an S3 bucket, a
`latest` release, or a file on `gh-pages`, and let revalidation keep the site current.

## Downloads and the cache

`url` must be an `http(s)` URL. The plugin caches the download under `cacheDir`
(`node_modules/.astro` by default), together with the `ETag` and `Last-Modified` headers the server
sent. The `cache` option decides what the next build does with it:

| `cache`        | Behaviour                                                                            |
| -------------- | ------------------------------------------------------------------------------------ |
| `'revalidate'` | Default. Sends `If-None-Match` / `If-Modified-Since`; a `304` reuses the cached copy. |
| `'force'`      | Never contacts the server when a cached copy exists. Fastest, and offline-safe.       |
| `'bypass'`     | Downloads on every build.                                                             |

If the host is unreachable, or answers with an error, and a cached copy exists, the build logs a
warning and uses the cached copy. A documentation build should not fail just because a release asset
was briefly unavailable. With no cached copy, the build fails, and the error message names the URL
and the status.

:::caution[A dump is content for your site]
Everything in the dump ends up on your rendered pages. The markdown renderer passes raw HTML through
unsanitised, the same trust model as mkdocstrings. Treat a dump like the rest of your docs, and only
use dumps whose source you trust.
:::

## Committing the dump instead

Ship the dump inside the docs repository. The build then touches no network, and you can review the
change in a pull request:

```js title="astro.config.mjs" {2}
starlightPydocs({
  packages: [{ name: 'mypkg', source: { file: './dumps/mypkg.json' } }],
});
```

The plugin resolves `file` relative to the Astro project root. A missing file fails the build, and
the message names the path it looked for. A dump is a large JSON file, so expect noisy diffs. If
your repository cannot tolerate that, use the URL form instead.

## Extraction order

The plugin gets a dump in a fixed order: an explicit `runner.command` first, then a `source` dump,
then `uvx --from griffe`, then `python -m griffe`. A `runner.command` therefore beats `source`. If
you set one globally, every package runs it, including packages that carry a `source`.

## The command in detail

Without `uvx`, when `griffe` is already installed:

```sh /-f\b/ /-d\b/
griffe dump -f -d google --search src mypkg -o api.json
```

This is exactly what the plugin runs for itself. The flags:

- `-f` (`--full`) adds file paths, line numbers, and visibility flags. Source links, member
  filtering, and provenance all need this data. Without `-f`, every object arrives as a bare stub.
- `-d <style>` parses docstrings into structured sections. Set `<style>` to `google`, `numpy`,
  `sphinx`, or `auto`. Without `-d`, a docstring stays one raw string, and the plugin cannot render a
  parameter table, returns block, or admonition.
- `--search` (`-s`) points at the directory containing the package, and can be repeated.
- `-o` writes the JSON to a file. Without it, griffe writes to standard output.

Add the flags your package needs, and keep them in step with what the docs site expects to render.
A pydantic model layer needs the Griffe extension installed alongside Griffe (`--with`) and enabled
(`-e`). `-D` takes parser options as JSON:

```sh /-D\b/ /-e\b/ "--with"
uvx --with griffe-pydantic --from griffe griffe dump -f -d google \
  -D '{"returns_named_value": false}' -e griffe_pydantic --search src mypkg -o api.json
```

## Source links with a dump from elsewhere

Griffe builds source links from the paths it recorded, which are relative to the directory where
griffe ran. Run the dump command from the repository root, and `{path}` is already
repository-relative. This is what a forge URL needs:

```js title="astro.config.mjs" {7-13}
starlightPydocs({
  packages: [
    {
      name: 'mypkg',
      source: { url: 'https://…/v2.1.0/api.json' },

      // No root: the paths in the dump are already
      // relative to the repository.
      sourceLink: {
        host: 'github',
        repo: 'you/mypkg',
        ref: 'v2.1.0',
      },
    },
  ],
});
```

Do not set `sourceLink.root` for a dump produced on another machine. The plugin applies `root` to
the absolute path griffe recorded, and that path belongs to the CI runner, so it means nothing
locally. `root` is for the case where extraction runs on this machine and the sources sit outside
the Astro project. See [Source links](/starlight-pydocs/guides/source-links/).

Griffe's own `source_link` is another option. Griffe 2 records a commit-pinned blob URL for each
object when it runs inside a git checkout. A dump made in CI often carries working links with no
`sourceLink` configuration at all.

## Checking a dump

A dump is an object keyed by package name, with the whole tree underneath:

```sh
jq 'keys' api.json                          # ["mypkg"]
jq '.mypkg.members | keys' api.json         # the top-level surface
jq '.mypkg.members.Report.docstring.parsed[0].kind' api.json   # "text" if -d ran
```

If `docstring.parsed` is missing, the dump was made without `-d`. The pages will then carry
signatures with no parameter tables or admonitions. If objects have no `filepath`, the dump was made
without `-f`. Source links and the visibility-based member filters then have nothing to work from.
The plugin cannot detect either problem for you: both only produce thinner pages, not a failed
build. Check a dump the first time a new pipeline produces one. A file that is not a griffe dump, or
does not contain the configured package, does fail the build. The error names the file and lists
what the dump does contain.