AstroEco is Contributing…
Display your GitHub pull requests using astro-loader-github-prs
Closes #17060
Problem
In hybrid mode (output: 'static' + adapter), prerendered pages that are redirect targets were incorrectly being included in the SSR bundle, causing massive bundle size inflation. The reporter saw their deployment grow from ~1.4MB to 68MB due to this issue.
Root Cause
The getRoutesForEnvironment function in packages/astro/src/vite-plugin-pages/pages.ts was unconditionally adding redirect target routes to the result set without checking their prerender flag. This meant that even when a redirect target was meant to be prerendered (and thus should only exist in the static environment), it would still get bundled into the SSR function.
Solution
Added a prerender check to the redirect target filtering logic:
if (route.redirectRoute && route.redirectRoute.prerender === isPrerender)This ensures redirect targets are only included in their appropriate environment - prerendered redirects stay in static, and SSR redirects stay in SSR.
Verification
- All 65 existing redirect tests continue to pass
- Added comprehensive unit tests covering the environment filtering logic
- Issue reporter (@spaceemotion) confirmed the fix works - their deployment is now 990KB (down from 68MB) and builds 30 seconds faster
Files Changed
packages/astro/src/vite-plugin-pages/pages.ts— Added prerender check to redirect target filteringpackages/astro/test/units/redirects/environment-filtering.test.ts— New unit tests covering the filtering logic
Changes
Bump volar-service-* deps 0.0.70 → 0.0.71 to resolve yaml CVE-2026-33532
volar-service-yaml@0.0.70 pulls yaml-language-server@~1.20.0 → yaml@2.7.1,
which is vulnerable to CVE-2026-33532 (GHSA-48c2-rrv3-qjmp, stack-overflow DoS,
fixed in yaml 2.8.3). volar-service-yaml@0.0.71 moves to
yaml-language-server@~1.23.0 → yaml@2.8.3.
Bumping the full volar-service-* set in lockstep to keep them aligned against
@volar/* ~2.4.28. This propagates the fix down to @astrojs/check via its
@astrojs/language-server ^2.16.7 range, no change needed there.
Testing
No tests added; patch version bump of volar dependencies.
Docs
This should have zero user-facing effects beyond removing CVE-2026-33532 from
their security scanners.
Description
While implementing support for markdown.processedDirs in a Starlight plugin, I realized on our implementation was a bit fragile. I would guess nobody ever ran into this issue in practice as this behavior existed since we initially introduced heading links.
For example, a configured path like ./src/content/custom/ could process ./src/content/custom-test/index.md.
Summary
Closes #17021.
toImportName() in @astrojs/markdoc builds the JS identifier used for generated component imports from a Markdoc tag name by replacing hyphens with underscores:
function toImportName(unsafeName: string) {
return unsafeName.replace('-', '_');
}String.prototype.replace with a string pattern only replaces the first occurrence, so a tag named lead-capture-inline becomes lead_capture-inline — not a valid JS identifier — and the generated import statement is rejected by Rollup with a confusing parse error that points at the .mdoc file's frontmatter instead of the real cause:
[ERROR] [vite] ✗ Build failed
src/content/articles/example.mdoc (8:22): Expected ',', got '-'
(Note that you need plugins to import files that are not JavaScript)
Single-hyphen tags worked because the one replacement covers them; the incomplete fix dates back to PR #7599 (2023), whose only fixture was marquee-element (one hyphen), so multi-hyphen tags were never exercised.
Fix
function toImportName(unsafeName: string) {
- return unsafeName.replace('-', '_');
+ return unsafeName.replaceAll('-', '_');
}Tests
The existing render-with-components fixture gains:
- a
my-cool-tag(two hyphens) registration inmarkdoc.config.ts - a
MultiHyphen.astrocomponent - usage in
with-components.mdoc - a
renders content - with multi-hyphen tag namesassertion inrender-components.test.ts
The test fails at fixture.build() time on the pre-fix code (the build itself errors with the Rollup parse error before render even starts), so it's a clean regression guard.
Test plan
-
pnpm testinpackages/integrations/markdoc— 48/48 pass on this branch (47 existing + 1 new) - Verified the new test fails on
mainby stashing only thetoImportNamechange and re-running — 1 failed | 45 passed, confirming the test catches the actual regression and not just vacuous coverage - Changeset added (
@astrojs/markdocpatch)
Notes for review
- An
astrobot-houstontriage report on the issue thread proposed the same one-character fix; this PR ships it with the supporting fixture wiring + regression test. - I intentionally did NOT widen
toImportName()to do broader JS-identifier sanitization beyond hyphens (theTODO: more checks that name is a safe JS variable namecomment hints at it). That's a separate, larger scope worth its own discussion.
Follow-up to #17049, fixing the E2E failures discussed in #17049 (comment). Root-cause analysis in #17049 (comment) — this PR targets the flue/fix-17047 branch so it can be merged into #17049 directly.
Changes
src/prerenderer.ts: drop the status-based failure check. It treated any non-2xx prerender response as a build failure, but pages may intentionally return non-2xx while prerendering — the E2E Cloudflare fixture'smissing.astroreturnsnew Response(null, { status: 404 })in production builds, which is what broke the E2E jobs. Thex-astro-prerender-errorheader is now the only failure signal.src/utils/prerender.ts: addinstallPrerenderErrorPropagation(). The header path was previously dead code:app.render()never rejects on render errors becauseAstroHandlercatches the throw and the productionDefaultErrorHandlerrenders a 500 error page, so the bufferingcatchinhandlePrerenderRequestnever fired (the new test was passing only via the status check). This override replicates core'sBuildErrorHandlersemantics on the worker app: render errors (status 500 with anerrorand noresponse) are rethrown, while intentional error responses and 404s flow through the default handler unchanged.src/utils/handler.ts: install the override whenisPrerenderis true.
Error flow for a genuine render crash: throw → AstroHandler catch → patched renderError rethrows → app.render rejects → handlePrerenderRequest catch → 500 + x-astro-prerender-error header → Node-side render() throws → build fails.
Testing
test/prerenderer-errors.test.tspasses (both tests) — a page throwing during prerendering still fails the build.packages/astro/e2e/fixtures/cloudflarebuilds successfully again with the intentional 404 inmissing.astro— the exact case that failed the E2E jobs.- Full adapter test suite results match a baseline run of the unmodified #17049 code.
🤖 Generated with Claude Code
Changes
- Enables HMR for CSS files imported with
?rawsyntax. Previously, modifying these files required a manual browser refresh to see changes. - Updates the
astro:hmr-reloadplugin to check if style modules exist in the client module graph before skipping them. SSR-only style modules (like?rawimports) now trigger a full page reload.
Testing
- Added unit test
'sends full-reload for ?raw CSS imports (SSR-only style modules)'inpackages/astro/test/units/vite-plugin-hmr-reload/hmr-reload.test.ts - Issue reporter confirmed the fix resolves their issue with the preview release
Docs
- No docs update needed, this fixes existing documented behavior for
?rawCSS imports
Closes #17032
What's the problem?
Closes #17001.
With trailingSlash: "always" set, dynamic file endpoints like src/pages/api/[name].json.ts require a trailing slash in dev:
| Path | Expected | Dev | Build |
|---|---|---|---|
/api/bar.json |
200 | 404 | 200 |
/api/bar.json/ |
404 | 200 | 404 |
Static file endpoints (/feed.xml) worked correctly. Only dynamic ones were broken.
Root cause
trailingSlashForPath in create-manifest.ts was:
type === 'endpoint' && pathname && hasFileExtension(pathname) ? 'never' : config.trailingSlashFor any route with a dynamic segment, pathname is null (it's only set for fully-static paths). The null short-circuits the && chain before hasFileExtension ever runs, so the function falls back to config.trailingSlash regardless of whether the route has a file extension.
The build path works differently — it resolves concrete paths from getStaticPaths params — which is why the mismatch only surfaces in dev.
Fix
Pass the segment-joined route string as a fallback when pathname is null. joinSegments always preserves file extensions even for dynamic segments:
// /api/[name].json.ts -> /api/[name].json
joinSegments(segments) // '/api/[name].json'const trailingSlashForPath = (
pathname: string | null,
config: AstroConfig,
type: 'page' | 'endpoint',
route?: string,
): AstroConfig['trailingSlash'] =>
type === 'endpoint' && hasFileExtension(pathname ?? route ?? '') ? 'never' : config.trailingSlash;At all three call sites (createFileBasedRoutes, createRoutesFromEntriesByDir, createInjectedRoutes), route is now computed before the trailing slash call and passed through.
Testing
Added 'dynamic file endpoints force trailingSlash never regardless of config. issues#17001' to packages/astro/test/units/routing/manifest.test.ts. It covers:
- dynamic file endpoint matches without trailing slash
- dynamic file endpoint does not match with trailing slash
- existing static file endpoint behavior is unchanged
Changes
- Fixes prerendered routes with non-ASCII characters (Thai, Korean, Cyrillic, etc.) incorrectly returning 301 redirects to trailing slash URLs despite
trailingSlash: "never"setting - Adds guarded
decodeURI()call before filesystem lookup inserve-static.tsso percent-encoded URLs match actual directory names on disk - Resolves SEO issues where canonical redirect loops cause search engines to drop non-ASCII pages
Testing
- Added Thai test fixture
สวัสดี.astrowith prerendering enabled - Added test verifying non-ASCII prerendered route serves 200 without trailing slash
- Added test verifying non-ASCII prerendered route with trailing slash redirects to no-slash URL
Docs
- No docs update needed — this fixes existing documented
trailingSlash: "never"behavior
Closes #16989
Changes
- Fixes image compilation regression for Cloudflare adapter users with static output. The image optimizer was looking in wrong directory (
dist/_astro/instead ofdist/client/_astro/), causing ENOENT build failures. - Replaces hardcoded
outDirusage withgetClientOutputDirectory(settings)helper inprepareAssetsGenerationEnv()to respectpreserveBuildClientDirsetting.
Testing
- Confirmed by issue reporter (@kitschpatrol) that the fix resolves the build failure
- Existing core image test suite (207 tests) and preserve-build-client-dir tests (7 tests) continue to pass
Docs
- No docs update needed, this fixes broken functionality to work as originally documented
Closes #16919
Closes #17047
Changes
- Pages that throw during prerendering now correctly fail the build with a non-zero exit code instead of silently emitting truncated HTML
- The Cloudflare adapter now buffers the full response body inside workerd before returning it over HTTP, so streaming errors are caught and surfaced as 500 responses
- Prerender error messages are included in a custom
x-astro-prerender-errorheader and checked by the Node-side build process
When a component throws mid-render (e.g. an unregistered <Font> CSS variable), the previous implementation would commit HTTP status 200 before the stream completed. The stream would then abort, but the Node-side build process received the truncated body as a successful 200 response and wrote it to disk, causing astro build to exit 0 with broken output.
Testing
- Added
packages/integrations/cloudflare/test/prerenderer-errors.test.tswith test case'fails the build when a page throws during prerendering' - Added
prerenderer-render-errortest fixture with a page that throws during rendering to verify the fix
Docs
- No docs update needed — this fixes existing documented behavior (builds should fail when pages throw during prerendering)
Changes
Follow up of withastro/compiler-rs#45
Updates astro to use the latest version of the compiler
Testing
Green CI
Docs
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.
next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.
Releases
@astrojs/alpinejs@1.0.0-beta.1
Patch Changes
@astrojs/cloudflare@14.0.0-beta.2
Patch Changes
@astrojs/markdoc@2.0.0-beta.1
Patch Changes
@astrojs/mdx@7.0.0-beta.2
Patch Changes
@astrojs/netlify@8.0.0-beta.1
Patch Changes
@astrojs/node@11.0.0-beta.1
Patch Changes
@astrojs/preact@6.0.0-beta.1
Patch Changes
@astrojs/react@6.0.0-beta.1
Patch Changes
@astrojs/solid-js@7.0.0-beta.1
Patch Changes
@astrojs/svelte@9.0.0-beta.3
Patch Changes
@astrojs/vercel@11.0.0-beta.1
Patch Changes
@astrojs/vue@7.0.0-beta.1
Patch Changes
@astrojs/markdown-satteri@0.3.1-beta.1
Patch Changes
Changes
- Fixes CSS incorrectly loading on pages that don't import it when using
client:onlyislands in production builds - Adds missing
isCSSRequestfilter to the client:only CSS association loop inplugin-css.ts, matching the existing filter in the non-client:only code path - Prevents CSS from unrelated modules being associated with pages when Rollup bundles pure utility modules into the same chunk as CSS-importing modules
- Reporter confirmed dramatic CSS reductions: frontpage went from 14 stylesheets/718.2 KB to 1 stylesheet/481.2 KB
Testing
- Adds reproduction test
client-only-css-chunk-leak.test.tsthat verifies CSS is only loaded on pages that actually import it
Docs
- No docs update needed — this fixes incorrect behavior to match documented CSS scoping for islands
Closes #17043
Changes
Under experimental.advancedRouting, the composable astro/hono pages() handler delegated straight to PagesHandler.handle(), with none of the app-level error handling that AstroHandler provides on the standard path. A page throwing during render therefore propagated to the host framework, which answered with its own default error response (Hono: plain-text Internal Server Error) instead of rendering the custom 500.astro page (#16952).
- Added
PagesHandler.handleWithErrorFallback(app, state)(core/pages/handler.ts): wraps route dispatch in a try/catch that logs the error and renders it viaapp.renderError(..., { status: 500, error }), the same behaviourAstroHandler.render()has on the standard path. Forwardingerrorkeeps the documentederrorprop on500.astroworking. The logic lives in the handler module becausecore/fetch/index.tsdeliberately keeps its exports as thin, logic-free wrappers. - When no route matched,
handleWithErrorFallback()returns a 404 response marked withX-Astro-Error(the same patternhandle()uses for the un-dispatched redirect case), so the app's existing post-check renders the 404 error page a level up. This case is reachable because #16911 intentionally leavesrouteDataunset when the custom 404 route is prerendered (or absent), wherePagesHandler.handle()would otherwise throw aTypeErrorand the catch would turn those unmatched requests into 500s. The 500 path keeps callingrenderError()directly: the marker cannot carry the error object, so500.astrowould lose itserrorprop and the stack would not be logged. - Switched
pages()incore/fetch/index.tsto delegate to the new method (delegation only, no logic added). - Added a changeset (
astropatch).
Testing
- Added
'renders the custom 500 page when a page throws during render'- verifies the fullpages()pipeline returns HTTP 500 with the custom 500 page content when a page throws during render - Added
'returns a marked 404 for the app post-check when the custom 404 route is prerendered'- verifies unmatched requests return HTTP 404 carrying theX-Astro-Errormarker instead of throwing - Verified end-to-end against the reproduction from #16952 (Hono +
app.use(pages()), node standalone):GET /boom->500with the custom 500 page (previously Hono's plain-textInternal Server Error),GET /does-not-exist->404serving the prerendered 404 page with the marker stripped from the final response,GET /->200; the thrown error is logged with its stack trace
Out of scope (noticed while testing)
Errors thrown by Astro middleware itself (src/middleware.ts) on the composable path (app.use(middleware())) still reach the host framework's default error handler instead of 500.astro. On the standard path, AstroHandler.render()'s try/catch wraps the whole middleware chain, so middleware errors get the same 500.astro treatment. The equivalent catch can't simply live inside the composable middleware(): its next is the host's next(), so it would also intercept errors thrown by host middleware running below it in the chain and hide them from the user's own app.onError. The all-in-one astro() handler already provides full parity. A possible follow-up would be an error-page helper exported from astro/hono (e.g. app.onError(onError())) so composable users can opt in - happy to open a separate issue for this.
Docs
- No docs update needed, this fixes broken functionality in an experimental feature so it matches the documented custom-error-page behavior (the 404 counterpart was #16911)
Closes #16952
Closes #17039
Changes
- Middleware file watcher now triggers HMR for files inside the
src/middleware/directory, not justsrc/middleware.{ts,js}files directly - Updates the Vite plugin path matcher to include both
middleware.(existing) andmiddleware/(new) prefixes, matching the fix pattern used for actions in #16932
Testing
- Added
packages/astro/test/units/middleware/middleware-hmr.test.tswith 8 test cases covering path matching for both file and directory patterns
Docs
- No docs update needed — this fixes existing documented behavior that was broken for directory-based middleware organization
Stacked on top of #16335 (targets feat/cdn-cache-providers, not main), since it builds directly on the Cloudflare cache provider added there.
Problem
The Cloudflare cache provider emits only a content-derived conditional validator. setHeaders() passes Last-Modified (and an ETag only if the user supplied one) straight through setConditionalHeaders. But cache hints carry just lastModified, and routeRules carry no validator at all.
So a code-only deploy — one that changes rendered output without changing content — leaves the validator unchanged. The most common case is hashed asset URLs (/_astro/<hash>.css, <hash>.js) embedded in server-rendered HTML: a new build rewrites the HTML body, but lastModified is identical. Conditional revalidation then resolves to 304 Not Modified, and clients keep serving HTML that references assets from the previous deployment (broken styles/scripts). Purging the edge cache on deploy doesn't help, because it can't invalidate validators already held by browsers — only a changed validator can.
This affects any route-caching site that ships build-hashed assets, independent of how lastModified is produced.
Fix
The provider already reads the Worker version id from CF_VERSION_METADATA to add an astro-version:<id> purge tag. This reuses that id to fold the version into a weak ETag (alongside Last-Modified when present):
ETag: W/"<versionId>:<lastModified-ms>" // or W/"<versionId>" when no lastModified
- The validator now changes on a redeploy as well as on a content edit (the
lastModifiedcomponent), so revalidation returns a fresh200after a deploy.If-None-Matchtakes precedence overIf-Modified-Sinceper RFC 9110. - Weak (
W/) because responses are only guaranteed semantically equivalent for a given version, not byte-for-byte. - Applied as a default: an explicitly provided
etagis respected verbatim. - Only runs when
CF_VERSION_METADATAis configured; otherwise behavior is unchanged (content-only validator).
Tests
Extends cache-provider.test.ts (and the cache-provider fixture):
/api(tags, nolastModified) → weakETagembedding the same id as theastro-version:tag.- new
/lastmodroute →ETagcombines version + contentlastModified;Last-Modifiedstill emitted. - new
/explicit-etagroute → an explicitetagis not overridden.
No separate changeset: this enhances the unreleased provider from #16335, so I documented the behavior under that PR's existing @astrojs/cloudflare changeset (clear-planets-divide.md).
Note: I wasn't able to run the full integration suite (monorepo build +
wranglerpreview) in my environment — relying on CI here. The change is isolated tosetHeadersand mirrors the existing provider patterns.
What this fixes
joinPaths() in @astrojs/internal-helpers filters out non-string arguments (so callers can pass undefined for optional segments), but the branch that detects the last segment compared the map index against the original, unfiltered argument count:
export function joinPaths(...paths: (string | undefined)[]) {
return paths
.filter(isString)
.map((path, i) => {
if (i === 0) {
return removeTrailingForwardSlash(path);
} else if (i === paths.length - 1) { // ← unfiltered length
return removeLeadingForwardSlash(path);
} else {
return trimSlashes(path);
}
})
.join('/');
}When a skipped (undefined) argument precedes the final one, the filtered array is shorter than paths.length, so the real last segment no longer matches i === paths.length - 1. It falls into the else branch and gets trimSlashes(), which strips its trailing slash — even though joinPaths is supposed to preserve the trailing slash of the last segment (that is exactly what removeLeadingForwardSlash does, as opposed to trimSlashes).
Reproduction
joinPaths('/base', undefined, 'docs/setup/'); // → '/base/docs/setup' (expected '/base/docs/setup/')
joinPaths('/foo', 'bar/', undefined); // → '/foo/bar' (expected '/foo/bar/')
joinPaths(undefined, 'pl', 'docs/setup/'); // → 'pl/docs/setup' (expected 'pl/docs/setup/')The invariant being violated: a skipped argument should yield the same result as omitting it. joinPaths(a, undefined, b) must equal joinPaths(a, b).
The fix
Compute the filtered array once and base the last-segment check on its length:
export function joinPaths(...paths: (string | undefined)[]) {
const segments = paths.filter(isString);
return segments
.map((path, i) => {
if (i === 0) {
return removeTrailingForwardSlash(path);
} else if (i === segments.length - 1) {
return removeLeadingForwardSlash(path);
} else {
return trimSlashes(path);
}
})
.join('/');
}Calls where every argument is a string (the vast majority of call sites, e.g. joinPaths(base, route)) are unchanged, since segments.length === paths.length in that case.
This is the underlying utility bug behind the prependWith-with-trailing-slash case noted in the root-cause analysis of #17034 (where getLocaleRelativeUrl calls joinPaths(base, prependWith, locale, path) with prependWith possibly undefined).
Tests
Added a joinPaths test block to packages/internal-helpers/test/path.test.ts covering simple joins, inner-slash trimming, and the skipped-argument regression. The new assertions fail on main and pass with this change.
pnpm --filter @astrojs/internal-helpers test→ 64 passingastroi18n unit tests (test/units/i18n/astro_i18n.test.ts) → 37 passing, unchanged
Related: #17034
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.7
Patch Changes
-
#17035
197e50eThanks @astrobot-houston! - FixesgetRelativeLocaleUrl,getAbsoluteLocaleUrl, andgetAbsoluteLocaleUrlListto strip trailing slashes whentrailingSlash: 'never'is configured -
#16967
3719765Thanks @astrobot-houston! - Fixes double URL-encoded paths returning 400 Bad Request on on-demand routesPreviously, any URL containing a double-encoded character (like
%255B, which is[encoded twice) was unconditionally rejected with a400 Bad Requestbefore middleware or route handlers could run. This broke embedded tools like Sanity Studio whose client-side router legitimately produces double-encoded URLs.The fix replaces the rejection approach with iterative decoding — multi-level percent-encoding is now fully resolved to its canonical form before being passed to middleware and route matching. This preserves the security fix for CVE-2025-66202 (middleware authorization bypass via double encoding) because middleware now always sees the fully decoded path, making bypass impossible. For example,
/api/%2561dminis decoded to/api/admin, which middleware can correctly block. -
#16882
621beb7Thanks @jettwayio! - fix(render): honour compressHTML when joining head elements -
#16892
8d753b0Thanks @astrobot-houston! - Fixes custom elements in MDX having their children'sslotattribute stripped by the JSX runtimeWhen custom elements (tags with hyphens like
<my-element>) are used in MDX files, theslotHTML attribute on their children is now correctly preserved. Previously, the shared JSX runtime would treatslotas an Astro slot assignment and remove it from the output, breaking Shadow DOM named slot distribution for web components. -
#16957
544ee76Thanks @thelazylamaGit! - Fixes stale inline CSS in server-rendered HTML after CSS file edits during devWhen editing a CSS file (
.css,.scss, etc.) during development, the inline<style>tags in server-rendered HTML would retain old CSS content instead of updating. This caused a brief flash of old CSS (FOUC) on fresh page loads before Vite's client-side HMR corrected the styles.The fix ensures that Astro's per-route dev CSS virtual modules are invalidated in both the SSR module graph and the module runner's evaluation cache when a style file changes, so the next page render picks up the fresh CSS.
-
#17044
2220d22Thanks @astrobot-houston! - Fixes CSS fromclient:onlyislands leaking to unrelated pages when Rollup bundles non-CSS-importing modules into the same chunk as CSS-importing modules -
#17040
7c4763dThanks @astrobot-houston! - Fixes HMR not triggering for files inside thesrc/middleware/directory during dev -
#16672
52fc862Thanks @martinheidegger! - Fixes support for numeric IDs in YAML frontmatter when using content collection references -
#16762
9de80aeThanks @alexanderdombroski! - Adds a JSON schema to the Wrangler configuration file generated when runningastro add cloudflare
Description
Follow-up to #3923 (comment), this PR improves the comment describing how we import the Sätteri integration to workaround an Astro limitation.
The comment should be clear enough to explain the issue and the workaround. Regarding a potential fix in Astro for the underlying issue, I would definitelly need to think more about it. So far, the only idea I had is to have the Vite module runner used for loading the config to stay open instead of closing immediately, and return some kind of disposable. The disposable would be used to properly close it only when needed, e.g. after a build or when the dev server is closed, when the dev server is restarted, after a sync, etc. The getViteConfig() case would also need to be handled. On top of the extra cost of keeping the module runner open, it's also a lot of orchestration to pass around such disposable with the current architecture.
A workaround that I only briefly tested for fun is to by-pass Vite and rely on a native dynamic import to load @astrojs/markdown-satteri, e.g. using () => new Function('return import("@astrojs/markdown-satteri")') but that's definitely not something we want to do (even tho it seems to work) 😅
Changes
- Validates the request URL origin against
allowedDomainsindefault-handler.tsbefore constructing thestatusURLused to fetch prerendered error pages. WhenallowedDomainsis configured and the host matches, the original origin is used. Otherwise, falls back tolocalhost.
Testing
- Added two test cases to
error-pages.test.ts: one verifying an unvalidated host is replaced withlocalhost, and one verifying a validated host is preserved.
Docs
- No docs update needed.
Changes
- Constrains generated live loader type extraction to the same bounds as
LiveLoader. - Ensures
LiveLoaderDataTypesatisfies theRecord<string, any>constraint required by live data result types. - Adds a focused declaration-checking regression for generated
.astro/content.d.tsand a patch changeset forastro.
Fixes #17025
Testing
pnpm exec prettier --check .changeset/green-dingos-check.mdpnpm exec biome check packages/astro/templates/content/types.d.ts packages/astro/test/content-collections-type-inference.test.tspnpm -C packages/astro exec astro-scripts test "test/content-collections-type-inference.test.ts" --strip-typespnpm --filter astro... build:cipnpm -C packages/astro test:typesgit diff --check
Docs
No docs update; this only fixes generated TypeScript declarations.
Changes
This PR restore our "Needs repro" workflow by using the gh CLI. To note that I used the houston bot token.
Testing
I already created a similar workflow and it should work for us.
Green CI
Docs
N/A
Changes
- Prevent
@astrojs/upgradefrom selecting a requested dist-tag when that tag resolves to a version older than the currently installed package. - Preserve prerelease comparison for installed versions like
7.0.0-beta.3instead of coercing them to stable versions. - Add a regression test covering
astroupgrading to beta while an integration remains on a newer stable release.
Why
Running @astrojs/upgrade beta could downgrade companion packages when their beta dist-tag pointed to an older prerelease than the currently installed stable version.
Fixes #17024
Testing
npx --yes pnpm@11.0.9 -C packages/upgrade buildnpx --yes pnpm@11.0.9 -C packages/upgrade testnpx --yes pnpm@11.0.9 lint
Changes
- Validates attribute names in
addAttribute()against the HTML spec before interpolating them into output. Keys containing characters invalid in attribute names (",',>,/,=, or whitespace) are silently dropped.
Testing
- Added
addAttribute rejects invalid attribute keyssuite (7 cases) covering keys with", spaces,>,',=, plus positive cases for normal names (data-foo,aria-label) and namespaced attributes (on:click,xmlns:happy). - Added
spreadAttributes rejects invalid attribute keyssuite (1 case) verifying invalid keys are dropped while valid siblings are preserved.
Docs
- No docs update needed. Internal rendering hardening with no user-facing API change.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.6
Patch Changes
-
#16765
b10e86eThanks @fkatsuhiro! - Fixes an issue where renaming an image file while the dev server is running triggers a build error. Now Astro correctly hot-reloads the image without crashing. -
#17026
add3df1Thanks @matthewp! - HardensaddAttributeto drop attribute names containing characters that are invalid per the HTML spec (",',>,/,=, whitespace) -
#17033
ffda27bThanks @matthewp! - Validates the request origin againstallowedDomainsbefore fetching prerendered error pages. WhenallowedDomainsis configured and the Host header matches, the original origin is used. Otherwise, the fetch falls back tolocalhost.
@astrojs/netlify@7.0.13
Patch Changes
Changes
- Fixes #17021
toImportNamein@astrojs/markdoc'scontent-entry-type.tsusedString.prototype.replacewith a string pattern, which only replaces the first hyphen. A Markdoc tag named e.g.my-cool-tagrendered via thecomponent()utility generatedimport Tagmy_cool-tag from …— an invalid JS identifier — and the build failed with a misleading Rollup parse error pointing at the.mdocfrontmatter.- One-line fix:
replace('-', '_')→replaceAll('-', '_').
Testing
- Reproduced on a real project: a
lead-capture-inlinetag mapped to an.astrocomponent viacomponent()breaksastro buildbefore the fix and builds/renders correctly after (verified against the built HTML output). - Single-hyphen tags are unaffected (
replaceAllis a strict superset of the previous behavior). toImportNameis module-private, so no unit test is attached; happy to add a fixture-based test topackages/integrations/markdoc/testif preferred.
Docs
No docs change needed — multi-hyphen tag names were already expected to work; this restores the documented component() behavior.
Changes
Prevents an unnecessary page reload during ClientRouter back/forward navigations when the browser's Navigation API handles the transition before popstate fires, making from and to the same URL.
Testing
Update: Added a test case that reproduces the bug by pushing a fake history entry, then pressing back
Manually tested in an Astro project where /work and its sub-routes (e.g. /work/slug-1) run as a SPA kept in sync via the browser's Navigation API. Other routes like /about or / still trigger Astro's View Transitions normally. For /work and its sub-routes:
-
Listening to
astro:before-preparationand callingpreventDefault()when both the current and destination URLs are under/work, which stops ClientRouter from fetching new HTML. -
Using the browser's Navigation API (
event.intercept()) to update the app's own state instead.
Pressing back from /work/slug-1 to /work/ causes two things to happen in sequence:
-
The browser's Navigation API fires. Our
event.intercept()handler runs, updating the app's state and changing the URL to/work/. -
popstatefires. The ClientRouter'sonPopStatecallstransition().
At this point the URL is already /work/. But originalLocation was never updated to /work/slug-1 — because that earlier forward navigation was done via nav.navigate(), which the ClientRouter doesn't track. So transition() sees from as /work/ (the stale originalLocation) and to as /work/ (the current URL). They match, the ClientRouter thinks nothing changed, falls through to doPreparation(), and eventually calls location.href = to.href, which triggers an unnecessary full page navigation.
With the guard added to transition(), from.href === to.href returns early before any of that happens, and the full page navigation never occurs.
Docs
No docs needed. Bugfix with no API or behavior change.
Changes
- Tightens the regex produced by
remotePatternToRegexso wildcard hostname and pathname patterns match strictly:*.requires exactly one subdomain (bare apex no longer matches)**.requires one or more subdomains (zero subdomains no longer matches)/*matches a single path segment only, not deeper paths
- Anchors the generated regex with
$so partial prefix matches are not possible
Testing
- Updated existing assertion:
*.example.orgno longer matches bareexample.org - Added assertion:
/images/*does not match/images/a/b.jpg
Docs
- No docs update needed -- this is an internal regex generation change with no user-facing API impact.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.40.0
Minor Changes
-
#3923
edf2e6bThanks @Princesseuh! - Adds support for Astro 6.4 and the new Sätteri Markdown processor.It is now possible to opt into using Astro's 6.4 Sätteri Markdown processor by installing the
@astrojs/markdown-satteripackage and configuring it in yourastro.config.mjsfile:// astro.config.mjs import { defineConfig } from 'astro/config'; import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), }, });
⚠️ BREAKING CHANGE: The minimum supported version of Astro is now v6.4.5.Please update Starlight and Astro together:
npx @astrojs/upgrade
Community Starlight plugins and Astro integrations may also need to be manually updated to work with Sätteri. If you encounter any issues, please reach out to the plugin or integration author to see if it is a known issue or if an updated version is being worked on.
Patch Changes
- #3923
edf2e6bThanks @Princesseuh! - Updates Expressive Code to version 0.43.1.
Changes
- Disables
persist-credentialson the checkout step in the issue triage workflow. When credentials are persisted, git always uses the defaultGITHUB_TOKEN(read-only) for pushes, even whengitPush()explicitly providesFREDKBOT_GITHUB_TOKEN. Disabling it lets the privileged token be used as intended.
Testing
- No test changes. CI-only fix verified against this failed run.
Docs
- No docs needed — CI-only change.
Changes
Fixes the version of the logger
Testing
N/A
Docs
/cc @withastro/maintainers-docs for feedback!
Changes
Remove the source code of deprecated package @astrojs/db. Remove related astro db CLI commands.
Testing
The actions-blog and actions-react-19 e2e fixtures only used @astrojs/db as a data store, so they now use a tiny file-backed JSON store instead.
Docs
Added a Changesets file to mention that some CLI commands like astro db are gone.
IMPORTANT: THIS MUST BE DONE AS A MERGE COMMIT
Changes
- Merges
mainintonextto bring in latest stable releases (astro 6.4.5, @astrojs/db 0.21.3, @astrojs/cloudflare 13.7.0, @astrojs/mdx 6.0.3, @astrojs/node 10.1.4, @astrojs/markdown-satteri 0.3.0). - Conflict resolution kept
nextversions for all package.json files and merged CHANGELOG entries (alpha entries first, then stable entries).
Testing
- No test changes. This is a branch merge with only version/changelog conflicts.
Docs
- No docs needed. Version merges do not affect user-facing documentation.
Changes
Closes AST-179
This PR exists from prerelease mode.
Note
To merge on the day of when we merge all PRs, right before the release
Testing
N/A
Docs
N/A
Changes
- Update all
session.skill()calls to use registered skill names instead of file paths (e.g.triage/reproduce.md->triage). Flue runtime 0.8 removed support for addressing sub-files within a skill; the old runtime was lax about this. - Pass
stepandinstructionsvia args to direct the agent to the correct sub-skill file within each skill. - Add
.agents/skills/merge/SKILL.mdso the merge skill gets discovered by the runtime (it had noSKILL.mdand was invisible).
Testing
- No test changes. This is a CI workflow fix — validation is the next issue triage run succeeding instead of failing with
Skill "triage/reproduce.md" not registered.
Docs
- No docs needed. Internal CI workflow change only.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to next, this PR will be updated.
next is currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, run changeset pre exit on next.
Releases
astro@7.0.0-beta.3
Major Changes
-
#17010
0606073Thanks @ocavue! - Removes theastro db,astro login,astro logout,astro link, andastro initCLI commands.The
@astrojs/dbpackage is now deprecated. We recommend using a database client (Drizzle, Kysely, etc.) directly instead. -
#16877
3b7d76eThanks @matthewp! - Enables advanced routing by default.The advanced routing feature introduced behind a flag in v6.3.0 is no longer experimental and is now enabled by default.
This gives full control over how requests flow through your application, with first-class support for frameworks like Hono.
Advanced routing now uses
src/fetch.tsas default entrypoint instead ofsrc/app.ts.If you were previously using this feature without a custom entrypoint, please configure
fetchFileor rename your entrypoint tosrc/fetch.ts, and then remove the experimental flag from your Astro config:import { defineConfig } from 'astro/config'; export default defineConfig({ experimental { - advancedRouting: true, }, + fetchFile: 'app.ts' // optional, you only need this if you cannot rename your entrypoint. });fetchFileis now a top-level config option instead of being nested underexperimental.advancedRouting. If you were using a custom entrypoint, please update your Astro config to move its configuration:// astro.config.mjs export default defineConfig({ - experimental: { - advancedRouting: { - fetchFile: 'my-custom-entrypoint.ts', - }, - }, + fetchFile: 'my-custom-entrypoint.ts', })You can also set
fetchFile: nullto disable the entrypoint if you are usingsrc/fetch.tsfor another purpose, or don’t need advanced routing features.If you have been waiting for stabilization before using advanced routing, you can now do so.
Please see the advanced routing guide in docs for more about this feature.
Minor Changes
-
#16998
57dcc31Thanks @matthewp! - ExposesgetFetchState()fromastro/honoas a public APIThe
getFetchState()function retrieves or lazily creates aFetchStatefrom a Hono context object. This allows third-party packages to build Hono middleware that interacts with Astro's per-request state, giving theastro/honoAPI the same extensibility asastro/fetch.import { Hono } from 'hono'; import { getFetchState, pages } from 'astro/hono'; const app = new Hono(); app.use(async (context, next) => { const state = getFetchState(context); state.locals.message = 'Hello from custom middleware'; await next(); }); app.use(pages()); export default app;
-
#16996
300641eThanks @florian-lefebvre! - Adds asubsetfield to theFontDatatype exposed viafontDatafromastro:assets. When using multiple font subsets (e.g.,subsets: ["latin", "korean"]), each font data entry now includes the subset name, making it possible to distinguish between font entries for different subsets that share the same weight and style. -
#16745
f864a80Thanks @ematipico! - The custom logger feature introduced behind a flag in v6.2.0 is no longer experimental and is available for general use.This feature provides better control over Astro's logging infrastructure by allowing you to replace the default console output with custom logging implementations (e.g., structured JSON). This is particularly useful for on-demand rendering when connecting to log aggregation services such as Kibana, Logstash, CloudWatch, Grafana, or Loki.
Astro provides three built-in log handlers (
json,node, andconsole), and you can also create your own.JSON logging
import { defineConfig, logHandlers } from 'astro/config'; export default defineConfig({ logger: logHandlers.json({ pretty: true, level: 'warn', }), });
Custom logger
import { defineConfig } from 'astro/config'; export default defineConfig({ logger: { entrypoint: '@org/custom-logger', }, });
Additionally,
context.loggeris now always available in API routes and middleware, even without a custom logger configured.If you were previously using this feature, please remove the experimental flag from your Astro config:
import { defineConfig } from 'astro/config'; export default defineConfig({ - experimental: { - logger: { - entrypoint: '@org/custom-logger', - }, - }, + logger: { + entrypoint: '@org/custom-logger', + }, });If you have been waiting for stabilization before using custom loggers, you can now do so.
Please see the Logger docs for more about this feature.
-
#16981
0d6d644Thanks @ematipico! - Removes the settingexperimental.queuedRendering. The new rendering engine is now stable and replaces the old one.As part of the stabilization, the queued rendering has been improved, and some features have been removed:
- The construction of the queue has been removed, instead now Astro uses a streaming approach where components are rendered and flushed as they are encountered.
- The node polling feature has been removed because it doesn't yield concrete savings.
- The content cache has been descoped, and how only tag names are cached.
If you were previously using this experimental feature, you must remove this experimental flag from your configuration as it no longer exists:
// astro.config.mjs import { defineConfig } from "astro/config"; export default defineConfig({ experimental: { - queuedRendering: {} } });
Changes
Exports the existing getFetchState() function from astro/hono so third-party packages and custom Hono middleware can access the per-request FetchState.
Testing
Added 3 unit tests in test/units/hono/index.test.ts:
- Creates a new
FetchStatefrom Hono context - Returns the same instance on subsequent calls
- Custom middleware can set
localsviagetFetchState
Docs
Changes
- Reverts #16720, which added a
Cloudflare-Workersnavigator check to theisNodevariable inpackages/astro/src/runtime/server/render/util.ts. This change causedisNodeto returnfalseinside workerd, forcing the rendering pipeline ontorenderToReadableStreaminstead ofrenderToAsyncIterable. For large prerendered sites on Cloudflare (~14k pages), this caused a ~6x build time regression (6-7 min to 40 min).
Testing
- No new tests. The regression is workerd-specific and only manifests at scale with the Cloudflare adapter. A preview release should be created so the reporter on #16973 can verify the fix.
Docs
- No docs needed — this restores previous behavior.
I’ve tested the latest version of the LSP server locally and found a regression in quick fixes.
Related to: #15908
Changes
- Restores the Add import quick fix for unimported .astro components with generated TypeScript names ending in AstroComponent
Testing
- Added coverage for local, nested, aliased, and generated-name Astro components.
- Tests verify that imports omit the AstroComponent suffix, keep the original diagnostic, and edit the source .astro file.
Changes
Fixes #14657.
Allows the Astro grammar to keep looking for lang / type on following lines of <style> and <script> opening tags.
Added grammar fixtures for multiline style languages (scss, less, sass) and script types (ts, application/ld+json, module).
Testing
corepack pnpm -C packages/language-tools/vscode test:grammar
Docs
No docs needed, syntax highlighting fix.
Changes
- Fixes user-authored
<link rel="stylesheet">being silently stripped from React components rendered by Astro, in bothdevandbuild. - The regex that removes React 19's auto-injected resource hints incorrectly matched
rel="stylesheet". Since React 19 only auto-injects<link rel="preload">,stylesheetwas removed from the match list so user-authored links are preserved.
Fixes #16987.
Testing
- Added a test to
react-19-preloads.test.tsasserting a user-authored<link rel="stylesheet">survives inside<astro-island>output. - Updated the
react-19-preloadsfixture component to include a stylesheet link.
Docs
- No docs update needed; this restores previously expected behavior (a regression fix).
Fixes #16442.
The collections.json manifest used for content intellisense was only regenerated during a full content sync, so adding or removing content files during astro dev left editor autocomplete stale until a restart.
This PR:
- Registers debounced
add/unlinkwatcher listeners that regenerate the manifest on content file changes. They're re-registered on each full sync (alongside the loaders, afterremoveAllTrackedListeners), and skipped on selective syncs to avoid stacking duplicates. Writes inside.astroare ignored to prevent a regenerate→write→watch loop. - Rebuilds the manifest
entriesmap from the current store instead of merging into the previous one, so files deleted since the last run are dropped instead of lingering.
Added unit tests covering: present-file inclusion, deleted-file removal, dev-time regeneration on file change, and the .astro self-write guard. All listener behavior is guarded behind experimental.contentIntellisense, so there's no change when the flag is off.
Disclosure: this change was developed with AI assistance.
Fixes #16974 so startup message shows on experimental.logger destination, not default terminal.
Currently it fires before any request arrives, always missing the user-configured destination.
Changes
- Adds
getLogger().then()before logListeningOn to correct destination. - Makes
adapterLoggerre-create itself when the underlying logger instance changes, so it always reflects the resolved logger regardless of first access
Testing
Before Implementation
After Implementation
Confirmed the first line of stdout is {"message":"Server listening on ...","label":"@astrojs/node","level":"info"} when manually building standalone server.
Added unit test verifying that adapterLogger re-creates itself when pipeline.getLogger() replaces the underlying logger instance. Both packages build without errors.
Docs
No update needed.
This PR fixes a typo I spotted in the first place:
Cummunity-Themes -> Community-Themes
It also makes minor changes to 3 sentences to improve their readability.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| actions/checkout | action | patch | v6.0.2 → v6.0.3 |
| changesets/action | action | minor | v1.8.0 → v1.9.0 |
Release Notes
actions/checkout (actions/checkout)
v6.0.3
- Fix checkout init for SHA-256 repositories by @yaananth in #2439
- fix: expand merge commit SHA regex and add SHA-256 test cases by @yaananth in #2414
changesets/action (changesets/action)
v1.9.0
Minor Changes
-
#636
b072bccThanks @bluwy! - Add a new@changesets/action/pr-commentsub-action to comment on PRs -
#625
8795eeeThanks @bluwy! - Add a new@changesets/action/pr-statussub-action to generate the changeset status comment for PRs as an alternative to the Changesets Bot.
Patch Changes
-
#535
34f64f6Thanks @Andarist! - Fixed an issue with GitHub releases not being created for successfully published packages when some packages failed to be published to the registry. -
#632
1d54b9eThanks @bluwy! - Simplify internal implementation to get changelog entries for a package version -
#629
e0c90aaThanks @bluwy! - Fix custom version and publish command argument parsing -
#645
f9585d9Thanks @Andarist! - Improved force-push handling when usingcommitMode: "github-api"so updating an existing branch no longer temporarily resets the target branch to the base commit, avoiding cases where GitHub closes open pull requests during the update. This should remove a possibility of a GitHub state race that caused the force-pushed PRs not being reopened.
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
defineProviderGettersinFetchStatenow always defines asessiongetter on context objects, even when no session provider has been registered. This ensuresAstro.session/ctx.sessionis always a present property regardless of whether thesessions()handler is included in the pipeline.- Accessing
sessionwithout storage configured logs a warning on first access: "no session storage is configured. Either configure the storage manually or use an adapter that provides session storage." This restores the helpful warning that existed in 6.2 before the advanced routing refactor moved session setup into the provider system.
Testing
- Added two unit tests in
test/units/fetch/index.test.ts: one verifying the property is always present (returningundefined), one verifying the warn-once behavior withSpyLogger.
Docs
- No docs needed. No user-facing API change.
Changes
Closes AST-166
This PR stabilise queued rendering, however there's a twist! The old queued engine is gone, and it's been replaced with a "streaming engine".
This engine is still queue based, but it renders the nodes on the go, so a two-step engine is now a one step engine. The old strategy didn't work very well, and it had some flaws. The new strategy is simpler and faster than the queue double pass and recursion.
Also, I removed pooling. It doesn't yield concrete benefits.
As for content cache, it is now scoped so the single tag names. The content itself isn't cached, as well as the attributes.
Testing
Green CI
Docs
Changes
- Removes
provide,resolve, andfinalizeAllfrom the publicAstroFetchStateinterface. The concreteFetchStateclass retains them for internal use (session, cache). - Removes
App.Providersmodule augmentation interface — nothing depends on it since built-in providers are already explicitly typed onAPIContext. - Removes
extends App.ProvidersfromAPIContext. - Marks
ContextProvider<T>as@internal.
Per the discussion on #16877, we do not want 3rd-party handlers extending the Astro global via FetchState#provide yet. This keeps the machinery working internally while removing it from the public API surface.
Testing
No test changes — existing context provider tests continue to pass since the internal class is unchanged.
Docs
Summary
Three JSDoc example blocks in errors-data.ts — for InvalidGetStaticPathParam, InvalidGetStaticPathsEntry, and InvalidGetStaticPathsReturn — open with the route file pages/blog/[id].astro but then return params keyed on slug:
{ params: { slug: "blog" } },
{ params: { slug: "about" } }For a dynamic route [id].astro, getStaticPaths() must return params keyed on the segment name id. A slug key would itself trigger a routing error, so these examples — whose whole purpose is to teach correct getStaticPaths() usage — are internally inconsistent.
These JSDoc blocks also generate the public Error Reference pages, so the mismatch is user-facing (e.g. docs.astro.build/en/reference/errors/invalid-get-static-path-param/).
Change
Replace the slug param key with id in all six lines so each example matches its declared [id].astro route. This matches the neighboring blocks GetStaticPathsExpectedParams and GetStaticPathsInvalidRouteParam, which already use { params: { id: ... } } for an [id] route.
Docs/comment example only — no runtime code changes.
Summary
Preserve empty request cookie values in Astro.cookies.get().
Related: #16983
User-facing problem
A request with Cookie: foo= currently produces two conflicting results:
cookies.has('foo') === truecookies.get('foo') === undefined
That collapses present but empty into the same result as missing.
Expected behavior
For Cookie: foo=, cookies.get('foo')?.value should be ''.
Why this looks like a regression
I opened #16983 with a minimal reproduction and the full history.
Short version: get() used to preserve empty values, then started checking if (value) in c69bf18a4e on 2025-03-18. A later refactor in 018fbe90f4 on 2025-03-26 made the has() / get() mismatch more obvious.
What changed
- treat
''as a valid parsed cookie value inAstro.cookies.get() - add a regression test for
cookie: 'foo='
Validation
npm exec --yes pnpm@11.5.0 -- exec astro-scripts test "test/units/cookies/*.test.ts" --strip-types --teardown ./test/units/teardown.ts
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/cloudflare@13.7.0
Minor Changes
-
#16571
d4b0cd1Thanks @MA2153! - Sets immutable cache headers for static assetsStatic assets under
_astrocan be cached to improve performance. The adapter now automatically injects aCache-Controlheader at build time when possible.
Patch Changes
-
#16968
7a5c001Thanks @astrobot-houston! - Fixes a build crash when usingexperimental.advancedRoutingwith a customfetchFilethat statically importscffrom@astrojs/cloudflare/fetch. The circular dependency between@astrojs/cloudflare/fetchandastro/app/entrypointcausedcreateApporcreateGetEnvto beundefinedat module evaluation time. Initialization is now deferred to the firstcf()call, breaking the cycle. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/markdown-satteri@0.3.0
Minor Changes
-
#16969
4a31f90Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Settingmarkdown.syntaxHighlightto'prism'now highlights your code blocks with Prism.// astro.config.mjs import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), syntaxHighlight: 'prism', }, });
astro@6.4.5
Patch Changes
-
#16985
4ecff32Thanks @maximslo! - Fixes theexperimental.loggerdestination not being used for the "Server listening on..." startup message. The logger is now resolved before the server starts listening, andadapterLoggerre-creates itself when the underlying logger changes so the startup message uses the correct destination. -
#16947
e0703a6Thanks @ematipico! - FixesAstro.request.urlnot reflecting validatedX-Forwarded-Proto/X-Forwarded-Hostheaders whensecurity.allowedDomainsis configured. Previously, onlyAstro.urlwas updated with the forwarded origin whileAstro.request.urlretained the socket-derived URL, causing the two to diverge behind TLS-terminating proxies. -
#16997
dc45246Thanks @matthewp! - Reverts a change toisNoderuntime detection that caused a significant build time regression for Cloudflare adapter users with large prerendered sites
@astrojs/db@0.21.3
Patch Changes
- #16964
b048826Thanks @Princesseuh! - Deprecates the@astrojs/dbintegration. We no longer have the bandwidth to maintain this package, and we recommend that users directly use the database client of their choice (Drizzle, Kysely, etc.) in their Astro projects instead.
@astrojs/mdx@6.0.3
Patch Changes
-
#16969
4a31f90Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Settingmarkdown.syntaxHighlightto'prism'now highlights your code blocks with Prism.// astro.config.mjs import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri(), syntaxHighlight: 'prism', }, });
@astrojs/node@10.1.4
Patch Changes
- #16985
4ecff32Thanks @maximslo! - Fixes theexperimental.loggerdestination not being used for the "Server listening on..." startup message. The logger is now resolved before the server starts listening, andadapterLoggerre-creates itself when the underlying logger changes so the startup message uses the correct destination.
Changes
Update a test so that it can pass in the next branch (with @astrojs/compiler-rs).
@astrojs/compiler-rs re-serializes CSS values from rgb(255, 165, 0) to orange to this test didn't pass in the next branch.
Unblock #16962
Testing
rgb(0, 255, 0) CI.
Docs
N/A
Changes
What the title says, it's easy enough, just needed hooking things up
Testing
Added / updated tests
Docs
N/A, I don't think we documented that it didn't work right now
Changes
- Defers
createApp()andsetGetEnv()initialization in@astrojs/cloudflare/fetchto the firstcf()call - Prevents circular import crashes when custom
fetchFilestatically importscffrom@astrojs/cloudflare/fetch - Uses lazy initialization pattern via
ensureInitialized()helper to break the module evaluation cycle
Testing
- Added
fetch-lazy-init.test.tscovering customfetchFilescenarios with static imports - Verified existing cf-helpers tests continue passing
- Confirmed no regression with
advancedRouting: true(boolean) usage
Docs
- No docs update needed, this fixes the documented usage pattern that was previously broken
Closes #16956
Changes
- Replaces blanket rejection of double-encoded paths with iterative decoding in
validateAndDecodePathname(). Fixes 400 errors for legitimate double-encoded URLs like Sanity Studio's%255B/%255D(double-encoded[/]). - Ensures middleware always sees the fully decoded canonical path. Attack paths like
/api/%2561dminnow decode to/api/adminand middleware correctly blocks them with 401 instead of the previous blanket 400. - Maintains CVE-2025-66202 security protections while eliminating false positives for legitimate client router URLs.
Testing
- Added 25 tests in
test/units/util/validate-and-decode-pathname.test.tscovering iterative decoding, Sanity Studio cases, and edge cases. - Added 13 tests in
test/units/app/double-encoding-bypass.test.tsverifying middleware still blocks security attacks. - Updated 2 tests in
test/middleware.test.tsfor new behavior where middleware sees decoded paths.
Docs
- No docs update needed, this fixes broken behavior to match existing expectations.
Closes #16960
Changes
🦀
Testing
All tests should pass, tests relying on unified stuff (remark, rehype plugins, etc) got the unified markdown processor added manually
Docs
Changes
The newly introduced compressHTML: 'jsx' is now the default option in V7, requested by our steward. The Rust compiler always had support for this unlike the Go one, so it's really just switching up the default.
Testing
Tests should pass, added tests for this specific behavior
Docs
Needs a PR for that
Changes
We no longer have the bandwidth to maintain this package and are generally not as interested as we once were regarding maintaining our own DB layer considering how much more mature tools like Drizzle and Prisma have become over time.
This package has also generally caused a lot of headaches for not much considering how little usage it has.
Testing
N/A
Docs
withastro/docs#13985 (ish, probably also needs a main callout if we merge this now..)
Changes
This pull request adds retries for remote image dimension inference in Astro's image service. Currently, if a transient network error happens during build, the entire build fails, which can lead to several builds failing till it succeeds in some environments, making this a QoL improvement.
I am not sure if this counts as a patch or minor change? It doesn't add new API but kinda changes how builds behave regarding images, so I defaulted to minor for the moment.
Testing
I added new unit tests in remote-probe.test.ts + integration tests in core-image-infersize.test.ts to test this plus a combination of scenarios like redirects, not allowed remote sources, 5xx errors, etc.
Docs
It's probably worth a mention in the main Images guide or Image Service API, I'm not exactly sure where the best place would be so I will wait before making a PR in docs.
Changes
astro sync was much slower with @astrojs/cloudflare because type generation started the Cloudflare dev runtime even though no requests are served. During the invocation of the temporary type generation dev server, the adapter now:
- Clears
configureServerfrom@cloudflare/vite-pluginplugins so the Cloudflare dev runtime never starts. - Sets
optimizeDeps: { noDiscovery: true, include: [] }on every environment so no dependencies are pre-bundled. devandpreviewcontinue to start the Cloudflare runtime as expected.
Testing
- Measured
astro synctype generation with the MRE:
| Adapter | astro sync |
|---|---|
| none | 37 ms |
@astrojs/node |
37 ms |
@astrojs/vercel |
36 ms |
@astrojs/netlify |
29 ms |
@astrojs/cloudflare (before) |
1.16 s |
@astrojs/cloudflare (after) |
56 ms |
Docs
- No docs needed. This is a build-time performance fix with no user-facing API change.
Closes #16332
Changes
- Fixes static file endpoints using dynamic routing where .html extensions in getStaticPaths params caused NoMatchingStaticPathFound build errors
- Modifies FetchState.#stripHtmlExtension() to only strip .html from page routes, not endpoint routes, by adding this.routeData.type === 'page' guard
- Resolves v5-to-v6 regression where endpoint routes with .html in param values would fail to build
Testing
- Added regression test preserves .html in pathname for endpoint routes with dynamic params in packages/astro/test/units/fetch/index.test.ts
- Verified fix resolves the reproduction case and produces correct dist/file.html output
Docs
- No docs update needed, this restores expected behavior from v5.
Closes #16941
Closes: #16780
Changes
- Invalidate per-route dev CSS virtual modules when CSS files change, preventing stale server-rendered inline styles after HMR.
- Treat
*.css?rawimports as SSR dependencies instead of normal style modules, so raw CSS strings rendered withset:htmlrefresh correctly. - Builds on the Astrobot fix from #16783, with additional coverage for the
?rawimport path.
Testing
- Added a package integration test that starts an Astro dev server, edits global CSS, and verifies the server-rendered inline
<style>output updates. - Added unit coverage for dev CSS virtual module invalidation.
- Added unit coverage for
*.css?rawimports going through SSR invalidation instead of the normal CSS HMR skip path. pnpm -C packages/astro run build:cipnpm -C packages/astro exec astro-scripts test "test/units/vite-plugin-hmr-reload/hmr-reload.test.ts" --strip-types
Docs
No docs changes. This fixes dev-server HMR behavior without changing user-facing APIs.
Changes
What the title says, this release fixes some bugs and adds some options to GFM and math
Testing
Tests should pass!
Docs
N/A, we don't document Sätteri options, we just link to its docs.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.4.4
Patch Changes
-
#16926
1b39ae8Thanks @narendraio! - PreventsApp.match()from throwing on request paths that contain an invalid percent-sequence. -
#16924
2c0bc94Thanks @astrobot-houston! - Fixes an issue where editing a client-side component (e.g. withclient:idle,client:load, etc.) caused an unnecessary full program reload of the backend during development. -
#16958
2c1d50fThanks @fkatsuhiro! - Fixes a bug where static file endpoints usinggetStaticPathswith.htmlin dynamic param values (e.g.{ path: 'file.html' }) would fail with aNoMatchingStaticPathFounderror during build. The.htmlsuffix is no longer incorrectly stripped from endpoint route pathnames. -
#16855
c610cdaThanks @astrobot-houston! - Fixes dynamic routes returning 500 "TypeError: Missing parameter" when using domain-based i18n routing in SSR. -
#16946
606c37bThanks @ematipico! - FixesAstro.routePatternto preserve original casing of dynamic parameter names from filenames. Previously, a file atsrc/pages/blog/[postId].astrowould return/blog/[postid]forAstro.routePatterndue to an internal.toLowerCase()call. It now correctly returns/blog/[postId]. -
#16720
16d49b6Thanks @thomas-callahan-collibra! - Fix an issue where dynamic routes would return the string[object Object]instead of the expected content, in certain runtimes. -
#16703
17390a6Thanks @henrybrewer00-dotcom! - Fixes styles being stripped when the project root is started with a path whose case differs from the actual filesystem case (e.g. runningastro devfromd:\dev\appwhile the folder on disk isD:\dev\app). -
#16855
c610cdaThanks @astrobot-houston! - FixesAstro.currentLocalereturning the default locale instead of the domain's locale on dynamic routes served from a mapped domain.
@astrojs/mdx@6.0.2
Patch Changes
-
#16955
9a93d68Thanks @Princesseuh! - Updates Sätteri processor to v0.8.0. See its changelog for details on bugs fixed and features added. -
Updated dependencies [
9a93d68]:- @astrojs/markdown-satteri@0.2.2
@astrojs/markdown-satteri@0.2.2
Patch Changes
- #16955
9a93d68Thanks @Princesseuh! - Updates Sätteri processor to v0.8.0. See its changelog for details on bugs fixed and features added.
Changes
Closes #16945
We create a new request and apply the correct symbols.
Testing
Added new unit tests
Docs
Changes
Fixes an issue where routePattern was transformed into lower case
Closes #16942
Testing
Tests added by the bot.
Docs
Fixes #16078
Summary
- include tsconfig-matched
.astrofiles in the TypeScript plugin project - inject the installed Astro package's
env.d.tsandastro-jsx.d.tsinto the TS plugin host - add focused unit and VS Code fixture coverage for
Astro.localsreferences
Verification
pnpm -C packages/language-tools/ts-plugin buildpnpm -C packages/language-tools/ts-plugin exec mocha --ui tdd --require tsx "test/units/**/*.test.mts"pnpm -C packages/language-tools/vscode build && pnpm -C packages/language-tools/ts-plugin test
Last fetched: | Scheduled refresh: Every Saturday
See Customizing GitHub Activity Pages to configure your own
Inspired by prs.atinux.com