---
changelog:
  category: Release
  version: blume@1.6.0
date: '2026-09-04T05:43:31Z'
seo:
  description: >-
    Native GraphQL API reference. Point the new top-level graphql config block
    at a schema — SDL text or an introspection JSON result, local or remote —
    and Blume…
title: blume@1.6.0
type: changelog
---
### Minor Changes

- 089f0d2: Native GraphQL API reference. Point the new top-level `graphql` config block at a schema — SDL text or an introspection JSON result, local or remote — and Blume generates one real page per root field (grouped as Queries, Mutations, and Subscriptions) plus one page per named type (Objects, Input Objects, Enums, Interfaces, Unions, and custom Scalars), all in the sidebar, search, llms.txt, and OG images like any hand-written doc. Operation pages show arguments with defaults and deprecations, a generated example operation with typed variables and a matching example response, live code samples, and the same Try it playground the OpenAPI reference ships — a plain JSON POST of `{ query, variables }` against the configured `endpoint`, with `playground.proxy` support for CORS-restricted APIs. Type pages cross-link every type reference and list where each type is used.
- 65e3853: Add `github.host` so docs hosted on a GitHub Enterprise instance get working repo links. Every repo-derived URL — the header mark, per-page edit links, the agent manifest's `repository`, the OG card's slug, and `<GithubInfo>` — was built against a hardcoded `github.com`, so an Enterprise repo produced links into the public site. `host` defaults to `https://github.com`, must be an HTTP(S) URL, and is normalized to its origin so nothing lands mid-link. The REST base `<GithubInfo>` queries is derived from it (an `api.` subdomain for an Enterprise Cloud data-residency tenant, `/api/v3` for Enterprise Server) or set outright with `github.api`, and a `host` prop points one card at a different instance. `<GithubInfo>` withholds its token from a cleartext-HTTP API base rather than sending it over the wire, and warns once per base so a private repo's missing counts are explained.
- 2fa67b8: Custom heading anchors and table-of-contents markers, matching Fumadocs' syntax so migrated content works verbatim. Append `[#custom-id]` to any heading to pin its anchor id — the marker never renders, links keep working after a heading is reworded, and pinned anchors stay identical across translated locales, where auto-generated ids differ per language. `[!toc]` keeps a heading on the page but out of the table of contents; `[toc]` does the reverse, adding a TOC-only entry that renders as an invisible anchor target so its link still scrolls somewhere — useful for labeling sections built from components rather than prose. Markers chain in any order (`## Heading [toc] [#id]`), work in Markdown and MDX, are stripped from search indexing, and `blume validate` resolves anchor links against pinned ids (including case-sensitive ones). Marker parsing is always on: a heading that previously ended in literal marker-shaped text now parses as marked — wrap the bracket text in inline code (`` ## Using `[toc]` ``) to render it literally. The one exception is CommonMark's own: a trailing bracket whose label has a link-reference definition on the page (`[toc]: /url`, including one nested in a block quote or list item) is a shortcut reference link and stays in the heading text; a bare `[toc]:` with no destination defines nothing.
- beec2d2: Add `navigation.actions` and `navigation.cta` — plain links in the header, and the one filled button. `navigation.featured` pins secondary links to the top of the sidebar and there was no header slot at all, so a docs site arriving from a hosted platform lost whatever its old top bar asked the reader to do. `cta` is singular by contract. Both hide below the `sm` breakpoint, where the header has room for the logo and the drawer toggle and nothing else — except `cta` on a page with no navigation toggle, where it stays, since nothing else can surface it on a phone.
- beec2d2: `navigation.repo` now takes a URL as well as a boolean. `github` drives the per-page edit link, the header mark and the agent manifest's `repository` together, so a project whose docs repo is private had to unset all three — and then had no way to show a mark at all. An absolute URL points it anywhere on GitHub, an organization for instance, while `true` keeps deriving it from `github` and `false` still hides it.
- a669f80: The Notion source now targets `@notionhq/client` v5 and Notion's data source API: a database is read through its first data source. Upgrade the peer dependency with `npm install @notionhq/client@^5`.
- 9ec4de4: Add a built-in `obsidian` content source. Point it at an Obsidian vault and Blume reads the notes in place — no export step, and nothing generated into your repo:
  
  ```ts
  export default defineConfig({
    content: {
      sources: [{ type: "obsidian", vault: "vault", prefix: "notes" }],
    },
  });
  ```
  
  - `[[Wikilinks]]` become route links, addressed by note name across the whole vault the way Obsidian addresses notes: custom link text (`[[Note|label]]`, and `[[Note\|label]]` inside a table), heading anchors (`[[Note#Install]]`, `[[#Install]]`), block references (`[[Note#^id]]`), full and partial paths (`[[folder/Note]]`, `[[Note.md]]`), and a frontmatter `slug`. A name two notes share resolves to an exact path match first, then to the first note in vault order, and warns only when a link actually resolves through the collision.
  - Heading anchors resolve against the target note's real headings, matched the way Obsidian writes them (inline formatting stripped) and slugged by the same pass that fills the page manifest.
  - Frontmatter keeps Blume's page meta plus any key declared in `frontmatter.extend` or, for notes of that type, in a content type's `frontmatter`; every other Obsidian property (including `tags`, `aliases`, and `cssclasses`) is dropped, so a vault written with the Properties UI builds cleanly.
  - Locale directories and version snapshots inside the vault publish under their locale and version like filesystem content.
  - An unresolved wikilink degrades to plain text with a warning, and one to a missing heading keeps the page link; single-line `%%comments%%` are stripped and a wikilink inside an HTML comment is left alone; fenced, indented, and inline code pass through verbatim; symlinked folders are followed; untitled notes take their filename as the title; dot-folders (`.obsidian`, `.trash`, plugin caches) and the usual never-content directories are skipped by both the scan and the dev watcher; `blume version cut` leaves a vault inside `content.root` out of the snapshot.
  - Vault notes get git "Last updated" dates when the vault is inside the repository, and "Edit this page" links through `github.dir` for a vault beside a monorepo docs app.
  
  `blume init` offers the source and seeds a first note. Not yet lowered: callouts, embeds, multi-line comments, aliases as link targets, and backlinks.
  
  Also in this release: `frontmatter`'s `stringify` no longer re-parses a string body (a body opening with a `---` divider was read as a second front matter block), and git last-modified dating now skips the scan entirely when no local source exposes a content root — a custom staged source that sets `sourcePath` without a `contentRoot` is no longer dated — set `contentRoot` on the source to date its pages.
- 20a5da5: Content includes: `<include>./path.mdx</include>` splices another file into a page at build time — the same syntax Fumadocs ships, so migrated content works unchanged. Markdown/MDX targets are embedded as content (front matter stripped, nested includes with cycle detection, relative images rebased onto the including page); any other extension — or an explicit `lang` attribute — embeds the file as a fenced code block, with `meta` for the fence meta string. Paths resolve relative to the including file, or from the content root with a leading `/`. Spliced content flows through every surface: headings join the page's table of contents and anchor index, search indexes the included text, the `.md`/`.mdx` mirrors and llms-full.txt serve the expanded document, broken links inside a partial are reported against the partial file, and editing a partial in `blume dev` reloads the pages that include it. Underscore-prefixed files (`_snippets/`) are already excluded from routing, so partials need no configuration. Unresolvable statements surface as `BLUME_INCLUDE_NOT_FOUND`, `BLUME_INCLUDE_CYCLE`, and `BLUME_INCLUDE_OUTSIDE_ROOT` diagnostics.

### Patch Changes

- 68f48c1: Downlevel components in the Ask AI grounding corpus, the way the `.md` mirror, `llms-full.txt` and MCP `get_page` already do. Ask AI was built from the search documents' verbatim Markdown, so a page whose body is a `<CardGroup>` of `<Card>`s reached the model as JSX with no prose in it — and section landing pages, which are exactly that shape, rank first for section-level questions. `prop={frontmatter.*}` expressions now resolve here too.
- fd08e26: Require Astro 7.2.3+, which renames the dev SSR entry to `virtual:astro:server-app` so full reloads no longer re-request it with a spurious `.js` suffix — and drop the `serverAppResolvePlugin` shim that worked around the old id corrupting the dev server on content renames.
- 1715b14: Give each AsyncAPI channel or operation that references the same component its own copy before traits merge. The aliases shared one object, so the first merge leaked into the second and stripped `traits` from the component table entry itself.
- 0fe3d43: Darken the green and orange text that misses WCAG AA in light mode. Both drew at `-700` over a 15% tint of their own hue, landing at 4.32:1 and 4.44:1 against the 4.5:1 bar for text that size, while every other hue already cleared it (teal 4.69:1, red 5.17:1, blue 5.71:1, purple 5.82:1, violet 6.00:1, yellow 6.02:1). At `-800` they reach 6.20:1 and 6.25:1, with the fill weight unchanged. This covers the GET and PUT method badges (and GraphQL `QUERY`), the matching badges in a reference's sidebar, 2xx and 4xx response status chips, and `<Badge>`'s `success` and `warning` variants. The `deprecated` label on an operation moves too: at `orange-600` on the page background it was 3.59:1, the worst of the set. Dark mode was already passing and is unchanged.
- 0fe3d43: Darken the callout icons that miss the WCAG 1.4.11 non-text contrast bar in light mode. A callout's icon names its type, so it is meaningful UI rather than decorative: `check`/`success` and `warning` sat at 2.94:1 and 2.95:1 against their own tint and now take `-700` (4.52:1 and 4.67:1), and `note`'s icon was muted-foreground at 70% over the muted surface (2.68:1) and is now full strength (4.64:1). Dark mode was already passing.
- 4c0fdfc: Keep camelCase word boundaries in API reference operation routes. An `operationId` went through the generic slugger, which lowercases before it replaces non-slug characters, so `processSlackInteraction` collapsed to `/reference/integrations/processslackinteraction`. Operation ids are usually camelCase because OpenAPI generators reuse them as SDK method names, and renaming them just to get readable URLs couples SDK naming to presentation. The key now hyphenates the boundaries first: a lower-case letter or digit followed by a capital, and the last capital of an acronym before a capitalized word, so `processSlackInteraction` becomes `process-slack-interaction` and `getHTTPResponse` becomes `get-http-response`. Already kebab-case ids and the method-plus-path fallback are unchanged. GraphQL field and type names and AsyncAPI operation ids derive their routes through the same key, so a GraphQL `addPet` mutation moves from `/graphql/mutations/addpet` to `/graphql/mutations/add-pet` and an AsyncAPI `publishUserSignedup` operation from `publishusersignedup` to `publish-user-signedup`.
- 0fe3d43: Darken the typed `<Card>` icons and the stroked `<Badge>` variants that still missed the light-mode contrast bar. A card's `check`, `warning`, and `note` icons carried the same values the callout icons moved off (2.94:1, 2.95:1, and 2.68:1 against their tint) and now share the callout table. A stroked badge has no fill of its own and inherits whatever surface it sits on — inside a `note` callout, `success` at `-700` was 4.47:1 — so `success` and `warning` now take `-800` whether filled or stroked.
- 68f48c1: Downlevel `<Card>` and `<CardGroup>` to Markdown on the agent-facing surfaces. Both are Blume's own components and neither had a serializer, so a section landing page — the shape `blume init` scaffolds and the Mintlify migrator emits — published raw JSX to `/<route>.md`, `llms-full.txt`, MCP `get_page` and the Ask AI corpus. A card becomes its title as a link over its body, with the `cta` last; a group becomes everything it holds, block by block. Serializers in `ai.markdownComponents` gain `childBlocks()` — every direct child of the element in order, each already downleveled — which is what `CardGroup` is built on.
- e823872: Harden two scanners against pathological input: HTML comments stripped before anchor detection are replaced with a space so their neighbors can't splice into new markup, and `github.dir` plus source/meta route prefixes trim slashes linearly instead of with a regex that was quadratic on a run of `/`.
- ce20984: Make room for the copy button in code blocks that have no language bar to hold it. Inside a component whose chrome is `not-prose` — `<Tabs>`, `<CodeGroup>`, `<Steps>`, `<Callout>`, `<Card>`, `<Accordion>` and the rest — the bar is cleared but the button is not, and the block's `padding-top` was the 1rem chosen for a block with no chrome at all, so the button painted over the first line of code. The same applied to an untitled `<CodeBlock>` in plain prose, which renders no bar. Both now reserve the button's strip, as does a raw `<pre><code>` block written in prose, which gets a button but has no language bar either; the clearance is keyed on the docs layout that injects the button, so a custom page layout without it keeps the plain inset.
- 35b5c6a: Support trailing `{#custom-id}` heading markers as an equivalent of `[#custom-id]` — verbatim in `.md`, and as the escape `\{#custom-id\}` in `.mdx`, where a bare `{…}` is a JSX expression. `blume check` now reports a bare `{#id}` in an `.mdx` page (or in a partial it includes) as `BLUME_MDX_CURLY_ANCHOR` instead of leaving it to fail at compile time, and `blume translate` is told to preserve the marker. Fragment-link validation also recognizes ids on raw HTML elements (`<a id="…">`, including tags wrapped over several lines) as anchor targets, ignoring ids inside code, inline code, HTML comments, `<Prompt>` blocks, and component props.
- ce20984: Drop the page-actions divider when the rail has no table of contents. The actions block carries its own top border as a separator under the contents, but the contents only render when a page has a heading within `toc.minHeadingLevel`–`toc.maxHeadingLevel` — so on a page without one the block drew a rule across the top of an empty column. The rail now decides once whether an outline renders and passes that to the block (`divider` prop, default on), so the two cannot disagree.
- 1715b14: Keep a spaced Shiki line range (` ```ts {1, 3-5} `) as one fence-meta token. The range used to split on its whitespace, and the `3-5}` fragment was promoted to the code block's title.
- ce20984: Cap the search dialog's section-filter row so the results keep the dialog. The dialog is a fixed height and the filter chips wrapped without a bound, so on a site with many sections the chips took the height and the results list got the remainder — measured on an 18-section corpus, the row wanted 266px of the 480 and left about one visible result. The row now shows two rows of chips plus the top of a third as the scroll cue, chips no longer wrap on long section names, the selected chip is scrolled into view when the chips reorder as the query narrows, and each chip exposes its selected state as `aria-pressed`.
- 6d4ffec: Load the font subsets your locales need. Remote fonts used to load only the Latin subset, so Vietnamese, Central European, Cyrillic, and Greek text fell back to the system font. Blume now derives subsets from `i18n.locales`, and remote families accept a `subsets` option to pin the list.
- 1715b14: Treat an empty `graphql.endpoint` like an omitted one. The playground and code samples fall back to the endpoint placeholder instead of targeting an empty URL.
- ceb7a9f: Fix the image lightbox becoming impossible to close after the first in-page navigation. `medium-zoom` injected its stylesheet into `<head>` once, when its module first loaded, and the client router's head swap discarded it on the next navigation; with the transition rule gone, the close animation's `transitionend` never fired, so Escape, a backdrop click, and scrolling all left the zoomed image open (and the `zoom-in` cursor disappeared) until a hard reload. The stylesheet now ships in the layout's page CSS, which the router carries across swaps, and the library loads as its `pure` build with no runtime injection.
- 03f5b36: Upgrade js-yaml to v5. Front matter parses exactly as before: dates still become `Date` objects and `<<` merge keys still resolve.
- aee337e: JSON-LD can now say what a site is and who runs it. `seo.organization` adds an **Organization** node to every page — name and URL default to the site's, with a logo, `sameAs` profiles, the email and telephone as a `ContactPoint`, and the address as a `PostalAddress` — which the WebSite and article nodes cite as `publisher`. `seo.software` adds a **SoftwareApplication** node to the homepage with the product's name, description, category, operating system, license, an `Offer` when a price is set (`0` for free software), and registry or repository `sameAs` links; `software: true` takes every default. Both need `deployment.site`, and custom pages built on `PageLayout` or `RootLayout` pick them up automatically.
- dff7930: Scan link-reference definitions in linear time. A line of deeply nested block-quote or list containers that never reaches a `[label]:` no longer backtracks exponentially while headings are extracted.
- aee337e: `llms.txt` now tells agents when to use the site and where its machine-readable surface lives. A new `ai.llmsTxt.details` option inserts free-form Markdown after the title and summary — the llms.txt spec's details block — for "when to use" guidance, the install command, or the package name in the site's own words. The generated file also closes with two sections that need no configuration: **Agent skills** lists each skill published through `ai.skills` with its description, and **Agent resources** links every artifact the build emits — `llms-full.txt`, the per-page `.md` mirror, the MCP server and its discovery document, the skills index, the API catalog, `agent-readability.json`, and the sitemap — each only when it exists.
- aee337e: The hosted MCP server now exposes every page as an MCP resource alongside its tools. `resources/list` enumerates the pages at their served URLs (a `blume:` URI when no `deployment.site` is configured) with a `text/markdown` type, title, and description; `resources/read` returns the page's agent Markdown — the same output `get_page` serves — and accepts a listed URI, a bare route, or a `.md` mirror URL. An unknown URI answers with the protocol's resource-not-found code. The server card advertises the `resources` capability, so clients and readiness scanners that attach context by URI can browse the docs without calling a tool.
- 1715b14: Corrections to the `blume-migrate` skill: a Docusaurus or Fumadocs folder with `collapsible: false` maps to that folder's `display: "flat"` instead of being dropped, several Fumadocs GraphQL sources map to `graphql.sources` rather than a single `spec`, a Nextra `docsRepositoryBase` on a non-`github.com` origin sets `github.host` to that origin, and the favicon convention reads `icon.{svg,png,ico}` or `favicon.{svg,png,ico}`.
- 24ac208: Darken `--blume-muted-foreground` from `oklch(0.54 0 0)` to `oklch(0.53 0 0)` and lighten the `tip` tint on callouts and cards from `accent/10` to `accent/6` so muted body text clears WCAG AA on every tinted surface, not just on the page. At `0.54` muted text was 5.06:1 on the background but 4.38:1 inside a `danger` callout and 4.50:1 inside an `info` one; at `0.53` those rise to 4.57:1 and 4.69:1 and the page to 5.28:1. `tip` tints with the configurable accent, whose default is near black, so no text lightness alone could make it safe — even at `0.53` it sat at 4.25:1 over the 10% tint. At 6% it clears 4.5:1 against any accent (4.62:1 worst case on pure black). Dark-mode tokens are unchanged.
- aee337e: The default 404 page now helps readers and agents recover instead of dead-ending. Under a "Where to look next" heading it links every top-level section (each tab's resolved target), the sitemap when a `deployment.site` makes one possible, and `llms.txt` when it's enabled — so an agent that followed a stale URL lands on a real HTTP 404 whose body points at the site map and the docs index. The three new labels (`notFound.suggestions`, `notFound.sitemap`, `notFound.llms`) are translatable through `ui` like the rest of the page's copy.
- 43931fb: Localize the "Open in chat" prompt. The prompt handed to the chat provider ("Read `<url>` so I can ask you questions about this page.") was hardcoded in English regardless of the site's locale. It now comes from the UI dictionary as `actions.openInChatPrompt` — with `{url}` replaced by the page's raw-Markdown URL at load time — so localized sites send a localized prompt, every shipped starter pack carries a translation, and `i18n.ui` can override the wording like any other chrome string.
- ce20984: Keep long unbreakable tokens inside the content column. Prose sat at `overflow-wrap: normal`, so an API permission, a broker list, a bare URL used as its own link text or a module-qualified name in a generated reference painted past the column — and nothing between the paragraph and the viewport clips it, so the run landed in the document's scroll width and dragged the whole page sideways on a phone. Headings had their own opt-in; the property is now set once on the prose root and inherited, so running text, headings and inline code are covered by the same rule. Tables are unchanged: an auto-layout column always grows to fit its widest token, so a wide table still relies on its scroll wrapper.
- 5559319: Close the site's dropdowns — the page actions (Export, Open in chat, Connect to MCP), the header language switcher and the nav selectors — when clicking outside them, pressing Escape, or moving keyboard focus out of the panel, instead of leaving the panel open until its own trigger is clicked again. Escape returns focus to the trigger when pressed from inside the menu, and an Escape aimed at the search dialog no longer closes a menu beneath it.
- ce20984: Give the API reference's sticky sample panel its own scroll region. A grid item's sticky containing block is its grid row, and the row is as tall as its tallest item — so expanding a request schema on the left made the left column tower over the panel and handed sticky an enormous travel budget, pinning the panel with everything below the viewport fold unreachable until the left column ended. Bounding it to the viewport turns the overhang into a scroll. The tradeoff runs the other way on an operation with a short left column and a long sample: the row is then only as tall as the capped panel, and the overhang is reached by scrolling the panel rather than the page; a playground response is scrolled into view when it lands below that fold. The scroll region is styled like the sidebar and the table of contents — thin, border-colored thumb, inset from the Request card's edge, stable gutter — wheel input chains to the page once the panel reaches its end, and the focus ring on the panel's first control is no longer clipped by the scroller's edge. Applies to the OpenAPI, AsyncAPI and GraphQL operation pages, which now share one `OperationPanel` component.
- 1715b14: Give the API playground proxy a 30-second upstream deadline, shared across a redirect chain. The client already aborted its own request after 30 seconds, but that never reached the server-side fetch, so a documented API that accepted the connection and never answered held a server request slot until the platform killed it. A timed-out upstream is now the same JSON 502 as an unreachable one.
- 1715b14: Emit the request body in the playground's JavaScript and Python code samples as the raw editor text, never re-read as a JavaScript object literal or Python dict. The generated sample now sends byte-for-byte what the live request sends: a `__proto__` key stays a key, an id past 2^53 keeps its digits, and `1e400` is not re-serialized as `Infinity`.
- beec2d2: A protocol-relative `navigation.featured` href (`//host/path`) no longer picks up a locale prefix on non-default locales, where it became `/fr//host/path`. `localizePath` now draws the same line as `withBasePath`: a leading slash alone doesn't make a route.
- fe966ca: Redesign the default 404 page: a two-column layout with the title and home link on the left and a vertical "Where to look next" list of sections, sitemap, and llms.txt on the right, stacking on small screens.
- e2153d5: Restore live Markdown hot reload in migrated `.`-rooted projects. Astro's content watcher now honors the docs collection's negated globs, so the generated dev config no longer hides Astro's cache dir from the watcher — the escape hatch that previously cost `.md` body edits a dev-server restart in that layout.
- 2bfe840: Accept `@sanity/client` v8 as a peer dependency for the Sanity content source.
- fc24f9d: Keep a Scalar-rendered API reference on the page's theme. Scalar decided light or dark on its own — from its own localStorage key or the OS setting — and never read Blume's `data-theme`, so a reader whose stored preference disagreed with what their browser reports to `prefers-color-scheme` (Brave with its own dark color scheme over a light OS, for one) got a light navbar over a dark reference, with two toggles that didn't agree. The reference layout now pins Scalar's color mode to the current theme before it mounts, which also hides Scalar's own switch, and mirrors later flips of Blume's toggle onto the embed. A `scalar.forceDarkModeState` or `scalar.darkMode` set through the escape hatch is left to Scalar, as before.
- dd1ed31: Add `search.indexing.includeCodeBlocks` to index fenced code (body and title) in the source-built search indexes — the client index, hosted syncs, and the MCP `search_docs` tool — while keeping the plain-text default. `.mdx` pages are now parsed as MDX for indexing, and components are downleveled with the same serializers the agent surfaces use, so prose and fences inside `<Steps>`/`<Tabs>` and the text a `<Card>` or `<TypeTable>` shows are indexed instead of being folded into raw HTML.
- 1715b14: Keep an explicit script subtag when a search locale carries a POSIX suffix. `az_Cyrl.UTF-8` fell back to `az`, which maximizes to Latin script, so segmentation stayed off and Cyrillic content produced no search tokens; `sr_Latn.UTF-8` did the reverse. The codeset and modifier suffix is now dropped before the tag is parsed.
- 52ac092: Add a `syncKey` prop to `Tabs`: only groups sharing the same key switch together, so unrelated groups that happen to share a tab title stay independent. Nested tab groups now also keep their own panels instead of having them adopted by an ancestor group.
- d009a19: Keep the dark theme on the page during client-side navigation. The client router replaces the root element's attributes with the incoming page's, which dropped `data-theme` until Blume re-applied it after the swap; in between, the router's scroll restoration computed the new page's styles with the light palette, so the sidebar, header tabs, language selector, search button, feedback buttons, previous/next links and page actions visibly animated from light to dark on every navigation. The theme is now stamped onto the incoming document before the swap, so it never drops.
- ce20984: Emit `data-depth` on each table-of-contents item. The heading's level was computed and then written only into an inline `padding-inline-start`, so a project wanting to style the rail by level had to match on that style string — which breaks silently if the indent ever changes.
- 52ac092: Add `ts2js` code fences: mark a `ts` or `tsx` block with the `ts2js` meta keyword and it renders as synced TypeScript/JavaScript tabs, with the JavaScript variant generated automatically — type syntax and type-only imports stripped, formatting, comments, and JSX preserved.
- 72c9abe: Parse the project's `tsconfig.json` path aliases with the TypeScript compiler API instead of `get-tsconfig`. Blume's `get-tsconfig` v4 and Astro's pinned v5 left two copies in every install, and a stale CI `node_modules` cache could drop Astro's nested copy, failing `blume build` with `The requested module 'get-tsconfig' does not provide an export named 'readTsconfig'`. TypeScript already ships with Blume, so there is now one fewer dependency and alias resolution follows tsc exactly.
- aee337e: `blume build` now audits the Vercel function bundle after a server build and fails when the bundle is missing a package its server code imports. The Vercel adapter's dependency trace silently drops any bare import it can't resolve from the project root — under an isolated linker (pnpm, Bun's isolated mode) that includes Blume's own runtime dependencies, such as the MCP server's `@modelcontextprotocol/sdk` — and the deployed function then fails on every request with `FUNCTION_INVOCATION_FAILED` while the build, CI, and warm-cache previews all stay green. The build now reports each missing package with the chunk that imports it and the `npm install -D …` line that fixes it; a missing Blume dependency fails the build, a project's own missing external is a warning.
