AstroEco is Contributing…
Display your GitHub pull requests using astro-loader-github-prs

Changes
Deno's latest patch dropped a flag on deno eval, seemingly by accident, that Netlify uses. Deno's CLI throws when it sees an unexpected flag (ugh), so this was breaking everything. This PR pins to the last working version.
Testing
Tests should pass!
Docs
N/A

Changes
Found this bug on a site that uses prims, and the adapter would attempt to optimise only when the page was navigated.
Testing
N/A
Docs
N/A

Changes
Fixes #17823.
createConsoleLogger takes an options object ({ level }), but the non-runnable dev entrypoint was calling it with the bare level string:
setLogger(manifest, createConsoleLogger(manifest.logLevel));Destructuring { level } out of a string yields undefined, so the logger was constructed with no level. Every call then goes through isLogLevelEnabled(undefined, level), which evaluates levels[undefined] <= levels[level] — undefined <= 30 is false — so every message is dropped before it ever reaches the destination. That is why Astro.logger / context.logger are completely silent while console.log still shows up.
This only affects adapters whose dev server runs in a non-runnable environment (workerd, i.e. @astrojs/cloudflare), because that path loads astro/app/entrypoint/dev (core/app/entrypoints/virtual/dev.ts). The Node adapter's dev server goes through vite-plugin-app/createAstroServerApp.ts, which injects an already-constructed logger, so it was never affected — matching the report.
The fix is to pass the options object the function actually expects.
Testing
Added packages/astro/test/units/logger/dev-entrypoint.test.ts, which stubs the virtual:astro:manifest / virtual:astro:fetchable modules via registerHooks, calls createApp() from the dev entrypoint, and asserts the manifest's logger reports the configured level and actually emits both error and info records.
- The new test fails against the old code (
logger.level()isundefinedand nothing is written) and passes with the fix. packages/astrologger unit tests: 88 passing, 0 failing.- Full
pnpm --filter astro test:unit: 3360 tests, 3359 passing, 0 failing. tsc -bonpackages/astrois clean.
Docs
No docs change needed — this restores documented Astro.logger behaviour rather than changing it.

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.10
Patch Changes
-
#4160
88442c3Thanks @ArmandPhilippot! - Adds the missing translations for Expressive Code in the following locales:ar,hi,it,ja,ko,pt,zh-CN, andzh-TW. -
#4159
a0e0ba2Thanks @trueberryless! - Adds 1 new icon:npmx

Description
Follow-up on withastro/docs#14460 (comment)
Expressive Code only has English and German translations built-in. Astro Docs has more translations: we should take advantage of this to upstream the missing translations to Starlight.
I only included the missing ones: ar, de, hi, it, ja, ko, pt, zh-CN, and zh-TW.
The following are not included:
es: Astro Docs / Starlight (seems identical)pl: Astro Docs / Starlight (seems identical)ru: Astro Docs / Starlight (expressiveCode.copyButtonTooltipseems different)- edit: I forgot about French... well, the Starlight translation looks better to me so... withastro/docs#14477
And, I haven't checked if the translation are correct... I trust Astro Docs translators. 😅

Description
Although I can already feel custom icons tingling in my fingertips, I would like to add one of my favorite icons of 2026: npmx.

Changes
- Fixes the sitemap emitting the homepage as
<loc>https://example.com</loc>instead of<loc>https://example.com/</loc>whentrailingSlash: 'never'orbuild.format: 'file'is set. - Removes the stream-replacement workaround in
write-sitemap.tsandwrite-sitemap-chunk.ts(added in #10772 for an oldsitemap.jsbug).SitemapStreamv9 no longer appends trailing slashes, so the workaround now only ever stripped the root URL's slash. Also drops the now-unusedstream-replace-stringdependency and theAstroConfigargument those functions no longer need.
Testing
- Updated
trailing-slash.test.tsto assert the root URL keeps its/across thetrailingSlash: 'never',build.format: 'file', andbasecases, locking in the fix as a regression test.
Docs
- No docs update needed; this is a bug fix with no user-facing API or config change.
Closes #17848

Changes
- Prebundles
astro/logger/jsonin Cloudflare server environments only when JSON logging is active, avoiding a mid-request optimizer reload that can break first-request React SSR. Fixes #17834.
Testing
- Extends Cloudflare config tests to cover default, console, and JSON logger optimization.
Docs
- No docs update needed because this corrects internal dev-server dependency optimization.

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.2.10
Patch Changes
-
#17833
413a6e7Thanks @astro-factory! - Fixes prerender conflict warnings to correctly identify the route that first rendered a duplicate pathname, instead of misattributing the conflict to an unrelated route that merely matches the URL pattern -
#17755
157c500Thanks @matthewp! - Fixes a bug where editing a content collection entry duringastro devon Windows kept serving stale content until the dev server was restarted. The data store now notifies the dev server directly after each write instead of relying only on the file watcher, which can miss the atomic rename that commits the write on some platforms.
@astrojs/cloudflare@14.2.6
Patch Changes
-
#17854
07b919fThanks @ematipico! - Added@astrojs/prismto the list of dependencies to optimise. The dev server is now faster for sites that use Prism as code highlighter. -
#17850
1301c37Thanks @matthewp! - Fixes React SSR failures on the first Cloudflare dev request when JSON logging is enabled -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.4
@astrojs/sitemap@3.7.4
Patch Changes
- #17851
52d3f56Thanks @astro-factory! - Fixes the sitemap outputting a URL with an empty path for the homepage (e.g.https://example.cominstead ofhttps://example.com/) whentrailingSlashis set to"never"orbuild.formatis set to"file"
@astrojs/language-server@2.16.16
Patch Changes
- #17715
a51c533Thanks @wakqasahmed! - Fixesastro checksilently skipping.astrofiles that are only reachable through a TypeScript project reference (a tsconfig referenced viareferencesin another tsconfig). These files are now checked and reported like any other.astrofile.

Changes
- Escapes string values passed to
set:textbefore Astro JSX rendering - Preserves intentional raw script and style children
Testing
- Adds MDX build and dev coverage for
set:textin script and style elements
Docs
- No docs update needed; this aligns with the documented
set:textbehavior

Changes
You can in theory import HTML files inside .ts files, re-export them, use them inside Astro files etc. Found while messing around with content mappers.
Testing
Tested manually
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.2.9
Patch Changes
-
#17846
b441180Thanks @Princesseuh! - Fixes importing.htmlfiles not being typed outside of.astrofiles -
#17831
32e8b44Thanks @astro-factory! - Fixes a crash when requesting/index.htmlagainst a dynamic page route like[slug].astro -
#17841
c35448eThanks @astro-factory! - Fixes SSR manifest containing staleentryModulesreferences to prerender-only chunks that no longer exist in the final build output -
#17847
eb87a23Thanks @matthewp! - Fixesset:textescaping in MDX script and style elements

Fixes #17843
What's wrong
plugin-manifest.ts substitutes @@ASTRO_MANIFEST_REPLACE@@ with a regex that runs in a build post hook — i.e. on already-generated chunk code, after the minifier has run. The character class was ['"], quotes only.
Rolldown/oxc's minifier normalises string literals to template literals, so a minified server build emits the placeholder as `@@ASTRO_MANIFEST_REPLACE@@` and the regex does not match. The placeholder ships verbatim, deserializeManifest reads rootDir/srcDir/outDir/… off a plain string, and the first new URL(undefined) throws:
service core:user:<worker>: Uncaught TypeError: Invalid URL string.
at entry.mjs:13:29846 in wiThe build exits 0, so nothing surfaces until runtime. On @astrojs/cloudflare the Worker cannot boot at all.
Setting build.minify at the top level rather than on the ssr environment produces the same error at build time instead, because the prerender environment inherits it and its renderer hits the same unsubstituted manifest.
The fix
Accept a backtick in the character class, in both places the pattern appears:
plugin-manifest.ts— the manifest placeholder (the one I reproduced).vite-plugin-server-islands.ts— the two server-islands placeholders. I did not reproduce a failure there (that code is tree-shaken out of my app), but the mechanism is identical, so they are fixed alongside rather than left as a latent copy of the same bug.
Verification
Astro 7.1.2, Vite 8.1.5 (rolldown-native), @astrojs/cloudflare 14.1.3, minification enabled on the ssr environment via astro:build:setup:
| before | after | |
|---|---|---|
grep -c '@@ASTRO_MANIFEST_REPLACE@@' dist/server/entry.mjs |
1 | 0 |
grep -c 'file:///' dist/server/entry.mjs (manifest injected) |
0 | 1 |
Worker boots under wrangler dev |
✗ Invalid URL string |
✓ |
prerendered route / |
— | 200 |
server-rendered route /status |
— | 200 |
With the fix the minified bundle also came out 27% smaller gzipped than the unminified one (874.5 kB → 633.8 kB), which is what prompted the investigation.
Possible follow-up
The failure mode here is silent — nothing warned that an expected substitution had not happened, and the broken artifact passed every build check. Asserting that the substitution occurred (and failing the build otherwise) would stop a future quoting change from reintroducing a server bundle that cannot boot. Happy to add that here or separately if you'd like it.

Summary
When a hybrid build includes lazily imported modules used exclusively by prerendered pages, the shared BuildInternals.entrySpecifierToBundleMap retains those specifiers after the prerender output is deleted. This causes the SSR manifest to reference chunk files that don't exist in the final dist/server/ or dist/client/ output, resulting in missing resolver URLs at runtime.
Fix
Following the established pattern from stripPrerenderedRouteStyles() (PR #16517), this fix:
- Adds a
prerenderOnlyEntrySpecifiersset toBuildInternalsto track specifiers written exclusively by the prerender build environment. - Updates
plugin-internals.tsto populate that set during the prerendergenerateBundlephase. - Strips those prerender-only entries from
entryModulesduring SSR manifest injection inplugin-manifest.ts.
This ensures every entry specifier in the SSR manifest resolves to a file that actually exists in the final server output.
Validation
Reporter @yumam0815 confirmed the fix eliminates all stale manifest references in their downstream validation:
- Stale/missing manifest references: 12 → 0
- Missing resolver URLs: 12 → 0
- Tested on macOS arm64 and Ubuntu 24.04 with Node 22 and Node 24
- workerd HTTP suite: 54/54 passing
Closes #17838

Changes
- Updates Astro’s optional Sharp dependency to 0.35.4.
- Refreshes the lockfile and package-age exceptions.
Testing
- No test changes; this is a dependency-only update.
Docs
- No docs update needed because there are no API changes.

Changes
- Prerender conflict warnings now name the route that actually first rendered a duplicate pathname. Previously the warning called
matchRoute(), which returns the first route whose URL pattern matches the path rather than the route that emitted it, so the "winning" route in the message could be unrelated. - Changes
builtPathsfromSet<string>toMap<string, RouteData>inpackages/astro/src/core/build/generate.ts. The map records which route first claimed each normalized pathname, and duplicate detection reads the winning route from the map instead of guessing via pattern matching. - Also handles same-route duplicates: a single route emitting the same pathname more than once now produces a correct conflict warning (or error, under
prerenderConflictBehavior: 'error') instead of passing through silently.
Testing
- Adds
prerender-conflict-same-routefixture and two cases inpackages/astro/test/prerender-conflict.test.tscovering the same-route duplicate scenario: one asserts the warning names the correct winning route, one asserts the build throws with the correct message underprerenderConflictBehavior: 'error'.
Docs
- No docs update needed; this corrects the accuracy of an existing build warning with no user-facing API or behavior change.
Closes #17832

Changes
- Prevents
/index.htmlrequests from crashing when an on-demand dynamic page such as[slug].astromatches the raw pathname. This affects the defaultbuild.format: 'directory', where HTML-suffixed paths are not normalized before route matching. - Restores the original pathname when stripping
.htmlwould invalidate the route Astro already selected, keeping parameter extraction consistent with that match.
Testing
- Adds unit coverage for root and nested dynamic page routes where stripping
/index.htmlbreaks the selected route pattern. - Covers the existing
/foo.htmlbehavior, where stripping remains valid because the normalized pathname still matches the dynamic route.
Docs
No docs update needed because this prevents an internal routing crash without changing a public API.
Closes #17827

Changes
We have disabled duplicate-threshold in e18e/action-dependency-diff (by setting it to a large number 100, instead of the default value 1). This PR re-enabled it.
Why we disabled it?
If we enable duplicate-threshold, the previous version of e18e/action-dependency-diff will emit all duplicated packages in the pull request comments. In the astro repo, that’s 200+ packages in total. An example bot comment is shown below:
This information in the comment is too verbose and not useful at all. I guess that's why we disabled it.
Why enable it now?
In the latest e18e/action-dependency-diff v1.7.x, I've contributed a feature that changes duplicate-threshold to only scan the changed dependency in a pull request.
For example, PR #17450 introduced some duplicated package. Now the CI would comment the following content:
The bot comment is much more useful now.
Testing
Green CI
Docs
N/A

What?
Adds a new with-aws-fargate example demonstrating how to deploy an Astro static site to AWS ECS Fargate via an unprivileged Nginx container using Terraform and GitHub Actions.
Why?
Deploying containerized Astro builds directly to AWS ECS/Fargate behind an Application Load Balancer and CloudFront is a common cloud hosting architecture. Providing a standardized IaC baseline simplifies infrastructure setup for teams managing their own AWS environments.
How?
The infrastructure configuration and deployment workflow were generated using deploy-stack to establish standard Terraform and keyless OIDC GitHub Actions patterns.

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.2.8
Patch Changes

Description
Adds 1 new icon: hypothesis, for Hypothesis (hypothes.is), the open-source web annotation service.
This follows on from the Goodreads icon in #4145. I raised it on Discord first:
By the way, as I look at my site now, I am missing one more social icon for hypothes.is. Maybe I will also open a PR if you are fine. What do you think, since it is a more niche website for annotating text, and so far, I conquered it with the following (as on the screenshot below) using
icon: 'document'
I'm opening this so the mark is concrete to look at rather than hypothetical - very happy to close it if you'd rather not take this one. It's a more niche service than Goodreads or WhatsApp, and I don't want to spam the icon set on account of my own site.
The case for it, for what it's worth: Hypothesis is an annotation layer for documentation and long-form web reading, so it's arguably closer to Starlight's audience than some social platforms. And the icon set already includes comparably specialised entries like forgejo, sourcehut, nostr, jsr and pkl.
About the icon: monochrome 24x24 mark from the official brand page, via Simple Icons (CC0-1.0), optimised with SVGOMG at precision 2 per your note on #4145 - it comes out at 390 bytes. The h. and the dot are knockouts, so it inverts correctly in dark mode like the existing facebook and linkedin marks. I checked it renders cleanly from 16px up.
Placed after goodreads, following where recent social icons have been added.

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.9
Patch Changes

Description
The sidebar now finds all groups containing the current page in one traversal. Previously, each group repeatedly flattened its subtree, causing unnecessary work for deeply nested sidebars.
Should save some memory and a bit of milliseconds.

Description
Replaced repeated route and sidebar scans with maps, sets, and small caches. This reduces build work on documentation sites with many pages and avoids attaching unused route data to static paths.
This PR doesn't affect anything visual

Changes
- Prevents TypeScript code actions from inserting generated component exports into
.astrosource. - Tracks the generated export range so user-authored exports and script edits remain unchanged. Fixes #17811.
Testing
- Adds organize-import coverage for generated exports, matching user exports, and script tags.
Before
After
Docs
- No docs update needed; this restores expected language-server behavior.

Changes
- Derive generated and default Cloudflare compatibility dates from the installed workerd version. Fixes #17796.
- Expose the date through
@astrojs/cloudflare/infoand align Cloudflare dependencies.
Testing
- Cover project-local adapter resolution, derived defaults, and user-provided dates.
Docs
- No docs update needed; this corrects internal default behavior.

Changes
- Where possible, update
consolecalls with the logger instead - In some cases, this affects public APIs in non breaking ways so it needs to be a minor
- I'd like to know if what I've done looks okay, or if some changes need to be reverted in some places
- Made partially with Claude
Testing
Added
Docs
- Changesets
- withastro/docs#14470

Description
- This PR adds a new feature which has been discussed and approved here.
Adds 1 new icon: goodreads.
The path is the monochrome Goodreads g mark on a 24x24 viewBox, taken from Simple Icons (CC0-1.0) and optimised with SVGOMG, per the guidance in #4135. It's the same path that was reviewed in the discussion.
Placed after tiktok, following where the recently added social icons have been landing.

Changes
- The experimental
svgOptimizerdid not forward the SVG's file path to SVGO'soptimize(). Plugins likeprefixIdsthat derive per-file values from the path fell back to the same generic prefix for every file, so inlining multiple SVGs on one page produced colliding IDs. SvgOptimizer.optimizenow takes the file path as a second parameter, andsvgoOptimizer()forwards it into SVGO's config, soprefixIds(and similar plugins) generate distinct prefixes per file.- This changes the
SvgOptimizerinterface by adding a requiredpathparameter. SincesvgOptimizeris experimental, this is acceptable, but customSvgOptimizerimplementations will need to pass through the new parameter.
Testing
- Added a fixture with two SVGs sharing an element ID and a page inlining both, then asserted via
prefixIdsthat the resulting IDs differ across files.
Docs
- No docs update needed;
SvgOptimizeris an experimental interface and its usage docs don't specify theoptimizesignature.
Fixes #17728

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.2.7
Patch Changes
-
#17415
55d38c8Thanks @iseraph-dev! - Deserializes each route once when loading the SSR manifest -
#17772
023b48bThanks @matthewp! - Fixes route selection for normalized request paths in adapter and development request handling -
#17819
633855bThanks @matthewp! - Updates generated and default Cloudflarecompatibility_datevalues to match the installed runtime and requires Wrangler^4.125.0 -
#17813
ae26d18Thanks @matthewp! - Fixesrewrite()andnext(payload)for GET and HEAD requests with host-provided bodies -
#17816
a0d2fe3Thanks @astro-factory! - Fixes the experimentalsvgOptimizernot generating unique per-file ID prefixes when using SVGO'sprefixIdsplugin
@astrojs/cloudflare@14.2.5
Patch Changes
-
#17819
633855bThanks @matthewp! - Updates generated and default Cloudflarecompatibility_datevalues to match the installed runtime and requires Wrangler^4.125.0 -
#17675
44d384cThanks @danielmlr! - Adds the Worker version to the cache metadata of cached responses when theCF_VERSION_METADATAbinding is configured. Responses carry anastro-version:<id>cache tag for version-specific purging, and responses that already sendLast-Modifiedget a weakETagthat folds the version in. Conditional revalidation then returns fresh content after a deploy that changes rendered output but not content — most commonly the hashed asset URLs in server-rendered HTML. Without the binding, nothing changes. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.4
@astrojs/language-server@2.16.15
Patch Changes
- #17820
89e5349Thanks @matthewp! - Fixessource.organizeImportsleaking generated Astro component exports into.astrofiles
astro-vscode@2.16.20
Patch 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.
Releases
astro@7.2.6
Patch Changes

Changes
- Prevents
rewrite()and sequencednext(payload)calls from returning 500 when an incoming GET or HEAD request contains a body. - Omits bodies when constructing rewritten GET and HEAD requests, while preserving body forwarding for methods such as POST.
Testing
- Adds rewrite and middleware sequence coverage for incoming GET and HEAD requests with bodies and POST body preservation.
Docs
- No docs update needed because this corrects an internal request-handling edge case.
Fixes #17801

Changes
- The ambient manifest was getting resolved to the fallback. This fixes it by externalizing that import so that Vite resolves it like it normally does, not during the optimization phase.
Testing
- I added Cloudflare dev coverage for a custom worker using
FetchState. Was missing a dev test here.
Fixes astro.build dev
Docs
- No docs update needed because this restores expected behavior.

Changes
- Fixes a regression from 7.2.3, introduced in cf29bec
- It was happening in a complex private project so I got Claude to reproduce and fix it
Testing
New tests
Docs
Changeset

Changes
This PR getSafeErrorMessage by removing the redundant String.raw wrapper around the return value of extractStringFromFunction.

Description
- This PR adds a new feature which has been discussed and approved [here](link to GitHub or Discord discussion).
Addresses #4135
Adds a WhatsApp icon to the set.
Before (no WhatsApp icon):
After (WhatsApp icon next to Telegram's):


This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| devalue | ^5.9.0 → ^5.9.1 |
||
| fastify (source) | ^5.12.0 → ^5.12.1 |
Release Notes
fastify/fastify (fastify)
v5.12.1
⚠️ Security release
- Fix for GHSA-w2qp-rph6-63g4
- Fix for GHSA-3m5p-2c4r-xxw2
What's Changed
- [Backport 5.x] test: fix reply.mediaType by @github-actions[bot] in #6950
- [Backport 5.x] ci: allow backport in pr title by @github-actions[bot] in #6952
- [Backport 5.x] fix: run preClose hook exactly once per declaring instance by @github-actions[bot] in #6954
- [Backport 5.x] fix: report canonical route url for hidden prefix slash route by @github-actions[bot] in #6961
Full Changelog: fastify/fastify@v5.12.0...v5.12.1
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- 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.

This PR contains the following updates:
Release Notes
netlify/primitives (@netlify/blobs)
v10.7.13
Bug Fixes
- blobs: better error message when using site stores during prebuild (#737) (80fe1f0)
- deps: use extracted
@netlify/test-utils(5f9c2f9)
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev-utils bumped from 4.4.7 to 5.0.0
- @netlify/otel bumped from ^6.0.5 to ^6.0.6
- dependencies
v10.7.12
Bug Fixes
v10.7.11
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/otel bumped from ^6.0.4 to ^6.0.5
- dependencies
v10.7.10
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev-utils bumped from 4.4.6 to 4.4.7
- @netlify/otel bumped from ^6.0.3 to ^6.0.4
- dependencies
v10.7.9
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev-utils bumped from 4.4.5 to 4.4.6
- @netlify/otel bumped from ^6.0.2 to ^6.0.3
- dependencies
v10.7.8
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev-utils bumped from 4.4.4 to 4.4.5
- @netlify/otel bumped from ^6.0.1 to ^6.0.2
- dependencies
v10.7.7
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev-utils bumped from 4.4.3 to 4.4.4
- @netlify/otel bumped from ^6.0.0 to ^6.0.1
- dependencies
v10.7.6
Bug Fixes
netlify/primitives (@netlify/functions)
v5.3.0
Features
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/types bumped from 2.7.0 to 2.8.0
- dependencies
v5.2.2
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/types bumped from 2.6.0 to 2.7.0
- dependencies
v5.2.1
Bug Fixes
netlify/framework-adapters (@netlify/vite-plugin)
v2.12.9
Bug Fixes
v2.12.8
Bug Fixes
v2.12.7
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev bumped from 4.18.6 to 4.18.7
- @netlify/dev-utils bumped from ^4.4.5 to ^4.4.6
- dependencies
v2.12.6
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev bumped from 4.18.5 to 4.18.6
- @netlify/dev-utils bumped from ^4.4.4 to ^4.4.5
- dependencies
v2.12.5
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev bumped from 4.18.4 to 4.18.5
- @netlify/dev-utils bumped from ^4.4.3 to ^4.4.4
- dependencies
v2.12.4
Dependencies
- The following workspace dependencies were updated
- dependencies
- @netlify/dev bumped from 4.18.3 to 4.18.4
- dependencies
vercel/nft (@vercel/nft)
v1.11.0
Features
v1.10.2
Bug Fixes
v1.10.0
Features
v1.9.0
Features
v1.5.0
Features
v1.4.0
Features
vitejs/vite (vite)
v8.2.2
Features
Bug Fixes
- bundled-dev: handle lazy request error (#23291) (3ba026d)
- bundled-dev: hot update through circular imports instead of reloading (#23259) (3dbddef)
- config: resolve sourcemap paths against sourcemap location (#23239) (05a003e)
- css: don't pass empty targets to lightningcss (#23295) (2804636)
- define: fix match escaped dots to support $-prefixed define keys (#23249) (dcf88bd)
- deps: update all non-major dependencies (#23217) (ba958bd)
- deps: update rolldown-related dependencies (#23218) (83ecb2c)
- module-runner: exclude completed modules from in-flight cycle detection (fix #22999) (#23009) (d9b10a9)
- optimizer: close custom extension analysis bundles (#23207) (8fb7675)
- reduce Windows 8.3-short-name detection false-positives (#23066) (02cffa9)
- respect
resolve.preserveSymlinkswhen resolving root (fix #23197) (#23198) (8413052) - ssr: rewrite computed key of destructing parameter (#23307) (9db0b61)
- vite: update outdated upstream file links in license comments (#23285) (c0f2fc6)
Documentation
Miscellaneous Chores
Code Refactoring
- use JSON import attributes instead of readFileSync in constants (#23258) (1d9fa39)
- use named regex constants over inline literals (#22964) (5c1c6c6)
Tests
- define: close rolldown bundler after generate (#23231) (b4d66fe)
- module-runner: add TLA circular import case (#23299) (4a261f2)
- module-runner: simplify server-hmr tests (#23300) (599b44b)
- ssr: add destructing assignment case for moduleRunnerTransform (#23308) (cb77e2a)
Build System
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- 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
- Fixes an issue where
@astrojs/cloudflarewithimageService: "compile"assigned@astrojs/cloudflare/image-transform-endpointin development mode (astro dev). - When paired with
passthroughImageService(), format conversion is disabled (delete options.format), omittingf=from the/_imagequery string. This caused the Cloudflare transform endpoint to crash with400 Bad Request (Unsupported format: null). - Updates
case "compile":inpackages/integrations/cloudflare/src/utils/image-config.tsto useGENERIC_ENDPOINT(astro/assets/endpoint/generic) whencommand === "dev", matching the behavior ofcustom. - Added changeset for
@astrojs/cloudflare.
Testing
- Added unit tests in
packages/integrations/cloudflare/test/image-config.test.tsverifying thatimageService: "compile"resolves toastro/assets/endpoint/genericindevmode andCLOUDFLARE_PASSTHROUGH_ENDPOINTinbuildmode. - Verified that tests fail on the unpatched code (
AssertionError: Expected 'astro/assets/endpoint/generic', got '@astrojs/cloudflare/image-transform-endpoint') and pass with 0 errors when the fix is applied. - Tested locally in an Astro project with
imageService: "compile"andpassthroughImageService()to verify<Image />components load withHTTP 200in dev.
Docs
No documentation changes needed.

Changes
- Fixes a
TypeError: Cannot assign to read only propertycrash in the Content Layer when a content collection schema applies Zod.readonly()(which freezes parsed data at runtime) to objects containingimage()fields. - Root cause:
MutableDataStore.scopedStore().set()stripped theIMAGE_IMPORT_PREFIXfrom image values in place using neotraverse'sforEach+ctx.update(), which performs direct property assignment (safe_set). On runtime-frozen objects this throws. - This is a regression from #17631, which moved prefix-stripping from read-time (
structuredClone) to store-time (in-place mutation). - Fix: traverse immutably with neotraverse's
mapinstead of mutating viaforEach. Replacements are written into copies, so frozen data is handled without reverting tostructuredClone(avoiding the class-instance serialization problems #17631 fixed). When no image references are found, the original object is stored unchanged, preserving the previous zero-copy behavior for entries without images.
Closes #17793
Testing
- Added a regression test in
packages/astro/test/units/content-collections/mutable-data-store.test.ts:strips image prefixes from frozen data without mutating it (Zod .readonly() schemas, issue #17793). It asserts that prefixed image values are stripped in the stored entry, that the caller's frozen object is left untouched, and thatimageImports/assetImportsare recorded correctly. - Verified red/green locally:
- Without the fix, the new test fails with exactly the reported error:
TypeError: Cannot assign to read only property 'hero' of object '#<Object>'atsafe_set→ctx.update. - With the fix, all 42 tests in
test/units/content-collections/pass (pnpm exec astro-scripts test "test/units/content-collections/**/*.test.ts" --strip-types --teardown ./test/units/teardown.ts→tests 42, pass 42, fail 0).
- Without the fix, the new test fails with exactly the reported error:
- Changeset added (
.changeset/frozen-readonly-content-schemas.md,astro: patch).
Docs
No docs changes needed — this makes an already-valid Zod schema pattern (.readonly() + image()) work as users would expect; no behavior or API surface changed otherwise.

Changes
- The incremental build cache now works with
build.concurrency > 1; previously Astro disabled the cache with a warning. - When collecting incremental metadata, each prerendered path runs with an isolated store. Content entries and image transforms used by that render are recorded in its store, allowing concurrent pages to be cached independently.
- Custom prerenderers can return this metadata alongside their response. The Cloudflare adapter implements this for workerd prerendering.
Testing
- Added unit coverage for metadata isolation across concurrent renders and separately bundled Astro modules.
- Added end-to-end coverage for concurrent incremental builds in Astro and Cloudflare, including content dependencies, optimized images, and cache restoration.
Docs
- No docs update needed because the concurrency limitation was only exposed through a build-time warning, and the changeset documents the experimental adapter API change.

Changes
- Sets the Vercel NFT trace base to the common ancestor of the project root and server entry so builds support
outDiroutsideroot. - Uses platform-aware ancestry checks with filesystem-root termination, preventing build hangs on Windows and root-boundary layouts.
Testing
- Adds an integration fixture that verifies an external
outDirproduces a function containing all traced chunks. - Adds unit coverage for entries inside the project root, sibling output directories, and filesystem-root boundaries.
Docs
- No docs update needed because this fixes internal dependency tracing for existing
rootandoutDiroptions.
Closes #17761

Changes
- When
langortypeappeared on a later line of a<style>or<script>opening tag, VS Code ignored it and highlighted the embedded content as default CSS or JavaScript. Multiline tags now use the requested Sass, TypeScript, JSON-LD, or other language grammar just like single-line tags. Fixes #14657. - The TextMate grammar now prescans ordinary opening-tag attributes and applies the language scope when it reaches a complete literal
langortypevalue. This preserves attribute highlighting without treating dynamic values, unrelated attribute names, or prefixes such asmodulexas language selectors.
Testing
- Added multiline Sass and TypeScript grammar fixtures and snapshots covering preceding attributes, default-language fallbacks, JSON-LD and module scripts, quoted and unquoted values, dynamic values, and lookalike attribute names and values.
Docs
- No docs update needed because this corrects syntax highlighting for already-valid
<style>and<script>formatting.

Changes
- Astro's Volar proxy caches
getCompletionsAtPositionthe first time it is accessed. Vue later replaces that method, but the stale wrapper prevents Vue's auto-import logic from running. - Invalidates the cached method on assignment so the next request rebuilds the wrapper around Vue's decorated method, restoring auto-imports when Astro loads first. Fixes #15962.
Testing
- Adds a regression test proving methods assigned by a later TypeScript plugin replace cached wrappers.
Docs
- No docs update needed; this restores existing editor behavior.

Changes
- Fixes #17697 by wrapping Astro frontmatter in an async function during Cloudflare dependency scans, so top-level
returnstatements are valid without fragile token rewriting. - Hoists static imports outside the wrapper so Rolldown still discovers dependencies, and removes the unused esbuild scanner left behind by the Vite 8 migration.
Testing
- Adds unit coverage for import forms, top-level and bare returns,
import.meta, frontmatter without imports, and regex literals containing quote characters. - Updates the Cloudflare top-level-return fixture to reproduce the regex-literal dependency scan failure.
Docs
- No docs update needed because this fixes internal dependency-scanning behavior without changing user-facing APIs or configuration.

Description
- When adding support for the Sätteri Markdown processor, we missed that because Sätteri provides a URL in its file object, we were treating it with the same logic we used for the
<Code>component when trying to detect the locale (which passesAstro.url). Because the assumptions are quite different this broke localisation of Expressive Code code blocks when using with the Sätteri processor. - This PR fixes that by detecting Sätteri’s
file://URLs and ignoring them. - I’ve added some new unit tests for
getBlockLocale()although I’m a bit conflicted because these test don’t actually check the interface between real world inputs and thegetBlockLocale()method, so I suppose a future change to thefileobject passed by a Markdown processor could still cause a similar regression, but at least this is better than nothing. - Doing this also brought these files newly into view for Vitest, so dropped our coverage down. I’ve added some additional tests for other parts of the Expressive Code config processing logic to keep coverage high.
(It might be best to hide whitespace changes while reviewing the tests because I wrapped some existing tests in this file in a describe() block but otherwise they are unchanged.)

Changes
- Adds
apngto the format union returned byimage(). #17774 addedapngtoVALID_INPUT_FORMATSbut not to the matchingImageFunctiontype, so images from a collection schema no longer type-check againstImageMetadata— this breaksastro checkonexamples/blog, andmainhas been red since it merged.
Testing
- Adds a type test asserting the
image()schema'sformatmatchesImageInputFormat; it fails without the fix. This runs intest:types, which is why the drift is caught here rather than by widening theastro checkpath filter.
Docs
- Not needed, this restores the intended type for an existing API.

Changes
Enables the Node module compile cache. Vite and some other projects started doing this a few years back to great result. I tried it in local and it's a pretty good boost in local for little cost (a few mb, at worse).
I got gains ranging from 50 to 200ms on the various commands of the CLI trying it in local, with most notably some of our examples astro build being 100-200ms faster, pretty neat.
Testing
Tested manually
Docs
N/A

Changes
- Prevents
memoryCache()from storing responses with cookies queued byAstro.cookiesorAstro.session. Fixes #17775. - Detects attached outgoing cookies without consuming them, preserving final
Set-Cookiehandling.
Testing
- Adds App pipeline coverage for uncached Astro cookie and session responses, including subsequent session updates.
Docs
- No docs update needed because this restores the existing
Set-Cookiecache exclusion 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
@astrojs/starlight@0.41.8
Patch Changes
-
#4142
cacbc9fThanks @alebelcor! - Adds 1 new icon:whatsApp -
#4133
3944311Thanks @delucis! - Internal refactor: numberedidattributes in the<Tabs>component are now page-specific instead of using a global counter -
#4138
cd4b665Thanks @delucis! - Fixes localisation of code block UI elements when using the Sätteri Markdown processor

Changes
glob()now loads content files whose names contain a colon (e.g.Guide: Architecture.md). Previously these failed withThe URL must be of scheme fileand the entry was dropped from the collection.encodeURI()preserves colons, sonew URL('Guide:%20Architecture.md', base)parsedGuide:as a URL scheme rather than a path segment. Prefixing./forces the constructor to resolve it as a relative-path reference. Applied at the three call sites that build a URL from a glob result;isConfigFile()also now encodes the path so config-file matching works for these names.
Testing
- Adds
loads files whose names contain a colontoglob-loader.test.ts, covering that the entry is loaded and its body is readable. - The test writes its fixture to a temp directory at runtime instead of committing one, because colons are reserved in Windows filenames and a committed fixture makes
git checkoutfail on Windows CI. The case is skipped on Windows.
Docs
- No docs update needed; this restores the documented behavior of
glob()for filenames it was already expected to handle.
Closes #17762

Changes
- Fixes #17709 by allowing
.apngimports to resolve asImageMetadatawith their source, dimensions, and APNG format for use with standard<img>elements. - Keeps APNG out of the supported optimization formats so
getImage(),<Image>, and<Picture>report an unsupported format instead of stripping animation.
Testing
- Adds a real two-frame APNG fixture and verifies its
2x3dimensions andapngmetadata format. - Covers plain
<img>rendering and rejection throughgetImage(),<Image>, and<Picture>.
Docs
- No docs update needed because APNG uses the existing image metadata API and the existing unsupported-format error directs users to a standard
<img>element.

Description
This PR refactors some of the code to use the new Sätteri 0.10 APIs now available in Astro 7.2.4 (but technically 7.2.5 for a fix).
One of the most meaningful change is our components that were previously using rehype and creating processors to parse the default slot content are now using the new Sätteri htmlToHast() API.
Regarding the changeset, not quite sure yet the level of details we should use, e.g. should we just say we updated some deps or go as far as mentioning that we can now only check paths to transform once per plugin type thanks to Sätteri conditional plugin bundles.
Note that GitHub displays some renamed files as deletions + additions because they changed significantly (mostly indentations tho). The rename-only commit may be easier to review separately.
Remaining tasks
- Wait for withastro/astro#17766 to be released.
- Will need some updates once #4133 is merged.
- Adress all
TODO(HiDeoo)comments.

Changes
- Repeatedly encoded characters can make a request select a catch-all route instead of the exact page users expect. For example,
/docs/%2567uidecan be matched differently from its normalized/docs/guidepath, including between development and deployed apps. - Fully normalizes pathnames before adapter and development route matching, so route selection and rendering use the same pathname while preserving adapter-provided route data.
Testing
- Adds app-level coverage confirming a multi-encoded request selects the exact route before adapter rendering.
Docs
- No docs update needed because this corrects internal route selection without changing public APIs.

Changes
Added GitHub discussions link for support options.
Testing
N/A
Docs
N/A

Summary
buildBackgroundArgs() in packages/astro/src/cli/server.ts reconstructed the child process argv from a hardcoded allowlist that only forwarded 6 flags (--port, --host, --config, --root, --allowed-hosts, --json). Seven additional flags recognized by flagsToAstroInlineConfig() — --mode, --site, --base, --out-dir, --verbose, --silent, --open — were silently dropped when running in background mode.
This was especially impactful for --mode, which controls which .env.[mode] file Vite loads. Users running astro dev --background --mode staging would get the default .env with no warning. The problem was further amplified by AI agent auto-detection silently routing users into background mode without any opt-in.
Fix
Added the 7 missing flag forwarding entries to buildBackgroundArgs(), following the same patterns already used for existing flags. Also adds unit tests covering the new flags.
Reporter @aheckerling confirmed the fix resolves their issue.
Closes #17768

Changes
- Fixes an SSR build failure that occurs when a user defines
vite.environments.ssrin their Astro config: the server entry was emitted asindex.mjsinstead ofentry.mjs, breaking adapters (e.g.@astrojs/vercel) that expectentry.mjsin theastro:build:donehook. - In
createViteBuildConfig()(packages/astro/src/core/build/vite-build-config.ts), user-providedenvironmentswere spread before Astro's managedprerender/client/ssrenvironments. Because object key order follows insertion order, a user-suppliedenvironments.ssrkey ended up earlier in the object than expected, and theastro:resolve-inputplugin (which relies onssrbeing resolved last) saved the wrong rolldown input. - Destructures the Astro-managed environment keys (
ssr,prerender,client) out of the user-providedenvironmentsbefore spreading, so they're always re-inserted last and in a fixed order. - Also fixes a bug where user-provided non-build environment config (e.g.
resolve.external) was silently dropped instead of merged into the managed environment config. - Adds a changeset.
Closes #17760
Testing
Added 5 unit tests in packages/astro/test/units/build/vite-build-config.test.ts (environment key ordering block) covering:
ssrstaying last in the environments object even when the user definesenvironments.ssr.- User non-build properties (
resolve.external) being preserved on thessr,prerender, andclientenvironments. - Managed keys appearing exactly once, in the expected order, when the user defines all three.
Also confirmed fixed manually by @matthewp.
Docs
No docs changes needed — this is a bugfix restoring existing documented behavior, not a change to public API or user-facing config.

Summary
When Astro 7.2 introduced the lock file mechanism for astro preview (PR #17174), it did not include a way to bypass the lock check. When --ignore-lock was subsequently added for astro dev (PR #17331), it was not extended to astro preview. This meant that users running multiple simultaneous preview servers (e.g., in Playwright E2E test setups) would always hit the "Another astro preview server is already running" error, with no workaround.
What Changed
- Extends
--ignore-locksupport topackages/astro/src/cli/preview/index.ts, reusing the existingisIgnoreLock()helper from the dev module. - Adds conflict detection:
--ignore-lockis rejected when combined with--forceor--background, mirroring the dev server behavior. - When
--ignore-lockis set, the lock file check and write are skipped, allowing multiple preview servers to run simultaneously on different ports. - Adds
--ignore-lockto the preview--helpoutput. - Includes a unit test (
packages/astro/test/units/preview/ignore-lock-flag.test.ts) and a patch changeset.
Confirmation
@ematipico confirmed the fix: "The preview seems to be working".
Closes #17720
Docs
Needs a docs PR for the new flag

Description
- Closes #4132
- Uses a per-page counter so that the HTML output by
<Tabs>does not change on one page as a side effect of adding/removing tabs in other pages. - Before, tab HTML would include markup like
id="tab-panel-37"indicating the 37th panel rendered during a build/dev session. These IDs now contain two indexes: one for the instance on the page, another for the panel within that instance, e.g.id="tab-panel-1-3"for the fourth panel in the second<Tabs>on a page (the counts are zero-indexed). - Initially when reading #4132 I considered closing it as “working as intended” because tab IDs are an internal API detail and don’t matter too much. However, I think it’s valuable to fix as build systems that diff page content and upload changed files may benefit from this by not needing to reupload multiple files just because of a
<Tabs>change on another page earlier in the build. - Made this a patch as I don’t think there are any reasonable ways people could be using the current
idformat but happy to reconsider if someone knows of one.

Changes
This PR fixes the Sätteri processor option types to accept all plugin entries supported by Sätteri v0.10.3 released yesterday.
Testing
I added a test using a conditional plugin factory which is a new supported type of plugin entry.
Docs
This is a type-only change.

Summary
Fixes #17748
When building an Astro site with output: "static" and the Cloudflare adapter configured with imageService: "compile" (or "cloudflare-binding"), original image files referenced directly in pages (e.g. <a href={image.src}>, og:image, etc.) were missing from dist/client/_astro/ while resized variants were emitted.
Cause
- During prerendering in the Cloudflare preview/workerd worker, raw
srcaccesses add image paths toglobalThis.astroAsset.referencedImagesinside the workerd isolate. - The
/__astro_static_imagesendpoint returned onlystaticImagestransforms and did not serializeglobalThis.astroAsset.referencedImages. - Consequently, the Node-side build (
generate.ts) had an emptyreferencedImagesset and deleted the original unoptimized images because it assumed they were only used as intermediate transform sources.
Fix
- Extended
handleStaticImagesRequestinpackages/integrations/cloudflare/src/utils/prerender.tsto serializereferencedImagesalongsideentries. - Updated
collectStaticImagesinpackages/integrations/cloudflare/src/prerenderer.tsto deserialize and mergereferencedImagesinto Node'sglobalThis.astroAsset.referencedImages. generate.tsnow sees the referenced source paths and leaves the original image assets indist/client/_astro/.

Summary
When using the Cloudflare adapter, Vite's dep optimizer triggers the load hook for astro/assets/fonts/runtime.js eagerly during startup — before the HTTP server has started listening. This caused RemoteRuntimeFontFileUrlResolver.resolve() to throw a "Server address unavailable" error (surfaced as the misleading "URL is invalid" error) because the server address was baked in as null at module load time.
Fix
Updates RemoteRuntimeFontFileUrlResolver.resolve() to use the requestUrl parameter (already part of the RuntimeFontFileUrlResolver interface but previously ignored) as a fallback when the server address is null. When the address is unavailable, requestUrl.origin is used to construct the font file URL instead. This works across runtime boundaries (Node.js and workerd) without requiring shared state or deadlock-prone awaits.
Two new unit tests were added to packages/astro/test/units/assets/fonts/infra.test.ts covering the fallback behavior.
Confirmation
@matthewp confirmed this fixes the problem.
Closes #17722

Summary
Fixes two bugs in astro build error reporting that caused incorrect or missing file locations and misleading hints when MDX (or other plugins) throw transform errors.
Bug 1: Variable scoping in collectErrorMetadata() (packages/astro/src/core/errors/dev/utils.ts)
Inside the err.forEach((error) => { ... }) loop, three references used e (the outer/parent Vite/rolldown error) instead of error (the current sub-error). This caused the sub-error's correct loc, plugin, and stack to be overwritten with data from the parent error — resulting in wrong file locations and spurious "Browser APIs are not available" hints.
if (e.stack)→if (error.stack)collectInfoFromStacktrace(e)→collectInfoFromStacktrace(error)generateHint(e)→generateHint(error)
Bug 2: Missing line:col in MDX plugin (packages/integrations/mdx/src/vite-plugin-mdx.ts)
The MDX plugin's catch block set err.loc = { file: id, line: e.line, column: e.column }, but oxc parser errors don't expose .line/.column as properties — the position info is only available as a "10:5: ..." prefix in the error message. This caused location to render as file:0:0. A fallback that parses line:col from the message string was added.
Tests
Added 3 unit tests in packages/astro/test/units/errors/dev-utils.test.ts covering the aggregate error path for collectErrorMetadata().
Confirmed by @matthewp.
Closes #17735

Summary
Adds "allowScripts": { "esbuild": true } to all 24 example package.json files and introduces ensureNpmScriptsAllowed() in packages/create-astro/src/actions/dependencies.ts to handle third-party templates as well.
Why
npm v11 introduced allowScripts — a security feature requiring explicit approval of packages with install scripts. esbuild (a direct dependency of astro) runs a postinstall script to download platform-specific binaries. Without this approval, npm v11 emits a warning on every npm install, and npm v12 (expected July 2026) will make it a hard failure.
This is the npm counterpart to the pnpm v11 strictDepBuilds fix from PR #17205, which added ensurePnpmBuildsAllowed(). The new ensureNpmScriptsAllowed() follows the same pattern.
Confirmation
@matthewp confirmed this fix resolves the issue.
Closes #17745

Changes
- Fixes #17335. Astro updates the content store after a collection changes, but Windows sometimes misses the file-change event needed to clear Vite's cached content.
- The content store now tells Vite directly when Astro finishes writing the update, so invalidation no longer depends on that unreliable event.
- Vite clears its cached content and route data before reloading the page, allowing the updated content to appear.
Testing
store-write-notifications.test.ts: write notifications fire for the store file (single-file and chunked), asset imports file, not for identical data, and stop after unsubscribe.content-virtual-mod.test.ts: simulates the Windows failure mode. A store write with no watcher event still invalidates and reloads once, the watcher echo is skipped, and a later external watcher event still invalidates.
Docs
- No docs update needed; this is a bug fix with no API or behavior change beyond dev HMR working as documented.

Summary
Fixes a bug where astro dev would incorrectly treat a stale lock file as valid when the new process inherited the same PID as the old one — a common occurrence in Docker containers, where PID namespaces reset on restart.
Root Cause
In packages/astro/src/core/dev/lockfile.ts, isLockFileProcessAlive() checks whether the PID in the lock file belongs to a running Astro process. When a container restarts, the new astro dev process often gets the same PID as the previous one. The function would find itself running, correctly identify it as an Astro command, and conclude the lock file was still valid — blocking startup.
Fix
Added a self-PID guard at the top of isLockFileProcessAlive():
if (data.pid === process.pid) {
return false;
}The current process cannot be the server recorded in the lock file (it hasn't started one yet), so a matching PID always means the lock file is stale.
A unit test covering this case was also added in packages/astro/test/units/dev/lockfile.test.ts.
Confirmed by
@RashiqAzhan confirmed the fix resolves the issue: "I can confirm this is working for me. No more --force is required."
Closes #17744

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.2.5
Patch Changes
-
#17758
5f419e2Thanks @astro-factory! - Fixes a bug whereexperimental_getFontFileURL()rejected valid font URLs when using the Cloudflare adapter -
#17416
493796bThanks @iseraph-dev! - Skips no-op pathname writes when normalizing SSR request URLs -
#17712
bd374b7Thanks @fkatsuhiro! - Updates deprecation messages target from Astro 7 to 8 -
#17719
dac1768Thanks @astrobot-houston! - Fixes session ID validation to reject non-UUID cookie values before using them as storage keys -
#17770
84eb7e7Thanks @astro-factory! - Fixes--mode,--site,--base,--out-dir,--verbose,--silent, and--openflags being silently dropped when usingastro dev --backgroundorastro preview --background -
#17713
d035290Thanks @wakqasahmed! - Fixescontent-modules.mjsnot removing entries for deleted or renamed content files, which could cause Vite to attempt to resolve non-existent modulesAs part of this fix,
#moduleImportsis now fully rebuilt fromdeferredRenderentries before every write, so a module import added only through the publicaddModuleImport()API without a correspondingdeferredRenderentry in the store will no longer be preserved across writes. -
#17743
adc750fThanks @contactjawad! - FixesAstro.preferredLocaleandAstro.preferredLocaleListignoringAccept-Languagequality values when they are absent or0. An entry without an explicitq=now correctly counts as quality1.0(per RFC 7231) and an entry withq=0is treated as not acceptable, so the highest-quality locale is selected regardless of header order. -
#17757
660991cThanks @astro-factory! - Fixes build errors showing wrong file location, missing line:col, and misleading hints when a plugin error (e.g. from MDX) is wrapped by Vite's build error -
#17783
60b14ffThanks @matthewp! - Fixes a type error when passing an image from a content collectionimage()schema to a component or<Image />. The schema returned byimage()was missing theapngformat, so it no longer matched the type of an imported image. -
#17664
d483125Thanks @astrobot-houston! - Fixes an issue where Astro CSP support didn't correctly handle cases"unsafe-inline"resource. Now when"unsafe-inline", Astro won't emit hashes for the directive specified. -
#17810
0fc5f65Thanks @florian-lefebvre! - Fixes a regression in the content collections that could cause images to not be resolved -
#17781
aa33b44Thanks @matthewp! - FixesmemoryCache()storing responses that set cookies throughAstro.cookiesorAstro.session -
#17787
6661fbeThanks @astro-factory! - Fixesserver:defercrashing the dev server with "undefined is not a function" when a deferred component imports fromastro:i18n -
#17750
dd0e3acThanks @dobrodob! - Fixes a regression wheretransition:persiststopped working for<audio>and<video>elements. -
#17774
fe1d16dThanks @astro-factory! - Adds support for importing.apngfiles as image metadata for use with standard<img>elements. Astro's image components reject APNG files to avoid removing their animation -
#17799
8797754Thanks @astro-factory! - Fixes i18nfallbackType: "rewrite"returning 500 instead of 404 when the fallback locale also has no matching static path for a prerendered dynamic route -
#17741
99d3d3dThanks @ericswpark! - Bumps the Astro compiler to the latest version. Changelog. -
#17782
3578d45Thanks @Princesseuh! - Improves the performance of the Astro CLI in local by enabling Node's module compilation cache. -
#17705
2043e4fThanks @astrobot-houston! - Fixes incremental builds serving cached HTML that references stale CSS filenames after a stylesheet-only edit -
#17754
3d50dfdThanks @astro-factory! - Fixes the dev server refusing to start in Docker containers after a restart due to PID reuse in the lock file check -
#17769
bbda94dThanks @astro-factory! - Fixes a build failure when definingvite.environments.ssrin the Astro config. User-provided environment config forssr,prerender, orclientis now properly deep-merged with Astro's internal environment settings instead of silently breaking the server entry naming. -
#17776
0874da8Thanks @astro-factory! - Fixes theglob()content loader failing to load files with colons in their names (e.g.,Guide: Architecture.md) -
Updated dependencies [
0762a83,0c99615]:- @astrojs/markdown-satteri@0.3.8
create-astro@5.2.4
Patch Changes
- #17756
f88c875Thanks @astro-factory! - Fixesnpm installwarnings on npm v11+ about esbuild's install scripts not being covered byallowScripts. AddsensureNpmScriptsAllowed()to pre-approve esbuild inpackage.jsonbefore runningnpm install, matching the existing pnpm v11 compatibility fix.
@astrojs/cloudflare@14.2.4
Patch Changes
-
#17789
7c541a7Thanks @astro-factory! - Fixes dep scanning failure when.astrofrontmatter contains regex literals with quote characters (e.g./"/g) -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.4
@astrojs/mdx@7.0.8
Patch Changes
-
#17757
660991cThanks @astro-factory! - Fixes build errors showing wrong file location, missing line:col, and misleading hints when a plugin error (e.g. from MDX) is wrapped by Vite's build error -
#17766
0762a83Thanks @HiDeoo! - Fixes Sätteri processor option types to accept all plugin entries supported by Sätteri v0.10.3.
@astrojs/netlify@8.2.4
Patch Changes
-
#17752
e362d4cThanks @matthewp! - Fixes generated Netlify Image CDN allowlists to reject remote URLs that contain an allowed image origin only within their path or query string -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.4
@astrojs/vercel@11.0.8
Patch Changes
- #17794
dd29ce8Thanks @astro-factory! - Fixes a bug where@vercel/nftfile tracing silently dropped all dependency files whenoutDirwas configured outsideroot, causing deployed functions to crash withERR_MODULE_NOT_FOUND
astro-vscode@2.16.19
Patch Changes
- #17791
426eaa1Thanks @matthewp! - Fixes missing Vue template auto-import completions when the Astro extension loads first
@astrojs/markdown-satteri@0.3.8
Patch Changes

Changes
- For
config.domainswe should be using^so that the URL matches at the front of the URL. - This prevents query params from matching.
Testing
- Tests explain what this fixes best.
Docs
N/A, bug fix

Changes
reifyMediaElements()(added in #17603, shipped in 7.2.1) runs after thetransition:persistelements from the old document have been moved into the new body, so it also replaced the live<audio>/<video>nodes carried over from the previous page. Those nodes were never parsed byDOMParserand are not inert; re-creating them resetscurrentTime/pausedand drops listeners and framework refs, which breaks persistent players on every<ClientRouter />navigation.- Collect the media nodes of the old body before the swap (
new Set(oldElement.querySelectorAll('video, audio'))) and skip exactly those inreifyMediaElements(). A node that was live and is still in the new body got there throughtransition:persist— at any nesting depth (including a persist container nested inside a persisted one that has no counterpart on the new page) or with the attribute on the media element itself — so the check is precise by construction. Media parsed from the new document are never in the set and still get reified, so #17601 stays fixed for them.
Closes #17749
Testing
- Two new e2e tests in
packages/astro/e2e/view-transitions.test.ts(fixturesnested-persist-one/two.astro): an expando set on the persisted<video>before navigation must still be there afterwards — (1)transition:persiston the media element itself (existingvideo-one/twofixture), (2) media inside an inner persist container that has no counterpart on the next page and travels with its matched outer container. Run locally in Chrome Stable: both fail onmain(2 failed) and pass with this branch (2 passed). The existing<video> can persisttest only asserts thatcurrentTimegrows, which a freshly re-created autoplaying element also satisfies — that is why the regression was not caught. - Also reproduced in a real app (discours/publy, persistent audio player): with stock 7.2.2 the persisted
<audio>loses its identity after a link navigation; with this change applied todist/transitions/swap-functions.jsand the app rebuilt, the app's e2e passes. Details: discours/publy#1410 (comment)
Docs
- No docs update needed; this restores the behaviour
transition:persistalways had before 7.2.1.

Changes
Make sure image-size doesn't hang infinitely on some malformed images. This code has been ported from a community fork called image-size-next
Testing
Added new tests
Docs
N/A

Changes
Updated the issue template configuration to rename 'Chat' to 'Chat & support' and added a new support option for GitHub discussions.
Testing
N/A
Docs
N/A

Changes
Fixes #17726.
With build.format: 'preserve' the build asks for pages as /welcome.html, and #stripHtmlExtension() removes that framework-injected suffix so the pipeline sees the canonical path:
this.pathname = this.pathname.replace(/\/index\.html$/, '/').replace(/\.html$/, '');The first replacement keeps the trailing slash; the second one drops it. Route patterns are compiled with the configured trailing slash, so under trailingSlash: 'always' the route no longer matches its own path. getParams() returns nothing and stringifyParams() then throws TypeError: Missing parameter for any dynamic route — the build fails with no way out other than changing config.
The trailing slash is now restored after stripping when trailingSlash is 'always', so the pathname matches the pattern it came from. Nothing changes for 'ignore' or 'never', and /index.html was already handled by the first replacement.
This needs both settings together, which is why it looked unreproducible at first — build.format: 'preserve' alone is fine, and trailingSlash: 'always' alone is fine.
Testing
Two cases in packages/astro/test/units/fetch/index.test.ts, next to the existing .html stripping test:
trailingSlash: 'always'—/welcome.htmlresolves to/welcome/trailingSlash: 'ignore'—/welcome.htmlresolves to/welcome, unchanged
Reverting the source change fails the first with trailing slash should survive .html stripping. Worth noting these tests import from dist/, so the package has to be rebuilt for that check to mean anything — reverting only src leaves them passing.
test/units/fetch (80) and test/units/routing (56) both pass.
Docs
No docs change — this restores the documented behaviour of a supported config combination.

Changes
- Updates
compiler-rsto0.4.0
Testing
Tested with pnpm build and pnpm test
Docs
No behavior change AFAIK other than the bugfix for the define:vars 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
astro@7.2.4
Patch Changes
-
#17747
a90ff66Thanks @Princesseuh! - Fixes builds hanging when an image file is malformed -
#17701
05763a0Thanks @matthewp! - Fixes base path stripping to respect path-segment boundaries. With a configuredbasesuch as/docs, a request like/docs-archive/pageis no longer treated as being under the base, so routing andcontext.url.pathnamenow agree on the same pathname. -
#17742
70b449dThanks @Kjubikstronk! - Fixesastro buildthrowingTypeError: Missing parameterfor dynamic routes whenbuild.format: 'preserve'andtrailingSlash: 'always'are used together. Stripping the framework-injected.htmlsuffix dropped the trailing slash that the compiled route pattern requires, so the route no longer matched itself and its params resolved as empty. -
#17703
771b0a9Thanks @astrobot-houston! - FixesAstro.sitealways beingundefinedwhen rendering components via the Container API, even whensiteis set inastroConfig -
Updated dependencies [
05763a0,bc171af]:- @astrojs/internal-helpers@0.10.4
- @astrojs/markdown-satteri@0.3.7
- @astrojs/markdown-remark@7.2.4
@astrojs/cloudflare@14.2.3
Patch Changes
- Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
- @astrojs/underscore-redirects@1.0.4
@astrojs/markdoc@2.0.8
Patch Changes
- Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
@astrojs/mdx@7.0.7
Patch Changes
-
#17731
bc171afThanks @Princesseuh! - Updates Sätteri processor to v0.10.3. See its changelog for details on bugs fixed and features added. -
Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
- @astrojs/markdown-remark@7.2.4
@astrojs/netlify@8.2.3
Patch Changes
- Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
- @astrojs/underscore-redirects@1.0.4
@astrojs/node@11.1.4
Patch Changes
- Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
@astrojs/preact@6.0.4
Patch Changes
- Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
@astrojs/react@6.0.4
Patch Changes
- Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
@astrojs/vercel@11.0.7
Patch Changes
-
#17687
0a22ff5Thanks @asmyshlyaev177! - FixesmiddlewareMode: 'edge'not running your middleware whenisris also enabledPreviously, enabling both options deployed the edge middleware but never reached it: requests went straight to the ISR function, which skips rendering entirely on a cache hit. Middleware now runs at the edge for ISR-backed routes before the cached response is served, and query strings are preserved when it forwards the request.
-
Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
@astrojs/internal-helpers@0.10.4
Patch Changes
- #17701
05763a0Thanks @matthewp! - Fixes base path stripping to respect path-segment boundaries. With a configuredbasesuch as/docs, a request like/docs-archive/pageis no longer treated as being under the base, so routing andcontext.url.pathnamenow agree on the same pathname.
@astrojs/markdown-remark@7.2.4
Patch Changes
- Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4
@astrojs/markdown-satteri@0.3.7
Patch Changes
-
#17731
bc171afThanks @Princesseuh! - Updates Sätteri processor to v0.10.3. See its changelog for details on bugs fixed and features added. -
Updated dependencies [
05763a0]:- @astrojs/internal-helpers@0.10.4

Changes
Adds renderComponent() to the Astro container APIs. The added feature to this function is that it renders the scripts and styles that are declared in the component. It uses the compiler the runtime to dynamically attach and swap them.
import Component from "./components/Footer.astro"
const container = await experimental_AstroContainer.create();
export const GET: APIRoute = async () => {
return new Response(
await container.renderComponent(Component, {
props: { message: 'Hello ' },
slots: { default: 'World' },
}),
);
};What do you think?
Testing
Added new tests
Docs
Will create one

Changes
- Removes the issue-triage github action. We have a separate GitHub app now that includes issue triage (same code) and review.
- Adds factory-preview.yml. Since triage is being driven by a GitHub app now, it cannot do preview releases since we need OIDC. The solution is to have an action the bot can call to do the releases. That's what this is for.
Testing
N/A
Docs
N/A

Changes
Updated to the latest version of Sätteri. This update brings on a bunch of features people have been asking for (root visitors, rehype-raw equivalent, bug fixes. etc)
Testing
Tests should pass!
Docs
N/A, we don't document the current version of Sätteri used

Description
Adds new LinkCard entries for two new plugins to the starlight community plugins section:
- starlight-pydocs, a Starlight version of mkdocstrings that uses Griffe for Python static analysis and then builds the Starlight pages from that JSON.
- starlight-quiz, a Starlight version of mkdocs-quiz
Convention seemed to be to link the GitHub source repositories, so that's what I did. If you prefer, I can link the docs websites instead:
Thank you for Astro and Starlight! 🌟

Changes
- Switches from astro-review.yml -> factory.yml, a general purpose agent bot that includes review and other things.
- Only review enabled right now. It works exactly the same as astro-review.yml.
Testing
Not testable
- Needs to be installed in the repo after this is merged.
Docs
N/A

Changes
Fixes a bug I found while using the chunked collection storage. This fix splits the writing, so it should avoid the OOM I experienced
Testing
Added new tests.
I will create a preview release and test it
Docs
N/A

Description
This PR updating Vite to version 8.2.0 included a change to warn for features incompatible with the native loader when using the bundle loader (default).
Vite is planning to switch to the native loader by default so this change is just preparing for that future change.
In our case, this means that every Vitest workspace emit such warnings so it ends up being a lot of noise, e.g. here is one example for 1 workspace:
(!) Your Vite config uses features that are unsupported by `configLoader: 'native'`, which is planned to become the default in a future major version of Vite:
- import "../test-config" without a file extension (vitest.config.ts:1:36). Add the file extension
- import "../integrations/vite-virtual-modules" without a file extension (../test-config.ts:5:51). Add the file extension
- import "../utils/plugins" without a file extension (../test-config.ts:6:65). Add the file extension
- import "./test-plugin-utils" without a file extension (../test-config.ts:7:41). Add the file extension
- import "../integrations/vite-layer-order" without a file extension (../test-config.ts:8:50). Add the file extension
- import "../utils/plugins" without a file extension (../test-plugin-utils.ts:2:45). Add the file extension
- import "../utils/error-map" without a file extension (../../utils/plugins.ts:4:41). Add the file extension
- import "../utils/collection-fs" without a file extension (../../integrations/vite-virtual-modules.ts:5:39). Add the file extension
- import "../utils/user-config" without a file extension (../../utils/plugins.ts:9:8). Add the file extension
- import "../utils/user-config" without a file extension (../../integrations/vite-virtual-modules.ts:6:38). Add the file extension
- import "./translations" without a file extension (../../utils/plugins.ts:10:37). Add the file extension
- import "../utils/git" without a file extension (../../integrations/vite-virtual-modules.ts:7:40). Add the file extension
- import "./translations-fs" without a file extension (../../utils/plugins.ts:11:47). Add the file extension
- import "../utils/plugins" without a file extension (../../integrations/vite-virtual-modules.ts:8:41). Add the file extension
- import "../integrations/shared/absolutePathToLang" without a file extension (../../utils/plugins.ts:12:63). Add the file extension
- import "./collection-fs" without a file extension (../../utils/plugins.ts:13:40). Add the file extension
- import "./collection" without a file extension (../../utils/collection-fs.ts:3:60). Add the file extension
- import "../../types" without a file extension (../../integrations/shared/absolutePathToLang.ts:1:38). Add the file extension
- import "../schemas/i18n" without a file extension (../../utils/translations-fs.ts:5:39). Add the file extension
- import "../schemas/components" without a file extension (../../utils/user-config.ts:3:39). Add the file extension
- import "./localeToLang" without a file extension (../../integrations/shared/absolutePathToLang.ts:2:30). Add the file extension
- import "./createTranslationSystem" without a file extension (../../utils/translations-fs.ts:6:41). Add the file extension
- import "../schemas/expressiveCode" without a file extension (../../utils/user-config.ts:4:38). Add the file extension
- import "./slugToLocale" without a file extension (../../integrations/shared/absolutePathToLang.ts:3:30). Add the file extension
- import "./user-config" without a file extension (../../utils/translations-fs.ts:7:38). Add the file extension
- import "../schemas/favicon" without a file extension (../../utils/user-config.ts:5:31). Add the file extension
- import "../schemas/head" without a file extension (../../utils/user-config.ts:6:34). Add the file extension
- import "../../types" without a file extension (../../integrations/shared/slugToLocale.ts:1:38). Add the file extension
- import "../../types" without a file extension (../../integrations/shared/localeToLang.ts:1:38). Add the file extension
- import "../schemas/logo" without a file extension (../../utils/user-config.ts:7:34). Add the file extension
- import "../schemas/i18n" without a file extension (../../utils/createTranslationSystem.ts:2:39). Add the file extension
- import "../../utils/i18n" without a file extension (../../integrations/shared/localeToLang.ts:2:38). Add the file extension
- import "../schemas/pagefind" without a file extension (../../utils/user-config.ts:8:62). Add the file extension
- import "../translations/index" without a file extension (../../utils/createTranslationSystem.ts:3:33). Add the file extension
- import "../schemas/sidebar" without a file extension (../../utils/user-config.ts:9:35). Add the file extension
- import "./user-config" without a file extension (../../utils/i18n.ts:3:38). Add the file extension
- import "./i18n" without a file extension (../../utils/createTranslationSystem.ts:4:38). Add the file extension
- import "../schemas/site-title" without a file extension (../../utils/user-config.ts:10:63). Add the file extension
- import "./user-config" without a file extension (../../utils/createTranslationSystem.ts:5:38). Add the file extension
- import "../schemas/social" without a file extension (../../utils/user-config.ts:11:35). Add the file extension
- import "./translations" without a file extension (../../utils/createTranslationSystem.ts:6:51). Add the file extension
- import "../schemas/tableOfContents" without a file extension (../../utils/user-config.ts:12:49). Add the file extension
- import "./i18n" without a file extension (../../utils/user-config.ts:13:38). Add the file extension
- import "../schemas/i18n" without a file extension (../../translations/index.ts:1:35). Add the file extension
- JSON import "./cs.json" without import attributes (../../translations/index.ts:2:16). Add `with { type: 'json' }`
- JSON import "./en.json" without import attributes (../../translations/index.ts:3:16). Add `with { type: 'json' }`
- JSON import "./es.json" without import attributes (../../translations/index.ts:4:16). Add `with { type: 'json' }`
- JSON import "./ca.json" without import attributes (../../translations/index.ts:5:16). Add `with { type: 'json' }`
- JSON import "./de.json" without import attributes (../../translations/index.ts:6:16). Add `with { type: 'json' }`
- JSON import "./ja.json" without import attributes (../../translations/index.ts:7:16). Add `with { type: 'json' }`
- JSON import "./pt.json" without import attributes (../../translations/index.ts:8:16). Add `with { type: 'json' }`
- JSON import "./fa.json" without import attributes (../../translations/index.ts:9:16). Add `with { type: 'json' }`
- JSON import "./fi.json" without import attributes (../../translations/index.ts:10:16). Add `with { type: 'json' }`
- JSON import "./fr.json" without import attributes (../../translations/index.ts:11:16). Add `with { type: 'json' }`
- JSON import "./gl.json" without import attributes (../../translations/index.ts:12:16). Add `with { type: 'json' }`
- JSON import "./he.json" without import attributes (../../translations/index.ts:13:16). Add `with { type: 'json' }`
- JSON import "./id.json" without import attributes (../../translations/index.ts:14:16). Add `with { type: 'json' }`
- JSON import "./it.json" without import attributes (../../translations/index.ts:15:16). Add `with { type: 'json' }`
- JSON import "./nl.json" without import attributes (../../translations/index.ts:16:16). Add `with { type: 'json' }`
- JSON import "./da.json" without import attributes (../../translations/index.ts:17:16). Add `with { type: 'json' }`
- JSON import "./th.json" without import attributes (../../translations/index.ts:18:16). Add `with { type: 'json' }`
- JSON import "./tr.json" without import attributes (../../translations/index.ts:19:16). Add `with { type: 'json' }`
- JSON import "./ar.json" without import attributes (../../translations/index.ts:20:16). Add `with { type: 'json' }`
- JSON import "./nb.json" without import attributes (../../translations/index.ts:21:16). Add `with { type: 'json' }`
- JSON import "./zh-CN.json" without import attributes (../../translations/index.ts:22:16). Add `with { type: 'json' }`
- JSON import "./ko.json" without import attributes (../../translations/index.ts:23:16). Add `with { type: 'json' }`
- JSON import "./sv.json" without import attributes (../../translations/index.ts:24:16). Add `with { type: 'json' }`
- JSON import "./ro.json" without import attributes (../../translations/index.ts:25:16). Add `with { type: 'json' }`
- JSON import "./ru.json" without import attributes (../../translations/index.ts:26:16). Add `with { type: 'json' }`
- JSON import "./vi.json" without import attributes (../../translations/index.ts:27:16). Add `with { type: 'json' }`
- JSON import "./uk.json" without import attributes (../../translations/index.ts:28:16). Add `with { type: 'json' }`
- JSON import "./hi.json" without import attributes (../../translations/index.ts:29:16). Add `with { type: 'json' }`
- JSON import "./zh-TW.json" without import attributes (../../translations/index.ts:30:18). Add `with { type: 'json' }`
- JSON import "./pl.json" without import attributes (../../translations/index.ts:31:16). Add `with { type: 'json' }`
- JSON import "./sk.json" without import attributes (../../translations/index.ts:32:16). Add `with { type: 'json' }`
- JSON import "./lv.json" without import attributes (../../translations/index.ts:33:16). Add `with { type: 'json' }`
- JSON import "./hu.json" without import attributes (../../translations/index.ts:34:16). Add `with { type: 'json' }`
- JSON import "./el.json" without import attributes (../../translations/index.ts:35:16). Add `with { type: 'json' }`
- import "../integrations/expressive-code" resolves to a directory index (../../schemas/expressiveCode.ts:2:53). Import the index file directly
- import "./badge" without a file extension (../../schemas/sidebar.ts:4:39). Add the file extension
- import "./icon" without a file extension (../../schemas/social.ts:2:28). Add the file extension
- import "../utils/path" without a file extension (../../schemas/sidebar.ts:5:48). Add the file extension
- import "../components-internals/Icons" without a file extension (../../schemas/icon.ts:2:43). Add the file extension
- import "../user-components/file-tree-icons" without a file extension (../../components-internals/Icons.ts:1:27). Add the file extension
Set `VITE_CONFIG_NATIVE_IGNORE_WARNING=true` to suppress this warning.
Multiplied by the number of workspaces, this makes the output very noisy and hard to read while working on tests.
This PR removes such warnings by using the VITE_CONFIG_NATIVE_IGNORE_WARNING=true environment variable. This means that it only suppresses the warnings and does not really fix them:
- An environment variable is the only way to suppress the warnings, as there is no equivalent configuration option.
- Such default loader change would probably have quite the impact on the Astro ecosystem, and we don't know yet how we will handle it.
- Fixing the project to be compatible with the native loader is entirely possible right now, but with #3572 around the corner:
- It will be a lot of changes probably touching every file in the project (extension-less imports) so having 2 PRs touching so many files in parallel will just be annoying.
- Doing it in #3572 does not make a lot of sense and will only make the review more difficult
Considering all this, this PR just suppresses the warnings for now, making running tests locally readable again, and we can deal with the real fix later, at least once #3572 is merged.

Changes
#ensureSessionID()now validates the client-supplied session cookie value against a UUID v4 format before accepting it as the session ID. Non-UUID values are silently rejected and a fresh UUID is generated instead.- Closes #17718. Only affects setups where the session driver shares a storage backend with other data and no key prefix is used — all default adapter configurations are unaffected.
Testing
- Adds a test case that sends a crafted non-UUID cookie value (
not-a-uuid-at-all) and asserts that the resulting session ID is a valid UUID distinct from the crafted value.
Docs
- No docs update needed; this is an internal validation fix with no API surface changes.

Fixes #17478
Problem
When a project uses TypeScript project references (references in tsconfig.json), astro check (and the editor language server) silently drops .astro files that live inside a referenced tsconfig's project. Only the root tsconfig's files were checked with Astro's extraFileExtensions; files pulled in through a reference were not.
Root cause
In @volar/kit's createChecker.js, the root tsconfig is parsed with ts.parseJsonSourceFileConfigFileContent(...), explicitly passing the language plugins' extraFileExtensions (so .astro is included). The project-reference walker (visit()) instead reuses TypeScript's own internally-resolved ref.commandLine for each referenced project, which was resolved without knowledge of extraFileExtensions. As a result, .astro files inside a referenced project were never added to the referenced project's file list.
Revised approach (see review discussion)
The first version of this PR patched @volar/kit@2.4.28 directly via pnpm's patchedDependencies. A cold-start review caught a blocking problem with that approach: @astrojs/language-server and @astrojs/check both build with tsc -b (no bundling) and declare @volar/kit as a normal runtime dependency. pnpm's patchedDependencies only rewrites node_modules inside this monorepo for local dev/CI — it is never encoded into the published npm tarballs. Anyone running npm install @astrojs/check (or @astrojs/language-server) would still get the real, unpatched @volar/kit, so the original bug would remain for every CLI/CI user — the exact scenario in #17478. The only place that actually got the fix was the VS Code extension, because it bundles with esbuild and inlines the patched code at build time.
This PR now implements the fix directly in @astrojs/language-server's own source instead, so it ships in the real published packages:
createTypeScriptChecker'ssetupcallback runs once per project — the root project and each project reference — and receives that project'sconfigFileNameand a mutablelanguageServiceHost.- For each project, we re-parse its own tsconfig with the language plugins'
extraFileExtensions(the same call shape@volar/kitalready uses for the root tsconfig) and merge any newly-found files intolanguageServiceHost.getScriptFileNames(). This affects the actual TypeScriptProgramused for diagnostics. getRootFileNames()(used byAstroCheck.lint()to enumerate the whole project when no explicit file list is given) reads project-reference file lists from a separate internal host that isn't reachable fromsetup, so it gets its own equivalent patch onthis.linter.getRootFileNames.- No changes to the
@volar/kitdependency at all —patches/@volar__kit@2.4.28.patchand thepatchedDependenciesentries inpnpm-workspace.yaml/pnpm-lock.yamlhave been removed.
There is still an open upstream fix for the same root cause in volar.js itself (volarjs/volar.js#315). If/when that lands and this repo's @volar/kit pin is bumped past it, our in-source workaround in check.ts becomes redundant (harmless, since it re-derives the same file list) and can be removed.
Test plan
- Regression test in
packages/language-tools/language-server/test/check/check.test.ts: a.astrofile with a type error added to the project-references fixture, asserting viachecker.linter.getRootFileNames()(per review feedback, independent of the diagnostics-count assertion) that the referenced project's file list includes it, plus asserting the error count and file-checked count. - Verified the assertions fail without the fix and pass with it, using the real, unpatched
@volar/kit@2.4.28from npm (not a pnpm patch) — confirms the fix is present in code that will actually reach published packages. -
node --test test/check/check.test.tsinpackages/language-tools/language-server: 10/10 passing, including a clean rebuild ofastro,@astrojs/markdown-satteri,@astrojs/svelte, and@astrojs/vueto confirm the two previously-"pre-existing" failures were an artifact of an unbuilt local environment, not a real repo-level issue. - Updated changeset for
@astrojs/language-serverdescribing the in-source fix.

Fixes #17707
Changes
content-modules.mjswas only ever appended to via#moduleImports, so deleting or renaming a content file withdeferredRenderleft a stale entry pointing at a non-existent file. This caused Vite to attempt to resolve the missing module (e.g. in dev, after removing an MDX/Markdown page).- Added
#rebuildModuleImports(), mirroring the existing#rebuildAssetImports()pattern used for the asset-imports stale-entry fix (#16097). It clears#moduleImportsand rebuilds it from the current_collectionsstate. writeModuleImports()now calls#rebuildModuleImports()before writing, anddelete(),clear(), andclearAll()now also trigger a debounced module-imports rewrite (previously they only rewrote asset imports).
Testing
Added two unit tests to packages/astro/test/units/content-collections/mutable-data-store.test.ts:
removes stale module imports when an entry is deletedremoves stale module imports when a collection is cleared
Ran the full mutable-data-store.test.ts suite locally (8 tests, all passing):
✔ removes stale image asset import after entry image path is updated (issue #16097)
✔ removes asset imports when an entry is deleted
✔ removes asset imports when a collection is cleared
✔ removes stale module imports when an entry is deleted
✔ removes stale module imports when a collection is cleared
✔ reproduces race condition: concurrent writeToDisk() calls lose data
✔ strips image prefixes and records their paths as out-of-band imageImports
✔ does not set imageImports when the entry has no images
Docs
No user-facing API changes; no docs needed.

Changes
Please refar this comment from @florian-lefebvre san.
IIRC all comments/deprecations that talk about Astro 7 are wrong because v7 was a small focused major. Instead they should mention v8
In this PR, deprecation warnings and inline comments from Astro 7.0 to 8.0.

Changes
- Adds
FetchStateresponse finalizers and original-route detection for composable fetch handlers. - Restores cookies, sessions, CDN cache defaults, asset fallback, and prerendering for Cloudflare custom entrypoints. Fixes #17600.
Testing
- Expands custom-entrypoint coverage for cookies, sessions, cache headers, assets, Hono, and prerendering.
- Adds
FetchStateand mutable/immutable response finalization tests.
Docs

Changes
- CSS modules in the prerender build have empty
code— Vite extracts their content into separate assets, leaving the module'scodeas"". BecausehashModules()was skipping modules with no code, CSS edits never changed the dependency hash andcanSkip()incorrectly returnedtrue, restoring cached HTML that referenced the old, now-missing_astro/*.cssfilename. - When a module's code is empty and its ID matches
CSS_LANGS_RE,hashModules()now reads the source file from disk and includes its contents in the hash. CSS edits now produce a different dependency hash, causing affected pages to re-render with the correct stylesheet reference.
Closes #17704
Testing
- Added two unit tests in
plugin-incremental.test.tsunderCSS modules (#17704): one that asserts the hash changes when the CSS file is modified on disk, and one that asserts the hash is stable when the file is unchanged.
Docs
- No docs update needed — this fixes a bug in
experimental.incrementalBuild, which is not yet documented beyond the config reference.

Closes #17682
Changes
Astro.siteis now correctly set when rendering components via the Container API. Previously,astroConfig.sitepassed toAstroContainer.create()was accepted in the type signature but never read — the value was never forwarded to the internalcreateManifest()call or written onto theSSRManifest, soAstro.sitewas alwaysundefined.- Wires
astroConfig.sitethroughAstroContainer.create()→ constructor →createManifest(), and adds'site'to theAstroContainerManifestPick type so a pre-built manifest can also carry the value.
Testing
- Added
'Astro.site reflects astroConfig.site'— verifies thatAstro.sitematches the URL set inastroConfig.site. - Added
'Astro.site is undefined when astroConfig.site is not set'— verifies the default behavior remains unchanged.
Docs
No docs update needed — AstroContainer.create() already documents the astroConfig.site option; this fix makes it work as documented.

Changes
- Base stripping now only removes a configured
basewhen the pathname is the base itself or continues at a path-segment boundary. Withbase: '/docs', a request like/docs-archive/pageis treated as outside the base instead of being rewritten to/page. This keeps route matching andcontext.url.pathnamein agreement. - Consolidates the three duplicated base-stripping implementations (
BaseApp.removeBase,FetchState.#computePathname, and the i18n domain helper) into a single sharedstripRequestBasehelper in@astrojs/internal-helpers, matching the boundary logic the router already uses instripBase.
Testing
- Adds
base-prefix-boundary.test.tscovering single-character extensions of the base prefix (/docsX/...,/docs2/...,/docs-/...), asserting they do not resolve to a route under the base.
Docs
- No docs update needed; this corrects internal pathname handling with no public API change.

Changes
computePreferredLocaleListcompared object-form locale codes exactly, while every other locale comparison ini18n/utils.tsnormalizes both sides first. That one raw comparison is now normalized like the rest.- The result is a self-contradiction on a single request:
sortAndFilterLocalesfilters on normalized codes, so a locale configured as{ path: 'english', codes: ['en-us'] }passes the filter when a browser sendsAccept-Language: en-US, and is then silently dropped by the exact comparison.Astro.preferredLocalereturns'en-us'whileAstro.preferredLocaleListreturns[]. - The two branches of the same loop disagreed: the string-locale branch already normalized (so
locales: ['en-us']works today), the object-form branch did not. Underscore codes such asen_USwere affected the same way, sincenormalizeTheLocalemaps_to-. - The configured casing is still what gets returned — the comparison is normalized, but the original
codeis what's pushed, matching howgetLocaleByPathcompares normalized and returns the configured value.
Production change is one line:
- if (code === browserLocale.locale) {
+ if (normalizeTheLocale(code) === normalizeTheLocale(browserLocale.locale)) {One thing I deliberately left out: computePreferredLocale breaks out of its codes loop on first match (#16600), and this list function doesn't. That's a visible behaviour difference between the pair, but it's a separate change with its own semantics, so I'd rather ask than bundle it — happy to follow up if you want them aligned.
Testing
Added to the existing packages/astro/test/units/i18n/i18n-utils.test.ts, in the style of the surrounding cases:
- object-form codes matched case-insensitively (
codes: ['en-us']vsen-US) — failed before this change, returning[] - object-form codes with an underscore separator (
codes: ['en_US']) — failed before, returning[] - object-form matches sorted by quality value — failed before
- the configured casing is returned for an exact-case object-form code
- string locales still match case-insensitively
- a code is returned rather than the path for a multi-code entry
The last three pass both before and after, so they guard against a regression rather than describing the fix.
Locally: 32/32 in that file, and the full i18n unit suite (396 tests), tsc -b, format:ci, biome and eslint all pass. One caveat — astro-scripts test fails on my machine with ERR_UNKNOWN_FILE_EXTENSION for .ts on Node 22.17.1 (the repo's .nvmrc is 24.14.0); it fails identically on files I didn't touch, so I ran the units via the equivalent node --experimental-strip-types --test. CI on Node 24 will run them through the wrapper.
Docs
No docs change needed. Astro.preferredLocaleList is already documented as returning the list of matching locales; this makes the object-form config shape behave as documented, rather than changing any documented behaviour.

Changes
- Shallow-clones
langAliasbefore passing it to Shiki'screateHighlighter()inpackages/internal-helpers/src/shiki.ts. Shiki'sRegistry.loadLanguage()writes built-in language aliases (e.g.js,cjs,mjsfor JavaScript) directly into thelangAliasobject it receives. Because Astro passed the same object reference from the resolved config, those aliases leaked back intoconfig.markdown.shikiConfig.langAlias. SincecomputeConfigHash()runs after the Vite build, the hash then reflected which languages appeared in code blocks rather than the user's actual config, causing the incremental build cache to be invalidated on every content change.
Closes #17693
Testing
- Adds
packages/internal-helpers/test/shiki.test.tswith a test that highlights a JavaScript code block and asserts the originallangAliasobject passed tocreateShikiHighlighteris not mutated.
Docs
- No docs update needed; this is an internal bug fix with no user-facing API change.

Changes
- Require a directory boundary when deciding whether an MDX file is under
src/pages. - Prevent sibling directories such as
src/pages-oldandsrc/pages2from receiving page-only automatic charset injection in both the unified and Sätteri processors.
Testing
- Added focused coverage for real pages and similarly prefixed sibling directories across both processor paths.
- Ran:
pnpm -C packages/integrations/mdx test - Ran:
pnpm -C packages/integrations/mdx build - Ran:
pnpm lint:ai
Docs
No docs changes. This corrects internal page classification without changing the public API.

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.2.3
Patch Changes
-
#17724
97140b2Thanks @ematipico! - Fixes an issue where Astro could run out of memory whenexperimental.collectionStorageis set tochunkedand there are multiple concurrent updates to the same collection. -
#17636
51723b1Thanks @matthewp! - Fixes the dev server sometimes matching against stale routes after pages were added, removed, or renamed, requiring a dev server restart to pick up the change -
#17636
51723b1Thanks @matthewp! - Fixes the composable request helpers (astro/fetch) throwing an error when used on a request that had been rewritten withAstro.rewrite()ornext() -
#17636
51723b1Thanks @matthewp! - Refactors Astro's internal server-side request handling. This is an internal change: all documented public APIs, includingAppandNodeApp, keep their existing signatures and behavior.The undocumented internal
app.pipelineproperty and theAppPipelineexport fromastro/apphave been removed. Adapters that usedapp.pipeline.getLogger()to wait for the configured log destination can call the newapp.getLogger()instead.As a result of this refactor,
new FetchState(request)fromastro/fetchnow works anywhere inside a built Astro server — including customsrc/fetch.tsentrypoints — without the request needing to first pass throughapp.render(). Previously this threw an error, breaking patterns like the Cloudflare adapter's advanced custom-worker setup. -
#17723
c3b9aedThanks @florian-lefebvre! - Fixes a link in font providers JSDoc annotations -
#17699
e28d227Thanks @ArmandPhilippot! - Fixes several documentation issues related to the JSDoc for configuration options.- When hovering over the
serverandfontsoptions, the JSDoc for the nested options was displayed instead of the JSDoc for the top-level property. - Two i18n configuration options were being used incorrectly in the examples.
- The indentation of some code blocks was broken on hover.
- When hovering over the
-
#17572
2066f39Thanks @matthewp! - Fixes a crash when a request arrives with a malformed port in theHostheader (for exampleexample.com:65536orexample.com:8080:8080). Such a host made the constructed request URL invalid, and the fallback that was meant to recover reused the same invalid host and threw again. The request URL now degrades to a host the server controls when the incoming host cannot be parsed, so the request is handled instead of erroring. -
#17685
9f15609Thanks @astrobot-houston! - Fixes a dev server error where an SSR full reload triggered by a third-party Vite plugin (such as@tailwindcss/vite) could fail withFailed to load url astro:server-app.js -
#17636
51723b1Thanks @matthewp! - Improves error handling for custom log destinations. When the configured logger fails to load, Astro now reports the error and continues with the default console logger instead of failing the first request. -
#17631
cf29becThanks @matthewp! - FixesgetCollection()andgetEntry()throwingDataCloneErrorwhen a collection schema transform returns aTemporal.PlainDateor other class instance. -
Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
- @astrojs/markdown-remark@7.2.3
- @astrojs/markdown-satteri@0.3.6
@astrojs/cloudflare@14.2.2
Patch Changes
- Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
- @astrojs/underscore-redirects@1.0.4
@astrojs/markdoc@2.0.7
Patch Changes
- Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
@astrojs/mdx@7.0.6
Patch Changes
- Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
- @astrojs/markdown-remark@7.2.3
@astrojs/netlify@8.2.2
Patch Changes
- Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
- @astrojs/underscore-redirects@1.0.4
@astrojs/node@11.1.3
Patch Changes
-
#17636
51723b1Thanks @matthewp! - Updates the adapter to wait for the configured log destination through Astro's newapp.getLogger()API. This release requires Astro 7.2.1 or later. -
Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
@astrojs/preact@6.0.3
Patch Changes
- Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
@astrojs/react@6.0.3
Patch Changes
- Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
@astrojs/vercel@11.0.6
Patch Changes
-
#17680
ce9f1daThanks @astrobot-houston! - Fixes server islands returning 404 responses in Vercel deployments usingoutput: "static" -
Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
@astrojs/internal-helpers@0.10.3
Patch Changes
- #17696
8c193f6Thanks @astrobot-houston! - Fixes incremental build cache invalidation caused by Shiki mutating thelangAliasconfig object when loading languages
@astrojs/ts-plugin@1.10.11
Patch Changes
- #17668
bef9db5Thanks @lazerg! - Fixes Astro's ambient types leaking into unrelated TypeScript projects. In a monorepo with hoistednode_modules, the plugin found the sharedastroinstall from any project and injectedenv.d.tsandastro-jsx.d.tsinto it, which pulled@types/nodeinto projects that never asked for it. The plugin now only injects those types when the project actually depends onastroor has anastro.config.*file.
@astrojs/markdown-remark@7.2.3
Patch Changes
- Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3
@astrojs/markdown-satteri@0.3.6
Patch Changes
- Updated dependencies [
8c193f6]:- @astrojs/internal-helpers@0.10.3

Changes
- Require a directory boundary when checking whether a file is inside
src/pages. - Prevent sibling directories such as
src/pages-oldandsrc/pages2from receiving page-only handling. - Add regression coverage for pages, nested pages, endpoints, and sibling-directory false positives.
Testing
pnpm --filter astro exec astro-scripts test "test/units/util/*.test.ts" --strip-types(41 passed)pnpm --filter astro buildpnpm lint:ai
Docs
No docs changes. This fixes internal path classification without changing the public API.

Description
We noticed while working on #4121 that our current size limit checks won’t reflect changes to the page sidebars because we test against the start template’s landing page.
This PR adds one of the example guide pages in the starter template to our size limit checks so we also track changes in size to the more standard documentation page layouts as suggested by @trueberryless.
While there I’ve also slightly reduced the size limit for the HTML pages to be a bit closer to the current output sizes and trigger a failure earlier if those increase.

Changes
- Updates the dependency diffing action to 1.7.1. Primarily to fix a bug for PRs from branches that were behind
mainwhere they would report changes that were inaccurate as you can see in #17688 (comment) for example. The issue should be fixed by e18e/action-dependency-diff#177 which is included in the 1.7.1 release. - Versions 1.6 and 1.7 updated internal dependencies, improved trusted publishing checks, and fixed duplicate scanning (a feature we don’t use) so otherwise should be a safe update.
Testing
Tested upstream, 🤞
Docs
n/a

Description
This PR refactors Starlight’s mobile menu toggle to use the Popover API instead of the current JS-powered <button> with aria-expanded pattern.
-
A small amount of JS remains but it’s now only a progressive enhancement for focus trapping. The core functionality of opening/closing the mobile menu and locking body scroll works even if JS breaks for whatever reason.
-
The JS is now in the
PageFramecomponent as a custom element that attaches to the popover body instead of the menu button. It reacts to global events (matchMedia()) and the popover’s"toggle"event so it makes more sense to live alongside the element it attaches to. This also means the menu button component is now easier to override as any button with the correctpopovertargetattribute will work. -
CSS switches from hooking into
[aria-expanded]to using the:popover-openpseudo class. -
We no longer add a
data-mobile-menu-expandedattribute to<body>when the menu is open and usebody:has(#starlight__sidebar:popover-open)in CSS instead, which works without the JS-managed attribute. -
While I was updating the button, I switched from
aria-labelto a visually hidden span of text, which is generally the recommended pattern for labels where possible.
Browser compatibility
Use of the Popover API requires slightly bumping our minimum supported browsers:
- Firefox 121 (December 2023) => 125 (April 2024)
- Chromium 111 (March 2023) => 116 (August 2023)
- Safari 16.4 (March 2023) => 17.0 (September 2023) (for both macOS and iOS)
According to browsersl.ist, comparing before and after shows a drop in global coverage of 1 percentage point. The newest minimum supported browser will be Firefox 125, released 29 months ago.
N.B. that umbrella compatibility measures such as wf-popover show support only arriving later in some browsers. However, IIUC this is due to subfeatures that we are not relying on not being ready yet and the features we require are safe even in these older browser versions:
Safari on iOS had a long-standing bug in versions 17.0–18.2 which prevented tap-away clicks closing popovers as is expected. However, we do not require that behaviour in Starlight mobile menus as there is nowhere a user expects to click away to and the main control is the button.
Accessibility
The button with popovertarget and popover combo has built-in accessible roles equivalent to our previous aria-expanded pattern. I tested with VoiceOver in Firefox, Chrome, and Safari on macOS 26.5.1 and found the behaviour to be a very slight improvement in my opinion compared to the current announcements although both are basically equivalent. Would be great to test in more scenarios!
Demo
Here’s a small screen capture of the live Starlight docs and compared to this branch, showing the menu working without JavaScript after these changes:
popover.mp4

Changes
- No code changes, just tests
Testing
- Uses a local mock rather than going to GitHub, to prevent flakiness caused by GitHub not responding in time.
Docs
N/A, test fix

Changes
With isr enabled, middlewareMode: 'edge' is silently inert.
The adapter builds and deploys _middleware.func, but every on-demand route's
dest is set to the ISR function, so nothing ever routes to it. Middleware
still runs — but only inside the ISR function, which Vercel skips entirely on a
cache hit. The observable result is middleware that works on a cold entry and
then stops running once the entry is warm, with no error and no log.
This is the ordering problem: middleware has to run before the cache is
consulted, not behind it.
- Route on-demand pages at
_middlewarerather than_isrwhen a middleware
entry point exists, so the edge function is actually reached. - Collect those route patterns and inline them into the generated middleware,
sonext()forwards to/_isr?x_astro_path=…for a route the ISR function
backs, and/_renderfor one it doesn't. Cached responses are still served
from cache; only the entry point moves. _imageand_server-islandskeep going straight to_render, unchanged.- Routes matched by
isr.excludestill resolve to_renderthroughnext(). - Prerendered pages are untouched: no route entry, served as static files.
x_astro_path carries the original pathname, so the ISR cache key stays the
request path rather than collapsing to /_isr; x_astro_path_token is the
build token added in #17370. Without isr, or without a middleware file,
nothing about the output changes.
Testing
New: packages/integrations/vercel/test/isr-edge-middleware.test.ts — 16 tests
over two fixtures.
isr-with-edge-middleware asserts against the real build output, since the bug
is entirely a property of the emitted config.json:
- pages, dynamic routes, endpoints and the 404 resolve to
_middleware _imageand_server-islandsstill resolve to_render- prerendered pages get no route entry and ship as static HTML
- configured
redirectsstill resolve ahead of the middleware - the ISR prerender config and its
expirationsurvive
and then imports the generated middleware.mjs with fetch stubbed, to check
where next() actually forwards:
- a cached route →
/_isr, withx_astro_pathand a token - a dynamic route →
/_isr?x_astro_path=/cached/42, the real path, because that
path is the cache key - an
isr.excluderoute →/_render - a query string does not leak into
x_astro_path - the response still reaches the middleware, headers intact
isr-edge-no-middleware covers middlewareMode: 'edge' with no middleware file
present: routes go straight to _isr as before and no middleware function is
built.
Verified as a regression guard by reverting src/ and re-running: 4 of the 16
fail, all of them on dest.
Also ran the full suite — core unit (3307), core integration (1240), all 18
integration packages including @astrojs/vercel (58), language-tools (98), and
e2e in chrome and firefox. No new failures; the pre-existing ones
(test/fonts.test.ts cancellations, a handful of e2e) don't touch the adapter.
Reproduction
Repo with reproduction (2 branches) https://github.com/asmyshlyaev177/astro-isr-middleware-repro
And deployed versions:
https://astro-isr-middleware-repro.vercel.app/
https://astro-isr-mw-patched.vercel.app/

Changes
- Removed the
.fluefolder from the repository. We don't use it anymore. - Adds
evals.jsonfiles and an harness that we can test when want. The harness isn't wired to our testing infra because it spends tokens.
Testing
Tested locally and all assertions pass
Docs

Closes #17684
Changes
- Prefixes
ASTRO_DEV_SERVER_APP_IDwithvirtual:(changing it fromastro:server-apptovirtual:astro:server-app), matching the convention already used by the siblingvirtual:astro:appmodule in the same file. Vite'sModuleGraph._resolveUrl()skips URL normalization for IDs that start withvirtual:— without this prefix, Vite appended.jsto the stored URL, so full-reload attempts viarunner.import("astro:server-app.js")failed to match Astro'sresolveIdfilter. - Fixes the error
Failed to load url astro:server-app.jsthat appeared when a third-party Vite plugin (e.g.@tailwindcss/vite) triggered an SSR full reload on a non-page file.
Testing
- No automated test added — reproducing this requires a live dev server with
@tailwindcss/vitetriggering an SSR full reload, which is outside the current unit/integration test infrastructure. - Fix was manually verified against the minimal reproduction from the issue; editing
src/content/test.mdnow produces[vite] program reloadwithout theastro:server-app.jserror, and the server continues serving successfully. Confirmed by the issue reporter (@mavam).
Docs
No docs update needed — this is an internal virtual module ID fix with no user-facing API change.

Closes #17679
Changes
- The
glob()andfile()content loaders now respectprerenderConflictBehaviorwhen a duplicate entry ID is detected. Previously, both loaders always emitted a hardcodedlogger.warn()regardless of the config setting. "error"throwsDuplicateContentEntrySlugErrorduring content sync;"ignore"suppresses the warning entirely;"warn"(the default) preserves the existing behavior.
Testing
- Added tests to
file-loader.test.tscovering"error"(throws),"warn"(logs), and"ignore"(silent) modes for duplicate IDs in thefile()loader. - Added tests to
glob-loader.test.tscovering the same three modes for duplicate IDs in theglob()loader.
Docs
No docs update needed — prerenderConflictBehavior is already documented; this extends its existing behavior to a new context without changing its API or semantics.

Changes
- Fixes
server:defercomponents returning 404 responses on Vercel when usingoutput: "static". - Preserves a dedicated server build directory and packages it through the existing Vercel serverless and middleware path only when Astro runs the SSR build for discovered server islands. Fully static sites continue to emit no serverless function, and server build files are not published as static assets.
Closes #17678
Testing
- Adds a static server-islands fixture that verifies
_render.func, its route configuration, and rendering through the packaged function. - Verifies the server entry is excluded from static assets and a fully static fixture does not create a serverless function.
Docs
- No docs update needed because this restores the documented
server:deferbehavior for static output.

Description
- Replace "Markdown plugins" links
We have recently updated the Markdown guide in Astro Docs (withastro/docs#14297) and the section was renamed "Markdown processor plugins". There was two occurrences in Starlight docs.
- Replace an example using Astro DB
Astro DB has been removed in Astro 7 (withastro/docs#13985) and the Astro DB guide now redirects to the integration page with an aside saying "Removed". I think it is better to use an example that is still relevant.

Changes
Remote images whose URL has no file extension fail with 400 Unsupported format: null under the cloudflare-binding image service (the adapter default).
Astro's baseService.validateOptions only sets options.format when it can infer a source format from the URL, and since #16665 deliberately leaves it undefined otherwise so the image service resolves the format from the source bytes instead — which is what the Sharp service does at sharp.ts#L155-L156. With format undefined, no f parameter is emitted onto the /_image URL, and transformStream treated a missing f as a client error.
Extensionless remote images are the common case here — GitHub avatars like https://avatars.githubusercontent.com/u/192622539?s=200&v=4 have no extension to infer from, which is why every theme author avatar on astro.build is currently broken:
$ curl -sD - -o /dev/null 'https://astro.build/_image?href=https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F192622539%3Fs%3D200%26v%3D4&w=300&h=300'
HTTP/1.1 400 Bad Request
Unsupported format: null
# ...the same URL with an explicit format works
$ curl -so /dev/null -w '%{http_code} %{content_type}\n' '...&w=300&h=300&f=webp'
200 image/webptransformStreamnow takes the source's media type and uses it whenfis absent: SVG sources pass through unchanged, everything else is encoded as WebP. This mirrors core'sresolveDefaultOutputFormat, which webp-encodes every non-SVG source.- Requests that explicitly ask for a format the IMAGES binding cannot produce (e.g.
f=tiff) still return400. - This also fixes SVG images, which core requests as
f=svgand which previously failed the same way — the IMAGES binding cannot emit SVG, so those bytes are served as-is.
Fixes withastro/astro.build#2610
Testing
Added four cases to test/binding-image-service.test.ts (build + preview, exercising the IMAGES binding for real) covering remote/local sources with no f, and SVG passthrough. A local HTTP server serves images from extensionless paths to reproduce the GitHub avatar shape. Also added the no-f case to test/dev-image-endpoint.test.ts.
All four new binding tests fail on main with 400 and pass with this change; the seven pre-existing tests in that file are unaffected.
Note
Two things worth flagging for maintainers, both pre-existing and left untouched here:
caches.defaultis persisted undertest/fixtures/binding-image-service/.wrangler/state/v3/cache, so a previously-cached200can mask a genuine failure for any stable/_imageURL. I had to clear that directory to see the new tests fail onmain.createPreviewServerreturns the requested port rather than the bound one (preview.ts#L97), so the whole suite 404s if port 4321 is already taken.
Docs
No docs changes needed. This restores the documented behavior of image.remotePatterns/image.domains for remote images; there's no API or configuration surface change.

Changes
- A deploy that changes rendered output without changing content leaves a cached page's validator untouched. Revalidation answers
304, so clients keep HTML that points at/_astro/<hash>.cssfrom the previous build. Every validator the Cloudflare provider can send today comes from the caller and describes the content, never the build. - With the
CF_VERSION_METADATAbinding configured, the provider reads the Worker version id, adds anastro-version:<id>cache tag for version-specific purging, and folds the id into a weakETag(W/"<id>:<lastModified-ms>") on responses that already sendLast-Modifiedand noetagof their own. Without the binding, every header is exactly what it was before. - Picks up #17038, which targeted
feat/cdn-cache-providersand was closed when that branch was deleted. The version tag it built on never reachedmain, so this adds it.
Testing
test/cache-provider.test.tsgains four cases against the preview server: the version tag on a cacheable response, the weakETagcarrying the same id and thelastModifiedtimestamp, an explicitetagsurviving untouched, and a cacheable response without a validator still getting none. Two fixture pages (/lastmod,/explicit-etag) and theversion_metadatabinding support them.- No automated case for the
prerenderEnvironment: 'node'build path, since it would need a second fixture build per run. Checked by hand against a fixture build with a prerendered route and the provider enabled.
Docs
- The adapter README does not cover route caching, so there is nothing to update there. The behavior is worth a paragraph in the Cloudflare adapter guide on docs.astro.build, and I will open that PR once this lands.

Changes
- Moves
satterifromdevDependenciestodependenciesin@astrojs/mdx. The package has unconditional staticfrom 'satteri'imports across four files undersrc/satteri/, making it a runtime requirement. Listing it only as adevDependencymeant pnpm never linked it into@astrojs/mdx's isolatednode_modules, soastro buildfailed withERR_MODULE_NOT_FOUNDin strict pnpm setups (e.g.hoist: false, Vercel monorepo deploys). The import resolved accidentally in non-strict layouts only because pnpm hoistedsatterias a transitive dep ofastro → @astrojs/markdown-satteri.
Testing
- No new tests added. The bug is a packaging/manifest issue that cannot be covered by unit tests run inside the monorepo (which hoists all deps). The fix was confirmed in an isolated
hoist: falsepnpm project by @danielmlr, and by thepackageExtensionscounter-check in their report.
Docs
- No docs update needed; this is an internal dependency declaration fix with no user-visible API change.
Closes #17371

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.2.2
Patch Changes
-
#17611
9bc3207Thanks @thelazylamaGit! - Fixes component styles rendered from content entries remaining stale until a second save when an adapter uses Astro's fallback development environment -
#17634
2267eeeThanks @astrobot-houston! - Fixes incremental builds dropping optimized images for cached pages when using acollectStaticImagesprerenderer (e.g.@astrojs/cloudflarewith compile-time image optimization) -
#17650
4cdf128Thanks @astrobot-houston! - Fixes intermittentImageNotFounderrors during build on projects with many images. The build now limits concurrent image file reads to avoid exhausting OS file descriptors (EMFILE) and retries transient I/O errors with backoff. Non-transient errors are no longer silently swallowed. -
#17683
2378221Thanks @astrobot-houston! - FixesprerenderConflictBehaviornot applying to content collection duplicate ID warnings in theglob()andfile()loaders. Setting it to'error'now throws during content sync, and'ignore'suppresses the warning. -
#17659
90c6ea4Thanks @astrobot-houston! - Fixes the Fonts API breakingexperimental.incrementalBuildcaching by embedding a build-local, randomly-assigned server port in generated code used for the dependency hash -
#17630
fd1d9eeThanks @ericclemmons! - Fixes incremental builds becoming prohibitively slow for sites with many pages or content entries that share a large dependency graph. -
#17690
93beeccThanks @NgoQuocViet2001! - Prevents files in directories whose names start withpagesfrom being treated as page routes -
#17671
09f0dc7Thanks @tarikermis! - Fixesastro devrefusing to start after a Docker container restart when an unrelated process reuses the PID from a persisted lock file. Astro now checks the process command across platforms, so stale lock files are cleaned up and--forcedoes not signal the unrelated process.
@astrojs/node@11.1.2
Patch Changes
- #17400
c1cf110Thanks @tianrking! - Return a 404 instead of a 500 for unknown parameters that match a prerendered dynamic endpoint.

Closes #17656
Changes
- Checks the command for the PID stored in the dev or preview lock file instead of assuming that any live process with the same PID is Astro.
- Uses an exact PID lookup on Linux, macOS, and Windows. This cleans up stale locks after container restarts and prevents
--forcefrom signalling an unrelated process when the command can be verified.
Testing
- Added Unix and Windows command matching cases, including the Windows
.cmdshim and unrelated commands. - Added coverage for matching processes, reused PIDs, unavailable process lookups, and stale lock cleanup through the real process lookup.
Docs
No docs update needed because the CLI workflow and user-facing messages stay the same.

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 adds a configuration file that adds some configuration for the upcoming automated reviews.
Testing
Tested in a different repository
Docs
N/A

Changes
- The TS plugin is registered globally, so it runs for every project in a workspace. Since #17269 it calls
addAstroTypes()unconditionally, andfindAstroPackageDirectory()just walks up the tree looking fornode_modules/astro/. With a hoistednode_modules(pnpmnodeLinker: hoisted, npm, classic Yarn) that lookup succeeds from any sibling project, soenv.d.tsandastro-jsx.d.tswere injected into projects that have nothing to do with Astro. Those files transitively pull in@types/node, and its global shims then win overlib.dom.d.ts, so "Go to Definition" onBlob,fetchorURLin a browser-only app lands in@types/node. - Adds an
isAstroProject()guard in front of the injection: the nearestpackage.jsonhas to listastro, or there has to be anastro.config.*next to it. The language server already does this throughgetAstroInstall(), the plugin was the one place missing it.
Closes #17667
Testing
- Three cases in
packages/language-tools/ts-plugin/test/units/astro-types.test.mtsover a fixture monorepo with a hoistednode_modules: a React project that only reachesastrothrough the shared root is skipped, a project that depends onastrois detected, and so is one with anastro.config.mjsbut no dependency.
Docs
- No docs change, this only narrows when the plugin injects its own ambient types.

Changes
Error stack traces printed to the terminal lose every other frame.
formatErrorStackTrace filters the stack with a module-level global regex:
const STACK_LINE_REGEXP = /^\s+at /g;
...
const stackLines = (err.stack || '').split('\n').filter((line) => STACK_LINE_REGEXP.test(line));RegExp.prototype.test on a /g regex advances lastIndex on every match. The next call starts searching mid-string, where ^ cannot match, so it returns false — and then resets lastIndex to 0, which lets the frame after that match again. The result is that exactly half the frames are dropped:
const STACK_LINE_REGEXP = /^\s+at /g;
const stack = [
'Error: boom',
' at first (/app/src/pages/index.astro:3:1)',
' at second (/app/src/lib/a.ts:10:5)',
' at third (/app/src/lib/b.ts:2:2)',
' at fourth (/app/src/lib/c.ts:4:4)',
];
stack.filter((l) => STACK_LINE_REGEXP.test(l));
// → [ 'at first …', 'at third …' ] // second and fourth are gone
stack.filter((l) => /^\s+at /.test(l));
// → [ 'at first …', 'at second …', 'at third …', 'at fourth …' ]IRRELEVANT_STACK_REGEXP on the next line has the same shape — it is also only ever asked whether one line matches, and it feeds findIndex, which decides where the stack gets truncated.
Neither regex needs the g flag, so this drops it from both. Nothing else changes: both are still single-line predicates.
Testing
Added packages/astro/test/units/errors/format-error-message.test.js:
- every frame of a four-frame stack survives
formatErrorMessage() - formatting the same error twice produces the same string (the leaked
lastIndexis per-regex module state, so repeated calls were order-dependent)
The first test fails on main with missing stack frame: at second (/app/src/lib/a.ts:10:5) and passes with this change.
Docs
Not applicable — no API or behaviour that is documented changes; printed stacks just stop losing lines.

Changes
- When
'unsafe-inline'is present inscript-src,style-src,script-src-elem, orstyle-src-elem, Astro no longer emits auto-generated hashes on that directive. Per the CSP spec, browsers silently ignore'unsafe-inline'when a hash is present in the same directive — so users who need'unsafe-inline'(e.g. to allow third-party scripts that inject dynamic inline styles) were getting a broken policy with no workaround. - Adds a
hasUnsafeInline()helper incsp.tschecked at three points: thescript-srcbaseline, thestyle-srcbaseline, and insiderenderSpecificDirective()for-elemvariants. The previous fix (#14798) had only addressedstyle-src-attr, which happened to never emit hashes anyway. - Updates the
security.cspconfig docs to describe this suppression behavior.
Testing
- 7 new unit tests in
packages/astro/test/units/csp/render-csp.test.tscovering: hash suppression onstyle-src,script-src,style-src-elem,script-src-elem, render-time extra hashes withunsafe-inline, and a guard confirming hashes are not suppressed when'unsafe-inline'is scoped only to-attr(which doesn't affect the baseline directive).
Docs
- Inline JSDoc in
packages/astro/src/types/public/config.tsupdated to document the hash-suppression behavior when'unsafe-inline'is used.
Closes #17663

Changes
- The
virtual:astro:assets/fonts/runtime/font-file-url-resolvervirtual module was embedding the font HTTP server'sAddressInfo(including an OS-assigned ephemeral port) as a JSON literal directly in its compiled source text. Because the port changes on every build, any route that transitively imports<Font />got a differentdependencyHashon every run, silently defeatingexperimental.incrementalBuildfor any project using the Fonts API from a shared layout. - Fixes this by assigning the server address to a named variable (
__ASTRO_FONTS_SERVER_ADDRESS__) in the generated module, then stripping that variable declaration inresolveAssetPlaceholders()(inplugin-incremental.ts) before the module code is hashed. The stable variable reference remains in the hashed code; only the volatile declaration (with the changing port) is removed. This follows the same pattern already used to normalize asset emit handles before hashing.
Closes #17626
Testing
- Added
packages/astro/test/incremental-build-fonts.test.ts: runs two consecutive builds of a fixture that uses the Fonts API withexperimental.incrementalBuild: trueand asserts the route'sdependencyHashis identical across both builds. - Added the corresponding fixture (
packages/astro/test/fixtures/incremental-build-fonts/) with a dynamic route using<Font />and a stablecacheKeyfromgetStaticPaths().
Docs
No docs update needed — this is a bug fix for two experimental features; no user-facing API or behavior contract changed.

Changes
- Fixes a
MaxListenersExceededWarningthat fires after ~11 keep-alive requests whenstaticHeaders: trueandsecurity.cspare both active on the Node adapter.serve-static.tscallscreateRequestFromNodeRequest()solely for route matching viaapp.match(), but that function wires anAbortControllercloselistener on the socket that was never cleaned up. On keep-alive connections the listener count grows by one per request. The fix addsgetAbortControllerCleanup(req)?.()immediately afterapp.match(), using the same cleanup pattern already applied toserve-app.tsin #15735.
Testing
- Added a
'Static headers listener cleanup'test suite topackages/integrations/node/test/static-headers.test.tsthat sends 30 keep-alive requests and asserts noMaxListenersExceededWarningis emitted.
Docs
- No docs update needed; this is an internal resource-management fix with no user-facing API change.
Closes #17657

Overview
Adds graceful shutdown support to the Node.js standalone adapter. When the process receives SIGTERM or SIGINT, the server stops accepting new connections and waits for all in-flight requests to complete before exiting.
What's new
- SIGTERM / SIGINT handling — the server closes gracefully on either signal
- Force-destroy timeout — if connections don't drain within the timeout, remaining connections are force-destroyed. Default is 10s, configurable via
ASTRO_NODE_GRACEFUL_SHUTDOWN_TIMEOUT(milliseconds) - Opt-out — set
ASTRO_NODE_GRACEFUL_SHUTDOWN=disabledto skip signal handler registration entirely
Testing
Adds an integration test suite (graceful-shutdown.test.ts) covering:
- No new connections accepted after
server.close() - SIGTERM and SIGINT each independently stop the server (SIGINT tested on a fresh server)
- Multiple concurrent in-flight requests all complete before
closed()resolves - Force-destroy fires after timeout when a connection is permanently stalled
Docs
Docs have not been updated yet.

Changes
Context #17521
- Replaces
semverwith the smaller ESM-nativeverkitpackage for Astro's Node.js version gate, update checks, integration resolution, and the upgrade CLI. - Removes unused
semverdependencies from@astrojs/ts-pluginwhile preserving existing version-handling behavior.
Testing
- No test changes; the migration preserves the behavior covered by the existing Astro CLI and upgrade tests.
Docs
- No docs update needed because this refactor does not change public APIs or user-facing behavior.

Changes
emitImageMetadatanow uses a concurrency-limited file reader (max 200 simultaneousfs.readFilecalls) to prevent exhausting OS file descriptors on projects with tens of thousands of images. Previously, all image imports were read concurrently with no limit, causingEMFILE: too many open fileserrors — especially afterastro checkorastro devalready consumed file descriptors.- Transient I/O errors (
EMFILE,ENFILE,EAGAIN,EBUSY) are retried with exponential backoff instead of failing immediately. - The bare
catchthat silently swallowed all errors (includingEMFILE) is replaced: onlyENOENTreturnsundefined; other errors are re-thrown with the real OS error message. This makesImageNotFoundaccurate — it now only fires when the file genuinely doesn't exist.
Closes #17649
Testing
- Added
packages/astro/test/units/assets/emit-image-metadata.test.tscovering:undefinedid returnsundefined, a missing file (ENOENT) returnsundefined, and a real JPEG file returns correct width/height/format metadata.
Docs
No docs update needed — this is a build reliability fix with no user-facing API change.

Changes
-
The adapter injects a
SESSIONKV binding with noid, which Wrangler treats as a
request to auto-provision a namespace on deploy. That needs an API token with
Workers KV Storage: Edit, so deploys without it fail with a generic
Authentication error [code: 10000]against/storage/kv/namespaces— after the
build already succeeded, and with nothing in the message pointing back at sessions. -
astro buildnow warns when that binding is about to be provisioned, naming the
permission and both escape hatches:[@astrojs/cloudflare] The "SESSION" KV binding has no `id`, so `wrangler deploy` will provision a new KV namespace. This requires an API token with the "Workers KV Storage: Edit" permission. To use an existing namespace, add `kv_namespaces: [{ binding: "SESSION", id: "<id>" }]` to your Wrangler config. To skip sessions entirely, set `session: false` in your Astro config. -
Silent when the user already declared the binding, when
session: false, and during
dev(which never provisions anything). Deduplicated to one warning per build, since
the customizer runs once per worker (entry, prerender, previews). -
No change to the emitted Wrangler config. The existing
!needsSessionKVBinding || hasSessionBindingternary is extracted into a named
injectsSessionBindingso the warning condition and the emission condition can't
drift apart, but the output is identical. -
The warning lives in
cloudflareConfigCustomizerrather than theastro:config:setup
hook because that's the only place that knows whether an id-less binding is actually
being emitted — warning fromindex.tswould also fire for users who correctly
declaredSESSIONwith anid.
Closes #17640
Testing
Five cases added to the existing test/session-false.test.ts, which already covers the
sibling session: false behavior: warns with both key phrases, dedupes across workers,
silent when user-declared, silent when sessions disabled, and doesn't throw without a
logger.
Being upfront about verification: the suite imports from dist/, so it needs a full
monorepo install and build, which I did not run locally. I verified the logic by
compiling wrangler.ts standalone and running the same assertions plus three
regressions asserting binding emission is unchanged (8/8 passing). The committed test
file itself has therefore not been executed, and tsc -b is unconfirmed — I'd
appreciate CI confirming both.
Docs
No docs change needed: this adds no API or config surface, and the new logger option
is internal to the customizer.
That said, the underlying gotcha — that a default-on adapter feature makes deploy
require Workers KV Storage: Edit — isn't currently called out on the Cloudflare
sessions docs page, and arguably should be.
/cc @withastro/maintainers-docs for feedback!

The remote image fetch in redirectValidation.ts passes no signal or timeout to fetchFn. A slow or unresponsive image origin server will stall the fetch indefinitely - blocking SSR responses in production and the dev server during development. Added AbortSignal.timeout(10_000) to the fetch options so the call aborts after 10 seconds rather than hanging forever.


Changes
- Primary purpose of this pull-request is to refactor internal requests to get rid of the
PipelineandAppclasses as used internally. These were essentially "god objects" that held state related to the server-side app. - The problem with these objects were that there was no way to pass them into
FetchStatewhen access from outside of theAppclass. For example in Cloudflare you can create a custom worker which is the entrypoint to the application. - I realized that the
manifestis the one true god-object in SSR, and we could simply derive all state from that. So this new architecture is much more functional. Derived state is created ascreateManifestMemoandcreateAsyncManifestMemowhich are keyed on themanifest. Anything that needs this state can simply import it now. - Everything else in this PR is just conforming to the above.
Appremains as its the external API for adapters, but mostly just defers to the functional approach now.
Fixes #17591
Testing
- Mostly refactored existing tests which relied in the Pipeline.
Docs
- N/A, just a refactor

Changes
- Fixes
experimental.incrementalBuilddropping optimized images for cached pages when an adapter usescollectStaticImages(e.g.@astrojs/cloudflarewith compile-time image optimization). When a cached page and a re-rendered page share the same source image with different transforms, the merge loop ingeneratePageswas replacing the entire entry with.set(path, entry), discarding transforms thatrestoreStaticImages()had already replayed into the global static image list. The fix merges adapter transforms into existing entries instead of overwriting them, matching the deduplication pattern already used byrestoreStaticImages(). Closes #17633.
Testing
- Added
packages/astro/test/units/build/incremental-images.test.tswith two unit tests: one verifying that restored transforms are preserved when adapter images share the same source path, and one verifying that new source paths from adapter images are still added when no restored entry exists.
Docs
- No docs update needed — this is a bug fix for
experimental.incrementalBuild, and no user-facing API or behavior contract changed.

Changes
- A collection schema transform returning a
Temporal.PlainDateor other class instance no longer throwsDataCloneErrorfromgetCollection()/getEntry(). - Gets rid of
structuredCloneusage, so any types supported bydevalue()should work.
Testing
image-references.test.ts: reworked, removed old testsmutable-data-store.test.ts: asserts image prefixes are stripped to plain srcs and their paths recorded asimageImports, and that entries without images record nothing.
Docs
- N/A, bug fix

Changes
Optimizes experimental cache from O(R × G) to O(G × R) using a Merkle-style hashing algorithm to content roots that heavily share dependencies.
R= # of roots. (e.g. 8,000 content pages)G= graph size.
flowchart LR
subgraph Before["Before: O(R × G)"]
P1["Entry 1"] --> G1["Walk graph"]
P2["Entry 2"] --> G2["Walk graph"]
P3["Entry 3"] --> G3["Walk graph"]
end
subgraph After["After: O(G + R)"]
G["Analyze graph once"] --> H["Hash cache"]
H --> R1["Entry 1: O(1) lookup"]
H --> R2["Entry 2: O(1) lookup"]
H --> R3["Entry 3: O(1) lookup"]
end
Testing
The performance degradation was discovered on my fork of cloudflare-docs where I enabled incrementalBuild. ~10m builds now timed out at 30 minutes.
- TODO: Can this be published on pkg.pr.new to validate?
13:01:47.975
> astro build
13:01:47.976
13:01:58.504
18:01:58 [astro-skills] Setting up Agent Skills Discovery routes
13:01:58.504
18:01:58 [astro-skills] Agent Skills Discovery routes configured
13:02:01.502
18:02:01 [build] Waiting for integration "@cloudflare/nimbus-docs", hook "astro:config:setup"...
13:02:14.225
18:02:14 [WARN] [@cloudflare/nimbus-docs] [nimbus-docs] (nimbus/duplicate-slug, warning) 3 routes are served by an explicit src/pages file that shadows a content entry at the same URL:
13:02:14.225
/ai/models ← src/content/docs/ai/models/index.mdx, src/pages/ai/models/index.astro
13:02:14.225
/ruleset-engine/rules-language/fields/reference ← src/content/docs/ruleset-engine/rules-language/fields/reference/index.mdx, src/pages/ruleset-engine/rules-language/fields/reference/index.astro
13:02:14.225
/workers-ai/models ← src/content/docs/workers-ai/models/index.mdx, src/pages/workers-ai/models/index.astro
13:02:14.225
13:02:14.226
Astro serves the page and drops the content route (deterministic). Intended when a content page wraps a custom page component; verify each shadow is intentional.
13:02:18.146
18:02:18 [content] Syncing content
13:02:18.149
18:02:18 [content] Astro config changed
13:02:18.150
18:02:18 [content] Clearing content store
13:02:28.869
18:02:28 [skills-loader] Loaded 11 skill(s) from "skills"
13:02:41.475
18:02:41 [content] Synced content
13:02:41.488
18:02:41 [types] Generated 27.00s
13:02:41.489
18:02:41 [build] output: "static"
13:02:41.490
18:02:41 [build] mode: "static"
13:02:41.490
18:02:41 [build] directory: /opt/buildhome/repo/dist/
13:02:41.490
18:02:41 [build] Collecting build info...
13:02:41.495
18:02:41 [build] ✓ Completed in 43.00s.
13:02:41.498
18:02:41 [build] Building static entrypoints...
13:04:25.008
18:04:25 [astro-icon] Loaded icons from src/icons, ph, simple-icons, vscode-icons
13:31:14.749
Build took too long and was timed outDocs

Changes
- When both
security.cspandexperimental.clientPrerenderare enabled, Astro now injects a single static<script type="speculationrules">at render time using"source": "document"with a CSS selector (a[data-astro-prefetch]orawhenprefetchAllis enabled). This produces a deterministic payload whose hash can be computed at build time and added to the CSPscript-srcdirective. - The client-side prefetch code detects existing document-source speculation rules in the DOM and skips dynamic per-URL injection, which would generate unwhitelistable hashes.
Closes #17599
Testing
- New unit test file
packages/astro/test/units/csp/speculation-rules.test.tscoveringgenerateSpeculationRulesContent()output shape and hash determinism.
Docs
- No docs update needed; this fixes a bug in the interaction between two existing features without changing any user-facing API or configuration.

Changes
- Bumps the
astropeer dependency in@astrojs/cloudflarefrom^7.0.0to^7.2.0. The adapter importsbeginContentEntryCollection,beginImageCollection,endContentEntryCollection, andendImageCollectionfromastro/app, which were added in 7.2.0. The wider range allowed npm to silently resolveastro@7.1.x, causing a crypticMISSING_EXPORTbuild failure with no install-time warning.
Closes #17622
Testing
- No new tests added — the fix is a manifest correction; existing adapter tests cover the affected code paths.
Docs
- No docs update needed; this corrects a peer dependency declaration, not a user-facing API.

Closes #17624
Changes
- Entries whose frontmatter
slugis an unquoted YAML number (e.g.slug: 20260624) now survive repeated syncs. YAML parses an unquoted number as a JSnumber, but theuntouchedEntriesSet holds string keys from the store. TheSet.deletecall used strict equality, sodelete(20260624)was a no-op against"20260624", leaving the entry marked "untouched" and deleted in the cleanup pass on every sync after the first. generateIdDefault()now callsString(data.slug)instead of relying on theas stringtype assertion, which was compile-time-only and did nothing at runtime.- The
generateIdwrapper also coerces the return value withString()so customgenerateIdcallbacks returning non-string values are handled defensively as well.
Testing
- Added a unit test in
packages/astro/test/units/content-layer/glob-loader.test.tsthat callscontentLayer.sync()twice and asserts the numeric-slug entry (id === '20260624') is present after both syncs and that the entry count is stable. - Added a fixture file
src/content/space/numeric-slug.mdwithslug: 20260624to back the new test case.
Docs
No docs update needed — this is a bug fix for an internal ID coercion issue with no API surface change.

Changes
Update example template to refer to Astro 7.0
Testing
No test changed since this is a simple text update in example template.
Docs
No document changed since this is a simple text update in example template.

Changes
- Dynamic redirect routes in
createRedirectsFromAstroRoutesnow honour the user-configured status code. Previously, the dynamic branch hardcoded301(route.type === 'redirect' ? 301 : 200), so a redirect configured as{ destination: '/new', status: 302 }would be written as301in the_redirectsfile. The static branch already calledgetRedirectStatus(route)correctly; this applies the same call to the dynamic branch. - Since
301is cached essentially permanently by browsers, an incorrect status is very difficult to reverse in production.
Closes #17619
Testing
- Two new test cases in
packages/underscore-redirects/test/astro.test.ts: one verifies that a dynamic redirect with{ destination, status: 302 }emits302, the other verifies that a string-form redirect still defaults to301.
Docs
- No docs update needed — this restores already-documented behavior (configured status is respected).

Changes
- Since #15908 the generated TSX exports a component as
BlogPostAstroComponent, so TypeScript never matches<BlogPost />to it and stops offering "Add all missing imports" or "Add import from …". Completions still worked because they strip the suffix afterwards, while code actions need TypeScript to find the export first. patchTSXnow also re-exports the component under its clean name, which keeps the suffixed function name that avoids conflicts with same-name imports inside the file.rewriteAstroImportTextturns the resultingimport { BlogPost } from './BlogPost.astro'back into a default import. Other named imports from.astrofiles, such asProps, are left alone.- Astro components are now offered once in the auto-import completion list instead of twice.
Fixes #17617
Testing
- New
packages/language-tools/language-server/test/typescript/code-actions.test.tsasks the server for quick fixes on an unimported component and checks the import edit. It fails onmain. - New
patchTSXandrewriteAstroImportTextunit tests, plus a count assertion on the existing component auto-import completion test. - The
@astrojs/language-serverand@astrojs/checksuites pass.
Docs
None, this is a bug fix with no API change.

Changes
- closes #17615
- With
experimental.incrementalBuild, a route that imports more than one asset was sometimes re-rendered even though nothing about it changed. An imported image's module code carries an__ASTRO_ASSET_IMAGE__<handle>__placeholder, other assets carry Vite's__VITE_ASSET__<handle>__, and the handle comes fromemitFilein the order the modules finish transforming, so two builds of the same sources can swap the handles around. The route hash then changes while the output stays byte-for-byte identical. - Before hashing a module, the placeholders are replaced with the file name each handle resolves to. Those names are content-hashed, so the hash stays stable across builds and still changes when an asset's contents change. Replacing the placeholder with a fixed token would also stop the churn, but then an image edited without a dimension change would leave the hash untouched and the restored HTML would point at a file name the build no longer emits.
Testing
New unit tests in test/units/build/plugin-incremental.test.ts drive the plugin with swapped emit handles, for images and for other assets, and assert the hash is unchanged, plus the reverse case where a different resolved file name does change it. Also confirmed against the reproduction in the issue, where 50 builds now produce the same hash, and against a variant of it importing a video and a PDF rather than images.
Docs
No user-facing behavior change, so nothing to document.

Fixes: #17621
When navigating away from a route with ClientRouter and then returning to it, later CSS edits can trigger an HMR update without changing the visible page. A manual reload temporarily restores HMR until another navigation causes the problem again.
Vite keeps references to the <style data-vite-dev-id> elements it creates and updates those same elements during CSS HMR. ClientRouter head swaps can remove a Vite-managed style element and replace it with a new element containing the same CSS. Vite still holds the original element reference, so subsequent updates are applied to a detached element instead of the stylesheet currently in the document.
This can affect any route-specific style managed by Vite, including Svelte and other framework component styles, imported CSS, and Astro component styles. Svelte components make the problem particularly visible because their styles can be inserted asynchronously after hydration. Vue already has special handling for browser-transformed scoped styles, which this change preserves.
Changes
- Tracks Vite-managed style elements by their
data-vite-dev-idand reuses the existing element during ClientRouter head swaps. This preserves the element reference used by Vite’s CSS HMR runtime. - Observes styles inserted asynchronously by client framework runtimes so they can also be retained across later navigations.
- Refreshes the contents of server-generated styles when the same Vite ID represents different CSS on the next route, while preserving Vue scoped styles that have been transformed in the browser.
Testing
- Adds coverage for navigating away from and back to a route before updating a nested Svelte component style and an imported stylesheet.
- Verifies that both updates use native CSS HMR without causing a full page reload.
- Adds coverage using a real Vite-created style node to verify that ClientRouter preserves its identity while applying changed CSS from the incoming document.
- Existing Vue scoped-style tests continue to cover preservation of browser-transformed CSS.
Docs
No docs changes. This fixes development-only ClientRouter and Vite HMR coordination without changing public APIs or production output.

Fixes: #17672
When editing a style block in an Astro component rendered from an MDX content entry, the first save triggers a reload but the page can still render the previous CSS. Saving the file again triggers another reload and finally displays the change.
This happens when Astro cannot run the adapter’s SSR environment directly and uses its internal astro environment to collect styles from content entries. In my case this was because I was using the Cloudflare workerd environment.
Astro’s dev CSS plugin does not currently run in the fallback environment, so the first update reloads the page before that environment has collected the latest transformed CSS.
This change enables the existing dev CSS plugin in the fallback astro environment. The updated CSS is then collected during the first file change, allowing the first reload to render the new styles instead of requiring a second save.
Changes
- Applies the dev CSS collection plugin to Astro’s fallback runnable environment, used when an adapter’s SSR environment cannot be run directly, such as with Cloudflare workerd.
- Ensures content-rendered styles are refreshed before an SSR reload, instead of requiring a second save for the latest CSS to appear.
Testing
- Adds coverage verifying that the dev CSS plugin applies to the fallback
astroenvironment.
Docs
No docs changes. This fixes internal development-server CSS collection behavior.

Changes
Add a test case missed in #17605
Testing
This change is only the addition of a test case
Docs
No docs needed for the addition of a test case

PR #17383 fixed stale server-rendered CSS during HMR, but its client-module detection included a broad same-file fallback intended to support speculative query variants such as ?used and ?direct.
Those variants are not part of the verified Astro/Vite update path, and another style module associated with the same source file is not necessarily capable of applying the update. It could represent a different component style index or a non-injected CSS import.
This follow-up removes hasClientStyleModuleByFile() and retains only the module matching behavior supported by observed requests and Astro’s CSS collection code.
Changes
- Removes the same-file fallback previously used to decide whether a client style module could handle an update.
- Uses exact client module IDs when selecting native Vite CSS HMR. The only normalized difference is the bare
inlineparameter that Astro adds when loading component CSS text server-side. - Keeps
.css?rawand.css?inlineimports on the SSR invalidation path because they export strings rather than injecting client styles.
Testing
- Updates coverage to verify that a different style index from the same component does not count as a matching client module.
- Adds coverage for Astro’s server-side
inlinecomponent requests mapping to their exact client equivalent. - Covers CSS string imports, reordered component-style queries, and incomplete or non-style requests.
Docs
No docs changes. This tightens internal HMR module matching without changing public APIs.

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.2.1
Patch Changes
-
#17612
7133730Thanks @thelazylamaGit! - Fixes CSS hot module replacement after navigating between pages withClientRouter -
#17628
4ada248Thanks @astrobot-houston! - Fixes a CSP violation when using bothsecurity.cspandexperimental.clientPrerenderwithdata-astro-prefetchlinks. The dynamically injected<script type="speculationrules">now uses a static"source": "document"approach with a CSS selector, producing a deterministic payload that is hashed and included in the CSPscript-srcdirective at build time. -
#17605
89e4647Thanks @ashleigh-yeoman! - Fixes middleware HMR not responding to changes in imported modules. Previously, only direct edits to the middleware file would trigger a reload. -
#17582
bd2c1a5Thanks @astrobot-houston! - Fixes a regression where content collectionreference()fields silently accepted entry IDs that don't exist, such as an ID that doesn't match a loader's slugified version of it. Astro now logs an error for references that point to a missing entry after all loaders finish syncing. -
#17661
97b0cc7Thanks @ArmandPhilippot! - Improves Markdown options documentation with links to the Markdown guide and official processors. -
#17349
4328c73Thanks @astrobot-houston! - Fixes an issue where requests handled by the dev prerender environment (e.g./_imagewith@astrojs/cloudflare'sprerenderEnvironment: 'node') returned a 500 when a prerendered catch-all route existed, because non-prerendered route modules were imported in an environment where their runtime-specific APIs are unavailable -
#17603
722eed6Thanks @astrobot-houston! - Fixes<video>and<audio>elements being non-functional after navigating via view transitions (<ClientRouter />) -
#17616
3a890d2Thanks @lazerg! - Fixesexperimental.incrementalBuildre-rendering unchanged routes that import more than one asset. The route's dependency hash depended on the order the assets finished building, so two builds of identical sources could produce different hashes. The hash is now based on the file name each asset resolves to. -
#17547
fba468cThanks @dmgawel! - ImprovesgetCollection()andgetEntry()performance for entries without local image references -
#17602
16e0d9dThanks @astrobot-houston! - Fixes a build error caused by hash collisions in generated content collection image import identifiers
@astrojs/cloudflare@14.2.1
Patch Changes
-
#17627
ba6a9f6Thanks @astrobot-houston! - Fixes theastropeer dependency range from^7.0.0to^7.2.0. The adapter imports symbols (beginContentEntryCollection,beginImageCollection,endContentEntryCollection,endImageCollection) fromastro/appthat were added in Astro 7.2.0, so earlier versions fail at build time with aMISSING_EXPORTerror. -
Updated dependencies [
0891ac9]:- @astrojs/underscore-redirects@1.0.4
@astrojs/netlify@8.2.1
Patch Changes
- Updated dependencies [
0891ac9]:- @astrojs/underscore-redirects@1.0.4
@astrojs/node@11.1.1
Patch Changes
- #17658
8b211a5Thanks @astrobot-houston! - Fixes an EventEmitter memory leak when serving static pages over keep-alive connections withstaticHeadersenabled and CSP (security.csp) active
@astrojs/language-server@2.16.14
Patch Changes
- #17618
2630631Thanks @lazerg! - Fixes the missing "Add all missing imports" and "Add import from" quick fixes for Astro components
@astrojs/underscore-redirects@1.0.4
Patch Changes
- #17620
0891ac9Thanks @astrobot-houston! - Fixes dynamic redirect routes to honour user-configured status codes instead of hardcoding 301. Previously, a redirect configured with{ destination: '/new', status: 302 }would be emitted as 301 in the_redirectsfile when the route was dynamic.

Changes
Fixes the three @astrojs/cloudflare session-false tests failing on main and blocking #17558.
The adapter's astro:config:setup gained an unguarded read of config.experimental.collectionStorage in #17543 (src/index.ts:289). session-false.test.ts calls that hook with a mock config that has no experimental key, so it threw TypeError: Cannot read properties of undefined (reading 'collectionStorage'). This PR adds the missing key to the mock:
experimental: { collectionStorage: 'single-file' },A resolved AstroConfig always has it, since the schema defaults it to 'single-file', so the mock was incomplete rather than the adapter.
Testing
- Fixed the failing Cloudflare tests.
- Node and Netlify are unaffected since their mocks don't read
experimental.
Docs
None needed, test-only change. No changeset for the same reason.

Changes
Fixes the previous attempt to get HMR working for middleware. Now any imports (and transitive imports) in middleware.ts can trigger HMR for middleware.ts, instead of just itself.
This is meant to be a cleaner and better-tested version of #17597
Closes #17590
Testing
3 tests added to packages/astro/test/middleware.test.ts, covering the 3 main HMR cases
middleware.tsis modified- A module imported by
middleware.tsis modified - A transitive dependency (a module imported by a module imported by
middleware.ts) is modified
Docs
No docs update needed. This PR fixes a bug in HMR behavior, with no API changes.

Changes
<video>and<audio>elements are now fully functional after navigating to a page via<ClientRouter />. Previously, media controls were permanently disabled and playback was impossible after any client-side navigation.- The root cause is
DOMParser.parseFromString(), which parses incoming page HTML into an inert document where the browser never initializes the media stack. Moving those elements into the live DOM does not retroactively initialize it. The fix adds areifyMediaElements()post-swap step (following the existingattachShadowRoots()pattern) that replaces each<video>/<audio>element with a fresh copy created viadocument.createElement(), forcing the browser to properly initialize the media stack.
Testing
- No automated test added — the fix is client-side browser code that exercises the browser's media stack initialization, which cannot be meaningfully tested in Node.js.
Docs
- No docs update needed; this is a bug fix restoring behavior that was always expected to work.
Closes #17601

Changes
- Content collection image imports in
.astro/content-assets.mjsnow use sequential identifiers (__ASTRO_IMAGE_IMPORT_0,__ASTRO_IMAGE_IMPORT_1, …) instead of hash-based names. The previousshorthash()-derived names used a 32-bit hash that could collide for different image paths, causing aPARSE_ERRORat build time. Sequential indices are collision-free by construction. - Removes the now-unused
importIdToSymbolNameexport andshorthashimport fromresolveImports.ts.
Closes #17595
Testing
- Adds
packages/astro/test/units/content-layer/asset-imports.test.ts: verifies that two image paths whose filenames produce identicalshorthash()values (imgAa.jpg/imgBB.jpg) are assigned distinct import identifiers.
Docs
- No docs update needed — this is an internal codegen detail with no user-facing API change.

Adds https://lilypond.ky.fyi to the Starlight Showcase following the contribution guidelines.

Changes
app.use(cf())from@astrojs/cloudflare/hononow type-checks correctly in projects that usewrangler types. Previously,HonoCloudflareContextLikedeclaredexecutionCtx: ExecutionContext, binding it to the globalExecutionContexttype. Whenwrangler typesgeneratesworker-configuration.d.ts, that global gains required members (tracing,exports) that Hono's ownExecutionContextdoesn't have, making the handler contravariant-incompatible withMiddlewareHandler.- Replaces
executionCtx: ExecutionContextwith an inline structural type listing only the three members actually consumed downstream (waitUntil,passThroughOnException,props). This matches the approachastro/honoalready uses for its duck-typed context, and the narrowed type remains assignable to internal callers (cfFetch,createLocals) without any casts.
Testing
- No new automated test — the mismatch only surfaces when the user's
wrangler types-generatedExecutionContextglobal is in scope, which the package's own test compilation doesn't exercise (it compiles against@cloudflare/workers-types, whoseExecutionContexthas fewer/optional members). The fix was verified against the reporter's repro at https://github.com/iseraph-dev/repro-astro-cf-hono-types, whereastro checkgoes from 1 error to 0.
Docs
- No docs change needed — this is a type-only fix with no behavior or API surface change.
Closes #17593

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.7
Patch Changes

Description
- Fixes our Sätteri plugin for supporting code in RTL documents in newer versions of Astro
- Fixes the failing test in #4113
- Sätteri now has a
ctx.parent()we can use to get parents so we can remove the more convoluted code for working this out. Strictly speaking these do not work 1:1 — before, we’d skip applying attributes to a<code>element even if it was deeply nested in a<pre>whereas now we only skip if the<code>is a direct child of the<pre>. However, in practice, I think this covers all the scenarios we were intending? - Existing tests here should pass, showing that the changes work for people on older versions of Astro. And I ran the changes against #4113 too to ensure it fixes the failing test there.

Description
- Updates monorepo versions of
astroto latest, fixes #3991 in our own docs - Also updates other
@astrojs/*packages - Updates
sharpto latest in examples and docs

Description
While reviewing another PR, I noticed we use pnpm dlx to run pkg-pr-new for preview releases.
This is not recommended because pnpm dlx would always resolves to the latest version which could have unexpected behavior due to some upstream breaking changes but also increases supply-chain risks.
Caution
In CI environments, avoid npx, pnpm dlx, yarn dlx, and bunx for this step. Install pkg-pr-new as a dependency and execute it from the lockfile (npm exec, pnpm exec, yarn, or bun run).
This PR fixes that by installing pkg-pr-new as a dev dependency and running it using pnpm exec.

Changes
I hit this while following up on #17481. The same header mutation that crashed the Cloudflare adapter on cached image responses also lives in core: applyCacheHeaders() calls response.headers.set() directly on the response a route returns, and CacheHandler deletes CDN-Cache-Control and Cache-Tag from the response a provider returns. Both throw TypeError: immutable when the response headers can't be modified, which is the case for any response returned straight from fetch(). A route that proxies an upstream (return fetch(...) plus cache.set({ maxAge })) gets a 500 on every request. A user already reported this error class when combining cache.set() with a Vary header on Cloudflare (comment on #17408). I haven't verified that path, it needs the Cloudflare dev runtime, but the repro here is adapter free and fails on plain Node.
The fix mirrors what #17481 did in the adapter: try the mutation, and when it throws, rebuild the response with new Response(response.body, response) and apply the headers to the copy. The first .set() throws before changing anything, so applying them again can't duplicate headers. Header stripping now also checks has() first, so provider responses without CDN headers aren't rebuilt for nothing.
One heads up: pnpm install with the pinned pnpm also dropped a stale triage/gh-17583 importer from the lockfile that #17584 left behind. I can split that out if you'd rather keep it.
Testing
Two new fixtures. Without the fix all four new tests fail with a 500 instead of a 200: a memoryCache() route returning a fetch() proxied response, and a provider whose onRequest serves responses with immutable headers, where the CDN header stripping used to crash even when those headers weren't present. With the fix they pass and the second request is served as a cache HIT. The cache unit suite (162 tests) and the neighbouring integration tests still pass. I also reproduced the crash end to end against published astro 7.1.6 with @astrojs/node before writing the fix.
Docs
No docs changes. The changeset describes the fix.

Changes
Skip the failing type check tests in ecosystem-ci. This will make ecosystem-ci pass.
I think it is better to make the tests pass so we can easily catch regressions. If we want to catch regressions for types, we can enable these tests later when it passes.
I didn't add a changeset as this is completely an internal change.
Testing
I ran ecosystem-ci against my fork by running node ecosystem-ci.ts astro in the local modified ecosystem-ci repo.
Docs
No docs change as this is an internal change.

Changes
Astro's config schema imported @astrojs/markdown-satteri at the top level, so Sätteri and its optional platform-specific native binaries landed in the module graph on every astro dev/build, even for projects with no Markdown files. When npm skips the unavailable optional binary for the host platform (for example Windows without @bruits/satteri-wasm32-wasi), the bundler then can't resolve it and the build crashes.
This puts the default processor behind a dynamic import, so @astrojs/markdown-satteri is only resolved when Markdown actually gets rendered. A project with no Markdown never pulls it into the graph.
Testing
I added a unit test checking that the default markdown.processor still resolves to the Sätteri processor. The existing markdown.processor integration test already covers rendering with the default, and it plus the full unit suite pass. The Windows build crash from the report needs the missing optional binary, so it can't be reproduced on Linux CI.
Docs
No user-facing behavior change, so no docs needed.
Fixes #17585

Changes
- Solid component libraries that ship pre-compiled browser artifacts via the
exports.solidcondition (e.g.@kobalte/core) were left external during prerendering. Node resolved them via thedefaultcondition instead, which picks up browser-only code that callstemplate()and other APIs stubbed withnotSup()insolid-js/web/dist/server.js— crashingastro buildwith "Client-only API called on the server side". - Uses
crawlFrameworkPkgsfromvitefuto discover all packages that declaresolid-jsas a peer dependency, then adds them toresolve.noExternalfor non-client environments (e.g.prerender). This forces Vite to bundle those packages so it can apply thesolidexport condition correctly, matching the established pattern from@astrojs/svelte(PR #16210). - Adds
vitefuto@astrojs/solid-js'sdependencies(previously it was only available as a transitive dep via other packages).
Testing
No automated test added — configEnvironmentPlugin is internal and the solid integration has no existing test infrastructure for this; the svelte integration's equivalent fix also lacks a unit test. Fix was confirmed working by the reporter against both a minimal reproduction and a production project (12 pages).
Docs
No docs update needed — this restores expected build behavior with no API changes.
Closes #17583

Changes
reference()fields in content collections no longer silently accept entry IDs that don't exist in the store. After all loaders finish syncing,ContentLayernow walks every entry's data to find reference objects ({ id, collection }) and logs an error for any that point to a missing entry. This catches cases likeauthor: "John-Doe"where theglob()loader slugified the actual entry ID tojohn-doe.- This is a regression from the Zod 4 upgrade (PR #14956), which removed the inline Zod validation that previously caught invalid references. Inline validation can't be restored because loaders run in parallel and the referenced collection may not be populated yet, so validation runs post-sync instead.
Closes #17322
Testing
- Two new unit tests in
packages/astro/test/units/content-layer/data-transforms.test.ts: one verifying that an invalid reference (John-Doewhere onlyjohn-doeexists) produces an error log, and one verifying that a valid reference produces no error.
Docs
- No docs update needed; this restores previously documented behavior that was accidentally removed.

Changes
package-manager-detector recently supported the devEngines field, but for our custom strategies option passing, we redefine the list and didn't include the devEngines-field option, so this PR includes it.
Its default value had devEngines-field.
Testing
Didn't test as should be a simple change
Docs
Added a changeset.

This PR contains the following updates:
Release Notes
mochajs/mocha (mocha)
v11.8.0
v11.7.6
🩹 Fixes
- make
describe().timeout()work (aafe6fd) - test: replace
wmicusage with native Windows API (#5694) (73ebdfa)
🧹 Chores
prettier/prettier (prettier)
v3.9.6
v3.9.5
Markdown: Cap ordered list mark at 999,999,999 (#19351 by @tats-u)
CommonMark parsers only support ordered list item numbers up to 999,999,999.
With this change, Prettier now caps the ordered list item number at 999,999,999 to ensure that the output is correctly parsed as an ordered list by CommonMark parsers. Numbers larger than 999,999,999 are not parsed as list item numbers and are left unchanged in the output:
<!-- Input -->
999999998. text
999999998. text
999999998. text
999999998. text
1234567890123456789012) text
<!-- Prettier 3.9.4 -->
999999998. text
999999999. text
1000000000. text
1000000001. text
1234567890123456789012) text
<!-- Prettier 3.9.5 -->
999999998. text
999999999. text
999999999. text
999999999. text
1234567890123456789012) textMarkdown: Avoid corrupting empty link with title (#19487 by @andersk)
Do not remove <> from an inline link or image with an empty URL and a title, as this removal would change its interpretation.
<!-- Input -->
[link](<> "title")
<!-- Prettier 3.9.4 -->
[link]( "title")
<!-- Prettier 3.9.5 -->
[link](<> "title")Less: Remove extra spaces after [ in map lookups (#19503 by @kovsu)
// Input
.foo {
color: #theme[ primary];
color: #theme[@name];
color: #theme[@@name];
}
// Prettier 3.9.4
.foo {
color: #theme[ primary];
color: #theme[ @name];
color: #theme[ @@name];
}
// Prettier 3.9.5
.foo {
color: #theme[primary];
color: #theme[@name];
color: #theme[@@name];
}CSS: Prevent addition space in type() with + (#19516 by @bigandy)
This fixes the addition space before + in CSS type() declaration. For example type(<number>+) was being converted into type(<number> +) which is invalid CSS and does not work.
/* Input */
div {
border-radius: attr(br type(<length>+));
}
/* Prettier 3.9.4 */
div {
border-radius: attr(br type(<length> +));
}
/* Prettier 3.9.5 */
div {
border-radius: attr(br type(<length>+));
}Less: Remove spaces between merge markers and colons (#19517 by @kovsu)
// Input
a {
box-shadow + : 0 0 1px #​000;
}
// Prettier 3.9.4
a {
box-shadow+ : 0 0 1px #​000;
}
// Prettier 3.9.5
a {
box-shadow+: 0 0 1px #​000;
}Markdown: Preserve wiki links with aliases (#19527 by @kovsu)
<!-- Input -->
[[Foo:Bar]]
<!-- Prettier 3.9.4 -->
[[Foo]]
<!-- Prettier 3.9.5 -->
[[Foo:Bar]]TypeScript: Fix comments being dropped on shorthand type import/export specifiers (#19565 by @kirkwaiblinger)
// Input
export { type /* comment */ T } from "foo";
import { type /* comment */ T } from "foo";
// Prettier 3.9.4
Error: Comment "comment" was not printed. Please report this error!
// Prettier 3.9.5
export { type /* comment */ T } from "foo";
import { type /* comment */ T } from "foo";Miscellaneous: Preserving comments' placement property (#19567 by @Janther)
Prettier@3.9.0 deleted an undocumented property on comments, which was already used by plugins, comment.placement is now available again after comment attach.
Flow: Stop enforcing empty module declaration to break (#19568 by @fisker)
// Input
declare module "foo" {}
// Prettier 3.9.4
declare module "foo" {
}
// Prettier 3.9.5
declare module "foo" {}
Angular: Support expression for exhaustive typechecking (#19571 by @fisker)
<!-- Input -->
@switch (state.mode) {
@default never(state);
}
<!-- Prettier 3.9.4 -->
@switch (state.mode) {
@default never;
}
<!-- Prettier 3.9.5 -->
@switch (state.mode) {
@default never(state);
}TypeScript: Ignore comments inside mapped type when checking type parameter comments (#19572 by @fisker)
// Input
foo<{
// comment
[key in keyof Foo]: number
}>();
// Prettier 3.9.4
foo<
{
// comment
[key in keyof Foo]: number;
}
>();
// Prettier 3.9.5
foo<{
// comment
[key in keyof Foo]: number;
}>();Less: Fix adjacent block comments being corrupted (#19574 by @kovsu)
// Input
/* a *//* b */
/* a */* {
color: red;
}
// Prettier 3.9.4
/* a */
/* b */
/* a * {
color: red;
}
// Prettier 3.9.5
/* a */ /* b */
/* a */
* {
color: red;
}JavaScript: Handle dangling comments in SwitchStatement (#19581 by @fisker)
// Input
switch (foo) {
// comment
}
// Prettier 3.9.4
switch (
foo
// comment
) {
}
// Prettier 3.9.5
switch (foo) {
// comment
}TypeScript: Remove space in comment-only object type (#19583 by @fisker)
// Input
var foo = {
/* comment */
};
type Foo = {
/* comment */
};
// Prettier 3.9.4
var foo = {/* comment */};
type Foo = { /* comment */ };
// Prettier 3.9.5
var foo = {/* comment */};
type Foo = {/* comment */};v3.9.4
v3.9.3
v3.9.2
v3.9.1
sveltejs/svelte (svelte)
v5.56.10
Patch Changes
-
fix: preserve CSS escape sequences when printing selectors (#18667)
-
fix: parse
:nth-child(2n of.foo)whereofis not followed by whitespace (#18611) -
fix: transform expressions inside labeled statements during server compilation (#18617)
-
docs: clarify that context lookup includes the current component and all ancestors (#18581)
-
fix: apply CSS custom properties with falsy values on components (#18634)
-
fix: correctly print
{#await ... catch x}et al (#18645) -
fix: ignore comments of Program node during migration script (#18656)
-
fix: reliably resolve append_style to its correct root (#18614)
-
fix: clean up removed capture event handlers from spread attributes (#18618)
-
fix: don't corrupt renderer type during SSR's legacy
bind:retry loop (#18616) -
fix: treat concise arrow function bodies as implicit returns when calculating blockers (#18613)
-
fix: give effect teardowns the value from before the first write in a flush (#18620)
-
fix: avoid double-calling a derived reference when destructuring
$derivedof another$derivedduring server-side rendering (#18668) -
fix: preserve namespaces in CSS type selectors (#18678)
-
fix: increment private state fields through a non-
thisreceiver (#18622) -
chore: deduplicate client and server context helpers (#18580)
-
fix: release
last_propagated_eventafter event propagation settles so it no longer retains the last event's target subtree (#18569) -
fix: allow custom elements to receive async values as props (#18661)
-
fix: strip comments from inline
stylevalues in linear time (#18553) -
fix: prevent declaration comments from breaking server derived references (#18641)
-
perf: make async blocker analysis scale linearly with the number of top-level references (#18549)
-
fix: preserve short-circuiting for logical assignments to private state fields (#18594)
v5.56.9
Patch Changes
-
fix: skip controlled each fast path while another batch is pending (#18625)
-
fix: better whitespace handling inside printer (#18638)
-
fix: don't duplicate comments in attributes (#18636)
-
fix: preserve CSS comments in the AST printer (#18637)
v5.56.8
Patch Changes
-
fix: call
onerrorand provide a workingresetwhen hydrating a failed boundary (#18556) -
fix: preserve select selection when spread attributes omit value (#18561)
v5.56.7
Patch Changes
- chore: provide
indentoption forprint(#18474)
v5.56.6
Patch Changes
-
perf: skip unnecessary blocker analysis when compiling components without top-level await (#18548)
-
fix: rerun derived that had an abort controller on reconnection (#18551)
v5.56.5
Patch Changes
-
chore: drop dead code that make TSGO fail (#18496)
-
fix: don't (re)connect deriveds when read inside branch/root effects (#18527)
-
fix: skip unnecessary derived effect in earlier batch (#18525)
-
fix: avoid declaration tag warning in event handlers (#18500)
-
fix: abort deriveds own AbortSignal when it disconnects (#18400)
-
fix: ensure
$state.eager()is correctly transormed for SSR output (#18530) -
fix: correctly transform declaration tags during SSR (#18492)
-
fix: transform computed keys in keyed
{#each}destructuring patterns (#18521) -
fix: chain preprocessor sourcemaps with an empty
sources[0]instead of dropping them (#18518) -
fix: clear previous_task reference after abort in Tween to prevent memory leak on interrupted tweens (#18541)
-
fix: don't treat declaration tags as parts inside each blocks (#18507)
v5.56.4
Patch Changes
-
fix: include wrapping parentheses in
{@const}declaratorendposition (#18436) -
fix: always unset reactivity context after restoring it (#18453)
-
fix: don't notify
searchParamssubscribers when the URL changes without affecting the search string (#18425) -
fix: strip
?from optional parameters in<script lang="ts">so generated JavaScript is valid (#18448)
v5.56.3
Patch Changes
-
fix: ignore errors that occur in destroyed effects (#18384)
-
fix: type BigInts in
$state.snapshot(...)return values (#18388)
v5.56.2
Patch Changes
-
fix: properly track effect end node for async sibling component (#18371)
-
fix: prevent false-positive reactivity loss warning (#18373)
-
chore: bump esrap dependency (#18372)
-
fix: ignore declaration tags for animation directive (#18366)
-
fix: reject pending async deriveds on discard (#18308)
v5.56.1
Patch Changes
-
fix: error at compile time on duplicate snippet/declaration tag definitions (#18351)
-
fix: parse declaration tag contents more robustly (#18353)
-
fix: correctly transform references to earlier declarators in a declaration tag (e.g.
{let a = $state(0), b = $derived(a * 2)}) (#18348) -
fix: avoid spurious
state_referenced_locallywarnings for$deriveddeclarations in declaration tags (#18348) -
fix: tolerate whitespace before
let/constin declaration tags (#18348) -
fix: prevent infinite loop when a tag's expression ends with a trailing
/at the end of the input (#18350) -
fix: more robust parsing of declaration tags with regards to
type(#18330) -
fix: preserve newlines in spread input values when the
typeattribute is applied aftervalue(#18345) -
fix: update
SvelteURLSearchParamswhen setting duplicate keys to the same joined value (#18336) -
fix: check references for blockers on server, too (#18352)
v5.56.0
Minor Changes
- feat: allow declarations in the template (#18282)
Patch Changes
-
perf: use
createElementinstead ofcreateElementNSfor HTML elements (#18262) -
perf: store
current_sourcesas aSetfor O(1) membership checks (#18278) -
perf: deduplicate identical hoisted templates within a component (#18320)
-
perf: hoist
rest_propsexclude list as a module-scopeSet(#18252)
v5.55.10
Patch Changes
-
fix: unlink errored and otherwise finished batch (#18264)
-
perf: walk composedPath() directly in delegated event propagation (#18268)
-
fix: transfer effects when merging batches (#18254)
-
fix: allow
$derived(await ...)in disconnected effect roots (#18273) -
fix: remove temporary raw-text hydration markers (#18269)
-
fix: propagate async
@constblockers through closure references so template expressions like{(() => host)()}correctly wait for the awaited value (#18309) -
fix: properly unlink batches (#18298)
-
fix: settle discarded batch (#18290)
-
fix: declare
let:directives before{@const}declarations on slotted elements (#18271) -
fix: resume outro-ed branches if they were kept around (#18291)
-
fix: avoid waterfall-warning when async resolves to same value (#18297)
-
fix: correctly coordinate component-level effects inside async blocks (#18260)
-
fix: make unnecessary commit work less likely (#18263)
-
chore: add tag name to
a11y_click_events_have_key_eventswarning (#18272) -
fix: catch rejected promises while merging/committing (#18266)
v5.55.9
Patch Changes
-
fix: don't unset batch when calling
{#await ...}promise (#18243) -
fix: promise-ify
{#await await ...}expressions on the server and correctly hydrate them on the client (#18243) -
fix: deduplicate dependencies that are added outside the init/update cycle (#18243)
-
fix: avoid false-positive batch invariant error (#18246)
-
fix: inline primitive constants in attribute values during SSR (#18232)
v5.55.8
Patch Changes
-
fix(print): handle
svelte:bodyand fix keyframe percentage double-printing (#18234) -
fix: execute uninitialized derived even if it's destroyed (#18228)
-
fix: use named symbols everywhere (#18238)
-
fix: don't run teardown effects when deriveds are unfreezed (#18227)
-
fix: unset context synchronously in
run(#18236)
v5.55.7
Patch Changes
-
fix: prevent XSS on
hydratablefrom user contents (a16ebc67bbcf8f708360195687e1b2719463e1a4) -
chore: bump devalue (#18219)
-
fix: disallow empty attribute names during SSR (
547853e2406a2147ad7fb5ffeba95b01bd9642da) -
fix: harden regex (
d2375e2ebcab5c88feb5652f1a9d621b8f06b259) -
fix: move Svelte runtime properties to symbols (
e1cbbd96441e82c9eb8a23a2903c0d06d3cda991)
v5.55.6
Patch Changes
-
fix: leave stale promises to wait for a later resolution, instead of rejecting (#18180)
-
fix: keep dependencies of
$state.eager/pending(#18218) -
fix: reapply context after transforming error during SSR (#18099)
-
fix: don't rebase just-created batches (#18117)
-
chore: allow
nullforpendingin typings (#18201) -
fix: flush eager effects in production (#18107)
-
fix: rethrow error of failed iterable after calling
return()(#18169) -
fix: account for proxified instance when updating
bind:this(#18147) -
fix: ensure scheduled batch is flushed if not obsolete (#18131)
-
fix: resolve stale deriveds with latest value (#18167)
-
chore: remove unnecessary
increment_pendingcalls (#18183) -
fix: correctly compile component member expressions for SSR (#18192)
-
fix: reset
source.updatedstack traces afterflush(#18196) -
fix: replacing async 'blocking' strategy with 'merging' (#18205)
-
fix: allow
@debugtags to reference awaited variables (#18138) -
fix: re-run fallback props if dependencies update (#18146)
-
fix: abort running obsolete async branches (#18118)
-
fix: ignore comments when reading CSS values (#18153)
-
fix: wrap
Promise.allinsaveduring SSR (#18178) -
fix: ignore false-positive errors of
$inspectdependencies (#18106)
v5.55.5
Patch Changes
-
fix: don't mark deriveds while an effect is updating (#18124)
-
fix: do not dispatch introstart event with animation of animate directive (#18122)
v5.55.4
Patch Changes
privatenumber/tsx (tsx)
v4.23.12
Bug Fixes
This release is also available on:
v4.23.11
v4.23.10
Bug Fixes
This release is also available on:
v4.23.9
Bug Fixes
This release is also available on:
v4.23.8
Bug Fixes
This release is also available on:
v4.23.7
Bug Fixes
- prevent tsImport cache collisions (4e5a138)
This release is also available on:
v4.23.6
v4.23.5
v4.23.4
Bug Fixes
This release is also available on:
v4.23.3
Bug Fixes
This release is also available on:
v4.23.2
v4.23.1
Bug Fixes
- support tsImport after global preload (8d4ffc2)
- watch: avoid clearing piped output (95d0672)
- watch: treat script and dependency paths literally (79fddde)
Performance Improvements
- index transform cache lazily (e818ad6)
- load esbuild lazily in CLI (d067938)
- map Node TypeScript formats directly (cdcc623)
- use sync module hooks on Node v22.22.3+ (f8992f1)
This release is also available on:
v4.23.0
Bug Fixes
- avoid redundant filesystem probes during module resolution (257bbbb), closes privatenumber/tsx#809
Features
- add multi-scenario startup benchmark suite (c178197), closes privatenumber/tsx#809 #809 hi#signal privatenumber/tsx#145 #809
This release is also available on:
v4.22.5
Bug Fixes
- isolate hook state per async module.register() registration (a305f36)
This release is also available on:
v4.22.4
Bug Fixes
This release is also available on:
microsoft/vscode-uri (vscode-uri)
v3.2.0
Changes:
- #65: chore: bump minor version to 3.2.0
- #59: Restore the default export
- #63: Bump brace-expansion from 2.1.1 to 2.1.4
- #64: Bump js-yaml from 4.3.0 to 4.3.1
- #62: Bump fast-uri from 3.1.4 to 3.1.5
- #61: Bump js-yaml from 4.2.0 to 4.3.0
- #60: Bump fast-uri from 3.1.2 to 3.1.4
- #58: chore: bump patch version to 3.1.1
- #57: Use Yarn resolutions to resolve remaining mocha audit alerts
- #56: Bump fast-uri from 3.1.0 to 3.1.2
See More
- #55: Bump picomatch from 2.3.1 to 2.3.2
- #54: Bump webpack from 5.94.0 to 5.104.1
- #52: Bump glob from 10.3.10 to 10.5.0
- #51: Bump js-yaml from 4.1.0 to 4.1.1
- #50: chore: bump action and node versions
- #49: Bump serialize-javascript from 6.0.1 to 6.0.2
This list of changes was auto generated.
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- 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.

This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| @fastify/middie | ^9.1.0 → ^9.3.3 |
||
| devalue | ^5.8.1 → ^5.9.0 |
||
| fastify (source) | ^5.7.4 → ^5.12.0 |
||
| node-mocks-http | ^1.17.2 → ^1.18.1 |
Release Notes
fastify/middie (@fastify/middie)
v9.3.3
⚠️ Security Release
Fix for:
What's Changed
- refactor(types): migrate from tsd to tstyche by @Tony133 in #260
- chore: update depedabot setting by @climba03003 in #263
- chore: bump fastify-plugin from 5.1.0 to 6.0.0 in the dependencies group by @dependabot[bot] in #264
- chore(package.json): fix delvedor's personal url by @Fdawgs in #265
- chore: replace http with https in urls by @Fdawgs in #266
Full Changelog: fastify/middie@v9.3.2...v9.3.3
sveltejs/devalue (devalue)
v5.9.0
Minor Changes
07d6a38: feat: exportfilterArrayIndices, the array-index filtering used by theindicesOfstringify operation, so custom operations can reuse it instead of reimplementing it07d6a38: feat: add pluggableoperationsoption toparse/unflatten, allowing customization of how values are constructed while reviving (e.g. cross-realm or foreign-runtime revival)5b53532: feat: add pluggableoperationsoption tostringify/stringifyAsync, allowing customization of how values are introspected during serialization (e.g. side-effect-free or foreign-runtime serialization)
v5.8.2
Patch Changes
48cc81f: fix: serialize DataView subviews with the correct byte offset and lengthcd6da94: fix: resolve circular references through custom revivers when payload is already hydrated29a3382: fix:unevalnow produces valid output for a repeated emptyMaporSet8c0db06: fix: serialize Temporal values referenced more than once inuneval3770846: fix: emit valid JS forBigInt64ArrayandBigUint64Arrayinuneval756265a: fix: preserve shared-reference identity for Map keys inunevalfaa8a05: fix: emit uneval reconstructions before the statements that reference them06129ad: fix: do not grow sparse arrays by one slot in uneval
fastify/fastify (fastify)
v5.12.0
What's Changed
- chore(sponsor): add testmu ai by @Eomm in #6922
- docs: add per-route logging for Google Cloud Functions by @slegarraga in #6907
- test: remove @sinonjs/fake-timers by @climba03003 in #6934
- [Backport 5.x] fix: remove raw response headers by @github-actions[bot] in #6939
- [Backport 5.x] test: use process-warning spyWarning for testing by @github-actions[bot] in #6945
- [Backport 5.x] feat: add
Reply.prototype.mediaTypeby @github-actions[bot] in #6946 - [Backport 5.x] fix(content-type): return
undefinedfor invalid media types by @github-actions[bot] in #6947
Full Changelog: fastify/fastify@v5.11.3...v5.12.0
v5.11.3
What's Changed
- fix: clear trailer state when removing all trailers by @Ram-blip in #6845
- docs: document percent-decoded route params as untrusted input by @mcollina in #6903
- docs(errors): document what the default error handler sends by @basteez in #6894
- docs(readme): fix grammar and cjs capitalization by @minirang in #6899
- fix: pass null instead of undefined to requestCompleted on success by @lazerg in #6837
- docs(encapsulation): clarify nested plugin scopes by @jean-michelet in #6893
- chore: Bump fastify/workflows/.github/workflows/lock-threads.yml from 6.0.0 to 7.0.0 by @dependabot[bot] in #6916
- chore: Bump fastify/workflows/.github/workflows/nested-quality.yml from 6.0.0 to 7.0.0 by @dependabot[bot] in #6917
- fix: recognize constructor-assigned built-in properties as decorator … by @aquie00t in #6892
- fix: reset lastIndex before testing global/sticky content-type RegExp parsers by @zelinewang in #6846
New Contributors
- @minirang made their first contribution in #6899
- @lazerg made their first contribution in #6837
- @zelinewang made their first contribution in #6846
Full Changelog: fastify/fastify@v5.11.2...v5.11.3
v5.11.2
v5.11.1
What's Changed
- fix: add http method override warning by @jean-michelet in #6879
- fix(types): allow explicit http2: false in server options by @Tony133 in #6888
- docs: clarify what attachValidation exposes on request.validationError by @basteez in #6891
- fix: do not force close in-flight connections when forceCloseConnections is 'idle' by @aquie00t in #6889
New Contributors
Full Changelog: fastify/fastify@v5.11.0...v5.11.1
v5.11.0
What's Changed
- chore: Bump markdownlint-cli2 from 0.22.1 to 0.23.0 by @dependabot[bot] in #6839
- fix: normalize method in findRoute by @Ram-blip in #6838
- docs: fix incorrect description for validateInput in Request.md by @thePranav-kpk in #6823
- feat: rfc10008 http query method by @climba03003 in #6832
- perf: reuse cached content types for response serialization by @gurgunday in #6854
- chore(sponsor): add n-ix by @Eomm in #6852
- ci: pin actions to commit-hash by @Fdawgs in #6853
- ci(dependabot): temporarily ignore typescript updates by @Tony133 in #6869
- style(types): enforce max line length by @jean-michelet in #6864
- docs(ecosystem): update fastify-prisma repo URL by @zrosenbauer in #6724
- docs(getting-started): align import with exported routes by @Solaris-star in #6859
- docs(server): fix contradictory requestIdHeader default by @sobol-sudo in #6872
- fix(validation): correctly update falsy values from validator by @jackjin1997 in #6483
- docs: specific warning suppression by @jean-michelet in #6871
- ci(links-check): pin linkinator server root to the workspace by @aquie00t in #6877
- docs: update TypeScript docs to reference Fastify 5.x by @Sasireddy001 in #6880
- docs: use async trailer handlers in Reply examples by @Ram-blip in #6856
- docs: fix logController migration example and links by @aquie00t in #6876
- fix: uncatchable throws in writeHead when using async hook by @climba03003 in #6881
- fix: honor quoted-string in
Content-Typeparameter values by @aquie00t in #6865 - chore(.npmrc): add min-release-age by @Fdawgs in #6873
- chore: Bump actions/setup-node from 6.4.0 to 7.0.0 by @dependabot[bot] in #6866
- chore: Bump actions/labeler from 6.2.0 to 7.0.0 by @dependabot[bot] in #6867
New Contributors
- @Ram-blip made their first contribution in #6838
- @Solaris-star made their first contribution in #6859
- @sobol-sudo made their first contribution in #6872
- @jackjin1997 made their first contribution in #6483
- @Sasireddy001 made their first contribution in #6880
Full Changelog: fastify/fastify@v5.10.0...v5.11.0
v5.10.0
v5.9.0
What's Changed
- feat: add request.mediaType by @climba03003 in #6653
- docs: remove deprecated leveldb plugin and update ecosystem by @Tony133 in #6661
- chore(sponsor): add bestforandroid by @Eomm in #6659
- ci: drop Node.js 20 from yarn matrix in package-manager-ci.yml by @Tony133 in #6662
- fix: prevent duplicate res.end in sendTrailer with sync callbacks by @climba03003 in #6676
- fix: avoid duplicate closeIdleConnections call on native servers by @trivikr in #6669
- fix: error.code not present on some routing errors by @mcollina in #6678
- fix: correct isCustomSerializerCompiler flag check by @eddieran in #6657
- fix: validate invalid route logLevel at registration by @maxpetrusenko in #6523
- docs: update contribution rules by @Tony133 in #6670
- fix: use ContentType parser for response schema lookup by @UlisesGascon in #6685
- ci(ci): use shared quality workflow by @Fdawgs in #6688
- fix(types): allow request.getValidationFunction() to return undefined by @trivikr in #6665
- fix: do not trust forwarded host/proto when socket is missing by @mcollina in #6684
- perf: defer ContentType parsing in getSchemaSerializer until needed by @aquie00t in #6692
- perf: cache parsed ContentType objects in ContentTypeParser by @aquie00t in #6694
- perf: add typeof guard before toString.call in send and onSendEnd by @aquie00t in #6693
- chore: Bump pnpm/action-setup from 5.0.0 to 6.0.4 by @dependabot[bot] in #6704
- chore: Bump actions/github-script from 8 to 9 by @dependabot[bot] in #6705
- chore: Bump JustinBeckwith/linkinator-action from 2.4.0 to 2.4.2 by @dependabot[bot] in #6706
- docs: correct return503OnClosing comment in route.js by @mcollina in #6712
- fix: enable diagnostics tracking for async error handlers by @irzix in #6458
- fix: ignore duplicate trailer completions by @mcollina in #6714
- feat: add support of onMaxParamLength by @climba03003 in #6716
- chore: introduce TSTyche for type testing by @mrazauskas in #6532
- docs(reference): grammar and readability fixes by @Fdawgs in #6710
- chore: update depedabot setting by @climba03003 in #6715
- fix: include hint and docs URL in FSTWRN004 warning message by @aquie00t in #6723
- ci(ci): do not pass secrets to reusable workflow by @Fdawgs in #6744
- docs: add fastify-intlayer to ecosystem documentation by @aymericzip in #6594
- chore: Bump concurrently from 9.2.1 to 10.0.0 by @dependabot[bot] in #6752
- docs(Errors): fix incorrect usage of root fastify inside plugin scope by @Rpaudel379 in #6731
- ci: add node 26 to test matrices by @Fdawgs in #6728
- docs(Warnings): remove retired FSTWRN002 warning code by @leestana01 in #6754
- fix: chunk large HTTP/2 buffer replies by @mcollina in #6746
- chore: migrate type tests to TSTyche assertions (part one) by @mrazauskas in #6726
- chore: migrate type tests to TSTyche assertions (part two) by @mrazauskas in #6727
- docs: fix doubled braces in serializerCompiler signature by @DucMinhNe in #6747
- docs: remove HackerOne reporting link by @jhcpeixoto in #6735
- refactor(decorate): replace
findwithsomeinhasKeyfor correct boolean semantics by @aquie00t in #6759 - fix: replace
AssertionErrorwithFST_ERR_PLUGIN_DEPENDENCY_NOT_REGISTEREDincheckDependenciesby @aquie00t in #6774 - docs(ecosystem): add @inferdi/fastify by @maxrendel in #6742
- docs(typo): Write-Plugin.md by @zakirimadullahprogrammer-tech in #6776
- docs: fix duplicate anchor IDs causing broken TOC links by @AliMahmoudDev in #6770
- docs: add fastify-ata as a JSON Schema validator option by @mertcanaltin in #6733
- chore: rename type test files by @mrazauskas in #6762
- docs: update ajv-errors guidance by @Herrtian in #6741
- chore(warnings): correct duplicate 'not' typos in inline comments by @mixelburg in #6713
- docs: add fastify-param-schema-validation to ecosystem by @Player1205 in #6760
- docs(ecosystem): add @thecodepace/fastify-http-query by @Puppo in #6785
- chore: Bump esbuild from 0.25.12 to 0.28.1 in /test/bundler/esbuild in the npm_and_yarn group across 1 directory by @dependabot[bot] in #6788
- fix: clear socket._meta after response to prevent keep-alive leaks by @nerkoux in #6799
- fix: avoid double slash when joining nested prefixes by @rohithvegesna in #6803
- docs(sponsors): Update sponsors list by removing two entries by @Eomm in #6792
- chore: Bump fastify-plugin from 5.1.0 to 6.0.0 by @dependabot[bot] in #6801
- chore(package.json): fix delvedor's personal url by @Fdawgs in #6808
- chore: replace http with https in urls by @Fdawgs in #6809
- chore: add new sponsor by @Eomm in #6813
- chore: Bump actions/checkout from 6 to 7 by @dependabot[bot] in #6812
- fix: hasRequestDecorator/hasReplyDecorator misses constructor-assigned built-in properties by @LeSingh1 in #6753
- docs: migrate Zod type provider to official @fastify package by @Tony133 in #6686
- docs: add warning about empty string coercion with nullable types by @ritambh888 in #6452
- docs: fix incorrect code examples in Hooks and Server reference by @rrodj in #6622
- docs: update Serverless guide Dockerfile to a supported Node.js version by @harish885 in #6789
- docs(types): mark request metadata accessors as untrusted input by @mcollina in #6572
- Bumped v5.9.0 by @Eomm in #6816
New Contributors
- @eddieran made their first contribution in #6657
- @irzix made their first contribution in #6458
- @aymericzip made their first contribution in #6594
- @Rpaudel379 made their first contribution in #6731
- @leestana01 made their first contribution in #6754
- @DucMinhNe made their first contribution in #6747
- @jhcpeixoto made their first contribution in #6735
- @maxrendel made their first contribution in #6742
- @zakirimadullahprogrammer-tech made their first contribution in #6776
- @AliMahmoudDev made their first contribution in #6770
- @mertcanaltin made their first contribution in #6733
- @Herrtian made their first contribution in #6741
- @mixelburg made their first contribution in #6713
- @Player1205 made their first contribution in #6760
- @Puppo made their first contribution in #6785
- @nerkoux made their first contribution in #6799
- @rohithvegesna made their first contribution in #6803
- @LeSingh1 made their first contribution in #6753
- @ritambh888 made their first contribution in #6452
- @rrodj made their first contribution in #6622
- @harish885 made their first contribution in #6789
Full Changelog: fastify/fastify@v5.8.5...v5.9.0
eugef/node-mocks-http (node-mocks-http)
v1.18.1
- fix(types): stop collapsing custom intersection types in createResponse/createMocks by @MGough in #337
v1.18.0
- Replace parseurl with WHATWG URL API by @Saturate in #315
- feat: type _getJSONData method by @nicoabie in #324
- feat: type _getChunks method by @nlisgo in #326
- Deprecate Node 16, add Node 24 by @eugef in #328
- feat(types): Add
QUERYtoRequestMethodunion by @RobinTail in #335
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- 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
- closes #17574
imageService: 'custom'(and the fallback case) left Astro's default dev image endpoint in place, which importsviteandnode:fsand cannot load inside workerd, so every/_imagerequest returned 500 in dev- use the generic fetch-based endpoint in dev, matching the other image service modes; a user-configured
image.endpointis left untouched - warn in dev when
imageService: 'custom'resolves to the Sharp service (including when noimage.serviceis configured), since Sharp's native binding cannot run inside workerd in dev or production (see #17574 (comment))
Testing
- new
custom-image-service.test.ts - new
setImageConfigunit tests inimage-config.test.ts - existing
Docs
- none, bug fix

Closes #17508
Changes
- Adds
neotraverseto theALWAYS_NOEXTERNALlist invite-plugin-environment/index.ts, forcing it to be bundled into the prerender output instead of emitted as a bare external import. - Before this fix, if any other package in the dependency tree required
neotraverse@^0.6.x, npm would hoist that older copy to the project root. The prerender bundle (written todist/.prerender/, outsidenode_modules/astro) would then resolve the bareimport { forEach } from "neotraverse"to the hoisted 0.6.x copy — which doesn't exportforEach— crashing the build. Bundlingneotraverseensures Astro's own copy is always used regardless of what's hoisted in the project.
Testing
- No new tests added; the fix is a one-line config addition. Existing Content Layer integration tests cover the affected code paths and confirm no regression.
Docs
- No docs update needed; this is an internal dependency resolution fix with no user-facing API changes.

Changes
- Fixes a crash in the Node adapter when a request arrives with a malformed port in the
Hostheader (e.g.example.com:65536,example.com:8080:8080). The bad host made the request URL invalid, and the existingcatchfallback rebuilt the URL from the same host and threw again. buildRequestUrl(shared bycreateRequestFromNodeRequestandcreateRequest) now degrades in steps — full URL, origin only, then a server-controlled host (localhost, with the listening port when known) — so URL construction never throws.parseHostnow rejects a host with more than onehostname:portpair, which previously passed by inspecting only the first two colon-separated segments.
Testing
- Unit tests in
node.test.tscovering malformed hosts throughcreateRequestFromNodeRequest(no throw, parseable URL) plus a valid max-port control, and acreateRequestcase asserting a duplicated-port host is rejected. - An end-to-end
@astrojs/nodetest that drives a standalone server, sends a craftedHostover a raw socket, and asserts a follow-up request still succeeds.
Docs
- No docs update needed; this is an internal reliability fix with no API change.

Closes #17329
Changes
- Cookies set via
Astro.cookies.set()inside a custom404.astroor500.astroare now correctly included in the final response. Previously they were silently dropped. - Two bugs in
mergeResponses(packages/astro/src/core/errors/default-handler.ts): (1) when both the original and error page hadAstroCookies, the error page's cookies were appended tooriginalResponse.headers— an object already copied intonewHeadersand no longer connected to the merged response — so they went nowhere; fixed by replacing the loop withoriginalCookies.merge(newCookies). (2) theseen-set guard that deduplicates merged headers treatedset-cookieas a single-value header, blocking error-page cookies when the original response already set one; fixed by always appendingset-cookieentries.
Testing
- Added
packages/astro/test/error-page-cookies.test.tswith three cases: error page cookies survive when the original page throws, error page cookies survive when no original cookies exist, and both middleware and 404 error page cookies are preserved together.
Docs
- No docs update needed. This restores the behavior that
Astro.cookies.set()is documented to provide; no API surface changed.

Changes
@astrojs/vercelcreates.vercel/output/server/with a plainmkdirSync, soastro buildcrashes withEEXISTwhen that directory already exists, for example when two builds run against the same project root. This creates it with{ recursive: true }, the same way thestatic/directory one line above is already created.- It also awaits
emptyDir(staticDir), which was fire-and-forget before and could race the copy calls that follow it.
Fixes #17568
Testing
The existing @astrojs/vercel test suite passes. Reproducing the race needs two overlapping builds sharing one project root, which the current integration harness has no way to set up, so I did not add a new test.
Docs
No docs changes. This is an internal bug fix with no change to the public API.

Changes
- Fixes
fontProviders.googleicons()downloading the entire ~3.9MB Google Icons font instead of only the requested glyphs whenexperimental.glyphscontains more than one name. The root cause is a bug in unifont (unjs/unifont#336) where glyph names are joined with.join("")(no separator), producing an invalidicon_namesquery parameter that causes Google's API to silently return the full font. The workaround pre-joins multiple glyphs into a single comma-separated string before passing them to unifont, so unifont's.join("")produces the correct value.
Testing
- Adds
packages/astro/test/units/assets/fonts/googleicons-glyphs.test.tscovering theresolveFontcall with multiple glyphs, a single glyph, undefined options, and an empty glyphs array — verifying the patching logic doesn't throw for any of these shapes.
Docs
- No docs update needed;
experimental.glyphsbehavior is unchanged from the user's perspective — this restores the documented subsetting behavior.
Closes #17565
Last fetched: | Scheduled refresh: Every Saturday
See Customizing GitHub Activity Pages to configure your own
Inspired by prs.atinux.com

