GitHub Pull Requests
Optional build-time activity feed using astro-loader-github-prs
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.41.2
Patch Changes
Changes
Fixes an error that could appear after the dev server restarts when using an adapter that reuses its dev runtime across restarts (e.g. @astrojs/cloudflare). A request would fail with a 500:
The file does not exist at "node_modules/.vite/deps_ssr/astro_compiler-runtime.js?v=6419660d" which is in the optimize deps directory. The dependency might be incompatible with the dep optimizer. Try adding it to `optimizeDeps.exclude`.
- Root cause: on restart,
vite-plugin-astrore-ranconfigEnvironmentand pushedastroonto the carried-overresolve.conditionsagain, producing a duplicate. The duplicate changed the environment's optimizeDeps config hash, forcing a spurious dependency re-optimization on every restart. That re-optimization could leave the reused runtime importing pre-bundled files that had just been rewritten, producing the error above. - Fix: only add the
astrocondition when it isn't already present, keeping the config hash stable across restarts so no needless re-optimization is triggered.
Testing
- Adds a
vite-plugin-astrounit test assertingconfigEnvironmentadds theastrocondition exactly once when called repeatedly (simulating restart).
Docs
- No docs update needed; this is an internal dev-server bug fix with no API change.
Description
- Fixes #3967 (comment)
- We were relying on a
type: "html"AST node for icons in our Markdown aside plugin (which provides support for our:::notetype syntax). This happens to work when the MDX integration is configured withoptimize: true(perhaps accidentally based on chatting to @Princesseuh). Starlight enables optimization by default, but if a user manually addsmdx(), optimization can be disabled, breaking this behaviour. - The fix is to use a
mdxJsxTextElementAST node when processing MDX files that represents<Fragment set:html={icon}>. - Sätteri doesn’t currently provide a way to know if a plugin is running against an MDX file or a regular Markdown file, so for now this PR uses a basic check of the file’s extension. Not sure if we’re happy to publish with this to get the bug fixed or wait on an official flag.
Description
This PR updates the Playwright version used in the monorepo to workaround a browser installation issue on Node.js 24 and Node.js 26 (reported on Discord)
Basically, looks like Node.js 24.16.0 bumped libuv which surfaced this early-exit issue which was fixed in microsoft/playwright#40747.
I confirmed the issue by running locally npx playwright uninstall --all and then pnpm test:e2e on Node.js 24.17.0 which hang indefinitely. After this change and running both commands again, the installation and tests run successfully.
Fixes #16767
In React 19 dev mode, the check() renderer probe calls the component inside a Tester wrapper during SSR. When the component uses hooks, React emits an "Invalid hook call" warning because the hooks run in Tester's render context instead of the component's own.
The warning is a false positive — the component works correctly. This wraps the probe render with a targeted console.error suppressor that filters only the "Invalid hook call" message, leaving all other errors intact.
Changes
Docs is ahead of us in Prettier version and it breaks our smoke tests
Testing
N/A, though smoke tests should now pass
Docs
N/A
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.5
Patch Changes
-
#17202
c6d254dThanks @matthewp! - Refactors path alias resolution to use Vite's nativetsconfigPathsoptionThis is an internal change with no expected impact on user projects. Astro now defers tsconfig and jsconfig
pathsalias resolution to Vite, keeping a small fallback for a few CSS cases Vite does not yet handle. -
#17123
72e29bdThanks @martrapp! - Fixes an issue where the ClientRouter wipes head elements after page transitions if the<head>contains aserver:defercomponent. -
#17232
257505eThanks @matthewp! - Fixes a bug where<style>tags from components such as a content collection'sContentcould be silently dropped from the output when anawaitappeared before the component in an.astrofile's markup. -
#17193
a7352fdThanks @jan-kubica! - Fixes the background dev server failing to start whenastrois hoisted outside the project'snode_modules(for example bun workspaces). The background process is now spawned from Astro's own resolved location instead of a path assumed under the project root.
@astrojs/cloudflare@14.0.3
Patch Changes
Changes
- Adds
@astrojs/cloudflare/entrypoints/servertooptimizeDeps.includefor the server (ssr) environment so the worker entrypoint is pre-bundled during dev startup. - Previously it was referenced only via
wrangler.jsonc'smain, so Vite's dep scan never reached it and discovered it lazily on the first request — logging✨ new dependencies optimized: @astrojs/cloudflare/entrypoints/serverand triggering a re-optimization (and reload).
Testing
- No automated test added; verified manually by running
astro devon theastro-dev-platformfixture with a cleared.vitecache and confirming the entrypoint now appears indeps_ssr/_metadata.json(pre-bundled up front) instead of being optimized lazily.
Docs
- No docs update needed; this is an internal dev-server optimization with no user-facing API change.
Changes
This PR restores two workflows:
- Automatic closure for missing reproduction. I adapted the script I created in the biome repository to the Astro repository. The script works in the other repo, so it's tested (with the secrets we have)
- Restore the "Wontfix" workflow using the
ghCLI
Testing
Green CI
Docs
N/A
Description
This PR adds zizmor to make Starlight's GitHub actions more robust and follow best practice security recommendations.
I adapted all actions to comply with zizmor's basic rules and also created a .github/zizmor.yml configuration file to ignore specific warnings which we accept (like usages of pull_request_target triggers in two workflow files).
I also added a new .github/workflows/zizmor.yml action, which validates all actions to continue to follow best practices.
Warning
Additionally, I adapted a small logic in .github/workflows/preview-release.yml by removing an external action that can be replaced with a simple gh CLI command (suggested by zizmor as well).
To be honest, it would be nothing short of a miracle if all workflows worked on the first try.
Removed duplicate entry for '**/examples/' in the ESLint config.
Description
- Closes #
- What does this PR change? Give us a brief description.
- Did you change something visual? A before/after screenshot can be helpful.
Adds Starlight MD3 to the community themes list.
This PR adds:
- a new theme entry for Starlight MD3 in
docs/src/content/docs/resources/themes.mdx - light and dark preview images for the theme
- a link to the live demo site
Theme demo:
https://axiaobo7788.github.io/starlight-material-design-theme/
Theme source:
https://github.com/aXiaobo7788/starlight-material-design-theme
npm package:
https://www.npmjs.com/package/starlight-theme-md3
This is a docs/resources update only and does not change Starlight runtime behavior.
Testing
Not run in this fork. The change only adds a community theme listing and static preview assets.
The linked theme package was separately built, packaged, and published as starlight-theme-md3@0.1.3.
Changes
- Fixes
<style>tags from propagating components (e.g. a content collection'sContent) being silently dropped when anawaitappears before the component in slot markup. - Head propagation discovers these components by eagerly pre-rendering slots. An
awaitin the slot suspended that pre-render before the component was reached, so it registered too late and its styles were never collected. Astro now awaits pending async slot pre-renders while collecting head content on routes that use propagation. Routes without propagation are unaffected and keep streaming.
Testing
- Adds a content-collections integration test: a
Contentrendered after anawaitin slot markup, asserting its propagated stylesheet survives. - Adds unit tests for the head-collection loop covering an async slot that registers a propagator after an
await, including a nested case.
Docs
- No docs update needed — restores expected behavior that component styles are collected regardless of where
awaitappears.
Closes #17218
Summary
Fix high severity security issue in packages/astro/src/core/session/handler.ts.
Vulnerability
| Field | Value |
|---|---|
| ID | V-003 |
| Severity | HIGH |
| Scanner | multi_agent_ai |
| Rule | V-003 |
| File | packages/astro/src/core/session/handler.ts:45 |
| Assessment | Likely exploitable |
| Chain Complexity | 2-step |
Description: The session handler accepts attacker-provided session identifiers, allowing session fixation attacks where an attacker sets a user's session ID before login and hijacks the session after authentication.
Evidence
Exploitation scenario: Send malicious link with pre-set session ID parameter to user, then wait for user to authenticate and hijack their session.
Scanner confirmation: multi_agent_ai rule V-003 flagged this pattern.
Production code: This file is in the production codebase, not test-only code.
Threat Model Context
This is a Node.js library - vulnerabilities affect downstream consumers who use this package.
Changes
packages/astro/src/core/session/runtime.ts
Verification
- Build passes
- Scanner re-scan confirms fix
- LLM code review passed
Security Invariant
Property: Protected endpoints reject unauthenticated requests
Regression test
import { createSessionHandler } from 'packages/astro/src/core/session/handler';
describe('Protected endpoints reject unauthenticated requests', () => {
const payloads = [
{ description: 'missing token', token: null },
{ description: 'expired token', token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MzAwMDAwMDB9.invalid' },
{ description: 'malformed token', token: 'malformed.token.here' },
{ description: 'empty token', token: '' },
{ description: 'valid token', token: 'valid.token.here' }
];
test.each(payloads)('rejects adversarial input: $description', async ({ token }) => {
const handler = createSessionHandler();
const mockRequest = {
headers: {
authorization: token ? `Bearer ${token}` : undefined
}
};
const mockResponse = {
status: jest.fn().mockReturnThis(),
json: jest.fn()
};
await handler(mockRequest as any, mockResponse as any);
if (token !== 'valid.token.here') {
expect(mockResponse.status).toHaveBeenCalledWith(401);
} else {
expect(mockResponse.status).not.toHaveBeenCalledWith(401);
}
});
});This test guards against regressions — it's useful independent of the code change above.
Automated security fix by OrbisAI Security
Changes
- Fixes a race condition where
<style>tags from propagating components (e.g. content collectionContentwithpropagation: 'self') were silently dropped from the build output whenawaitappeared in the template/markup section of an.astrofile. Moving theawaitto the frontmatter was the only workaround. - The root cause:
AstroComponentInstancepre-renders slots eagerly to trigger propagator registration, but when a slot function is async it returns a Promise.bufferPropagatedHeadran before the Promise resolved, collecting zero propagators and dropping their styles. - Fix: async slot pre-render Promises are tracked on a new
pendingSlotEvaluationsfield ofSSRMetadata.bufferPropagatedHeadawaits them all before collecting head parts.
Closes #17218
Testing
- Added
'awaits pending async slot evaluations before collecting head parts'topackages/astro/test/units/render/head-propagation/runtime.test.ts. It pushes a delayed Promise that registers a propagator, then verifiesbufferPropagatedHeadwaits for it and collects the resulting style tag. - Updated
runtime-adapters.test.tsandserver-islands-render.test.tsfixtures to include the newpendingSlotEvaluationsfield onSSRMetadata.
Docs
No docs update needed — this is a bug fix restoring already-documented behavior (styles from components should always be collected regardless of where await appears).
Summary
The core SSR request path parses request.url and rebuilds a few small things more often than it has to. This cuts that repeated work on the hot path. Nothing about the rendered output changes, and no public API moves; it's all internal.
This started as a bigger PR and was trimmed after review. The cross-adapter URL sharing (a new RenderOptions.url option) and the createOutgoingHttpHeaders rewrite are out, and the adapter side will come back on its own as a minor. What's left here is internal to astro core and stays a patch.
What changed
- Domain-based i18n only looks at the
Hostheader when adomains-*strategy is configured. That condition is now worked out once in the constructor, so every other app skips the probe, and the URL parse behind it, in bothmatch()andrender(). TrailingSlashHandleruses the raw pathname and search thatFetchStatecaptures before it normalizes the URL, instead of parsingrequest.urla second time to get them back.getParamschecks the route's own pattern and then its fallback routes, stopping at the first match, rather than building throwaway arrays with.map().map().find().- The in-memory cache provider reuses the
URLalready sitting oncontext.url(and checks the request method first) instead of parsingcontext.request.urlagain. - Domain-based i18n parses each
domainLookupTablekey once and keeps it per table, instead of re-parsing every key on every request.
Testing
No behavior change, so nothing new to test. The paths above are covered by the existing unit suites (routing, i18n, app, cache, fetch), which pass, and the typecheck is clean.
Benchmarks
Micro-benchmarks, median of 7 runs on the same machine, for the changes that stayed in this PR:
| Before | After | |
|---|---|---|
| Domain i18n lookup (4 domains) | 1331 ns | 285 ns |
| Cache request (memory provider) | 474 ns | 243 ns |
getParams matching |
54 ns | 40 ns |
On a render-heavy page this is lost in the noise, since HTML rendering dominates. It shows up more on light responses like endpoints, redirects and 304s, where per-request setup is a bigger slice of the work.
Investigated and measured with help from Claude Opus 4.8.
Changes
- Inline
.astro<style>blocks now respectvite.build.target(andcssTarget) even whenvite.build.minify/cssMinifyisfalse. Previously, the@astrojs/compiler-rscompiler used lightningcss internally for CSS scoping and inadvertently modernized CSS syntax (e.g.min-width: 1000px→width >= 1000px). With minification enabled, Vite'sminifyCSSstep would reverse this via target-based lowering. With minification disabled, that lowering was skipped entirely, leaving modernized syntax in the output regardless of the configured target. - Adds a
lowerCssToTargets()step incompile.tsthat runs lightningcss withminify: falseand the user's configured targets after the compiler returns CSS, but only whencommand === 'build',cssMinifyis falsy, and acssTargetis set. The target string conversion (esbuild/Vite format → lightningcss format) mirrors Vite's internalconvertTargetslogic.
Closes #17225
Testing
- Adds
packages/astro/test/config-vite-css-target-no-minify.test.tswith a new fixture (config-vite-css-target-no-minify/) targetingsafari14withminify: false. Covers: MQ range syntax is not used (min-widthpreserved), andinsetshorthand is not used (longhandtop/right/bottom/leftpreserved).
Docs
- No docs update needed — this is a bug fix restoring already-documented behavior for an existing config option.
Note: A changeset has not been added to this PR yet. One should be added before merge (
patchbump forastro).
Changes
- Dynamic file endpoints like
src/pages/api/[name].json.tswithtrailingSlash: "always"now correctly return 200 for/api/bar.jsonand 404 for/api/bar.json/in dev mode, matching production behavior and the documented Astro v6 rule that file endpoints are never served with a trailing slash. - Root cause:
trailingSlashForPathcheckedpathname && hasFileExtension(pathname), butpathnameisnullfor dynamic routes (any segment with a param). The fix adds an optionalroutefallback parameter — the string fromjoinSegments— which preserves file extensions even for dynamic segments (e.g.,/api/[name].json), allowing the file-extension check to work correctly.
Testing
- Added
'dynamic file endpoints force trailingSlash never. issues#17001'inpackages/astro/test/units/routing/manifest.test.ts: creates a[name].json.tspage undertrailingSlash: "always"and asserts the generated route pattern matches without a trailing slash and rejects with one.
Docs
- No docs update needed — this restores already-documented Astro v6 behavior.
Closes #17001
Changes
getCollection()in dev mode silently returned an empty array for content collections with ~500k+ entries. The virtual module for the data store useddataToEsm()to serialize the store as a JS object literal, which for large collections produces ~7.5MB of JS source. Vite'sssrTransformScriptpassed this to rolldown/oxc-parser'sparseAstAsync(), whose resulting AST was too large to cross the NAPI bridge — throwing"Failed to convert rust String into napi string", silently caught inImmutableDataStore.fromModule(), returning an empty store.- Replaced
dataToEsm(parsed, { compact: true })withexport default JSON.parse(${JSON.stringify(jsonData)}). This embeds the data as a single string literal, keeping the AST tiny regardless of collection size. As a bonus,JSON.parse()of a string literal is faster to evaluate than a large object literal. Removed the now-unuseddataToEsmimport.
Testing
- No new tests added — generating 500k entries in CI is too slow. All existing content collection tests (59/59 content-collections, 64/64 content-layer unit tests) continue to pass.
Docs
- No docs update needed; this is a dev-mode bug fix with no API changes.
Closes #17220
Removed the LinkCard for 'starlight-utils' from the documentation as I no longer have time to maintain this (and it's very out of date). In the documentation site I offer some alternatives for the functionality that this package included.
Changes
- Warms up the dev server before the experimental client prerender prefetch tests run.
- Avoids racing immediate page assertions against Vite dev reloads on Windows.
Testing
- Updates the existing prefetch E2E setup for the client prerender block.
Docs
- No docs needed; test-only change.
Changes
resolveTargetVersion()now compares the dist-tag version against the currently installed version before applying it. If the dist-tag resolves to an older version than what's installed, the function falls back tolatestinstead of downgrading.- Fixes the case where
pnpm dlx @astrojs/upgrade betawould silently downgrade companion packages (e.g.@astrojs/sitemapfrom3.7.3→3.6.1-beta.3) when theirbetadist-tag pointed to a pre-release that predated the current stable. resolveTargetVersionis now exported so it can be unit-tested directly.
Testing
- Added
describe('resolveTargetVersion')inpackages/upgrade/test/verify.test.tswith three cases: no-downgrade when the beta dist-tag is older than the installed version; correct upgrade when the beta dist-tag is newer; and fallback tolatestwhen the requested dist-tag doesn't exist on the registry.
Docs
- No docs update needed; this is a bug fix for internal
@astrojs/upgradebehavior with no API surface change.
Closes #17024
Changes
- Clarifies that RouteCache provides a shared static-path table for matching, props resolution, and prerender generation.
- Documents why dev request handling does not clear the route cache per request.
Testing
- Not run; comment-only change.
Docs
- No user-facing docs update needed; this only documents an internal invariant.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.4
Patch Changes
-
#17212
7ba0bb1Thanks @matthewp! - Ensures transition directive values are HTML-escaped when rendered on hydrated islands -
#17224
dc5e52fThanks @astrobot-houston! - Fixes trailing slash handling for dynamic file endpoints in dev mode. Dynamic file endpoints (e.g.,src/pages/api/[name].json.ts) withtrailingSlash: "always"incorrectly required a trailing slash in dev mode, returning 404 for/api/bar.jsonand 200 for/api/bar.json/. -
#17067
23f9446Thanks @fkatsuhiro! - Fixed a bug where the development toolbar did not output a warning even though the implicit ARIA role and the manually specified role were duplicated. -
#17234
d5fbee8Thanks @ocavue! - Adds support forsharpv0.35. pnpm users no longer need to approvesharp's build script (seeallowBuilds) when on v0.35. -
#17223
5970ef4Thanks @astrobot-houston! - FixesgetCollection()returning empty in dev mode for large content collections (500k+ entries) -
#17184
799e5cdThanks @Princesseuh! - Upgrades the Rust compiler to the latest, which fixes some bugs. Refer to its changelog for more information. -
#17208
da8b573Thanks @matthewp! - Hardens forwarded header handling so the internal request helper validatesX-Forwarded-Hostagainstsecurity.allowedDomainsbefore trustingX-Forwarded-ForforclientAddress. Previously it only checked that the header was present, which was inconsistent with the publiccreateRequesthelper. This aligns both code paths; behavior is unchanged for correctly configured proxies.
@astrojs/rss@4.0.19
Patch Changes
- #17209
fbcfa03Thanks @matthewp! - Hardens RSS feed generation by escaping thesourceandenclosureitem fields. These fields are now serialized as structured XML values, ensuring that special characters in values likesource.titleandenclosure.typeare always treated as text rather than markup, consistent with how other feed fields are handled.
create-astro@5.2.1
Patch Changes
- #17205
e37dfe2Thanks @astrobot-houston! - Fixes dependency installation when creating Astro projects with pnpm 11+
@astrojs/cloudflare@14.0.2
Patch Changes
-
#17049
ffceaa2Thanks @astrobot-houston! - Fixes prerender errors being silently swallowed when pages throw during rendering in workerd, causingastro buildto exit 0 and emit truncated HTML. The response body is now fully buffered inside workerd before being sent back to the build process, so streaming errors are caught and surfaced as build failures with clear error messages. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
Changes
- HTML-escapes transition directive values before copying them onto hydrated island attributes.
- Keeps transition attributes on the island wrapper while preserving the existing render path.
Testing
- Adds hydration unit coverage that renders generated island HTML and asserts transition directive attribute values are encoded.
Docs
- No docs update needed; this aligns internal island attribute rendering with existing escaping behavior.
Changes
Needed for smoke tests
Testing
Smoke tests should pass!
Docs
N/A
Changes
- Builds the
sourceandenclosureitem elements as structured XML objects instead of interpolating values into a string and re-parsing them. This ensures special characters in fields likesource.titleandenclosure.typeare always serialized as text, consistent with how every other feed field is already handled.
Testing
- Adds two cases covering
sourceandenclosurefields containing XML special characters, asserting the values round-trip verbatim and produce exactly one element each.
Docs
- No docs update needed; this is an internal serialization change with no API surface change.
Changes
- Aligns the internal
createRequestFromNodeRequesthelper with the publiccreateRequest:X-Forwarded-Foris only trusted forclientAddresswhenX-Forwarded-Hostactually matchessecurity.allowedDomains, instead of just checking that the header is present. - No behavior change for correctly configured proxies; this only tightens consistency between the two request helpers.
Testing
- Adds a
createRequestFromNodeRequesttest block covering: trustedHost, trustedX-Forwarded-Host, a non-matchingX-Forwarded-Host(now falls back to the socket address), and the no-allowedDomainscase.
Docs
- No docs update needed;
allowedDomainsbehavior is unchanged from a user's perspective.
Description
- What does this PR change? Give us a brief description.
Hey everyone! 👋 I recently spent a couple of weeks building a local-first browser editor for Starlight called Axiom Studio. I originally built it to make my own MDX and frontmatter editing workflow easier and safer without needing any external CMS or database.
Thought it might be useful for other indie makers and developers in the Astro ecosystem, so I added it to the community tools list in plugins.mdx. (I also included a short demo video on the link if you're curious to see how the local editing actually works in action). Let me know if the format looks good!
- Did you change something visual? A before/after screenshot can be helpful.
Nope, no visual changes to the docs themselves. Just added the new<LinkCard>to the bottom of the grid.
Changes
- Adds an
ensurePnpmBuildsAllowed()helper that writesallowBuildsentries foresbuildandsharpintopnpm-workspace.yamlbefore runningpnpm install. This mirrors the existingensureYarnLock()pattern for Yarn Berry. - pnpm v11.0.0 set
strictDepBuildstotrueby default, causingpnpm installto exit non-zero when any dependency has an unapproved build script. Bothesbuildandsharp(Astro dependencies) have postinstall scripts, sopnpm create astrowould fail withERR_PNPM_IGNORED_BUILDSand leavenode_modulesuninstalled. - If
allowBuildsis already present inpnpm-workspace.yaml, the helper skips writing to avoid overriding user configuration.
Testing
- No new tests added — the helper runs during the live
pnpm installcall and is difficult to unit test in isolation. All 120 existing tests continue to pass. - Confirmed end-to-end by issue reporter (@jettwayio):
npx create-astro --template starlightnow createsnode_modulessuccessfully with pnpm v11.
Docs
- No docs update needed — this is an internal scaffolding fix with no user-facing API change.
Note: A changeset for
create-astrois needed before merging.
Changes
- Enables Vite's native
resolve.tsconfigPathsby default. - Keeps Astro's custom alias plugin as a documented deprecated fallback for the cases it already supports.
- Adds comments clarifying the fallback CSS/module support and future removal intent.
Testing
- No new tests; existing alias coverage verifies the fallback behavior remains intact.
Docs
- No docs update needed because this changes internal resolution defaults.
Related #17163
Changes
This removes a lot of deps from Astro because rehype and co are quite heavy for just doing this. We already use ultrahtml elsewhere, so why not here.
Testing
Tests should pass
Docs
N/A
It is better to merge #17179 first (while it does change only the remaining twitter:* meta tags).
Changes
- Corrected the
twitter:cardmeta tag to usenameinstead ofproperty. Resource - Applied the fix in the blog component and matching e2e fixtures.
Testing
- Verified generated HTML no longer includes the Twitter meta tags as
propertybutname. - Confirmed remaining Twitter metadata continues to render correctly.
Docs
- No docs changes needed.
Resources
Is there a resource or something that states that those tags are redundant? You should provide those resources together with the PR
Normally the link I'd have shown would've been: https://developer.x.com/en/docs/twitter-for-websites/cards/overview/markup
But it doesn't work because they've removed it and one cannot find it anywhere.
But Wayback Machine comes to help:
- https://web.archive.org/web/20240613190853/https://developer.x.com/en/docs/twitter-for-websites/cards/overview/markup
- https://web.archive.org/web/20240616101429/https://developer.x.com/en/docs/twitter-for-websites/cards/guides/getting-started
As you can see for twitter:* meta tags, name is used.
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@">
<meta name="twitter:creator" content="@">
<meta property="og:url" content="">
<meta property="og:title" content="">
<meta property="og:description" content="">
<meta property="og:image" content="">This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.41.1
Patch Changes
Description
- Closes #3987
- Updates Expressive Code to the new release that supports Astro 7
Changes
- The background dev server (auto-enabled for agentic environments) fails to start when
astrois hoisted outside the project'snode_modules(for example bun workspaces). The foreground server is unaffected. cli/dev/background.tsspawnsresolve(rootPath, 'node_modules', 'astro', 'bin', 'astro.mjs'), which doesn't exist in a hoisted layout, so the detached child exits withERR_MODULE_NOT_FOUNDand the parent reportsDev server process exited before becoming ready.- Fix: resolve the bin from Astro's own location (
astro/package.json→../bin/astro.mjs) so it spawns itself regardless of install layout.
Same family as #17135 / #17190.
Testing
Manual: an Astro app in a bun workspace with hoisted deps, background mode. Before: Dev server process exited before becoming ready (.astro/dev.log: Module not found …/node_modules/astro/bin/astro.mjs). After: starts normally.
Docs
None — restores existing behavior in monorepos.
Changes
Hardens a flaky e2e test: Dev Toolbar - Audits › can warn about perf issue for below the fold image in absolute container.
The audit gates highlight creation on the image's load event (apps/audit/index.ts awaits load so the above/below-the-fold position checks have accurate layout). The test clicked the audit button and immediately asserted toHaveCount(1) with the default 6s expect timeout. When the optimized image loads slowly (e.g. a contended CI runner), the highlight is not created in time and the assertion intermittently sees 0 highlights, failing every retry.
The test now waits for the image to finish loading before triggering the audit, so the assertion no longer races the image load. No production code changes.
This flaked (all 3 retries) on unrelated CI, e.g. https://github.com/withastro/astro/actions/runs/28129089505/job/83301140796
Testing
pnpm --filter astro exec playwright test dev-toolbar-audits.test.tsruns 11 passing, executed twice for stability.- The targeted test passes locally. The change only adds a wait before the audit runs, so it cannot make the assertion less reliable.
Docs
No docs changes; this is test-only. No changeset, since there are no published package changes.
Changes
Custom Markdoc transform functions were being dropped for tags and nodes whose names need bracket access, like side-note.
Here is why. When you set a custom render component, resolveComponentImports removes the transform unless that transform "respects" render (issue #9708, added in #15335). It worked that out by string-matching the transform's source for config.tags?.<key>?.render or config.nodes?.<key>?.render, which only covers dot notation.
A key like side-note can't be read with dot notation at all (config.tags?.side-note?.render parses as a subtraction), so those transforms have to use bracket notation: config.tags?.['side-note']?.render. The old check never matched that, so the transform was always treated as not respecting render and deleted, which broke the tag.
The detection now handles bracket notation alongside dot notation, plus optional chaining and whitespace. Includes a changeset (@astrojs/markdoc patch).
Fixes #17118.
Testing
Added packages/integrations/markdoc/test/resolve-component-imports.test.ts, a unit test over the exported resolveComponentImports:
- a render-respecting transform on a dashed tag (
side-note, via bracket access) is kept; - a transform that does not respect render is still removed so
renderwins.
I checked it both ways: the first case fails on main (transform deleted) and passes with this change. The full package suite is green too. pnpm --filter @astrojs/markdoc test runs 50 tests, including the existing render-with-transform test for #9708. tsc -b, biome check, prettier --check, and eslint are all clean on the changed files.
Docs
No docs changes. This restores the documented behavior (a custom render together with a custom transform) for tag and node names that contain dashes, and there is no public API change.
Changes
astro check(and any command that auto-installs dependencies throughgetPackage) failed to findtypescriptand@astrojs/checkwhen Astro itself is installed outside the project directory (e.g. a pnpm global/virtual store, or a monorepo where the store isn't under the project).- Root cause:
getPackageresolved the dependency withrequire.resolve(pkg, { paths: [cwd] })but then loaded it with a bareimport(pkg). A bare specifier resolves relative to Astro's own location, not the project, so the import threwERR_MODULE_NOT_FOUNDeven though the package existed in the project. - Fix: import the resolved absolute path via
pathToFileURL(...), so dependencies are loaded from the project directory. The same fix is applied to the post-install import path. Resolving first withrequire.resolveis preserved (it also avoids caching a failed ESM import before installing). - Added a changeset (
astropatch).
Fixes #17135.
Note: the root cause was already diagnosed by @astrobot-houston in the issue, and @matthewp verified the same fix via the
pkg.pr.newpreview (astro@9a53f77). Since the previous fix branch was lost, this PR provides a ready implementation with a regression test.
Testing
- Added
packages/astro/test/units/cli/install-package.test.ts: installs a dependency only inside a temporary project'snode_modules(outside Astro's resolution scope) and assertsgetPackageloads it from the providedcwd. This fails on the old bare-importbehavior and passes with the fix. pnpm --filter astro exec astro-scripts test "test/units/cli/install-package.test.ts" --strip-types→ 1 passing.biome check,prettier --check, andeslintpass on the changed files.
Docs
No docs changes needed — this restores the documented behavior (resolving dependencies from the user's project) without changing any public API.
Changes
- In dev mode, when multiple routes match a URL pattern, an error thrown inside one route's
getStaticPaths()no longer prevents other candidate routes from being tried. Previously, any non-routing error was immediately re-thrown, causing valid sibling routes to return a 500 instead of their correct response. - The fix stores the first non-routing error and defers re-throwing it until all candidates are exhausted without a match, so the error still surfaces when there's genuinely no valid route.
Closes #9819
Testing
- Added
packages/astro/test/units/routing/dev-match-fallthrough.test.tswith two cases: one verifying that route B is matched when route A'sgetStaticPathsthrows, and one verifying the error is still re-thrown when no valid alternative exists.
Docs
- No docs update needed; this is a dev-mode bug fix with no API or user-facing behavior change beyond the bug itself.
Changes
- When pnpm's
minimumReleaseAgepolicy blocks an install,@astrojs/upgradenow shows a clear message explaining that pnpm's policy is the cause, instead of the generic "Dependencies failed to install" message. - Fixes
shell.tsfalling back tostdoutwhenstderris empty on failure. pnpm writes itsMINIMUM_RELEASE_AGE_VIOLATIONerror to stdout, so the previous code discarded it entirely and threw a meaningless "Process exited with code 1" error.
Testing
- Adds
'pnpm minimumReleaseAge error shows specific message'inpackages/upgrade/test/install.test.ts: verifies that aMINIMUM_RELEASE_AGE_VIOLATIONshell error surfaces theminimumReleaseAgemessage and suppresses the generic fallback.
Docs
- No docs update needed — this is a CLI error message improvement with no API or configuration surface change.
Closes #17182
Changes
- Adds
astro/virtual-modules/transitions.jsto the Cloudflare adapter's serveroptimizeDeps.includelist. This prevents Vite from discovering the module mid-request during cold SSR, which was triggering an optimizer reload that invalidated React's hook dispatcher and caused "Invalid hook call" /useContextnull errors when using<ClientRouter />.
Testing
- No new tests — the bug only manifests with npm-installed packages (not workspace-linked), so it cannot be reproduced in the monorepo's test suite. The reporter confirmed the fix works in their minimal repro.
Docs
- No docs update needed — this is an internal dev-server reliability fix.
Closes #17166
Changes
- This reverts a patch to changesets added in #15813
- It is no longer needed and causes a turborepo warning (and perhaps we could look at whether it should be upstreamed)
Testing
n/a — dependency change only, existing tests should pass, as should our next release
Docs
n/a
Changes
- Adds a
--no-aiflag tocreate astro, so users can opt out of creatingAGENTS.mdandCLAUDE.mdfiles if they don’t need them. - Avoids people having to manually clean up after running
create astro.
Testing
Added a small test to make sure the CLI flag is interpreted correctly.
Docs
AFAIK we only document the full list of flags in the package README, so I’ve updated that. That’s what’s linked in the docs here: https://docs.astro.build/en/install-and-setup/#cli-installation-flags
/cc @withastro/maintainers-docs for feedback!
Summary
- update Issue Triage to withastro/triagebot-action v0.3.8
- picks up restored preview release publishing and expanded triage logging
Tests
- Not run (workflow pin update only)
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
create-astro@5.2.0
Minor Changes
- #17185
d64b09bThanks @delucis! - Adds a--no-aiflag to allow users to opt out of creatingAGENTS.mdandCLAUDE.mdfiles when running create astro
astro@7.0.3
Patch Changes
-
#17189
24d2c9eThanks @astrobot-houston! - Fixes a bug where an error thrown inside one route'sgetStaticPaths()would prevent other valid routes from being matched in dev mode -
#16932
8f4a3dbThanks @fkatsuhiro! - Fixes HMR for action files during development. Editing files insrc/actions/now takes effect on the next request without requiring a dev server restart. -
#17087
fb0ab02Thanks @jp-knj! - Fixes localized custom error pages in i18n projects so routes like/pt/404are used for missing localized pages and return the correct status code
@astrojs/cloudflare@14.0.1
Patch Changes
-
#17175
7a7d879Thanks @astrobot-houston! - Fixesastro devOOM crashes for@astrojs/cloudflareusers on Vite 8 by migrating the frontmatter scan plugin to Rolldown-compatible options. -
#17187
0db4b57Thanks @matthewp! - Fixes React invalid hook warning during cold SSR optimizer reload when using ClientRouter -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/markdoc@2.0.1
Patch Changes
- #17142
973ea49Thanks @astrobot-houston! - Fixes a crash when rendering shiki-highlighted code blocks inside list items
@astrojs/upgrade@0.7.3
Patch Changes
- #17188
675d11dThanks @astrobot-houston! - Fixes@astrojs/upgradeshowing a generic error when pnpm'sminimumReleaseAgepolicy blocks installation. The error message now explains that pnpm's policy blocked the update and suggests running the install command manually.
Summary
- update issue triage workflow to triagebot-action v0.3.7
- v0.3.7 adds Flue event logging so triage runs show thinking/tool/log progress in Actions
Testing
git diff --check
Changes
- Fixes
astro devOOM crashes for@astrojs/cloudflareusers on Vite 8. The adapter's.astrofrontmatter scan plugin was registered viaoptimizeDeps.esbuildOptions.plugins, which Vite 8 deprecates and ignores. Without the plugin, Vite's HTML scanner misparses.astrofrontmatter comments containing backticks (e.g.// Use a `<script>` tag), causing a RolldownPARSE_ERRORthat skips pre-bundling entirely and can OOM the workerd isolate at startup. - Adds a Rolldown-compatible frontmatter scan plugin (
rolldown-plugin-astro-frontmatter.ts) and switches theconfigEnvironmenthook to register it viaoptimizeDeps.rolldownOptions. Also removes thebanner: { js: '' }workaround, which was esbuild-specific and no longer needed.
Closes #17168
Testing
- No new tests added — existing suites (
ssr-deps,top-level-return,ts-astro-import,user-optimize-deps) already cover the frontmatter scan plugin behavior and all pass with the new Rolldown plugin.
Docs
- No docs update needed; this is a bug fix with no user-facing API changes.
Summary
- update issue triage workflow to triagebot-action v0.3.6
- v0.3.6 fixes verified-fix PR creation for legacy
flue/fix-*branches and delays the issue verified label until PR creation succeeds
Testing
git diff --check
Summary
- update issue triage workflow to triagebot-action v0.3.5
- v0.3.5 adds mocked triage-flow integration coverage in triagebot-action
Testing
git diff --check
Summary
- update issue triage workflow to triagebot-action v0.3.3
- v0.3.3 fixes the bundled action syntax error from duplicate
createRequiredeclarations - v0.3.3 also adds integration tests that run the built action entrypoint and standalone Flue session initialization
Testing
git diff --check
Summary
- update issue triage workflow to use triagebot-action v0.3.2
- v0.3.2 fixes Flue session initialization now that the triage code runs as a standalone GitHub Action instead of under
flue run
Testing
git diff --check
Summary
- update issue triage workflow to use triagebot-action v0.3.1
- v0.3.1 fixes hyphenated action input parsing for inputs like
read-token
Testing
git diff --check
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.2
Patch Changes
- Updated dependencies [
3b5e994]:- @astrojs/markdown-satteri@0.3.2
@astrojs/markdown-satteri@0.3.2
Patch Changes
- #17165
3b5e994Thanks @Princesseuh! - Fixes headings being listed twice in a page'sheadingsmetadata when an integration (such as Starlight) assigns heading IDs with its own heading pass before adding anchor links
Changes
That plugin can run twice in some situations, so it shouldn't blindly append to data
Testing
Added a test
Docs
N/A
Changes
- Remove obsolete
<meta http-equiv="X-UA-Compatible" content="IE=edge">tags from Astro examples, fixtures, and templates. - Keep the rest of the head markup unchanged.
- Simplify HTML output and remove legacy IE-only boilerplate.
Testing
- Updated the affected fixtures and example files.
- Verified the change is limited to removing the legacy meta tag.
Docs
- No docs update needed.
- This change removes a legacy browser compatibility tag that is no longer useful for modern browser output.
Changes
- Adds a new
createIslandSignalfunction exported from@astrojs/solid-js/signalsthat lets users create signals in.astrofrontmatter and pass them to multipleclient:*Solid components. All islands sharing the same signal get a single reactive instance on the client, so updating it in one island updates all others. - Signals are detected on the server via Symbol-based branding (Solid signal getters are plain functions with no intrinsic identity, unlike Preact signals which are duck-typeable objects). The server serializes a
data-solid-signalsJSON attribute mapping prop names to shared signal IDs ("sg0","sg1", etc.). On the client, a module-levelsharedSignalMapcreates or reusescreateSignaltuples by ID. - Supports getter props, setter props (encoded with a
!suffix on the signal ID), and signals nested inside arrays or objects. - Signal values remain callable during SSR (unlike Preact, where
.peek()scalars replace signals before render). Props are replaced with scalar values only after rendering, before Astro'sserializePropsruns.
Testing
packages/integrations/solid/test/signals.test.ts— Integration tests verifyingdata-solid-signalsserialization: shared signals between islands get the same ID, array signals produce[id, index]tuples, object signals produce[id, key]tuples, and SSR output contains correct values.packages/astro/e2e/solid-signals.test.ts— E2E tests verifying signals hydrate with the correct initial value and that clicking a button in one island reactively updates a shared signal displayed in another island.
Docs
- Docs update needed to document the new
@astrojs/solid-js/signalsexport andcreateIslandSignalAPI. Not included in this PR.
Changes
Fixes an issue where using --background --host didn't take the new URLs into consideration.
Testing
Added new tests
Docs
No changes. The feature should be supported out of the box.
Closes #17156
Changes
- Tsconfig path aliases in CSS
url()calls (e.g.background-image: url('@assets/ok.png')) now resolve correctly during build, restoring behavior from Astro 6. - The
astro:tsconfig-alias-csspre-transform plugin previously only rewrote@importspecifiers. Added acssUrlREregex to also rewriteurl()references before Vite processes them. In Vite 8, CSSurl()resolution does not go through userresolveIdhooks, making the pre-transform rewrite the only viable approach.
Testing
- New fixture and test (
alias-css-url.test.ts) covering the regression: a<style>tag usingurl('@assets/ok.png')with a tsconfig@assets/*alias, verified to produce a resolved asset path in the build output.
Docs
- No docs update needed; this restores existing documented behavior for tsconfig aliases in CSS.
Changes
The job wasn't working properly anymore, and I believe the cause was the concurrency part.
Testing
Green CI
Docs
Changes
With the upgrade to Astro 7 I ran into:
[sass] Error: Can't find stylesheet to import.
╷
2 │ @import "@styles/_variables.scss";
in .astro files that have <style lang="scss">.
This extends the workaround fix for aliases.
Testing
Tested it by changing this locally.
Extended the css test case to also test scss imports.
Docs
No doc updates needed, just restores previous behavior.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.1
Patch Changes
-
#17151
ccceda3Thanks @matthewp! - Fixesastro devincorrectly starting in background mode for Warp terminal users. Hybrid environments like Warp are no longer treated as AI agents for auto-background detection. -
#17158
164df87Thanks @ematipico! - Fixesastro dev --background --hostnot listing the network addresses. The background server start output andastro dev statusnow show every exposed network URL, matching the foreground dev server. -
#17141
d785b9dThanks @astrobot-houston! - Fixes responsive image CSS overriding user styles defined inside CSS@layerblocks. The generated image styles are now wrapped in@layer astro.images, ensuring they have lower cascade priority than user-defined layers. -
#17150
1a61386Thanks @matthewp! - Fixesastro dev --backgroundfailing on Windows with "Failed to spawn background dev server process"
Changes
- Switches agent detection in
astro devfromisAgent()todetectAgenticEnvironment().type === "agent", excluding"hybrid"environments from auto-background mode.isAgent()returnstruefor both"agent"and"hybrid"types, which causes false positives in terminals like Warp that setTERM_PROGRAM=WarpTerminal. - Bumps
am-i-vibingfrom^0.3.0to^0.4.0which classifies Warp as"hybrid"and includes the latest provider list.
Reported in ascorbic/am-i-vibing#89 and Discord.
Testing
- No test changes. The fix is a one-line predicate change in
isRunByAgent()and there are no existing tests for agent detection.
Docs
- No docs update needed. This is a bug fix to existing behavior.
Changes
- Spawns
process.execPath(node) directly withnode_modules/astro/bin/astro.mjsinstead of going through thenode_modules/.bin/astroshim. On Windows, npm/pnpm/yarn create.cmdbatch file shims in.bin/that cannot be executed bychild_process.spawn()withoutshell: true, causingspawn()to fail andchild.pidto beundefined.
Closes #17146
Testing
- No new tests. The spawn codepath in
background.tshas no integration test coverage on any platform — existing tests only cover pure formatting/parsing helpers.
Docs
- No docs changes needed. This is an internal bug fix; the
--backgroundflag API is unchanged.
Changes
- Fixes a
TypeError: node.children.map is not a functioncrash when using@astrojs/markdocwith the shiki extension and placing a fenced code block inside a list item. - The root cause is an async/sync mismatch: the shiki
fencetransform isasync, so Markdoc's internaltransformChildren()returns aPromisewhen a code fence is nested in a list. Markdoc's built-inlistnode has a customtransform()that passes this Promise directly tonew Tag()without awaiting it, leavingTag.childrenas an unresolved Promise.createTreeNode()inTreeNode.tsthen crashes calling.map()on it. - Fixed by adding
await Promise.resolve(node.children)increateTreeNode()before calling.map(), which defensively resolves any Promise-valued children. Synchronous case is unaffected (resolving a non-Promise is a no-op).
Closes #17126
Testing
- Added a test case in
packages/integrations/markdoc/test/syntax-highlighting.test.tsthat renders a.mdocfile with a shiki-highlighted code fence nested inside a list item, confirming the page builds without error and the highlighted code is present in the output.
Docs
- No docs update needed — this is a bug fix for an internal rendering crash with no API changes.
Closes #17139
Changes
- Wraps all CSS generated by
generateImageStylesCSS()in@layer astro.images { … }. Per the CSS cascade spec, unlayered styles always beat layered styles regardless of specificity — so the previous unlayered:where([data-astro-image=constrained]){max-width:100%}was silently overriding any usermax-widthdefined inside a@layer, even though:where()has zero specificity. Putting these defaults into a named layer restores the original intent of:where(): Astro's image styles should always be easy for users to override. - The layer name
astro.imagesuses a dotted namespace to avoid collisions with user-defined layer names while remaining valid for CSS processors (e.g. lightningcss). - Note for users: to guarantee
astro.imagessits below your own layers, declare the order explicitly:@layer astro.images, layout, page, components;. Without an explicit order declaration, the first-seen layer wins — which may or may not beastro.imagesdepending on stylesheet load order.
Testing
- Updated assertions in
packages/astro/test/units/assets/utils.test.tsto expect the@layer astro.images { … }wrapper around the generated CSS output.
Docs
- No docs update needed.
image.responsiveStylesis already documented as producing overridable defaults; this fix makes that promise actually true when users write CSS layers.
Changes
Root cause
When nested css file content updated, delete metadata, this cause full-reload.
if (isUpdatedFileCssDep) {
astroFileToCompileMetadata.delete(astroFile);
}
Solution
Prevent full-reload by using compile metadata, and fall back to deleting metadata if undetected.
try {
const code = await readFile(astroFile, 'utf-8');
await compile(code, astroFile);
} catch {
astroFileToCompileMetadata.delete(astroFile);
}
Testing
- Prepared test data in the fixture
- Mocked the HMR context
- Mocked and executed the handleHotUpdate function
- Verified that the metadata was not deleted
Before implementation
After implementation
Docs
close #16310
What changed
- adds a Cloudflare fixture with an Astro Action
- verifies two consecutive builds succeed while reusing the warm Vite dependency cache
- removes the broad three-attempt fixture build retry that masked stale prebundle failures
Why
Astro issue #16933 reported Cloudflare builds failing when src/actions/index.ts was present because dependency optimization could observe a stale prebundle. The runtime startup change in #16961 addressed the production failure; this PR adds deterministic regression coverage so that behavior remains protected and optimizer races fail visibly instead of being hidden by retries.
Validation
- Cloudflare integration build passes
- optimizer-sensitive test subset: 11 passed
- full Cloudflare adapter suite: 263 passed, 1 skipped, 0 failed
- Biome and Prettier checks pass
- lockfile frozen install check passes
Changes
ctx.request.signalin the dev server now firesabortand setssignal.aborted = truewhen the client disconnects, matching production behavior with the Node adapter.- In
vite-plugin-app/app.ts, anAbortControlleris created per request, itsabort()is wired to the socket'scloseevent, and the signal is passed tocreateRequest()via the existinginitoption — no new API surface required.
Testing
- Adds a
dev-signal-testAPI endpoint fixture that awaits eithersignal.abortor a 5-second timeout, returning{ aborted }. - Adds a new
describeblock inrequest-signal.test.tsthat starts a dev server, aborts a fetch mid-flight, and asserts the endpoint observedaborted = true; a follow-up non-aborted request assertsaborted = false.
Docs
No docs update needed — this restores parity between dev and production; no user-facing API changed.
Closes #17120
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.0
Major Changes
-
#16965
57ead0dThanks @Princesseuh! - Makes'jsx'the default value forcompressHTMLAstro now strips whitespace from your HTML using JSX rules by default, the same way frameworks like React do. Whitespace and line breaks around elements are removed, but meaningful whitespace within a single line — like a space between two inline elements — is preserved. To keep a space that would otherwise be removed, write it explicitly in your source, for example with
{" "}.This can change rendered output where whitespace between inline elements was previously meaningful. To keep Astro's earlier behavior, set
compressHTML: truefor HTML-aware compression, orcompressHTML: falseto preserve all whitespace. -
#16610
c63e7e4Thanks @matthewp! - Adds background dev server management for AI coding agents.When an AI coding agent is detected,
astro devnow automatically starts the dev server as a detached background process. This prevents the dev server from blocking the agent's terminal and allows it to continue working while the server runs.A lock file (
.astro/dev.json) is written when the dev server starts, recording the server's URL, port, and PID. This prevents duplicate servers from being started for the same project.New flag and subcommands
astro dev --background— Start the dev server as a background process (this is what runs automatically when an agent is detected).astro dev stop— Stop a running background dev server.astro dev status— Check if a dev server is running and display its URL, PID, and uptime.astro dev logs— View logs from a background dev server. Use--follow(-f) to stream new output as it's written.
These allow you to start and manage dev servers programmatically and were designed with AI coding agents in mind.
What should I do?
No action is required. If you are not using an AI coding agent,
astro devbehaves exactly as before. If you are using an agent, background mode is enabled automatically — the agent will receive the server URL and PID, and can useastro dev stopto shut it down.To opt out of automatic background mode when an agent is detected, set the environment variable
ASTRO_DEV_BACKGROUND=0before runningastro dev. -
#17010
0606073Thanks @ocavue! - Removes the@astrojs/dbpackage as it is no longer maintained.The
@astrojs/dbpackage were deprecated in v6.4.5 and is now removed. This means theastro db,astro login,astro logout,astro link, andastro initCLI commands have also been removed.If you were using Astro DB in your project, remove
@astrojs/dbfrom your project's dependencies and replace it with one of the following alternatives:- Node.js built-in SQLite: Node.js now includes a built-in
node:sqlitemodule (available since Node.js v22.5.0). This is a good option if you are using the Node.js adapter and were using@astrojs/dbfor local SQLite storage. - Drizzle ORM: If you were using
@astrojs/dbfor its Drizzle-based schema and query API, you can use Drizzle directly with any supported database. - Other database libraries: Use any database library that suits your deployment platform (e.g. Turso, PlanetScale, Neon).
- Node.js built-in SQLite: Node.js now includes a built-in
-
#16462
c30a778Thanks @Princesseuh! - Replaces the Go compiler with a Rust-based version.The Rust-based Astro compiler (
@astrojs/compiler-rs) is now the default compiler. This new compiler is faster and more reliable, leading to faster build times and iteration cycles during development.This new compiler is more strict regarding invalid syntax. For example, unclosed HTML tags will now throw an error instead of being ignored. It also does not attempt to correct semantically invalid HTML anymore, instead leaving it to the browser to handle, similar to other tools or
document.write()in JavaScript.The previous Go-based compiler has been removed, along with the
experimental.rustCompilerflag used to opt into the Rust compiler. If you were settingexperimental.rustCompilerin yourastro.config.mjs, you can now remove it. No other action is required. -
#16966
6650ec2Thanks @Princesseuh! - Makes Sätteri the default Markdown processorAstro now renders
.mdfiles withsatteri()from@astrojs/markdown-satteri, its native Markdown pipeline, instead of the remark/rehype pipeline.@astrojs/markdown-remarkis no longer installed by default.To keep using the remark/rehype pipeline, install
@astrojs/markdown-remarkand set it as your processor:// astro.config.mjs import { defineConfig } from 'astro/config'; import { unified } from '@astrojs/markdown-remark'; export default defineConfig({ markdown: { processor: unified(), }, });
The deprecated
markdown.remarkPlugins,markdown.rehypePlugins, andmarkdown.remarkRehypeoptions still work, but now require@astrojs/markdown-remarkto be used. -
#16877
3b7d76eThanks @matthewp! - Enables advanced routing by default.The advanced routing feature introduced behind a flag in v6.3.0 is no longer experimental and is now enabled by default.
This gives full control over how requests flow through your application, with first-class support for frameworks like Hono.
Advanced routing now uses
src/fetch.tsas default entrypoint instead ofsrc/app.ts.If you were previously using this feature without a custom entrypoint, please configure
fetchFileor rename your entrypoint tosrc/fetch.ts, and then remove the experimental flag from your Astro config:import { defineConfig } from 'astro/config'; export default defineConfig({ experimental { - advancedRouting: true, }, + fetchFile: 'app.ts' // optional, you only need this if you cannot rename your entrypoint. });fetchFileis now a top-level config option instead of being nested underexperimental.advancedRouting. If you were using a custom entrypoint, please update your Astro config to move its configuration:// astro.config.mjs export default defineConfig({ - experimental: { - advancedRouting: { - fetchFile: 'my-custom-entrypoint.ts', - }, - }, + fetchFile: 'my-custom-entrypoint.ts', })You can also set
fetchFile: nullto disable the entrypoint if you are usingsrc/fetch.tsfor another purpose, or don’t need advanced routing features.If you have been waiting for stabilization before using advanced routing, you can now do so.
Please see the advanced routing guide in docs for more about this feature.
-
#16725
10229f7Thanks @ArmandPhilippot! - Removes deprecated APIs exported fromastro:transitions.In Astro 6.x, some helpers available in
astro:transitionsandastro:transitions/clientwere deprecated.In Astro 7.0, the following APIs can no longer be used in your project:
TRANSITION_BEFORE_PREPARATIONTRANSITION_AFTER_PREPARATIONTRANSITION_BEFORE_SWAPTRANSITION_AFTER_SWAPTRANSITION_PAGE_LOADisTransitionBeforePreparationEvent()isTransitionBeforeSwapEvent()createAnimationScope()
What should I do?
Remove any occurrence of
createAnimationScope():-import { createAnimationScope } from 'astro:transitions';Replace any occurrence of the other APIs using the lifecycle event names directly:
-import { - TRANSITION_AFTER_SWAP, - isTransitionBeforePreparationEvent, -} from 'astro:transitions/client'; -console.log(isTransitionBeforePreparationEvent(event)); +console.log(event.type === 'astro:before-preparation'); -console.log(TRANSITION_AFTER_SWAP); +console.log('astro:after-swap');
Learn more about all utilities available in the View Transitions Router API Reference.
Minor Changes
-
#16998
57dcc31Thanks @matthewp! - ExposesgetFetchState()fromastro/honoas a public APIThe
getFetchState()function retrieves or lazily creates aFetchStatefrom a Hono context object. This allows third-party packages to build Hono middleware that interacts with Astro's per-request state, giving theastro/honoAPI the same extensibility asastro/fetch.import { Hono } from 'hono'; import { getFetchState, pages } from 'astro/hono'; const app = new Hono(); app.use(async (context, next) => { const state = getFetchState(context); state.locals.message = 'Hello from custom middleware'; await next(); }); app.use(pages()); export default app;
-
#16996
300641eThanks @florian-lefebvre! - Adds asubsetfield to theFontDatatype exposed viafontDatafromastro:assets. When using multiple font subsets (e.g.,subsets: ["latin", "korean"]), each font data entry now includes the subset name, making it possible to distinguish between font entries for different subsets that share the same weight and style. -
#16745
f864a80Thanks @ematipico! - The custom logger feature introduced behind a flag in v6.2.0 is no longer experimental and is available for general use.This feature provides better control over Astro's logging infrastructure by allowing you to replace the default console output with custom logging implementations (e.g., structured JSON). This is particularly useful for on-demand rendering when connecting to log aggregation services such as Kibana, Logstash, CloudWatch, Grafana, or Loki.
Astro provides three built-in log handlers (
json,node, andconsole), and you can also create your own.JSON logging
import { defineConfig, logHandlers } from 'astro/config'; export default defineConfig({ logger: logHandlers.json({ pretty: true, level: 'warn', }), });
Custom logger
import { defineConfig } from 'astro/config'; export default defineConfig({ logger: { entrypoint: '@org/custom-logger', }, });
Additionally,
context.loggeris now always available in API routes and middleware, even without a custom logger configured.If you were previously using this feature, please remove the experimental flag from your Astro config:
import { defineConfig } from 'astro/config'; export default defineConfig({ - experimental: { - logger: { - entrypoint: '@org/custom-logger', - }, - }, + logger: { + entrypoint: '@org/custom-logger', + }, });If you have been waiting for stabilization before using custom loggers, you can now do so.
Please see the Logger docs for more about this feature.
-
#16981
0d6d644Thanks @ematipico! - Removes the settingexperimental.queuedRendering. The new rendering engine is now stable and replaces the old one.As part of the stabilization, the queued rendering has been improved, and some features have been removed:
- The construction of the queue has been removed, instead now Astro uses a streaming approach where components are rendered and flushed as they are encountered.
- The node polling feature has been removed because it doesn't yield concrete savings.
- The content cache has been descoped, and how only tag names are cached.
If you were previously using this experimental feature, you must remove this experimental flag from your configuration as it no longer exists:
// astro.config.mjs import { defineConfig } from "astro/config"; export default defineConfig({ experimental: { - queuedRendering: {} } }); -
#17116
f95e58eThanks @ascorbic! - Stabilizes route caching, removing theexperimental.cacheandexperimental.routeRulesflags and replacing them with the top-levelcacheandrouteRulesconfiguration options.Route caching, introduced experimentally in v6.0.0, is now stable. It gives you a platform-agnostic way to cache responses from on-demand rendered pages and endpoints, based on standard HTTP caching semantics.
Update your config to move
cacheandrouteRulesout of theexperimentalblock:// astro.config.mjs import { defineConfig, memoryCache } from 'astro/config'; export default defineConfig({ - experimental: { - cache: { - provider: memoryCache(), - }, - routeRules: { - '/blog/[...path]': { maxAge: 300, swr: 60 }, - }, - }, + cache: { + provider: memoryCache(), + }, + routeRules: { + '/blog/[...path]': { maxAge: 300, swr: 60 }, + }, });Set caching directives in your routes with
Astro.cache(in.astropages) orcontext.cache(in API routes and middleware), and Astro translates them into the appropriate headers or runtime behavior depending on your configured cache provider. You can also define cache rules for routes declaratively in your config usingrouteRules, without modifying route code.See the route caching guide for more information.
Patch Changes
-
#16980
1f07343Thanks @matthewp! - Removesstate.provide(),state.resolve(),state.finalizeAll(), andApp.Providersfrom the public advanced routing API. These context provider extension points are now internal-only. If you were using them in an integration, uselocalsto share per-request state instead. -
#17111
c0f33edThanks @ematipico! - Harden the limits on the number of decoding on the URL. -
#16982
1e000e2Thanks @matthewp! - Improves the warning when accessingAstro.sessionwithout session storage configured. Thesessionproperty is now always defined on the context object, and accessing it without configuration logs a helpful message instead of silently returningundefined. -
#16335
9a53f77Thanks @ascorbic! - Adds shared helper utilities for CDN cache provider authors for route cachingExports
astro/cache/provider-utilswith helpers for building platform-specific cache-control headers, generating path-based invalidation tags, and normalizing invalidation options. These are used internally by the first-party Netlify, Vercel, and Cloudflare cache providers. -
#17095
e84ebc0Thanks @matthewp! - Improves build performance by removing an unfiltered transform hook from theastro:head-metadata-buildplugin. Head propagation modules are now identified by their module ID (?astroPropagatedAssets) instead of scanning every module's source code. -
#17041
4c4a91cThanks @iseraph-dev! - Fixes a bug where the advanced routingastro/hono/astro/fetchpages()handler returned the host framework's defaultInternal Server Errorresponse instead of rendering the custom500.astropage when a page threw during render. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way. -
#17097
5e340d7Thanks @iseraph-dev! - Fixes a bug where the advanced routingastro/hono/astro/fetchmiddleware()handler returned the host framework's defaultInternal Server Errorresponse instead of rendering the custom500.astropage when middleware threw. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way. Errors surfaced throughnext(the host framework's downstream chain) still propagate to the host's own error handler. -
#15819
cafec4eThanks @delucis! - Fixes--portflag being ignored after a Vite-triggered server restart (e.g. when a.envfile changes) -
#17104
b074a37Thanks @iseraph-dev! - Fixes the custom500.astropage receiving an emptyerrorprop when the error originated in middleware. -
#17078
04547ecThanks @astrobot-houston! - Fixes a spuriousAstro.request.headerswarning on prerendered pages whensecurity.allowedDomainsis configured. The internalallowedDomainsheader validation now skips prerendered routes, since they use synthetic requests with no real headers. -
#16603
deaaf3fThanks @alexanderniebuhr! - Removes the warning that Astro does not support vite v8, since Astro v7 does support vite v8 -
#16335
9a53f77Thanks @ascorbic! - Passes theRequestobject toCacheProvider.setHeaders()for route cachingCache providers now receive the incoming
Requestas a second argument tosetHeaders(options, request). This allows CDN providers to read the request URL, headers, and other properties when generating cache response headers, for example to auto-tag responses with their pathname for path-based invalidation. -
#17098
637a1b6Thanks @matthewp! - Fixes internal Astro headers leaking from directpages()handler responses -
#17090
3cf76c0Thanks @matthewp! - Fixes Vite and Rolldown build warnings -
#16434
ee079d4Thanks @ematipico! - Fixes an issue where i18n domains would return 404 whentrailingSlashis set tonever. -
Updated dependencies [
7e7ab87,ff7b718,241250b]:- @astrojs/markdown-satteri@0.3.1
@astrojs/alpinejs@1.0.0
Major Changes
Patch Changes
@astrojs/cloudflare@14.0.0
Major Changes
Minor Changes
-
#16335
9a53f77Thanks @ascorbic! - Adds an opt-in CDN cache provider for Astro route caching on Cloudflare Workers[!WARNING]
This provider requires the Cloudflare Workers Cache feature, which is currently in private beta. It is opt-in: nothing changes unless you importcacheCloudflare()and set it as your provider. But without beta access it does not work and should not be used. Cloudflare Workers run in front of the cache, so cached responses are never served, and callingcache.invalidate()throws an error.Setup
Import
cacheCloudflare()from@astrojs/cloudflare/cacheand set it as your cache provider:import { defineConfig } from 'astro/config'; import cloudflare from '@astrojs/cloudflare'; import { cacheCloudflare } from '@astrojs/cloudflare/cache'; export default defineConfig({ adapter: cloudflare(), cache: { provider: cacheCloudflare(), }, });
The adapter automatically enables the Worker caching layer when a Cloudflare cache provider is configured. No manual wrangler.jsonc changes are needed.
Caching responses
Use
Astro.cache.set()in your pages and API routes to cache responses. The provider setsCloudflare-CDN-Cache-ControlandCache-Tagheaders, which are read by Cloudflare's built-in caching layer. Cache hits bypass Worker execution entirely, meaning your Worker is not invoked for cached responses.--- Astro.cache.set({ maxAge: 300, tags: ['products'] }); const data = await fetchProducts(); --- <ProductList items={data} />
You can also set cache rules for groups of routes in your config:
cache: { provider: cacheCloudflare() }, routeRules: { '/products/[...slug]': { maxAge: 3600, tags: ['products'] }, '/api/[...path]': { maxAge: 60, swr: 600 }, },
Invalidation
Purge cached responses by tag or path from any API route or server endpoint:
// src/pages/api/purge.ts export async function POST({ request, cache }) { await cache.invalidate({ tags: ['products'] }); return new Response('Purged'); } // Path-based invalidation (implemented via an auto-generated path tag) await cache.invalidate({ path: '/products/123' });
Both tag-based and path-based invalidation are supported.
Patch Changes
-
#16961
96398e8Thanks @adamchal! - Speeds upastro syncby no longer starting the Cloudflare runtime during type generation -
#16671
fd926fdThanks @alexanderniebuhr! - Removes deprecations warnings added in Astro v6 for Cloudflare specific Astro.locals properties. -
#17027
241250bThanks @ocavue! - Triggers beta prereleases for packages that are still on alpha -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/markdoc@2.0.0
Major Changes
Patch Changes
@astrojs/mdx@7.0.0
Major Changes
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning. -
#17129
ff7b718Thanks @Princesseuh! - Adds support for modifying frontmatter programmatically with the default Markdown processor.A Sätteri plugin can now read and mutate
ctx.data.astro.frontmatter, and Astro uses the result as the page's frontmatter, in both Markdown and MDX.
Patch Changes
-
#17124
7e7ab87Thanks @Princesseuh! - Updatessatterito0.9.0. See the Sätteri changelog for details. -
#17027
241250bThanks @ocavue! - Triggers beta prereleases for packages that are still on alpha
@astrojs/netlify@8.0.0
Major Changes
Minor Changes
-
#16335
9a53f77Thanks @ascorbic! - Adds a CDN cache provider for Astro route caching on NetlifySetup
Import
cacheNetlify()from@astrojs/netlify/cacheand set it as your cache provider:import { defineConfig } from 'astro/config'; import netlify from '@astrojs/netlify'; import { cacheNetlify } from '@astrojs/netlify/cache'; export default defineConfig({ adapter: netlify(), cache: { provider: cacheNetlify(), }, });
Caching responses
Use
Astro.cache.set()in your pages and API routes to cache responses on Netlify's edge network. The provider uses Netlify's durable cache so cached responses are shared across all edge nodes, reducing function invocations.--- Astro.cache.set({ maxAge: 300, tags: ['products'] }); const data = await fetchProducts(); --- <ProductList items={data} />
You can also set cache rules for groups of routes in your config:
cache: { provider: cacheNetlify() }, routeRules: { '/products/[...slug]': { maxAge: 3600, tags: ['products'] }, '/api/[...path]': { maxAge: 60, swr: 600 }, },
Invalidation
Purge cached responses by tag or path from any API route or server endpoint:
// src/pages/api/purge.ts export async function POST({ request, cache }) { await cache.invalidate({ tags: ['products'] }); return new Response('Purged'); } // Path-based invalidation await cache.invalidate({ path: '/products/123' });
Both tag-based and path-based invalidation are supported.
Patch Changes
-
#17027
241250bThanks @ocavue! - Triggers beta prereleases for packages that are still on alpha -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/preact@6.0.0
Major Changes
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
Patch Changes
@astrojs/react@6.0.0
Major Changes
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
Patch Changes
@astrojs/solid-js@7.0.0
Major Changes
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
Patch Changes
@astrojs/svelte@9.0.0
Major Changes
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning. -
#16549
9d9d516Thanks @ocavue! - Updates@sveltejs/vite-plugin-svelteto v7. No user action is necessary.
Patch Changes
@astrojs/vercel@11.0.0
Major Changes
Minor Changes
-
#16335
9a53f77Thanks @ascorbic! - Adds a CDN cache provider for Astro route caching on VercelSetup
Import
cacheVercel()from@astrojs/vercel/cacheand set it as your cache provider:import { defineConfig } from 'astro/config'; import vercel from '@astrojs/vercel'; import { cacheVercel } from '@astrojs/vercel/cache'; export default defineConfig({ adapter: vercel(), cache: { provider: cacheVercel(), }, });
Caching responses
Use
Astro.cache.set()in your pages and API routes to cache responses on Vercel's edge network. The provider setsVercel-CDN-Cache-ControlandVercel-Cache-Tagheaders on responses.--- Astro.cache.set({ maxAge: 300, tags: ['products'] }); const data = await fetchProducts(); --- <ProductList items={data} />
You can also set cache rules for groups of routes in your config:
cache: { provider: cacheVercel() }, routeRules: { '/products/[...slug]': { maxAge: 3600, tags: ['products'] }, '/api/[...path]': { maxAge: 60, swr: 600 }, },
Invalidation
Purge cached responses by tag or path from any API route or server endpoint:
// src/pages/api/purge.ts export async function POST({ request, cache }) { await cache.invalidate({ tags: ['products'] }); return new Response('Purged'); } // Path-based invalidation await cache.invalidate({ path: '/products/123' });
Both tag-based and path-based invalidation are supported. Tag invalidation is a soft invalidation, marking cached responses as stale so they can be revalidated in the background via stale-while-revalidate.
Patch Changes
@astrojs/vue@7.0.0
Major Changes
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
Patch Changes
create-astro@5.1.0
Minor Changes
- #17122
cbd6123Thanks @matthewp! - Adds a defaultAGENTS.mdfile to new projects with dev server instructions and documentation links. Also creates aCLAUDE.mdsymlink (with hard link fallback) pointing toAGENTS.md.
@astrojs/node@11.0.0
Patch Changes
-
#17054
d426b67Thanks @astrobot-houston! - Fixes an issue where Astro files with non-ASCII characters in their name weren't correctly served after the build. -
#17027
241250bThanks @ocavue! - Triggers beta prereleases for packages that are still on alpha
@astrojs/markdown-satteri@0.3.1
Patch Changes
-
#17124
7e7ab87Thanks @Princesseuh! - Updatessatterito0.9.0. See the Sätteri changelog for details. -
#17129
ff7b718Thanks @Princesseuh! - Adds support for modifying frontmatter programmatically with the default Markdown processor.A Sätteri plugin can now read and mutate
ctx.data.astro.frontmatter, and Astro uses the result as the page's frontmatter, in both Markdown and MDX. -
#17027
241250bThanks @ocavue! - Triggers beta prereleases for packages that are still on alpha
Changes
Reworks the way metadata flows through Sätteri so that plugins can modify it, like you can on remark. This makes the code very similar to remark's and unlocks the ability for users to modify the frontmatter.
Testing
Added tests
Docs
Will need some updates, but nothing is technically untrue if this is merged, so could be done separately.
Changes
scripts/turbo-run-affected.jsappended the changed-file range[origin/<base>...HEAD]to every--filter, including exclusions like!./packages/language-tools/**/*. That scoped the exclusion to packages that themselves changed (and skipped the{…}directory wrapping it applies to normal directory filters), so the exclusion stopped applying.- As a result, an excluded package was still selected when affected by an upstream change:
@astrojs/ts-plugin(affected wheneverastrochanges) got pulled into thetest:integrationssuite, which has noxvfb, so its VS Code test segfaults. It normally hides behind a turbo cache hit and only surfaces on a cold cache (e.g. a PR that touchespnpm-lock.yaml). - Fix: pass exclusion filters (
!…) through unchanged so they stay absolute. The ts-plugin/VS Code tests then run only in the dedicatedtest-language-toolsjob (which sets upxvfband already marks them non-failing). - No changeset: this only touches CI scripting, with no changes to any published package.
Testing
Verified with turbo run test … --dry=json (planning only, no tests executed), using the stabilise-cache range — a real case where astro changed but no language-tools package did, which is what triggers the bug:
- Inclusion only —
--filter="@astrojs/*[origin/main...origin/stabilise-cache]"selects@astrojs/ts-pluginand@astrojs/language-server(affected via their dependency onastro). - Old behavior — adding the range-scoped exclusion
--filter="!./packages/language-tools/**/*[origin/main...origin/stabilise-cache]"(what the script produced before): ts-plugin and language-server are still selected. ❌ - Fixed behavior — adding the absolute exclusion
--filter="!./packages/language-tools/**/*"(what the script produces now): ts-plugin and language-server are excluded. ✅
So test:integrations no longer pulls in language-tools packages, and the ts-plugin/VS Code tests run only in the dedicated test-language-tools job.
Docs
No docs needed: this is internal CI tooling with no user-facing behavior change.
Changes
Just a bump to latest, fixes a few issues and improves performance with plugins.
Testing
Should pass
Docs
N/A
The client router didn't copy server islands marker comments during head swap.
Now it does.
Closes #17105
Server islands within the <head> might very well not match their initial intention.
Nevertheless, I decided for this fix as it was quicker and less disruptive than documenting and checking that server:defer isn't used inside the <head>. Also, I saw no technical reason to prevent this and maybe the abiliability to comes in handy some day ;-)
Testing
New e2e test
Docs
n.a. / bug fix.
Changes
create-astronow generates anAGENTS.mdin new projects with dev server background mode instructions and links to commonly needed Astro docs.- Creates a
CLAUDE.mdsymlink pointing toAGENTS.md, with a hard link fallback for Windows environments without Developer Mode.
Testing
- Added
generateAgentsMdtests verifying the background dev command and documentation links are present.
Docs
- No docs update needed. This is a scaffolding change that generates a file for AI coding agents, not a user-facing API.
Changes
- Adds new config
serverIslandHostname - Updates manifests (serialized and plugin) to use the new config value
- Refactors existing logic for prepending
config.basesuch that there won't be duplicated slashes
Testing
Tested using a local project that will make use of the feature.
Smoke tests:
- Run
astro dev
-- with new config set -- observe that the config value is included at the beginning of the server island URL
-- with the new config unset -- observe that the config value is unchanged - Run
astro build
-- with new config set -- observe that generated static code includes new config value at the beginning of the server island URL
-- with the new config unset -- observe that the config value is unchanged
Ran unit tests for packages/astro
Docs
This could affect the user's behavior if a user sets this value and isn't aware that they need to deploy the server side application to wherever this new hostname is proxying.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
main is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on main.
Releases
astro@7.0.0-beta.6
Minor Changes
-
#17116
f95e58eThanks @ascorbic! - Stabilizes route caching, removing theexperimental.cacheandexperimental.routeRulesflags and replacing them with the top-levelcacheandrouteRulesconfiguration options.Route caching, introduced experimentally in v6.0.0, is now stable. It gives you a platform-agnostic way to cache responses from on-demand rendered pages and endpoints, based on standard HTTP caching semantics.
Update your config to move
cacheandrouteRulesout of theexperimentalblock:// astro.config.mjs import { defineConfig, memoryCache } from 'astro/config'; export default defineConfig({ - experimental: { - cache: { - provider: memoryCache(), - }, - routeRules: { - '/blog/[...path]': { maxAge: 300, swr: 60 }, - }, - }, + cache: { + provider: memoryCache(), + }, + routeRules: { + '/blog/[...path]': { maxAge: 300, swr: 60 }, + }, });Set caching directives in your routes with
Astro.cache(in.astropages) orcontext.cache(in API routes and middleware), and Astro translates them into the appropriate headers or runtime behavior depending on your configured cache provider. You can also define cache rules for routes declaratively in your config usingrouteRules, without modifying route code.See the route caching guide for more information.
Patch Changes
-
#17090
3cf76c0Thanks @matthewp! - Fixes Vite and Rolldown build warnings -
Updated dependencies [
7e7ab87]:- @astrojs/markdown-satteri@0.3.1-beta.2
create-astro@5.1.0-beta.0
Minor Changes
- #17122
cbd6123Thanks @matthewp! - Adds a defaultAGENTS.mdfile to new projects with dev server instructions and documentation links. Also creates aCLAUDE.mdsymlink (with hard link fallback) pointing toAGENTS.md.
@astrojs/cloudflare@14.0.0-beta.3
Patch Changes
-
#16961
96398e8Thanks @adamchal! - Speeds upastro syncby no longer starting the Cloudflare runtime during type generation -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/mdx@7.0.0-beta.4
Patch Changes
- #17124
7e7ab87Thanks @Princesseuh! - Updatessatterito0.9.0. See the Sätteri changelog for details.
@astrojs/markdown-satteri@0.3.1-beta.2
Patch Changes
- #17124
7e7ab87Thanks @Princesseuh! - Updatessatterito0.9.0. See the Sätteri changelog for details.
Follow-up to #3923, this PR cleans up various parts regarding aside and heading link icons:
- The Unified/Sätteri aside plugins now uses the same Starlight icons as the existing ones already rendered by the
<Aside>component - The Unified/Sätteri heading link plugins and
<AnchorHeading>component now uses a new common Starlight icon (link-altto match the Unicons naming) - As it felt weird to only add new
link-alticon, I also added the matchinglinkicon
The diff also shows how much more minified the aside component icons are compared to our previous Markdown plugin ones. I wonder if we forgot to minify them or just used a different preset 🤷
Not quite sure if a changeset for the deduping part of the PR should be added. Visually, nothing changed, and it's mostly SVG path changes. And if yes, not quite sure how to phrase it as my attempts so far kinda hinted wrong icons were rendered which is not the case 😅 Finally managed to write something I'm happy with.
Changes
Stabilises the route caching feature. Moves the cache and routeRules configuration from experimental to top-level config.
It is not enabled by default, and needs a cache provider to be configured. This will likely be enabled for adapters once #16335 lands.
Testing
Updated tests
Docs
Draft docs: withastro/docs#13977
Changes
I moved the code for issue triage over to https://github.com/withastro/triagebot-action so that:
- Easier to test in isolation, don't have to "merge and pray" like we do now when making changes.
- Potentially use triage in other projects, although no plans to add it yet.
Additionally I took the opportunity to make it into a full state machine, there are no more unknown states and the triage bot can recover from, for example, the reporter saying that the issue isn't fixed by the pending fix.
Also standardized the label names for consistency with triage: {name} format.
This PR removes the Flue code which now lives there, and uses the action.
Testing
- The action has its own unit tests (27 passing) covering the FSM router and label management.
Docs
- No docs update needed. This is an internal CI automation change.
Changes
This PR cherry-pick the changes we just shipped in Astro v6.4.8
Testing
Green CI
Docs
N/A
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to 6-legacy, this PR will be updated.
Releases
astro@6.4.8
Patch Changes
- #17109
27c80eaThanks @ematipico! - Harden the limits on the number of decoding on the URL.
Changes
Improves the hardening of decoded URLs
Testing
Added new tests
Docs
N/A
Changes
- When a non-root
baseis set, the Cloudflare adapter now relocatespublic/_redirectsto the assets root (dist/client/_redirects) alongside.assetsignoreand_headers. Previously, the file was left atdist/client/<base>/_redirectswhere Cloudflare Workers Assets can't find it, silently dropping all user-defined redirects in production. - Because relocation happens before the auto-generated-redirects step, user-defined rules land at the root first and any route-derived redirects are correctly appended afterward.
Testing
- Added two test cases to
with-base.test.ts: one verifying_redirectsexists at the client root (not nested under the base path), and one verifying user-defined redirect content frompublic/_redirectsis preserved after build. - Added
public/_redirectsto thewith-basefixture.
Docs
- No docs update needed; this is a bug fix restoring behavior consistent with the default
base: '/'case.
Closes #17101
Changes
- Updates the fix-verification flue workflow model from
claude-sonnet-4-20250801(which does not exist) toclaude-sonnet-4-6(Claude Sonnet 4.6, the current production Sonnet). The previous modelclaude-sonnet-4-20250514was deprecated, and #17100 attempted to update it but used a non-existent model ID.
Testing
- No test changes. The fix-verification workflow runs in CI via the Fix Verify GitHub Action — a successful run confirms the model resolves correctly.
Docs
- No docs needed; internal CI workflow change only.
Changes
Follow-up to #17041 and #17097. The #17097 description flagged one item as out of scope: on the composable astro/hono path the custom 500.astro rendered with an empty error prop, so Astro.props.error was undefined and the page could not show what threw. It turns out this is not specific to astro/hono, and not a middleware-handler issue. It lives in the production error handler and affects the standard adapter as well.
When middleware throws, the path's catch calls app.renderError(..., { error }). DefaultErrorHandler.renderError then renders the matching 500.astro by running the middleware chain again (so middleware can still set headers, locals, etc. on the error page). The error state reuses the original request, so the same user middleware runs again and throws again. The handler catches that and retries with skipMiddleware: true ("middleware may be the cause of the error"), but the retry object dropped the original error. It fell out of the spread and reached the error page as undefined.
The result: any path that rendered a custom 500 for a middleware-thrown error lost Astro.props.error (the standard @astrojs/node adapter, the all-in-one astro() handler, and the composable middleware()). Page-render errors via pages() were unaffected, because the middleware does not re-throw on the error render, so the first attempt succeeds with the error intact. The dev error handler was also unaffected; it already forwards an error on its retry.
The fix carries the original error through the skip-middleware retry (one line in packages/astro/src/core/errors/default-handler.ts). BuildErrorHandler delegates to DefaultErrorHandler, so prerendered error pages pick up the same fix.
Testing
- Added a unit test in
test/units/fetch/index.test.ts(themiddleware()block): user middleware throws and the custom/500page echoesAstro.props.error, asserting the thrown message reaches the page. It fails before the change (the error page renders blank) and passes after. The full unit suite stays green (2942 passing, 3 skipped). - Verified end to end against the #17092 reproduction (Hono,
app.use(middleware())+app.use(pages()), node standalone, a500.astrothat printsAstro.props.error):GET /boom(middleware throws) now renders500.astrowith the messageboom from middleware, previously blank.GET /page-throwsstill renders500.astrowith its message (no regression).GET /does-not-existstill renders the custom404, andGET /still returns200.
Notes
- The retry passes the original error (the cause of the 500), which is what every non-retry path already passes. The dev handler instead forwards the re-thrown error on its retry; aligning the two is cosmetic and left out of this change.
- The deliberate "no error to report" reroute paths (the status-code 404/500 reroutes that pass
error: null/undefined) are unchanged.
Changes
- The LLM returns literal
\n(backslash + n) instead of actual newlines when producing a string via tool-call results. Unescapes them before posting to GitHub so comments render with real line breaks.
Testing
- No test changes. The
.flue/workflow code has no test harness; verified by inspecting the hex dump of the affected comment (bytes5c 6einstead of0a).
Docs
- No docs needed — internal bot infrastructure change.
Changes
- Updates the fix-verification flue workflow model from
claude-sonnet-4-20250514(EOL) toclaude-sonnet-4-20250801. The old model is no longer available, causing verification runs to fail.
Testing
- No test changes — config-only update.
Docs
- No docs needed — internal CI workflow change.
Changes
- Keeps a user-configured
image.servicewhen the Cloudflare adapter is usingimageService: 'compile', so customgetURL()andgetHTMLAttributes()hooks are used while prerendering markup in workerd. - Loads the configured custom service during the Node-side image generation pass, so custom
transform()hooks are used for generated image assets. - Extends
imageService: 'custom'to also generate optimized image assets at build time, mirroringcompile. It runs the configuredimage.service(or Astro's default Sharp service when none is set) during the Node-side generation pass, while continuing to use the configured service for runtime image handling.
imageService |
user image.service |
Before | After |
|---|---|---|---|
'compile' |
none (default Sharp) | Workerd-safe service for prerendered markup; Sharp on the Node side for generation; Sharp-free worker bundle. | Unchanged. |
'compile' |
custom | Replaced the custom service with the workerd-safe service, so custom markup and transform() hooks were skipped. |
Preserves the custom service for prerendered markup and runs its transform() hook during image generation. |
'custom' |
none (default Sharp) | No build-time asset generation; the default Sharp service is bundled into the worker (where it cannot run). | Generates assets with Sharp on the Node side. Runtime bundle unchanged — the default Sharp service is still bundled (and remains Workers-incompatible), per the documented custom tradeoff. |
'custom' |
custom | No build-time asset generation; the custom service was used only at runtime. | Generates assets with the custom service at build time and continues to use it for runtime image handling. |
'cloudflare' | 'cloudflare-binding' | 'passthrough' |
— | Existing adapter behavior for those modes. | Unchanged. |
The
compilerows also cover the compound formimageService: { build: 'compile', runtime: ... }.
Testing
- Reworks the Cloudflare compile image service test coverage into a build-time generation matrix that exercises both
imageService: 'compile'andimageService: 'custom'across three service configurations: no user service (Astro's default Sharp service), a Sharp-free custom service, and a Sharp-backed custom service. Each cell asserts:- hashed
_astro/*.webpassets are generated at build time (real WEBP bytes, or the customtransform()marker for the Sharp-free user service); - custom
getHTMLAttributes()markup is preserved; - the worker bundle deps match each mode's contract:
compilestays Sharp-free (Sharp runs only on the Node side);customreflects its configured runtime service — clean for a Sharp-free user service, but dragging Sharp in for both a Sharp-backed user service and the no-image.servicedefault.
- hashed
Docs
- The Cloudflare adapter guide could be updated to mention that the
'compile'mode “uses image.service, if defined otherwise a combination of internal dependencies to transform images locally at build time for prerendered routes.” And it could also mention that'custom'will do the same thing during build time. But, it may not be necessary—I feel like this is what I expected to be the case. Happy to draft awithastro/docsPR. - Draft docs PR withastro/docs#14095
Related PRs
- This is related to #16194, which adds an opt-in compound
{ build: 'cloudflare-binding' }mode so the Cloudflare Images binding transforms prerendered images during the build (writing optimized bytes directly, falling back to Sharp); this PR keeps local image-service generation paths working with custom image services and brings build-time generation tocustommode. The behaviors should compose:compileandcustomrespect custom local image services, while the opt-incloudflare-bindingbuild mode uses the Images binding.
Closes #16201
Changes
- Moves internal route/reroute metadata from response headers onto
FetchState, preventing directpages()responses from leakingx-astro-route-typeorx-astro-reroute. - Updates routing, i18n, endpoint, and no-op middleware handling to use state fields instead of internal response headers.
Testing
- Updated fetch unit tests to assert direct
pages()responses do not expose Astro internal headers. - Verified fetch, i18n, routing, and endpoint tests.
Docs
- No docs needed — internal metadata handling with no user-facing API changes.
Changes
On the composable astro/hono path, middleware() delegated straight to AstroMiddleware.handle(), with none of the app-level error handling AstroHandler provides on the standard path. A throw in the user's own src/middleware.ts therefore reached the host framework's default error handler (Hono: plain-text Internal Server Error) instead of rendering the custom 500.astro. The all-in-one astro() handler and the standard adapter path both render 500.astro for the same throw, so the gap was specific to composing middleware() on its own. This is the middleware counterpart of #16952 / #17041, which fixed the pages() side and flagged this as out of scope (#17092).
- Added
AstroMiddleware.handleWithErrorFallback(app, state, renderRouteCallback)(core/middleware/astro-middleware.ts): wraps the middleware chain in a try/catch that logs the error and renders it viaapp.renderError(..., { status: 500, error }), the same behaviourAstroHandler.render()has on the standard path andPagesHandler.handleWithErrorFallback()got in #17041. - #17041 noted the catch couldn't simply live here, because
middleware()'snextis the host'snext(): a plain try/catch would also swallow errors thrown by host middleware running below it in the chain and hide them from the user's ownapp.onError. A sentinel handles that. Errors surfaced through the route-dispatch callback (the host'snext, i.e. middleware mounted belowmiddleware()) are tracked and re-thrown, so only errors from Astro's own middleware become500.astrowhile host errors still reachapp.onError. - Added the same unmatched-route guard
pages()got in #17041. When no route matched (the custom 404 page is prerendered or absent, sorouteDatais unset), it returns a 404 marked withX-Astro-Errorfor the app's post-check instead of running middleware against a missing route. Without the guard,AstroMiddleware.handle()callsgetProps()on the missing route, throws aTypeError, and the 500 catch above turns the unmatched request into a500. This matchesAstroHandler, which 404s before running middleware on the standard path. - Switched
middleware()incore/fetch/index.tsto delegate to the new method (delegation only, no logic added - that file keeps its exports as thin wrappers). - Added a changeset (
astropatch).
The triage on #17092 explored the same sentinel approach.
Testing
- Added
'renders the custom 500 page when user middleware throws'- verifies a throw in user middleware returns HTTP 500 with the custom 500 page content. - Added
're-throws errors from the next callback instead of rendering the 500 page'- verifies an error surfaced throughnextpropagates to the caller (so the host's error handling still runs) instead of being rendered as500.astro. - Added
'returns a marked 404 without running middleware when the custom 404 route is prerendered'- verifies an unmatched request returns HTTP 404 with theX-Astro-Errormarker and that user middleware andnextare not run. - Verified end-to-end against the reproduction from #17092 (updated to cover both gaps; Hono,
app.use(middleware())+app.use(pages()), node standalone), and confirmed the composable path now matches the all-in-oneastro()on every case:GET /boom(throws insrc/middleware.ts) ->500with the custom 500 page, previously Hono's plain-textInternal Server Error.GET /page-throws->500with the custom 500 page (the #17041 path, still working).GET /->200.- With a host (Hono) middleware mounted below
middleware()that throws, the request still reachesapp.onErrorrather than500.astro. GET /does-not-exist(unmatched) with a prerendered custom 404 ->404rendering the custom 404 page, previously500; with no custom 404 -> the default404page.
Out of scope (noticed while testing)
On the middleware path the rendered 500.astro gets an empty error prop, so the page can't show what threw. This looks pre-existing rather than introduced here: the all-in-one astro() handler behaves the same, while page-render errors via pages() do populate error. Better addressed separately.
Docs
- No docs update needed - this restores the documented custom-error-page behaviour on the composable path (the
pages()counterpart was #17041).
Closes #17092
Changes
- Removes the unfiltered
transformhook fromastro:head-metadata-buildthat ran on every module in the build. This plugin was consuming 36% of total plugin time in PLUGIN_TIMINGS reports. Head propagation modules are now identified by checking for the?astroPropagatedAssetsquery param on their module ID ingenerateBundle, instead of scanning every module's source code for the"use astro:head-inject"directive. - Applies the same ID-based check to the dev plugin (
astro:head-metadata) for consistency. - Removes the now-unused
hasHeadPropagationCallfunction and the"use astro:head-inject"directive fromtemplates/content/module.mjs(redundant — the virtual content module already setspropagation: "in-tree"via module metadata).
Testing
- All 24 remaining head propagation unit tests pass (7 hint detection tests removed with the deleted module).
- Head propagation prerender,
astro-head, and CSS order integration tests pass. - Manual verification with
examples/blog: added a component with global CSS to an MDX content entry, confirmed styles appear on the post page and do not bleed onto the blog index. Also confirmed that disabling the new check causes no regression because MDX uses thepropagation: "self"metadata path independently.
Docs
- No docs needed — internal build performance improvement with no user-facing API changes.
Changes
Integration entrypoints are unsafe to import inside the app because they're:
- Not written with bundling in mind
- Can import some build-time only stuff
This PR fixes this by exporting the container renderer definition from a separate entrypoint, deprecating the old one for all integrations.
Testing
Updated tests to import from the new entrypoint
Docs
Changes
- Removes the deprecated
resolve.aliascustomResolveroption from theastro:tsconfig-aliasplugin. CSS@importalias resolution is now handled by a separateenforce: "pre"transform hook that rewrites aliased specifiers to absolute paths before Vite's CSS plugin processes them. The existingresolveIdhook for JS/TS imports is unchanged. - Disables Rolldown's
checks.pluginTimingsin the build config to suppress the[PLUGIN_TIMINGS]warning that fires by default.
Testing
- All 4 existing alias test suites pass (11 tests), including
alias-tsconfig.test.ts"works in css @import" which validates multi-path tsconfig alias resolution in CSS@importstatements.
Docs
- No docs needed — internal warning suppression with no user-facing API changes.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
main is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on main.
Releases
astro@7.0.0-beta.5
Major Changes
-
#16965
57ead0dThanks @Princesseuh! - Makes'jsx'the default value forcompressHTMLAstro now strips whitespace from your HTML using JSX rules by default, the same way frameworks like React do. Whitespace and line breaks around elements are removed, but meaningful whitespace within a single line — like a space between two inline elements — is preserved. To keep a space that would otherwise be removed, write it explicitly in your source, for example with
{" "}.This can change rendered output where whitespace between inline elements was previously meaningful. To keep Astro's earlier behavior, set
compressHTML: truefor HTML-aware compression, orcompressHTML: falseto preserve all whitespace.
Patch Changes
-
#17111
c0f33edThanks @ematipico! - Harden the limits on the number of decoding on the URL. -
#17095
e84ebc0Thanks @matthewp! - Improves build performance by removing an unfiltered transform hook from theastro:head-metadata-buildplugin. Head propagation modules are now identified by their module ID (?astroPropagatedAssets) instead of scanning every module's source code. -
#17041
4c4a91cThanks @iseraph-dev! - Fixes a bug where the advanced routingastro/hono/astro/fetchpages()handler returned the host framework's defaultInternal Server Errorresponse instead of rendering the custom500.astropage when a page threw during render. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way. -
#17097
5e340d7Thanks @iseraph-dev! - Fixes a bug where the advanced routingastro/hono/astro/fetchmiddleware()handler returned the host framework's defaultInternal Server Errorresponse instead of rendering the custom500.astropage when middleware threw. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way. Errors surfaced throughnext(the host framework's downstream chain) still propagate to the host's own error handler. -
#17104
b074a37Thanks @iseraph-dev! - Fixes the custom500.astropage receiving an emptyerrorprop when the error originated in middleware. -
#17098
637a1b6Thanks @matthewp! - Fixes internal Astro headers leaking from directpages()handler responses
@astrojs/mdx@7.0.0-beta.3
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
@astrojs/preact@6.0.0-beta.2
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
@astrojs/react@6.0.0-beta.2
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
@astrojs/solid-js@7.0.0-beta.2
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
@astrojs/svelte@9.0.0-beta.4
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
@astrojs/vue@7.0.0-beta.2
Minor Changes
-
#17093
4585fe5Thanks @Princesseuh! - Replaces the import entrypoint ofgetContainerRenderer()A new
container-rendererentrypoint exportinggetContainerRenderer()has been added to the following integrations: React, Preact, Svelte, SolidJS, Vue, and MDX. This prevents bundlers from trying to bundle unrelated exports from the package root when only the Container API is used.If you are using the Container API, update your import statements to use the new entrypoint. The following example updates the
getContainerRenderer()import for React:- import { getContainerRenderer } from '@astrojs/react'; + import { getContainerRenderer } from '@astrojs/react/container-renderer';
Importing
getContainerRenderer()from the package root still works, but is now deprecated and logs a warning.
Close: #12175
Closes AST-188
Changes
When it defines a locale-specific page like /pt/404.astro,
- Astro resolves it for missing
/pt/*routes, returns the correct 404 status for/pt/404, - keeps normal routes like
/docs/404as normal pages.
Testing
Added focused unit tests for the new localized error route helpers and app routing behavior, then reviewed the generated test cases to make sure they match the existing test style and cover the important cases.
The tests cover:
- selecting localized
/404and/500routes when available - falling back to the global error route when no localized route exists
- returning 404 for
/pt/404 - keeping normal localized pages like
/pt/aboutas 200 - keeping normal routes like
/docs/404as normal pages - fetching prerendered localized 404 pages from the correct output path
Docs
This may need docs because this PR fixes support for localized custom error pages
Description
EDIT: I removed the "fixes (issue number)", as that is a redirect issue and will be addressed separately (#3957).
I was doing a manual setup with an existing project to add Starlight. I first noticed the broken link for integrations, which this PR addresses. I also found I needed to setup Astro first, but there wasn't a link to it in the document.
This PR:
fixes the broken link for the integrations page on the manual setup page- adds a link to the manual setup for Astro in the neighboring content
makes these changes across all translations with a manual-setup.mdx page
I made these changes by hand, but relied on AI to identify the correct text to update for adding the manual setup for Astro link. I checked with translation tools and they look correct, but I cannot say I have 100% confidence the added link encapsulates the correct text perfectly across all languages. I have tested this locally.
This is my first contribution to Astro - let me know if you have any suggestions. I can always remove the added link for Manual Setup of Astro if desired. I realize a discussion may have been warranted for that type of improvement.
Changes
- Updates
.changeset/config.jsonbaseBranchfromorigin/nexttoorigin/main. After the v7 release mergednextintomain,mainis the active development branch. The staleorigin/nextref causespnpm changeset statusto fail in CI workflows like preview releases.
Testing
- No test changes. This is a config-only fix.
Docs
- No docs needed.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
main is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on main.
Releases
astro@7.0.0-beta.4
Major Changes
-
#16966
6650ec2Thanks @Princesseuh! - Makes Sätteri the default Markdown processorAstro now renders
.mdfiles withsatteri()from@astrojs/markdown-satteri, its native Markdown pipeline, instead of the remark/rehype pipeline.@astrojs/markdown-remarkis no longer installed by default.To keep using the remark/rehype pipeline, install
@astrojs/markdown-remarkand set it as your processor:// astro.config.mjs import { defineConfig } from 'astro/config'; import { unified } from '@astrojs/markdown-remark'; export default defineConfig({ markdown: { processor: unified(), }, });
The deprecated
markdown.remarkPlugins,markdown.rehypePlugins, andmarkdown.remarkRehypeoptions still work, but now require@astrojs/markdown-remarkto be used.
Patch Changes
- #17078
04547ecThanks @astrobot-houston! - Fixes a spuriousAstro.request.headerswarning on prerendered pages whensecurity.allowedDomainsis configured. The internalallowedDomainsheader validation now skips prerendered routes, since they use synthetic requests with no real headers.
@astrojs/node@11.0.0-beta.2
Patch Changes
- #17054
d426b67Thanks @astrobot-houston! - Fixes an issue where Astro files with non-ASCII characters in their name weren't correctly served after the build.
Changes
This PR updates our workflows to release form 6-legacy branch
Closes AST-180
Testing
Green CI
Docs
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight-markdoc@0.7.0
Minor Changes
-
#3951
1202dd4Thanks @HiDeoo! -⚠️ BREAKING CHANGE: The minimum supported version of Starlight is now 0.41.0Please use the
@astrojs/upgradecommand to upgrade your project:npx @astrojs/upgrade
@astrojs/starlight@0.41.0
Minor Changes
-
#3951
1202dd4Thanks @HiDeoo! - Adds support for Astro v7, drops support for Astro v6.Upgrade Astro and dependencies
⚠️ BREAKING CHANGE: Astro v6 is no longer supported. Make sure you update Astro and any other official integrations at the same time as updating Starlight:npx @astrojs/upgrade
Community Starlight plugins and Astro integrations may also need to be manually updated to work with Astro v7. If you encounter any issues, please reach out to the plugin or integration author to see if it is a known issue or if an updated version is being worked on.
⚠️ BREAKING CHANGE: This release drops official support for Chromium-based browsers prior to version 111 (released 07 March 2023) and Safari-based browsers prior to version 16.4 (released 27 March 2023). You can find a list of currently supported browsers and their versions using this browserslist query.
Patch Changes
Changes
- Fixes spurious
Astro.request.headerswarning when usingsecurity.allowedDomainswith prerendered pages. The warning was incorrectly triggered by Astro's internal forwarded header resolution, not by user code. - Skips the
#applyForwardedHeaders()call for prerendered routes since forwarded headers are meaningless for synthetic requests with no real headers.
Testing
- Added
packages/astro/test/units/app/prerender-allowed-domains.test.tswith tests verifying no header access warnings for prerendered routes and that header access still works for non-prerendered routes.
Docs
- No docs update needed, this fixes a bug without changing user-facing behavior.
Closes #17077
Description
This PR adds Persian as a new language for the Starlight documentation and includes the initial translated pages.
Changes
-
Added
fato the locales configuration indocs/astro.config.mjs,docs/lunaria.config.json -
Added the Persian language subtag to
.github/labeler.yml -
Added the Persian translation for:
docs/src/content/docs/fa/index.mdxdocs/src/content/docs/fa/404.mdx
Notes
This is the initial step for Persian localization. Additional documentation pages will be translated and submitted in future pull requests.
Changes
Adds logic for astro add cli command to create .gitignore entries for cloudflare and netlify.
See also roadmap discussion
Testing
Tested locally using pnpm pack on an empty astro repo. Also created new test file for the cli add command.
Docs
The logger provides enough of a guide, so additional docs don't seem necessary.
https://github.com/gajus/zod-compiler compiles Zod schemas at build time to 2-75x time faster express
Changes
Fixes component URL resolution when hydrated component entry chunks are merged by Rollup manualChunks.
When a user configures manualChunks() to merge framework component modules, Rollup can emit a small proxy chunk for the component entry that re-exports from the final manual chunk. Astro previously mapped the hydrated component entry to that proxy chunk, causing astro-island component-url to reference the intermediary chunk instead of the final merged chunk.
This change resolves those proxy entry chunks to the directly imported output chunk that contains the original component module, while preserving the existing chunk when it already contains the entry module.
Testing
Added regression coverage for a Vue hydrated component fixture with manualChunks(id) { if (id.includes('.vue')) return 'components'; }.
The test verifies that:
astro-islandcomponent-urlpoints at the emittedcomponents.*.jsmanual chunk- the manual chunk is emitted
- generated HTML does not reference the intermediary
Foo.*.jsproxy chunk
Validation
rtk pnpm -C packages/astro buildrtk pnpm -C packages/integrations/vue exec astro-scripts test "test/app-entrypoint.test.ts" --match "manual chunk"rtk pnpm -C packages/integrations/vue exec astro-scripts test "test/app-entrypoint.test.ts"rtk git diff --check
Fixes #17016
Changes
- Refactored selector to target only elements with an explicit role attribute:
selector:[role]:is(${[...a11y_implicit_semantics.keys()].join(',')},input) - Fixed logic (
if (role === implicitRole) return false;) that was causing validation failures to be silently ignored. - Bypass redundant role warnings for implicit ARIA role exceptions.
if(
(localName === 'ul' && role === 'list') ||
(localName === 'ol' && role === 'list') ||
(localName === 'li' && role === 'listitem'))
{
return false;
}
Testing
- Add e2e tests for a11y-no-redundant-roles
- Added Playwright tests to verify the fixed dev toolbar audit rule under both violating and valid scenarios.
- Verify 8 redundant role violation cases
- Ensured that warnings properly trigger when explicit roles duplicate their implicit ARIA.
- Verify 7 valid exception and override cases
- Confirmed that no false are triggered for legitimate browser-bug workarounds and intentional role overrides.
Before implementation
After implementation
Docs
Changes
- Removes the unconditional
redirectRouteinclusion fromgetRoutesForEnvironmentinpackages/astro/src/vite-plugin-pages/pages.ts. In hybrid mode (output: 'static'+ adapter), this was pulling prerendered pages into the SSR bundle when they were targets of config-level redirects, inflating the bundle by orders of magnitude (reporter saw 68MB vs ~1.4MB). - This is a simpler alternative to #17061. That PR added a
route.redirectRoute.prerender === isPrerenderguard, which is correct but redundant — redirect targets already appear in the routes list with their ownprerenderflag and are included by the existingroute.prerender === isPrerendercheck during their own iteration. Removing the block entirely is the right fix because:- SSR redirect responses are generated from route metadata alone (
renderRedirectusessegments/pathnamefor theLocationheader). No component is loaded —AstroHandler.render()short-circuits beforegetModuleForRouteis ever called. - The only codepath that needs the target's component is prerendering (for
getStaticPathson dynamic targets), but prerendered redirects and their targets share the sameprerenderflag, so both are already in the prerender environment's route set.
- SSR redirect responses are generated from route metadata alone (
Closes #17060
Supersedes #17061
Testing
- Added
packages/astro/test/units/redirects/environment-filtering.test.tswith 4 test cases covering all combinations of redirect/target prerender flags: SSR redirect to prerendered target (must exclude target from SSR set), prerendered redirect to prerendered target (both included), SSR redirect to SSR target (both included), prerendered redirect to SSR target (must exclude target from prerender set).
Docs
- No docs update needed — this is an internal build optimization fix with no user-facing API change.
Changes
Bump volar-service-* deps 0.0.70 → 0.0.71 to resolve yaml CVE-2026-33532
volar-service-yaml@0.0.70 pulls yaml-language-server@~1.20.0 → yaml@2.7.1,
which is vulnerable to CVE-2026-33532 (GHSA-48c2-rrv3-qjmp, stack-overflow DoS,
fixed in yaml 2.8.3). volar-service-yaml@0.0.71 moves to
yaml-language-server@~1.23.0 → yaml@2.8.3.
Bumping the full volar-service-* set in lockstep to keep them aligned against
@volar/* ~2.4.28. This propagates the fix down to @astrojs/check via its
@astrojs/language-server ^2.16.7 range, no change needed there.
Testing
No tests added; patch version bump of volar dependencies.
Docs
This should have zero user-facing effects beyond removing CVE-2026-33532 from
their security scanners.
Description
While implementing support for markdown.processedDirs in a Starlight plugin, I realized on our implementation was a bit fragile. I would guess nobody ever ran into this issue in practice as this behavior existed since we initially introduced heading links.
For example, a configured path like ./src/content/custom/ could process ./src/content/custom-test/index.md.
Follow-up to #17049, fixing the E2E failures discussed in #17049 (comment). Root-cause analysis in #17049 (comment) — this PR targets the flue/fix-17047 branch so it can be merged into #17049 directly.
Changes
src/prerenderer.ts: drop the status-based failure check. It treated any non-2xx prerender response as a build failure, but pages may intentionally return non-2xx while prerendering — the E2E Cloudflare fixture'smissing.astroreturnsnew Response(null, { status: 404 })in production builds, which is what broke the E2E jobs. Thex-astro-prerender-errorheader is now the only failure signal.src/utils/prerender.ts: addinstallPrerenderErrorPropagation(). The header path was previously dead code:app.render()never rejects on render errors becauseAstroHandlercatches the throw and the productionDefaultErrorHandlerrenders a 500 error page, so the bufferingcatchinhandlePrerenderRequestnever fired (the new test was passing only via the status check). This override replicates core'sBuildErrorHandlersemantics on the worker app: render errors (status 500 with anerrorand noresponse) are rethrown, while intentional error responses and 404s flow through the default handler unchanged.src/utils/handler.ts: install the override whenisPrerenderis true.
Error flow for a genuine render crash: throw → AstroHandler catch → patched renderError rethrows → app.render rejects → handlePrerenderRequest catch → 500 + x-astro-prerender-error header → Node-side render() throws → build fails.
Testing
test/prerenderer-errors.test.tspasses (both tests) — a page throwing during prerendering still fails the build.packages/astro/e2e/fixtures/cloudflarebuilds successfully again with the intentional 404 inmissing.astro— the exact case that failed the E2E jobs.- Full adapter test suite results match a baseline run of the unmodified #17049 code.
🤖 Generated with Claude Code
What's the problem?
Closes #17001.
With trailingSlash: "always" set, dynamic file endpoints like src/pages/api/[name].json.ts require a trailing slash in dev:
| Path | Expected | Dev | Build |
|---|---|---|---|
/api/bar.json |
200 | 404 | 200 |
/api/bar.json/ |
404 | 200 | 404 |
Static file endpoints (/feed.xml) worked correctly. Only dynamic ones were broken.
Root cause
trailingSlashForPath in create-manifest.ts was:
type === 'endpoint' && pathname && hasFileExtension(pathname) ? 'never' : config.trailingSlashFor any route with a dynamic segment, pathname is null (it's only set for fully-static paths). The null short-circuits the && chain before hasFileExtension ever runs, so the function falls back to config.trailingSlash regardless of whether the route has a file extension.
The build path works differently — it resolves concrete paths from getStaticPaths params — which is why the mismatch only surfaces in dev.
Fix
Pass the segment-joined route string as a fallback when pathname is null. joinSegments always preserves file extensions even for dynamic segments:
// /api/[name].json.ts -> /api/[name].json
joinSegments(segments) // '/api/[name].json'const trailingSlashForPath = (
pathname: string | null,
config: AstroConfig,
type: 'page' | 'endpoint',
route?: string,
): AstroConfig['trailingSlash'] =>
type === 'endpoint' && hasFileExtension(pathname ?? route ?? '') ? 'never' : config.trailingSlash;At all three call sites (createFileBasedRoutes, createRoutesFromEntriesByDir, createInjectedRoutes), route is now computed before the trailing slash call and passed through.
Testing
Added 'dynamic file endpoints force trailingSlash never regardless of config. issues#17001' to packages/astro/test/units/routing/manifest.test.ts. It covers:
- dynamic file endpoint matches without trailing slash
- dynamic file endpoint does not match with trailing slash
- existing static file endpoint behavior is unchanged
Changes
- Fixes prerendered routes with non-ASCII characters (Thai, Korean, Cyrillic, etc.) incorrectly returning 301 redirects to trailing slash URLs despite
trailingSlash: "never"setting - Adds guarded
decodeURI()call before filesystem lookup inserve-static.tsso percent-encoded URLs match actual directory names on disk - Resolves SEO issues where canonical redirect loops cause search engines to drop non-ASCII pages
Testing
- Added Thai test fixture
สวัสดี.astrowith prerendering enabled - Added test verifying non-ASCII prerendered route serves 200 without trailing slash
- Added test verifying non-ASCII prerendered route with trailing slash redirects to no-slash URL
Docs
- No docs update needed — this fixes existing documented
trailingSlash: "never"behavior
Closes #16989
Changes
- Fixes image compilation regression for Cloudflare adapter users with static output. The image optimizer was looking in wrong directory (
dist/_astro/instead ofdist/client/_astro/), causing ENOENT build failures. - Replaces hardcoded
outDirusage withgetClientOutputDirectory(settings)helper inprepareAssetsGenerationEnv()to respectpreserveBuildClientDirsetting.
Testing
- Confirmed by issue reporter (@kitschpatrol) that the fix resolves the build failure
- Existing core image test suite (207 tests) and preserve-build-client-dir tests (7 tests) continue to pass
Docs
- No docs update needed, this fixes broken functionality to work as originally documented
Closes #16919
Closes #17047
Changes
- Pages that throw during prerendering now correctly fail the build with a non-zero exit code instead of silently emitting truncated HTML
- The Cloudflare adapter now buffers the full response body inside workerd before returning it over HTTP, so streaming errors are caught and surfaced as 500 responses
- Prerender error messages are included in a custom
x-astro-prerender-errorheader and checked by the Node-side build process
When a component throws mid-render (e.g. an unregistered <Font> CSS variable), the previous implementation would commit HTTP status 200 before the stream completed. The stream would then abort, but the Node-side build process received the truncated body as a successful 200 response and wrote it to disk, causing astro build to exit 0 with broken output.
Testing
- Added
packages/integrations/cloudflare/test/prerenderer-errors.test.tswith test case'fails the build when a page throws during prerendering' - Added
prerenderer-render-errortest fixture with a page that throws during rendering to verify the fix
Docs
- No docs update needed — this fixes existing documented behavior (builds should fail when pages throw during prerendering)
Changes
Follow up of withastro/compiler-rs#45
Updates astro to use the latest version of the compiler
Testing
Green CI
Docs
Description
This PR adds support for Astro 7.
Known issues
| Symbol | Status |
|---|---|
| 🟢 | Fixed |
| 🟡 | Fix pending review or release |
| 🔴 | No fix yet, under investigation |
-
🟢 Many warnings (1 in dev, 6 in build) due to the use of
resolve.alias[].customResolvernow deprecated and probably related to the Astroastro:tsconfig-aliasvite plugin still using it.[WARN] [vite] `resolve.alias` contains an alias with `customResolver` option. This is deprecated and will be removed in Vite 9. Please use a custom plugin with a resolveId hook and `enforce: 'pre'` instead.
- Fixed by withastro/astro#17090
-
🟢 Warning at build time due to some plugins taking significant time during the build process which is due to the
pluginTimingsbuilt-in Rolldown check enabled by default. Feels weird in a release about speed to get warnings about some things being slow. Maybe we should disable this check by default?[WARN] [vite] [PLUGIN_TIMINGS] Your build spent significant time in plugins. Here is a breakdown: - @mdx-js/rolldown (41%) - astro:head-metadata-build (36%) - vite-plugin-astro-expressive-code (4%) - astro:server-islands (3%) - vite:css-post (2%)
- Fixed by withastro/astro#17090
-
🟢 Type-checking issue in
packages/starlight/integrations/remark-asides.ts- Fixed by bruits/satteri#97
-
🟢 Some duplicated heading IDs in the Sätteri databag
- Fixed by withastro/astro#17165
Remaining tasks
- Investigate
size-limitCSS size change (probably Vite 8/Rolldown related but need to see what and confirm the hypothesis)- Experimenting with lazy barrel pptimization
- Bump Astro dependencies to latest stable versions once released.
- Bump some peer dependencies versions once ready to release.
- Search and address any
TODO(HiDeoo)comments. - Add changesets.
- Make sure changesets use the correct new Starlight version number (e.g.
0.41.0). - Make sure to have updated links to v7 migration guide.
- Make sure changesets use the correct new Starlight version number (e.g.
- Update the banner link in
docs/src/content/docs/index.mdxto point to the correct version tag. - Re-enable link validation
- Will probably happen post-release once the plugin is updated
- Merge with the following Lunaria directive:
@lunaria-ignore:docs/src/content/docs/pt-pt/index.mdx;docs/src/content/docs/uk/index.mdx;docs/src/content/docs/zh-cn/index.mdx
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.
next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.
Releases
@astrojs/alpinejs@1.0.0-beta.1
Patch Changes
@astrojs/cloudflare@14.0.0-beta.2
Patch Changes
@astrojs/markdoc@2.0.0-beta.1
Patch Changes
@astrojs/mdx@7.0.0-beta.2
Patch Changes
@astrojs/netlify@8.0.0-beta.1
Patch Changes
@astrojs/node@11.0.0-beta.1
Patch Changes
@astrojs/preact@6.0.0-beta.1
Patch Changes
@astrojs/react@6.0.0-beta.1
Patch Changes
@astrojs/solid-js@7.0.0-beta.1
Patch Changes
@astrojs/svelte@9.0.0-beta.3
Patch Changes
@astrojs/vercel@11.0.0-beta.1
Patch Changes
@astrojs/vue@7.0.0-beta.1
Patch Changes
@astrojs/markdown-satteri@0.3.1-beta.1
Patch Changes
Changes
- Fixes CSS incorrectly loading on pages that don't import it when using
client:onlyislands in production builds - Adds missing
isCSSRequestfilter to the client:only CSS association loop inplugin-css.ts, matching the existing filter in the non-client:only code path - Prevents CSS from unrelated modules being associated with pages when Rollup bundles pure utility modules into the same chunk as CSS-importing modules
- Reporter confirmed dramatic CSS reductions: frontpage went from 14 stylesheets/718.2 KB to 1 stylesheet/481.2 KB
Testing
- Adds reproduction test
client-only-css-chunk-leak.test.tsthat verifies CSS is only loaded on pages that actually import it
Docs
- No docs update needed — this fixes incorrect behavior to match documented CSS scoping for islands
Closes #17043
Changes
Under experimental.advancedRouting, the composable astro/hono pages() handler delegated straight to PagesHandler.handle(), with none of the app-level error handling that AstroHandler provides on the standard path. A page throwing during render therefore propagated to the host framework, which answered with its own default error response (Hono: plain-text Internal Server Error) instead of rendering the custom 500.astro page (#16952).
- Added
PagesHandler.handleWithErrorFallback(app, state)(core/pages/handler.ts): wraps route dispatch in a try/catch that logs the error and renders it viaapp.renderError(..., { status: 500, error }), the same behaviourAstroHandler.render()has on the standard path. Forwardingerrorkeeps the documentederrorprop on500.astroworking. The logic lives in the handler module becausecore/fetch/index.tsdeliberately keeps its exports as thin, logic-free wrappers. - When no route matched,
handleWithErrorFallback()returns a 404 response marked withX-Astro-Error(the same patternhandle()uses for the un-dispatched redirect case), so the app's existing post-check renders the 404 error page a level up. This case is reachable because #16911 intentionally leavesrouteDataunset when the custom 404 route is prerendered (or absent), wherePagesHandler.handle()would otherwise throw aTypeErrorand the catch would turn those unmatched requests into 500s. The 500 path keeps callingrenderError()directly: the marker cannot carry the error object, so500.astrowould lose itserrorprop and the stack would not be logged. - Switched
pages()incore/fetch/index.tsto delegate to the new method (delegation only, no logic added). - Added a changeset (
astropatch).
Testing
- Added
'renders the custom 500 page when a page throws during render'- verifies the fullpages()pipeline returns HTTP 500 with the custom 500 page content when a page throws during render - Added
'returns a marked 404 for the app post-check when the custom 404 route is prerendered'- verifies unmatched requests return HTTP 404 carrying theX-Astro-Errormarker instead of throwing - Verified end-to-end against the reproduction from #16952 (Hono +
app.use(pages()), node standalone):GET /boom->500with the custom 500 page (previously Hono's plain-textInternal Server Error),GET /does-not-exist->404serving the prerendered 404 page with the marker stripped from the final response,GET /->200; the thrown error is logged with its stack trace
Out of scope (noticed while testing)
Errors thrown by Astro middleware itself (src/middleware.ts) on the composable path (app.use(middleware())) still reach the host framework's default error handler instead of 500.astro. On the standard path, AstroHandler.render()'s try/catch wraps the whole middleware chain, so middleware errors get the same 500.astro treatment. The equivalent catch can't simply live inside the composable middleware(): its next is the host's next(), so it would also intercept errors thrown by host middleware running below it in the chain and hide them from the user's own app.onError. The all-in-one astro() handler already provides full parity. A possible follow-up would be an error-page helper exported from astro/hono (e.g. app.onError(onError())) so composable users can opt in - happy to open a separate issue for this.
Docs
- No docs update needed, this fixes broken functionality in an experimental feature so it matches the documented custom-error-page behavior (the 404 counterpart was #16911)
Closes #16952
Closes #17039
Changes
- Middleware file watcher now triggers HMR for files inside the
src/middleware/directory, not justsrc/middleware.{ts,js}files directly - Updates the Vite plugin path matcher to include both
middleware.(existing) andmiddleware/(new) prefixes, matching the fix pattern used for actions in #16932
Testing
- Added
packages/astro/test/units/middleware/middleware-hmr.test.tswith 8 test cases covering path matching for both file and directory patterns
Docs
- No docs update needed — this fixes existing documented behavior that was broken for directory-based middleware organization
What this fixes
joinPaths() in @astrojs/internal-helpers filters out non-string arguments (so callers can pass undefined for optional segments), but the branch that detects the last segment compared the map index against the original, unfiltered argument count:
export function joinPaths(...paths: (string | undefined)[]) {
return paths
.filter(isString)
.map((path, i) => {
if (i === 0) {
return removeTrailingForwardSlash(path);
} else if (i === paths.length - 1) { // ← unfiltered length
return removeLeadingForwardSlash(path);
} else {
return trimSlashes(path);
}
})
.join('/');
}When a skipped (undefined) argument precedes the final one, the filtered array is shorter than paths.length, so the real last segment no longer matches i === paths.length - 1. It falls into the else branch and gets trimSlashes(), which strips its trailing slash — even though joinPaths is supposed to preserve the trailing slash of the last segment (that is exactly what removeLeadingForwardSlash does, as opposed to trimSlashes).
Reproduction
joinPaths('/base', undefined, 'docs/setup/'); // → '/base/docs/setup' (expected '/base/docs/setup/')
joinPaths('/foo', 'bar/', undefined); // → '/foo/bar' (expected '/foo/bar/')
joinPaths(undefined, 'pl', 'docs/setup/'); // → 'pl/docs/setup' (expected 'pl/docs/setup/')The invariant being violated: a skipped argument should yield the same result as omitting it. joinPaths(a, undefined, b) must equal joinPaths(a, b).
The fix
Compute the filtered array once and base the last-segment check on its length:
export function joinPaths(...paths: (string | undefined)[]) {
const segments = paths.filter(isString);
return segments
.map((path, i) => {
if (i === 0) {
return removeTrailingForwardSlash(path);
} else if (i === segments.length - 1) {
return removeLeadingForwardSlash(path);
} else {
return trimSlashes(path);
}
})
.join('/');
}Calls where every argument is a string (the vast majority of call sites, e.g. joinPaths(base, route)) are unchanged, since segments.length === paths.length in that case.
This is the underlying utility bug behind the prependWith-with-trailing-slash case noted in the root-cause analysis of #17034 (where getLocaleRelativeUrl calls joinPaths(base, prependWith, locale, path) with prependWith possibly undefined).
Tests
Added a joinPaths test block to packages/internal-helpers/test/path.test.ts covering simple joins, inner-slash trimming, and the skipped-argument regression. The new assertions fail on main and pass with this change.
pnpm --filter @astrojs/internal-helpers test→ 64 passingastroi18n unit tests (test/units/i18n/astro_i18n.test.ts) → 37 passing, unchanged
Related: #17034
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.7
Patch Changes
-
#17035
197e50eThanks @astrobot-houston! - FixesgetRelativeLocaleUrl,getAbsoluteLocaleUrl, andgetAbsoluteLocaleUrlListto strip trailing slashes whentrailingSlash: 'never'is configured -
#16967
3719765Thanks @astrobot-houston! - Fixes double URL-encoded paths returning 400 Bad Request on on-demand routesPreviously, any URL containing a double-encoded character (like
%255B, which is[encoded twice) was unconditionally rejected with a400 Bad Requestbefore middleware or route handlers could run. This broke embedded tools like Sanity Studio whose client-side router legitimately produces double-encoded URLs.The fix replaces the rejection approach with iterative decoding — multi-level percent-encoding is now fully resolved to its canonical form before being passed to middleware and route matching. This preserves the security fix for CVE-2025-66202 (middleware authorization bypass via double encoding) because middleware now always sees the fully decoded path, making bypass impossible. For example,
/api/%2561dminis decoded to/api/admin, which middleware can correctly block. -
#17066
2f4d92aThanks @matthewp! - Fixes prerendered redirect targets being incorrectly bundled into the SSR function in hybrid mode, causing massive bundle size inflation -
#16882
621beb7Thanks @jettwayio! - fix(render): honour compressHTML when joining head elements -
#16892
8d753b0Thanks @astrobot-houston! - Fixes custom elements in MDX having their children'sslotattribute stripped by the JSX runtimeWhen custom elements (tags with hyphens like
<my-element>) are used in MDX files, theslotHTML attribute on their children is now correctly preserved. Previously, the shared JSX runtime would treatslotas an Astro slot assignment and remove it from the output, breaking Shadow DOM named slot distribution for web components. -
#16957
544ee76Thanks @thelazylamaGit! - Fixes stale inline CSS in server-rendered HTML after CSS file edits during devWhen editing a CSS file (
.css,.scss, etc.) during development, the inline<style>tags in server-rendered HTML would retain old CSS content instead of updating. This caused a brief flash of old CSS (FOUC) on fresh page loads before Vite's client-side HMR corrected the styles.The fix ensures that Astro's per-route dev CSS virtual modules are invalidated in both the SSR module graph and the module runner's evaluation cache when a style file changes, so the next page render picks up the fresh CSS.
-
#17044
2220d22Thanks @astrobot-houston! - Fixes CSS fromclient:onlyislands leaking to unrelated pages when Rollup bundles non-CSS-importing modules into the same chunk as CSS-importing modules -
#17040
7c4763dThanks @astrobot-houston! - Fixes HMR not triggering for files inside thesrc/middleware/directory during dev -
#16672
52fc862Thanks @martinheidegger! - Fixes support for numeric IDs in YAML frontmatter when using content collection references -
#16762
9de80aeThanks @alexanderdombroski! - Adds a JSON schema to the Wrangler configuration file generated when runningastro add cloudflare -
#17046
ef771ecThanks @ematipico! - Improves the diagnostics emitted when Astro parses incorrect.astrofiles.
Description
Follow-up to #3923 (comment), this PR improves the comment describing how we import the Sätteri integration to workaround an Astro limitation.
The comment should be clear enough to explain the issue and the workaround. Regarding a potential fix in Astro for the underlying issue, I would definitelly need to think more about it. So far, the only idea I had is to have the Vite module runner used for loading the config to stay open instead of closing immediately, and return some kind of disposable. The disposable would be used to properly close it only when needed, e.g. after a build or when the dev server is closed, when the dev server is restarted, after a sync, etc. The getViteConfig() case would also need to be handled. On top of the extra cost of keeping the module runner open, it's also a lot of orchestration to pass around such disposable with the current architecture.
A workaround that I only briefly tested for fun is to by-pass Vite and rely on a native dynamic import to load @astrojs/markdown-satteri, e.g. using () => new Function('return import("@astrojs/markdown-satteri")') but that's definitely not something we want to do (even tho it seems to work) 😅
Changes
- Validates the request URL origin against
allowedDomainsindefault-handler.tsbefore constructing thestatusURLused to fetch prerendered error pages. WhenallowedDomainsis configured and the host matches, the original origin is used. Otherwise, falls back tolocalhost.
Testing
- Added two test cases to
error-pages.test.ts: one verifying an unvalidated host is replaced withlocalhost, and one verifying a validated host is preserved.
Docs
- No docs update needed.
Changes
This PR restore our "Needs repro" workflow by using the gh CLI. To note that I used the houston bot token.
Testing
I already created a similar workflow and it should work for us.
Green CI
Docs
N/A
Changes
- Prevent
@astrojs/upgradefrom selecting a requested dist-tag when that tag resolves to a version older than the currently installed package. - Preserve prerelease comparison for installed versions like
7.0.0-beta.3instead of coercing them to stable versions. - Add a regression test covering
astroupgrading to beta while an integration remains on a newer stable release.
Why
Running @astrojs/upgrade beta could downgrade companion packages when their beta dist-tag pointed to an older prerelease than the currently installed stable version.
Fixes #17024
Testing
npx --yes pnpm@11.0.9 -C packages/upgrade buildnpx --yes pnpm@11.0.9 -C packages/upgrade testnpx --yes pnpm@11.0.9 lint
Changes
- Validates attribute names in
addAttribute()against the HTML spec before interpolating them into output. Keys containing characters invalid in attribute names (",',>,/,=, or whitespace) are silently dropped.
Testing
- Added
addAttribute rejects invalid attribute keyssuite (7 cases) covering keys with", spaces,>,',=, plus positive cases for normal names (data-foo,aria-label) and namespaced attributes (on:click,xmlns:happy). - Added
spreadAttributes rejects invalid attribute keyssuite (1 case) verifying invalid keys are dropped while valid siblings are preserved.
Docs
- No docs update needed. Internal rendering hardening with no user-facing API change.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.6
Patch Changes
-
#16765
b10e86eThanks @fkatsuhiro! - Fixes an issue where renaming an image file while the dev server is running triggers a build error. Now Astro correctly hot-reloads the image without crashing. -
#17026
add3df1Thanks @matthewp! - HardensaddAttributeto drop attribute names containing characters that are invalid per the HTML spec (",',>,/,=, whitespace) -
#17033
ffda27bThanks @matthewp! - Validates the request origin againstallowedDomainsbefore fetching prerendered error pages. WhenallowedDomainsis configured and the Host header matches, the original origin is used. Otherwise, the fetch falls back tolocalhost.
@astrojs/netlify@7.0.13
Patch Changes
Changes
Prevents an unnecessary page reload during ClientRouter back/forward navigations when the browser's Navigation API handles the transition before popstate fires, making from and to the same URL.
Testing
Update: Added a test case that reproduces the bug by pushing a fake history entry, then pressing back
Manually tested in an Astro project where /work and its sub-routes (e.g. /work/slug-1) run as a SPA kept in sync via the browser's Navigation API. Other routes like /about or / still trigger Astro's View Transitions normally. For /work and its sub-routes:
-
Listening to
astro:before-preparationand callingpreventDefault()when both the current and destination URLs are under/work, which stops ClientRouter from fetching new HTML. -
Using the browser's Navigation API (
event.intercept()) to update the app's own state instead.
Pressing back from /work/slug-1 to /work/ causes two things to happen in sequence:
-
The browser's Navigation API fires. Our
event.intercept()handler runs, updating the app's state and changing the URL to/work/. -
popstatefires. The ClientRouter'sonPopStatecallstransition().
At this point the URL is already /work/. But originalLocation was never updated to /work/slug-1 — because that earlier forward navigation was done via nav.navigate(), which the ClientRouter doesn't track. So transition() sees from as /work/ (the stale originalLocation) and to as /work/ (the current URL). They match, the ClientRouter thinks nothing changed, falls through to doPreparation(), and eventually calls location.href = to.href, which triggers an unnecessary full page navigation.
With the guard added to transition(), from.href === to.href returns early before any of that happens, and the full page navigation never occurs.
Docs
No docs needed. Bugfix with no API or behavior change.
Changes
- Tightens the regex produced by
remotePatternToRegexso wildcard hostname and pathname patterns match strictly:*.requires exactly one subdomain (bare apex no longer matches)**.requires one or more subdomains (zero subdomains no longer matches)/*matches a single path segment only, not deeper paths
- Anchors the generated regex with
$so partial prefix matches are not possible
Testing
- Updated existing assertion:
*.example.orgno longer matches bareexample.org - Added assertion:
/images/*does not match/images/a/b.jpg
Docs
- No docs update needed -- this is an internal regex generation change with no user-facing API impact.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.40.0
Minor Changes
-
#3923
edf2e6bThanks @Princesseuh! - Adds support for Astro 6.4 and the new Sätteri Markdown processor.It is now possible to opt into using Astro's 6.4 Sätteri Markdown processor by installing the
@astrojs/markdown-satteripackage and configuring it in yourastro.config.mjsfile:// astro.config.mjs import { defineConfig } from 'astro/config'; import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), }, });
⚠️ BREAKING CHANGE: The minimum supported version of Astro is now v6.4.5.Please update Starlight and Astro together:
npx @astrojs/upgrade
Community Starlight plugins and Astro integrations may also need to be manually updated to work with Sätteri. If you encounter any issues, please reach out to the plugin or integration author to see if it is a known issue or if an updated version is being worked on.
Patch Changes
- #3923
edf2e6bThanks @Princesseuh! - Updates Expressive Code to version 0.43.1.
Changes
- Disables
persist-credentialson the checkout step in the issue triage workflow. When credentials are persisted, git always uses the defaultGITHUB_TOKEN(read-only) for pushes, even whengitPush()explicitly providesFREDKBOT_GITHUB_TOKEN. Disabling it lets the privileged token be used as intended.
Testing
- No test changes. CI-only fix verified against this failed run.
Docs
- No docs needed — CI-only change.
Changes
Fixes the version of the logger
Testing
N/A
Docs
/cc @withastro/maintainers-docs for feedback!
Changes
Remove the source code of deprecated package @astrojs/db. Remove related astro db CLI commands.
Testing
The actions-blog and actions-react-19 e2e fixtures only used @astrojs/db as a data store, so they now use a tiny file-backed JSON store instead.
Docs
Added a Changesets file to mention that some CLI commands like astro db are gone.
IMPORTANT: THIS MUST BE DONE AS A MERGE COMMIT
Changes
- Merges
mainintonextto bring in latest stable releases (astro 6.4.5, @astrojs/db 0.21.3, @astrojs/cloudflare 13.7.0, @astrojs/mdx 6.0.3, @astrojs/node 10.1.4, @astrojs/markdown-satteri 0.3.0). - Conflict resolution kept
nextversions for all package.json files and merged CHANGELOG entries (alpha entries first, then stable entries).
Testing
- No test changes. This is a branch merge with only version/changelog conflicts.
Docs
- No docs needed. Version merges do not affect user-facing documentation.
Changes
Closes AST-179
This PR exists from prerelease mode.
Note
To merge on the day of when we merge all PRs, right before the release
Testing
N/A
Docs
N/A
Changes
- Update all
session.skill()calls to use registered skill names instead of file paths (e.g.triage/reproduce.md->triage). Flue runtime 0.8 removed support for addressing sub-files within a skill; the old runtime was lax about this. - Pass
stepandinstructionsvia args to direct the agent to the correct sub-skill file within each skill. - Add
.agents/skills/merge/SKILL.mdso the merge skill gets discovered by the runtime (it had noSKILL.mdand was invisible).
Testing
- No test changes. This is a CI workflow fix — validation is the next issue triage run succeeding instead of failing with
Skill "triage/reproduce.md" not registered.
Docs
- No docs needed. Internal CI workflow change only.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.
next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.
Releases
astro@7.0.0-beta.3
Major Changes
-
#17010
0606073Thanks @ocavue! - Removes theastro db,astro login,astro logout,astro link, andastro initCLI commands.The
@astrojs/dbpackage is now deprecated. We recommend using a database client (Drizzle, Kysely, etc.) directly instead. -
#16877
3b7d76eThanks @matthewp! - Enables advanced routing by default.The advanced routing feature introduced behind a flag in v6.3.0 is no longer experimental and is now enabled by default.
This gives full control over how requests flow through your application, with first-class support for frameworks like Hono.
Advanced routing now uses
src/fetch.tsas default entrypoint instead ofsrc/app.ts.If you were previously using this feature without a custom entrypoint, please configure
fetchFileor rename your entrypoint tosrc/fetch.ts, and then remove the experimental flag from your Astro config:import { defineConfig } from 'astro/config'; export default defineConfig({ experimental { - advancedRouting: true, }, + fetchFile: 'app.ts' // optional, you only need this if you cannot rename your entrypoint. });fetchFileis now a top-level config option instead of being nested underexperimental.advancedRouting. If you were using a custom entrypoint, please update your Astro config to move its configuration:// astro.config.mjs export default defineConfig({ - experimental: { - advancedRouting: { - fetchFile: 'my-custom-entrypoint.ts', - }, - }, + fetchFile: 'my-custom-entrypoint.ts', })You can also set
fetchFile: nullto disable the entrypoint if you are usingsrc/fetch.tsfor another purpose, or don’t need advanced routing features.If you have been waiting for stabilization before using advanced routing, you can now do so.
Please see the advanced routing guide in docs for more about this feature.
Minor Changes
-
#16998
57dcc31Thanks @matthewp! - ExposesgetFetchState()fromastro/honoas a public APIThe
getFetchState()function retrieves or lazily creates aFetchStatefrom a Hono context object. This allows third-party packages to build Hono middleware that interacts with Astro's per-request state, giving theastro/honoAPI the same extensibility asastro/fetch.import { Hono } from 'hono'; import { getFetchState, pages } from 'astro/hono'; const app = new Hono(); app.use(async (context, next) => { const state = getFetchState(context); state.locals.message = 'Hello from custom middleware'; await next(); }); app.use(pages()); export default app;
-
#16996
300641eThanks @florian-lefebvre! - Adds asubsetfield to theFontDatatype exposed viafontDatafromastro:assets. When using multiple font subsets (e.g.,subsets: ["latin", "korean"]), each font data entry now includes the subset name, making it possible to distinguish between font entries for different subsets that share the same weight and style. -
#16745
f864a80Thanks @ematipico! - The custom logger feature introduced behind a flag in v6.2.0 is no longer experimental and is available for general use.This feature provides better control over Astro's logging infrastructure by allowing you to replace the default console output with custom logging implementations (e.g., structured JSON). This is particularly useful for on-demand rendering when connecting to log aggregation services such as Kibana, Logstash, CloudWatch, Grafana, or Loki.
Astro provides three built-in log handlers (
json,node, andconsole), and you can also create your own.JSON logging
import { defineConfig, logHandlers } from 'astro/config'; export default defineConfig({ logger: logHandlers.json({ pretty: true, level: 'warn', }), });
Custom logger
import { defineConfig } from 'astro/config'; export default defineConfig({ logger: { entrypoint: '@org/custom-logger', }, });
Additionally,
context.loggeris now always available in API routes and middleware, even without a custom logger configured.If you were previously using this feature, please remove the experimental flag from your Astro config:
import { defineConfig } from 'astro/config'; export default defineConfig({ - experimental: { - logger: { - entrypoint: '@org/custom-logger', - }, - }, + logger: { + entrypoint: '@org/custom-logger', + }, });If you have been waiting for stabilization before using custom loggers, you can now do so.
Please see the Logger docs for more about this feature.
-
#16981
0d6d644Thanks @ematipico! - Removes the settingexperimental.queuedRendering. The new rendering engine is now stable and replaces the old one.As part of the stabilization, the queued rendering has been improved, and some features have been removed:
- The construction of the queue has been removed, instead now Astro uses a streaming approach where components are rendered and flushed as they are encountered.
- The node polling feature has been removed because it doesn't yield concrete savings.
- The content cache has been descoped, and how only tag names are cached.
If you were previously using this experimental feature, you must remove this experimental flag from your configuration as it no longer exists:
// astro.config.mjs import { defineConfig } from "astro/config"; export default defineConfig({ experimental: { - queuedRendering: {} } });
Changes
Exports the existing getFetchState() function from astro/hono so third-party packages and custom Hono middleware can access the per-request FetchState.
Testing
Added 3 unit tests in test/units/hono/index.test.ts:
- Creates a new
FetchStatefrom Hono context - Returns the same instance on subsequent calls
- Custom middleware can set
localsviagetFetchState
Docs
Changes
- Reverts #16720, which added a
Cloudflare-Workersnavigator check to theisNodevariable inpackages/astro/src/runtime/server/render/util.ts. This change causedisNodeto returnfalseinside workerd, forcing the rendering pipeline ontorenderToReadableStreaminstead ofrenderToAsyncIterable. For large prerendered sites on Cloudflare (~14k pages), this caused a ~6x build time regression (6-7 min to 40 min).
Testing
- No new tests. The regression is workerd-specific and only manifests at scale with the Cloudflare adapter. A preview release should be created so the reporter on #16973 can verify the fix.
Docs
- No docs needed — this restores previous behavior.
I’ve tested the latest version of the LSP server locally and found a regression in quick fixes.
Related to: #15908
Changes
- Restores the Add import quick fix for unimported .astro components with generated TypeScript names ending in AstroComponent
Testing
- Added coverage for local, nested, aliased, and generated-name Astro components.
- Tests verify that imports omit the AstroComponent suffix, keep the original diagnostic, and edit the source .astro file.
Changes
Fixes #14657.
Allows the Astro grammar to keep looking for lang / type on following lines of <style> and <script> opening tags.
Added grammar fixtures for multiline style languages (scss, less, sass) and script types (ts, application/ld+json, module).
Testing
corepack pnpm -C packages/language-tools/vscode test:grammar
Docs
No docs needed, syntax highlighting fix.
Fixes #16974 so startup message shows on experimental.logger destination, not default terminal.
Currently it fires before any request arrives, always missing the user-configured destination.
Changes
- Adds
getLogger().then()before logListeningOn to correct destination. - Makes
adapterLoggerre-create itself when the underlying logger instance changes, so it always reflects the resolved logger regardless of first access
Testing
Before Implementation
After Implementation
Confirmed the first line of stdout is {"message":"Server listening on ...","label":"@astrojs/node","level":"info"} when manually building standalone server.
Added unit test verifying that adapterLogger re-creates itself when pipeline.getLogger() replaces the underlying logger instance. Both packages build without errors.
Docs
No update needed.
This PR fixes a typo I spotted in the first place:
Cummunity-Themes -> Community-Themes
It also makes minor changes to 3 sentences to improve their readability.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| actions/checkout | action | patch | v6.0.2 → v6.0.3 |
| changesets/action | action | minor | v1.8.0 → v1.9.0 |
Release Notes
actions/checkout (actions/checkout)
v6.0.3
- Fix checkout init for SHA-256 repositories by @yaananth in #2439
- fix: expand merge commit SHA regex and add SHA-256 test cases by @yaananth in #2414
changesets/action (changesets/action)
v1.9.0
Minor Changes
-
#636
b072bccThanks @bluwy! - Add a new@changesets/action/pr-commentsub-action to comment on PRs -
#625
8795eeeThanks @bluwy! - Add a new@changesets/action/pr-statussub-action to generate the changeset status comment for PRs as an alternative to the Changesets Bot.
Patch Changes
-
#535
34f64f6Thanks @Andarist! - Fixed an issue with GitHub releases not being created for successfully published packages when some packages failed to be published to the registry. -
#632
1d54b9eThanks @bluwy! - Simplify internal implementation to get changelog entries for a package version -
#629
e0c90aaThanks @bluwy! - Fix custom version and publish command argument parsing -
#645
f9585d9Thanks @Andarist! - Improved force-push handling when usingcommitMode: "github-api"so updating an existing branch no longer temporarily resets the target branch to the base commit, avoiding cases where GitHub closes open pull requests during the update. This should remove a possibility of a GitHub state race that caused the force-pushed PRs not being reopened.
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
defineProviderGettersinFetchStatenow always defines asessiongetter on context objects, even when no session provider has been registered. This ensuresAstro.session/ctx.sessionis always a present property regardless of whether thesessions()handler is included in the pipeline.- Accessing
sessionwithout storage configured logs a warning on first access: "no session storage is configured. Either configure the storage manually or use an adapter that provides session storage." This restores the helpful warning that existed in 6.2 before the advanced routing refactor moved session setup into the provider system.
Testing
- Added two unit tests in
test/units/fetch/index.test.ts: one verifying the property is always present (returningundefined), one verifying the warn-once behavior withSpyLogger.
Docs
- No docs needed. No user-facing API change.
Changes
Closes AST-166
This PR stabilise queued rendering, however there's a twist! The old queued engine is gone, and it's been replaced with a "streaming engine".
This engine is still queue based, but it renders the nodes on the go, so a two-step engine is now a one step engine. The old strategy didn't work very well, and it had some flaws. The new strategy is simpler and faster than the queue double pass and recursion.
Also, I removed pooling. It doesn't yield concrete benefits.
As for content cache, it is now scoped so the single tag names. The content itself isn't cached, as well as the attributes.
Testing
Green CI
Docs
Changes
- Removes
provide,resolve, andfinalizeAllfrom the publicAstroFetchStateinterface. The concreteFetchStateclass retains them for internal use (session, cache). - Removes
App.Providersmodule augmentation interface — nothing depends on it since built-in providers are already explicitly typed onAPIContext. - Removes
extends App.ProvidersfromAPIContext. - Marks
ContextProvider<T>as@internal.
Per the discussion on #16877, we do not want 3rd-party handlers extending the Astro global via FetchState#provide yet. This keeps the machinery working internally while removing it from the public API surface.
Testing
No test changes — existing context provider tests continue to pass since the internal class is unchanged.
Docs
Summary
Three JSDoc example blocks in errors-data.ts — for InvalidGetStaticPathParam, InvalidGetStaticPathsEntry, and InvalidGetStaticPathsReturn — open with the route file pages/blog/[id].astro but then return params keyed on slug:
{ params: { slug: "blog" } },
{ params: { slug: "about" } }For a dynamic route [id].astro, getStaticPaths() must return params keyed on the segment name id. A slug key would itself trigger a routing error, so these examples — whose whole purpose is to teach correct getStaticPaths() usage — are internally inconsistent.
These JSDoc blocks also generate the public Error Reference pages, so the mismatch is user-facing (e.g. docs.astro.build/en/reference/errors/invalid-get-static-path-param/).
Change
Replace the slug param key with id in all six lines so each example matches its declared [id].astro route. This matches the neighboring blocks GetStaticPathsExpectedParams and GetStaticPathsInvalidRouteParam, which already use { params: { id: ... } } for an [id] route.
Docs/comment example only — no runtime code changes.
Summary
Preserve empty request cookie values in Astro.cookies.get().
Related: #16983
User-facing problem
A request with Cookie: foo= currently produces two conflicting results:
cookies.has('foo') === truecookies.get('foo') === undefined
That collapses present but empty into the same result as missing.
Expected behavior
For Cookie: foo=, cookies.get('foo')?.value should be ''.
Why this looks like a regression
I opened #16983 with a minimal reproduction and the full history.
Short version: get() used to preserve empty values, then started checking if (value) in c69bf18a4e on 2025-03-18. A later refactor in 018fbe90f4 on 2025-03-26 made the has() / get() mismatch more obvious.
What changed
- treat
''as a valid parsed cookie value inAstro.cookies.get() - add a regression test for
cookie: 'foo='
Validation
npm exec --yes pnpm@11.5.0 -- exec astro-scripts test "test/units/cookies/*.test.ts" --strip-types --teardown ./test/units/teardown.ts
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/cloudflare@13.7.0
Minor Changes
-
#16571
d4b0cd1Thanks @MA2153! - Sets immutable cache headers for static assetsStatic assets under
_astrocan be cached to improve performance. The adapter now automatically injects aCache-Controlheader at build time when possible.
Patch Changes
-
#16968
7a5c001Thanks @astrobot-houston! - Fixes a build crash when usingexperimental.advancedRoutingwith a customfetchFilethat statically importscffrom@astrojs/cloudflare/fetch. The circular dependency between@astrojs/cloudflare/fetchandastro/app/entrypointcausedcreateApporcreateGetEnvto beundefinedat module evaluation time. Initialization is now deferred to the firstcf()call, breaking the cycle. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/markdown-satteri@0.3.0
Minor Changes
-
#16969
4a31f90Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Settingmarkdown.syntaxHighlightto'prism'now highlights your code blocks with Prism.// astro.config.mjs import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), syntaxHighlight: 'prism', }, });
astro@6.4.5
Patch Changes
-
#16985
4ecff32Thanks @maximslo! - Fixes theexperimental.loggerdestination not being used for the "Server listening on..." startup message. The logger is now resolved before the server starts listening, andadapterLoggerre-creates itself when the underlying logger changes so the startup message uses the correct destination. -
#16947
e0703a6Thanks @ematipico! - FixesAstro.request.urlnot reflecting validatedX-Forwarded-Proto/X-Forwarded-Hostheaders whensecurity.allowedDomainsis configured. Previously, onlyAstro.urlwas updated with the forwarded origin whileAstro.request.urlretained the socket-derived URL, causing the two to diverge behind TLS-terminating proxies. -
#16997
dc45246Thanks @matthewp! - Reverts a change toisNoderuntime detection that caused a significant build time regression for Cloudflare adapter users with large prerendered sites
@astrojs/db@0.21.3
Patch Changes
- #16964
b048826Thanks @Princesseuh! - Deprecates the@astrojs/dbintegration. We no longer have the bandwidth to maintain this package, and we recommend that users directly use the database client of their choice (Drizzle, Kysely, etc.) in their Astro projects instead.
@astrojs/mdx@6.0.3
Patch Changes
-
#16969
4a31f90Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Settingmarkdown.syntaxHighlightto'prism'now highlights your code blocks with Prism.// astro.config.mjs import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), syntaxHighlight: 'prism', }, });
@astrojs/node@10.1.4
Patch Changes
- #16985
4ecff32Thanks @maximslo! - Fixes theexperimental.loggerdestination not being used for the "Server listening on..." startup message. The logger is now resolved before the server starts listening, andadapterLoggerre-creates itself when the underlying logger changes so the startup message uses the correct destination.
Changes
Update a test so that it can pass in the next branch (with @astrojs/compiler-rs).
@astrojs/compiler-rs re-serializes CSS values from rgb(255, 165, 0) to orange to this test didn't pass in the next branch.
Unblock #16962
Testing
rgb(0, 255, 0) CI.
Docs
N/A
Changes
What the title says, it's easy enough, just needed hooking things up
Testing
Added / updated tests
Docs
N/A, I don't think we documented that it didn't work right now
Changes
- Defers
createApp()andsetGetEnv()initialization in@astrojs/cloudflare/fetchto the firstcf()call - Prevents circular import crashes when custom
fetchFilestatically importscffrom@astrojs/cloudflare/fetch - Uses lazy initialization pattern via
ensureInitialized()helper to break the module evaluation cycle
Testing
- Added
fetch-lazy-init.test.tscovering customfetchFilescenarios with static imports - Verified existing cf-helpers tests continue passing
- Confirmed no regression with
advancedRouting: true(boolean) usage
Docs
- No docs update needed, this fixes the documented usage pattern that was previously broken
Closes #16956
Changes
- Replaces blanket rejection of double-encoded paths with iterative decoding in
validateAndDecodePathname(). Fixes 400 errors for legitimate double-encoded URLs like Sanity Studio's%255B/%255D(double-encoded[/]). - Ensures middleware always sees the fully decoded canonical path. Attack paths like
/api/%2561dminnow decode to/api/adminand middleware correctly blocks them with 401 instead of the previous blanket 400. - Maintains CVE-2025-66202 security protections while eliminating false positives for legitimate client router URLs.
Testing
- Added 25 tests in
test/units/util/validate-and-decode-pathname.test.tscovering iterative decoding, Sanity Studio cases, and edge cases. - Added 13 tests in
test/units/app/double-encoding-bypass.test.tsverifying middleware still blocks security attacks. - Updated 2 tests in
test/middleware.test.tsfor new behavior where middleware sees decoded paths.
Docs
- No docs update needed, this fixes broken behavior to match existing expectations.
Closes #16960
Changes
🦀
Testing
All tests should pass, tests relying on unified stuff (remark, rehype plugins, etc) got the unified markdown processor added manually
Docs
Changes
The newly introduced compressHTML: 'jsx' is now the default option in V7, requested by our steward. The Rust compiler always had support for this unlike the Go one, so it's really just switching up the default.
Testing
Tests should pass, added tests for this specific behavior
Docs
Changes
We no longer have the bandwidth to maintain this package and are generally not as interested as we once were regarding maintaining our own DB layer considering how much more mature tools like Drizzle and Prisma have become over time.
This package has also generally caused a lot of headaches for not much considering how little usage it has.
Testing
N/A
Docs
withastro/docs#13985 (ish, probably also needs a main callout if we merge this now..)
Changes
This pull request adds retries for remote image dimension inference in Astro's image service. Currently, if a transient network error happens during build, the entire build fails, which can lead to several builds failing till it succeeds in some environments, making this a QoL improvement.
I am not sure if this counts as a patch or minor change? It doesn't add new API but kinda changes how builds behave regarding images, so I defaulted to minor for the moment.
Testing
I added new unit tests in remote-probe.test.ts + integration tests in core-image-infersize.test.ts to test this plus a combination of scenarios like redirects, not allowed remote sources, 5xx errors, etc.
Docs
It's probably worth a mention in the main Images guide or Image Service API, I'm not exactly sure where the best place would be so I will wait before making a PR in docs.
Changes
astro sync was much slower with @astrojs/cloudflare because type generation started the Cloudflare dev runtime even though no requests are served. During the invocation of the temporary type generation dev server, the adapter now:
- Clears
configureServerfrom@cloudflare/vite-pluginplugins so the Cloudflare dev runtime never starts. - Sets
optimizeDeps: { noDiscovery: true, include: [] }on every environment so no dependencies are pre-bundled. devandpreviewcontinue to start the Cloudflare runtime as expected.
Testing
- Measured
astro synctype generation with the MRE:
| Adapter | astro sync |
|---|---|
| none | 37 ms |
@astrojs/node |
37 ms |
@astrojs/vercel |
36 ms |
@astrojs/netlify |
29 ms |
@astrojs/cloudflare (before) |
1.16 s |
@astrojs/cloudflare (after) |
56 ms |
Docs
- No docs needed. This is a build-time performance fix with no user-facing API change.
Closes #16332
Changes
- Fixes static file endpoints using dynamic routing where .html extensions in getStaticPaths params caused NoMatchingStaticPathFound build errors
- Modifies FetchState.#stripHtmlExtension() to only strip .html from page routes, not endpoint routes, by adding this.routeData.type === 'page' guard
- Resolves v5-to-v6 regression where endpoint routes with .html in param values would fail to build
Testing
- Added regression test preserves .html in pathname for endpoint routes with dynamic params in packages/astro/test/units/fetch/index.test.ts
- Verified fix resolves the reproduction case and produces correct dist/file.html output
Docs
- No docs update needed, this restores expected behavior from v5.
Closes #16941
Closes: #16780
Changes
- Invalidate per-route dev CSS virtual modules when CSS files change, preventing stale server-rendered inline styles after HMR.
- Treat
*.css?rawimports as SSR dependencies instead of normal style modules, so raw CSS strings rendered withset:htmlrefresh correctly. - Builds on the Astrobot fix from #16783, with additional coverage for the
?rawimport path.
Testing
- Added a package integration test that starts an Astro dev server, edits global CSS, and verifies the server-rendered inline
<style>output updates. - Added unit coverage for dev CSS virtual module invalidation.
- Added unit coverage for
*.css?rawimports going through SSR invalidation instead of the normal CSS HMR skip path. pnpm -C packages/astro run build:cipnpm -C packages/astro exec astro-scripts test "test/units/vite-plugin-hmr-reload/hmr-reload.test.ts" --strip-types
Docs
No docs changes. This fixes dev-server HMR behavior without changing user-facing APIs.
Changes
What the title says, this release fixes some bugs and adds some options to GFM and math
Testing
Tests should pass!
Docs
N/A, we don't document Sätteri options, we just link to its docs.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.4
Patch Changes
-
#16926
1b39ae8Thanks @narendraio! - PreventsApp.match()from throwing on request paths that contain an invalid percent-sequence. -
#16924
2c0bc94Thanks @astrobot-houston! - Fixes an issue where editing a client-side component (e.g. withclient:idle,client:load, etc.) caused an unnecessary full program reload of the backend during development. -
#16958
2c1d50fThanks @fkatsuhiro! - Fixes a bug where static file endpoints usinggetStaticPathswith.htmlin dynamic param values (e.g.{ path: 'file.html' }) would fail with aNoMatchingStaticPathFounderror during build. The.htmlsuffix is no longer incorrectly stripped from endpoint route pathnames. -
#16855
c610cdaThanks @astrobot-houston! - Fixes dynamic routes returning 500 "TypeError: Missing parameter" when using domain-based i18n routing in SSR. -
#16946
606c37bThanks @ematipico! - FixesAstro.routePatternto preserve original casing of dynamic parameter names from filenames. Previously, a file atsrc/pages/blog/[postId].astrowould return/blog/[postid]forAstro.routePatterndue to an internal.toLowerCase()call. It now correctly returns/blog/[postId]. -
#16720
16d49b6Thanks @thomas-callahan-collibra! - Fix an issue where dynamic routes would return the string[object Object]instead of the expected content, in certain runtimes. -
#16703
17390a6Thanks @henrybrewer00-dotcom! - Fixes styles being stripped when the project root is started with a path whose case differs from the actual filesystem case (e.g. runningastro devfromd:\dev\appwhile the folder on disk isD:\dev\app). -
#16855
c610cdaThanks @astrobot-houston! - FixesAstro.currentLocalereturning the default locale instead of the domain's locale on dynamic routes served from a mapped domain.
@astrojs/mdx@6.0.2
Patch Changes
-
#16955
9a93d68Thanks @Princesseuh! - Updates Sätteri processor to v0.8.0. See its changelog for details on bugs fixed and features added. -
Updated dependencies [
9a93d68]:- @astrojs/markdown-satteri@0.2.2
@astrojs/markdown-satteri@0.2.2
Patch Changes
- #16955
9a93d68Thanks @Princesseuh! - Updates Sätteri processor to v0.8.0. See its changelog for details on bugs fixed and features added.
Changes
Closes #16945
We create a new request and apply the correct symbols.
Testing
Added new unit tests
Docs
Changes
Fixes an issue where routePattern was transformed into lower case
Closes #16942
Testing
Tests added by the bot.
Docs
Fixes #16078
Summary
- include tsconfig-matched
.astrofiles in the TypeScript plugin project - inject the installed Astro package's
env.d.tsandastro-jsx.d.tsinto the TS plugin host - add focused unit and VS Code fixture coverage for
Astro.localsreferences
Verification
pnpm -C packages/language-tools/ts-plugin buildpnpm -C packages/language-tools/ts-plugin exec mocha --ui tdd --require tsx "test/units/**/*.test.mts"pnpm -C packages/language-tools/vscode build && pnpm -C packages/language-tools/ts-plugin test
Changes
- With View Transitions,
hashchangeevents are now emitted when navigating between hash anchors on the same page, like clicking anchor links. - The event includes
oldURLandnewURLproperties matching standard browser behavior. - This behaviour is useful for apps like documentation sites, where we want to sync the highlighted element in the sidebar to the hash.
- This follows up on #14746 and handles the browser-parity cases called out in review. Hash removal (
/long-page#sectionto/long-page) is left to the browser so Astro does not emithashchangefor cases where native navigation would not.
Testing
- Added e2e tests in
view-transitions.test.ts:- Test that
hashchangefires for same-page hash navigation - Test that
hashchangefires when navigating between hashes on the same page - Test that back/forward navigation with hashes does not double-fire events
- Test that
hashchangedoes NOT fire when removing a hash - Test that
hashchangedoes NOT fire when going back after removing a hash - Test that
hashchangedoes NOT fire for cross-page navigation
- Test that
- Updated the existing
View Transitions Ruletest so hash removal and the later back/forward path assert native navigation without a ClientRouter transition.
Docs
No docs update needed. This fixes existing ClientRouter behavior to match how hashchange works during normal browser navigation.
Description
In the starlight docs, it mentions docsSchema() can modify fields, however it is unable to modify z.enum fields The likely cause is from how ZodIntersection are treated.
Why this happens
The Zod documentation explicitly notes that intersections (a logical "AND") produce a ZodIntersection instance rather than a ZodObject.
From the Zod v4 docs:
When merging object schemas, prefer
A.extend(B)over intersections. Using.extend()will give you a new object schema, whereasz.intersection(A, B)returns aZodIntersectioninstance which lacks common object methods like pick and omit.
This limitation also affects any tooling that expects to operate on a ZodObject, including schema‑amending utilities like docsSchema().
-
Closes #3935
-
What does this PR change? Give us a brief description.
This PR replace theandintersection indocsSchemawithextendto support the modifying ofZodEnum. -
Did you change something visual? A before/after screenshot can be helpful.
Nothing was changed.
Changes
- Fixes image 404s when using
@astrojs/cloudflareadapter with static output and no explicitimageServiceconfig - Makes
normalizeImageServiceConfig()output-mode-aware: defaults to'compile'for static output,'cloudflare-binding'for server output - Moves image service normalization into
astro:config:setuphook whereconfig.outputis available - Maintains backward compatibility for all existing configurations
Testing
- Added
packages/integrations/cloudflare/test/units/image-config.test.tswith 7 test cases covering static/server defaults, explicit overrides, and compound configs - Verified existing image service test suites pass (static, compile, binding, dev endpoint, external)
Docs
- No docs update needed, this fixes the default behavior to match user expectations for static sites
Closes #16931
This PR fix ci error which cause type error from #16929
-------- Following content is cloned from #16929 --------
Changes
- Fixes HMR for action files during development. Editing files in src/actions/ now takes effect immediately without requiring a dev server restart.
- Adds
clearActions()method to dev-specific pipelines (RunnablePipeline and NonRunnablePipeline) to reset the cached resolved actions, matching the existingclearMiddleware()pattern while ensuring production paths remain completely untouched. - Wires up file change detection in the actions Vite plugin to invalidate the virtual module and send an astro:actions-updated HMR event when action files are modified.
- Adds HMR listeners in both dev app entrypoints to clear the actions cache when the event fires.
Testing
- Added unit tests in packages/astro/test/units/actions/actions-hmr.test.ts covering cache invalidation behavior.
- Verified the fix resolves the issue reported in #16913 where action endpoint returned stale values after file edits.
- Confirmed by @fkatsuhiro that the fix works as expected.
Docs
- No docs update needed, this restores expected HMR behavior for action files.
Closes #16913
Changes
App.match() decodes the request pathname with decodeURI(), which throws URIError: URI malformed when the path contains an invalid percent-sequence that is not valid UTF-8 — e.g. %C0%AF (an overlong-UTF-8 encoding of /, extremely common from automated path-traversal / .env scanners).
Because matching runs before App.render(), the error escaped the adapter's request handler as an uncaught exception (HTTP 500) that user middleware could not catch. Adapters such as @astrojs/cloudflare call app.match() directly per request, so the worker fetch handler crashed.
This adds a safeDecodeURI() helper that mirrors the existing guarded decode already used in getPathnameFromRequest(), and uses it at all matchRoute / matchAllRoutes call sites in core/app/base.ts. Malformed paths now fall back to the raw, undecoded pathname and are matched (typically a 404) instead of crashing.
Testing
Added packages/astro/test/units/app/malformed-uri.test.ts:
match()does not throw on/%C0%AFand returnsundefinedrender()does not return a 500 for the malformed path- valid routes still match
All app unit tests pass locally (the two unrelated csrf/logger unit failures reproduce on main without this change and are environmental).
Closes #16916
Changes
- Fixes a v6 regression where editing client-side components (
client:idle,client:load,client:visible, etc.) causes a full backend program reload during development - Returns empty array from
astro:hmr-reloadplugin when all processed modules exist only in the client module graph, preventing Vite's default SSR HMR propagation - Matches the existing pattern used for style modules (CSS/SCSS) that was fixed in PR #14924
Testing
- Added unit test
returns empty array for client-only modules to prevent unnecessary backend reloadscovering the specific case where all modules are client-side - Verified fix prevents
[vite] program reloadand server-side module re-execution when editing client components - Confirmed no regression for SSR-only module invalidation
Docs
- No docs update needed - this fixes a bug to restore expected v5 behavior
Closes #15951
This PR fixes two typos I spotted in the project.
Changes
- Fixes clean URLs (e.g.,
/about) not resolving to static HTML files when usingbuild.format: 'file'or'preserve'with the Node.js adapter - Adds
extensions: ['html']option to thesend()call inserve-static.tswhenbuildFormatis'file'or'preserve', enabling thesendlibrary to automatically append.htmlwhen resolving extensionless paths - Resolves issue where only the index page worked while all other prerendered pages returned 404 despite the
.htmlfiles existing on disk
Testing
- Added
packages/integrations/node/test/build-format-file.test.tswith 6 test cases covering both'file'and'preserve'formats - Tests verify clean URL resolution, explicit
.htmlextension handling, and correct file output structure - Verified existing tests continue to pass with no regressions
Docs
- No docs update needed — this fixes existing documented behavior that was broken
Closes #16570
Description
Simplifies the insertDirective method returned by context.csp (in packages/astro/src/core/fetch/fetch-state.ts).
The previous implementation had a misleading else branch:
insertDirective(payload) {
if (state?.result?.directives) {
state.result.directives = pushDirective(state.result.directives, payload);
} else {
state?.result?.directives.push(payload);
}
},Two problems:
- Dead code.
state.result.directivesis always initialized to an array (seefetch-state.ts:407—directives: manifest.csp?.directives ? [...manifest.csp.directives] : []). Because empty arrays are truthy in JS, theifbranch fires wheneverstate.resultexists. Theelseis unreachable in practice. - The
elsewould crash if reached. Calling.pushon the value that theif-condition just established is nullish would throw aTypeError. The optional-chaining?.doesn't help — it only short-circuits up throughstate?.result?, not the final.pushcall.
This PR replaces both branches with the same single-statement pattern used by the sibling insertScriptResource / insertStyleResource methods directly below it.
Testing
Pure refactor of unreachable code; behavior under all currently-reachable inputs (i.e. whenever state.result exists, which is the only state the existing if-branch matches) is unchanged.
Docs
Not user-visible; no docs changes needed.
Changeset
Included.
Changes
- Adds
astro/fetchandastro/honoto the Cloudflare adapter's server environmentoptimizeDeps.includelist. These exports were introduced with advanced routing (#16366, #16729) but never added to the pre-bundle list, causing Vite to discover them at runtime and emit anew dependencies optimized: astro/fetchwarning during dev.
Testing
- No new tests. This is a config-only change to an existing
optimizeDeps.includearray.
Docs
- No docs update needed — this is an internal dev-server optimization fix with no user-facing API change.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.3
Patch Changes
-
#16900
17a0fbdThanks @ocavue! - Bumpsdevaluedependency to v5.8.1 -
#16016
0d85e1bThanks @felmonon! - Fix a false positive in the dev toolbar accessibility audit for anchors with text inside closed<details>elements. -
#16911
79c6c46Thanks @astrobot-houston! - Fixes a bug whereexperimental.advancedRoutingwithastro/honohandlers threwTypeError: Cannot read properties of undefined (reading 'route')for unmatched routes instead of rendering the custom 404 page. -
#16899
239c469Thanks @matthewp! - Fixes a false "does not call the middleware() handler" warning when usingastro()in a customsrc/app.tsand the first request is a redirect route. -
#16887
493acdbThanks @astrobot-houston! - FixesredirectToDefaultLocalenot working after the Advanced Routing refactoring. -
#16908
ef53ab9Thanks @florian-lefebvre! - Improves optimized fallbacks generation when using the Fonts API by using better metrics for bold variants
@astrojs/cloudflare@13.6.1
Patch Changes
-
#16914
4bdd240Thanks @matthewp! - Fixesastro/fetchandastro/honobeing discovered at runtime during dev instead of pre-bundled -
#16693
9e6edc2Thanks @ArmandPhilippot! - Fixes a link in the documentation specifying where to open an issue for the adapter. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/netlify@7.0.12
Patch Changes
-
#16693
9e6edc2Thanks @ArmandPhilippot! - Fixes a link in the documentation specifying where to open an issue for the adapter. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/node@10.1.3
Patch Changes
-
#16922
7dce185Thanks @astrobot-houston! - Fixes prerendered pages returning 404 when usingbuild.format: 'file'orbuild.format: 'preserve'with the Node adapter in standalone mode.Previously, clean URLs like
/aboutwould fail to resolve toabout.htmlon disk, because the static file handler only supported the defaultdirectoryformat (about/index.html). Now the handler correctly resolves clean URLs to.htmlfiles when the build format produces them.
@astrojs/preact@5.1.5
Patch Changes
@astrojs/react@5.0.7
Patch Changes
Changes
- Fixes
experimental.advancedRoutingwithastro/honohandlers crashing on unmatched routes instead of rendering the 404 page - Replaces broken component-name comparison in
FetchState.#resolveRouteData()with the existinggetCustom404Route()helper that matches by route path - Ensures the
routeData-always-set invariant is maintained, preventingTypeError: Cannot read properties of undefined (reading 'route')
Testing
- Added
'falls back to the 404 route when no route matches'test to verifyrouteDataresolves to the/404route for unmatched requests - Added
'renders the 404 page for unmatched routes instead of throwing'test to verify the fullpages()pipeline renders HTTP 404 with custom 404 content instead of throwing
Docs
- No docs update needed, this fixes broken functionality in an experimental feature
Closes #16907
Changes
Close #16744
During running astro dev server, getStaticPaths() results were cached indefinitely, so updates to the content collections API (or any external data source) were never reflected without restarting the dev server. This fix clears the route cache on every request in dev mode, ensuring getStaticPaths() always re-runs with the latest data.
Testing
Before implementation
After implementation
Docs
Changes
- Addresses withastro/roadmap#1366
- Generating fallbacks for bold variants is now improved by using better metrics, eg. "Arial Bold" instead of "Arial"
- I also tried Italic specific variants but metrics are almost always identical to the normal variant so I removed them
- Made with the help of AI
Testing
Updated
Docs
Changeset, no docs
Changes
AstroHandler.handle()now eagerly sets allPipelineFeaturesbits so the one-shotwarnMissingFeaturescheck never fires a false positive.- Fixes false "does not call the middleware() handler" warning when using
astro()in a customsrc/app.tsand the first request is a redirect.
Testing
- Added unit test: sends a redirect through
astro()with middleware configured and asserts all feature bits are set. - Verified the test fails without the fix (
usedFeatures= 3, only redirects|sessions) and passes with it (63, all features).
Docs
- No docs needed.
Changes
The @astrojs/vercel test suite is occasionally failing in CI with an anonymous suite-level timeout (e.g. this run):
✖ node_modules/.astro/test.mjs (60004ms) 'test timed out after 60000ms'
ℹ tests 40 · pass 36 · cancelled 1 · skipped 3
This PR makes two changes:
- Raise the suite-level
--timeoutfrom 60s to 100s, giving the cumulative run more headroom. - Add an explicit
{ timeout: 30000 }to every test. A single stuck test now fails fast and by name at 30s instead of silently consuming the shared suite budget and surfacing as the anonymoustest.mjstimeout. So if we see a timeout error next time, we know exactly which test causes the timeout.
Testing
Green CI.
Docs
N/A
Description
It has now technically been more than 3 days since we added proper releases with version tags to withastro/automation, so we can now remove the minimumReleaseAge workaround for withastro/automation updates in Renovate.
This PR also updates the reusable workflow references from the main branch comment to the v1.0.0 version tag comment used by Renovate.
Description
- Closes #3912
- Closes #3916
- Updates the minimum version of Pagefind bundled with Starlight to 1.5.2
- Adds support for the new ranking options added in Pagefind v1.5
- In the end I decided not to make the config a
z.looseObject()as discussed in #3912 (comment). The issue with loose objects is you don’t get as good feedback for typos and I think maybe new properties are rare enough that we can keep up-to-date while providing clear diagnostics for people who typo a key name?
Changes
- Fixes custom elements losing their
slotHTML attributes when rendered through MDX, restoring proper Shadow DOM slot distribution - Adds a guard in
extractSlots()to skip slot extraction when the parent is a custom HTML element (contains hyphen) - Custom elements still route through the renderer pipeline for SSR while preserving standard HTML slot attributes
Testing
- Added
'preserves slot attribute on children of custom elements'test case topackages/astro/test/units/render/jsx-custom-elements.test.ts - Verified all existing custom element tests pass (renderer routing, fallback behavior)
- Confirmed MDX slots and children tests remain unaffected
Docs
- No docs update needed, as this fixes broken behavior to match documented web component standards.
Closes #16891
Changes
Integrations that are not updated yet for 6.4.0 still read the plugins from the legacy keys, so we need to keep them on the config. At worse, the plugins get dropped, at very bad, the integration doesn't expect the config to not be there anymore and crashes.
Caution, the fix is imo super goofy, but I couldn't find a better way that didn't break some other usages, hopefully anyway this code path gets hits less and less as we go forward
Testing
Added unit tests for the config folding
Docs
N/A
Description
While working on #3926, I stumbled upon a todo comment related to Node.js version that this PR addresses as the minimum Node.js version supported by Astro is now 22.12.0 and the windows option for fileURLToPath() is available since 22.1.0.
Changes
Replace the length > 0 guard and manual loop in renderAllHeadContent with a single join('') call. An empty array already returns '' from join, so the guard and loop are unnecessary.
Testing
No testing required. Behaviour is identical in all cases.
Docs
This change does not affect docs.
Changes
- What does this change?
- Be short and concise. Bullet points can help!
- Before/after screenshots can help as well.
- Don't forget a changeset! Run
pnpm changeset. - See https://contribute.docs.astro.build/docs-for-code-changes/changesets/ for more info on writing changesets.
Testing
Docs
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.2
Patch Changes
-
#16889
b94bcfdThanks @Princesseuh! - Fixes aplugins is not iterablecrash when using a pre-6.0@astrojs/mdxalongside integrations (e.g. Starlight) that setmarkdown.remarkPlugins,markdown.rehypePlugins, ormarkdown.remarkRehype. -
#16878
b9f6bb9Thanks @fkatsuhiro! - Fixes an issue where on-demand (SSR) dynamic routes would return 404 when a prerendered dynamic route with the same URL pattern was sorted first alphabetically. In production builds with@astrojs/nodeadapter, if[a_prebuild].astro(prerender=true) came before[b_ssr].astroalphabetically, requests to URLs not in the prerendered route's static paths would 404 instead of falling through to the SSR route. The fix adds fallthrough logic so that when a prerendered dynamic route matches but can't serve the request, Astro tries subsequent matching routes.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.1
Patch Changes
- #16883
eeb064cThanks @Princesseuh! - Restores theastro/jsx/rehype.jsentry point so that older versions of@astrojs/mdxcontinue to work when used with Astro 6.x. This entry point will be removed in Astro 7.0.
@astrojs/mdx@6.0.1
Patch Changes
- Updated dependencies [
eeb064c]:- @astrojs/markdown-satteri@0.2.1
@astrojs/markdown-satteri@0.2.1
Patch Changes
- #16883
eeb064cThanks @Princesseuh! - Fixes missing provenance information on the publish
Changes
In f732f3c I moved this rehype plugin from Astro to the MDX integration, but previous versions of MDX have no reasons to not work, I think, so restore it until Astro 7
Testing
N/A
Docs
N/A
Changes
In renderAllHeadContent, each group of head elements — styles, links and scripts — is joined with a hardcoded '\n' separator. This ignores the existing result.compressHTML flag that controls whitespace in other parts of the rendering pipeline.
Switch to a conditional separator so compressed output omits newlines between head elements, consistent with doctype injection and other compressHTML-aware rendering paths.
Testing
No testing required. The change only affects whitespace in rendered output and is gated behind the existing compressHTML flag, which is already covered by the test suite.
Docs
This change does not affect docs. compressHTML behaviour is unchanged — this fix brings head element rendering in line with how it is already documented.
Description
Follow-up to this comment, this PR configures Renovate to manage Node.js versions in our CI workflows so we can pin them, have some consistency across all Node.js versions used in our workflows, and respect our minimumReleaseAge configuration.
- All workflows use the latest Node.js LTS version (respect the
minimumReleaseAgeconfiguration) - Except
.github/workflows/ci.ymlwhich uses the latest patch of the minimum Node.js major supported by Astro (matches the current behavior of the CI workflow except versions are now pinned and respect theminimumReleaseAgeconfiguration)
I also configure Node.js Renovate PRs to be roughly every two weeks only, so we don't get too many PRs but open to feedback on this.
I validated the config using npx --yes --package renovate -- renovate-config-validator.
Improved and translated missing strings
Changes
- Fixes production builds where SSR dynamic routes fail when prerendered dynamic routes sort alphabetically first
- Adds Router.matchAll() method to find all routes matching a pathname instead of just the first
- Updates production router logic to fall through from prerendered dynamic routes to SSR routes when the static path doesn't match
- Preserves existing behavior for static prerendered routes (exact paths)
Testing
- Added regression test for issue #16834 in packages/astro/test/units/fetch/index.test.ts
- Added unit tests for Router.matchAll() in packages/astro/test/units/routing/router-match.test.ts
- Verified all existing routing, fetch, app, and ssr-prerender tests continue passing
Docs
No docs update needed, as this fixes broken documented behavior rather than changing the API
Closes #16834
Changes
- Removes the
experimental.advancedRoutingflag and enables the feature by default. fetchFileis now a top-level config option (string | null, default'fetch') instead of being nested underexperimental.advancedRouting.- The vite plugin always resolves
src/fetch.tsif present — no flag needed. fetchFile: nulldisables the entrypoint.
Testing
- No new tests. Existing tests continue to pass with the flag removed.
Docs
Description
This PR adds support for Astro 6.4 and the Sätteri processor introduced in that version. It does so by checking what (if any) markdown.processor is set, and adding plugins accordingly to that.
Versions earlier than 6.4 are also still supported, when markdown.processor isn't present, Starlight pushes plugins to the now deprecated remarkPlugins / rehypePlugins options instead.
This PR does not remove the hard dependency on @astrojs/markdown-remark, it would be possible to with similar patterns as the ones done for Sätteri, but I figured that it wasn't up to me to decide that.
- Closes #3934
Changes
The merge-main-to-next workflow can not work if there are conflicts in any of:
package.json:pnpm/action-setupcan't readpackageManagerfield frompackage.jsonpnpm-lock.yaml:pnpm install --frozen-lockfileerrors out.flue/*.ts:flueitself can't parse its own workflow
A broken me can't fix a broken you.
This PR splits them into two Git checkouts:
| path | role | |
|---|---|---|
| 🩺 Doctor | runtime/ |
clean checkout, hosts flue CLI and .flue/ configs |
| 🤕 Patient | operand/ |
post-merge tree, may be full of conflict markers |
Testing
Let's merge this, manually trigger this workflow from the GitHub web UI, and see if it works.
Docs
Internal CI change. No docs needed.
Changes
- Adds a
session: falseconfig option that opts the project out of session support entirely. - Introduces an indirection through
core/session/provider.tsthat the newastro:session-providerVite plugin swaps for a disabled stub whensession: false, so Rollup can tree-shakecore/session/runtime.tsout of the SSR bundle. - Registers a throw-on-access shim under
session: falsesoAstro.session(andcontext.session) surfaces a clearSessionDisabledErrorinstead of beingundefined, per maintainer guidance in withastro/roadmap#1352 (comment). - Adapter changes (
@astrojs/cloudflare,@astrojs/netlify,@astrojs/node): skip auto-wiring the default session driver whensession: false.
import { defineConfig } from 'astro/config';
export default defineConfig({
session: false,
});Projects that do not set session: false see no behavior change.
Closes the design intent of withastro/roadmap#1352.
Why
Astro core unconditionally bundles the session runtime (AstroSession + unstorage) into SSR output, and adapters auto-wire a default driver. There is no first-class opt-out today. For projects that never use sessions, this carries cold-start parse cost (especially on serverless/edge), latent attack surface in cookie/session-storage code paths, and a larger dependency tree.
A note on the RFC: it was written before the recent Pipeline Features / virtual:astro:session-driver refactor. The phrase "core/render-context.js has an unconditional import { AstroSession }" is no longer literally true on main — render-context.ts does not import the session runtime any more. The actual remaining boundary is at core/session/handler.ts:3, which is statically imported by core/fetch/index.ts, core/routing/handler.ts, and core/errors/default-handler.ts. That static import keeps runtime.ts and unstorage in the SSR bundle even though their work is gated at runtime. The fix in this PR replaces that static-import boundary with a virtual-module indirection.
Design
- The three hot-path callers (
core/fetch/index.ts,core/routing/handler.ts,core/errors/default-handler.ts) now importprovideSessionfromcore/session/provider.tsinstead ofcore/session/handler.tsdirectly. provider.tsis a one-line re-export fromhandler.ts(the default case). It is a real file so Node's ESM loader resolves it in environments without Vite (library tooling, test loaders).- A new Vite plugin
astro:session-providerintercepts resolution ofprovider.jsand redirects it toprovider-disabled.ts(a tiny stub that does not importruntime.tsorunstorage) whensettings.config.session === false. This is structurally similar to how the existingvirtual:astro:session-driverplugin chooses driver code, just applied to a real file path because the static-import boundary lives inside Astro's own source rather than user-emitted code. routing/handler.tsrunsprovideSessionwhen sessions are configured or explicitly disabled, so the throw-on-access shim is registered in the disabled case andAstro.sessionaccess surfacesSessionDisabledErrorinstead of returningundefined.sessionsDisabled: trueis added toSSRManifestso the routing handler can distinguish "no driver configured" from "explicitly disabled."- Each adapter that auto-wires a default driver gains a short-circuit at the top of its session block:
if (session !== false) { /* auto-wire */ }.
Decisions called out
- Throw on access vs. silent no-op. Throw — explicit maintainer guidance in #1352.
Astro.session.Xundersession: falsesurfacesSessionDisabledErrorwith a hint pointing back at the config. - Tree-shake via Vite
resolveIdswap vs.defineflag. TheresolveIdswap, structurally similar to the existingvirtual:astro:session-driverpattern. Adefine/early-return approach does not remove the static import ofAstroSessionand so does not actually dropunstoragefrom the bundle. Real-file (vs.virtual:) because the indirection lives in Astro's own source —virtual:schemes don't survive throughastro-scripts buildintodist/and can't be loaded by Node's ESM loader in non-Vite contexts (library tooling, thenode:testloader used here). experimental.cacheinteraction. Out of scope.experimental.cachealso usesunstorage. If a user setssession: falseand enablesexperimental.cache,unstoragestays in the bundle. Treating them independently to keep this PR focused.
Testing
- Unit (
test/units/sessions/session-false.test.ts):SessionSchemaparsesfalse, rejects other falsy values.sessionConfigToManifest(false)returnsundefined;sessionsDisabled(false)returnstrue.- Disabled provider registers a
'session'key whosecreate()throwsSessionDisabledError.
- Integration (
test/session-false.test.ts):- Routes that never read
Astro.sessionare unaffected. - Routes that read
Astro.sessionundersession: falseget a 500 (the throw propagates). - The built SSR output contains no
createStorage(top-levelunstorageexport) and noclass AstroSession.
- Routes that never read
Bundle-size measurement
Built test/fixtures/session-false with the test adapter, with and without session: false:
| Mode | Files | Total bytes | unstorage in bundle |
AstroSession class in bundle |
|---|---|---|---|---|
default (no session: false) |
8 | 408,348 (398.8 KiB) | yes | yes |
session: false |
8 | 395,490 (386.2 KiB) | no | no |
| delta | — | −12,858 (−12.6 KiB, −3.1%) | — | — |
This is the floor of the savings — measured against a fixture where no adapter auto-wires a driver. On Cloudflare/Netlify/Node, the adapter would otherwise also wire its driver (Cloudflare KV / Netlify Blobs / fs-lite) into the bundle, so the real-world delta on those targets is larger.
Docs
Will open a follow-up PR against withastro/docs covering:
- The
session: falseoption on theastro.configreference page. - A short note on the session API page (
Astro.session) describing the disabled-mode error.
The Astro core changeset captures user-facing wording.
Out of scope / non-goals
- Removing
experimental.cache'sunstoragedependency (see above). - Vercel/Deno adapters: neither auto-wires a default session driver today, so no adapter change is needed there.
- Dev-server behavior for
session: falseis the same as production (the virtual module routes identically in both).
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/mdx@6.0.0
Major Changes
-
#16848
f732f3cThanks @Princesseuh! - Adds a newmarkdown.processorconfiguration option, allowing you to choose an alternative Markdown processor.Websites with many Markdown/MDX files tend to be slow to build because the unified ecosystem (e.g., remark, rehype) is slow to process. This feature introduces the ability to replace this part of the build pipeline with another processor.
The default processor is
unified(). This means that existing configurations remain unchanged and your remark/rehype plugins continue to work.// astro.config.mjs import { defineConfig } from 'astro/config'; import { unified } from '@astrojs/markdown-remark'; import remarkToc from 'remark-toc'; export default defineConfig({ markdown: { processor: unified({ remarkPlugins: [remarkToc], }), }, });
In addition to this new configuration option, Astro provides a new alternative processor based on Rust: Sätteri. You can choose to use it now by installing
@astrojs/markdown-satteri, importing thesatteri()processor, and adapting your existing configuration:// astro.config.mjs import { defineConfig } from 'astro/config'; import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri({ features: { directive: true }, }), }, });
This processor does not support the remark and rehype plugins. This means you may need to convert them to MDAST or HAST plugins to retain your current functionality.
The existing top-level
markdown.remarkPlugins,markdown.rehypePlugins,markdown.remarkRehype,markdown.gfm, andmarkdown.smartypantsoptions still work, but are now deprecated and will be removed in a future major update. The matchingremarkPlugins,rehypePlugins, andremarkRehypeoptions on the MDX integration are also deprecated for the same reason. To anticipate their removal, move them ontounified({...})(or your preferred plugin processor) :// astro.config.mjs import { defineConfig } from 'astro/config'; import remarkToc from 'remark-toc'; import rehypeSlug from 'rehype-slug'; + import { unified } from '@astrojs/markdown-remark'; export default defineConfig({ markdown: { + processor: unified({ + remarkPlugins: [remarkToc], + rehypePlugins: [rehypeSlug], + remarkRehype: true, + gfm: true, + smartypants: true, + }), - remarkPlugins: [remarkToc], - rehypePlugins: [rehypeSlug], - remarkRehype: true, - gfm: true, - smartypants: true, }, });For more information on enabling and using this feature in your project, see our Markdown guide. To give feedback on this new Rust processor, see the Native Markdown / MDX parsing and processing RFC.
Minor Changes
-
#16848
f732f3cThanks @Princesseuh! - Adds support for using@astrojs/markdown-satterito parse.mdxfiles.// astro.config.mjs import { defineConfig } from 'astro/config'; import mdx from '@astrojs/mdx'; import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri({ features: { directive: true }, }), }, integrations: [mdx()], });
Note that the
recmaPluginsoption is not supported when using Sätteri as your MDX processor. If you would like to use Sätteri for Markdown files, but still use Unified for MDX, you can pass a different Markdown processor to the MDX integration:// astro.config.mjs import { defineConfig } from 'astro/config'; import mdx from '@astrojs/mdx'; import { satteri } from '@astrojs/markdown-satteri'; import { unified } from '@astrojs/markdown-remark'; import myPlugin from './my-recma-plugin.js'; export default defineConfig({ markdown: { processor: satteri({ features: { directive: true }, }), }, integrations: [ mdx({ recmaPlugins: [myPlugin], processor: unified(), }), ], });
Patch Changes
- Updated dependencies [
f732f3c,f732f3c,f732f3c]:- @astrojs/internal-helpers@0.10.0
- @astrojs/markdown-remark@7.2.0
- @astrojs/markdown-satteri@0.2.0
astro@6.4.0
Minor Changes
-
#16468
4cff3a1Thanks @matthewp! - Adds a newpreserveBuildServerDiradapter featureAdapters can now set
preserveBuildServerDir: truein their adapter features to keep thedist/server/directory structure for static builds, mirroring the existingpreserveBuildClientDiroption. This is useful for adapters that require a consistentdist/client/anddist/server/layout regardless of build output type.setAdapter({ name: 'my-adapter', adapterFeatures: { buildOutput, preserveBuildClientDir: true, preserveBuildServerDir: true, }, });
-
#16848
f732f3cThanks @Princesseuh! - Adds a newmarkdown.processorconfiguration option, allowing you to choose an alternative Markdown processor.Websites with many Markdown/MDX files tend to be slow to build because the unified ecosystem (e.g., remark, rehype) is slow to process. This feature introduces the ability to replace this part of the build pipeline with another processor.
The default processor is
unified(). This means that existing configurations remain unchanged and your remark/rehype plugins continue to work.// astro.config.mjs import { defineConfig } from 'astro/config'; import { unified } from '@astrojs/markdown-remark'; import remarkToc from 'remark-toc'; export default defineConfig({ markdown: { processor: unified({ remarkPlugins: [remarkToc], }), }, });
In addition to this new configuration option, Astro provides a new alternative processor based on Rust: Sätteri. You can choose to use it now by installing
@astrojs/markdown-satteri, importing thesatteri()processor, and adapting your existing configuration:// astro.config.mjs import { defineConfig } from 'astro/config'; import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri({ features: { directive: true }, }), }, });
This processor does not support the remark and rehype plugins. This means you may need to convert them to MDAST or HAST plugins to retain your current functionality.
The existing top-level
markdown.remarkPlugins,markdown.rehypePlugins,markdown.remarkRehype,markdown.gfm, andmarkdown.smartypantsoptions still work, but are now deprecated and will be removed in a future major update. The matchingremarkPlugins,rehypePlugins, andremarkRehypeoptions on the MDX integration are also deprecated for the same reason. To anticipate their removal, move them ontounified({...})(or your preferred plugin processor) :// astro.config.mjs import { defineConfig } from 'astro/config'; import remarkToc from 'remark-toc'; import rehypeSlug from 'rehype-slug'; + import { unified } from '@astrojs/markdown-remark'; export default defineConfig({ markdown: { + processor: unified({ + remarkPlugins: [remarkToc], + rehypePlugins: [rehypeSlug], + remarkRehype: true, + gfm: true, + smartypants: true, + }), - remarkPlugins: [remarkToc], - rehypePlugins: [rehypeSlug], - remarkRehype: true, - gfm: true, - smartypants: true, }, });For more information on enabling and using this feature in your project, see our Markdown guide. To give feedback on this new Rust processor, see the Native Markdown / MDX parsing and processing RFC.
Patch Changes
-
#16468
4cff3a1Thanks @matthewp! - Skips the static preview server when an adapter provides its ownpreviewEntrypoint, allowing the adapter to handle both static and dynamic routes -
#16811
e0e26dbThanks @matthewp! - FixesX-Forwarded-HostandX-Forwarded-Protoheaders being ignored when set in a customsrc/app.tsfetch handler before creatingFetchState -
#16468
4cff3a1Thanks @matthewp! - Fixes the static preview server to respectpreserveBuildClientDir, serving files frombuild.clientinstead ofoutDirwhen the adapter requires it -
#16770
1e2aa11Thanks @matthewp! - Fixes a race condition where the Vite dep optimizer could lose React dependencies in dev mode when using Astro Actions -
#16468
4cff3a1Thanks @matthewp! - Exempts internal routes (e.g. server islands) fromgetStaticPaths()validation, fixing server island rendering on static sites -
#16468
4cff3a1Thanks @matthewp! - Fixes preview for static sites that contain non-prerendered routes. Previously, the preview command ignored SSR routes discovered during route scanning and always used the static preview server. -
Updated dependencies [
f732f3c,f732f3c]:- @astrojs/internal-helpers@0.10.0
- @astrojs/markdown-remark@7.2.0
@astrojs/cloudflare@13.6.0
Minor Changes
-
#16729
01aa164Thanks @matthewp! - Adds@astrojs/cloudflare/fetchand@astrojs/cloudflare/honoexports for composing Cloudflare-specific setup with Astro's advanced routing handlers.@astrojs/cloudflare/fetchFor use with
astro/fetchin a custom fetch handler:import { astro, FetchState } from 'astro/fetch'; import { cf } from '@astrojs/cloudflare/fetch'; export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { const state = new FetchState(request); const asset = await cf(state, env, ctx); if (asset) return asset; return astro(state); }, };
@astrojs/cloudflare/honoFor use with
astro/honoas Hono middleware:import { Hono } from 'hono'; import { actions, middleware, pages, i18n } from 'astro/hono'; import { cf } from '@astrojs/cloudflare/hono'; const app = new Hono<{ Bindings: Env }>(); app.use(cf()); app.use(actions()); app.use(middleware()); app.use(pages()); app.use(i18n()); export default app;
Both handlers configure SESSION KV bindings, static asset serving via the ASSETS binding,
locals.cfContext, client address,waitUntil, and prerendered error page fetch.
Patch Changes
-
#16868
f9bae95Thanks @helio-cf! - Fixes user options passed tocloudflare({...})(remoteBindings,inspectorPort,persistState,configPath,auxiliaryWorkers) being silently ignored duringastro preview. The adapter now resolves the full@cloudflare/vite-pluginconfig once at integration setup time and reuses that single resolved value across the dev/build plugin, the prerenderer's preview server, and theastro previewentrypoint, so user options can no longer be dropped at one of the call sites. -
#16468
4cff3a1Thanks @matthewp! - Fixes static Cloudflare builds with server islands or image endpoints that failed at preview time due to mismatched output directories. -
Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
- @astrojs/underscore-redirects@1.0.3
@astrojs/internal-helpers@0.10.0
Minor Changes
- #16848
f732f3cThanks @Princesseuh! - Addsmarkdown,frontmatter, andshikihelper modules, shared by Astro's content pipeline.
@astrojs/markdown-remark@7.2.0
Minor Changes
-
#16848
f732f3cThanks @Princesseuh! - Adds a newmarkdown.processorconfiguration option, allowing you to choose an alternative Markdown processor.Websites with many Markdown/MDX files tend to be slow to build because the unified ecosystem (e.g., remark, rehype) is slow to process. This feature introduces the ability to replace this part of the build pipeline with another processor.
The default processor is
unified(). This means that existing configurations remain unchanged and your remark/rehype plugins continue to work.// astro.config.mjs import { defineConfig } from 'astro/config'; import { unified } from '@astrojs/markdown-remark'; import remarkToc from 'remark-toc'; export default defineConfig({ markdown: { processor: unified({ remarkPlugins: [remarkToc], }), }, });
In addition to this new configuration option, Astro provides a new alternative processor based on Rust: Sätteri. You can choose to use it now by installing
@astrojs/markdown-satteri, importing thesatteri()processor, and adapting your existing configuration:// astro.config.mjs import { defineConfig } from 'astro/config'; import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri({ features: { directive: true }, }), }, });
This processor does not support the remark and rehype plugins. This means you may need to convert them to MDAST or HAST plugins to retain your current functionality.
The existing top-level
markdown.remarkPlugins,markdown.rehypePlugins,markdown.remarkRehype,markdown.gfm, andmarkdown.smartypantsoptions still work, but are now deprecated and will be removed in a future major update. The matchingremarkPlugins,rehypePlugins, andremarkRehypeoptions on the MDX integration are also deprecated for the same reason. To anticipate their removal, move them ontounified({...})(or your preferred plugin processor) :// astro.config.mjs import { defineConfig } from 'astro/config'; import remarkToc from 'remark-toc'; import rehypeSlug from 'rehype-slug'; + import { unified } from '@astrojs/markdown-remark'; export default defineConfig({ markdown: { + processor: unified({ + remarkPlugins: [remarkToc], + rehypePlugins: [rehypeSlug], + remarkRehype: true, + gfm: true, + smartypants: true, + }), - remarkPlugins: [remarkToc], - rehypePlugins: [rehypeSlug], - remarkRehype: true, - gfm: true, - smartypants: true, }, });For more information on enabling and using this feature in your project, see our Markdown guide. To give feedback on this new Rust processor, see the Native Markdown / MDX parsing and processing RFC.
Patch Changes
- Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
@astrojs/markdown-satteri@0.2.0
Minor Changes
-
#16848
f732f3cThanks @Princesseuh! - Adds@astrojs/markdown-satteri, a Markdown processor based on Sätteri, a fast Markdown pipeline written in Rust.Sätteri is much faster than the default Remark-based processor, and supports a wide range of Markdown features out of the box, without requiring additional plugins. In the future, we plan to make this the default Markdown processor in Astro.
npm install @astrojs/markdown-satteri
// astro.config.mjs import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), }, });
Note that this processor currently does not support Prism syntax highlighting, and require using
syntaxHighlight: 'shiki'or disabling syntax highlighting altogether for now.
Patch Changes
- Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
@astrojs/markdoc@1.0.6
Patch Changes
-
#16848
f732f3cThanks @Princesseuh! - Removes@astrojs/markdown-remarkfrom@astrojs/markdoc's dependencies in favour of Astro's internal markdown utilities now that Astro's Markdown support is processor agnostic. -
Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
@astrojs/netlify@7.0.11
Patch Changes
- Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
- @astrojs/underscore-redirects@1.0.3
@astrojs/node@10.1.2
Patch Changes
- Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
@astrojs/preact@5.1.4
Patch Changes
- Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
@astrojs/react@5.0.6
Patch Changes
- Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
@astrojs/vercel@10.0.8
Patch Changes
- Updated dependencies [
f732f3c]:- @astrojs/internal-helpers@0.10.0
User options like remoteBindings, inspectorPort, persistState, configPath, and auxiliaryWorkers were silently dropped during astro preview because the preview entrypoint only received the adapter-computed half of the config.
Fixes #16867
Changes
- Fixes user options passed to
cloudflare({...})(remoteBindings,inspectorPort,persistState,configPath,auxiliaryWorkers) being silently dropped duringastro preview. The preview entrypoint was only receiving the adapter-computed half of the@cloudflare/vite-pluginconfig viaglobalThis. - Restructures the adapter to resolve the full
@cloudflare/vite-pluginconfig exactly once atastro:config:setuptime. All three call sites (dev/build plugin, prerenderer preview server,astro previewentrypoint) now spread a single pre-mergedcfPluginConfig. The unmerged adapter-only half is a localconstthat goes out of scope, so no future call site can accidentally drop user options. - Same bug class as #16705 (fixed for the prerenderer in #16707). This closes the equivalent gap on the
astro previewpath and structurally prevents it from recurring. - Changeset included.
Testing
I've tested it with local npm patch, let me know if we have some e2e tests for these
Docs
No docs added, just a fix
Changes
- moved some files inside unit/ because they are unit tests
- moved many tests to unit tests
- removed those files that don't have assertions anymore (and related fixtures)
- moved some assertions to unit test
Testing
Green CI
Docs
Changes
This PR fixes an outdated link in README.md of the Partytown integration.
Changes
- Include
modin the SSR-branch cache entry incallGetStaticPathsso the newcached.mod === modfast-path actually hits on subsequent SSR requests for the same component.
Before this patch, the SSR branch wrote { ...cached, staticPaths: entry } without mod. The fast-path added in #16776 (cached?.staticPaths && cached.mod === mod) therefore always failed on the second request, control fell back into the SSR branch, routeCache.set() ran again, and RouteCache.set() emitted Internal Warning: route cache overwritten at the top of the function — once per dynamic-route component per worker isolate, on every request after the first, in production.
The static branch already stores mod so it was unaffected.
Closes #16863.
Testing
Added a unit test in packages/astro/test/units/routing/getstaticpaths-cache.test.ts:
does not log "route cache overwritten" on repeated SSR requests for the same module
It constructs a RouteCache in production mode with a capturing logger destination, calls callGetStaticPaths three times with ssr: true, prerender: false, the same mod, and asserts no route cache overwritten message was logged. The existing HMR and prerendered-route tests continue to pass.
Docs
No docs changes — fixes an internal warning, no public API change.
Description
This PR bumps the Node.js version used in the Lunaria workflow to the latest Node.js 22 version like in our other workflows instead of the specific 22.12.0 version.
We recently updated to pnpm 11 which requires Node.js 22.13 or higher and would error out with the following message when running the workflow:
ERROR: This version of pnpm requires at least Node.js v22.13
The current version of Node.js is v22.12.0
Changes
Fixes Astro's dev image endpoint when adapters use a prerender-only dev server, such as Cloudflare static output with a custom image service.
The internal image endpoint is now treated as prerendered in dev so the prerender handler can serve it, while preserving its query string for image transform parameters.
Refs #16752
Changes
- Content-collection markdown images no longer emit an empty
srcset=""attribute when using the noop/passthrough image service. TheupdateImageReferencesInBodyfunction inpackages/astro/src/content/runtime.tsnow conditionally includessrcsetonly when the image service returns actual values (image.srcSet.values.length > 0), matching the existing guard inpackages/astro/src/vite-plugin-markdown/images.ts.
Testing
- Added
test/passthrough-image-content.test.ts— builds a content collection with a local markdown image usingpassthroughImageService, assertssrcsetis absent and the imagesrcis valid. - Existing
astro-assets.test.ts(sharp service) andpassthrough-image-service.test.tsboth pass with no regressions.
Docs
- No docs update needed — this is a validation fix for existing behavior.
Changes
- The
getActions()method inbase-pipeline.tswas missing an assignment tothis.resolvedActions. - This caused the actions module to be dynamically re-imported on every single action invocation instead of returning the cached module.
- This PR adds the missing assignment to properly cache the result and improve performance on action requests, matching the existing caching pattern used in
getMiddleware()andgetSessionDriver(). - Included a changeset via
pnpm changeset.
Testing
No new tests were added. Because this is purely an internal backend caching optimization, it doesn't change the output or behavior of the pipeline, only the execution time of repeated action calls. A standard reproduction link is not applicable here. The bug and fix can be verified statically by inspecting the previous code:
} else if (this.actions) {
return this.actions(); // <- missing assignment to this.resolvedActions
}Docs
No docs added. This is a purely internal server-side performance optimization. It does not affect any public APIs, configuration options, or user-facing behavior, so no documentation updates are required.
Changes
Adds a new Markdown processor based on https://github.com/bruits/satteri, it's fast! It supports all of Astro's features, apart from Prism syntax highlighting in this first version.
Testing
Added tests to the package
Docs
Changes
The markdown.processor part of withastro/roadmap#1364, please see the RFC for details.
Testing
Updated tests that relied on specific remark plugins and what not. Added tests using a custom processor to make sure it works from e2e
Docs
Changes
- Upgrades
@flue/cliand@flue/runtimefrom 0.3.10 to 0.8.0, migrating to the newcreateAgent()+run()API and the agent/workflow directory split. - Scopes
GITHUB_TOKENto read-only (contents: read) in all flue workflow permissions blocks. The agent sandbox only receives this read-only token forghCLI reads (issue views, searches, etc.). - Moves all write operations (git push, post comment, add/remove labels, create PR) out of the agent sandbox and into orchestrator-level code that uses
FREDKBOT_GITHUB_TOKENdirectly. The agent never sees the write-capable token. - Adds
gitPush()andgitDeleteBranch()helpers in.flue/lib/github.tsthat run viachild_process.execoutside the sandbox with the privileged token. - Splits
merge-fix/github.tsintoreadHeaders()(base token) andwriteHeaders()(privileged token) so API reads and writes use separate credentials.
The security model is now: the agent can read anything via gh but cannot write to the repo, post comments, or push code on its own. All mutations go through controlled orchestrator functions.
Testing
- No test changes — these are internal CI agent infrastructure changes.
Docs
- No docs update needed — no user-facing changes.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to 5-legacy, this PR will be updated.
Releases
astro@5.18.2
Patch Changes
Fixes #16838.
Changes
The problem (#16838): @astrojs/sitemap writes per-URL <lastmod> into the child sitemaps but never into the <sitemap> entries of sitemap-index.xml. The index gets a <lastmod> only if you set the global lastmod option, and then every entry carries the same date. So the index cannot tell a crawler which child sitemap actually changed — even though the freshness data is already computed and sitting in the child sitemaps.
This PR derives each index entry's <lastmod> from the child sitemap it points to:
- Each
<sitemap>entry is stamped with the most recent<lastmod>among the URLs that land in that file. URLs are written in source order,limitper file, so the date is computed fromitems.slice(i * limit, (i + 1) * limit). - Works for both chunked (
chunks) and non-chunked output, and stays accurate when a sitemap overflows into multiple numbered files. - When a child sitemap has no per-URL
lastmod, the entry falls back to the configuredlastmodoption — existing behaviour preserved. customSitemapsentries keep using the globallastmod(there are no items to derive a date from).
Before / after, for the reproduction in #16838:
<!-- before -->
<sitemap><loc>https://example.com/sitemap-0.xml</loc></sitemap>
<!-- after -->
<sitemap><loc>https://example.com/sitemap-0.xml</loc><lastmod>2024-09-15T00:00:00.000Z</lastmod></sitemap>The changeset is patch. It is a behaviour change for anyone setting per-URL lastmod via serialize (their index now carries accurate per-file dates), so happy to bump to minor if preferred.
Testing
New test/index-lastmod.test.ts:
- Chunked — distinct
lastmodvalues acrossblog/glossarychunks; asserts each index entry surfaces the newest date in its child sitemap, and that a chunk with no per-URLlastmodfalls back to the configuredlastmod. - Non-chunked, multiple files —
entryLimit: 1so each URL gets its own file; asserts every index entry'slastmodequals the date in the child sitemap it points to (exercises the per-file slicing fori > 0).
Full @astrojs/sitemap suite passes (40/40). biome, eslint, knip, and tsc -b are clean.
Docs
No docs change needed — this refines the existing lastmod behaviour with no new or changed API surface. The lastmod option keeps working as a fallback for child sitemaps without per-URL dates.
Changes
Document on config reference page that compressHTML: "jsx" is only available starting Astro 6.2.0.
Without this reference, it looks like the option has always been available but users of older versions (e.g. 6.1.9) will see the following message that contradicts current documentation:
[config] Astro found issue(s) with your configuration:
! compressHTML: Expected type "boolean", received "string"
I tried to follow the writing style from i18n.routing which also introduced a new option, by starting the sentence similarly.
From some past PRs I checked, doesn't seem like minor doc text change requires changeset, please correct me otherwise.
Testing
Not tested since it's a doc text change.
Docs
This is a doc change itself, which I previously mistakenly tried to do on the generated doc: withastro/docs#13920
Description
This PR disables the minimum release age check of 3 days recently added for withastro/automation updates.
While reviewing #3903, and even after retrying the PR, I was confused why the renovate/stability-days check was still pending, even though all the updates were released around 10 days ago (last change for withastro/automation - last release for pnpm/action-setup).
I'm not a Renovate expert, but I think I finally understand the reason:
- The
withastro/automationis a digest update as we're using the latest commit SHA on themainbranch in workflow files without a version tag (@<sha> # main). - As documented here, for digest updates, having a release timestamp that can have
minimumReleaseAgeenforced is "Generally not supported". - In Renovate 42, the absence of a release timestamp will be treated as if the release is not yet past the timestamp, which provides a safer default.
I'm not 100% sure, but I think this is the reason why the renovate/stability-days check is still pending, and will probably be always pending, preventing us from merging other updates.
A few extra points:
- If merging this PR and retrying #3903 does not resolve the issue, we can revert this PR and investigate further.
- If this works, it could still be a short-term solution and maybe a more long term solution would be to add proper releases with tags to
withastro/automation. - No matter what we end up doing, we will probably need to do the same to the Docs repo as I think withastro/docs#13873 may have the same issue.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.39.3
Patch Changes
-
#3910
dddf405Thanks @andreialba! - Improves Romanian UI translations -
#3924
02f2ce1Thanks @BouRock! - Improves Turkish UI translations -
#3928
11a7ed2Thanks @delucis! - Updates Pagefind to v1.5 and adds support for Pagefind’s newdiacriticSimilarityandmetaWeightsadvanced ranking options -
#3927
e944870Thanks @HiDeoo! - Refactors internal file path handling for Starlight content collections.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.8
Patch Changes
-
#16830
f2bf3cbThanks @matthewp! - Fixes 404s for dynamically imported JS chunks when using an adapter withassetQueryParams(e.g. Vercel skew protection) -
#16831
ace96baThanks @astrobot-houston! - Fixes a misleadingGetStaticPathsRequirederror when a redirect is configured from a dynamic route to a static (or less-dynamic) destination. For example,'/project/[slug]': '/'previously produced a confusing error pointing atindex.astro. Astro now detects the parameter mismatch at config validation time and throws a clearInvalidRedirectDestinationerror naming the missing parameters. -
#16702
b7d1758Thanks @matthewp! - Fixes scoped styles from.astrocomponents being dropped when rendered inside MDX content (<Content />fromrender(entry)) passed through a named slot using<Fragment slot="X">. The Fragment component now eagerly evaluates its slot contents to ensure propagating components register their styles before head content is flushed. -
#16823
3df6a45Thanks @astrobot-houston! - Fixes missing CSS for conditionally rendered Svelte components in production builds -
#16836
3d7adfaThanks @LongYC! - Document compressHTML: "jsx" config is only available since Astro v6.2.0 -
#16864
334ce13Thanks @cheets! - Fixes a false-positiveInternal Warning: route cache overwrittenlogged on every SSR request for dynamic routes
@astrojs/cloudflare@13.5.5
Patch Changes
-
#16607
98297afThanks @alexanderflodin! - Fixes incorrectassets.directoryin the generatedwrangler.jsonwhen abasepath is configured -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/sitemap@3.7.3
Patch Changes
- #16837
783c4a6Thanks @jdevalk! - Improves<lastmod>accuracy in the sitemap index. Each<sitemap>entry insitemap-index.xmlis now stamped with the most recentlastmodof the URLs in the child sitemap it points to, instead of repeating a single global date on every entry. When a child sitemap has no per-URLlastmod, the entry falls back to thelastmodoption as before. This gives search engines a per-file freshness signal, so they can tell which child sitemaps actually changed without refetching all of them.
@astrojs/svelte@8.1.2
Patch Changes
- #16496
4d79750Thanks @fkatsuhiro! - Fixed an issue where type errors occurred during testing library type checks because Astro overrides Svelte 5 component types.
@astrojs/language-server@2.16.10
Patch Changes
Changes
- Fixes misleading
GetStaticPathsRequirederror when redirects from dynamic routes to static destinations are configured (e.g.,'/project/[slug]': '/') - Adds parameter validation in
createRedirectRoutes()to check that destination routes have all required parameters from source routes - Extends
InvalidRedirectDestinationerror message to clearly identify missing parameters, preventing confusion about which file is causing the issue
Testing
- Added unit test for dynamic origin redirecting to static destination (
/project/[slug]→/) - Added unit test for dynamic origin with more params than destination (
/old/[id]/[page]→/posts/[id]) - Both tests verify the error is
InvalidRedirectDestinationwith clear parameter information, not the misleadingGetStaticPathsRequired
Docs
- No docs update needed, as this fixes an existing error case to be clearer rather than introducing new functionality.
Closes #16482
Changes
- Moves
plugin-chunk-importsfromrenderChunktogenerateBundleso it runs after Vite's CSS plugin cleans up pure CSS wrapper chunks. Previously, appending?dpl=...query params duringrenderChunkbroke Vite's regex-based/* empty css */replacement, leaving dangling imports to deleted chunks that 404 at runtime.
Fixes #16520
Testing
- New
css-pure-chunk-query-params.test.tswith a Vue fixture where two async components share the same CSS import, forcing Rollup to create a shared pure CSS chunk. Verifies no JS imports reference deleted files, and that query params are still appended to surviving imports.
Docs
- No docs needed — internal build pipeline fix with no user-facing API change.
Changes
- Adds
GH_TOKENto the "Remove Preview Label" step in the preview release workflow. PR #16810 replaced theactions-ecosystem/action-remove-labelsaction with aghCLI call but did not set the token —ghrequiresGH_TOKENexplicitly, unlike GitHub Actions which inject auth automatically for actions.
Testing
- No test changes. This is a CI-only fix — the step was failing with exit code 4 on every preview release since #16810 merged.
Docs
- No docs needed — internal CI fix.
Fixes #16817
When a tsconfig uses project references that include .vue or .svelte files, TypeScript's getOutputDeclarationFileName returns the input path unchanged (since changeExtension doesn't recognize these extensions). This creates self-referencing entries in the redirect maps (Hello.vue → Hello.vue), causing infinite recursion in findSourceFile.
The fix provides getParsedCommandLine on the language service host, which is TypeScript's official hook for customizing how referenced tsconfigs are parsed. Our implementation parses them normally but filters .vue/.svelte/.astro files from the file list, preventing the self-referencing redirect entries. Project references remain fully functional for all standard TS/JS files.
Changes
- Fixes CSS for conditionally rendered Svelte components being missing from production builds when the condition is
falseduring SSR - During the SSR build, tracks which component exports were actually rendered (
ssrRenderedExportsonBuildInternals) - Saves CSS assets before deletion during client build deduplication and restores them only when the
cssScopeTotarget component was not rendered in SSR — meaning the styles were genuinely tree-shaken and missing from the page. CSS for components that were rendered in SSR (and already on the page) stays deleted to avoid duplicate stylesheets. - Updates test fixture to use deterministic condition (
$state(false)with$effect()) instead of random condition that was passing by coincidence
Testing
- Updated
packages/integrations/svelte/test/fixtures/conditional-rendering/src/components/Parent.svelteto use$state(false)instead ofMath.random() > 0.5for deterministic reproduction - Existing
conditional-rendering.test.tsnow consistently verifies that conditionally rendered component CSS is included in production builds - Verified that the existing
0-css.test.ts"remove unused styles from client:load components" test continues to pass (CSS for normally renderedclient:loadcomponents is still correctly deduplicated)
Docs
- No docs update needed — this fixes existing documented behavior rather than introducing new functionality
Closes #16251
Changes
Adds styleDirective.unsafeInline opt-in flag to Astro's CSP configuration (fixes #14798).
Per the CSP spec, browsers silently ignore unsafe-inline when hashes or nonces are present in the same directive. This meant users who needed unsafe-inline for styles (e.g. third-party libraries, inline style attributes) were forced to abandon Astro's CSP feature entirely — losing script hashing in the process.
When styleDirective.unsafeInline: true is set:
- Astro emits
unsafe-inlinein style-src instead of style hashes - Script hashing is unaffected
Testing
Unit tests added in test/units/csp/rendering.test.ts covering:
unsafe-inlineis emitted and style hashes are omitted when the flag is set- Script hashes are unaffected when the flag is set
- Default behavior (hashes emitted, no
unsafe-inline) is unchanged
Docs
The unsafeInline property is documented inline in config.ts alongside the existing hashes and resources properties. The CspStyleDirective exported type is also updated.
/cc @withastro/maintainers-docs for feedback!
Changes
- Fixes Node adapter in middleware mode incorrectly JSON-stringifying
Bufferbodies fromserverless-http - Adds binary data check (
ArrayBuffer.isView()andinstanceof ArrayBuffer) before generic object branch inmakeRequestBody - Buffer, Uint8Array, and other typed arrays now pass through directly as valid
BodyInitvalues
Testing
- Added 5 new test cases in
test/units/app/node.test.tscovering Buffer, Uint8Array, ArrayBuffer, plain objects, and strings - Verifies binary data passes through unchanged while preserving existing string/object handling
- All existing tests pass with no regressions
Docs
- No docs update needed, this restores expected behavior for a specific integration pattern
Closes #16820
Changes
- Custom elements (tags with hyphens like
<my-element>) in MDX files now go through the renderer pipeline, matching.astrofile behavior - Adds hyphen check in JSX runtime so custom elements fall through to
renderComponentToString()instead ofrenderElement() - Preserves backward compatibility — custom elements with no registered renderer still render as raw HTML
Testing
- Added unit tests in
packages/astro/test/units/render/jsx-custom-elements.test.tscovering renderer pipeline integration and fallback behavior - Verified standard HTML elements continue using
renderElement()path - Confirmed no regressions in existing MDX and render test suites
Docs
- No docs update needed — this fixes existing documented behavior rather than introducing new functionality
Closes #16273
This change is so that users can look at manifest.routes[].styles and add Preload link headers. Previously we didn't include this information for prerendered routes because we didn't think it was necessary.
Changes
- Populates external stylesheet URLs in the SSR manifest for prerendered routes. Previously these had
styles: [], making it impossible for workers to discover which CSS files belong to a prerendered page. Inline styles are still omitted (already in the HTML).
Testing
- No test changes — this is a data-only change to the serialized manifest.
Docs
- No docs needed.
Changes
This PR pin the exact version used by the merge-main-to-next workflow to match the same version we use in the repository. I should fix the failures.
Testing
Merge and wait once it's triggered again
Docs
N/A
tldr, this basically allows for:
export default {
async fetch(request: Request): Promise<Response> {
console.log('Request URL:', request.url); // https://localhost:4321
const state = new FetchState(request);
return await astro(state);
},
} satisfies Fetchable;Changes
- Moves X-Forwarded-Proto/Host/Port resolution into
FetchState's constructor, so headers set by user code insrc/app.tsbefore creatingFetchStateare reflected inAstro.urlandAstro.clientAddress. Previously these headers were resolved before the Fetchable handler'sfetch()was called, making user modifications invisible. - Removes the now-redundant forwarded header processing from the dev server's
handleRequest()invite-plugin-app/app.ts. The production-sidecreateRequest()innode.tsis kept as-is for backward compatibility with third-party adapters that call it directly without going throughFetchState. clientAddressis also resolved fromX-Forwarded-ForinsideFetchStatewhen the host is trusted, unless already provided via render options.
Closes #16797
Testing
- 11 new unit tests in
test/units/fetch/index.test.tscovering: ignored headers withoutallowedDomains, proto/host/port applied individually and together, rejected untrusted hosts,clientAddressfromX-Forwarded-For(trusted/untrusted/pre-set), the core issue scenario (headers set infetch()beforeFetchState), and a full pipeline render with forwarded headers.
Docs
- No docs update needed — this is a bug fix to existing behavior with
experimental.advancedRouting.
Changes
This PR adds:
- zizmor to our CI to check for incorrect usage of our workflows. As for now, I tuned the tool to target high confidence and high severity
- I run the tool in our actions, and applied the suggestions
- removed an action from our preview release
- removed a possible template injection
There's still a warning
warning[secrets-inherit]: secrets unconditionally inherited by called workflow
--> .github/workflows/format.yml:12:11
|
12 | uses: withastro/automation/.github/workflows/format.yml@497c9268ad4267c842a8f6c4d830ad0182d6f6b3 # main
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this reusable workflow
...
15 | secrets: inherit
| ---------------- inherits all parent secrets
|
= note: audit confidence → High
However it's not easy fixable at the moment
Note
I plan to address even warnings, but for now I'll focus on high profile errors.
Testing
Green CI
Docs
Changes
Closes #16781
It hardens and fixes the double encoding algorithm.
Testing
Added a bunch of unit tests, and an integration test.
Docs
N/A
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.7
Patch Changes
-
#16821
9c76b12Thanks @astrobot-houston! - Fixes request body handling in the Node adapter whenreq.bodyis aBuffer,Uint8Array, orArrayBuffer. Previously, binary body data was incorrectly JSON-stringified (producing{"type":"Buffer","data":[...]}) instead of being passed through directly. This affected libraries likeserverless-httpthat setreq.bodyto aBuffer. -
#16785
de96360Thanks @astrobot-houston! - Fixesvite.build.minify,vite.build.sourcemap, andvite.build.rollupOptions.output(e.g.compact) being ignored for client-side builds. These top-level Vite build options are now properly forwarded to the client environment, with environment-specific overrides (vite.environments.client.build.*) taking priority when set. -
#16819
b5dd8f1Thanks @astrobot-houston! - Fixes custom elements in MDX files bypassing the renderer pipeline. Custom elements (tags containing hyphens like<my-element>) in.mdxfiles are now routed through registered renderers for SSR, matching the behavior of.astrofiles. If no renderer claims the element, it falls back to rendering as raw HTML. -
#16808
765896cThanks @ematipico! - Fixes dynamic routes returning 400 Bad Request when the URL contains a literal%character, such as paths built withencodeURIComponent('%?.pdf') -
#16804
90d2acaThanks @jp-knj! - Fixes a v6 regression whereastro:i18ncould not be imported from client<script>blocks.
@astrojs/cloudflare@13.5.4
Patch Changes
-
#16769
428cb1bThanks @astrobot-houston! - Forwards user-providedoptimizeDepssettings (exclude, include, esbuildOptions.loader) to SSR/prerender environments. Previously, top-levelvite.optimizeDepsin the Astro config was silently ignored for server environments because Vite 6 scopes it to client-only and the adapter'sconfigEnvironmenthook did not forward it. This caused packages with non-standard file types (e.g..datafiles) to fail during dev-mode dependency optimization with errors like "No loader is configured for '.data' files". -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
Adds guardrails to the triage skill so the agent bails out after 2 failed server starts instead of looping until timeout.
Prompted by this run where the agent confirmed the bug early but wasted ~10 minutes fighting a stale server process and ran out of time before writing its report.
Changes:
- SKILL.md: General "don't get stuck" rule at the top level
- reproduce.md: Server Management Rules subsection (bail after 2 failures, stop before restart, one repro run is enough, prefer build over dev/preview)
- diagnose.md: Brief server management reminder in the instrumentation step
Changes
It seems that emdash-cms/emdash#1116 was caused by #16708
This PR reverts it
Testing
Preview release
Docs
Changes
- Adds
5-legacyto thebranchesfilter on the preview release workflow so PRs targeting5-legacycan get preview releases via thepr previewlabel.
Testing
- No test changes.
Docs
- No docs needed.
Changes
- Re-enables all Svelte e2e tests that were disabled when the monorepo upgraded to Vite 8.
@sveltejs/vite-plugin-svelte@6.2.4now has experimental Vite 8 support, and all previously-skipped tests pass. - Uncomments
@astrojs/svelteintegration setup and Svelte component usage across 25 e2e fixture files (configs + pages), and removes.skipfrom 11 e2e test files. - The Cloudflare
svelte-rune-depstest still fails (500 in dev mode) so it remains skipped with an updated reason. Theasync-renderingtest is a separate Svelte upstream issue and was not touched.
Testing
- No new tests added — this PR re-enables ~20 existing test cases across
svelte-component,nested-in-svelte,nested-in-react,nested-in-vue,nested-in-preact,nested-in-solid,nested-recursive,client-only,csp-client-only,errors, andview-transitionse2e suites.
Docs
- No docs needed — test-only change.
Closes AST-87
Changes
This PR fixes this failure: https://github.com/withastro/astro/actions/runs/25864427348/job/76002567202#step:7:13
It's caused by conflicts=true, which leaves possible conflict markers in package.json. Then the pnpm action comes, and tries to use the version from the manifest, which is broken.
This PR pins the pnpm version to use in the GH action
Testing
Green CI. We will see if it works after the next release.
Docs
Changes
- Before creating a PR, the fix-verification agent now checks if one already exists for the
flue/fix-{issueNumber}branch. If a PR exists, it posts a comment linking to it instead of failing with a 422 from the GitHub API. - Adds a
findPullRequesthelper to.flue/lib/github.tsthat queries open PRs by head branch.
Testing
- No automated tests; this is CI workflow glue code. Verified the logic matches the GitHub Pulls API behavior.
Docs
- No docs needed — internal CI change only.
Changes
Update esbuild and tsx to the latest versions. This reduces the vite instances installed under node_modules/ and resolves the test failure in Examples astro check / astro check CI job.
Before:
We have 3 vite instances installed. astro and @example/with-vitest use different vite.
$ git status
On branch chore/merge-main-into-next
$ pnpm -r why vite | egrep ' astro@|vite@8|@example|Found'
vite@8.0.13 peer#4033 (3 variations)
│ └── astro@7.0.0-alpha.1 (devDependencies)
├── astro@7.0.0-alpha.1 (dependencies)
│ └── astro@7.0.0-alpha.1 (dependencies)
vite@8.0.13 peer#5d0f (3 variations)
vite@8.0.13 peer#96d1 (3 variations)
│ ├── @example/with-tailwindcss@0.0.1 (dependencies)
│ ├── @example/container-with-vitest@0.0.1 (dependencies)
│ └── @example/with-vitest@0.0.1 (dependencies)
Found 1 version, 3 instances of vite
After:
We have 2 vite instances installed. astro and @example/with-vitest share the same vite.
$ git status
On branch ocavue/chore/merge-main-into-next
$ pnpm -r why vite | egrep ' astro@|vite@8|@example|Found'
vite@8.0.13 peer#1781 (2 variations)
│ ├── @example/with-tailwindcss@0.0.1 (dependencies)
│ ├── @example/container-with-vitest@0.0.1 (dependencies)
│ ├── astro@7.0.0-alpha.1 (devDependencies)
│ └── @example/with-vitest@0.0.1 (dependencies)
├── astro@7.0.0-alpha.1 (dependencies)
│ ├── astro@7.0.0-alpha.1 (dependencies)
vite@8.0.13 peer#ade4 (2 variations)
Found 1 version, 2 instances of vite
Testing
Docs
Changes
- Fixes
vite.build.minify,vite.build.sourcemap, and rollupoutputoptions being ignored when configured through the top-levelvite.buildin astro.config.ts - Adds proper fallback chains so client environment config inherits from top-level Vite config, with environment-specific overrides taking highest priority
- Resolves regression from Vite Environment API migration (PR #14306) where these options were hardcoded instead of respecting user configuration
Testing
- Added 8 new unit tests in
packages/astro/test/units/build/vite-build-config.test.tscovering minify, sourcemap, and rollup output inheritance scenarios - Tests verify environment-specific config takes priority over top-level config, which takes priority over defaults
- Existing sourcemap integration tests continue to pass
Docs
- No docs update needed, as this restores documented behavior that was broken by the Environment API migration
Closes #16268
Changes
This PR improves the printed time stamp of the build
Testing
Added unit tests
Docs
N/A
Description
Fixes #2697.
When the mobile menu is open on narrow viewports, Tab navigation currently escapes the menu after the last focusable element (the theme switcher) and lands on the underlying page content.
This PR sets the native inert attribute on .main-frame and the skip-to-content link while the mobile menu is expanded, so the browser keeps keyboard focus inside the header and sidebar without a hand-maintained list of focusable selectors. A matchMedia('(min-width: 50em)') listener collapses the menu and clears inert automatically if the viewport grows past the mobile breakpoint — so rotating or resizing a device cannot leave the page locked behind a stale trap.
Test plan
Verified manually against pnpm --filter starlight-docs dev at mobile (600×900) and desktop (1024×900) widths:
- Open the menu:
aria-expanded="true",inertset on both.main-frameand.sl-skip-link. - Tab through the menu: focus visits sidebar
<summary>group toggles (e.g. "Start Here", "Guides") and never lands inside.main-frame. Shift+Tab from the first sidebar item returns to the toggle/close button. - Programmatic
.focus()on a link inside.main-frameis refused by the browser while inert. - Resize to 1024px wide while open:
aria-expandeddrops to"false",inertis cleared, article links are interactive again. - Press Escape: menu closes and focus returns to the toggle (regression check).
Per maintainer request, no dedicated e2e tests are added in this PR — if we add coverage later, it should live in __e2e__/basics.test.ts so it shares the basics fixture build.
pnpm lint, pnpm typecheck, and pnpm --filter @astrojs/starlight test (519/519) all pass.
Changeset
Patch bump for @astrojs/starlight included.
Changes
- Adds
npmandnpxas allowed commands in the triage agent's flue sandbox. The agent usesnpx stackblitz-cloneto clone issue reproductions, butnpxwasn't a permitted command, causing the agent to waste time hunting for tools and ultimately fail (run).
Testing
- No test changes. This is a configuration-only change to the flue agent definition.
Docs
- No docs needed — internal CI tooling change.
Changes
- The HMR reload plugin now invalidates all recursively-invalidated modules in the SSR module runner's
evaluatedModulescache, not just the directly changed file.moduleGraph.invalidateModule()already walks importers and populates aninvalidatedModulesset, but the runner cache only cleared the leaf module. Barrel files (e.g.index.tsre-exporting components) stayed cached, soawait import("../components")returned stale exports even after a full page reload.
Fixes #16000
Testing
- New unit test in
hmr-reload.test.ts: "invalidates importers in the module graph for dynamic import chains" -- sets up a component with a barrel file importer and verifies the invalidation propagates through the mock module graph. - Updated mock
invalidateModuleto simulate Vite's recursive importer walk by adding importers to theseenset.
Docs
- No docs changes needed. This is a bug fix restoring behavior from Astro 5.
Changes
- The route cache (
callGetStaticPaths) now stores the module reference alongside cached static paths and compares identity on lookup. After HMR,modis a new object from a freshimport(), so the cache treats the old entry as stale and re-callsgetStaticPaths()with updated component references. This fixes the case where components passed as props viagetStaticPaths()served stale content in dev — even on manual refresh.
Fixes #16522
Testing
- New unit test in
getstaticpaths-cache.test.ts: "re-calls getStaticPaths when module identity changes (HMR)" — callscallGetStaticPathswith one module, confirms cache hit on same module, then passes a different module object and asserts the cache is bypassed and fresh props are returned.
Docs
- No docs changes needed. This is a bug fix restoring existing behavior from Astro 5.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.6
Patch Changes
-
#16774
8f77583Thanks @astrobot-houston! - Fixes markdown images with empty alt text () in content collections dropping thealtattribute entirely. Thealt=""attribute is now correctly preserved in the rendered HTML output, which is important for accessibility (indicating decorative images). -
#16776
3d10b5eThanks @matthewp! - Fixes HMR serving stale content when components are passed as props viagetStaticPaths() -
#16784
7453860Thanks @ematipico! - Improved the printing of the build time if it goes over the 60 seconds. -
#16665
3dbbceeThanks @Princesseuh! - Fixes remote SVG sources erroring withdangerouslyProcessSVGafter the v6.3 SVG-processing gate. The default Sharp service now resolves the output format from the source up-front when it can (URL extension,data:MIME, ESM metadata), and from the actual buffer at request time when it can't, so SVG sources pass through untouched without needing to setimage.dangerouslyProcessSVG: trueor an explicitformat="svg".The error message has also been updated to point at
format="svg"as the simpler workaround when an SVG source is encountered withoutdangerouslyProcessSVGenabled. -
#16777
1754b91Thanks @matthewp! - Fixes HMR serving stale content for dynamically imported components through barrel files -
#16730
068d924Thanks @harshagarwalnyu! - Fixes an issue where thefile()content loader did not generate a valid JSON Schema for collections whose JSON or YAML data is a top-level array instead of an object.
@astrojs/cloudflare@13.5.3
Patch Changes
-
#16801
d619277Thanks @ematipico! - Reverts a change to the esbuild dep-scan plugin that causedastro checkandastro buildto fail by making esbuild incorrectly bundlevirtual:modules (e.g. from expressive-code) -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
Changes
- Fixes markdown images with empty alt text (
) in content collections to renderalt=""instead of dropping the attribute entirely - Replaces JavaScript truthiness check with explicit
value != nullfiltering to preserve empty string attributes - Only affects content collections markdown path — pages-based markdown already handled this correctly
Testing
- Added test
'content collection images with empty alt preserve the alt attribute'inpackages/astro/test/core-image.test.tsto verify empty alt attributes are preserved - Verified all existing image, markdown, and content collection tests still pass
Docs
- No docs update needed, this fixes a regression to match existing expected behavior
Closes #16621
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.5
Patch Changes
-
#16771
07c8805Thanks @ematipico! - Fixespositionprop on<Image>and<Picture>components breaking Content Security Policy (CSP). -
#16593
50924ceThanks @yanthomasdev! - Improves error messages with more consistent and correct writing. -
#16757
5d661cdThanks @astrobot-houston! - Fixes dev server serving stale content when SSR-only modules change (e.g..astrofiles outside the project root in a monorepo, or dynamically imported components).Previously, the
astro:hmr-reloadplugin returned an empty array after detecting SSR-only module changes, which prevented Vite'supdateModulesfrom propagating the invalidation to the SSR module runner. The runner's evaluated module cache stayed stale, so subsequent requests continued returning old content.Now the plugin returns the SSR-only modules so Vite can process them through
updateModules, which properly invalidates the module runner's cache and ensures fresh content on the next request.
Summary
- fix a typo in the Romanian previous page label
- translate remaining English strings in
packages/starlight/translations/ro.json - add a changeset for the user-facing translation fix
Changes
- Uses the server to do
injectScriptentrypoint discover to prevent a race condition with the dep optimizer.
Closes #16766. Related: #16387.
Testing
- User confirmed the fix
- Reproduced locally against npm-installed astro using the reporter's reproduction. Before fix:
_metadata.jsoncontained 1 entry. After fix: 10 entries including all React deps. - No new test — the race condition requires packages installed from npm (not workspace links) and timing-dependent optimizer behavior.
Docs
- No docs changes needed.
Changes
- Forwards user-provided
optimizeDeps.exclude,optimizeDeps.include, andoptimizeDeps.esbuildOptions.loaderfrom the Astro config to SSR/prerender environments in the Cloudflare adapter - Fixes packages with non-standard file extensions (like
.data) failing during dev-mode dependency optimization - Resolves issue where top-level
vite.optimizeDepsconfiguration was silently ignored for server environments due to Vite 6's client-scoped design
Testing
- Added
packages/integrations/cloudflare/test/user-optimize-deps.test.tswith a test that imports a package with.datafiles to verify the exclude configuration works - Test uses a
fake-data-pkgfixture that would fail without the fix but succeeds whenoptimizeDeps.excludeis properly forwarded
Docs
- No docs update needed, this fix restores expected behavior where user Vite config applies to all environments.
Closes #16491
Root cause
The suggestion of Astro bot is included in #16124 . This fixing is clear the cache of disc. But it didn't solve the issue.
It seems the root cause is remaining cache of vite runner eveluatedModules cache.
Changes
Close #13149
Implemented invalidation for file paths and cache clearing in /packages/astro/src/content/vite-plugin-content-virtual-mod.ts.
Testing
Before Implementation
After Implementation

Description
This pull request, adds a simple new page template called blank that allows the end-user to fully customize what's rendered in the ContentPanel without having to worry about CSS hacks to hide things like the PageTitle which are present in the splash template. For more information see #3906
Additionally, it refactors a small section in the Page.astro that handles conditional rendering for pages with a hero image. Hopefully it improves visibility and maintainability in comparison to the previous version.
This pull request does not impact the default Starlight look.
Changes
There is a json schema file included in the wrangler npm package used to specify the structure of the cloudflare wrangler configurations.
This changes the astro add cloudflare command to include the schema reference when generating the wrangler.jsonc file.
- I included a changeset
Testing
Doesn't look like there are any existing tests for the astro add command.
Tested it locally instead.
Docs
Sufficient documentation exists for the add command and the CLI walks the user through it.
Changes
- Handles CSS
@propertyrules inside Astro<style>blocks before delegating to VS Code's generic CSS grammar. - Prevents the tokenizer from staying in CSS context after
</style>, so following<style>and<script>blocks keep correct highlighting.
Testing
- Adds a grammar fixture covering
@propertydescriptors followed by additional style and script blocks. - Adds a snapshot assertion that the closing style tags and following script block keep Astro scopes.
Docs
- No docs update needed because this fixes editor syntax highlighting behavior only.
Changes
- Fixes HMR for SSR-only modules in monorepo setups where shared components live outside the Astro project root
- Changes
astro:hmr-reloadplugin to return SSR-only modules fromhotUpdateinstead of an empty array, allowing Vite'supdateModules()to properly invalidate the SSR module runner cache - Resolves regression from Astro 4/5 where external file changes required dev server restart to be visible
Testing
- Added comprehensive unit tests in
packages/astro/test/units/vite-plugin-hmr-reload/hmr-reload.test.tscovering the key scenarios:- SSR-only modules are returned (not
[]) — the critical test that fails without the fix - Non-SSR environments are skipped correctly
- Style-only modules continue to return
[]as expected - Modules that exist in both client and SSR environments are excluded
- SSR-only modules are returned (not
Docs
- No docs update needed, this fixes existing functionality to work as documented
Closes #16754
Changes
- Fixes
getSSREnvironment()in module loader to return the passedssrEnvironmentparameter instead of hardcodedviteServer.environments['ssr'] - Resolves missing styles from Markdoc/MDX custom components in
<head>when using Cloudflare adapter withprerenderEnvironment: 'node'and wrapper components - The bug occurred because component metadata crawling used the wrong environment's module graph (empty workerd vs populated prerender)
Testing
- Added
packages/astro/test/units/dev/module-loader.test.tswith 2 unit tests confirminggetSSREnvironment()returns the correct environment - Verified the fix resolves the issue in the reporter's reproduction case
Docs
- No docs update needed, this fixes a bug with no API changes
Closes #16013
Changes
Reverts the two-job build/publish split from #16710 back to a single job. The split caused artifact upload timeouts and workspace config mismatches after the pnpm v11 upgrade. Skipping the pnpm cache instead eliminates the cache poisoning vector without needing to shuttle artifacts between jobs.
Testing
CI-only workflow change, validated by triggering a preview release.
Docs
No docs needed — internal CI change.
Changes
The hardened preview-release workflow (#16710) combined with the pnpm v11 upgrade broke preview releases. This generates a synthetic minimal workspace for the publish artifact and uses pnpm dlx to run pkg-pr-new without triggering workspace resolution. Also excludes test/src/e2e directories from the artifact to prevent upload timeouts.
Testing
- Added smoke tests for the staging script (
scripts/stage-preview-publish.test.ts).
Docs
No docs needed — internal CI change.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.
next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.
Releases
astro@7.0.0-alpha.2
Major Changes
-
#16610
c63e7e4Thanks @matthewp! - Adds background dev server management for AI coding agents.When an AI coding agent is detected,
astro devnow automatically starts the dev server as a detached background process. This prevents the dev server from blocking the agent's terminal and allows it to continue working while the server runs.A lock file (
.astro/dev.json) is written when the dev server starts, recording the server's URL, port, and PID. This prevents duplicate servers from being started for the same project.New flag and subcommands
astro dev --background— Start the dev server as a background process (this is what runs automatically when an agent is detected).astro dev stop— Stop a running background dev server.astro dev status— Check if a dev server is running and display its URL, PID, and uptime.astro dev logs— View logs from a background dev server. Use--follow(-f) to stream new output as it's written.
These allow you to start and manage dev servers programmatically and were designed with AI coding agents in mind.
What should I do?
No action is required. If you are not using an AI coding agent,
astro devbehaves exactly as before. If you are using an agent, background mode is enabled automatically — the agent will receive the server URL and PID, and can useastro dev stopto shut it down.To opt out of automatic background mode when an agent is detected, set the environment variable
ASTRO_DEV_BACKGROUND=0before runningastro dev. -
#16725
10229f7Thanks @ArmandPhilippot! - Removes deprecated APIs exported fromastro:transitions.In Astro 6.x, some helpers available in
astro:transitionsandastro:transitions/clientwere deprecated.In Astro 7.0, the following APIs can no longer be used in your project:
TRANSITION_BEFORE_PREPARATIONTRANSITION_AFTER_PREPARATIONTRANSITION_BEFORE_SWAPTRANSITION_AFTER_SWAPTRANSITION_PAGE_LOADisTransitionBeforePreparationEvent()isTransitionBeforeSwapEvent()createAnimationScope()
What should I do?
Remove any occurrence of
createAnimationScope():-import { createAnimationScope } from 'astro:transitions';Replace any occurrence of the other APIs using the lifecycle event names directly:
-import { - TRANSITION_AFTER_SWAP, - isTransitionBeforePreparationEvent, -} from 'astro:transitions/client'; -console.log(isTransitionBeforePreparationEvent(event)); +console.log(event.type === 'astro:before-preparation'); -console.log(TRANSITION_AFTER_SWAP); +console.log('astro:after-swap');
Learn more about all utilities available in the View Transitions Router API Reference.
Patch Changes
-
#16980
1f07343Thanks @matthewp! - Removesstate.provide(),state.resolve(),state.finalizeAll(), andApp.Providersfrom the public advanced routing API. These context provider extension points are now internal-only. If you were using them in an integration, uselocalsto share per-request state instead. -
#16982
1e000e2Thanks @matthewp! - Improves the warning when accessingAstro.sessionwithout session storage configured. Thesessionproperty is now always defined on the context object, and accessing it without configuration logs a helpful message instead of silently returningundefined. -
#16990
ebeb830Thanks @ocavue! - FixesAstro.request.urlnot reflecting validatedX-Forwarded-Proto/X-Forwarded-Hostheaders whensecurity.allowedDomainsis configured. Previously, onlyAstro.urlwas updated with the forwarded origin whileAstro.request.urlretained the socket-derived URL, causing the two to diverge behind TLS-terminating proxies.
@astrojs/svelte@9.0.0-alpha.2
Minor Changes
- #16549
9d9d516Thanks @ocavue! - Updates@sveltejs/vite-plugin-svelteto v7. No user action is necessary.
@astrojs/markdown-satteri@0.3.0-alpha.0
Minor Changes
-
#16969
4a31f90Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Settingmarkdown.syntaxHighlightto'prism'now highlights your code blocks with Prism.// astro.config.mjs import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), syntaxHighlight: 'prism', }, });
@astrojs/cloudflare@14.0.0-alpha.1
Patch Changes
-
#16990
ebeb830Thanks @ocavue! - Fixes a build crash when usingexperimental.advancedRoutingwith a customfetchFilethat statically importscffrom@astrojs/cloudflare/fetch. The circular dependency between@astrojs/cloudflare/fetchandastro/app/entrypointcausedcreateApporcreateGetEnvto beundefinedat module evaluation time. Initialization is now deferred to the firstcf()call, breaking the cycle. -
#16671
fd926fdThanks @alexanderniebuhr! - Removes deprecations warnings added in Astro v6 for Cloudflare specific Astro.locals properties. -
Updated dependencies [
1f07343,1e000e2,c63e7e4,ebeb830,10229f7]:- astro@7.0.0-alpha.2
- @astrojs/underscore-redirects@1.0.3
@astrojs/mdx@6.0.0-alpha.1
Patch Changes
-
#16969
4a31f90Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Settingmarkdown.syntaxHighlightto'prism'now highlights your code blocks with Prism.// astro.config.mjs import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), syntaxHighlight: 'prism', }, });
-
Updated dependencies [
4a31f90]:- @astrojs/markdown-satteri@0.3.0-alpha.0
Changes
The hardening split (#16710) combined with the pnpm v11 upgrade broke preview releases — the publish job's minimal artifact was missing patches/ and had unresolvable workspace:* deps. This generates synthetic workspace configs in the staging step and uses pnpm dlx instead of pnpm exec to skip workspace resolution entirely.
Testing
Note, this is me, the human. Preview releases are currently broken after we split up the job for security reasons (so installing code not have access to the id token).
I ran the script in this change and verified it does pack all of the things it's supposed to. I can't run a preview release to verify that works.
Also added some unit tests to the script itself.
Docs
No docs needed — internal CI change.
Changes
- Fixes the dev prerender/SSR handoff for Cloudflare
prerenderEnvironment: 'node'by delaying request body reads untilAstroServerApp.handleRequest()finishes route resolution inprerenderOnlymode. If the resolved route falls through to SSR, the POST body is still intact when the SSR handler receives it. - Adds a comment next to the prerender middleware gate explaining why the
matchAllRoutes()check is intentional: the final dev routing decision still needs the deeper route resolution inhandleRequest(), so future changes do not replace that gate withmatchRoute()again.
Testing
- Added a Cloudflare regression test in
packages/integrations/cloudflare/test/prerender-node-env.test.tsthat exercisesPOST /_actions/hellowith a prerendered catch-all route. - The same fixture continues to cover the surrounding
prerenderEnvironment: 'node'behavior for prerendered pages, SSR pages, and server islands.
Docs
- No docs update needed, because this fixes broken dev behavior rather than changing the supported API.
Closes #16746
Changes
Closes AST-44
This PR stabilise the logger feature
Testing
Green CI.
Docs
Changes
- Adds a sparse checkout step to the
publishjob in the preview release workflow sopnpm/action-setupcan read thepackageManagerfield frompackage.json. Thepublishjob previously had no checkout, so pnpm setup failed with "No pnpm version is specified."
Testing
- No test changes. This is a CI workflow fix — validation is the next preview release run succeeding.
Docs
- No docs needed — internal CI change only.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.4
Patch Changes
-
#16723
0f10bfeThanks @matthewp! - AddsfetchFileoption toexperimental.advancedRoutingto customize or disable the entrypoint fileexport default defineConfig({ experimental: { advancedRouting: { fetchFile: 'fetch.ts', }, }, });
-
#16723
0f10bfeThanks @matthewp! - Fixes Honocache()middleware to follow the standard wrapper pattern -
#16723
0f10bfeThanks @matthewp! - AddsApp.Providersinterface for typing custom context providers onAstroandctxdeclare namespace App { interface Providers { oauth: import('./lib/oauth').OAuthSession; } }
-
#16723
0f10bfeThanks @matthewp! - AddsFetchState.responseproperty, set automatically afterpages()ormiddleware()completesconst response = await middleware(state, (s) => pages(s)); console.log(state.response === response); // true
-
#16723
0f10bfeThanks @matthewp! - AddsFetchabletype export for typing the advanced routing entrypointimport type { Fetchable } from 'astro'; export default { async fetch(request) { return new Response('ok'); }, } satisfies Fetchable;
-
#16572
4a5a077Thanks @DORI2001! - Suppresses[WARN] Vite warning: unused imports from "@astrojs/internal-helpers/remote"during prerender builds. The package is now bundled alongsideastroin the prerender environment, matching how it is handled in the SSR environment. -
#16756
b6ee23dThanks @astrobot-houston! - Fixes styles from Markdoc/MDX custom components not being extracted to<head>in the dev server when using the Cloudflare adapter withprerenderEnvironment: 'node'and rendering content through a wrapper component. -
#16747
904d19aThanks @astrobot-houston! - Fixes Astro action requests failing inastro devwhen using the Cloudflare adapter withprerenderEnvironment: 'node'alongside a prerendered catch-all route such as[...page].astro.Actions and other SSR POST endpoints now continue to work in dev instead of returning an HTTP 500 error.
-
#16701
3495ce4Thanks @demaisj! - FixMapandSetinstances saved in a content collection being broken when retrieving entries. -
#16614
fca1c32Thanks @Eptagone! - Fixesentry.datatype inference when a live collection is configured without a schema. -
#16661
03b8f7fThanks @ocavue! - Updatestypescriptto v6. No changes are needed from users. -
#16681
c22770aThanks @dotnetCarpenter! - Fixes an issue where SVG images withwidth="0"orheight="0"incorrectly threw aNoImageMetadataerror instead of being treated as valid dimensions.
@astrojs/db@0.21.2
Patch Changes
@astrojs/cloudflare@13.5.2
Patch Changes
-
#16708
bb709ffThanks @fkatsuhiro! - Fixed a bug where a cascade of reloads would cause the page to crash during the first visit when building or developing with Cloudflare SSR in Astro v6 due to dependency loading issues. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/netlify@7.0.10
Patch Changes
-
#16661
03b8f7fThanks @ocavue! - Updatestypescriptto v6. No changes are needed from users. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/language-server@2.16.9
Patch Changes
-
#16661
03b8f7fThanks @ocavue! - Updatestypescriptto v6. No changes are needed from users. -
Updated dependencies [
03b8f7f]:- @astrojs/yaml2ts@0.2.4
@astrojs/ts-plugin@1.10.9
Patch Changes
-
#16661
03b8f7fThanks @ocavue! - Updatestypescriptto v6. No changes are needed from users. -
Updated dependencies [
03b8f7f]:- @astrojs/yaml2ts@0.2.4
astro-vscode@2.16.16
Patch Changes
- #16719
2b1df12Thanks @alexisintech! - fix MDX syntax highlighting for indented astro codeblocks
@astrojs/yaml2ts@0.2.4
Patch Changes
Changes
- Extracts the Vite
InlineConfigassembly frombuildEnvironments()into a newcreateViteBuildConfig()function invite-build-config.ts. The function takes settings, viteConfig, routes, plugins, builder, and isRollupInput, and returns the config object without executing any build. - The
buildAppcallback (which depends oninternals) is passed in as a parameter, keeping the function free of build-time state.
Testing
- New
test/units/build/vite-build-config.test.tswith 12 unit tests covering: user rollup output overrides (assetFileNames,chunkFileNames,entryFileNames) preserved in top-level, client, and prerender environments; Astro defaults applied when no override;build.assetsprefix flowing into rollup templates;base,envPrefix, andcssMinifybehavior. - Removed 5 integration tests + 3 fixtures replaced by unit tests:
custom-assets-name.test.ts+ fixture (userassetFileNamesfunction override)entry-file-names.test.ts+ fixture (cliententryFileNamesoverride)astro-assets-dir.test.ts+ fixture (build.assetsprefix)config-vite.test.tsfirst describe block (prerender rollup output override)astro-css-bundling.test.tscustom assetFileNames section (prerenderassetFileNamesoverride)
Docs
- No docs needed — internal refactor with no user-facing behavior change.
Changes
- Replaces the
astro-globalintegration test suite with unit tests usingcreateTestApp. CoversAstro.site,Astro.url,Astro.routePattern,Astro.isPrerendered, and middleware locals propagation. - Removes the integration test file, fixture directory, and all associated fixture files (21 files including a 7MB penguin image).
Testing
- New unit test file at
test/units/render/astro-global.test.tswith 10 tests covering the same Astro globals behavior. Runs in ~330ms vs seconds for the integration tests. - Dropped 4 tests that provided no meaningful coverage: 3 tested
import.meta.glob()(Vite behavior, not Astro globals) and 1 was accidentally asserting a 404 response.
Docs
- No docs needed — test-only change.
Changes
Replaces the flaky integration test added in d2e25c6, which fetched a remote image from kaleidoscopic-biscotti-6fe98c.netlify.app.
The replacement is a set of offline unit tests for propsToFilename that cover the same filename-sanitization logic without any network calls.
Testing
See changed tests
Docs
N/A
Changes
- Fixes a reflected XSS where slot names on hydrated (
client:*) components were interpolated raw intodata-astro-templateandastro-slot nameHTML attributes during SSR. An attacker could break out of the attribute context and inject arbitrary HTML when a slot name is derived from user input. AppliesescapeHTML()to the slot name key at both interpolation points incomponent.ts.
Testing
- Added
slot-name-escape.astrofixture page that uses a slot name containing"><img src=x onerror=alert(1)>on aclient:loadcomponent - Added test case in
astro-children.test.tsverifying no injected<img>element exists and the attribute value contains properly escaped entities
Docs
- No docs update needed — this is an internal security fix with no user-facing API change.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.3
Patch Changes
- #16737
bd84f33Thanks @matthewp! - Fixes a reflected XSS vulnerability where slot names on hydrated components were not HTML-escaped in SSR output
@astrojs/mdx@5.0.6
Patch Changes
- #16579
49e10e3Thanks @igor-koop! - Fixes an issue where thesmartypantsoption was ignored.
Changes
This PR addresses a flaky test shown in this run: https://github.com/withastro/astro/actions/runs/25853010563/job/75964157111?pr=16734
Retry #2 ───────────────────────────────────────────────────────────────────────────────────────
Error: There should be 3 page loads (for page one & three), and an additional loads for the back navigation
expect(received).toEqual(expected) // deep equality
Expected: 3
Received: 2
206 | loads.length,
207 | 'There should be 3 page loads (for page one & three), and an additional loads for the back navigation',
> 208 | ).toEqual(3);
| ^
209 | });
210 |
211 | test('Declarative Shadow DOM elements are attached after transitions', async ({
at D:\a\astro\astro\packages\astro\e2e\view-transitions.test.ts:208:5
Error Context: test-results\e2e-view-transitions-View--20f1e--ClientRouter-w-back-button-Firefox-Stable-retry2\error-context.md
Particularly, I used expect.poll and expect.toPass to refactor some tests with a magic timeout.
// Bad pattern
doSomething()
await page.waitForTimeout(500); // <-- A magic timeout number whether is too big (too slow) on a fast machine or too small (too fast) on a slow machine
expect(getResult()).toBe(expectedResult)
// Good pattern
doSomething()
await page.poll(getResult).toBe(expectedResult)
// Good pattern
doSomething()
await expect(() => { expect(getResult()).toBe(expectedResult) }).toPass()Testing
CI should pass
Docs
N/A
Changes
Previous, if generateImagesForPath() fails, an uncaught error will be thrown. This is an issue because it doesn't break the build() function itself, causing flaky tests.
For example, in this job run, the await fixture.build() returns a resolved promise, but the build is not actually successful.
astro:test: ✖ astro:image (37126.090234ms)
astro:test: ℹ Error: Test hook "before" at test/core-image.test.ts:992:3 generated asynchronous activity after the test ended. This activity created the error "TypeError: fetch failed" and would have caused the test to fail, but instead triggered an unhandledRejection event.
Later, when we want to check out the built result, we hit errors:
astro:test: ✖ failing tests:
astro:test:
astro:test: test at test/core-image.test.ts:1020:3
astro:test: ✖ writes out allowed remote images (1.309057ms)
astro:test: Error: ENOENT: no such file or directory, open '/home/runner/work/astro/astro/packages/astro/test/fixtures/core-image-ssg/dist/core-image-build-ssg/_astro/sponsors_1Wl70z.webp'
astro:test: at async open (node:internal/fs/promises:640:25)
astro:test: at async Object.readFile (node:internal/fs/promises:1283:14)
astro:test: at async TestContext.<anonymous> (file:///home/runner/work/astro/astro/packages/astro/test/core-image.test.ts:1025:17)
astro:test: at async Test.run (node:internal/test_runner/test:1125:7)
astro:test: at async Suite.processPendingSubtests (node:internal/test_runner/test:787:7) {
astro:test: errno: -2,
astro:test: code: 'ENOENT',
astro:test: syscall: 'open',
astro:test: path: '/home/runner/work/astro/astro/packages/astro/test/fixtures/core-image-ssg/dist/core-image-build-ssg/_astro/sponsors_1Wl70z.webp'
astro:test: }
astro:test:
Testing
CI should pass
Docs
This change doesn't affect users, so no changeset is needed. The error message is silently improved.
If I have a 404 image in the repo, here is what I get before and after when running astro build:
Before:
$ ./node_modules/.bin/astro build
21:21:59 [content] Syncing content
21:21:59 [content] Synced content
21:21:59 [types] Generated 253ms
21:21:59 [build] output: "static"
21:21:59 [build] mode: "static"
21:21:59 [build] directory: /Users/ocavue/code/github/astro/packages/astro/test/fixtures/core-image-ssg/dist/
21:21:59 [build] Collecting build info...
21:21:59 [build] ✓ Completed in 259ms.
21:21:59 [build] Building static entrypoints...
21:21:59 [vite] ✓ built in 350ms
21:21:59 [vite] ✓ built in 6ms
21:21:59 [build] Rearranging server assets...
generating static routes
21:21:59 ├─ /remote/index.html (+8ms)
21:21:59 ├─ /srcset/index.html (+1ms)
21:21:59 ✓ Completed in 16ms.
generating optimized images
21:21:59 ▶ /_astro/image 1.ZqYaJ2Hs_Zkuiyp.webp (reused cache entry) (+9ms) (1/3)
21:21:59 ▶ /_astro/image 1.ZqYaJ2Hs_2gIxxN.webp (reused cache entry) (+0ms) (2/3)
21:22:00 ✓ Completed in 1.21s. <-- ⚠️⚠️⚠️ Notice this "Completed in 1.21s" message
file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/remote.js:10
throw new Error(
^
Error: Failed to load remote image https://astro.build/this_img_does_not_exist.png. The request did not return a 200 OK response. (received 404))
at loadRemoteImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/remote.js:10:11)
at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
at async loadImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:237:12)
at async generateImageInternal (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:154:23)
at async generateImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:69:28)
at async generateImagesForPath (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:53:5)
at async file:///Users/ocavue/code/github/astro/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/index.js:394:36
Node.js v24.11.1
$ echo $?
1 <-- ⚠️⚠️⚠️ `astro build` exits with code 1After:
$ ./node_modules/.bin/astro build
21:27:17 [content] Syncing content
21:27:17 [content] Synced content
21:27:17 [types] Generated 258ms
21:27:17 [build] output: "static"
21:27:17 [build] mode: "static"
21:27:17 [build] directory: /Users/ocavue/code/github/astro/packages/astro/test/fixtures/core-image-ssg/dist/
21:27:17 [build] Collecting build info...
21:27:17 [build] ✓ Completed in 263ms.
21:27:17 [build] Building static entrypoints...
21:27:17 [vite] ✓ built in 377ms
21:27:17 [vite] ✓ built in 5ms
21:27:17 [build] Rearranging server assets...
generating static routes
21:27:17 ├─ /remote/index.html (+8ms)
21:27:17 ├─ /srcset/index.html (+1ms)
21:27:17 ✓ Completed in 16ms.
generating optimized images
21:27:17 ▶ /_astro/image 1.ZqYaJ2Hs_Zkuiyp.webp (reused cache entry) (+10ms) (1/3)
21:27:17 ▶ /_astro/image 1.ZqYaJ2Hs_2gIxxN.webp (reused cache entry) (+0ms) (2/3)
21:27:18 [WARN] [build] Unable to generate optimized image for https://astro.build/this_image_does_not_exist.png: Error: Failed to load remote image https://astro.build/this_image_does_not_exist.png. The request did not return a 200 OK response. (received 404))
Error generating image for https://astro.build/this_image_does_not_exist.png: Error: Failed to load remote image https://astro.build/this_image_does_not_exist.png. The request did not return a 200 OK response. (received 404))
Stack trace:
at file:///Users/ocavue/code/github/astro/packages/astro/dist/core/build/generate.js:195:21
Caused by:
Failed to load remote image https://astro.build/this_image_does_not_exist.png. The request did not return a 200 OK response. (received 404))
at loadRemoteImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/remote.js:10:11)
at async loadImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:237:12)
at async generateImage (file:///Users/ocavue/code/github/astro/packages/astro/dist/assets/build/generate.js:69:28)
at async file:///Users/ocavue/code/github/astro/node_modules/.pnpm/p-queue@9.1.0/node_modules/p-queue/dist/index.js:394:36
$ echo $?
1 <-- ⚠️⚠️⚠️ `astro build` still exits with code 1Notice that after this PR, you no longer see the Completed in 1.21s message in the shell because the build() function itself throws the error and never finishes. This should be a minor improvement for users.
Changes
file()loader JSON Schema generation now emits ananyOfschema instead of a plain record-only schema- Branch 1:
{ type: 'array', items: <itemSchema> }— validates top-level array JSON/YAML files (e.g.[{ name: 'red', color: '#f00' }]) - Branch 2:
{ type: 'object', additionalProperties: <itemSchema>, properties: { $schema: string } }— validates record-style files and preserves the$schemaproperty - Before: VS Code flagged top-level array files as invalid against the generated schema
- After: Both array and record shapes pass validation
- Fixes #16602
- Changeset included (
.changeset/tangy-chairs-smile.md)
Testing
Updated packages/astro/test/content-intellisense.test.ts — renamed and expanded the existing test to assert:
schema.anyOfexists with exactly 2 branches- Array branch:
type: 'array'withitems.propertiesmatching the collection item schema - Object branch:
type: 'object'withadditionalProperties.propertiesmatching the item schema and a$schemaproperty present
Run with:
pnpm test packages/astro/test/content-intellisense.test.tsDocs
No docs change needed — this is an internal schema generation improvement. The file() loader public API is unchanged; users who already use file() get correct IntelliSense automatically.
@astrojs/cloudflare/fetch
import { astro, FetchState } from 'astro/fetch';
import { cf } from '@astrojs/cloudflare/fetch';
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const state = new FetchState(request);
const asset = await cf(state, env, ctx);
if (asset) return asset;
return astro(state);
}
}@astrojs/cloudflare/hono
import { Hono } from 'hono';
import { actions, middleware, pages, i18n } from 'astro/hono';
import { cf } from '@astrojs/cloudflare/hono';
const app = new Hono<{ Bindings: Env }>();
app.use(cf());
app.use(actions());
app.use(middleware());
app.use(pages());
app.use(i18n());
export default app;Changes
- Adds
@astrojs/cloudflare/fetchexporting acf(state, env, ctx)function that applies Cloudflare-specific setup to aFetchState— session KV binding injection, static asset serving via ASSETS,locals.cfContext, client address fromcf-connecting-ip,waitUntil, and prerendered error page fetch. Returns aResponsefor asset hits,undefinedotherwise. - Adds
@astrojs/cloudflare/honoexporting acf()Hono middleware that does the same setup, readingenvandexecutionCtxfrom the Hono context automatically (no arguments needed). - Extracts shared helpers from the existing
handler.tsintocf-helpers.tsandcf.ts, then refactorshandler.tsto use them. Zero duplication between thehandle()entrypoint and the new exports.
Testing
- Adds
cf-helpers.test.tswith 18 unit tests coveringcreateLocals,getClientAddress,matchStaticAsset,fallbackToAssets, andcreateErrorPageFetch. Tests use mockEnvandExecutionContextobjects — no fixtures or build required. - All 207 existing Cloudflare adapter tests continue to pass after the
handler.tsrefactor.
Docs
Changes
- Reverts
npm ciback tonpm iin the VSCode extension publish step of the release workflow.npm cirequires apackage-lock.jsonwhich does not exist inpackages/language-tools/vscode/(and is globally gitignored). This was broken by #16711.
Testing
- No test changes. This is a CI workflow fix — validated by the next release that publishes the VSCode extension.
Docs
- No docs needed — internal CI change only.
Astro v7 PR
Changes
Removes the astro:transitions APIs deprecated in #14989 and scheduled to be removed in Astro v7.
Testing
Green CI.
Docs
Changeset added.
Docs PR: withastro/docs#13886
/cc @withastro/maintainers-docs for feedback!
Changes
- When the triage bot publishes a preview release, the issue now gets a
fix pending verificationlabel and the comment asks the reporter to confirm the fix works - A new
fix-verificationworkflow listens for comments on issues with that label. A flue agent (Sonnet) classifies whether the comment is a positive confirmation - If verified: removes the label, generates a PR title/body via the
astro-pr-writerskill, creates the PR from the existingflue/fix-{N}branch, and adds afix verifiedlabel to the PR
This closes the loop on the triage bot fix pipeline: reproduce → diagnose → fix → test → preview release → reporter verification → PR creation.
Testing
- No automated tests — this is CI/agent infrastructure. Will be validated by the next triage bot run that produces a fix with a preview release.
Docs
- No docs needed — internal CI automation, not user-facing.
Changes
Fetchabletype: Exported fromastroso users can typesrc/app.tswithsatisfies Fetchable.FetchState.response: Set byPagesHandlerandAstroMiddlewareafter rendering so callers can readstate.response.App.Providers: New extendable interface for typing custom context providers onctxandAstro.fetchFileconfig: Lets users rename the entrypoint away fromsrc/app.ts, or setnullto disable.- Hono
cache()wrapper: Now follows the standard middleware pattern like the other Hono wrappers.
Testing
Added 6 unit tests in test/units/fetch/index.test.ts:
state.responseis set afterpages()andmiddleware()- Context provider
provide/resolvelazy creation and caching finalizeAllruns finalize for resolved providers, skips unresolved ones
Docs
Added here: withastro/docs#13890
Changes
Fixes dynamic routes returning the string [object Object] under the following conditions:
- when used with Cloudflare Workers with the
nodejs_compatflag. The affected projects all use compatibility dates of2025-04-01or newer, not sure if older dates are affected. - only for large, complex projects where Rollup splits code in a way that triggers it. The exact level of complexity/size required is unknown. The fix should work for projects of all sizes though.
This was a very difficult issue to track down. Static routes were fine, and in the Worker logs I could see that the dynamic routes were being processed correctly (I see things like logs indicating success for loading external content and other expected internal logging from the build process) but the returned response was just the string [object Object] regardless of which page or what content was expected -- no error messages of any kind.
I did not open an issue for this because by the time I had enough detail to do so, Claude Code had provided a fix, so I'm just going straight to a PR.
See included Claude Code analysis below for full details.
Testing
I did not add tests because I'm not sure how to provide a sufficiently complicated test project -- this problem only surfaces in larger projects. I'm happy to assist adding tests if I can get some help.
I do have this fix running in 3 separate projects ranging in size and complexity from a stripped down copy of the Astro blog starter template for Workers (where the problem never exhibited but this fix does not cause any issues), up to a 1000-page hybrid site with upwards of 100 Astro components and a mix of dynamic and static routes (where the problem surfaced and the fix works).
Claude Code's explanation of the issue and proposed fix were arrived at after several rounds of analysis and refinements, and its explanation makes sense and matches what I've been experiencing. I specifically asked it to check that the changes do not affect anything for Deno or Node prerendering environments, only workerd.
Docs
I don't believe docs updates are needed because this fixes an issue with expected functionality with no changes needed by the user.
Claude's analysis, RCA, and proposed fix, which I've implemented in this PR
If you're interested, here's what my AI agent found
This analysis was done with Astro 6.2.x and Cloudflare 13.3.x, but I've confirmed the problem still exists in the current versions. I'm barred by work policy from installing packages newer than 7 days so I cannot test the latest versions directly, but the problem code still exists in the latest version, unchanged.This analysis was done by comparing two projects:
astro-blog-starter-templatewhich is a copy of the Astro starter for Workers, stripped of all but one dynamic and one static test pageweb-dev-collibra-astrowhich is our largest, most complicated project where the problem surfaced
This is its final summary, verbatim:
Fix Summary — Astro isNode false-positive in workerd
Symptom
In an Astro 6.2.x project deployed to Cloudflare Workers via @astrojs/cloudflare 13.3.x with nodejs_compat, any route with prerender: false returns an HTTP 200, content-type: text/html response whose body is the 15-byte string [object Object]. Static / prerendered routes are unaffected. Reproducible locally with astro build && astro preview.
Root cause
astro/dist/runtime/server/render/util.js defines:
const isNode = typeof process !== "undefined"
&& Object.prototype.toString.call(process) === "[object process]";astro/dist/runtime/server/render/page.js then chooses the response body shape:
if (isNode && !isDeno) {
body = await renderToAsyncIterable(...); // Node-only async iterable
} else {
body = await renderToReadableStream(...); // standard ReadableStream
}With nodejs_compat, workerd's process reports Object.prototype.toString.call(process) === "[object process]" until the @cloudflare/unenv-preset polyfill replaces it with a plain object. The output of renderToAsyncIterable is an object exposing Symbol.asyncIterator. Node's undici accepts async iterables as a Response body; workerd's Response does not, and falls back to String(body) → "[object Object]".
Why the bug surfaces in some projects but not others
The bug is a module-initialization-order race that only manifests once Rollup decides to code-split Astro's shared runtime.
Both the failing project and the working starter contain the same two pieces of code in the worker bundle:
// A — unenv polyfill assignment (replaces workerd's raw process)
globalThis.process = _process;
// B — Astro's isNode evaluation (reads process)
const isNode = typeof process !== "undefined"
&& Object.prototype.toString.call(process) === "[object process]";The order in which A and B run depends on whether Rollup keeps them in one chunk or splits them across two:
astro-blog-starter-template (2 pages, almost no Astro internals reused) — Rollup keeps everything in one chunk, worker-entry_DEEjL72T.mjs:
line 558: globalThis.process = _process; ← A runs first
line 5864: const isNode = ... ← B sees the unenv polyfill
Single module body, top-down execution → process is the plain-object unenv polyfill by the time isNode runs → isNode = false → renderToReadableStream → works.
web-dev-collibra-astro (many pages and components importing from astro/runtime/server/*) — Rollup factors Astro's shared runtime into its own chunk:
transition_DTdhAtUt.mjs:814 const isNode = ... ← B
worker-entry_2ArJYird.mjs:1282 globalThis.process = _process; ← A
worker-entry imports transition. ES Module spec executes imported module bodies before the importer's body, so B runs before A. isNode is evaluated against workerd's raw nodejs_compat process, latches to true, and renderToAsyncIterable is selected → broken response.
The threshold is whatever pushes Rollup over its code-splitting heuristic for shared dependencies. Any project with enough reuse of astro/runtime/server/* across pages or components will eventually trip it — it's not specific to Vue, custom integrations, or middleware.
I confirmed module init order is the differentiator by logging Object.prototype.toString.call(process) at module init vs. request time in the failing project: "[object process]" at init, "[object Object]" at request time — proving the polyfill assignment happens between the two, after isNode has already been frozen.
The fix
One line in packages/astro/src/runtime/server/render/util.ts:
const isNode = typeof process !== "undefined"
&& Object.prototype.toString.call(process) === "[object process]"
&& !(typeof navigator !== "undefined" && navigator.userAgent === "Cloudflare-Workers");Workerd's navigator.userAgent === "Cloudflare-Workers" is documented, stable, and present from t=0 — so the check is immune to the module-init-order race that broke the original process-only detection.
Behavior matrix
| Environment | process toString |
navigator.userAgent |
isNode before |
isNode after |
Behavior change |
|---|---|---|---|---|---|
| Node.js | "[object process]" |
undefined / Node ua | true |
true |
None |
| Deno | "[object process]" (Node compat shim) |
"Deno/x.y.z" |
true |
true |
None — also gated by !isDeno in renderPage |
| Browser | undefined | various, never "Cloudflare-Workers" |
false |
false |
None |
Cloudflare Workers (workerd) without nodejs_compat |
undefined | "Cloudflare-Workers" |
false |
false |
None |
Cloudflare Workers (workerd) with nodejs_compat, polyfill already installed before isNode eval |
"[object Object]" |
"Cloudflare-Workers" |
false |
false |
None (incidentally-correct case — the starter) |
Cloudflare Workers (workerd) with nodejs_compat, polyfill not yet installed |
"[object process]" |
"Cloudflare-Workers" |
true (wrong) |
false (correct) |
Bug fixed |
The change only flips behavior in the row where the original heuristic is wrong.
Cross-checks against the prerender flow
@astrojs/cloudflare exposes prerenderEnvironment: "node" | "workerd" (default "workerd").
prerenderEnvironment: "node": prerender runs in real Node. Nonavigatorglobal, originalisNode = truepreserved. Same as before. ✓prerenderEnvironment: "workerd"(default): prerender runs in workerd. New check correctly returnsfalse. TherenderToReadableStreampath produces aReadableStreambody, which Astro's prerender pipeline consumes the same way it consumes an async iterable. ✓
Verification performed
- Reproduced locally on
web-dev-collibra-astrowithastro build+astro preview—curl /dev/dynamicreturns[object Object](15 bytes). - Confirmed the path by logging
isNode,streaming, andbody.constructor.nameinside the bundledrenderPage—isNode=true, body constructor isObject(the async iterable), notReadableStream. - Confirmed module-init-order theory by logging
Object.prototype.toString.call(process)both at module init ("[object process]") and at request time ("[object Object]") — proves the polyfill assignment happens between the two. - Confirmed
astro-blog-starter-templateevaluatesisNode = falsebecause the unenv polyfill assignment (line 558) precedes theisNodeevaluation (line 5864) in the same chunk. - Applied the patch via
build-scripts/patch-astro-renderer.mjs; re-ran preview; dynamic route returns<!DOCTYPE html>...<div>TEST</div>, static route unchanged.
Details
Changes
Bug reported: #16667
FIXES #16667
The Astro extension was causing MDX highlighting errors, but only when the code snippets were indented:
BEFORE FIX:
AFTER FIX:
Testing
pnpm installpnpm -C packages/language-tools/vscode build- In Cursor: Run and Debug → Launch Client
- Opened an MDX file and tested an indented astro codeblock
Docs
No docs necessary
Changes
- Updates pnpm to v11
- Removes minimum release age exceptions that are no longer required
- Cleans up deps in a couple of fixtures that were accidentally using npm versions of monorepo packages
- Explicitly disallows postinstall scripts for all dependencies that have them
- Enables pnpm’s
trustPolicywhich prevents installing versions of packages that have reduced their publishing provenance. This required applying some limited exceptions to continue installing our currently installed packages.
Some semi-user-facing changes:
- Updated deps in the Netlify adapter
- Dropped support for older versions of VS Code so we can test with Node ≥22 which is pnpm 11’s minimum required version
Testing
Existing tests should pass
Docs
n/a — monorepo hardening only
Description
- Companion to #3902
- Tells Renovate to wait for 3 days before applying updates to match our pnpm policy
- In practice not super important as we mainly use Renovate for GitHub Actions, which don’t involve pnpm, but still good to keep these in sync
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| pnpm/action-setup | action | patch | v6.0.7 → v6.0.8 |
| pnpm/action-setup | action | patch | v6.0.5 → v6.0.8 |
| withastro/automation | action | digest | 3754a74 → 290b374 |
Release Notes
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Description
This PR updates to pnpm 11 and configures a 3-day minimum release age for dependencies to mirror the main astro monorepo: https://github.com/withastro/astro/blob/3abcc86c092ca2a3455fc0ffc72b6b7ca300f1a2/pnpm-workspace.yaml#L32
This helps defend a little against supply chain attacks, but also avoids us installing deps that then cause issues in the astro monorepo CI because they’re newer than their config allows.
Summary
- Adds
permissions: {}at the top level of every workflow that was missing it - Adds explicit job-level
permissionsto jobs that previously inherited the default token permissions - This ensures no workflow accidentally gets broad
GITHUB_TOKENaccess
Affected workflows: ci.yml, scripts.yml, check.yml, semgrep.yml, congrats.yml, continuous_benchmark.yml, issue-triage.yml, merge-fix.yml, merge-main-to-next.yml.
Part of supply chain hardening in response to the TanStack/Mini Shai-Hulud compromise.
Summary
- Removes pnpm store cache (
cache: "pnpm") from the release workflow - Removes Turbo remote cache (
TURBO_TOKEN/TURBO_TEAM) - Adds
--forceto the build step to skip any local Turbo cache - Adds
--frozen-lockfileto ensure the lockfile is the source of truth
The release workflow now does a fully clean install and build from scratch every time. This eliminates cache poisoning as an attack vector for the publish pipeline.
Part of supply chain hardening in response to the TanStack/Mini Shai-Hulud compromise.
Summary
Hardens the preview-release workflow against the attack vector used in the TanStack/Mini Shai-Hulud compromise.
Two changes:
- Block fork PRs from preview releases. Adds a
head.repo.full_name == github.repositorycheck so only branches withinwithastro/astrocan trigger preview builds. This prevents a malicious fork from running code in a privileged context. - Split into two jobs to isolate OIDC token. The
buildjob runspnpm installandpnpm buildbut has noid-token: write. Thepublishjob hasid-token: writebut only downloads build artifacts and runspkg-pr-new— it never executes user code. This ensures the OIDC token is never available during code execution, even for non-fork PRs.
The build job uploads only the minimal files needed for publishing (root workspace files + affected package directories), not the entire workspace.
Description
This PR contains various improvements to the E2E tests mostly focusing on CI performance.
Here is a table summarizing some of the most impactful changes comparing the before, before average, and after times on my personal machine and CI:
| Task | Host | Before1 | Before average | After |
|---|---|---|---|---|
| E2E tests | Personal Mac | 43.6s | N/A | 28.9s |
| E2E tests | CI - Linux | 2m 27s | 2m 34s | 1m 29s |
| E2E tests | CI - Windows | 5m 9s | 5m 11s | 3m 11s |
| A11y tests | Personal Mac | 1m 1s | N/A | 41s |
| A11y tests | CI - Linux | 2m 39s | 2m 43s | 1m 47s |
You can find below a review were I try to comment on the reasoning behind most of the changes.
Footnotes
-
For CI times, the "before" times are from merging to
mainthe recent autogenerated sidebar groups refactor. ↩
Summary
- Pins
actions/labelerfrom mutable@v4tag to commit SHAac9175f8a1f3625fd0d4fb234536d26811351594(v4.3.0) - This was the only action in the repo not pinned to SHA
- This workflow runs on
pull_request_targetwith write permissions, so a tag hijack would affect every PR including forks
Part of supply chain hardening in response to the TanStack/Mini Shai-Hulud compromise.
Changes
Close #16248
Root cause
Dependencies imported within .astro file frontmatter are not detected during Vite's initial Dependency Optimization phase. This leads to several issues.
Solution
I have added an onResolve handler to the astro-frontmatter-scan plugin to ensure proper dependency discovery
Testing
Before Implemented
After Implemented
Docs
Summary
Fixes #16705
Fixes remoteBindings: false being ignored during astro build. The Cloudflare prerenderer's internal Vite preview server now receives the user's adapter options, so remote-flagged bindings (e.g. a D1 database with remote: true in wrangler.toml) are emulated locally during build, matching the existing astro dev behavior.
Changes
The fix is accomplished by forwarding cloudflareOptions (which include remoteBindings setting) to createCloudflarePrerenderer()
Fixes #14013.
On case-insensitive filesystems (Windows, macOS), starting astro dev from a cwd whose case differs from the actual filesystem case (e.g. d:\dev\app vs. D:\dev\app) made the project root and Vite-resolved module ids disagree on case. normalizeFilename then failed its commonAncestorPath check on the absolute id, treated it as a server-relative URL, and concatenated it onto the root — producing a bogus path that missed the compile metadata cache and stripped scoped styles.
The lookup now falls back to a case-insensitive ancestor check, so absolute paths inside the project are recognized regardless of case and the cache hit succeeds.
Changes
- Content entry styles (
?astroPropagatedAssets) are now collected at render time instead of being baked into the module at transform time. The old approach captured a snapshot of the Vite module graph during the transform hook — if the graph was incomplete at that moment, styles were permanently missing. A newdev-style-collector.tsregistry (shared viaglobalThisSymbol.forto bridge the Vite plugin and SSR runner module caches) lets theContentcomponent inruntime.tscallgetStylesForURLfresh on each render. - Adds
templateDepthguards tohead/maybe-headinstructions incommon.tsso thatmaybeRenderHead()never fires inside an inert<template>element in layouts (e.g. theme-provider icon sprites).
Closes #16181
Testing
test/content-collections-dev-css.test.ts— dev server integration test verifying scoped styles from.astrocomponents in MDX content entries are present across multiple collections and with component indirection.test/units/render/inert-head-guard.test.ts— verifiestemplateDepthguard suppresses head rendering inside templates.test/units/render/head-in-template.test.ts— simulates Layout → ThemeProvider →<template>→ Icon pipeline, verifying styles land in<head>.
Docs
- No docs update needed — bug fix restoring expected behavior.
The updateImageReferencesInData function uses Traverse.map which uses a custom cloning algorithm and as such does not copy correctly instances of native objects such as Map and Set rendering them unusable.
Instead Travserse.forEach can be used after cloning the data object beforehand with the native structuredClone function.
Closes #16682
Changes
- Use
Travserse.forEachinstead ofTraverse.mapinupdateImageReferencesInData - Clone data before updating image references
- Update getEntry to not mutate data in store when updating image references
Testing
- 2 new tests added to cover Map/Set regressions
Docs
- no docs changes, this is a bug fix
fixes: #16688
Changes
There was an issue where passing the entry property, returned by the getLiveEntry function imported from astro:content, to the render function caused a type error.
This pull request adds tests and a changeset to the fix originally authored by Houston bot.
The
render()function type inpackages/astro/templates/content/types.d.tsonly has an overload for regular content collection entries (DataEntryMap[C][string]), but no overload forLiveDataEntry. When a project uses onlylive.config.ts,DataEntryMapis empty, sorender()acceptsnever. The live collections RFC and the existing test fixture both showrender(entry)being used with live entries — the type overload was simply missed when live collections were added in PR #13685.
I found a potential fix for this issue. The fix adds a
render()overload forLiveDataEntry<LiveLoaderDataType<C>>to the type template. It's a 4-line change that brings the type declarations in line with the runtime behavior. All existing tests (live loaders, content collections, type inference, render) continue to pass.
Testing
Added a test using the astro check command to verify that no type errors occur.
No changes were made to the test fixtures themselves. The error was originally occurring at the following location.
| import { getLiveEntry, render } from "astro:content"; | |
| const { entry } = await getLiveEntry("liveStuff", "123"); | |
| if (!entry) { | |
| return new Response(null, { status: 404 }); | |
| } | |
| const { Content } = await render(entry); |
src/pages/index.astro:10:34 - error ts(2345): Argument of type 'LiveDataEntry<{ ... }>' is not assignable to parameter of type 'never'.
10 const { Content } = await render(entry);
~~~~~Docs
N/A, bug fix
Changes
- When the triage bot produces a high-confidence fix (fix verified + unit test created), it now publishes a preview release via pkg.pr.new so reporters can immediately test the fix with
npm i https://pkg.pr.new/astro@<sha>. - The fix skill now generates a changeset file (
.changeset/<slug>.md) for every successful fix, making the branch PR-ready with proper changelog entries. - Preview URLs are included in the triage bot issue comment under a "Try this fix" section, giving reporters a one-liner install command.
Testing
- No test changes — this modifies CI agent infrastructure (skill instructions, Flue orchestrator, GHA workflow).
Docs
- No docs needed — this is internal CI tooling, not user-facing.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| pnpm/action-setup | action | patch | v6.0.6 → v6.0.7 |
Release Notes
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
- What does this change?
- Be short and concise. Bullet points can help!
- Before/after screenshots can help as well.
- Don't forget a changeset! Run
pnpm changeset. - See https://contribute.docs.astro.build/docs-for-code-changes/changesets/ for more info on writing changesets.
Testing
Docs
Changes
This PR removes a bunch of tests and assertions that are already checked against unit tests, so there's so reason to use a dev server for that.
The few that were still needed, they are checked against the build because it's the same behaviour
Testing
Green CI
Docs
Changes
- Adds
ASTRO_VITE_ENVIRONMENT_NAMES.astrotoisAstroServerEnvironment()inpackages/astro/src/environments.ts. The internal'astro'environment is a Node.js fallback used when the'ssr'environment is not runnable (e.g. Cloudflare adapter with workerd). Without this, Vite plugins like the script loader served actual client-side script content in this environment, causingHTMLElement is not definedcrashes on HMR reload.
Closes #16626
Testing
- New unit test
packages/astro/test/units/environments.test.tscoveringisAstroServerEnvironmentandisAstroClientEnvironmentfor all four environment names (ssr,prerender,astro,client).
Docs
- No docs update needed — this is an internal bug fix with no API change.
Changes
This PR targets the integration tests that user the dev server. The vast majority of the logic is already tested in a unit test environment, so they were removed.
some functions that were used in the dev server as middleware, they are extracted as a unit test function and tested.
Testing
Removed some fixtures. Green CI.
Docs
Changes
I encountered the following flaky test:
https://github.com/withastro/astro/actions/runs/25663263442/job/75329416110#step:8:7300
Error: EPERM: operation not permitted, rename 'D:\a\astro\astro\packages\astro\test\fixtures\astro-assets-prefix\node_modules\.astro\data-store.json.tmp' -> 'D:\a\astro\astro\packages\astro\test\fixtures\astro-assets-prefix\node_modules\.astro\data-store.json'
I investigated this error. Two test files test/asset-query-params.test.ts and test/astro-assets-prefix.test.ts are using the same fixture ./fixtures/astro-assets-prefix/. Although these two tests have different outDir as added in #16382, they still share the same cache directory ./fixtures/astro-assets-prefix/node_module/.astro/. This seems to be the reason for the flaky test above.
In this PR, I provide a unique cacheDir for every fixture that is used in multiple test files.
15 fixtures are affected:
Fixture (test/fixtures/…) |
Test files |
|---|---|
astro pages |
test/astro-pages.test.tstest/static-build-page-dist-url.test.tstest/static-build-vite-plugins.test.ts |
astro-assets |
test/asset-query-params.test.tstest/astro-assets.test.ts |
astro-assets-prefix |
test/asset-query-params.test.tstest/astro-assets-prefix.test.ts |
astro-basic |
test/astro-basic.test.tstest/astro-sync.test.tstest/config-mode.test.ts |
astro-dev-headers |
test/astro-dev-headers.test.tstest/chrome-devtools-workspace.test.ts |
css-order-layout |
test/css-order-layout.test.tstest/css-order.test.ts |
dev-render |
test/config-format.test.tstest/dev-base.test.tstest/dev-render-chunk.test.tstest/dev-render-components.test.tstest/integration-route-setup-hook.test.ts |
endpoint-routing |
test/endpoint-response.test.tstest/endpoint-routing.test.tstest/endpoint-runtime.test.ts |
fonts |
test/asset-query-params.test.tstest/fonts.test.ts |
middleware space |
test/featuresSupport.test.tstest/middleware.test.ts |
multiple-jsx-renderers |
test/asset-query-params.test.tstest/multiple-jsx-renderers.test.ts |
server-islands/hybrid |
test/csp-server-islands.test.tstest/server-islands.test.ts |
server-islands/ssr |
test/csp-server-islands.test.tstest/server-islands.test.ts |
ssr-assets |
test/ssr-assets.test.tstest/units/app/logger.test.ts |
ssr-request |
test/ssr-adapter-build-config.test.tstest/ssr-request.test.ts |
Testing
CI should pass.
Docs
N/A
Changes
- Applied
@typescript-eslint/no-floating-promisesrule to all tests; - Fixed all issues to pass ESLint. Check my code comments / PR comments for details.
- This PR is the first step. I eventually want to apply the rule to all code, including package sources.
Testing
CI should pass
Docs
N/A
Awesome work by @OfirHaf and @fkatsuhiro! Fixing bug #16633 🎉
This PR is a curated combination of #16674 and #16676.
What does this fix?
Closes #16633
SVG elements with explicit width="0" or height="0" attributes threw NoImageMetadata: Could not process image metadata instead of being imported normally. This is a valid and common pattern for SVG filter containers hidden from screen readers:
<svg width="0" height="0" aria-hidden="true">
<defs>...</defs>
</svg>Changes
issus: #16633
Root cause
Two falsy-zero checks along the metadata extraction path:
vendor/image-size/types/svg.ts — the calculate() function:
// Before: 0 is falsy, so an SVG with width="0" height="0" fell through
if (attrs.width && attrs.height) { ... }
// After: explicit null check allows 0 as a valid dimension
if (attrs.width != null && attrs.height != null) { ... }assets/utils/metadata.ts — post-probe validation:
// Before: !0 === true, triggering the error for zero dimensions
if (!result.height || !result.width || !result.type) { ... }
// After: only null/undefined signals a missing value
if (result.height == null || result.width == null || !result.type) { ... }Changes
packages/astro/src/assets/utils/vendor/image-size/types/svg.ts: replace truthiness check with null check incalculate()packages/astro/src/assets/utils/metadata.ts: replace truthiness check with null check inimageMetadata()
Both changes are minimal and surgical — they only affect the specific falsy-zero edge case while leaving all other behavior unchanged. SVGs with missing dimensions (null/undefined) still throw the appropriate error.
Testing
Befor implement testing
After implement testing

Changes
Fixes a regression introduced in #16366, where Astro.cache was undefined when experimental.cache was not configured.
The previous documented behaviour is for Astro.cache to always be defined as a no-op shim: cache.set() warns once, cache.invalidate() throws and cache.enabled can be used to gate. This allows library and user code can call cache methods without conditional checks. The cache provider registration was being gated at the call site on experimental.cache being configured, which meant the disabled shim branch inside the provider was unreachable and the Astro.cache getter was never attached to the context.
Testing
Added a regression test
Docs
Bug fix
Summary
createStaticHandler in @astrojs/node looks up per-route static headers (e.g. Content-Security-Policy) using header.pathname.includes(baselessPathname). The headers map is keyed by exact prerendered route pathnames written by core/build/generate.ts (routeToHeaders.set(pathname, ...)), and the comment immediately above the lookup states that headers are "keyed by base-less route paths" — so the intent is keyed lookup, not substring matching.
Substring matching causes the wrong headers to be applied whenever one prerendered route's path is a substring of another's:
- Two prerendered routes
/usersand/users/profile, each with a different CSP. /users/profileis inserted intorouteToHeadersfirst → it ends up first in the JSON array.- A request for
/usersmatches the route correctly viaapp.match, butheadersMap.find(h => h.pathname.includes("/users"))returns the/users/profileentry. - The wrong CSP is sent on the
/usersresponse.
The fix is a one-line change to use === instead of .includes().
Changes
packages/integrations/node/src/serve-static.ts: matchheader.pathnameby equality.- Changeset:
@astrojs/nodepatch.
Test plan
-
pnpm -C packages/integrations/node testpasses. - Manual: build a project with two prerendered routes where one path is a prefix of the other, both emitting different CSPs via the static-headers feature; confirm each route receives its own CSP under
@astrojs/nodestandalone.
Changes
This PR adjusts the reference zod support to allow numbers that are transformed to strings.
This has been an issue with collection-A markdown content that references collection-B entries with 1234 in the frontmatter as the yaml is parsed to numbers.
Testing
Added a small unit test that tests the case.
Docs
/cc @withastro/maintainers-docs I am not if documentation for this is needed.
Description
Opposite of:
but the goal is the same: to silence the Postinstall Scripts Unapproved notification when pnpm install.
References:
| Package | URL | Quote |
|---|---|---|
| esbuild | evanw/esbuild#4085 (comment) | The install script still optimizes the command by replacing the JavaScript shim by the actual binary executable, so that running the esbuild command directly doesn't have to run node first just to shell out to the executable. So it's not necessary for correctness (and you're welcome to disable the install script if you'd like to and if it still works for you) but it might result in esbuild being slower than it would have been otherwise. |
| sharp | https://sharp.pixelplumbing.com/install/ | When using |
Changesets are NOT required.
Footnotes
-
equivalent to
allowBuilds=falsein pnpm v10.26+. ↩
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.
next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.
Releases
astro@7.0.0-alpha.1
Patch Changes
- #16603
deaaf3fThanks @alexanderniebuhr! - Removes the warning that Astro does not support vite v8, since Astro v7 does support vite v8
Description
- Fixes #3890 (review) & #3890 (comment)
Tweak a comment: "non-Firefox" → "Chromium-based"
Safari has already supported the syntax :lang(zh, ja).
The position of Opera is the culprit.
This change is so trivial that we should ship it with other changes.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| pnpm/action-setup | action | patch | v6.0.5 → v6.0.6 |
| withastro/automation | action | digest | c8a2e8b → 3754a74 |
Release Notes
pnpm/action-setup (pnpm/action-setup)
v6.0.6
What's Changed
Full Changelog: pnpm/action-setup@v6.0.5...v6.0.6
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
- Adds a new "Write a Unit Test" step to the triage bot's fix skill, so it produces a regression test alongside every fix attempt.
- Creates
reference/unit-testing.mddocumenting unit test conventions, file placement, and the shared test utilities/mocks available in the repo. Referenced fromAGENTS.mdso any agent (not just triage) can use it. - Adds a Unit Test line to the triage bot's GitHub comment template.
Testing
- No test changes — this PR only modifies agent instructions and reference docs.
Docs
- No user-facing docs needed.
reference/unit-testing.mdis agent-facing only.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.39.2
Patch Changes
Description
- Fixes #3872 (comment)
:lang(zh, ja) used in #3872 shamefully has not been supported Chromium and Safari. As a result, text-autospace has been enabled only in Firefox. Replaced it with the combination of :lang(zh) and :lang(ja).
Screenshot in Chrome (Pay attention to the slight presence of whitespace around "Astro" and "Starlight"):
Before (https://starlight.astro.build/ja/getting-started/):
After:

Changes
Since the creation of astro:assets, there has been many lingering issues regarding default output formats. The way the pipeline has to work for SSG / SSR, perf reasons etc, means that we typically don't know the format of the image for real if its remote, which is mostly fine except for one case: SVGs. Since we don't want to convert SVGs to WebP by default, and also the reverse, we often need to know what we're working with to set the default format.
Previously, this wasn't that much of an issue, because imo, it's kinda edge casey and being remote images meant that anyway it's up to the user to authorize. However, now in 6.3 with dangerouslyProcessSVG, it's actually problematic to not really know what we're dealing it, because it could mean a SVG go through when it shouldn't, but also the reverse!
This PR tries to fix this through a few methods:
- Do a best effort guess at the format early on, allowing ourselves to fail sometimes
- ... If we failed and we're not converting, and we're in SSG, use the service's way of inferring formats to craft the proper file type, or let the service handle it (as it should, since images could anyway come from a random request!)
- Harden the service against these problems in various ways
Testing
Updated and added tests for certain of the newly covered cases
Docs
N/A
Changes
Updates the attribute escaping logic to use named entities (& and ") instead of numeric entities (& and ").
Currently, <title> tags render named entities, while other meta tags render numeric entities. Although both formats are semantically equivalent, this results in inconsistent raw HTML output.
This change aligns the escaping behavior across all head and meta tags so they consistently render named entities.
Closes #16657
Testing
Updates the test cases that rendered & and " to instead render & and ".
Docs
N/A
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.2
Patch Changes
-
#16675
11d4592Thanks @ascorbic! - Fixes a regression whereAstro.cachewasundefinedwhenexperimental.cachewas not configured.The previous documented behavior is for
Astro.cacheto always be defined as a no-op shim:cache.set()warns once,cache.invalidate()throws andcache.enabledcan be used to gate. This allows library and user code can call cache methods without conditional checks. The cache provider registration was being gated at the call site onexperimental.cachebeing configured, which meant the disabled shim branch inside the provider was unreachable and theAstro.cachegetter was never attached to the context. -
#16691
0f0a4ceThanks @matthewp! - FixesHTMLElement is not definederror during HMR when using components with client-side scripts (e.g. Starlight<Tabs>) and the Cloudflare adapter -
#16562
07529ecThanks @matthewp! - Fixes non-prerendered routes failing when a dynamic prerendered route exists in the same project withprerenderEnvironment: 'node' -
#16638
272185bThanks @ematipico! - Fixes a bug where the Astro compiler wasn't freed at the end of the build. After the fix, the memory used by the compiler is now correctly freed at the end of the build. -
#16544
d365c97Thanks @matthewp! - TightensisRemotePath()to reject control characters after a leading slash and fixes the dev image endpoint origin check -
#16685
889e748Thanks @farrosfr! - Improve validation messages forsecurity.csp.directiveswhenscript-srcorstyle-srcare incorrectly placed in thedirectivesarray. -
#16605
772f13aThanks @rururux! - FixesassetsPrefixnot being available onbuildfromastro:config/server. -
#16556
f38dec7Thanks @matthewp! - Rejects double-encoded URL paths with a 400 response instead of silently falling back to partial decoding -
#16659
38bcb25Thanks @jsparkdev! - Fixes&characters appearing as raw entity strings (e.g.&) in<meta>tags when viewed in link previews or raw HTML. -
Updated dependencies [
d365c97,9256345]:- @astrojs/internal-helpers@0.9.1
- @astrojs/markdown-remark@7.1.2
@astrojs/prism@4.0.2
Patch Changes
- #15723
9256345Thanks @rururux! - Fixes an issue where the<Prism />component failed to work in Cloudflare Workers.
@astrojs/cloudflare@13.5.1
Patch Changes
-
#16707
2ff3f8fThanks @helio-cf! - FixesremoteBindings: falsebeing ignored duringastro build. The Cloudflare prerenderer's internal Vite preview server now receives the user's adapter options, so remote-flagged bindings (e.g. a D1 database withremote: trueinwrangler.toml) are emulated locally during build, matching the existingastro devbehavior. -
#16652
98c32ccThanks @greatjourney589! - Fixes user-declared KV namespace bindings being duplicated in the generateddist/server/wrangler.json, which caused wrangler validation to fail with " assigned to multiple KV Namespace bindings." The Astro Cloudflare config customizer now returns only the auto-injectedSESSIONbinding and lets@cloudflare/vite-pluginmerge it with the user's wrangler config, instead of pre-merging the user's bindings into the output. -
#16272
4f9521eThanks @barry3406! - Fixes.astrofiles failing withNo matching export in "html:..." for import "default"when default-imported from a.tsfile -
#15723
9256345Thanks @rururux! - Fixes an issue where the<Prism />component failed to work in Cloudflare Workers. -
Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
- @astrojs/underscore-redirects@1.0.3
@astrojs/markdoc@1.0.5
Patch Changes
-
#15723
9256345Thanks @rururux! - Updates internal type usage from@astrojs/prism. -
Updated dependencies [
d365c97,9256345,9256345]:- @astrojs/internal-helpers@0.9.1
- @astrojs/markdown-remark@7.1.2
- @astrojs/prism@4.0.2
@astrojs/mdx@5.0.5
Patch Changes
- Updated dependencies [
9256345]:- @astrojs/markdown-remark@7.1.2
@astrojs/netlify@7.0.9
Patch Changes
-
#16716
04fdbb2Thanks @delucis! - Updates internal dependencies -
Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
- @astrojs/underscore-redirects@1.0.3
@astrojs/node@10.1.1
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
@astrojs/preact@5.1.3
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
@astrojs/react@5.0.5
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
@astrojs/svelte@8.1.1
Patch Changes
@astrojs/vercel@10.0.7
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
@astrojs/internal-helpers@0.9.1
Patch Changes
- #16544
d365c97Thanks @matthewp! - TightensisRemotePath()to reject control characters after a leading slash and fixes the dev image endpoint origin check
@astrojs/ts-plugin@1.10.8
Patch Changes
astro-vscode@2.16.15
Patch Changes
@astrojs/markdown-remark@7.1.2
Patch Changes
Closes #16590
Changes
- Fixes a regression introduced in #16555 where user-declared KV namespace bindings (e.g.
RATE_LIMIT,CACHE) appear twice in the generateddist/server/wrangler.json, causing wrangler to fail with"<binding> assigned to multiple KV Namespace bindings.". - The Astro Cloudflare config customizer now returns only the auto-injected
SESSIONbinding fromkv_namespaces, instead of pre-merging the user's existing bindings into the output. @cloudflare/vite-pluginalready merges the customizer's output with the user's wrangler config, so echoing the user's bindings caused the duplication.- Removed the now-unused
withSessionKVBinding()helper. - Added a changeset (
@astrojs/cloudflarepatch).
Affected versions reported in the issue: @astrojs/cloudflare@13.3.0–13.3.1 + astro@6.2.x. Last working combination was astro@6.1.9 + @astrojs/cloudflare@13.2.1, which matches the pre-#16555 behavior this PR restores.
Testing
- Updated two existing assertions in
packages/integrations/cloudflare/test/wrangler.test.tsthat locked in the buggy merged-output shape. - Added a dedicated regression test
does not include user-declared KV bindings in the output (regression #16590)covering the multi-binding scenario from the issue (RATE_LIMIT+CACHEdeclared by the user) and asserting the customizer returns only[{ binding: 'SESSION' }]. - All 25 tests in
wrangler.test.tspass locally.
Docs
No documentation changes needed. The fix restores documented and previously-shipped behavior — the public API and configuration surface (sessionKVBindingName, automatic SESSION binding provisioning) are unchanged.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.1
Patch Changes
Changes
- Restores the
FREDKBOT_GITHUB_TOKENenv var mapping that was accidentally dropped in the flue 0.3 upgrade (#16441). Without it,postGitHubCommentfails at the end of every triage run.
Testing
- No test changes. Verified by inspecting the failing run.
Docs
- No docs needed — CI-only change.
Description
Adds generic clock, desktop, mobile phone, app window, database, server, git branch, notes, question mark, analytics, and padlock icons. Also adds a new logo icon for SolidJS. The mobile icon has a bit of a funny name — mobile-android — but I kept it to match the source library’s name.
Which icons to include built-in is always a bit subjective, but I went through our icon library and tried to add a handful that might meet some more common needs on docs sites given I was already adding a couple based on user requests.
For the SolidJS logo, I asked on their Discord server to confirm which style they preferred for a monochrome icon (as it deviates from their more common blue gradient style).
Changes
- The generic image endpoint (
assets/endpoint/generic.ts) self-fetches local images from the same origin. #16519 added anisRemoteAllowedcheck on the response URL, but that check rejects local URLs (e.g.http://host/_astro/image.png) since they aren't inimage.domainsorimage.remotePatterns. This caused local images on non-prerendered pages to 404. - Extracted the fetch logic into
loadImage.tswith anisRemoteflag that gates theisRemoteAllowedcheck. Local images skip it — they're already protected by the same-origin guard in the caller.
Fixes #16644
Testing
- Added
test/units/assets/endpoint-load-image.test.tswith 4 cases: local image succeeds, unauthorized remote rejected, allowed remote succeeds, fetch failure handled. The local image test fails without the fix.
Docs
- No docs needed — this is a regression fix restoring prior behavior.
Changes
A flaky test was detected in astro-assets-prefix.test.ts in CI.
https://github.com/withastro/astro/actions/runs/25502409719/job/74839082587
The root cause is likely that tests are now running in parallel, and both astro-assets-prefix.test.ts and astro-assets-prefix-multi-cdn.test.ts share the same fixture while also calling fixture.clean() in their after() hooks, which deletes the cache folder.
When the test fails, the log shows "The collection "blog" does not exist or is empty. Please check your content config file for errors." and as shown here, the getDataStoreFile function references the .astro folder, which is exactly what clean() removes.
astro/packages/astro/src/content/content-layer.ts
Lines 468 to 470 in 45e50e4
| export function getDataStoreFile(settings: AstroSettings, isDev: boolean) { | |
| return new URL(DATA_STORE_FILE, isDev ? settings.dotAstroDir : settings.config.cacheDir); | |
| } |
astro/packages/astro/test/test-utils.ts
Lines 270 to 284 in 45e50e4
| clean: async () => { | |
| await fs.promises.rm(config.outDir, { | |
| maxRetries: 10, | |
| recursive: true, | |
| force: true, | |
| }); | |
| const astroCache = new URL('./node_modules/.astro', config.root); | |
| if (fs.existsSync(astroCache)) { | |
| await fs.promises.rm(astroCache, { | |
| maxRetries: 10, | |
| recursive: true, | |
| force: true, | |
| }); | |
| } | |
| }, |
To fix this, I merged the two test files into one.
An alternative would have been to give each test its own dedicated fixture, but since the two test cases are similar in nature, I went with the merge approach instead.
Testing
Green CI
Docs
N/A
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.39.1
Patch Changes
-
#3885
010eed1Thanks @ArmandPhilippot! - Fixes the version mentioned in an error message related to autogenerated sidebar groups support. -
#3887
b3c6990Thanks @delucis! - Adds 13 new icons:clock,desktop,mobile-android,window,database,server,code-branch,notes,question,question-circle,analytics,padlock, andsolidjs.
Description
The Starlight version mentioned in the error related to autogenerated sidebar groups support is wrong:
Support for autogenerated sidebar groups was removed in Starlight v0.38.0.
#3618 has been released earlier in 0.39.0, not 0.38.0.
I added a changeset because we need a new version for such a small change. 😅
Changes
Migrate a js test file to ts.
Testing
CI should pass.
Docs
N/A
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| changesets/action | action | minor | v1.7.0 → v1.8.0 |
Release Notes
changesets/action (changesets/action)
v1.8.0
Minor Changes
- #258
f5dbf72Thanks @tom-sherman! - Support draft version PR modes with a newprDraftinput. Usecreateto create new version PRs as drafts, oralwaysto also convert existing version PRs back to draft when updating them.
Patch Changes
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
A follow-up to #16471 and #16472
svelte2tsx@0.7.55 supports typescript v6, so we can also update the typescript peer dependency range in @astrojs/svelte.
Testing
CI should pass
Docs
A changeset file is added because we need to release a new version for @astrojs/svelte.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/cloudflare@13.5.0
Minor Changes
- #16639
4d72482Thanks @ematipico! - The adapter now depends on Astro 6.3.0.
@astrojs/node@10.1.0
Minor Changes
- #16639
4d72482Thanks @ematipico! - The adapter now depends on Astro 6.3.0.
Changes
The compiler was never torn down at the end of the build. This PR fixes it.
Testing
Manually tested.
Docs
N/A
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.39.0
Minor Changes
-
#3618
dcf6d09Thanks @HiDeoo! -⚠️ BREAKING CHANGE: This release changes how autogenerated links work in Starlight’s sidebar configuration.If you have sidebar groups using the
autogeneratekey, you must now wrap that configuration in anitemsarray:{ label: 'My group', - autogenerate: { directory: 'some-dir' }, + items: [{ autogenerate: { directory: 'some-dir' } }], }This change unlocks the possibility to mix autogenerated links and other links in a single group, for example:
{ label: 'Mixed group', items: [ 'example-page', { autogenerate: { directory: 'examples' } }, { label: 'More examples', link: 'https://example.com' }, ], }
This release also updates the shape of autogenerated sidebar entries in route data. Autogenerated links and groups in
Astro.locals.starlightRoute.sidebarnow include anautogenerateobject with the configureddirectoryvalue:{ type: 'link', label: 'Example', href: '/examples/example/', isCurrent: false, autogenerate: { directory: 'examples' } }
-
#3618
dcf6d09Thanks @HiDeoo! -⚠️ BREAKING CHANGE: This release changes the default collapsed state of autogenerated sidebar subgroups.Autogenerated subgroups no longer inherit the
collapsedvalue from their parent group. They are now expanded by default unless explicitly configured withautogenerate.collapsed.If your sidebar configuration relies on a collapsed parent group to also collapse its autogenerated subgroups, update your configuration to set
autogenerate.collapsedtotrue:{ label: 'Reference', collapsed: true, items: [ - { autogenerate: { directory: 'reference' } }, + { autogenerate: { directory: 'reference', collapsed: true } }, ], } -
#3845
4d755f5Thanks @delucis! - Adds a<link rel="alternate" hreflang="x-default" href="...">tag pointing to the default locale in multilingual sites. Thex-defaultalternate is used as a signal of which language to fall back to if no other is available. Learn more in Google’s SEO localization docs. -
#3862
ec70630Thanks @itrew! - Makes spacing of items in nested lists more consistent -
#3872
417a66cThanks @tats-u! - Enables the CSS propertytext-autospacein Chinese and Japanese documents.If you would prefer to disable autospacing in Chinese and Japanese pages, you can add the following custom CSS to your site:
[lang]:where(:lang(zh, ja)) { text-autospace: initial; }
-
#3797
9764ebdThanks @delucis! - Avoids the risk of layout shift when users expand and collapse sidebar groupsThis release can introduce additional padding to the site sidebar on certain devices to reserve space for scrollbars. You may wish to inspect your site sidebar visually when upgrading.
If you would prefer to keep the previous styling, you can add the following custom CSS to your site:
.sidebar-pane { scrollbar-gutter: auto; }
-
#3858
6672c35Thanks @delucis! - Updatesi18next, used for Starlight’s localization APIs, from v23 to v26There should not be any user-facing changes from this update
Description
Adds flush() and close() methods to AstroIntegrationLogger, mirroring AstroLogger. Both delegate to the underlying destination's optional flush / close hooks.
AstroLogger already exposes flush() and close() (added in #16404). AstroIntegrationLogger did not, so any code path that placed an AstroIntegrationLogger where an AstroLogger was expected — e.g. App.#prepareResponse calling this.logger.flush() — would crash with TypeError: this.logger.flush is not a function. The two logger classes now have a symmetric public surface.
Closes #16622.
Changes
packages/astro/src/core/logger/core.ts: addflush()andclose()toAstroIntegrationLogger, identical shape to the methods onAstroLogger.packages/astro/test/units/logger/logger.test.ts: coverAstroIntegrationLogger.flush()/.close()(delegation, no-throw, fork inheritance).- changeset:
patch.
Testing
pnpm --filter astro build:ci
pnpm --filter astro exec astro-scripts test test/units/logger/logger.test.ts --strip-types --teardown ./test/units/teardown.ts
# 10 pass, 0 fail
Docs
No public API docs change required — AstroIntegrationLogger.flush() / .close() mirror the existing AstroLogger methods (no behaviour change for destinations that don't define flush/close).
Changes
Fixes #16612
Testing
Should pass! We unfortunately cannot test this in the monorepo because TypeScript is of course always available
Docs
N/A
Changes
Some tests in core-image.test.ts have been flaky in Astro's CI.
https://github.com/withastro/astro/actions/runs/25425893932/job/74579340641?pr=16579
https://github.com/withastro/astro/actions/runs/25407064617/job/74520525804?pr=16614
After investigating, I found that the removeDir function, which is used to clean up the cache directory before tests, was not being awaited despite returning a Promise.
This PR adds the missing await to address the potential race condition.
Testing
Green CI
Docs
N/A
Changes
One of TypeScript's biggest strengths is static analysis. In practice, we rely on it in three places:
- In editors (e.g. vscode / TypeScript language service)
- During local and CI builds via
tsc - During local and CI linting via
eslintwith type-aware linting
All three ultimately depend on TypeScript project configuration through tsconfig.
Before #16241, Astro used three different tsconfig setups for these workflows:
- Editors used an effectively empty root
tsconfig.json, so the language service fell back to its default behavior. tscused per-package configs that only includedsrc/, excluding files likebin/andtest/.eslintused a separatetsconfig.eslint.json.
This inconsistency created a poor developer experience. For example, vscode could report type errors while CI remained green, making editor diagnostics unreliable.
This PR completes the TypeScript project references work introduced in previous PRs. After this PR, all three workflows share the same source of truth: the root tsconfig.json.
The root tsconfig.json now includes all src/, test/, scripts, and configuration files (e.g. prettier.config.mjs). It doesn't include SFC files (.astro, .svelte, etc.) and test fixtures, though.
Also, note that if a JS/TS file is included in eslint but not in the root tsconfig.json, eslint will show the following errors. This is beneficial because it ensures that we have all files added to tsconfig.json, thus these files are covered by tsc.
/astro/packages/astro/e2e/astro-island-hydration-error.test.js
0:0 error Parsing error: /astro/packages/astro/e2e/astro-island-hydration-error.test.js was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject
Testing
CI should pass
Docs
N/A
Changes
Migrate some tests from JS to TS.
These tests come from an old pull request, which was merged into main after the TypeScript migration was completed.
Testing
N/A
Docs
N/A
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| actions/labeler | action | minor | v6.0.1 → v6.1.0 |
Release Notes
actions/labeler (actions/labeler)
v6.1.0
Enhancements
- Add changed-files-labels-limit and max-files-changed configuration options to cap the number of labels added by @bluca in #923
Bug Fixes
- Improve Labeler Action documentation and permission error handling by @chiranjib-swain in #897
- Preserve manually added labels during workflow runs and refine label synchronization logic by @chiranjib-swain in #917
Dependency Updates
- Upgrade brace-expansion from 1.1.11 to 1.1.12 and document breaking changes in v6 by @dependabot in #877
- Upgrade minimatch from 10.0.1 to 10.2.3 by @dependabot in #926
- Upgrade dependencies (@actions/core, @actions/github, js-yaml, minimatch, @typescript-eslint) by @Copilot in #934
New Contributors
- @chiranjib-swain made their first contribution in #897
- @bluca made their first contribution in #923
- @Copilot made their first contribution in #934
Full Changelog: actions/labeler@v6...v6.1.0
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
The Houston bot for the triage workflow is currently not working.
https://github.com/withastro/astro/actions/runs/25424727887/job/74575139340
The log shows the following:
pnpm exec flue run issue-triage \
--target node \
--session-id "issue-triage-$ISSUE_NUMBER" \
--payload "{\"issueNumber\": $ISSUE_NUMBER}"
Unknown argument: --session-id
Looking into the flue repository, I couldn't find the --session-id argument anywhere.
Instead, I found that --id is defined as the correct argument.
https://github.com/withastro/flue/blob/808b34ee154b94025d5cb2b45288d88b7766ae0f/packages/cli/bin/flue.ts#L95-L108
I also confirmed that commit 8388ed1 in the flue repository changed --session-id to --id.
Based on this, I concluded that --id should be used instead of --session-id, and updated it accordingly.
Testing
I don't know how to do it 😔
Docs
N/A
Changes
Right now, types for entry.data field returned by getLiveEntry() are not working.
---
import { getLiveEntry } from "astro:content";
const { entry } = await getLiveEntry("example", "hello-world");
const data = entry?.data; // type `any` instead of defined type from loader.
---The cause is that the utility type ExtractDataType used here is missing, so entry.data is always of type any.
This pr adds the missing utility type to the file.
Testing
Tested by manually adding the missing utility type to the generated .astro/content.d.ts in my projects and verifing the expected types to work.
Docs
It adds missing typing.
Fixes the smoke test failure caused by astro-expressive-code@0.42.0 being too recently published to pass the 3-day minimumReleaseAge gate. Since this is a Starlight dependency used by our docs smoke test, it should always be installable regardless of release age.
Another PR updating depedencies. Expressive Code is the main motivator as the latest release updates to Shiki v4 (thanks again @ocavue!) so Starlight will share this with Astro instead of having v3 and v4 in the tree.
Prod dependencies
- Updates
astro-expressive-codein Starlight (0.41.7 => 0.42.0)
Dev depdendencies
- Updates
astroacross monorepo (6.1.9 => 6.2.2) - Updates
@astrojs/check(0.9.8 => 0.9.9) - Updates
pnpm(10.33.2 => 10.33.3) - Updates linters
eslint(10.2.1 => 10.3.0)globals(17.5.0 => 17.6.0)typescript-eslint(8.59.0 => 8.59.2)
Haven’t bothered with a changeset as we already have an unreleased “update deps” patch changeset in the pipeline. Should see no user-facing change in any case.
Changes
This PR adds the missing * line from a code sample in JSDoc, which basically removed the whitespace between the imports and the code.
This doesn't need a changeset, AFAIK
Testing
Docs
That's all it is.
Changes
Implements RFC: astro dev --background. When an AI coding agent is detected (via am-i-vibing), astro dev automatically starts the dev server as a detached background process so it doesn't block the agent's terminal.
- Adds
astro dev --backgroundflag andastro dev stop,astro dev status, andastro dev logssubcommands for managing background dev servers - Writes a lock file (
.astro/dev.json) on server start to prevent duplicate servers and enable the subcommands to find the running instance - Adds a
/_astro/statushealth endpoint for programmatic readiness checks astro dev logs --followstreams new log output as it's written, exits when the server diesastro dev stopandastro dev --background --forceescalate to SIGKILL if the process doesn't exit within 5 seconds of SIGTERM- Reports detected agent id, name, and type in
ASTRO_CLI_SESSION_STARTEDtelemetry events for all CLI commands
No action required from users. If no agent is detected, astro dev behaves exactly as before.
Testing
- Unit tests for lock file parsing, serialization, stale detection, and CLI output formatters (
test/units/dev/lockfile.test.ts,test/units/dev/dev-output.test.ts)
Docs
Changes
Fixes a regression from #16277 where the generated wrangler.json sets
assets.directory to include the base prefix (e.g. "../client/blog"
instead of "../client").
Cloudflare's asset binding resolves the full request URL path (including
the base) against the directory. With the prefixed path, requests would
resolve to a double-nested location (e.g. client/blog/blog/file.js),
causing 404s for all static assets.
Related: #16276
Testing
Added a test to with-base.test.ts verifying the assets directory in
the generated wrangler.json points to the un-prefixed client root.
Docs
fixes: #16604
Changes
As reported in the issue, the migration guide and deprecation comments recommended using build.assetsPrefix from astro:config/server as a replacement for the deprecated import.meta.env.ASSETS_PREFIX, but astro:config/server was not actually exporting build.assetsPrefix.
astro/packages/astro/client.d.ts
Lines 11 to 15 in 3740b24
| /** | |
| * The prefix for Astro-generated asset links if the build.assetsPrefix config option is set. This can be used to create asset links not handled by Astro. | |
| * @deprecated This will be removed in a future major version of Astro. Use `build.assetsPrefix` from `astro:config/server` instead. | |
| */ | |
| readonly ASSETS_PREFIX: string | Record<string, string>; |
This PR fixes the implementation so that build.assetsPrefix is correctly exported from astro:config/server.
Testing
Added tests to verify that build.assetsPrefix is properly exported and that its value can be read correctly.
Docs
The "Upgrade to Astro v6" page documents this under "Deprecated: import.meta.env.ASSETS_PREFIX" and recommends using build.assetsPrefix instead, but the "Imports from astro:config/server" reference page does not yet list build.assetsPrefix.
A follow-up PR to update the docs will be submitted separately.
Changes
- Moves conflict resolution and changeset cleanup into the
merge-main-to-nextaction so the branch is clean and buildable before CI runs. AI only runs when there are actual conflicts; clean merges skip it entirely. - Strips the
merge-fixaction down to just test fixing — no more conflict stripping, no--no-frozen-lockfile. Uses--frozen-lockfilesince the lockfile is already correct. - Adds critical rules to the
fix-testsskill based on analysis of a 1-hour timeout: never runpnpm install, time-box investigation to 5 min, diff before deep-reading source, batch bash commands.
Testing
- No test changes — these are CI workflow and skill instruction changes.
Docs
- No docs needed — internal CI automation only.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.0
Minor Changes
-
#16366
d69f858Thanks @matthewp! - Adds a newexperimental.advancedRoutingoption that lets you take full control of Astro's request handling pipeline by creating asrc/app.tsfile in your project.Today, Astro handles every incoming request through a fixed internal pipeline: trailing slash normalization, redirects, actions, middleware, page rendering, i18n, and so on. That pipeline works great for most sites, but as projects grow you often want to run your own logic between those steps — an auth check before rendering, a rate limiter before actions, custom logging around the whole stack. Advanced routing gives you that control.
When enabled, Astro looks for a
src/app.tsfile in your project. If it finds one, that file becomes the entrypoint for all server-rendered requests. You compose the pipeline yourself using the handlers Astro provides, and you can slot your own logic anywhere in the chain.Enabling advanced routing
// astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ experimental: { advancedRouting: true, }, });
Two ways to build your pipeline
Astro ships two entrypoints for advanced routing:
astro/fetchandastro/hono.astro/fetchis a low-level, framework-free API built on the Web Fetch standard. You create aFetchStatefrom the incoming request, then call handler functions in sequence. Each handler takes the state, does its work, and returns aResponse(orundefinedto pass through). This is the core primitive that everything else is built on:// src/app.ts import { FetchState, trailingSlash, redirects, actions, middleware, pages, i18n, } from 'astro/fetch'; export default { async fetch(request: Request) { const state = new FetchState(request); // Early exits — these return a Response only when they apply. const slash = trailingSlash(state); if (slash) return slash; const redirect = redirects(state); if (redirect) return redirect; const action = await actions(state); if (action) return action; // Middleware wraps page rendering; i18n post-processes the response. const response = await middleware(state, () => pages(state)); return i18n(state, response); }, };
astro/honowraps the same handlers as Hono middleware, so you can mix Astro's pipeline with Hono's ecosystem of middleware (logger, CORS, JWT, rate limiting, etc.) using theapp.use()pattern you already know:// src/app.ts import { Hono } from 'hono'; import { getCookie } from 'hono/cookie'; import { logger } from 'hono/logger'; import { actions, middleware, pages, i18n } from 'astro/hono'; const app = new Hono(); app.use(logger()); // Auth gate — only runs for /dashboard routes. app.use('/dashboard/*', async (c, next) => { const session = getCookie(c, 'session'); if (!session) return c.redirect('/login'); return next(); }); app.use(actions()); app.use(middleware()); app.use(pages()); app.use(i18n()); export default app;
Both approaches give you the same power — pick whichever fits your project. If you don't need a framework,
astro/fetchkeeps things minimal. If you want a rich middleware ecosystem,astro/honogets you there with one import.For more information on enabling and using this feature in your project, see the experimental advanced routing docs. To give feedback, or to keep up with its development, see the advanced routing RFC for more information and discussion.
-
#16366
d69f858Thanks @matthewp! - Adds aconsume()instance method toAstroCookies. This method marks the cookies as consumed and returns theSet-Cookieheader values. After consumption, any subsequentset()calls will log a warning, since the headers have already been sent.Previously this was only available as a static method
AstroCookies.consume(cookies). The static method is now deprecated but kept for backward compatibility with existing adapters. -
#16412
ba2d2e3Thanks @0xbejaxer! - Add retry and error event handling forastro-islandhydration import failures to reduce unrecoverable hydration errors on transient network failures. -
#16582
885cd31Thanks @Princesseuh! - Adds a newimage.dangerouslyProcessSVGflag to optionally enable processing SVG inputs. For security reasons, Astro will no longer rasterizes SVG image sources by default in its default image service and endpoint.Set
image.dangerouslyProcessSVG: trueto opt back into processing SVG inputs.// astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ // ... image: { dangerouslyProcessSVG: true, }, });
Note that this is a breaking change for users who were previously relying on Astro's default image service to rasterize SVG inputs, but it is a necessary change to improve security and prevent potential vulnerabilities.
-
#16519
1b1c218Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in
image.remotePatternsor a domain inimage.domains, Astro will fail with a helpful error message.In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:
export default defineConfig({ image: { domains: ['example.com', 'cdn.example.com'], }, });
{ /* Redirects to https://cdn.example.com/assets/image.png: */ } <Image src="https://example.com/assets/image.png" width="1920" height="1080" alt="An example image." />; { /* Redirects to https://malicious.com/image.png: */ } <Image src="https://example.com/bad-image.png" width="1920" height="1080" alt="An example image." />;
In cases where all redirects to HTTPS hosts should be trusted, the following configuration for
image.remotePatternscan be used:export default defineConfig({ image: { remotePatterns: [ { protocol: 'https', }, ], }, });
Patch Changes
-
#16592
9c6efc5Thanks @matthewp! - Escapes interpolated values in the dev server redirect HTML template, consistent with how the 404 template already handles them -
#16585
78f305eThanks @web-dev0521! - Fixesz.array(z.boolean())in form actions incorrectly coercing the string"false"totrue. Boolean array elements now use the same'true'/'false'string comparison as singlez.boolean()fields, so submitting["false", "true", "false"]correctly parses as[false, true, false]. -
#16567
12a03f2Thanks @matthewp! - Fixes deleted content collection entries persisting ingetCollection()results during dev -
#16595
ce9b25cThanks @web-dev0521! - FixespushDirectivein the CSP runtime duplicating the new directive once per existing non-matching directive. CallinginsertDirective()(or otherwise pushing a directive whose name is not yet in the list) now appends it exactly once, and a directive that merges with a later existing entry no longer leaves an unmerged copy behind. -
#16600
94e4b7cThanks @web-dev0521! - FixesAstro.preferredLocalereturning the wrong value wheni18n.localesmixes object-form entries ({ path, codes }) with string entries that normalize to the same locale. The first matching code in the configuredlocalesorder is now selected, matching the documented behavior. -
#16591
cce20f7Thanks @matthewp! - Uses a consistent generic error message in the image endpoint across all adapters -
#16629
f54be80Thanks @g-taki! - Fixes a bug where SSR responses inastro devcould crash withTypeError: this.logger.flush is not a function. -
#16589
3740b24Thanks @ArmandPhilippot! - Fixes an outdated code snippet in the documentation for session storage configuration. -
Updated dependencies [
354e231]:- @astrojs/telemetry@3.3.2
@astrojs/cloudflare@13.4.0
Minor Changes
-
#16519
1b1c218Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in
image.remotePatternsor a domain inimage.domains, Astro will fail with a helpful error message.In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:
export default defineConfig({ image: { domains: ['example.com', 'cdn.example.com'], }, });
{ /* Redirects to https://cdn.example.com/assets/image.png: */ } <Image src="https://example.com/assets/image.png" width="1920" height="1080" alt="An example image." />; { /* Redirects to https://malicious.com/image.png: */ } <Image src="https://example.com/bad-image.png" width="1920" height="1080" alt="An example image." />;
In cases where all redirects to HTTPS hosts should be trusted, the following configuration for
image.remotePatternscan be used:export default defineConfig({ image: { remotePatterns: [ { protocol: 'https', }, ], }, });
Patch Changes
- Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/language-server@2.16.8
Patch Changes
- #16627
5778cb7Thanks @Princesseuh! - Fixes unintended dependency on thetypescriptpackage being available to the language server
@astrojs/telemetry@3.3.2
Patch Changes
- #16260
354e231Thanks @gameroman! - Refactors internal config logic to remove thedlvdependency in favor of native logic
Changes
- Fix
pushDirectivein the CSP runtime duplicating the new directive once per existing non-matching directive. - Root cause: the
elsebranch inside the loop pushednewDirectiveon every non-match instead of once after the loop. Replaced thededuplicatedflag with amatchedflag and moved thenewDirectivepush outside the loop, guarded by!matched. - Closes #16594.
Before: pushDirective(["img-src 'self'", "style-src 'self'"], "script-src 'self'") → ["img-src 'self'", "script-src 'self'", "style-src 'self'", "script-src 'self'"] (duplicated + interleaved)
After: → ["img-src 'self'", "style-src 'self'", "script-src 'self'"]
Also fixes Mode 2: a directive that merges with a later existing entry no longer leaves an unmerged copy in front of the merged result.
Don't forget a changeset! Run
pnpm changeset. — Added:.changeset/fix-csp-push-directive-duplicate.md
Testing
- Added a
regression: issue #16594suite inpackages/astro/test/units/csp/runtime.test.tscovering:- Mode 1 — non-matching new directive appended exactly once.
- Mode 2 — merge with a later match, no unmerged copy left behind.
- Empty input list edge case.
- Many non-matches: directive-name uniqueness asserted via a
Mapcount. - Table-driven sweep across "non-match", "match in middle", "match at end" shapes, asserting both length and
Set-based name uniqueness.
- All 11 tests in
csp/runtime.test.tspass locally (node --test test/units/csp/runtime.test.ts).
Docs
No docs change required — pushDirective is internal CSP runtime; public insertDirective() semantics are unchanged (this restores them to the documented behavior).
Changes
This PR does an overhaul of all error messages to make them more consistent with each other, namely:
- fixing missing full stop at the end of messages.
- adding missing backticks to code excerpts.
- adding () to differentiate functions from simple properties/objects.
- improving error titles/messages with more context.
Testing
I am not sure if adding backticks in a few places could break rendering, we'll be sure once we get the automated PR on docs.
Docs
That's all it is.
Changes
- The redirect template in
3xx.tsinterpolated URL values directly into HTML without escaping. The sibling4xx.tstemplate already escapes its values usinghtml-escaper. This applies the same pattern to the redirect template for consistency.
Testing
- No new tests. The change adds
escape()calls around existing interpolations — the same approach4xx.tsalready uses.
Docs
- No docs update needed.
Changes
- The Node image endpoint (
node.ts) returns a generic'Internal Server Error'string on failure, but the generic adapter endpoint (generic.ts) was interpolating the error object into the response. This alignsgeneric.tswithnode.tsso all adapters use the same consistent error response.
Testing
- No new tests. Single-line string replacement in an error path — existing image endpoint tests cover the surrounding behavior.
Docs
- No docs update needed.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| withastro/automation | action | digest | e27ec6d → c8a2e8b |
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Description
- Closes #3871
Especially in Chinese and Japanese, the CSS property text-autospace: normal is preferred for better looking like books by automatically inserting spaces with the moderate width.
- https://developer.chrome.com/blog/css-i18n-features#inter-script_spacing_text-autospace
- https://developer.mozilla.org/docs/Web/CSS/text-autospace
- https://webkit.org/blog/16574/webkit-features-in-safari-18-4/#:~:text=Text%20auto%20space
- vuejs/vitepress#4996
- web-infra-dev/rspress#2920
- https://github.com/web-infra-dev/rspress/releases/tag/v2.0.0-rc.3
Without this, some users and AIs try to insert such spaces manually as workarounds but they are too wide and prevent keywords from searched.
Japanese:
Before:
After:
Note: the current Chinese content of Starlight translation is not affected because it took the workarounds like manually inserting spaces.
The other languages are not affected.
Changes
- Fix
z.array(z.boolean())in form actions incorrectly coercing the string"false"totrue. handleFormDataGetAllwas usingentries.map(Boolean), butFormDatavalues are always strings andBoolean("false") === true. Replaced with the same'true'/'false'comparison already used for singlez.boolean()fields inhandleFormDataGet.- Closes #16584.
Before: ["false", "true", "false"] → [true, true, true]
After: ["false", "true", "false"] → [false, true, false]
Testing
- Added a new
boolean arrayssuite inpackages/astro/test/units/actions/form-data-to-object.test.ts:should preserve "false" string values in boolean arrays— direct regression test for the reported case.should coerce mixed boolean array values correctly— table-driven coverage across all-true / all-false / mixed inputs.
- All 25 tests in
formDataToObjectpass locally (node --test test/units/actions/form-data-to-object.test.ts).
Docs
No docs change required — this is a behavior fix bringing array-element parsing in line with the already-documented single-boolean coercion behavior.
fixes #13297
Changes
In the current implementation, enabling prefetch settings causes links to be collected via document.getElementsByTagName('a') on the initial page load.
While this approach works for most cases, it fails to cover certain scenarios, such as the one reported in issue #13297.
The root cause of issue #13297 is that when a component with the server:defer attribute uses top-level await, its links are rendered after the Promise resolves, meaning they are not captured by the initial document.getElementsByTagName('a') call.
More broadly, the same issue occurs whenever links are dynamically added to the DOM after the initial page load.
To address this, this pull request adds the observeDynamicLinks option, which uses a MutationObserver to watch for dynamically added link elements.
This ensures that links added after the initial render are properly captured.
This feature is opt-in.
Why MutationObserver?
Investigation process
- First, I checked the code around
server:deferto see if it used any event notifications. - It did not, but I found that the prefetch code [uses the
astro:page-loadevent].
astro/packages/astro/src/prefetch/index.ts
Line 316 in 7711e47
document.addEventListener('astro:page-load', () => { - When I looked into whether
astro:page-loadcould be used here, I found that the constant defining this event name is marked as@deprecated, so I decided to look for a different approach.
astro/packages/astro/src/transitions/events.ts
Lines 12 to 13 in 7711e47
/** @deprecated This will be removed in Astro 7 */ export const TRANSITION_PAGE_LOAD = 'astro:page-load'; - I also considered that the root cause of issue #13297 is not specific to
server:defer, the same problem can occur in other situations where links are added to the DOM dynamically. For this reason, I wanted an approach that resolves the issue within the prefetch code itself, rather than adding prefetch-dependent logic to theserver:deferside. - As a result, I concluded that
MutationObserveris the best approach, as it is well-suited for observing DOM changes and allows the fix to be fully self-contained within the prefetch implementation.
Testing
Added tests to verify that the observeDynamicLinks option works correctly in the following scenarios:
- Links dynamically added via
server:defer, as reported in issue #13297 - Links dynamically added by a toggle component (
ToggleButton.jsx)
Docs
/cc @withastro/maintainers-docs for feedback!
Changes
SVGs are always troublesome in the end and wanting to actually process them is so uncommon that it's best to leave it to the user.
I decided to put it on a global image option so that other services could also use it, but I'd be okay with it being a service config as well, it's ok
Testing
Added a test
Docs
WIP
Changes
Minor adjustment to @astrojs/mdx so smartypants object options are passed to remark-smartypants, matching Astro's markdown behavior since v6.1.
Testing
Added a regression test that verifies smartypants object options (e.g. dashes: "oldschool") are applied in MDX. The test fails without this change (e.g., for dashes: "oldschool" an em-dash is expected while the default configuration produces an en-dash) and passes with it.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| pnpm/action-setup | action | patch | v6.0.4 → v6.0.5 |
Release Notes
pnpm/action-setup (pnpm/action-setup)
v6.0.5
What's Changed
Full Changelog: pnpm/action-setup@v6.0.4...v6.0.5
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
Fixes the Vite build warning reported in #15957:
[WARN] Plugin vite:reporter: Unused imports from "@astrojs/internal-helpers/remote":
– "matchHostname", "matchPathname", "matchPort", "matchProtocol"
plugin-internals.ts sets noExternal: ['astro'] for the prerender environment, which bundles astro but leaves @astrojs/internal-helpers as an external. Rollup then sees that those four helpers are imported by the bundled astro code but not consumed inside the bundle boundary, which triggers the unused-import warning.
Adding @astrojs/internal-helpers to noExternal alongside astro co-bundles it in the prerender environment, eliminating the warning. This is a first-party workspace package and the same pattern is already used elsewhere.
Testing
The warning is emitted by Vite/Rollup during the build phase and is not directly observable in the integration test suite without capturing stderr. Verified manually by running a project build with output: 'hybrid' before and after the change — the warning disappears with the fix applied.
Docs
No user-facing behavior change — no docs update needed.
Fixes #15957.
Fixes #16692
Changes
- Automatically injects
Cache-Control: public, max-age=31536000, immutablefor hashed Astro assets (/_astro/*) into Cloudflare's_headersfile at build time, so browsers cache assets across deploys without extra user configuration. - Skips injection when
build.assetsPrefixis set (assets are served from a different origin). - Skips injection when the user's existing
_headersalready has aCache-Controlrule whose URL pattern matches the assets path — Cloudflare merges duplicate matching rules' headers with a comma, which would produce contradictory cache directives. - Respects
baseconfig: the injected pattern is prefixed accordingly (e.g./blog/_astro/*). - Injection block is prepended before any existing user-defined headers so rule ordering in the file is predictable.
- Atomic
_headerswrite (write to.tmp, then rename) to avoid leaving the file truncated on a mid-write crash.
Testing
- Added unit tests for the new
headersFileHasCacheControlForPathutility inpackages/integrations/cloudflare/test/headers.test.ts, covering: empty files, exact patterns, global splat (/*),! Cache-Controldetach, non-matching rules, placeholder patterns (:name), host-prefixed patterns, comments/blank lines, base-prefixed paths, and case-insensitivity. - Extended the existing
with-baseintegration test to assert the injected block is present with the correct base prefix, that user-defined headers are preserved, and that the injected block is placed before user headers.
Docs
No documentation changes needed — this is an internal build-time improvement for Cloudflare deployments. The behavior is automatic and the skip conditions (logged via logger.info) are observable in build output. No new configuration options were added.
Other Info
Live preview of the fix: https://with-fix-astro-assets-cf-repro.ma2153.workers.dev/
Changes
- Invalidates the
astro:data-layer-contentvirtual module in the SSR module runner'sevaluatedModulescache when the data store changes, not just in the server-side module graph. The existinginvalidateModulecall only clears the server'stransformResult, but a subsequenttransformRequest(triggered during module resolution) re-populates it before the runner'sfetchModulecheck, causing a false cache hit that returns stale data.
Closes #16561
Testing
- No new tests added. Manually verified with a content collection setup: deleting entries correctly updates
getCollection()results on the next page load in dev.
Docs
- No docs update needed; this is a bug fix with no API change.
Changes
- Fixes #16564 —
data-astro-prefetch="tap"silently failing when the user clicks a nested child element (e.g.<span>,<img>,<svg>, Astro<Image />) inside an anchor e.targetontouchstart/mousedownis the deepest element under the pointer, not necessarily the<a>— useclosest('a')to walk up to the anchor before checking the strategy- Particularly impactful on slow connections / data-saver mode, where all strategies collapse to
tap— meaning zero prefetching was happening for any link with nested content for the users who would benefit most
Testing
- Added e2e fixture link
<a data-astro-prefetch="tap"><span>tap nested</span></a>and a corresponding page - Added e2e test in
prefetch.test.tsthat clicks the inner<span>and asserts the prefetch request fires
Docs
No docs change needed — this is a bug fix restoring already-documented behavior, not a new feature or API change.
Closes #16563
Summary
Calling session.delete(key) as the first mutation in a request (no prior get, set, has, keys, etc.) did not write back to session storage. The session stayed dirty in memory, but [PERSIST_SYMBOL]() skipped the save path because #data was still undefined, so the backing store kept the old value and the next request could still read the “deleted” key.
Root cause
set()initializes the in-memory map withthis.#data ??= new Map();delete()only didthis.#data?.delete(key), so#datacould remainundefined.- Persistence gated saves on
if (this.#dirty && this.#data), so delete-only flows never calledsetItemand#toDeletewas never applied to storage.
Fix
- Initialize the map in
delete()the same way as inset():this.#data ??= new Map()before removing the key, so the persist path runs and#ensureData()can merge, apply deletions, and serialize to the driver.
Testing
- Added a unit test that pre-seeds backing storage, calls only
delete()then persist, and asserts a newAstroSessionwith the same storage/session id no longer returns the deleted key. - Adjusted an existing persistence test so the follow-up session’s storage
getreturns parsed JSON (matching real unstorage behavior) aftersetItemwrites a devalue JSON string.
Docs
No documentation change required; behavior now matches what the session API already promises.
Contributor checklist (see CONTRIBUTING.md)
-
pnpm exec changesetfor theastropackage (user-facing bugfix) - Tests:
pnpm -C packages/astro exec astro-scripts test "test/units/sessions/astro-session.test.ts"
Fixes #16553
Changes
- Fixes non-prerendered routes (e.g. SSR endpoints using
cloudflare:workers) failing when a dynamic prerendered route like[page].astroexists in the same project withprerenderEnvironment: 'node'. - The dev prerender middleware was using
matchAllRoutes(), which returns every route matching a pathname. A request to/ssrwould match bothssr.astro(non-prerendered) and[page].astro(prerendered), and since one match was prerendered, the request was incorrectly routed to the Node handler. Replaced withmatchRoute(), which returns only the highest-priority match.
Testing
- Added a dynamic prerendered route (
[page].astrowithgetStaticPaths) to the existingprerender-node-envfixture. The existing "renders SSR page through workerd" test now exercises this scenario.
Docs
- No docs needed.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| withastro/automation | action | digest | 497c926 → e27ec6d |
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
- Updates
./test-utils.jsimports to./test-utils.tsin three.test.jsfiles that were missed when #16492 renamedtest-utils.js→test-utils.ts.
Testing
- No new tests. This fixes the existing
test:integration:jssuite which fails immediately withERR_MODULE_NOT_FOUND.
Docs
- No docs needed — test-only change.
Changes
Simplify db's tsconfig by removing tsconfig.virtual.json.
Its only purpose is to prevent incorrect auto-imports from editor hints and doesn’t affect the runtime output, so it should be relatively safe to remove it.
This file makes it tricky for the TypeScript project references refactor.
Testing
Green CI
Docs
N/A
Changes
- Double-encoded URL paths like
/api/%2561dmin/userscould bypass middleware auth checks because the normalization fallback leftctx.url.pathnamehalf-decoded. - Adds
MultiLevelEncodingErroras a distinct error type so callers can distinguish it from generic decode failures. #createNormalizedUrlnow re-throwsMultiLevelEncodingErrorinstead of falling back. Other decode errors (truly malformed URLs) still fall back gracefully.BaseApp.render()catchesMultiLevelEncodingErrorand returns400 Bad Request.
Testing
- Added
test/units/app/double-encoding-bypass.test.ts— 7 tests using a catch-all/api/[...path]endpoint behind auth middleware. Covers the direct path (401), single-encoded (401), double-encoded (400), multiple encoded segments (400), public routes (200), and non-protected API routes (200).
Docs
- No docs update needed — no user-facing API changes.
Changes
- Fixes a bug in
@astrojs/cloudflarewhere custom KV namespace bindings (e.g.MY_KV,CACHE) were silently removed when Astro's session functionality was enabled. - The root cause was in
packages/integrations/cloudflare/src/wrangler.ts: when injecting theSESSIONKV binding, the code returned a fresh single-item array instead of merging with the user's existingkv_namespaces. - Extracted a
withSessionKVBindinghelper that copies existing namespaces and appends the SESSION binding, preserving all user-defined bindings. - Fix applies to both the top-level config and the
previewsconfig (same code path). - Updated the existing test that was asserting the broken behavior, and added a new test covering the
previewscase.
Closes #16554
Testing
- Updated
packages/integrations/cloudflare/test/wrangler.test.ts: fixed the assertion in "adds SESSION binding when other KV bindings exist but not the session one" to verifyOTHER_KVis preserved alongsideSESSION. - Added a new test "preserves existing previews KV bindings when adding SESSION binding" covering the same scenario in the
previewsconfig. - No integration test needed: the bug and fix are fully exercised by the unit test suite for
cloudflareConfigCustomizer.
Docs
No docs change needed. This is a bug fix restoring behavior that users already rely on — their existing kv_namespaces entries in wrangler.toml now survive when sessions are enabled.
Changes
- Fixes a regex bug where
returnwas incorrectly rewritten inside string literals, template literals, and comments during esbuild's dep-scan / frontmatter error-check phase - Replaces the two-pass
\breturn\bregex with a single-pass state-machine regex that skips over all string/comment tokens before rewriting barereturnstatements - Fixes the primary report case:
gen.return(value)→ was producinggen.throw (value)(syntax error); now preserved correctly - Extracts a shared
replaceTopLevelReturns()helper intoutils.tsto deduplicate logic between the cloudflare esbuild plugin andcompile.ts
Affected files:
packages/integrations/cloudflare/src/esbuild-plugin-astro-frontmatter.tspackages/astro/src/vite-plugin-astro/utils.tspackages/astro/src/vite-plugin-astro/compile.ts
Closes #16551
Testing
The fix is a pure regex change with no new dependencies. Verified manually against the cases from the issue:
| Input | Before | After |
|---|---|---|
gen.return(val) |
gen.throw (val) ← syntax error |
unchanged ✓ |
"return value" |
"throw value" |
unchanged ✓ |
// return foo |
// throw foo |
unchanged ✓ |
`return ${x}` |
`throw ${x}` |
unchanged ✓ |
return foo |
return foo |
throw foo ✓ |
return; |
return; |
throw 0; ✓ |
No new tests added — the two affected code paths are internal-only (esbuild dep scan and compile-error enhancement), exercised by the existing integration test suite.
Docs
No user-facing behavior change — this only affects internal dep scanning and error reporting during compilation. No docs update needed.
Changes
fixes #16524.
with vite.css.transformer: 'lightningcss', scoped styles using a nested & selector inside :where(...) silently produce css where the scope attribute lands on the matched child instead of the intended parent. tailwind v4's space-x-*, space-y-*, and divide-* all expand to this shape and tailwind v4 ships with lightningcss in the loop, so any astro 6 + tailwind v4 project using these utilities in scoped <style> blocks targeting children from another component gets broken spacing in production while the css rule still looks present in the bundle.
what's happening: lightningcss runs inside vite's preprocessCSS() and lowers nesting before @astrojs/compiler injects scope attributes. by the time the compiler sees the css, .parent is buried inside :where(...) and isn't a top-level compound anymore, so the injector prepends [data-astro-cid-X] as a new leading compound — which constrains the wrong element.
fix in packages/astro/src/core/compile/style.ts: when the user has css.transformer: 'lightningcss', call preprocessCSS() with a shallow-cloned vite config whose css.lightningcss.exclude ORs in Features.Nesting, so lightningcss skips its nesting-lowering pass for this preprocess call. vite's final pipeline still lowers nesting for the bundle so output stays compatible with the user's targets. clone is non-mutating per call so it's safe under parallel .astro compiles. lightningcss is resolved from the user's project root via createRequire — same instance vite uses, no new dep added to packages/astro. falls back to the original config if lightningcss can't be resolved.
i think a better fix would live in @astrojs/compiler itself, teach the scope injector to recognize :where(<simple-compound>, ...) as a leading-compound wrapper and attach the cid to the inner compound instead of prepending a new one. similar in spirit to withastro/compiler#1153 but extended to cover the :where(...) case, and it'd cover any future css transform that produces a similarly shaped selector not just lightningcss.
Testing
added a regression fixture under packages/astro/test/:
lightningcss-scoped-nesting.test.ts— compiles the reporter's exact shape withvite.css.transformer: 'lightningcss'and asserts the scope id binds to.parent, not as a leading compound on the matched child.- fixture under
packages/astro/test/fixtures/lightningcss-scoped-nesting/— single page using the reporter's:where(& > :not(:last-child))selector inside a scoped<style>block.
reporter's reproduction repo: https://github.com/rklos/astro-css-bug-repro
Docs
no docs changes. silent regression in css output; behavior after the fix matches what the docs already describe for scoped styles + nested selectors.
Changes
upgrade/package.json has "build": "astro-scripts build \"src/index.ts\" --bundle && tsc". The output dist/index.js should be emitted by astro-scripts, while tsc should only emit .d.ts files.
In #16493, tsc emits both .js and .d.ts files, which overrides the bundled dist/index.js file that was just emitted by astro-scripts.
This PR updates upgrade/tsconfig.json to ensure that tsc won't emit any .js files.
Testing
Before this PR:
$ pnpm -w build
$ tree ./packages/upgrade/dist/
./packages/upgrade/dist/
├── actions
│ ├── context.d.ts
│ ├── context.js
│ ├── help.d.ts
│ ├── help.js
│ ├── install.d.ts
│ ├── install.js
│ ├── verify.d.ts
│ └── verify.js
├── index.d.ts
├── index.js
├── messages.d.ts
├── messages.js
├── shell.d.ts
└── shell.js
2 directories, 14 filesAfter this PR:
$ pnpm -w build
$ tree ./packages/upgrade/dist/
./packages/upgrade/dist/
├── actions
│ ├── context.d.ts
│ ├── help.d.ts
│ ├── install.d.ts
│ └── verify.d.ts
├── index.d.ts
├── index.js
├── messages.d.ts
└── shell.d.ts
2 directories, 8 filesAfter this PR, only one bundled dist/index.js file is emitted in dist/.
Docs
The previous and afterward outputs can both work, so there is no behavior change. No docs are needed.
Last fetched: | Scheduled refresh: Every Saturday
See astro-loaders documentation to configure your own
Inspired by prs.atinux.com