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

Changes
@astrojs/cloudflare@14.3.0 imports renderForPrerender from astro/app, but its astro peer range is still ^7.2.0. That export was added in Astro 7.3.0, so npm/pnpm install 14.3.0 on top of Astro 7.2.x with no peer warning, and the build fails:
[MISSING_EXPORT] "renderForPrerender" is not exported by "node_modules/astro/dist/core/app/entrypoints/index.js".
╭─[ node_modules/@astrojs/cloudflare/dist/utils/prerender.js:1:10 ]
1 │ import { renderForPrerender } from "astro/app";
Verified against the published tarballs:
| package | renderForPrerender |
|---|---|
astro@7.2.4, astro@7.2.10 |
not exported from dist/core/app/entrypoints/index.js |
astro@7.3.0 |
exported |
@astrojs/cloudflare@14.2.6 |
not imported |
@astrojs/cloudflare@14.3.0 |
imported in dist/utils/prerender.js |
Both sides were introduced together in #17795 (core + adapter) and released as astro@7.3.0 / @astrojs/cloudflare@14.3.0, but the peer range wasn't bumped. CI doesn't catch this because the monorepo resolves astro as workspace:*, so the declared range is never exercised.
This is easy to hit in practice: wrangler deploy auto-config runs astro add cloudflare on a project whose lockfile pins Astro 7.2.x, installs 14.3.0, and the deploy fails.
Testing
Peer-range-only change; no runtime behavior affected.
Docs
No docs change needed.

Changes
- A dev toolbar app's UI is destroyed on the first client-side navigation and never restored — silently, with no error and
app.statusstillready. DevToolbarCanvas.connectedCallback()assignsthis.shadowRoot.innerHTML, which replaces rather than appends, discarding whatever the app rendered into its canvas.- The canvas reconnects on every navigation.
- Nothing rebuilds it —
initApp()sits behind thehasBeenInitializedguard, soapp.init()never runs a second time. - Fix: write the style once in the constructor and drop
connectedCallback, which had no other job. - Only
DevToolbarCanvasis affected.DevToolbarWindowprojects through a<slot>andDevToolbarTooltiprebuilds from its attributes. - Changeset included (
patch).
| Before | After |
|---|---|
![]() |
![]() |
Testing
- Added
third-party app content survives a client-side navigationtopackages/astro/e2e/dev-toolbar.test.ts. No new fixture needed — the existingdev-toolbarfixture already has a third-party app, a<ClientRouter />layout, and linkedview-transition-a/bpages. - Confirmed the test catches the bug: passes with the fix, fails without it (
toHaveCount: Expected 1, Received 0), passes again once restored.
Docs

Changes
- Adds the
:disableDependencyDashboardpreset to.github/renovate.json5.config:recommendedenables the dashboard by default. So this disables it. - Issue not needed, you can go to https://developer.mend.io/
Testing
- N/A
Docs
- N/A

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.3.1
Patch Changes
- #17899
0389640Thanks @ematipico! - Fixes an error that prevented projects usingastro:assetsfrom starting or building


Changes
- MDX rendering treated any plain-string
<script>/<style>child as raw, unescaped markup, including a dynamic value like<script>{value}</script>.- Now this is escaped by default.
- Literals get collapsed into a
set:htmlattribute at compile time. This allows remark plugins to continue to work.
Testing
- Adds an MDX fixture/tests (
script-style-dynamic.mdx) covering dynamic<script>/<style>children
Docs
- N/A, bug fix.
This PR is aimed to temporarily fix formatting.
It’s currently broken due to a bug in Prettier’s parsing of the PageFrame.astro component. It does not like the JS class inside a <script> inside a conditional expression here:
starlight/packages/starlight/src/components/PageFrame.astro
Lines 9 to 42 in 726a99b
| { | |
| hasSidebar && ( | |
| <nav class="sidebar print:hidden" aria-label={Astro.locals.t('sidebarNav.accessibleLabel')}> | |
| <MobileMenuToggle /> | |
| <script> | |
| // The mobile menu uses the popover API to open and close without JavaScript. We enhance this | |
| // behaviour with focus trapping while the menu is open. | |
| class StarlightSidebarPane extends HTMLElement { | |
| constructor() { | |
| super(); | |
| // Close the menu when the viewport is resized between mobile and desktop sizes. | |
| matchMedia('(min-width: 50em)').addEventListener('change', () => this.hidePopover()); | |
| // Update the menu state when the sidebar popover is toggled. | |
| this.addEventListener('toggle', ({ newState }) => this.trapFocus(newState === 'open')); | |
| } | |
| /** Trap keyboard focus inside the header and menu when the menu is expanded. */ | |
| trapFocus(shouldTrap: boolean) { | |
| document.querySelectorAll<HTMLElement>('.main-frame, .sl-skip-link').forEach((el) => { | |
| el.toggleAttribute('inert', shouldTrap); | |
| }); | |
| } | |
| } | |
| customElements.define('sl-sidebar-pane', StarlightSidebarPane); | |
| </script> | |
| <sl-sidebar-pane popover="" id="starlight__sidebar" class="sidebar-pane"> | |
| <div class="sidebar-content sl-flex"> | |
| <slot name="sidebar" /> | |
| </div> | |
| </sl-sidebar-pane> | |
| </nav> | |
| ) | |
| } |
As a workaround, this PR adds PageFrame.astro to our .prettierignore so it is skipped by formatting. We can remove this later in #4175.

Changes
astro/app/manifestand@astrojs/cloudflare/cache/providerare now pre-declared in theoptimizeDeps.includearray of the@astrojs/cloudflare:environmentplugin. Without these entries, Vite discovers the modules mid-startup or mid-request, triggers a re-optimization, and workerd crashes referencing stale pre-rewrite chunk filenames — resulting inDev server process exited before becoming readyon every cold start.astro/app/manifestis added unconditionally alongside the existingastro/appentry (regression from PR #17787 which changed the import specifier invite-plugin-routes).@astrojs/cloudflare/cache/provideris added conditionally whenneedsWorkerCacheis true, mirroring the existing pattern used forastro/logger/json.
Testing
- Two regression tests added to
packages/integrations/cloudflare/test/typegen-phase.test.ts: one assertingastro/app/manifestis always present in the SSRoptimizeDeps.includelist, and one asserting@astrojs/cloudflare/cache/provideris present when Workers Caching is enabled.
Docs
- No docs update needed; this is an internal adapter configuration fix with no user-facing API change.
Closes #17893
Description
This PR updates the Astro MDX integration to v8, dropping a bunch of dependencies.
It also requires bumping the minimum supported Astro version from 7.2.5 to 7.2.10 and the dependencies for Sätteri/Remark integration too. We were already bumping these for the current release, so I’ve updated that changeset to reflect this.
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-docsearch@0.8.0
Minor Changes
- #3572
292fb17Thanks @HiDeoo! - Distributes package as JavaScript files with dedicated type declaration files instead of TypeScript source files.
@astrojs/starlight@0.42.0
Minor Changes
-
#3572
292fb17Thanks @HiDeoo! - Distributes package as JavaScript files with dedicated type declaration files instead of TypeScript source files. -
#4121
2623ae6Thanks @delucis! - Simplifies markup for Starlight’s mobile menu toggle⚠️ Potentially breaking change: If you use a theme plugin, custom styles, or component overrides targeting theMobileMenuTogglebutton orPageFramecomponents, you may need to adjust these for the new markup. The button is no longer wrapped in a<starlight-menu-button>custom element and no longer uses thearia-expandedattribute. Instead, you can use the.sl-menu-buttonclass name to target the button and the:popover-openpseudo-class to style the menu open state specifically.In the following example, custom styles for the menu button are updated for the new approach:
- starlight-menu-button button { + .sl-menu-button { color: var(--sl-color-text); } - starlight-menu-button[aria-expanded='true'] button { + .sl-menu-button:has(~ :popover-open) { color: var(--sl-color-text-accent-high); }
See
MobileMenuToggle.astroandPageFrame.astroon GitHub for the full source code of the updated components. -
#3572
292fb17Thanks @HiDeoo! - Removes thetaglineconfiguration option, which was never used.If your configuration included a
taglineoption, you can safely remove it without any replacement. -
#4134
6135f01Thanks @HiDeoo! - Updates internal@astrojs/mdx,@astrojs/markdown-satteri, andsatteridependencies.⚠️ BREAKING CHANGE: The following minimum versions are now required:astrov7.2.10 or later@astrojs/markdown-satteri0.4.0 or later (if you use it)@astrojs/markdown-remark7.3.0 or later (if you use it)
Please update Starlight and Astro together:
npx @astrojs/upgrade
-
#4121
2623ae6Thanks @delucis! - Refactors Starlight’s mobile menu toggle to work when JavaScript fails or is disabled⚠️ BREAKING CHANGE: This release drops official support for Chromium-based browsers prior to version 116 (released August 2023), Safari-based browsers prior to version 17.0 (released September 2023), and Firefox prior to version 125 (released April 2024). You can find a list of currently supported browsers and their versions using this browserslist query.This change also removes the
data-mobile-menu-expandedattribute, which was previously added to<body>while the mobile menu is open. If you have custom code that was depending on this attribute, you will need to update it to use a new selector to check if the mobile menu is open.In the following example, a custom background colour for the site header while the menu is open is updated for the new approach:
- [data-mobile-menu-expanded] header { + body:has(sl-sidebar-pane:popover-open) header { background-color: var(--sl-color-bg); }

Changes
Rewrites @astrojs/solid-js for Solid 2.0 as a new major (v8), following the @astrojs/svelte precedent for major framework transitions: v8 supports Solid 2.0 only (solid-js@^2.0.0-rc.5 + @solidjs/web@^2.0.0-rc.5 — caret ranges, so 2.0 stable satisfies them when it ships); v7 remains for Solid 1.x.
Core (first commit)
- Compiles through
@solidjs/vite-plugin(replacingvite-plugin-solid), with acompileroption for the native (Oxc) or Babel backend. - Islands render through Solid 2.0's first-class async model (
renderToStream, no Suspense wrapper): async islands settle fully on the server;Erroredboundaries contain sync and async errors. - Threads an asset manifest into server renders so
lazy()boundaries inside islands resolve module URLs, hydrate through serialized asset maps, and get their CSS inlined into island markup (deduped per page) — a live dev resolver in dev, the client build manifest in production (captured before the.vitecleanup for prerendering; persisted next to the server bundle for deployed SSR). check()probe-renders through the stream renderer with per-component caching, rejects foreign framework vnodes explicitly, and is Proxy-safe (fixes the class of issues behind #12354).- Drops the
solid-devtoolsoption (its 2.0 story is unsettled). - All Solid fixtures ported to 2.0; the previously-skipped
solid-componentsuite is re-enabled with new lazy-CSS coverage. astro add solid-jstsconfig preset now writesjsxImportSource: "@solidjs/web"(2.0 moves JSX types to renderer packages).
Server functions (experimental, second commit)
solid({ serverFunctions: true }) compiles "use server" functions in islands to typed RPC calls. The transport endpoint (default /_server) is injected as an Astro route, so requests flow through Astro's middleware pipeline — same auth guards and locals — in dev and production alike; getRequestEvent() inside a function exposes the request, Astro locals, and the API context. Island SSR runs inside a request-event scope, so direct in-process calls work during rendering. Requires an adapter (build fails with a clear error otherwise).
Server components (experimental, third commit)
serverFunctions: { components: true } enables "use server" functions returning components. They SSR inline in islands, are adopted at boot with zero endpoint requests, and later re-evaluations stream over the endpoint and morph in place, preserving client slot state and DOM identity.
Testing
- e2e:
solid-component,solid-circular,solid-recurse,nested-in-solid, plus a newsolid-server-functionssuite (endpoint round trip, middleware locals through the request event, t=0 server-component adoption + morph) — 26 tests. - unit:
solid-component(13, includes 3 pre-existing assertion bug fixes),slots-solid,react-and-solid,astro-slots-nested.
Notes for review
- Versioning:
package.jsonis set to8.0.0-beta.0with amajorchangeset; happy to adjust to whatever release flow you prefer (Solid 2.0 itself is in RC). examples/framework-solidis intentionally untouched: its component code is already 2.0-compatible, and its published-rangepackage.json(create-astro templates install from the registry) should bump at release, adding@solidjs/web(2.0 compiled output imports it).pnpm-workspace.yamladds@solidjs/*/solid-jstominimumReleaseAgeExcludeso freshly published RCs install past the age gate — needs a policy sign-off.- No existing Solid 2.0 issues/PRs found in the tracker; this is the first migration work filed.
Made with Cursor

Changes
- Fixes the dev server base middleware stripping the configured
basefrom any request whose pathname merely starts with it as a string, ignoring path-segment boundaries. - With
base: '/s', a request like/src/pages/index.astrowas treated as being under the base and rewritten to/rc/pages/index.astrobefore reaching Vite, so it could never resolve. Anybasevalue that is a non-segment prefix of a served path triggers this (/svs/src); bases like/docsdon't collide and hide the bug. - This is the same bug class #17701 fixed for
App.removeBase,FetchState, and the i18n domain helper: the dev middleware (evaluateBaseRewriteinvite-plugin-astro-server/base.ts) was the remaining spot still doing a plainstartsWithcheck. It now strips the base only when the pathname is exactly the base or lies underbase + '/', matching the policy ofstripRequestBase. - Only the match condition changes. The rewrite itself (an
url.replacethat preserves query strings), the exact-base-match behavior (/s→/), and thenot-found-subpath/not-found/check-publicbranches are unchanged.stripRequestBaseisn't reused directly because it never strips a/base, while this middleware is always installed and must keep passing every request through whenbaseis/.
Reproduction
-
pnpm create astro@latest(minimal template) -
Set
base: '/s'inastro.config.mjs -
Run
astro dev, then:curl -i "http://localhost:4321/src/pages/index.astro"Before this fix the request 404s because the middleware rewrote it to
/rc/pages/index.astro; after it, Vite serves the module. In practice this broke the root-relative requests the dev server itself emits without the base, taking down every page.
Testing
Added unit tests to test/units/dev/base-rewrite.test.ts covering the segment-boundary cases: with base: '/s', /src/... requests are no longer rewritten while /s (exact) and /s/... still are, query strings are preserved, and /docs-archive/page with base: '/docs' falls through to not-found.
Also verified manually against examples/minimal with base: '/s': requests to /src/... now pass through to Vite untouched, pages under /s/ and the base root /s render, public assets under the base resolve, and URLs outside the base still 404.
Docs
No docs needed: this restores the documented behavior of base in dev; no user-facing API changes.
🤖 Generated with Claude Code

Changes
- Restores cookies, sessions, cache defaults, asset fallback, and prerendering for Cloudflare custom entrypoints. Fixes #17600.
- Adds explicit
finalize(state, response)handling forastro/fetch;- Using the hono middleware this is applied manually.
- Replaces #17708
Testing
- Expands custom-entrypoint coverage for Fetch, Hono, errors, assets, sessions, and prerendering.
Docs
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.41.11
Patch Changes
-
#4167
32a5e29Thanks @HiDeoo! - Fixes a layout issue and anchor links appearing for hidden headings, e.g. footnote headings, whenmarkdown.headingLinksis enabled or the<AnchorHeading>component is used. -
#4148
cdfafd8Thanks @ematipico! - Optimizes sidebar data generation logic to speed up sites with large sidebars

Changes
- Skips storing memory-cache responses when
VarycontainsCookieor*, rather than dropping those values during cache-key construction. - Removes stale entries when background revalidation returns an unsupported variant.
Testing
- Updates
Varycoverage forCookie,*, and combined header values. - Adds regression coverage for stale-while-revalidate entries.
Docs
- A follow-up docs update is needed to clarify how the memory provider handles these
Varyvalues.

Changes
This function runs for every single page module and does a few expensive call, we can do them only once and re-use the map for lookups.
On a benchmark where pages are not very expensive, but there's a lot of them, this roughly cut the build time in half.
Testing
Tests should pass
Docs
N/A
Description
- Closes #4165
This PR fixes an issue with anchor links and hidden headings (e.g. footnote headings).
This PR does not use the approach from #4166 to prevent heading links to be added at the Markdown processor level:
- A user could have some custom CSS or client-side JS to show such headings at some point in time
- The issue also appears when using the
<AnchorHeading>component
Instead, this PR uses CSS to properly hide anchor links for hidden headings.
Remaining tasks
- Address all
TODO(HiDeoo)comments

Changes
- Adds a
keepAliveTimeoutoption to@astrojs/node, applied to the standalone server'shttp.Serverbefore it starts listening. - Leaving it unset keeps today's behavior exactly (whatever Node.js defaults to).
export default defineConfig({
adapter: node({ mode: 'standalone', keepAliveTimeout: 65_000 }),
});Why
Node.js destroys an idle keep-alive connection after 5 seconds by default (server.keepAliveTimeout), which is shorter than the idle timeout of most reverse proxies and load balancers — an AWS Application Load Balancer, for instance, defaults to 60 seconds (idle_timeout.timeout_seconds). AWS names this exact case in its ALB troubleshooting guide for HTTP 502: the target closed the connection with a TCP RST or FIN while the load balancer still had an outstanding request to it, and the guide tells you to check whether the keep-alive duration of the target is shorter than the load balancer's idle timeout.
The standalone adapter calls http.createServer(listener) and never touches keepAliveTimeout, so every standalone deployment behind a proxy sits on the wrong side of that ordering. The symptom is easy to misread: connections only stay idle long enough to reach the 5 second window when the server is quiet, so the 502 rate goes up as traffic goes down, and a busy deployment can look completely healthy.
There is currently no supported way for a user to configure this:
- Adapter options are JSON-serialized into a virtual module (
vite-plugin-config.ts), so a callback such asconfigureServer(server)cannot be passed through. - In standalone mode the built entry starts the server as a side effect of being imported, so there is no window in which to configure it.
- What is left is
ASTRO_NODE_AUTOSTART=disabledplus a custom launcher that importsstartServer()and mutatesserver.server.keepAliveTimeout— depending on the shape of the adapter's return value, and assigning afterlisten().
Prior art: Next.js exposes this as a first-class flag for the same reason (next start --keepAliveTimeout <ms>, assigned to server.keepAliveTimeout in its start-server). Node.js itself has since raised the default to 65 seconds on main (nodejs/node#62782) — but that is semver-major and unreleased, and every current release line still ships 5000.
I kept the scope to the single option behind the 502s. Happy to extend it to headersTimeout / requestTimeout if you'd prefer a broader surface.
Validation
The option crosses a JSON boundary on its way to the built entrypoint (vite-plugin-config.ts serializes adapter options into a virtual module), and JSON.stringify maps Infinity and NaN to null. Node then assigns anything to keepAliveTimeout without complaint, and gates on > 0 — so both values would have disabled the timeout, the exact opposite of the option's purpose, with no warning. Measured on Node v24.16.0:
| value | Keep-Alive response header |
|---|---|
| unset | timeout=5 |
65000 |
timeout=65 |
0 |
absent — timeout disabled (documented) |
Infinity / NaN → serialized as null |
absent — timeout disabled (now rejected) |
-1 |
timeout=-1 (now rejected) |
So the adapter rejects non-finite and negative values at build time, next to the existing mode check. This follows @astrojs/vercel, which validates maxDuration the same way.
Open question
createServer(listener, host, port, keepAliveTimeout?) adds a fourth positional parameter. It keeps preview.ts compiling unchanged, but the obvious follow-ups to this issue (headersTimeout, requestTimeout) would each add another slot. Happy to switch to an options bag — createServer(listener, host, port, { keepAliveTimeout }) — if you'd prefer; I left it positional to keep the diff small.
astro preview deliberately does not apply the option, and the JSDoc says so. Preview isn't sitting behind a load balancer, and threading it through would mean reading the built config out of the imported server module. Say the word if you'd rather have it consistent.
Testing
packages/integrations/node/test/keep-alive-timeout.test.ts builds a fixture in standalone mode and asserts what reaches the wire:
✔ advertises the configured timeout to clients → Keep-Alive: timeout=65
It is asserted on the response header rather than on server.keepAliveTimeout so it covers the whole path — adapter option → virtual config module → built entrypoint → http.Server — and because the header is what a reverse proxy actually reads. I verified it fails for the right reason: removing the argument at standalone.ts:32 turns it red with timeout=5.
packages/integrations/node/test/units/keep-alive-timeout.test.ts covers the unit level:
✔ keeps the Node.js default when the option is not set
✔ applies the configured value to the underlying server
✔ rejects -1
✔ rejects Infinity
✔ rejects NaN
✔ allows 0, which disables the timeout
The first case is asserted against a freshly created http.createServer() rather than a hardcoded 5000, so it survives Node changing its own default.
The rest of the package's suite passes (pnpm --filter @astrojs/node test); the only failure in my run was preview-host.test.ts losing port 4321 to an unrelated process on my machine, which fails identically on a clean checkout.
Docs
This adds a user-facing adapter option, so it needs an entry alongside the other @astrojs/node options. I'm glad to open the PR on withastro/docs — just say the word.
Last fetched: | Scheduled refresh: Every Saturday
See Customizing GitHub Activity Pages to configure your own
Inspired by prs.atinux.com

