blume@2.1.1
Patch Changes
-
579ae91: The Algolia sync no longer fails on long pages. Algolia caps a record at 10 KB on its Build and Grow plans and rejects the whole upload when one page’s record is larger, so the sync now splits a longer page into several records, each carrying the page’s title, description, and URL with a stretch of its text. It sets the index’s
attributeForDistincttourl(unless you set your own), so a split page appears once in results, at its best-matching record. -
dc89029: On hand-written API pages, an optional body field with no
defaultorplaceholdernow starts empty in the Try it panel and stays out of the request samples, instead of being sent as"string",0, ortrue. -
29abb61: The assistant route now exposes
Retry-Afterto the origins listed inai.assistant.cors, so a page on another site can read how long to wait after a429from the rate limit. Browsers hid the header from cross-origin callers before. -
05b0acf: The assistant keeps a question asked outside the docs, such as from the homepage or another site, to one language. It searched every translation before, so on a translated site the translations of each page crowded out the answer. Retrieval and the assistant’s own searches now keep to the locale the page’s URL names (
/ja), or the default locale. -
3ea55bb: Long conversations with the assistant keep working. The panel sent the whole conversation with every question, and once it passed the route’s 24,000 characters or 40 messages, every answer failed. It now sends the latest turns that fit, dropping the oldest first; the reader still sees the whole conversation.
useAssistantdoes the same. -
075d362: Update
@astrojs/reactto 7. Itsbabeloption is gone, so the React Compiler now runs on Oxc (oxc-transform-react) instead of Babel (babel-plugin-react-compiler). It still ships with Blume and stays on by default;react: { compiler: false }still turns it off. -
9772956: AsyncAPI
kcatsamples now follow the Kafka bindings and the server’s security. A channel binding’stopicreplaces the channel address, a message binding’skeykeys the message (key|payloadwith-K '|'), and a server that declares a SASL scheme adds the-Xsettings librdkafka needs, reading the credentials from$KAFKA_USERNAMEand$KAFKA_PASSWORD, with TLS forkafka-secure. When a channel’s servers declare different security, the Authorization section names the servers each scheme applies to and those that need none, instead of marking every scheme required everywhere. -
cf1f04a:
blume audit --urlnow requests each configured redirect’s old URL on the live site and reportsBLUME_AUDIT_REDIRECT_NOT_SERVEDwhen it isn’t redirected to the configured destination: an error when the old URL fails, and a warning when the host serves the build’s meta-refresh page instead of an HTTP redirect. -
4561f51:
blume auditno longer says a page always wins over a redirect configured from the same path. Which one readers get depends on the host, so the finding and its fix now say so. -
6c7a96e:
blume auditnow reads everyUser-agentgroup in robots.txt, not only*, and warns withBLUME_AUDIT_ROBOTS_BLOCKS_CRAWLERwhen a rule aimed at one crawler blocks it from pages the sitemap advertises. -
a0041e5:
blume auditno longer reports a full URL on the site’s own host but outsidedeployment.baseas a broken link: it names another app on that host, which is how the navigation docs say to link one. -
ed324f3:
blume auditno longer says a page nothing links to (asidebar.hiddenpage, say) is “reachable only from the sidebar”, and the fix for an indexable page missing from the sitemap now mentions apublic/sitemap.xmlthat replaces the generated one. -
08b514f: The generated
/changelogindex’s “No changelog entries yet.” line is now a UI string (changelog.empty), translated in every built-in language pack and overridable underi18n.ui, instead of always showing in English. -
605e65f: A new top-level
changelogconfig sets the generated/changelogindex’stitleanddescription, each a string or a per-locale map, so a site can rename its changelog without overriding UI strings underi18n.ui. They also title the index’s OG card and its Markdown mirror. -
2495c48:
--budget-js,--budget-css,--analyze, andblume auditnow read acloudflare()server build withbaseset fromdist/client/<base>/, where the adapter writes it. They readdist/clientbefore, so a budget measured nothing and passed, and the audit reported every page’s links and assets as broken. -
351f29c:
contentful()renders an embedded file that isn’t an image, like a PDF, as a link to it instead of a broken image, and warns withBLUME_SOURCE_UNRESOLVED_LINKwhen a page embeds or links an asset or entry the Delivery API didn’t return (an unpublished one, say) instead of dropping it silently. An embed of another entry of the same content type now resolves. NewhostandpreviewHostoptions reach a space with EU data residency, andfields.orderoncontentful(),payload(), andstrapi()reads a page’s sidebar order from a number field. -
0d7a9dc: A
contentful()rich text link to another entry of the source’s content type now links to that entry’s page, at the route it’s built at (prefix, nested slug, and locale included). A link to an entry of any other content type still keeps only its text, and a link to an entry the Delivery API didn’t return (an unpublished one, say) warns withBLUME_SOURCE_UNRESOLVED_LINK. -
3e3e65f:
blume devnow answers an exact redirect with its configuredstatus, as the production outputs do, instead of a301for every redirect to a docs page. -
2d13649:
blume devnow watches the local spec and overlay files your API references read, so saving a spec updates its reference without restarting the server. -
e69a55e:
blume doctornow reports a tab, selector item, featured link, header action, or call to action whose path matches no page (BLUME_NAV_MISSING_PAGE), the same warningblume devandblume buildprint. It checks against every route the site serves, custom.astropages, the generated changelog, and references included. -
c82742b:
codeSamples: []onopenapi()orgraphql()now shows no generated code samples, likecodeSamples: false, instead of falling back to the default cURL, JavaScript, and Python. -
2dda5fe: EPUB export now names the site as the book’s author and publisher and uses the page’s language, instead of “anonymous” and English. Links to other pages point at the live site, and an image the browser can’t fetch is left out with a console warning instead of failing the whole export.
-
7ab09df:
blume eval --agent claudeno longer loads your Claude Code settings files orCLAUDE.mdinto the reader and judge, the way Codex runs already skip your Codex config. Hooks, plugins, and memory could hand the reader context your docs never gave it. Your Claude Code login still works; set any other variables it needs, such asANTHROPIC_API_KEY, in your shell rather than insettings.json. -
bec8930:
blume eval --agent claudenow runs the reader and judge with every Claude Code built-in tool turned off, so the reader has only the docs tools. Its list of blocked tools had fallen behind Claude Code, which left newer tools such asMonitor(which runs shell commands) andSkill(which loads your installed skills) available to it. -
8dc7ed3:
blume eval --fixnow tells the agent to verify its edits with the same--agent,--file,--threshold, and--timeoutthe run used. It used to say a bareblume eval, which gradedevals.yamlwith Codex even when the run being fixed used Claude Code or another evals file. -
35af3c6: A missed
severity: warningquestion inblume evalnow reads as the warning it is. Its line shows⚠ warninstead of✖ fail, the summary counts it as warned rather than failed, and its finding prints as a⚠line instead of afix:line, matching the exit code, which already ignored it. Each question in the--jsonresults now carries itsseverity. -
d5ef2f0: An exact redirect from a page’s own URL now warns with
BLUME_REDIRECT_MATCHES_PAGEinblume validate,doctor,dev, andbuild: the redirect takes the URL over, so the page never publishes. Before, only a pattern redirect was flagged. -
e09dcd4:
blume validate --externalandblume audit --externalnow fail a link only when it’s dead: a 404 or 410, or a host name that doesn’t resolve. A refused or dropped connection is a warning, like a timeout or any other error status, since it’s more often the other site or the runner’s network than the link. The finding also names the reason a request failed (getaddrinfo ENOTFOUND …) instead of Node’s bare “fetch failed”. -
b28119d: Frontmatter that isn’t valid YAML, such as an unquoted value holding
:, is now reported asBLUME_FRONTMATTER_INVALIDat its file and line instead of failingblume validate,doctor,audit,build, anddevwithBLUME_INTERNAL. -
c505dbe:
githubReleases()takes abaseUrlfor a repository on GitHub Enterprise Server, and tags a draft release it includes (withdrafts: true)Draftinstead ofRelease. An RSS item now falls back to the page’sseo.description, so the releases feed describes each release with the summary of its notes. -
4cda96b: GraphQL example variables start at each argument’s declared default, so
first: Int = 20is20rather than0, and input objects fill their fields’ defaults the same way. Generated example queries and responses leave deprecated fields out.graphql()takes anauthoption, in the shape ofapi.authfor hand-written endpoint pages:{ method: "bearer" },"basic", or"key"with the headername(x-api-keyby default). With it, every operation page shows an Authorization section, the Try it panel gets a credential field, and the code samples send a placeholder credential. A per-sourceauthoverrides the adapter’s. -
922d680: The
BLUME_INCLUDE_MALFORMEDsuggestion no longer says onlylangandmetaattributes are read; it says attributes need lowercase names and quoted values, and that any other attribute is a prop. -
aa7539b:
blume initnow adds.env.local, where Blume says to put local secrets, to.gitignore. -
86da6d4:
blume init’s pnpm workspace note now says pnpm 11 and later stop the install over an unapproved esbuild build script; pnpm 10 only warns. -
0a38c27: The MCP
search_docsandlist_pagestools now match a number or boolean infiltersagainst the facet’s string form, the way facet values are stored, so{"priority": 1}finds pages withpriority: 1. Such a value used to be dropped, and the tool answered as if no filter had been sent. -
12c2213:
mdxRemote()fixes:blume validateresolves a link between remote files (./02-errors.mdx) to the page it publishes at instead of reporting it broken, a!pattern inincludenow excludes what it matches as it does forfilesystem()instead of including every file, agithub.pathwith nothing under it at the ref warns withBLUME_SOURCE_PATH_MISSING, and theBLUME_SOURCE_TRUNCATEDwarning no longer suggests narrowingpath, which can’t help. -
c8820c2: A Mermaid diagram that fails to parse now logs Mermaid’s parser error to the browser console, and
blume devshows it under the “Could not render this diagram.” message, so you can find the line to fix. Mermaid’s “Syntax error in text” graphic no longer appears at the bottom of the page. -
f442afe: The
blume-migrateskill’s Docusaurus reference now replaces<DocCardList />and generated-index categories with a folder’sdirectory: "card"listing, and turns anidthat differs from its filename into aslug, so the page keeps the URL Docusaurus built from its ID. -
2f798ce: The
blume-migrateskill’s Fumadocs reference now redirects URLs that lose a numeric filename prefix, hides the pages ameta.jsonpageslist without"..."left out of the sidebar, mapsfull: truetomode: wide, and reads newer scaffolds: collections inlib/source.ts, abaseUrlconstant,components/mdx.tsx, and the header title, GitHub link, and links inlib/layout.shared.tsx. -
22c4f41: The
blume-migrateskill now keeps a source’s inline math inline, rewriting$x$as$$x$$inside its sentence. It used to treat inline math as unsupported and move each formula onto its own line or drop it, though Blume renders$$…$$inline. -
449d11b: The
blume-migrateskill’s Nextra reference now moves a Nextra 4 default-locale folder (content/en/) up to the content root, keeps each page’s heading as itstitlewith the_metalabel assidebar.label, mapslayout: "full"tomode: wide, and keeps_metaordering in nested folders withdisplay: "group". -
b02e9a4: The
blume-migrateskill’s Starlight reference now carriesheadmeta tags (site verification,theme-color, and the like) over toseo.metatagsinstead of dropping them. -
74546a6: The
blume-migrateskill’s Starlight reference now reads the sidebar’sitems: [{ autogenerate }]shape from Starlight 0.39 on, carries Discord, X, and other social links over tofooter.socials, maps aFooteroverride to Blume’sPageFooterslot rather than the site footer, keeps per-fencewrap, and approximates splash pages withmode: center. -
258a9aa: A
<Component path>that names no example now gets aBLUME_EXAMPLE_NOT_FOUNDwarning at its line fromblume dev,blume build, andblume doctor, instead of only a “No example found” box on the page. Acomponents.tsoverride or island namedComponentreplaces the built-in, so itspathisn’t checked. -
1dd0e1e: Mixedbread search results now link to their pages. The endpoint read each result’s link from metadata nothing sets, so every result pointed back at the current page; it now finds the page whose file
mxbai store syncuploaded, shows that page’s title, and lists each page once. When the search endpoint fails, the dialog shows its error message instead of “No results”. -
ea6591b: With Mixedbread search, the search dialog now waits for the reader to pause typing before it sends a query, and cancels a query the reader has typed past. A search costs a request or a few instead of one per letter, which keeps readers under the search endpoint’s rate limit, and a slow earlier response can no longer replace newer results.
useSearch()and the WebMCP search tool still send each query at once. -
3d96865: The Mixedbread search endpoint now checks
rateLimitinblume devandblume build, answering429 Too Many Requestspast the limit. Onlyblume ejectused to limit it. -
458b6e6:
node()takesallowedDomains, set as Astro’ssecurity.allowedDomains, for a server behind a reverse proxy. Astro only reads the reader’s address fromX-Forwarded-Forfor a host listed there, so every reader behind a proxy shared one rate limit count; with the proxy’s host listed, each reader gets their own. -
62c4ec8: OpenAPI 3.2 specs now render as written, and a
queryoperation, for the HTTPQUERYmethod, gets a page, Try it, and code samples like any other method. The 3.2 additions Blume doesn’t render yet,additionalOperationsandin: querystringparameters, log aBLUME_OPENAPI_UNSUPPORTEDwarning instead of dropping out without a sign. -
66abad4: An operation’s Callbacks section now lists each callback’s parameters, such as a signature header, beside its request body and responses. A 3.1 or later spec that still keeps its webhooks under
x-webhooks, which renders none of them, now logs aBLUME_OPENAPI_X_WEBHOOKSwarning that says to rename the field towebhooks. -
8ac7650: An operation’s own
x-codeSamplesnow reach its Markdown copy, sollms-full.txt, the MCP server, and the assistant see your SDK calls, and site search indexes them like any code block. A sample whosesourceis a$refto a file is read relative to the spec, and one that can’t be read logs aBLUME_OPENAPI_CODE_SAMPLE_REFwarning instead of disappearing. -
ac5b593: OpenAPI references now warn about spec mistakes that used to render wrong without a sign: a
{name}in a path with no path parameter of that name (BLUME_OPENAPI_PATH_PARAMETER_MISSING), a path parameter the path never uses (BLUME_OPENAPI_PATH_PARAMETER_UNUSED), and asecurityrequirement naming a scheme thatcomponents.securitySchemesdoesn’t define (BLUME_OPENAPI_UNKNOWN_SECURITY_SCHEME). The pages still build. -
cea759c: Page actions now show in windows narrower than 1,280px and on phones. Edit on GitHub, Copy as Markdown, Export, Open in chat, and Connect to MCP sit at the bottom of the “On this page” dropdown above the content, their menus opening in place, and that dropdown now shows on a page with no headings too. The actions render once per page and move between the right rail and that dropdown as the window crosses 1,280px. A
TableOfContentslayout override receives the place they move into as children of itsmobilevariant, so render a<slot />there to keep them. -
e6713a4:
blume buildwithpagefind()now reports how many pages the search index holds. The “Indexed N page(s) for search” line used to count every HTML file in the build, including search-excluded pages, hidden pages, and the 404 page, which the index leaves out. -
b86f051: With
pagefind(), search now follows a switch to another language. Pagefind read the page’s language once, on the first search, so after picking another language in the switcher, which swaps the page without a reload, search kept returning results in the first language until a full page load. -
904eb98: Pagefind search results now link to each page’s own URL. They ended in a slash (
/quickstart/), which Blume doesn’t serve, so a host redirected every result andblume previewanswered Not Found. Withdeployment.baseset, they also carried the base twice (/docs/docs/quickstart/). -
1b89b4e: API reference parameter tables and the Try it panel now show a parameter’s description when the spec puts it on the parameter’s schema, as Elysia and oRPC do. A description on the parameter itself still wins.
-
8bfb639:
payload()now warns withBLUME_SOURCE_UNSUPPORTED_NODEwhen a page’s Lexical body holds a node it leaves out as a comment, like a block with no serializer or a relationship, naming the page and the node, instead of dropping it without a word. -
00ce3b9: The API playground’s built-in proxy now checks
rateLimitinblume devandblume build, answering429 Too Many Requestspast the limit. Onlyblume ejectused to limit it. -
e12b668: Printing a page, or exporting it to PDF, now leaves out everything after the article (last updated, feedback, previous and next links, the site footer) and the buttons on code blocks, prints collapsed
expandablecode blocks in full, and prints a page read in the dark theme in the light one. -
f5151c7: A redirect from one page to another now moves the page’s raw Markdown too:
/old.mdand/old.mdxredirect to/new.mdand/new.mdxwith the same status, inblume dev, the redirect files a static build writes, and every server build. They used to 404. -
c5f25f5: A reference source’s
labelnow names its sidebar group as written (GitHub OAuth (v2), not “Github OAuth V2” from its route), and a reference’s tag groups follow the spec’s declared tag order in the sidebar, the same order as its overview page, instead of sorting alphabetically. -
0dc7156: API reference schema tables now match the examples beside them: request body tables leave out
readOnlyproperties, and response tables leave outwriteOnlyones. A webhook’s or callback’s body, which your API sends, keeps itsreadOnlyproperties. -
0ff3d0e: A
sanity()body held in a Markdown string field now renders as Markdown instead of an empty page. A query that finds no documents whileSANITY_TOKENis unset now warns withBLUME_MISSING_SECRET, since a private dataset answers a query without a token with nothing rather than an error. -
4562581: The site stylesheet now scans your project’s
.jsxfiles for Tailwind classes, as the theming docs say, so a class used only in a.jsxisland is generated. Ejected apps scan them too. -
2c6022a: The search dialog’s “All languages” toggle now works with
pagefind(): it merges every language’s index into the search, where it used to return the page’s language alone. Withmixedbread(), which can’t limit a search to one language, the dialog no longer shows the toggle, since every search already spans every language. -
fa68c94: Search analytics can keep query text away from your analytics providers. With
search: { analytics: { queries: false } }, thesearchandsearch_selectevents carry the query’s length asqueryCharsinstead of its text, and the text travels only on theblume:trackDOM event, the way the assistant handles questions. By default queries are still sent as typed. -
7beba3e: Search now finds translations in a non-Latin script on a site whose default language is written in Latin script, such as the Japanese and Hindi pages of an English site. Each of those locales’ pages is indexed with its own word-segmenting tokenizer, so the search dialog, the MCP server’s
search_docs, and the assistant match them, whether the search is scoped to one language or runs across all of them. Latin-script pages keep the tokenizer they had. -
05a06e3: A failed search sync now fails the build when its admin key is set. Algolia, Orama Cloud, and Typesense syncs used to only warn when the upload failed, so a site could deploy against an index the build never updated; they now stop
blume buildwithBLUME_SEARCH_SYNC_FAILED. Without the key (or, for Orama Cloud, withoutindexId), the build still warns and skips the sync, so a build without secrets keeps working. -
55b417c: API references now read a field whose schema is an
allOfwith one member, the way drf-spectacular writes enums and nested serializers, as that member: it’s labeled with the member’s name or type instead ofobject, lists its allowed values, and the Try it panel gives it the member’s type and choices. A model that points back at itself through such a wrapper no longer crashes the build. -
e47a621: The sitemap now leaves out a page whose
seo.canonicalnames another URL, so following the duplicate-content fix no longer tripsBLUME_AUDIT_NON_CANONICAL_IN_SITEMAP. An archived-version page whose ownseo.canonicalnames itself stays listed. -
2b14092: A content source’s missing token or SDK is now reported as itself on a fresh build, instead of as
BLUME_SOURCE_FETCH_FAILEDwith the API’s error.blume dev,blume build, andblume doctorwarn about unset variables before any source fetches,notion()andcontentful()stop before their first request withBLUME_MISSING_SECRETwhen their token is unset, and a missing SDK fails asBLUME_SOURCE_SDK_MISSING. -
3114c92:
BLUME_MDX_CURLY_ANCHORnow also catches the spaced{ #id }and kramdown{: #id }heading anchors in.mdxpages, which fail the compile the same way{#id}does, and quotes the marker as written. Only the unspaced{#id}pins an anchor in.md. -
00c4707: With fallbacks off (
fallbackLocale: null), the language switcher no longer links to a translation that doesn’t exist. A language the page isn’t translated into is left out of the switcher, where it used to link to a 404. -
492fdf2:
blume translateno longer copies a page’sseo.canonicalinto its translations. The canonical names the source-language page, so every translation used to canonicalize to it and drop out of search in its own language; a translation now gets the default canonical, its own URL. -
68ed70d: The Try it panel’s CORS message now names the right setting for the page it’s on:
playground: { proxy: true }on theopenapi()orgraphql()reference, orapi: { playground: { proxy: true } }for hand-written API pages. It also says the proxy needs server output. -
0ea988e: Typesense syncs no longer take search down while they run. Each
blume buildimports your pages into a new collection, then points an alias with yourcollectionname at it and drops the collection it replaced, so searches keep reading the previous collection until the new one is complete, and a failed sync leaves it serving instead of leaving no collection at all. On the first sync, a collection that already has that name is replaced by the alias, and search keys scoped to the name keep working. The admin key needs to manage aliases as well as collections. -
7d904d4:
typesense()takes alocale, a two-letter language code such as"ja","zh", or"th". The sync then tokenizes the collection’s searched text fields for that language, so a query in a script written without spaces matches words inside a run of text, not only at its start. -
ef2ebd4: Typesense results rank by relevance again. The dialog sorted by relevance in ten buckets and then by
search.boost, which ranked pages inside each bucket by boost alone, so with equal boosts the best match could land behind weaker ones. It now sorts by relevance first and uses the boost to order results that match equally well. -
d4087e8: A
layoutoverride incomponents.tswhose key isn’t a layout slot (a typo, orfooterforFooter) now logs a warning that lists the slots, instead of being ignored silently. -
b780c28: Update dependencies:
@astrojs/vercel11.0.11,@pierre/diffs1.5.1,@scalar/astro0.4.26,dompurify3.4.16, andjs-yaml5.4.2. -
d1668f0:
upstash()rate limiting now counts a request and sets its window in one step. A window that ended mid-request could leave the reader’s count with no expiry, which locked them out of that route for good; a count left that way now gets a window again on the reader’s next request. -
7790b31:
upstash()takesurlEnvandtokenEnv, the names of the env vars its REST endpoint and token are read from, so a database added from Vercel’s Marketplace works with theKV_REST_API_URLandKV_REST_API_TOKENit sets:upstash({ urlEnv: "KV_REST_API_URL", tokenEnv: "KV_REST_API_TOKEN" }).blume buildwarns about the variables you name. -
95ff53e: Add
--ignore <glob>toblume validateandblume audit. With--external, an external link whose URL matches the glob is never requested or reported, so a placeholder domain, a local server, or a site that turns bots away no longer fails the check:--ignore "https://api.acme.example/**". Repeat the flag for more patterns. Internal links are always checked. -
d0db71f:
blume validatenow checks every link form Markdown and MDX render, not only one-line inline links: reference-style links (at their[label]: /targetdefinition), autolinks like<https://example.com>, links whose label wraps onto the next line, and lowercase HTML<a href>tags. An<a href>ships as written, so its relative path resolves against the page’s URL, as a browser reads it, and nobasePathis added to it. -
6b7366b:
Accept: text/markdownnegotiation on a Vercel server build now reads media types in any case and honors q-values the wayblume devand Cloudflare do:text/markdown;q=0gets HTML, and so does a header that weighstext/htmlabove Markdown. The prerendered 404’s JSON twin negotiates the same way. When Markdown and HTML both carry a q-value below 1, Vercel’s routing rules can’t compare them and serve HTML.blume devnow sendsVary: Accepton the HTML answer at a negotiated URL too, not only on the Markdown one. -
916861d: A
vercel()server build withbaseset now serves the site under it.@astrojs/vercelignores the base: it left the static files and routes at the root, so every page asset and server route 404’d, and its redirects came out as^/docsold$→/docs/docs/new. The build now moves the static files to.vercel/output/static/<base>/and the routes and redirects under the base.