Skip to content

AstroEco is Contributing…

Display your GitHub pull requests using astro-loader-github-prs

withastro/astro

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 filtering
  • packages/astro/test/units/redirects/environment-filtering.test.ts — New unit tests covering the filtering logic
withastro/astro

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.0yaml@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.0yaml@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.

withastro/starlight

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.

withastro/astro

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 in markdoc.config.ts
  • a MultiHyphen.astro component
  • usage in with-components.mdoc
  • a renders content - with multi-hyphen tag names assertion in render-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 test in packages/integrations/markdoc48/48 pass on this branch (47 existing + 1 new)
  • Verified the new test fails on main by stashing only the toImportName change and re-running — 1 failed | 45 passed, confirming the test catches the actual regression and not just vacuous coverage
  • Changeset added (@astrojs/markdoc patch)

Notes for review

  • An astrobot-houston triage 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 (the TODO: more checks that name is a safe JS variable name comment hints at it). That's a separate, larger scope worth its own discussion.
withastro/astro

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's missing.astro returns new Response(null, { status: 404 }) in production builds, which is what broke the E2E jobs. The x-astro-prerender-error header is now the only failure signal.
  • src/utils/prerender.ts: add installPrerenderErrorPropagation(). The header path was previously dead code: app.render() never rejects on render errors because AstroHandler catches the throw and the production DefaultErrorHandler renders a 500 error page, so the buffering catch in handlePrerenderRequest never fired (the new test was passing only via the status check). This override replicates core's BuildErrorHandler semantics on the worker app: render errors (status 500 with an error and no response) are rethrown, while intentional error responses and 404s flow through the default handler unchanged.
  • src/utils/handler.ts: install the override when isPrerender is 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.ts passes (both tests) — a page throwing during prerendering still fails the build.
  • packages/astro/e2e/fixtures/cloudflare builds successfully again with the intentional 404 in missing.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

withastro/astro

Changes

  • Enables HMR for CSS files imported with ?raw syntax. Previously, modifying these files required a manual browser refresh to see changes.
  • Updates the astro:hmr-reload plugin to check if style modules exist in the client module graph before skipping them. SSR-only style modules (like ?raw imports) now trigger a full page reload.

Testing

  • Added unit test 'sends full-reload for ?raw CSS imports (SSR-only style modules)' in packages/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 ?raw CSS imports

Closes #17032

withastro/astro

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.trailingSlash

For 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
withastro/astro

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 in serve-static.ts so 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 สวัสดี.astro with 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

withastro/astro

Changes

  • Fixes image compilation regression for Cloudflare adapter users with static output. The image optimizer was looking in wrong directory (dist/_astro/ instead of dist/client/_astro/), causing ENOENT build failures.
  • Replaces hardcoded outDir usage with getClientOutputDirectory(settings) helper in prepareAssetsGenerationEnv() to respect preserveBuildClientDir setting.

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

withastro/astro

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-error header 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.ts with test case 'fails the build when a page throws during prerendering'
  • Added prerenderer-render-error test 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)
withastro/astro

Changes

Follow up of withastro/compiler-rs#45

Updates astro to use the latest version of the compiler

Testing

Green CI

Docs

withastro/astro

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

withastro/astro

Changes

  • Fixes CSS incorrectly loading on pages that don't import it when using client:only islands in production builds
  • Adds missing isCSSRequest filter to the client:only CSS association loop in plugin-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.ts that 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

withastro/astro

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 via app.renderError(..., { status: 500, error }), the same behaviour AstroHandler.render() has on the standard path. Forwarding error keeps the documented error prop on 500.astro working. The logic lives in the handler module because core/fetch/index.ts deliberately keeps its exports as thin, logic-free wrappers.
  • When no route matched, handleWithErrorFallback() returns a 404 response marked with X-Astro-Error (the same pattern handle() 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 leaves routeData unset when the custom 404 route is prerendered (or absent), where PagesHandler.handle() would otherwise throw a TypeError and the catch would turn those unmatched requests into 500s. The 500 path keeps calling renderError() directly: the marker cannot carry the error object, so 500.astro would lose its error prop and the stack would not be logged.
  • Switched pages() in core/fetch/index.ts to delegate to the new method (delegation only, no logic added).
  • Added a changeset (astro patch).

Testing

  • Added 'renders the custom 500 page when a page throws during render' - verifies the full pages() 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 the X-Astro-Error marker instead of throwing
  • Verified end-to-end against the reproduction from #16952 (Hono + app.use(pages()), node standalone): GET /boom -> 500 with the custom 500 page (previously Hono's plain-text Internal Server Error), GET /does-not-exist -> 404 serving 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

withastro/astro

Closes #17039

Changes

  • Middleware file watcher now triggers HMR for files inside the src/middleware/ directory, not just src/middleware.{ts,js} files directly
  • Updates the Vite plugin path matcher to include both middleware. (existing) and middleware/ (new) prefixes, matching the fix pattern used for actions in #16932

Testing

  • Added packages/astro/test/units/middleware/middleware-hmr.test.ts with 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
withastro/astro

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 lastModified component), so revalidation returns a fresh 200 after a deploy. If-None-Match takes precedence over If-Modified-Since per 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 etag is respected verbatim.
  • Only runs when CF_VERSION_METADATA is configured; otherwise behavior is unchanged (content-only validator).

Tests

Extends cache-provider.test.ts (and the cache-provider fixture):

  • /api (tags, no lastModified) → weak ETag embedding the same id as the astro-version: tag.
  • new /lastmod route → ETag combines version + content lastModified; Last-Modified still emitted.
  • new /explicit-etag route → an explicit etag is 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 + wrangler preview) in my environment — relying on CI here. The change is isolated to setHeaders and mirrors the existing provider patterns.

withastro/astro

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 passing
  • astro i18n unit tests (test/units/i18n/astro_i18n.test.ts) → 37 passing, unchanged

Related: #17034

withastro/astro

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 197e50e Thanks @astrobot-houston! - Fixes getRelativeLocaleUrl, getAbsoluteLocaleUrl, and getAbsoluteLocaleUrlList to strip trailing slashes when trailingSlash: 'never' is configured

  • #16967 3719765 Thanks @astrobot-houston! - Fixes double URL-encoded paths returning 400 Bad Request on on-demand routes

    Previously, any URL containing a double-encoded character (like %255B, which is [ encoded twice) was unconditionally rejected with a 400 Bad Request before 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/%2561dmin is decoded to /api/admin, which middleware can correctly block.

  • #16882 621beb7 Thanks @jettwayio! - fix(render): honour compressHTML when joining head elements

  • #16892 8d753b0 Thanks @astrobot-houston! - Fixes custom elements in MDX having their children's slot attribute stripped by the JSX runtime

    When custom elements (tags with hyphens like <my-element>) are used in MDX files, the slot HTML attribute on their children is now correctly preserved. Previously, the shared JSX runtime would treat slot as an Astro slot assignment and remove it from the output, breaking Shadow DOM named slot distribution for web components.

  • #16957 544ee76 Thanks @thelazylamaGit! - Fixes stale inline CSS in server-rendered HTML after CSS file edits during dev

    When 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 2220d22 Thanks @astrobot-houston! - Fixes CSS from client:only islands leaking to unrelated pages when Rollup bundles non-CSS-importing modules into the same chunk as CSS-importing modules

  • #17040 7c4763d Thanks @astrobot-houston! - Fixes HMR not triggering for files inside the src/middleware/ directory during dev

  • #16672 52fc862 Thanks @martinheidegger! - Fixes support for numeric IDs in YAML frontmatter when using content collection references

  • #16762 9de80ae Thanks @alexanderdombroski! - Adds a JSON schema to the Wrangler configuration file generated when running astro add cloudflare

withastro/starlight

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) 😅

withastro/astro

Changes

  • Validates the request URL origin against allowedDomains in default-handler.ts before constructing the statusURL used to fetch prerendered error pages. When allowedDomains is configured and the host matches, the original origin is used. Otherwise, falls back to localhost.

Testing

  • Added two test cases to error-pages.test.ts: one verifying an unvalidated host is replaced with localhost, and one verifying a validated host is preserved.

Docs

  • No docs update needed.
withastro/astro

Changes

  • Constrains generated live loader type extraction to the same bounds as LiveLoader.
  • Ensures LiveLoaderDataType satisfies the Record<string, any> constraint required by live data result types.
  • Adds a focused declaration-checking regression for generated .astro/content.d.ts and a patch changeset for astro.

Fixes #17025

Testing

  • pnpm exec prettier --check .changeset/green-dingos-check.md
  • pnpm exec biome check packages/astro/templates/content/types.d.ts packages/astro/test/content-collections-type-inference.test.ts
  • pnpm -C packages/astro exec astro-scripts test "test/content-collections-type-inference.test.ts" --strip-types
  • pnpm --filter astro... build:ci
  • pnpm -C packages/astro test:types
  • git diff --check

Docs

No docs update; this only fixes generated TypeScript declarations.

withastro/astro

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

withastro/astro

Changes

  • Prevent @astrojs/upgrade from 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.3 instead of coercing them to stable versions.
  • Add a regression test covering astro upgrading 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 build
  • npx --yes pnpm@11.0.9 -C packages/upgrade test
  • npx --yes pnpm@11.0.9 lint
withastro/astro

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 keys suite (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 keys suite (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.
withastro/astro

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 b10e86e Thanks @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 add3df1 Thanks @matthewp! - Hardens addAttribute to drop attribute names containing characters that are invalid per the HTML spec (", ', >, /, =, whitespace)

  • #17033 ffda27b Thanks @matthewp! - Validates the request origin against allowedDomains before fetching prerendered error pages. When allowedDomains is configured and the Host header matches, the original origin is used. Otherwise, the fetch falls back to localhost.

@astrojs/netlify@7.0.13

Patch Changes

  • #17018 1310277 Thanks @matthewp! - Hardens remotePatterns regex generation to match canonical wildcard semantics more strictly

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3
withastro/astro

Changes

  • Fixes #17021
  • toImportName in @astrojs/markdoc's content-entry-type.ts used String.prototype.replace with a string pattern, which only replaces the first hyphen. A Markdoc tag named e.g. my-cool-tag rendered via the component() utility generated import Tagmy_cool-tag from … — an invalid JS identifier — and the build failed with a misleading Rollup parse error pointing at the .mdoc frontmatter.
  • One-line fix: replace('-', '_')replaceAll('-', '_').

Testing

  • Reproduced on a real project: a lead-capture-inline tag mapped to an .astro component via component() breaks astro build before the fix and builds/renders correctly after (verified against the built HTML output).
  • Single-hyphen tags are unaffected (replaceAll is a strict superset of the previous behavior).
  • toImportName is module-private, so no unit test is attached; happy to add a fixture-based test to packages/integrations/markdoc/test if preferred.

Docs

No docs change needed — multi-hyphen tag names were already expected to work; this restores the documented component() behavior.

withastro/astro

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:

  1. Listening to astro:before-preparation and calling preventDefault() when both the current and destination URLs are under /work, which stops ClientRouter from fetching new HTML.

  2. 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:

  1. The browser's Navigation API fires. Our event.intercept() handler runs, updating the app's state and changing the URL to /work/.

  2. popstate fires. The ClientRouter's onPopState calls transition().

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.

withastro/astro

Changes

  • Tightens the regex produced by remotePatternToRegex so 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.org no longer matches bare example.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.
withastro/starlight

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 edf2e6b Thanks @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-satteri package and configuring it in your astro.config.mjs file:

    // 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

withastro/astro

Changes

  • Disables persist-credentials on the checkout step in the issue triage workflow. When credentials are persisted, git always uses the default GITHUB_TOKEN (read-only) for pushes, even when gitPush() explicitly provides FREDKBOT_GITHUB_TOKEN. Disabling it lets the privileged token be used as intended.

Testing

Docs

  • No docs needed — CI-only change.
withastro/astro

Changes

Fixes the version of the logger

Testing

N/A

Docs

/cc @withastro/maintainers-docs for feedback!

withastro/astro

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.

withastro/astro

IMPORTANT: THIS MUST BE DONE AS A MERGE COMMIT

Changes

  • Merges main into next to 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 next versions 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.
withastro/astro

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

withastro/astro

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 step and instructions via args to direct the agent to the correct sub-skill file within each skill.
  • Add .agents/skills/merge/SKILL.md so the merge skill gets discovered by the runtime (it had no SKILL.md and 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.
withastro/astro

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 0606073 Thanks @ocavue! - Removes the astro db, astro login, astro logout, astro link, and astro init CLI commands.

    The @astrojs/db package is now deprecated. We recommend using a database client (Drizzle, Kysely, etc.) directly instead.

  • #16877 3b7d76e Thanks @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.ts as default entrypoint instead of src/app.ts.

    If you were previously using this feature without a custom entrypoint, please configure fetchFile or rename your entrypoint to src/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.
    });

    fetchFile is now a top-level config option instead of being nested under experimental.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: null to disable the entrypoint if you are using src/fetch.ts for 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 57dcc31 Thanks @matthewp! - Exposes getFetchState() from astro/hono as a public API

    The getFetchState() function retrieves or lazily creates a FetchState from a Hono context object. This allows third-party packages to build Hono middleware that interacts with Astro's per-request state, giving the astro/hono API the same extensibility as astro/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 300641e Thanks @florian-lefebvre! - Adds a subset field to the FontData type exposed via fontData from astro: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 f864a80 Thanks @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, and console), 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.logger is 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 0d6d644 Thanks @ematipico! - Removes the setting experimental.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: {}
      }
    });
withastro/astro

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 FetchState from Hono context
  • Returns the same instance on subsequent calls
  • Custom middleware can set locals via getFetchState

Docs

withastro/docs#14021

withastro/astro

Changes

  • Reverts #16720, which added a Cloudflare-Workers navigator check to the isNode variable in packages/astro/src/runtime/server/render/util.ts. This change caused isNode to return false inside workerd, forcing the rendering pipeline onto renderToReadableStream instead of renderToAsyncIterable. 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.
withastro/astro

Changes

Testing

Updated

Docs

withastro/astro

I’ve tested the latest version of the LSP server locally and found a regression in quick fixes.
Related to: #15908

Screenshot 2026-06-08 at 2 31 13 PM Screenshot 2026-06-08 at 2 31 31 PM

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.
withastro/astro

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.

withastro/astro

Changes

  • Fixes user-authored <link rel="stylesheet"> being silently stripped from React components rendered by Astro, in both dev and build.
  • The regex that removes React 19's auto-injected resource hints incorrectly matched rel="stylesheet". Since React 19 only auto-injects <link rel="preload">, stylesheet was removed from the match list so user-authored links are preserved.

Fixes #16987.

Testing

  • Added a test to react-19-preloads.test.ts asserting a user-authored <link rel="stylesheet"> survives inside <astro-island> output.
  • Updated the react-19-preloads fixture component to include a stylesheet link.

Docs

  • No docs update needed; this restores previously expected behavior (a regression fix).
withastro/astro

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/unlink watcher listeners that regenerate the manifest on content file changes. They're re-registered on each full sync (alongside the loaders, after removeAllTrackedListeners), and skipped on selective syncs to avoid stacking duplicates. Writes inside .astro are ignored to prevent a regenerate→write→watch loop.
  • Rebuilds the manifest entries map 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.

withastro/astro

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 adapterLogger re-create itself when the underlying logger instance changes, so it always reflects the resolved logger regardless of first access

Testing

Before Implementation

Screenshot 2026-06-08 at 3 07 04 AM

After Implementation

Screenshot 2026-06-08 at 3 11 34 AM

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.

withastro/starlight

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.

withastro/starlight

This PR contains the following updates:

Package Type Update Change
actions/checkout action patch v6.0.2v6.0.3
changesets/action action minor v1.8.0v1.9.0

Release Notes

actions/checkout (actions/checkout)

v6.0.3

Compare Source

changesets/action (changesets/action)

v1.9.0

Compare Source

Minor Changes
Patch Changes
  • #​535 34f64f6 Thanks @​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 1d54b9e Thanks @​bluwy! - Simplify internal implementation to get changelog entries for a package version

  • #​629 e0c90aa Thanks @​bluwy! - Fix custom version and publish command argument parsing

  • #​645 f9585d9 Thanks @​Andarist! - Improved force-push handling when using commitMode: "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.

withastro/astro

Changes

  • defineProviderGetters in FetchState now always defines a session getter on context objects, even when no session provider has been registered. This ensures Astro.session / ctx.session is always a present property regardless of whether the sessions() handler is included in the pipeline.
  • Accessing session without 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 (returning undefined), one verifying the warn-once behavior with SpyLogger.

Docs

  • No docs needed. No user-facing API change.
withastro/astro

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

withastro/docs#14019

withastro/astro

Changes

  • Removes provide, resolve, and finalizeAll from the public AstroFetchState interface. The concrete FetchState class retains them for internal use (session, cache).
  • Removes App.Providers module augmentation interface — nothing depends on it since built-in providers are already explicitly typed on APIContext.
  • Removes extends App.Providers from APIContext.
  • 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

withastro/astro

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.

withastro/astro

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') === true
  • cookies.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 in Astro.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
withastro/astro

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 d4b0cd1 Thanks @MA2153! - Sets immutable cache headers for static assets

    Static assets under _astro can be cached to improve performance. The adapter now automatically injects a Cache-Control header at build time when possible.

Patch Changes

  • #16968 7a5c001 Thanks @astrobot-houston! - Fixes a build crash when using experimental.advancedRouting with a custom fetchFile that statically imports cf from @astrojs/cloudflare/fetch. The circular dependency between @astrojs/cloudflare/fetch and astro/app/entrypoint caused createApp or createGetEnv to be undefined at module evaluation time. Initialization is now deferred to the first cf() call, breaking the cycle.

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/markdown-satteri@0.3.0

Minor Changes

  • #16969 4a31f90 Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Setting markdown.syntaxHighlight to '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 4ecff32 Thanks @maximslo! - Fixes the experimental.logger destination not being used for the "Server listening on..." startup message. The logger is now resolved before the server starts listening, and adapterLogger re-creates itself when the underlying logger changes so the startup message uses the correct destination.

  • #16947 e0703a6 Thanks @ematipico! - Fixes Astro.request.url not reflecting validated X-Forwarded-Proto/X-Forwarded-Host headers when security.allowedDomains is configured. Previously, only Astro.url was updated with the forwarded origin while Astro.request.url retained the socket-derived URL, causing the two to diverge behind TLS-terminating proxies.

  • #16997 dc45246 Thanks @matthewp! - Reverts a change to isNode runtime detection that caused a significant build time regression for Cloudflare adapter users with large prerendered sites

@astrojs/db@0.21.3

Patch Changes

  • #16964 b048826 Thanks @Princesseuh! - Deprecates the @astrojs/db integration. 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 4a31f90 Thanks @Princesseuh! - Adds support for Prism syntax highlighting to the Sätteri Markdown and MDX processors. Setting markdown.syntaxHighlight to '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 4ecff32 Thanks @maximslo! - Fixes the experimental.logger destination not being used for the "Server listening on..." startup message. The logger is now resolved before the server starts listening, and adapterLogger re-creates itself when the underlying logger changes so the startup message uses the correct destination.
withastro/astro

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

withastro/astro

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

withastro/astro

Changes

  • Defers createApp() and setGetEnv() initialization in @astrojs/cloudflare/fetch to the first cf() call
  • Prevents circular import crashes when custom fetchFile statically imports cf from @astrojs/cloudflare/fetch
  • Uses lazy initialization pattern via ensureInitialized() helper to break the module evaluation cycle

Testing

  • Added fetch-lazy-init.test.ts covering custom fetchFile scenarios 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

withastro/astro

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/%2561dmin now decode to /api/admin and 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.ts covering iterative decoding, Sanity Studio cases, and edge cases.
  • Added 13 tests in test/units/app/double-encoding-bypass.test.ts verifying middleware still blocks security attacks.
  • Updated 2 tests in test/middleware.test.ts for new behavior where middleware sees decoded paths.

Docs

  • No docs update needed, this fixes broken behavior to match existing expectations.

Closes #16960

withastro/astro

Changes

🦀

Testing

All tests should pass, tests relying on unified stuff (remark, rehype plugins, etc) got the unified markdown processor added manually

Docs

withastro/astro

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

withastro/astro

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.

Close #16738
Close #15431

Testing

N/A

Docs

withastro/docs#13985 (ish, probably also needs a main callout if we merge this now..)

withastro/astro

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.

/cc @withastro/maintainers-docs

withastro/astro

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 configureServer from @cloudflare/vite-plugin plugins so the Cloudflare dev runtime never starts.
  • Sets optimizeDeps: { noDiscovery: true, include: [] } on every environment so no dependencies are pre-bundled.
  • dev and preview continue to start the Cloudflare runtime as expected.

Testing

  • Measured astro sync type 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

withastro/astro

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

withastro/astro

Closes: #16780

Changes

  • Invalidate per-route dev CSS virtual modules when CSS files change, preventing stale server-rendered inline styles after HMR.
  • Treat *.css?raw imports as SSR dependencies instead of normal style modules, so raw CSS strings rendered with set:html refresh correctly.
  • Builds on the Astrobot fix from #16783, with additional coverage for the ?raw import 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?raw imports going through SSR invalidation instead of the normal CSS HMR skip path.
  • pnpm -C packages/astro run build:ci
  • pnpm -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.

withastro/astro

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.

withastro/astro

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 1b39ae8 Thanks @narendraio! - Prevents App.match() from throwing on request paths that contain an invalid percent-sequence.

  • #16924 2c0bc94 Thanks @astrobot-houston! - Fixes an issue where editing a client-side component (e.g. with client:idle, client:load, etc.) caused an unnecessary full program reload of the backend during development.

  • #16958 2c1d50f Thanks @fkatsuhiro! - Fixes a bug where static file endpoints using getStaticPaths with .html in dynamic param values (e.g. { path: 'file.html' }) would fail with a NoMatchingStaticPathFound error during build. The .html suffix is no longer incorrectly stripped from endpoint route pathnames.

  • #16855 c610cda Thanks @astrobot-houston! - Fixes dynamic routes returning 500 "TypeError: Missing parameter" when using domain-based i18n routing in SSR.

  • #16946 606c37b Thanks @ematipico! - Fixes Astro.routePattern to preserve original casing of dynamic parameter names from filenames. Previously, a file at src/pages/blog/[postId].astro would return /blog/[postid] for Astro.routePattern due to an internal .toLowerCase() call. It now correctly returns /blog/[postId].

  • #16720 16d49b6 Thanks @thomas-callahan-collibra! - Fix an issue where dynamic routes would return the string [object Object] instead of the expected content, in certain runtimes.

  • #16703 17390a6 Thanks @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. running astro dev from d:\dev\app while the folder on disk is D:\dev\app).

  • #16855 c610cda Thanks @astrobot-houston! - Fixes Astro.currentLocale returning the default locale instead of the domain's locale on dynamic routes served from a mapped domain.

@astrojs/mdx@6.0.2

Patch Changes

@astrojs/markdown-satteri@0.2.2

Patch Changes

withastro/astro

Changes

Closes #16945

We create a new request and apply the correct symbols.

Testing

Added new unit tests

Docs

withastro/astro

Changes

Fixes an issue where routePattern was transformed into lower case

Closes #16942

Testing

Tests added by the bot.

Docs

withastro/astro

Fixes #16078

Summary

  • include tsconfig-matched .astro files in the TypeScript plugin project
  • inject the installed Astro package's env.d.ts and astro-jsx.d.ts into the TS plugin host
  • add focused unit and VS Code fixture coverage for Astro.locals references

Verification

  • pnpm -C packages/language-tools/ts-plugin build
  • pnpm -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