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

Description
Adds a type-safe \ runcateMiddle(str, maxLength)\ string formatting helper function to \packages/astro/src/core/util.ts\ for cleanly formatting long file paths, route names, and component identifiers in Astro's build output.
Features
- Middle string truncation with ...\ ellipsis.
- Preserves head and tail context for readability.

Fixes #17484
Changes
- Fixes the dev server watcher's
matchesGlobfunction incorrectly treating negation patterns (!docs/drafts/**) as positive match conditions, causing unrelated files under the base directory to be ingested as content entries - Splits the pattern array into positive patterns and negation patterns, passing negations to picomatch's
ignoreoption to align the watcher's filtering semantics withtinyglobby's set-subtraction behavior
Testing
- The existing
handles negative matches in glob patternunit test verifies thattinyglobby-based initial sync correctly excludes negated patterns; the watcher fix changespicomatch.isMatchto match those same semantics
Docs
- No docs update needed — this is a bug fix that aligns runtime behavior with documented glob semantics

Fix Cloudflare pre-optimizing for internal helpers' picomatch dependency
Issue & Repro
- See my repro
pnpm why picomatchshows that@astrojs/internal-helpershas its own copy of picomatch which isn't in theoptimizeDepslist- This copy can then cause the dev server to fail a la #15796 because picomatch uses CJS
require()internally - Running
pnpm dev --forceon the repro gives the following error locally, stemming from the internal helper copy of picomatch:
require is not defined
Stack trace:
at runInRunnerObject (workers/runner-worker/index.js:107:3)
at null.<anonymous> (workers/runner-worker/index.js:350:37)
ELIFECYCLE Command failed with exit code 1.
Changes
- This quick fix therefore adds
'astro > @astrojs/internal-helpers > picomatch'to theoptimizeDepslist inpackages/integrations/cloudflare/src/index.ts
Testing
- Haven't added any further tests - the existing test for picomatch preoptimizing is in
packages/integrations/cloudflare/test/with-react.test.ts
Docs
- No docs added, this is fix is tiny

Changes
Fixes #17298
In server output, the prerender and SSR environments each bundle shared layouts separately, so the same stylesheet is emitted once per environment under different names (e.g. index.X.css and _..Y.css), both of which end up in dist/client/_astro/.
This PR makes the prerender build record its emitted CSS asset filenames keyed by each chunk's set of CSS source modules. The SSR build (which runs second) then renames its own CSS assets to the prerender filename when they are backed by the exact same CSS modules. Both environments then write the same filename, and the existing asset move to the client directory naturally collapses them into a single file referenced by both the prerendered HTML and the server-rendered pages.
Two implementation notes:
- Dedup key is module identity, not content hash. The earlier exploration on
triagebot/fix-17298used hash-only asset naming, which dedupes only when the two files are byte-identical. As the reporter noted after 7.0.6, the two files are now merely similar: plugins that scan each environment's module graph (Tailwind v4 via@tailwindcss/vite) pick up extra utility candidates from server-only modules (I traced.underline/.italicto strings in astro's bundled server runtime and.relative/.container/.lowercaseto the node adapter), so the SSR rendition contains utilities the prerender rendition doesn't. Keying by CSS source module set dedupes both the identical and the divergent case; the divergent utilities are scanner false positives from bundled JS, while all real class usage comes from source files both environments scan identically. - The rename mutates
asset.fileNamein place instead of deleting and re-adding the bundle entry: thegenerateBundlebundle object is a Rolldown proxy that silently ignores direct key assignment (verified empirically —bundle[newName] = assetleavesbundle[newName]undefined). MutatingfileNamere-keys the proxy and the writer emits byfileName, so the Vite manifest,ssrAssetsPerEnvironmenttracking, andpagesToCssall stay consistent with no further changes.
Testing
- New fixture + test
css-server-output-dedup: server output + test adapter + prerendered page and[...id]dynamic page sharing a layout,inlineStylesheets: 'never'. Asserts a single CSS file is emitted and that both the prerendered HTML and the server-rendered response link that same file. Fails onmain(two files, dynamic page linking its own copy), passes with this change. - All existing CSS suites pass:
test/*css*.test.ts→ 117 pass / 0 fail / 1 pre-existing skip, plustest/units/build/**→ 0 fail. - Verified against the original reproduction shape (Tailwind v4 +
output: 'server'+ node adapter + prerenderedindex+ dynamic route) using this branch:dist/client/_astro/goes from two similar files (7131 + 6019 bytes) to a single file, referenced by both the static HTML and the serialized manifest indist/server/entry.mjs.
Docs
N/A — build output bugfix, changeset included.
🤖 Generated with Claude Code

Changes
create-astro already detects nub as the active package manager (from npm_config_user_agent) and drives it correctly for install and astro add via the generic passthrough and nub dlx. Two printed run commands were still wrong for nub:
next-steps.ts— thecommandMaphad nonubentry, so nub users fell back tonpm run dev. Addednub: 'nub run dev'.template.ts(processTemplateReadme) — the non-npm path replacednpm runwith the bare package-manager name, turningnpm run devintonub dev. nub has no implicit script shortcut, so that is invalid. It now mapsnpm run <script>tonub run <script>, keeping the explicitrun.
pnpm/yarn/bun output is unchanged. Changeset included.
Testing
Verified the code path a nub user hits: detection yields packageManager === 'nub', commandMap.nub returns nub run dev, and processTemplateReadme rewrites npm run dev to nub run dev while leaving bare npm (e.g. npm install -> nub install) and the pnpm/yarn/bun branches untouched.
Docs
No docs change needed — this only corrects the package-manager commands create-astro prints for an already-detected manager.

Changes
- Restores
getFallbackas a named export fromastro:transitions/client. The function was still present in the source but was dropped from the explicit named-export list in the Vite virtual module and the TypeScript declaration file when PR #9090 switched fromexport *to an explicit list. Users following the official docs would hit both ats(2724)type error and a build-time "Missing export" error.
Testing
- No new tests added. The fix is a two-line addition to the named export list; all existing transition tests continue to pass.
Docs
- No docs change needed —
getFallbackis already documented at https://docs.astro.build/en/reference/modules/astro-transitions/#getfallback.
Closes #17482

Changes
Fixes #17408
- I ran into the same 500 as the issue describes: with the cache provider enabled, the second request to
/_image(a Workers cache hit) fails with "Can't modify immutable headers". - Responses from
caches.default.match()have immutable headers, and the request handler mutates them in place when it applies its defaultCloudflare-CDN-Cache-Control: no-storeheader. - I changed the handler to rebuild the response (
new Response(response.body, response)) when the mutation throws, and apply the headers again. The first mutation throws before changing anything, so nothing gets duplicated on the retry. - I also had to collect
Set-Cookieheaders before the rebuild, becausesetCookieHeaderslooks the response up by identity.
Testing
I added a binding-image-cache fixture (image binding + cache provider) with a test that fetches the same /_image URL twice. Before the fix the second request returned 500, now both return 200. The full adapter test suite passes for me locally (272 tests).
Docs
Bug fix only, no docs needed.

Changes
- In #17389 I only added support for URLs to keep things simple but I noticed relative string entrypoints would be harder
- The problem is that the loading is runtime based which is not really standard (eg. sessions drivers). I think I've spotted an error in production because of that when using URLs
- Claude refactored the loading logic to support it
Testing
Test added, all tests should pass
Docs
- Changeset
- withastro/docs#14295

Changes
When running npm create astro@latest and then using . for the output directory, Astro generates a package.json with an empty "" package name. In contrast, running npm create astro . uses the current working directory's basename.
This PR makes both invocation styles behave consistently by always deriving the package name from the generated project's basename. This behavior is also consistent with Vite's, Nuxt's, and Next's npm create commands.
Before / After
Testing
I tested locally and added tests for npm create astro with dot for the project location prompt.
Docs
Sufficient docs exist for the create astro command.
Description
- Closes #4051
- Currently we special case
http(s)://links in the sidebar and treat everything else as a relative link, prefixing locales, slashes, etc. - This PR switches to check if the link contains any protocol component instead of only checking for an HTTP protocol so that protocols like
mailto:or even app protocols likevscode://are supported. The implementation may look a bit funny but is essentially doing/^[^\/]*:/.test(), i.e. checking if a colon is present in the first segment of a link (/foo:baris a valid relative link, butfoo:baris treated as a protocolfoo:). This implementation seems to be an order of magnitude faster than the current regular expression we use. I ran a little benchmark that showed this running about 15x faster than the regular expression, which may matter given sidebars can be a performance-sensitive area.- There is still one use of
isAbsoluteUrl()in the code base, but it’s for the favicon URL, so I’ve left that as is — other protocols aren’t relevant thereand it isn’t performance sensitive in the same way the sidebar is.

There is a high security advisory on js-yaml that is resolved by v4.3.0: GHSA-52cp-r559-cp3m
I see that updating to v5 series might take some time, so in the meantime it would be valuable to update to 4.3.0 to resolve the security advisory if possible.
Here is Claude's summary of the observable changes to end-users as a result of updating to v4.3.0:
- Numbers with underscores no longer parse as numbers. count: 1_000 in frontmatter previously produced the number 1000; it now produces the string "1_000". This is js-yaml aligning with the YAML 1.2 spec, but it's a silent type change for any user relying on the old (nonstandard) behavior.
- Stricter rejection of malformed input. Top-level block scalars without content indentation are now rejected, and YAML nested deeper than 100 levels now throws (the new maxDepth default). Documents that previously loaded — even if technically malformed — can now error.
- Edge-case parse output changes from the correctness fixes: implicit block mapping key property parsing, trailing-whitespace folding in folded/flow scalars. Documents that were previously misparsed will now parse differently (correctly, but differently).
- Merge-key limits could theoretically reject documents with pathological numbers of << merges, though anything hitting those limits was likely a DoS vector anyway.

Changes
- Adds
background?: stringtoImageSharedPropsinpackages/astro/src/assets/types.ts, making thebackgroundprop visible to TypeScript when using<Image />and<Picture />. The prop already worked at runtime (props are spread intogetImage()), but was missing from the type, causingastro checkto reportProperty 'background' does not exist on type 'IntrinsicAttributes & Props'.
Testing
- No new test cases added; this is a type-level omission. Existing type tests pass with the fix, and
astro checkon a repro project reports 0 errors.
Docs
- No docs update needed. The
backgroundprop is already documented forgetImage()and<Image />on docs.astro.build; the component just lacked the corresponding type.
Closes #17471

Changes
Adds a cron workflows that purges all stale flue/* branches. Stale branches are the ones there its last commit is older than two months.
Testing
I tested the script locally with a token of mine, and it pulls the branches.
We can use the dry-run after merge
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.1.4
Patch Changes
-
#17472
4dc590cThanks @astrobot-houston! - Adds the missingbackgroundprop to the<Image />and<Picture />component types. The prop already worked at runtime, but was absent from the types, causingastro checkto report thatbackgrounddoes not exist on the component props -
#17421
f1448deThanks @iamkaleemsajjad-hue! - Fixes session runtime errors being silently swallowed byconsole.errorinstead of routing through Astro's logger -
#17421
f1448deThanks @iamkaleemsajjad-hue! - Fixes a session being left in a partial state after a storage failure duringsession.regenerate(), preventing unnecessary storage reads on subsequent operations
create-astro@5.2.3
Patch Changes
- #17423
08e8adbThanks @astrobot-houston! - Fixescreate-astrosilently writing template files to the wrong directory on Linux when the path contains non-ASCII characters.
@astrojs/markdoc@2.0.5
Patch Changes
-
#17460
3b93a1aThanks @astrobot-houston! - Fixes customtransformfunctions being dropped when a tag or node also specifies a customrendercomponent. User-written transforms are now always preserved; only Markdoc's built-in transforms are removed so the custom component wins. -
#17191
fc3fb2bThanks @eldardada! - Fixes customtransformfunctions being incorrectly dropped for tags and nodes whose names require bracket access (e.g.side-note). The check that detects whether a transform respects a customrendercomponent now recognizes bracket notation, optional chaining and whitespace, not only dot notation.
@astrojs/check@0.9.10
Patch Changes
- #17447
b01a692Thanks @ocavue! - Update dependencyyargsto version 18. See the yargs changelog for details.

When <style lang="sass"> (or any non-CSS/JS language) has the lang attribute on a different line than the opening <style tag, the TextMate grammar failed to detect the language and fell back to CSS/JS highlighting.
Root cause: The lang detection patterns in tags-lang used \G anchors, which can only match on the same line as the begin pattern. Multi-line attributes bypassed detection entirely, and the fallback #tags-lang-start-attributes would then take over permanently.
Fix:
- Changed
\G→(?:\\G|\\s+)in all three lang detection patterns so they can match on subsequent lines - Added
#tags-lang-fallback-start-attributeswith a restrictedbeginthat only matches lines containing>or/>(the last line of the tag open) - Used this restricted fallback instead of the original
#tags-lang-start-attributeswhich matched unconditionally
This ensures that multiline lang/type attributes are detected on any line, while attributes on the closing line of the tag open still work as before.
Test: Added test/grammar/fixtures/style/sass-multiline.astro snapshot test.
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.4
Patch Changes
-
#3936
712eeddThanks @miichom! - Fixes support for modifying Zod enums when passing anextendoption to Starlight’sdocsSchema() -
#4092
0896b91Thanks @delucis! - Fixes support for links containing a protocol likemailto:in the sidebar -
#4088
4486ba4Thanks @delucis! - Simplifies Starlight’s client-side sidebar state persistence script slightly

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/markdoc@2.0.4
Patch Changes
@astrojs/language-server@2.16.13
Patch Changes
- #17465
6a1c1d8Thanks @florian-lefebvre! - Fixes a case where errors in files included in tsconfig project references would never be caught
Description
- Closes #4083
- Closes #4084
- This PR removes our use of the
pagehideevent in our sidebar state persistence script. - As spotted in #4083 this has never been working as intended due to a typo as
pageHidein the event name. - We could fix the casing, but AFAICT we do not need the event at all:
- It hasn’t been firing for 1 year without anyone noticing.
- The main reason for
pagehidewas due to Safari’s partialvisibilitychangesupport. According to MDN’s data Safari fixed this in April 2021 (v14.1 on desktop, v14.5 on mobile), so we no longer needpagehideto cover those older browsers which fall outside of our browser support matrix. - As noted in #4083, even if it had been cased properly, it wouldn’t have fixed their specific issue. It was just a detail they noticed in passing.
Description
We had to introduce quite a few packages to minimumReleaseAgeExclude when co-ordinating the release alongside Astro v7 but should now be safe to remove this. This change helps protect us from accidentally installing a newer version of one of these packages unintentionally.

Changes
- Enables
includeProjectReferencein the Volar checker so thatastro checkfollows tsconfigreferencesand checks files from all referenced projects - It required updating how
astro-checkis run for theastropackage. I tried a bunch of things and the current state is the only one I managed to make work - Fixes #17464
Testing
- Test added
- Preview release (florian-lefebvre/astro-check-references#1)
Docs
Changeset

Routes session runtime diagnostics through \AstroLogger\ instead of \console.error/\logger.warn\ to respect user logging configuration.
Also resets the internal #partial\ flag after a storage failure during
egenerate()\ to allow subsequent reads to retry storage instead of using stale in-memory data.
Changes
- Added \AstroSessionOptions\ interface with \logger\ field
- Route all \console.error\ calls through \AstroLogger\
- Reset #partial = true\ after storage failure in
egenerate()\ - Updated tests to pass mock logger
Closes #17421

Changes
- Fixes user-written
transformfunctions being silently deleted when a customrendercomponent is specified. The previoustransformRespectsRenderheuristic inspected function source code for an Astro-internal string pattern (config.tags?.X?.render). Any transform not containing that exact string was removed — including valid user transforms usingthis.render. - Replaces the string-matching heuristic with reference equality via
isBuiltinMarkdocTransform, which compares the transform function against the built-inMarkdoc.nodes/Markdoc.tagsentries. Only actual Markdoc built-in transforms are removed; all user-written transforms are preserved.
Testing
- Adds
render-this-context.test.tswith a fixture that usesthis.renderin a tagtransformfunction, confirming props are passed correctly to the rendered component. - Existing
render-with-transform.test.ts(regression for #9708, spread-and-override pattern) continues to pass unmodified.
Docs
- No docs update needed — this restores previously documented Markdoc
transformbehavior.
Closes #17458

Fixes #17456
Changes
- Pre-bundles the Astro Actions server entrypoints and
astro/zodin the server environment'soptimizeDeps.include, avoiding the mid-request dependency discovery that crashed the dev server.
Testing
- No automated test: this is a timing-dependent dep-optimizer race. Verified manually with the reproduction in #17456.
Docs
- No docs update needed; internal dev-server fix with no API change.

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.1.3
Patch Changes
- #17427
630b382Thanks @astrobot-houston! - Fixes image optimization duringastro buildusing too many parallel processes in CPU-limited containers. Builds now respect the container's CPU limit, reducing peak memory usage and avoiding out-of-memory crashes.
@astrojs/cloudflare@14.1.4
Patch Changes

Closes #17404
Changes
-
Fixes build failure caused by satteri being bundled into the workerd prerender environment. A static
import { satteri }inconfig/schemas/base.tswas pulled into the prerender bundle via the Container API's import chain (container → ASTRO_CONFIG_DEFAULTS → base.ts → satteri). Cloudflare'sbrowserresolve condition resolved satteri to its WASM browser entry, which requires@napi-rs/wasm-runtime(not installed). The fix extractsASTRO_CONFIG_DEFAULTSinto a newdefaults.tsmodule (no satteri import) and defers satteri initialization to config resolution time invalidate.ts, which never runs inside the prerender bundle. -
Fixes runtime failures in workerd by removing Node.js built-in dependencies from the Container API.
container/index.tsimportednode:pathdirectly and pulled inesbuild(→node:url) via theclient-directive/index.tsbarrel re-export. Neither is available in workerd withoutnodejs_compat. Fixed by replacingposix.sepwith'/'and importinggetDefaultClientDirectivesfrom the directclient-directive/default.tsinstead of the barrel. -
Fixes masked prerender errors in the Cloudflare adapter. The prerender error handler was placing raw error messages (which can contain newlines) into HTTP headers, causing miniflare to throw
TypeError: Invalid header valueand hide the real error. Newlines are now stripped before the header is set.
Testing
- Added
packages/integrations/cloudflare/test/container-api.test.ts— integration test that builds a project using the Container API with the Cloudflare adapter and verifies the prerendered output is correct. - Updated
packages/astro/test/units/config/config-validate.test.tsto reflect thatmarkdown.processoris nowundefinedaftervalidateConfig(satteri initialization is deferred to the full config resolution step).
Docs
No docs update needed — this is a bug fix for an experimental API with no behavior change for users.


Changes
- Updated all four
@vercel/*dependencies to the latest versions. - For two of them with major version bumping, I've added a Changesets file with changelog links. I do not find any real breaking changes for users based on changelogs.
Testing
CI should pass.
Docs
Changesets file has added because we need to trigger a release to @astrojs/vercel.

Changes
- Updates
cookieto v2 - Migrates
AstroCookiesto the newparseCookie/stringifySetCookieAPI. - Refactored the code by a bit.
The only observable change is the default encode: values made entirely of cookie-safe characters are no longer percent-encoded in Set-Cookie headers, and every value still round-trips exactly as before.
Astro.cookies.set('foo', value) |
Set-Cookie before ( cookie v1) |
Set-Cookie after ( cookie v2) |
|---|---|---|
'bar' |
foo=bar |
foo=bar |
'http://localhost/path' |
foo=http%3A%2F%2Flocalhost%2Fpath |
foo=http://localhost/path |
'hello world' |
foo=hello%20world |
foo=hello%20world |
Changelog (not very useful IMO): https://github.com/jshttp/cookie/releases/tag/v2.0.0
Testing
Added a new test
Docs
Added a Changesets file because there is a tiny behavior change.
I could copy the Markdown table above into the Changesets file too if reviewers believe it would be better.

This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| js-yaml | ^4.1.1 → ^5.2.1 |
Release Notes
nodeca/js-yaml (js-yaml)
v5.2.1
Fixed
- Add
Mapsupport to !!omap (should work whenrealMapTagused)
Security
- Remove quadratic complexity from !!omap
addItem. Regression from v5
(usually not critical, because YAML11_SCHEMA is not default anymore).
v5.2.0
Added
- Added
maxTotalMergeKeys(10000) loader option to limit the total number of
keys processed by YAML merge (<<) across oneload()/loadAll()call. - Added
maxAliases(-1) loader option to limit the number of YAML aliases per
document.
Removed
maxMergeSeqLengthreplaced withmaxTotalMergeKeysfor limiting YAML merge
processing.
Fixed
- Round-trip of integers with exponential form (>=
1e21)
v5.1.0
Added
- Collection tags can finalize an incrementally populated carrier into a
different result value.
Changed
- [breaking]
quoteStylenow selects the preferred quote style; use the
restoredforceQuotesoption to force quoting non-key strings.
v5.0.0
Added
- Added named exports for schemas, tags, parser events and AST utilities.
- Reworked
JSON_SCHEMAandCORE_SCHEMAwith spec-compliant scalar resolution
rules, and addedYAML11_SCHEMA. - Added
realMapTagfor lossless mappings with non-string and complex keys.
Object-based mappings now reject complex keys instead of stringifying them. - Added
dump()transformoption for changing the generated AST before
rendering. - Added
dump()optionsseqInlineFirst,flowBracketPadding,
flowSkipCommaSpace,flowSkipColonSpace,quoteFlowKeys,quoteStyleand
tagBeforeAnchor. - Added formal data layers (events and AST) for modular data pipelines.
- Added low-level parser (to events), presenter and visitor APIs.
- Added the YAML Test Suite to the
test set.
Changed
- See the migration guide for upgrade notes.
- Rewritten in TypeScript and reorganized the public API around flat named
exports. - Reduced the set of exported schemas:
- YAML 1.2 schemas:
CORE_SCHEMA(loader default),JSON_SCHEMA,
FAILSAFE_SCHEMA. YAML11_SCHEMA, a combination of all YAML 1.1 tags (YAML 1.1 does not
specify a schema, only "types").
- YAML 1.2 schemas:
load/dumpdefault behaviour is now specified exactly via schemas:loadusesCORE_SCHEMA, without!!mergeby default.dumpusesYAML11_SCHEMA+CORE_SCHEMAfor the quoting check, to
guarantee backward compatibility by default.
!!setis now loaded as a JavaScriptSet.- Replaced the
TypeAPI with a tags API. Similar, but more precise and
simpler. See examples for details. Tags can be defined via
defineScalarTag(),defineSequenceTag()anddefineMappingTag(), or as a
spread + override of an existing tag. - Renamed
Schema.extend()toSchema.withTags(). - Expanded YAML 1.2 conformance and improved handling of directives, document
markers, block keys, multiline scalars, tag syntax and other things. load()now throws on empty input instead of returningundefined.- Moved browser builds to the
js-yaml/browserexport. - Deprecated the
loadAllsignature with an iterator (still works, but is a
candidate for removal).
Removed
- Removed deprecated
safeLoad(),safeLoadAll()andsafeDump()exports. - Removed
DEFAULT_SCHEMAand the nestedtypesexport. - Removed loader options
onWarning,legacyandlistener. - Removed dumper options
styles,replacer,noCompatMode,condenseFlow,
quotingTypeandforceQuotes. RenamednoArrayIndenttoseqNoIndent.
Formatting and representation are now configured through presenter options,
schemas and tag definitions. See migration guide on how to replace. - Removed support for importing internal files from
lib/.
v4.3.0
v4.2.0
Added
- Added
docs/safety.mdwith notes about processing untrusted YAML. - Added
maxDepth(100) loader option. Not a problem, but gives a better
exception instead of RangeError on stack overflow. - Added
maxMergeSeqLength(20) loader option. Not a problem aftermergefix,
but an additional restriction for safety. - Added sourcemaps to
dist/builds.
Changed
- Stop resolving numbers with underscores as numeric scalars, #627.
- Switched dev toolchains to Vite / neostandard.
- Updated demo.
- Reorganized tests.
dist/files are no longer kept in the repository.
Fixed
- Fix parsing of properties on the first implicit block mapping key, #62.
- Fix trailing whitespace handling when folding flow scalar lines, #307.
- Reject top-level block scalars without content indentation, #280.
- Ensure numbers survive round-trip, #737.
- Fix test coverage for issue #221.
- Fix flow scalar trailing whitespace folding, #307.
- Fix digits in YAML named tag handles.
Security
- Fix potential DoS via quadratic complexity in merge - deduplicate repeated
elements (makes sense for malformed files > 10K).
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about these updates again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.

This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| htmlparser2 | ^10.1.0 → ^12.0.0 |
Release Notes
fb55/htmlparser2 (htmlparser2)
v12.0.0
What's Changed
This release aligns HTML parsing with the WHATWG spec Almost all changes are to HTML mode only — XML mode is unaffected unless noted.
Raw-text & RCDATA tags
<iframe>,<noembed>,<noframes>, and<plaintext>are now raw-text tags, their content is no longer parsed as HTML<textarea>now decodes entities like<title>already did- Self-closing
<script/>,<style/>, etc. now enter their raw-text state (the/is ignored per spec) unlessrecognizeSelfClosingis enabled
SVG & MathML
- Tag names inside
<svg>are case-adjusted per spec (foreignObject,clipPath, etc.) - CDATA sections inside foreign content are treated as text
- Special-tag detection is disabled inside foreign content
- Stray
</svg>/</math>no longer corrupt the parser's context tracking
Comments & declarations
<!-->,<!--->,<!->,<!>now parse as valid comments per spec<?…>and non-DOCTYPE<!…>in HTML mode emit bogus comments instead of being silently dropped<!DOCTYPEhtml>(no space) is recognized as a DOCTYPE- Unclosed comments,
<!DOCTYPE,<?…,<
This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| diff | ^8.0.3 → ^9.0.0 |
Release Notes
kpdecker/jsdiff (diff)
v9.0.0
(All changes part of PR #672.)
-
ES5 support is dropped.
parsePatchnow usesTextDecoderandUint8Array, which are not available in ES5, and TypeScript is now compiled with the "es6"target. From now on, I intend to freely use any features that are deemed "Widely available" by Baseline. Users who need ES5 support should stick to version 8. -
C-style quoted strings in filename headers are now properly supported.
When the name of either the old or new file in a patch contains "special characters", both GNU
diffand Git quote the filename in the patch's headers and escape special characters using the same escape sequences that are used in string literals in C, including octal escapes for all non-ASCII characters. Previously, jsdiff had very little support for this;parsePatchwould remove the quotes, and unescape any escaped backslashes, but would not unescape other escape sequences.formatPatch, meanwhile, did not quote or escape special characters at all.Now,
parsePatchparses all the possible escape sequences that GNU diff (or Git) ever output, andformatPatchquotes and escapes filenames containing special characters in the same way GNU diff does. -
formatPatchnow omits file headers whenoldFileNameornewFileNamein the provided patch object areundefined, regardless of theheaderOptionsparameter. (Previously, it would treat the absence ofoldFileNameornewFileNameas indicating the filename was the word "undefined" and emit headers--- undefined/+++ undefined.) -
formatPatchno longer outputs trailing tab characters at the end of---/+++headers.Previously, if
formatPatchwas passed a patch object to serialize that had empty strings for theoldHeaderornewHeaderproperty, it would include a trailing tab character after the filename in the---and/or+++file header. Now, this scenario is treated the same as whenoldHeader/newHeaderisundefined- i.e. the trailing tab is omitted. -
formatPatchno longer mutates its input when serializing a patch containing a hunk where either the old or new content contained zero lines. (Such a hunk occurs only when the hunk has no context lines and represents a pure insertion or pure deletion, which for instance will occur whenever one of the two files being diffed is completely empty.) PreviouslyformatPatchwould provide the correct output but also mutate theoldLinesornewLinesproperty on the hunk, changing the meaning of the underlying patch. -
Git-style patches are now supported by
parsePatch,formatPatch, andreversePatch.Patches output by
git diffcan include some features that are unlike those output by GNUdiff, and therefore not handled by an ordinary unified diff format parser. An ordinary diff simply describes the differences between the content of two files, but Git diffs can also indicate, via "extended headers", the creation or deletion of (potentially empty) files, indicate that a file was renamed, and contain information about file mode changes. Furthermore, when these changes appear in a diff in the absence of a content change (e.g. when an empty file is created, or a file is renamed without content changes), the patch will contain no associated---/+++file headers nor any hunks.jsdiff previously did not support parsing Git's extended headers, nor hunkless patches. Now
parsePatchparses some of the extended headers, parses hunkless Git patches, and can determine filenames (e.g. from the extended headers) when parsing a patch that includes no---or+++file headers. The additional information conveyed by the extended headers we support is recorded on new fields on the result object returned byparsePatch. SeeisGitand subsequent properties in the docs in the README.md file.formatPatchnow outputs extended headers based on these new Git-specific properties, andreversePatchrespects them as far as possible (with one unavoidable caveat noted in the README.md file). -
Unpaired file headers now cause
parsePatchto throw.It remains acceptable to have a patch with no file headers whatsoever (e.g. one that begins with a
@@​hunk header on the very first line), but a patch with only a---header or only a+++header is now considered an error. -
parsePatchis now more tolerant of "trailing garbage"That is: after a patch, or between files/indexes in a patch, it is now acceptable to have arbitrary lines of "garbage" (so long as they unambiguously have no syntactic meaning - e.g. trailing garbage that leads with a
+,-, orand thus is interpretable as part of a hunk still triggers a throw).This means we no longer reject patches output by tools that include extra data in "garbage" lines not understood by generic unified diff parsers. (For example, SVN patches can include "Property changes on:" lines that generic unified diff parsers should discard as garbage; jsdiff previously threw errors when encountering them.)
This change brings jsdiff's behaviour more in line with GNU
patch, which is highly permissive of "garbage". -
The
oldFileNameandnewFileNamefields ofStructuredPatchare now typed asstring | undefinedinstead ofstring. This type change reflects the (pre-existing) reality thatparsePatchcan produce patches without filenames (e.g. when parsing a patch that simply contains hunks with no file headers).
v8.0.4
- #667 - fix another bug in
diffWordswhen used with anIntl.Segmenter. If the text to be diffed included a combining mark after a whitespace character (i.e. roughly speaking, an accented space),diffWordswould previously crash. Now this case is handled correctly.
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.

This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| @vercel/analytics (source) | ^1.6.1 → ^2.0.1 |
Release Notes
vercel/analytics (@vercel/analytics)
v2.0.1
What's Changed
New Contributors
Full Changelog: vercel/analytics@v2.0.0...v2.0.1
v2.0.0
What's Changed
Breaking Changes
- License changed from MPL-2.0 to MIT (#170)
- Nuxt: introduce module support. If you need to configure it, load
injectAnalytics()from@vercel/analytics/nuxt/runtime(#183)
Features
feat: load dynamic configuration(#184) — analytics config can now be loaded dynamically
Bug Fixes
fix: src and endpoint paths do not work when relative(#186)
Full Changelog: vercel/speed-insights@1.6.1...2.0.0
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.

ℹ️ Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| undici (source) | ^7.22.0 → ^8.8.0 |
Release Notes
nodejs/undici (undici)
v8.8.0
v8.7.0
What's Changed
- test: guard balanced-pool error port lookup by @mcollina in #5463
- perf: optimize http/2 request hot path by @mcollina in #5483
- fix: do not reject open-ended Range values in simpleRangeHeaderValue by @spokodev in #5490
- fix(eventsource): set use-URL-credentials flag by @Ram-blip in #5489
- test: deflake connect-timeout watchdog by @mcollina in #5197
- docs: correct npm script name and JSON syntax in examples by @lenoxfernando in #5496
- fix: reject non-ascii octets in validateCookiePath by @spokodev in #5452
- fix(readable): ignore late consume chunks by @marko1olo in #5375
- fix(h2): destroy the stream on abort instead of relying on close() by @staylor in #5462
- fix: ignore an unparseable Set-Cookie Expires attribute by @spokodev in #5488
- docs: add reproduction guide and update bug report template by @mcollina in #5451
- fix(h2): guard onResponse against a 'response' event delivered after completion by @staylor in #5440
- fix(h2): requeue request on GOAWAY'd session instead of crashing by @staylor in #5453
- fix: add static buildDispatch method to RedirectHandler type definition by @matthieusieben in #5442
- fix: auto-detect HTTP proxy tunneling by @mcollina in #5116
New Contributors
- @spokodev made their first contribution in #5490
- @lenoxfernando made their first contribution in #5496
- @staylor made their first contribution in #5462
Full Changelog: nodejs/undici@v8.6.0...v8.7.0
v8.6.0
v8.5.0
⚠️ Security Release
This release line addresses 8 security advisories. Most are fixed in
v8.5.0; the SOCKS5 pool-reuse issue was fixed earlier in v8.2.0.
Action required: Upgrade to undici 8.5.0 or later.
npm install undici@^8.5.0
Summary
| Advisory | CVE | Severity (CVSS) | Fixed in | Fix commit |
|---|---|---|---|---|
| GHSA-vxpw-j846-p89q | CVE-2026-12151 | High (7.5) | 8.5.0 | 32dbf0b3 |
| GHSA-38rv-x7px-6hhq | CVE-2026-9675 | High (7.5) | 8.5.0 | b4c287b3 |
| GHSA-vmh5-mc38-953g | CVE-2026-9697 | High (7.4) | 8.5.0 | 42d49559 |
| GHSA-hm92-r4w5-c3mj | CVE-2026-6734 | High (7.5) | 8.2.0 | a516f870 |
| GHSA-pr7r-676h-xcf6 | CVE-2026-9678 | Moderate (5.9) | 8.5.0 | cb105d7c |
| GHSA-p88m-4jfj-68fv | CVE-2026-9679 | Moderate (5.9) | 8.5.0 | 5655ea43 |
| GHSA-g8m3-5g58-fq7m | CVE-2026-11525 | Low (3.7) | 8.5.0 | 5655ea43 |
| GHSA-35p6-xmwp-9g52 | CVE-2026-6733 | Low (3.7) | 8.5.0 | 6ea54ef8 |
High severity
WebSocket DoS via fragment count bypass — CVE-2026-12151
GHSA-vxpw-j846-p89q · CWE-400, CWE-770
Fix: 32dbf0b3 websocket: limit the number of fragments in a message (also c5ed7875 handle empty fragments and stream limits)
A malicious WebSocket server can stream a large number of small or empty
continuation frames. Undici enforced a limit on cumulative payload size but did
not limit the number of fragments per message, leading to unbounded memory
growth and denial of service.
- Affected: applications using
new WebSocket(...)orWebSocketStream
against untrusted endpoints. - Workaround: none — upgrade is required.
WebSocket DoS via cumulative fragment bypass — CVE-2026-9675
GHSA-38rv-x7px-6hhq · CWE-400, CWE-770
Fix: b4c287b3 fix(websocket): enforce max payload size across fragments
Undici validated the size of individual frames but did not track cumulative size
across a fragmented message. An attacker could send many small fragments that
each pass per-frame validation but collectively exceed the configured limit,
causing memory exhaustion. This is a regression introduced in 8.1.0 (the
6.x and 7.x lines are not affected).
- Workaround: none — upgrade is required.
TLS certificate validation bypass in SOCKS5 ProxyAgent — CVE-2026-9697
GHSA-vmh5-mc38-953g · CWE-295
Fix: 42d49559 fix: honor requestTls when proxy is SOCKS5
The ProxyAgent silently discarded the requestTls option when configured with
a SOCKS5 proxy. TLS connections through the SOCKS5 tunnel ignored user-configured
parameters such as ca, cert, key, rejectUnauthorized, and servername,
falling back to the default Mozilla CA bundle. Applications relying on
certificate pinning to an internal CA were exposed to man-in-the-middle attacks.
- Affected:
ProxyAgent/Socks5ProxyAgentover SOCKS5 that rely on
requestTls. - Workaround: route traffic through an HTTP-proxy
ProxyAgent, where
requestTlsfunctions correctly.
Cross-origin request routing via SOCKS5 proxy pool reuse — CVE-2026-6734
GHSA-hm92-r4w5-c3mj · CWE-346 · Fixed in 8.2.0
Fix: a516f870 fix(socks5-proxy-agent): use per-origin pools to prevent cross-origin routing (#5041)
Socks5ProxyAgent reused a single connection pool across different origins
without verifying the pool's origin matched the requested origin. This could
route credentials and request data to unintended destinations, cause responses
from the wrong origin to be trusted, and enable HTTPS→HTTP downgrade.
- Affected: applications using
Socks5ProxyAgentacross multiple origins
(introduced via #4385). - Workaround: use a separate agent instance per origin.
Moderate severity
Cross-user information disclosure via shared cache whitespace bypass — CVE-2026-9678
GHSA-pr7r-676h-xcf6 · CWE-524
Fix: cb105d7c fix(cache): trim qualified field names
The cache interceptor mishandled responses with whitespace-padded
Cache-Control directives such as private=" authorization". In shared-cache
mode this could cause authenticated data to be cached and served to other users.
- Affected: apps using the cache interceptor in shared mode that forward
Authorizationupstream and receive non-canonical qualified directives. - Workaround: disable shared-cache mode for authenticated traffic, avoid
caching authenticated responses, or addVary: Authorizationupstream.
HTTP header injection via Set-Cookie percent-decoding — CVE-2026-9679
GHSA-p88m-4jfj-68fv · CWE-93
Fix: 5655ea43 fix(cookies): preserve values and parse SameSite strictly
parseSetCookie applied percent-decoding to cookie values, turning encoded
sequences like %0D%0A and %00 into literal bytes, contrary to RFC 6265 §5.4
and browser behavior. Applications forwarding parsed Set-Cookie values into
response headers were exposed to header injection, enabling session fixation,
open redirects, and cache poisoning. Introduced in 7.0.0 via
#3789.
- Workaround: sanitize values before forwarding — strip or reject CR, LF,
NUL,;, and=.
Low severity
Set-Cookie SameSite attribute downgrade — CVE-2026-11525
GHSA-g8m3-5g58-fq7m · CWE-183
Fix: 5655ea43 fix(cookies): preserve values and parse SameSite strictly
The cookie parser accepted SameSite values containing Strict, Lax, or
None as substrings rather than requiring exact matches per RFC 6265. Values
like SameSite=NoneOfYourBusiness parsed as None, and SameSite=StrictLax
parsed as Lax, silently weakening cookie security policies for apps that
forward parsed attributes.
HTTP response queue poisoning via keep-alive socket reuse — CVE-2026-6733
GHSA-35p6-xmwp-9g52 · CWE-367 (TOCTOU race condition)
Fix: 6ea54ef8 fix: guard idle socket validation to skip fresh sockets, hardened by c9fbe9d2 keep idle validation on native timers (#5397) and ac5394b8 keep idle validation on global timers (#5407)
An attacker controlling an upstream HTTP/1.1 server could inject unsolicited
responses onto idle keep-alive sockets. On socket reuse, the injected response
was associated with a new request, delivering responses to the wrong requests.
- Requirements: attacker-controlled/compromised upstream and active
keep-alive reuse. - Workaround: disable keep-alive reuse with
keepAliveTimeout: 0on the
Client or Pool.
Also in v8.5.0 (non-security)
v8.5.0 shipped the security fixes above alongside the following changes. These
are not security fixes — they are listed for completeness of the release. (The
two queue-poisoning hardening PRs, #5397
and #5407, are covered under
CVE-2026-6733 above and are not repeated here.)
- HTTP/2:
#5408don't rewindkPendingIdxpast in-flight requests ·#5391allow h2 POST request multiplexing ·#5406reap idle HTTP/2 sessions ·#5410preserve h2 queue on out-of-order completion - Features:
#5416addbodyMixin.textStream()·#5418align EventSource with spec - Docs / CI / tests:
#5413document request header validation ·#5383absorb h2 stream timeout resets (test) ·#5420remove stale repro + lint ·#5426extend Windows CI timeout ·#5427detect available python in WPT runner
Full changelog: v8.4.1...v8.5.0.
Credits
Per-advisory credits (as recorded in each GHSA):
- CVE-2026-12151 — reported by @lpinca & @Nadav0077; reviewed by @UlisesGascon.
- CVE-2026-9675 — reported by @mauriceng98 & @Str1ckl4nd; fixed by @mcollina & @KhafraDev; reviewed by @UlisesGascon.
- CVE-2026-9697 — reported by @tonghuaroot; reviewed by @UlisesGascon.
- CVE-2026-6734 — reported by @ChALkeR; reviewed by @mcollina; verified by @UlisesGascon.
- CVE-2026-9678 — fixed by @mcollina; reviewed by @UlisesGascon.
- CVE-2026-9679 — reported by @tndud042713; fixed by @mcollina; reviewed by @KhafraDev & @UlisesGascon.
- CVE-2026-11525 — fixed by @mcollina; reviewed by @UlisesGascon.
- CVE-2026-6733 — fixed by @mcollina; verified by @UlisesGascon.
v8.4.1
What's Changed
- test: avoid localhost lookup in fetch cookies tests by @mcollina in #5363
- fix: prevent race condition between onEnd and onTrailers in HTTP/2 client (#5216) by @mcollina in #5343
- fix(dns): skip requests without origin by @marko1olo in #5376
- docs: add Getting Started guide by @AliMahmoudDev in #5371
- docs: fix code examples that crash at runtime and other inaccuracies by @AliMahmoudDev in #5386
- fix: handle paused parser on socket end (issue #5360) by @mcollina in #5389
- fix(client): reject pipelined TLS altname errors by @marko1olo in #5373
- docs: fix multiple inaccuracies in API documentation by @AliMahmoudDev in #5384
- docs: fix remaining broken links in API documentation by @AliMahmoudDev in #5342
New Contributors
- @marko1olo made their first contribution in #5376
Full Changelog: nodejs/undici@v8.4.0...v8.4.1
v8.4.0
What's Changed
- fix: register connect listener before initiating requests in close-and-destroy test by @mcollina in #5272
- test: stabilize tls-cert-leak regression by @mcollina in #5306
- fix: replace tspl with native test context in test/examples.js by @mcollina in #5300
- http2: remove redundant request stream binding by @trivikr in #5302
- test: limit cache-tests workers on Windows by @mcollina in #5309
- test: use test context cleanup hooks in parser issue tests by @mcollina in #5282
- Add redirect option to strip headers on redirect by @mcollina in #5281
- chore(test): fix lint failure by @aduh95 in #5316
- chore(ci): use
npm ciinstead ofnpm installby @aduh95 in #5315 - docs: clarify formData security considerations by @mcollina in #5320
- docs: add EventSource server example by @Will-thom in #5321
- fix(core): simplify
addAbortListenerutil by @aduh95 in #5317 - build(deps-dev): bump ws from 8.20.0 to 8.21.0 by @dependabot[bot] in #5325
- build(deps-dev): bump jsondiffpatch from 0.7.3 to 0.7.6 by @dependabot[bot] in #5313
- docs: match undici EoL to node version it's bundled in by @trivikr in #5330
- fix: handle all HTTP/2 request stream sync errors by @mcollina in #5311
- fix: preserve timeout errors for HTTP/2 requests by @mcollina in #5091
- fix(core): normalize autoSelectFamily timeout AggregateError by @youcefzemmar in #5329
- chore(core): define
kEnumerablePropertyatomically by @aduh95 in #5332 - chore(core): use
regex.execinstead ofstring.matchby @aduh95 in #5331 - fix: reset invalid HTTP/2 sessions by @mcollina in #5310
- feat(connect): add
preferH2connector option to offer h2 first in ALPN by @Antamansid in #5327 - test: fix flaky http2 trailers test by @mcollina in #5338
- fix(mock): restore single-arg MockCallHistory.filterCallsByX by @youcefzemmar in #5328
- docs: document missing error types in Errors.md by @cesarvspr in #5339
- build(deps): bump github/codeql-action from 4.35.3 to 4.36.1 by @dependabot[bot] in #5346
- build(deps): bump actions/dependency-review-action from 4.9.0 to 5.0.0 by @dependabot[bot] in #5347
- build(deps): bump uWebSockets.js from v20.67.0 to v20.68.0 in /benchmarks by @dependabot[bot] in #5352
- build(deps): bump concurrently from 9.2.1 to 10.0.3 in /benchmarks by @dependabot[bot] in #5353
- build(deps): bump step-security/harden-runner from 2.19.1 to 2.19.4 by @dependabot[bot] in #5348
- build(deps): bump actions/checkout from 6.0.2 to 6.0.3 by @dependabot[bot] in #5351
- build(deps): bump codecov/codecov-action from 6.0.0 to 6.0.1 by @dependabot[bot] in #5349
- docs: improve connect option documentation in Client.md by @AliMahmoudDev in #5344
- fix(mock): do not persist snapshots on close in playback mode by @GeoffreyBooth in #5359
- fix(fetch): remove abort listener when request settles by @ATOM00blue in #5318
- test: add Node.js global fetch regression coverage by @mcollina in #5361
- fix(h2): make Client multiplex on h2 (#4143) by @mcollina in #5362
New Contributors
- @Will-thom made their first contribution in #5321
- @youcefzemmar made their first contribution in #5329
- @Antamansid made their first contribution in #5327
- @cesarvspr made their first contribution in #5339
- @AliMahmoudDev made their first contribution in #5344
- @ATOM00blue made their first contribution in #5318
Full Changelog: nodejs/undici@v8.3.0...v8.4.0
v8.3.0
What's Changed
- fix: preserve pool capacity after removing stale client by @trivikr in #5151
- build(deps): bump actions/github-script from 8.0.0 to 9.0.0 by @dependabot[bot] in #5157
- build(deps): bump actions/upload-artifact from 5.0.0 to 7.0.1 by @dependabot[bot] in #5162
- build(deps): bump peter-evans/create-pull-request from 8.1.0 to 8.1.1 by @dependabot[bot] in #5156
- chore(http2): collapse duplicate request stream setup by @trivikr in #5140
- perf(client): cache HTTP/2 authority by @trivikr in #5141
- build(deps-dev): bump borp from 0.20.2 to 1.0.0 by @dependabot[bot] in #4819
- types: add TOpaque to client connect options by @samuel871211 in #4928
- build(deps): bump tinybench from 5.1.0 to 6.0.1 in /benchmarks by @dependabot[bot] in #4688
- build(deps): bump codecov/codecov-action from 5.5.1 to 6.0.0 by @dependabot[bot] in #4950
- build(deps): bump actions/dependency-review-action from 4.8.1 to 4.9.0 by @dependabot[bot] in #4951
- test(fetch): add userinfo coverage for issue-4897 URLs by @mcollina in #4901
- perf: avoid duplicate pool dispatcher selection on backpressure by @trivikr in #5149
- build(deps): bump actions/setup-node from 6.2.0 to 6.4.0 by @dependabot[bot] in #5163
- build(deps): bump step-security/harden-runner from 2.14.1 to 2.19.1 by @dependabot[bot] in #5160
- build(deps): bump cronometro from 5.3.0 to 6.0.3 in /benchmarks by @dependabot[bot] in #4687
- build(deps): bump github/codeql-action from 4.35.1 to 4.35.3 by @dependabot[bot] in #5161
- build(deps-dev): bump neostandard from 0.12.2 to 0.13.0 by @dependabot[bot] in #4853
- build(deps): bump hendrikmuhs/ccache-action from 1.2.22 to 1.2.23 by @dependabot[bot] in #5158
- build(deps): bump fastify/github-action-merge-dependabot from 3.11.2 to 3.12.0 by @dependabot[bot] in #5159
- build(deps-dev): bump c8 from 10.1.3 to 11.0.0 by @dependabot[bot] in #4854
- build(deps): bump uWebSockets.js from v20.64.0 to v20.66.0 in /benchmarks by @dependabot[bot] in #5130
- docs: mention install() also installs WebSocket globals by @mcollina in #5174
- types: stop interfering with @types/node by @Renegade334 in #5173
- fix: align h2 empty body content-length methods with h1 by @trivikr in #5172
- build(deps-dev): bump fast-check from 4.6.0 to 4.7.0 by @dependabot[bot] in #5192
- build(deps-dev): bump typescript from 6.0.2 to 6.0.3 by @dependabot[bot] in #5191
- test: move cleanup from finally to after hooks by @trivikr in #5194
- test: resolve flaky timeout in issue-3356 by @trivikr in #5188
SnapshotAgent: AddnormalizeBodyandnormalizeQueryby @GeoffreyBooth in #5121- fix(socks5): use configured connector in Socks5ProxyAgent by @trivikr in #5168
- perf(http2): avoid isArray checks for common headers by @trivikr in #5170
- fix(test): make deduplicate body-streaming test non-flaky by @mcollina in #5196
- test(retry): add regression test for RetryAgent + HTTP/2 stream timeout (#5137) by @mcollina in #5176
- fix(socks5): preserve dispatch backpressure return value by @trivikr in #5166
- perf(http2): end zero-length request bodies with headers by @trivikr in #5169
- fix(test): make issue-2898-comment.js assertion robust against flakiness by @mcollina in #5208
- test: disable timeouts in h2 high concurrency regression by @trivikr in #5205
- test: deflake stream compat coverage by @mcollina in #5209
- fix(dispatcher): remove unreachable assert in writeBlob by @SAY-5 in #5231
- fix: clean up benchmark resources before worker exit by @trivikr in #5225
- test: avoid per-chunk assertions in diagnostics get by @trivikr in #5224
- test: capture cache test worker stderr and preserve failures by @trivikr in #5206
- chore: gitignore benchmarks/package-lock.json by @trivikr in #5228
- perf(proxy-agent): avoid extra header allocations in auth guard by @trivikr in #5164
- test(wpt): retry WPT server startup on port conflicts or timeout by @trivikr in #5215
- test: make websocket diagnostics ping-pong ordering deterministic by @trivikr in #5222
- test(websocket): fix flaky send test by @mcollina in #5232
- fix: prevent node-fetch test server close from hanging on Windows by @mcollina in #5246
- test: wait for cache test server to listen by @trivikr in #5242
- fix: accept unknown-size Content-Range values by @trivikr in #5120
- test: avoid global dispatcher state in mock client tests by @trivikr in #5258
- test: fix flaky permessage-deflate limit timeout by @mcollina in #5229
- test: drain request bodies in request tests by @trivikr in #5247
- test: reduce retry-after invalid date timing flake by @trivikr in #5250
- test: drive request timeout ticks after connect by @trivikr in #5251
- test: only fail max-listener checks on max-listener warnings by @trivikr in #5253
- test: avoid double-closing server in client-request test by @trivikr in #5255
- fix(retry-handler): validate response body length against Content-Range by @mcollina in #4975
- test: wait for inflight-and-close body cleanup by @trivikr in #5261
- fix(test): make http2-pseudo-headers test order-independent by @mcollina in #5234
- fix: preserve fetch multipart body on MockAgent fallback by @mcollina in #5269
- ci: build Node FFI fixtures for shared-builtin tests by @trivikr in #5275
- test: deflake issue-5137 stream count assertion by @trivikr in #5243
- fix: replace finished() with writable lifecycle tracking by @trivikr in #5001
- perf(client-h2): reuse request stream handlers by @trivikr in #5280
- fix: prevent pipeline body replay on redirect by @mcollina in #5274
- fix(types): remove throwOnError from Dispatcher.RequestOptions by @Zelys-DFKH in #5279
- fix: validate EOF for chunked h1 responses by @mcollina in #5273
- test: deflake parser-issues teardown by @mcollina in #5278
- test: make http2-alpn control requests explicit by @trivikr in #5252
- test: include cache-test worker metadata on failure by @trivikr in #5276
- test: include after in parser-issues by @trivikr in #5284
- cache formdata boundary by @KhafraDev in #5292
- build(deps-dev): bump fast-check from 4.7.0 to 4.8.0 by @dependabot[bot] in #5298
- test: retry crashed cache-test workers once by @trivikr in #5294
- Add Node 26 to the matrix by @mcollina in #5271
- perf(client-h2): reuse request upgrade stream handlers by @trivikr in #5293
- build(deps-dev): bump jest from 30.3.0 to 30.4.2 by @dependabot[bot] in #5297
- build(deps): bump uWebSockets.js from v20.66.0 to v20.67.0 in /benchmarks by @dependabot[bot] in #5299
- test: fix flaky http2-dispatcher WebSocket upgrade tests by @mcollina in #5304
New Contributors
- @Zelys-DFKH made their first contribution in #5279
Full Changelog: nodejs/undici@v8.2.0...v8.3.0
v8.2.0
What's Changed
- chore: use native addAbortListener by @trivikr in #5021
- fix: fix the logic for the UNDICI_NO_WASM_SIMD environment variable by @ShenHongFei in #5026
- fix(http2): send body for non-expectsPayload methods with content by @mcollina in #5030
- fix(fetch): correct 'navigator' typo to 'navigate' in fetchFinale by @deepview-autofix in #5044
- fix(webidl): correct signed integer bounds in ConvertToInt by @deepview-autofix in #5038
- fix(fetch): use || for CRLF check in multipart formdata-parser by @deepview-autofix in #5049
- fix(websocket): correct argument order in WebSocketStream UTF-8 failure by @deepview-autofix in #5050
- fix(pool): propagate useH2c to connector when connections > 1 by @SAY-5 in #5031
- fix(cache): return immutable staleAt in milliseconds by @deepview-autofix in #5048
- fix(socks5-proxy-agent): use per-origin pools to prevent cross-origin routing by @deepview-autofix in #5041
- fix(cache): evict oldest entries first in SqliteCacheStore prune by @deepview-autofix in #5039
- fix(socks5): correctly expand IPv6 '::' compressed notation by @deepview-autofix in #5046
- Remove unused func and unnecessary shim by @tsctx in #5053
- fix: reject malformed content-length request headers by @trivikr in #5060
- fix(request): reject NaN highWaterMark during option validation by @trivikr in #5062
- docs: fix broken links in docsify sidebar by @maruthang in #5065
- fix(fetch): prefer filename* over filename in multipart form-data by @maruthang in #5068
- fix(http2): reject websocket upgrades on non-200 responses by @trivikr in #5072
- feat: support username-only proxy authentication in ProxyAgent by @rossilor95 in #4935
- build(deps): bump uWebSockets.js from v20.58.0 to v20.64.0 in /benchmarks by @dependabot[bot] in #5083
- fix(client-h2): stop double-decrementing kOpenStreams on stream timeout by @SAY-5 in #5076
- fix(http2): reject upgrade streams closed before response headers by @trivikr in #5069
- fix(http2): allow GET and HEAD request bodies over h2 by @trivikr in #5058
- fix(cache): include query in cache key when opts.path is undefined by @maruthang in #5081
- fix: avoid premature cleanup of dispatcher in Agent by @bienzaaron in #5034
- fix(http2): record ping failures on the socket by @trivikr in #5075
- add undici security policy by @mcollina in #5056
- fix(mock): make filterCalls AND operator actually intersect results by @deepview-autofix in #5045
- fix(socks5): enforce authenticated state before CONNECT by @trivikr in #5097
- fix(cache): skip expired sqlite vary entries during lookup by @trivikr in #5095
- fix: enforce maxCachedSessions in TLS session cache by @trivikr in #5102
- fix(socks5): encode embedded IPv4 tails in IPv6 literals correctly by @trivikr in #5099
- fix: handle invalid HTTP/2 connection headers (#4356) by @mcollina in #5101
- fix(interceptor): add throwOnMaxRedirect to types and interceptor opts by @maruthang in #5066
- fix(websocket): avoid double-closing canceled stream readers by @colinaaa in #5105
- fix(cache): persist vary when updating sqlite cache entries by @trivikr in #5109
- refactor(h1): track HEAD keep-alive override as boolean by @trivikr in #5110
- client: cache llhttp wasm buffer view by @trivikr in #5115
- deps: update llhttp to 9.3.1 by @mcollina in #5113
- fix(http2): preserve accepted streams after GOAWAY by @trivikr in #5090
- fix: reuse parser WeakRef for timeout callbacks by @trivikr in #5125
- fix: stop buffering data after SOCKS5 connect by @trivikr in #5118
- perf(http2): avoid response header reserialization by @trivikr in #5085
- fix(cache): enforce sqlite maxCount after insert by @trivikr in #5112
- perf: reduce EventSourceStream parser allocations by @trivikr in #5032
- types(dispatcher): use OutgoingHttpHeaders for request headers by @maruthang in #5067
- cleanup: delete redundant .gitkeep file by @shivarm in #5133
- fix(http2): respect peer max concurrent streams by @trivikr in #5135
- test(http2): ensure websocket upgrade resumes queued requests by @trivikr in #5132
- test(mock): cover SnapshotAgent excludeUrls playback by @maruthang in #5080
- perf(client): parse h1 content-length statelessly by @trivikr in #5124
- perf(http2): reduce writeH2 per-request callback allocations by @trivikr in #5138
- chore(deps): add lockfile by @aduh95 in #5139
- perf: use byteLength property for binary body chunks by @trivikr in #5126
- fix(cache): allow streamed entries at maxEntrySize limit by @trivikr in #5129
- perf(http2): avoid cloning headers when removing status by @trivikr in #5127
- fix: validate H2CClient maxConcurrentStreams option by @trivikr in #5143
- perf: avoid redundant scans in BalancedPool dispatcher selection by @trivikr in #5146
- fix: replace stale pool clients under connection limit by @trivikr in #5145
New Contributors
- @deepview-autofix made their first contribution in #5044
- @SAY-5 made their first contribution in #5031
- @maruthang made their first contribution in #5065
- @bienzaaron made their first contribution in #5034
Full Changelog: nodejs/undici@v8.1.0...v8.2.0
[v8.1.0](https://redirect.github.com/nodejs/u
✂ Note
PR body was truncated to here.
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.

This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| @fastify/static | ^9.0.0 → ^10.1.0 |
Release Notes
fastify/fastify-static (@fastify/static)
v10.1.0
What's Changed
- fix: set Vary: Accept-Encoding for preCompressed responses by @LeSingh1 in #586
- feat: use
@fastify/errorfor errors and add optionsuppressWarningby @climba03003 in #599
New Contributors
- @LeSingh1 made their first contribution in #586
Full Changelog: fastify/fastify-static@v10.0.0...v10.1.0
v10.0.0
Breaking Changes
setHeadersnow usingFastifyReplyinstead ofResponse.
You should refactor your code to use the reply helpers.
For example,
// Before
const fastify = require('fastify')({logger: true})
const path = require('node:path')
fastify.register(require('@​fastify/static'), {
root: path.join(__dirname, 'public'),
prefix: '/public/', // optional: default '/',
setHeaders(res) {
res.setHeader('X-Test', 'Foo')
}
})
// After
const fastify = require('fastify')({logger: true})
const path = require('node:path')
fastify.register(require('@​fastify/static'), {
root: path.join(__dirname, 'public'),
prefix: '/public/', // optional: default '/',
setHeaders(reply) {
reply.header('X-Test', 'Foo')
}
})What's Changed
- chore!: bump content-disposition fom 1.0.1 to 2.0.1 by @climba03003 in #597
- fix: ignore unsupported deflate for precompressed assets by @jibin7jose in #596
- fix!: allow setHeaders to override send headers by @climba03003 in #598
New Contributors
- @jibin7jose made their first contribution in #596
Full Changelog: fastify/fastify-static@v9.3.0...v10.0.0
v9.3.0
What's Changed
New Contributors
Full Changelog: fastify/fastify-static@v9.2.0...v9.3.0
v9.2.0
What's Changed
- chore(.gitattributes): retain binary file eol style by @Fdawgs in #577
- refactor(types): migrate from tsd to tstyche by @Tony133 in #579
- fix: propagate return value from fastify.errorHandler by @abdulmunimjemal in #582
- chore: update depedabot setting by @climba03003 in #583
- chore: bump @fastify/compress from 8.3.1 to 9.0.0 by @dependabot[bot] in #590
- chore: bump @types/node from 25.9.4 to 26.0.0 in the dev-dependencies-typescript group by @dependabot[bot] in #592
- docs: fix broken links by @Fdawgs in #591
- docs: clarify precompressed allowedPath behavior by @mcollina in #593
New Contributors
- @abdulmunimjemal made their first contribution in #582
Full Changelog: fastify/fastify-static@v9.1.3...v9.2.0
v9.1.3
What's Changed
- fix: support wildcard prefixes with route params by @mcollina in #576
Full Changelog: fastify/fastify-static@v9.1.2...v9.1.3
v9.1.2
What's Changed
- fix: resolve wildcard paths in encapsulated contexts by @mcollina in #574
Full Changelog: fastify/fastify-static@v9.1.1...v9.1.2
v9.1.1
⚠️ Security Release
This fixes CVE CVE-2026-6410 GHSA-pr96-94w5-mx2h.
This fixes CVE CVE-2026-6414 GHSA-x428-ghpx-8j92.
What's Changed
Full Changelog: fastify/fastify-static@v9.1.0...v9.1.1
v9.1.0
What's Changed
- build(deps-dev): bump @types/node from 24.10.4 to 25.0.3 by @dependabot[bot] in #552
- test: move ignoreTrailingSlash under routerOptions by @lraveri in #553
- build(deps-dev): bump borp from 0.20.2 to 0.21.0 by @dependabot[bot] in #543
- chore: bump pino and borp dependencies, delete stale.yml by @Tony133 in #557
- chore: update syntax typescript by @Tony133 in #558
- chore(license): standardise license notice by @Fdawgs in #560
- fix: sendFile ignoring option overrides in some cases by @bakugo in #559
- chore: upgrade c8 to v11.0.0 and improvements test by @Tony133 in #564
- build(deps): bump fastify/workflows/.github/workflows/plugins-ci.yml from 5 to 6 by @dependabot[bot] in #566
New Contributors
- @lraveri made their first contribution in #553
- @Tony133 made their first contribution in #557
- @bakugo made their first contribution in #559
Full Changelog: fastify/fastify-static@v9.0.0...v9.1.0
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.

This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| @cloudflare/workers-types | ^4.20260526.1 → ^5.20260722.1 |
Release Notes
cloudflare/workerd (@cloudflare/workers-types)
v5.20260722.1
v5.20260721.1
v5.20260719.1
v5.20260718.1
v5.20260717.1
v5.20260716.1
v5.20260715.1
v5.20260714.1
v5.20260713.1
v5.20260712.1
v5.20260711.1
v5.20260710.1
v5.20260708.1
v5.20260707.1
v5.20260706.1
v5.20260705.1
v5.20260704.1
v5.20260703.1
v4.20260702.1
v4.20260701.1
v4.20260630.1
v4.20260629.1
v4.20260628.1
v4.20260627.1
v4.20260626.1
v4.20260625.1
v4.20260624.1
v4.20260623.1
v4.20260621.1
v4.20260620.1
v4.20260619.1
v4.20260617.1
v4.20260616.1
v4.20260615.1
v4.20260613.1
v4.20260612.1
v4.20260611.1
v4.20260610.1
v4.20260609.1
v4.20260608.1
v4.20260607.1
v4.20260606.1
v4.20260605.1
v4.20260604.1
v4.20260603.1
v4.20260602.1
v4.20260601.1
v4.20260531.1
v4.20260530.1
v4.20260529.1
v4.20260528.1
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.

AI discourse: most works are done by Claude Fable 5
Changes
astro:data-layer-contentnow always emitsexport default JSON.parse("...")instead of adataToEsm()object literal. The dev-only 5MB threshold from #17223 is removed, so dev and production emit the same code again.- Removes the now-unused
@rollup/pluginutilsdependency fromastro.
Why:
- The object literal's AST grows with the store. In dev, Vite's SSR transform feeds the module through rolldown/oxc-parser, which serializes the whole AST into a single JSON string across the NAPI bridge; for multi-MB stores that string exceeds V8's maximum string length and collections silently come back empty (#17220). #17223 worked around this with a dev-only size threshold; emitting a string unconditionally removes the failure class structurally instead of by heuristic.
- A string literal keeps the module's AST size constant, and
JSON.parseis roughly 1.7x faster than parsing the equivalent object literal (https://v8.dev/blog/cost-of-javascript-2019#json), so server cold starts get slightly faster as stores grow. - Precedent: Vite's own JSON plugin defaults to
json.stringify: 'auto', which emitsJSON.parse("...")for any JSON module over 10kB for the same reasons, and the chunked store mode (#17296) already ships serialized strings in both dev and production. dataToEsm()adds nothing here: the store's top level is a devalue-flattened array, so the generated module has a single default export and there are no named exports to tree-shake. Its output is essentially the on-disk JSON with unquoted keys.- Cost: the data store module in the (unminified) server bundle grows about 9% raw from string escaping, about 1% after gzip. Client bundles are unaffected; nothing client-reachable imports this module.
Testing
Green CI
Docs
No docs needed: this changes the internal representation of a virtual module with no user-facing behavior change. No changeset file is added.

ℹ️ Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| pnpm (source) | 11.5.0 → 11.13.1 |
Release Notes
pnpm/pnpm (pnpm)
v11.13.1: pnpm 11.13.1
Patch Changes
- Fixed
pnpm packapplying workspace-root ignore rules when a workspace package has its own.npmignorefile. - Keep the interactive
minimumReleaseAgeapproval prompt visible duringpnpm install. The progress reporter now pauses its redraws while a prompt is waiting for input instead of overwriting it, so the install no longer hangs on a question the user cannot see #13019. - Fixed
pnpm self-updatefailing to link native platform binaries stored in sibling global virtual store slots.
v11.13.0: pnpm 11.13
Minor Changes
-
Added
versioning.epicstopnpm-workspace.yaml. An epic ties a group of member packages to a lead package, constraining every member's major version to a band derived from the lead's major: while the lead is on majorM, members live inM*100 … M*100+99. Members move independently inside the band (patch, minor, and amajorintent that stays in-band); a bump that would carry a member past the band ceiling is rejected until the lead advances its own major. When a release plan takes the lead to a new stable major, every member re-bases to the band floor in the same plan. Membership is matched with pnpm's package selectors — name globs,./-prefixed directory globs, and!-prefixed negations. -
Added the
teamcommand for managing organization teams and team memberships on the registry, with create, destroy, add, rm, and ls subcommands and support for --otp, --parseable, and --json flags. -
Added native workspace release management #12952: the new
pnpm changecommand records change intents as changesets-compatible.changeset/*.mdfiles (pnpm change statusshows the pending release plan), and the barepnpm version -rconsumes them — bumping versions across the workspace with dependent propagation throughworkspace:ranges, fixed groups, amaxBumpcap,--filternarrowing, and--dry-run— writing changelogs, and recording consumed intents in a committed ledger that keeps cherry-picks and merge-backs between release branches safe. Packages can be moved onto per-package release lanes with the newpnpm lane <name> --filter <pkg>command and back withpnpm lane main --filter <pkg>(pnpm laneshows the membership), releasingX.Y.Z-lane.Nprereleases from the same runs that release stable versions of the packages on the main lane. Configuration lives under the newversioningkey ofpnpm-workspace.yaml(fixed,ignore,maxBump,lanes,changelog). When two workspace projects publish the same name, intent files,versioning.lanes, andversioning.fixed/ignoremay reference a project by its workspace-relative directory path (e.g."./pnpm/npm/pnpm") — the one additive extension to the changesets format, applied automatically bypnpm change.Release changelogs default to
registrystorage (versioning.changelog.storage): noCHANGELOG.mdis committed. Each release's section is composed at publish time and packed into the published tarball on top of the previously published version's changelog, and the consumed change intents are garbage-collected by a laterpnpm version -ronly once the registry confirms the version is published with its section. Setversioning.changelog.storage: repositoryto keep committedCHANGELOG.mdfiles instead. -
Added a new override selector form with an empty range —
"pkg@": "<version>"— called a convergence override. It rewrites a dependency edge only when its exact version satisfies the edge's declared range, so compatible consumers converge on one version while incompatible consumers keep their own resolution — now and for any dependent added in the future #12794.overrides: "form-data@": 4.0.6
The value must be an exact version. When a full resolution detects that every declared range also admits a newer version, pnpm warns that the override is stale and names the version to converge on. Previously an empty range in an override selector was undocumented and behaved like a bare (unscoped) override.
Patch Changes
-
A
tokenHelperset in the global pnpmauth.iniis no longer rejected as project-level configuration. The guard that blockstokenHelperfrom a project.npmrconly treated~/.npmrcas a trusted source, so a helper written toauth.ini(for example bypnpm config set) failed on every command and could not even be removed withpnpm config delete. AtokenHelperin a workspace or project.npmrcis still rejected. -
pnpm cache deletenow removes a package's metadata from every metadata cache directory (metadata,metadata-full, andmetadata-full-filtered), instead of only the one the current resolution mode reads. Previously a package cached under a different mode (e.g.metadata-full-filtered) was left behind. Closes #12753. -
Fixed an injected workspace dependency (
injectWorkspacePackages: true) incorrectly staying asfile:instead of deduping back tolink:when an unrelated, ordinary shared dependency resolved to a peer-suffixed variant for the target project's own copy but not for the injected occurrence. See #10433. -
pnpm deploynow supports workspaces that use catalogs. -
Fixed
pnpm deploywith a shared lockfile so localfile:tarball dependencies keep their package name in the generated deploy lockfile. This prevents warm-store deploys from failing withERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STOREwhen the tarball filename includes the version. -
Options that follow
create,exec, ortestappearing as a subcommand of another command are now parsed instead of being silently treated as positional parameters. For example,pnpm team create @​org:team --registry <url>previously ignored the--registryoption and sent the request to the default registry. -
pnpm add -g,pnpm update -g,pnpm setup, and the self-updater no longer fail withERR_PNPM_MISSING_TIMEwhentrustPolicy: no-downgradeorresolutionMode: time-basedis set in the global config #12883. The decision to fetch full registry metadata now lives in one place, and theno-downgradetrust policy always requests full metadata (matching the self-updater), since the trust evidence it checks is missing from abbreviated metadata even on registries that include thetimefield. -
pnpm listandpnpm whyno longer crash withEMFILE: too many open fileswhen a project has a large number of unsaved dependencies (packages present innode_modulesbut not in the lockfile). The reads of those packages are now concurrency-limited. -
The published
pnpmpackage no longer declaresdependenciesordevDependencies. Because the CLI bundles its runtime dependencies intodist/node_modules, those fields are dropped when packing, sonpm installof the tarball no longer tries to resolve internal-only packages such as@pnpm/test-ipc-server. Closes #12955. -
Fixed
pnpm publish --otpandpnpm publish --batch --otpto send the configured OTP to the registry. -
pnpm publishagain sends the package's README to the registry as metadata, so registries can render it on the package page. The readme is always included in the published metadata (matching the npm CLI), while theembed-readmesetting continues to control only whether the readme is written into thepackage.jsoninside the tarball. This restores the behavior that was lost when publishing became fully native. Closes #12966. -
Fixed the dependency status check wrongly reporting "up to date" when a
package.json,.pnpmfile.cjs, or patch file was edited in the same second as the previous install, on filesystems that record mtimes at whole-second resolution (for example ext4 with 128-byte inodes). The optimistic repeat-install fast path andverify-deps-before-runcompared mtimes strictly, so a same-second edit whose mtime rounded down looked unchanged and re-resolution was skipped. Such a file's whole second is now treated as possibly-modified, falling through to the content check; behavior on sub-second filesystems is unchanged. -
Retry package metadata requests when a registry or proxy returns
304 Not Modifiedto an unconditional request, preventing falseERR_PNPM_CACHE_MISSING_AFTER_304failures pnpm/pnpm#12882.If the retry also returns
304, reportERR_PNPM_META_NOT_MODIFIED_WITHOUT_CACHEinstead. -
Fixed
pnpm updateremoving transitive lockfile entries whendedupePeerDependentsis disabled and the selected package is absent pnpm/pnpm#12456. -
Limit modern deploy lockfiles and localized virtual stores to dependencies reachable from the selected dependency groups.
-
A
tokenHelpercommand is now given a 60-second time limit. A helper that hangs (deadlock, stuck I/O) is killed and reported as an error instead of leaving the command waiting forever. -
Fixed orphaned child processes on Windows when pnpm exits on an error while commands spawned by
pnpm execorpnpm dlxare still running (for example, when one project's command fails duringpnpm --recursive exec). The PIDs of these commands are now recorded when they are spawned and their whole process trees are terminated withtaskkillon an error exit. Previously the cleanup relied on enumerating the system process list, which is so slow on Windows that the enumeration hit its timeout and the cleanup was silently skipped #12406. -
pnpm packnow respects workspace-root.npmignoreand.gitignorefiles when packing workspace packages.
Platinum Sponsors
|
|
|
|
Gold Sponsors
|
|
|
|
|
|
|
|
|
|
|
v11.12.0: pnpm 11.12
Minor Changes
a897ef7: Custom fetchers exported from a pnpmfile can now delegate by returning a{ delegate: <resolution> }envelope: pnpm rewrites the package's resolution to the delegated shape and runs the built-in fetcher on it. This is the portable delegation form that also works in pacquet, wherecafsandfetcherscannot be passed to the hook. Related to pnpm/pnpm#11685.
Patch Changes
-
2b02764: The changed-packages filter (--filter "...[<since>]") no longer allows an option-like<since>value (such as--output=<path>) to be interpreted as a git option — git now rejects it as a bad revision. The repository root is also resolved to the nearest.gitentry, so the filter works in a git worktree checked out inside another repository's tree. -
43711ce:pnpm outdatedno longer checks the registry for dependencies that are resolved from locallink:,file:, orworkspace:references in the lockfile #12827. -
3c6718b: Fixed a deadlock in peer dependency resolution:pnpm installhung forever when a peer dependency cycle spanned a project's own dependencies and auto-installed peer providers, for example when installingelectron-builder@26.15.3#12921. -
252f15e: Fixed peer dependency auto-install picking a version the peer range rejects. In a workspace with several projects, a package declaring a peer dependency with a semver range (for example^1.0.0) could get the highest version found anywhere in the workspace (for example a2.0.0resolved for another project) instead of a version that satisfies the range. Peers are now deduplicated onto the highest preferred version that satisfies the declared range, and when none does, the range is resolved from the registry.Also fixed re-resolving with an existing lockfile hoisting a different peer version than a fresh install of the same manifest: root dependencies reused from the lockfile were invisible to peer hoisting, so a peer that a root dependency provides could be bound to another version.
-
a38adda:pnpm self-update <version>now installs the requested pnpm version when it matches the currently running version but is missing from the global self-update directory. -
6a85968:pnpm stage listnow stops paginating after a fail-safe cap of 1000 pages, so a misbehaving registry cannot keep the command looping forever. -
eee7c9a:verify-deps-before-runno longer spawns apnpm installwhen pnpm is executed in a directory that has nopackage.json. A mistyped command run outside a project (for examplepnpm witch 10 login) used to crash with a confusing error from the spawned install; now it fails with the regular "no package.json found" error.
Platinum Sponsors
|
|
|
|
Gold Sponsors
|
|
|
|
|
|
|
|
|
|
|
v11.11.0
Minor Changes
508b8c2: Added thepnpm accesscommand for managing package access and visibility on the registry, supporting listing packages and collaborators, getting and setting package status and MFA requirements, and granting or revoking team access.
Patch Changes
c70e33e: AllowallowBuildsentries for git-hosted packages to match by repository URL without pinning the resolved commit hash. This lets trusted git repositories keep running their build scripts after branch updates without approving each new commit, while package-name-only rules still do not approve git-hosted artifacts.3067e4f: Reduced peak memory usage during cold-cache dependency resolution. The metadata fetch is memoized for the whole resolution phase, and it was retaining each package's raw registry response body (used only to mirror the response to disk) for that entire time. The memoized cache now holds a body-less copy, so the raw body only lives as long as the call that writes the disk mirror. On large graphs that fetch full metadata (e.g. withminimumReleaseAgeortrustPolicyenabled) this cuts peak RSS by roughly 30%, back in line with pnpm 10. The resolved lockfile is unchanged.51300fd: Prevent a craftedpnpm-lock.yamlfrom writing package content outside the virtual store. A dependency path key whose name reconstructs to a path-traversal sequence (e.g.../../../tmp/x@1.0.0) is now rejected by the isolated (virtual-store) linker and the Plug'n'Play resolver map, matching the containment already applied to the hoisted linker. Under the global virtual store, a traversal in the version-derived path segment (e.g. a snapshotversion: "../../x") is now rejected atformatGlobalVirtualStorePath, the single point every global-virtual-store slot path funnels through — closing the same escape in the isolated linker, the resolver's dependency-graph builder, and the config-dependency installer.f8058eb: Reject symlinkedpnpm-lock.yamlfiles when reading or writing the env lockfile document.9318a11: AllowregistriesandnamedRegistriesto be configured in the globalconfig.yamlfile.51300fd: Fixed a path traversal vulnerability where a dependency whose manifestnamewas a scoped path traversal (e.g.@x/../../../<path>) could be written outsidenode_modulesto an attacker-controlled location duringpnpm install, even with--ignore-scripts. The isolated linker now validates the package name before using it as a directory name, matching the existing protection in the hoisted linker.14332f0: Fail instead of silently removing an optional dependency's locked entries frompnpm-lock.yamlwhen the registry cannot resolve it. Previously, when registry metadata lacked a version that the lockfile already pinned (for example, a mirror that had not synced a recent release yet),pnpm installandpnpm dedupesilently dropped the optional dependency's entries — emptying maps such as the platform binaries of@napi-rs/canvas— so the lockfile differed between machines and frozen installs on other hosts had nothing to link #12853.fecfe83: Fixed peer dependency resolution withautoInstallPeerswhen a workspace package depends on a version of a package that a transitive dependency's self-contained closure also provides for itself. The peer providers that are attached to the root project for reuse are no longer peer-resolved a second time in the root context, so packages inside such a closure no longer get their peers bound to the root project's incompatible version #4993.5a4daec:${...}environment-variable placeholders in thehttpProxy,httpsProxy,noProxy,proxy, andnoproxysettings are no longer expanded when these settings come from a project'spnpm-workspace.yaml. They now receive the same protection already applied toregistry,namedRegistries, andpnprServer.d1da02e:pnpm publishno longer prints credentials when the target registry is configured with inlineuser:pass@credentials (e.g.registry=https://user:pass@example.com/). They are now redacted both from the "publishing to registry" line and from the OIDC (trusted publishing) failure messages.dcfc611:pnpm self-updatenow honorstrustPolicy=no-downgrade. It resolves the target pnpm version against full registry metadata, so it refuses to switch to a version whose supply-chain trust evidence is weaker than an earlier-published one, the same way a regular install does.a8ad82d: Register thepnalias in generated shell completion scripts.25bd5c3: Fixed standalone installer downgrades from pnpm v12 to v11.23996e9:pnpm runtime set <name> <version>now validates its arguments: the name must benode,deno, orbun, and the version must not contain a comma. Previously these were interpolated straight into apnpm addselector, where an unsupported name or a comma (e.g.node 22,is-positive) could be misread as a list of packages or a local directory and install unintended packages or bins.
v11.10.0
Minor Changes
-
e2e3c81: Added theissuescommand as an alias ofbugs, sopnpm issuesopens the package's bug tracker URL in the browser. -
8491f8e: Added theprefixcommand which prints the current package prefix directory (or global prefix directory if-g/--globalis used). -
3425e80: Added an_authsetting for configuring registry authentication as a single structured (URL-keyed) value. It can be set in the global pnpm config (config.yaml) or, for CI, via thepnpm_config__authenvironment variable. The env form sidesteps the GitHub Actions / bash / zsh limitation that broke the existingpnpm_config_//host/:_authToken=…form (env var names containing/,:, or.are silently dropped). Closes #12314.The value is keyed by registry URL so each secret is explicitly bound to the host that may receive it. Registry URL keys must use
httporhttpsand must not include credentials, query strings, or fragments:export pnpm_config__auth='{"https://registry.npmjs.org":{"@​":{"authToken":"npm-token"},"@​org":{"authToken":"org-token"}}}'
The equivalent in the global
config.yaml:_auth: https://registry.npmjs.org: "@​": authToken: npm-token "@​org": authToken: org-token
Within each registry URL,
@means registry-wide/default credentials and package scopes like@orgbind credentials to that scope on the same host. The only supported credential field isauthToken(maps to_authToken/ bearer auth); the deprecatedbasicAuth/username+passwordforms are intentionally not accepted here.Each entry also infers a trusted registry route:
@routes the default registry (andpnpm add <pkg>resolves there), and@orgroutes that scope. Because the credential and destination host arrive in one trusted value, repo-controlledpnpm-workspace.yamlor project.npmrccannot redirect the token to a different host._authis honored only from the env var and the global config — it is ignored in a projectpnpm-workspace.yaml/.npmrc, so repo-controlled config can never supply registry auth. Precedence: CLI flags (--registry,--@​scope:registry) >pnpm_config__auth> globalconfig.yaml_auth>pnpm-workspace.yaml.Both
pnpm_config__auth(lowercase, documented form) andPNPM_CONFIG__AUTH(all-caps, the shell convention some CI runners apply) are honored. If both are set, lowercase wins unless it is empty, in which case uppercase is used. The env var wins over the globalconfig.yaml_authon a conflicting key.tokenHelperis not supported in_auth. Parsing is strict: a malformed value (bad JSON, wrong shape, invalid registry URL or scope, an unsupported credential field) fails fast with an error rather than being silently dropped.Pacquet parity note: the pacquet (Rust) port supports the same single credential field as the TS CLI:
authToken. -
a33eeec:pnpm self-updateandpackageManagerversion-switching can now install and link pnpm v12 (the Rust port), published with equal content under both thepnpmand@pnpm/exenames on thenext-12dist-tag. Its native binaries ship as@pnpm/exe.<platform>-<arch>packages, which pnpm's built-in installer links directly — no Node.js launcher, so the command pays no Node startup cost. v12 is initialized exactly like@pnpm/exe, including per-platform global-virtual-store hashing. From v12 onward the install converges on the unscopedpnpmpackage (the Rust exe) — even when updating from the SEA@pnpm/exebuild. -
1dd12bd: When resolving through a pnpr install-accelerator server, pnpm no longer forwards its own upstream registry credentials in the resolve request. Only theAuthorizationheader identifying the caller to pnpr is sent. The pnpr server now selects upstream credentials from its own route policy (operator-configured upstream credential aliases), so private dependencies resolve through a pnpr-managed alias the caller is authorized to use, rather than by sending the client's registry tokens to the server. -
1e81761: Expose web authenticationauthUrlanddoneUrlin JSON error output when OTP is required in a non-interactive terminal #12724.
Patch Changes
-
2f389d6: Added the Node.js release team's new signing key (Stewart X Addison,655F3B5C1FB3FA8D1A0CA6BDE4A7D232B936D2FD) to the embedded Node.js release keys, so runtimes whoseSHASUMS256.txtis signed by the new releaser verify successfully. -
acbdb94: Fixed shell tab completion not suggesting workspaces after the-Falias for--filteroption. -
dcabb78: Fixedpnpm up -r <pkg>bumping unrelated packages that have open semver ranges. Previously, any update mutation nullified the lockfile-derivedpreferredVersionsglobally, so packages with^x.y.zranges could re-resolve to newer compatible versions even though the user only asked to update a specific package. The install layer now always seedspreferredVersionsfrom the lockfile, and caller-supplied preferred versions (such as the vulnerability penalties ofpnpm audit --fix) layer on top of the seed instead of replacing it. The targeted package still bumps: the per-resolveupdateRequestedflag makes the resolver ignore the target's own lockfile pins.Closes #10662.
-
d539172: Fixed pnpm pack and pnpm publish failing when prepack generates files that are included in the package and postpack cleans them up. -
be6505a: Hardened global package management:- On Windows, removing or updating a global package now also cleans up the
node.exeflavor of a bin, so a stalenode.exeno longer survives onPATHafter uninstall, and a new global install no longer silently overwrites an existingnode.exe. pnpm add -g pnpm@<version>(and@pnpm/exe@<version>) is now rejected like the barepnpmform, pointing topnpm self-update.- Dependency aliases read from a global package's manifest are validated before being joined onto
node_modulespaths, preventing a tampered manifest from escaping the install directory. - Each global install group is created in its own freshly-made directory (no longer reusing a colliding or pre-existing path).
- Removing or updating a global package no longer unlinks a bin that belongs to a different globally installed package.
- On Windows, removing or updating a global package now also cleans up the
-
25c7388: pnpm now rejectsjsr:specifiers whose package name is not a valid npm package name — an empty scope or name (e.g.jsr:@​scope/), path separators inside the name, or any other shapevalidate-npm-package-namerejects — withERR_PNPM_INVALID_JSR_PACKAGE_NAMEinstead of silently converting them into a malformed@jsr/...npm package name. -
25c7388: pnpm now rejects named-registry specifiers (e.g.gh:) whose package name is not a valid npm package name — an empty scope (e.g.gh:@​/bar), path separators inside the name (e.g.gh:@​scope/../name), or any other shapevalidate-npm-package-namerejects — withERR_PNPM_INVALID_NAMED_REGISTRY_PACKAGE_NAMEinstead of passing the name through to registry URLs and metadata cache file paths. -
96da7c5: node-gyp'sgyp_main.pyandgypentrypoints are now packed with the executable bit in thepnpmand@pnpm/exetarballs. Without it, building native addons from source could fail with a permission error. -
99982b9: Sped up resolution and reduced memory use against registries that ignore npm's abbreviated metadata format and always return the full package document (for example, Azure DevOps Artifacts). pnpm now strips such documents down to the abbreviated field set before caching them. Resolution output is unchanged, and registries that honor the abbreviated format (such as the npm registry) pay no extra cost. -
11a7fdd: Sped up offline and--prefer-offlineresolution on large workspaces (e.g.pnpm dedupe --offline,pnpm install --offline). Package metadata loaded from the local cache is now kept in memory, so each package's metadata is parsed once per command instead of once per dependent that references it. -
2c7369d:pnpm pack-appnow rejects--entry/pnpm.app.entryand--output-dir/pnpm.app.outputDirvalues that are absolute paths or escape the project directory via..(or a symlink that resolves outside it), and refuses to write the produced executable when its target path already exists as a symlink (or other non-regular file). This prevents a repository-controlledpackage.jsonfrom embedding host files (such as an SSH key) into the produced executable, writing build artifacts outside the project, or overwriting an arbitrary file through a committed symlink. The new error codes areERR_PNPM_PACK_APP_ENTRY_OUTSIDE_PROJECT,ERR_PNPM_PACK_APP_OUTPUT_DIR_OUTSIDE_PROJECT, andERR_PNPM_PACK_APP_OUTPUT_FILE_NOT_REGULAR.When ad-hoc signing macOS targets,
pnpm pack-appnow runs the systemcodesignby absolute path and resolvesldidto a location outside the project, so a repository-controllednode_modules/.binonPATHcannot hijack the signer. -
ce5d5a5: Relative paths inpatchedDependenciesare now resolved against the lockfile directory when computing patch file hashes, so runningpnpm installfrom a subdirectory no longer fails withENOENTlooking for the patch file in the wrong location #12762. -
ebb4096:pnpm peersno longer reports a conflict for a missing peer dependency that is ignored viapnpm.peerDependencyRules.ignoreMissing. -
dcabb78: Fixed a prototype-pollution hazard when seeding preferred versions: a dependency named__proto__in a manifest or inpnpm-lock.yamlcould write throughObject.prototype(or crash the install) while the preferred-versions map was being built. The maps are now null-prototype objects, so crafted package names land as plain keys. -
f38e696: Hardenedpnpm deploy --forceso it refuses unsafe deploy targets such as workspace roots, parent directories, out-of-workspace paths, and symlinked target parents. -
806c3ec: pnpm no longer warns about ignored project-level auth settings whenPNPM_CONFIG_NPMRC_AUTH_FILEpoints at the project.npmrc— setting it to that file is an explicit opt-in to trusting it, so auth env variables in it are expanded pnpm/pnpm#12480. -
991405e: Restore differential rendering (ansi-diff) to fix duplicated output lines introduced by #12351. -
c121235: Fixed the topological order of--filtered commands (pnpm run,pnpm exec,pnpm publish,pnpm pack,pnpm rebuild) when the selected projects depend on each other only transitively through projects that were not selected. Previously such selected projects could run concurrently or in the wrong order; now a project always runs after the selected projects it transitively depends on, while projects without a real dependency relationship still run concurrently. This now also holds for prod-only filters (--filter-prod), which resolve order through the production dependency graph so transitive production dependencies are respected without pulling back the dev dependencies the filter drops, and for selections that mix--filterwith--filter-prod#8335. -
d539172:pnpm packandpnpm publishno longer follow a symlinked workspaceLICENSEfile when injecting it into a package that has no license of its own. Following the symlink could pack bytes from outside the workspace into the published tarball. -
dcabb78: Fixedpnpm up <pkg>producing a different result than a fresh install of the same manifests would. The resolver now distinguishesupdateRequested(true only for packages that match the user's update target) from the broaderupdateflag, and for the targeted package ignores only its own lockfile-derived preferred-version pins — so the target re-resolves exactly as if its lockfile entries were deleted andpnpm installran. Preferred versions a fresh install applies (manifest pins, versions propagated down the dependency chain, and the vulnerability-avoidance penalties ofpnpm audit --fix) stay in effect, so an update never installs duplicate versions that a reinstall from scratch would not reproduce. When a preferred version holds the update target below the newest version its range admits, pnpm now prints a warning explaining that reaching the newer version everywhere requires an override. -
dcabb78:pnpm update <dep>@​<version>now prints a warning when<dep>is only present as a transitive dependency: the requested version cannot be applied there (updates resolve the target the way a fresh install would), and the warning recommends adding the version topnpm.overridesinstead, which is the mechanism that does pin transitive dependencies. Closes #12744. -
a6c4d5f: When a dependency cannot be found in the registry (404) or the registry has no matching version, and a workspace project with the same name exists only at non-matching versions, the error now reports the available workspace versions (ERR_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE) instead of the raw registry failure pnpm/pnpm#1379. Other registry failures (authorization, network, server errors) still propagate unchanged. The pacquet (Rust) resolver applies the same behavior.
v11.9.0
Minor Changes
bae694f: Some registries generate tarballs on-demand and cannot provide an integrity checksum in their package metadata. In that case pnpm now computes the integrity from the downloaded tarball and stores it in the lockfile, so the entry is verifiable on subsequent installs instead of being written without an integrity (which would fail the next install). This also applies to--lockfile-only: the tarball is downloaded so its integrity can be computed. A lockfile entry that is still missing its integrity is rejected as aERR_PNPM_MISSING_TARBALL_INTEGRITYlockfile verification violation (the install fails closed) rather than being silently re-fetched.6c35a43: Added--exclude-peerstopnpm sbom. Withauto-install-peers(the default), peer dependencies resolve into the lockfile and are otherwise indistinguishable from the package's own dependencies. The flag drops peer dependencies (and any transitive subtree reachable only through them) from the SBOM. CycloneDX 1.7 has no scope or relationship that expresses "consumer-provided peer", so omission is the only spec-clean handling. The flag name matchespnpm list --exclude-peers; note the SBOM flag prunes a peer's exclusive subtree, which is stricter thanpnpm list(which only hides leaf peers).
Patch Changes
-
25a829e:pnpm audit --fixnow writes a single combinedminimumReleaseAgeExcludeentry per package (e.g.axios@0.18.1 || 0.21.1) instead of one entry per version, matching the format documented for the setting. Existing per-version entries inpnpm-workspace.yamlare merged into the combined form rather than left as duplicates. Installs that auto-collect immature versions intominimumReleaseAgeExcludenow report the same combined entries, so the "Added N entries" message matches what is written to the manifest #12534. -
1cbb5f2: Fixed non-deterministic peer resolution that could add or remove an optional transitive peer — for example@babel/core, reached throughstyled-jsx— from a package's peer-dependency suffix across otherwise identical installs, churning the lockfile and causing intermittentpnpm dedupe --checkfailures in CI. When a package's children are resolved by one occurrence (the "owner") and reused by a deeper consumer, whether that consumer inherited the owner's missing peers depended on whether the owner's resolution had finished yet — a race under concurrent resolution. The decision is now a function of the dependency graph's structure rather than resolution-completion order. -
d577eea: Fixed a Windows flakiness inpnpm dlxwhere a failed install could surface a spuriousEBUSY: resource busy or lockederror. The cleanup of a partially-populated dlx cache is now best-effort with retries and no longer masks the original error. -
ec7cf70: Shortened thepnpm dlxcache path so deep dependency trees no longer overflow Windows'MAX_PATH, which could make a dependency's lifecycle script fail withspawn cmd.exe ENOENT. -
05b95ab: Fixedpnpmhanging (and crashing with an unhandled promise rejection) when a non-retryable network error such asSELF_SIGNED_CERT_IN_CHAINoccurs while fetching from a registry. The error is now rejected through the returned promise instead of being thrown inside the detached retry callback. -
d3f68e2: Fix apnpm auditperformance regression on lockfiles that contain dependency cycles. The reachable-vulnerability pruning added in pnpm 11.5.1 only memoized acyclic subtrees, so any node whose subtree touched a cycle — together with all of its ancestors — was recomputed on every query, making the path walk quadratic. Reachability is now computed once per node using Tarjan's strongly-connected-components algorithm, so cyclic graphs are handled in linear time #12212.The audit path walk also no longer recurses, so a deeply nested dependency graph can no longer overflow the call stack, and the install path to each finding is tracked without per-node copying, keeping memory linear in the graph depth.
-
322f88f: Fix failed optional dependency updates so they don't rewrite unrelated dependency specs #11267. -
1488db1: WhenenableGlobalVirtualStoreis toggled on for a project that was previously installed without it, stale hoisted symlinks undernode_modules/.pnpm/node_modulesare now replaced instead of being left pointing at the old per-project virtual store location #9739. -
6545793: Fixedpnpm install --ignore-workspaceoverwriting theallowBuildsmap inpnpm-workspace.yaml. The ignored builds of a package with a build script were auto-populated intoallowBuildseven though--ignore-workspacewas passed, clobbering committedtrue/falsevalues with theset this to true or falseplaceholder #12469. -
fbdc0eb: FixedminimumReleaseAgeExcludeandtrustPolicyExcludeso multiple exact-version entries for the same package behave the same as a single||disjunction entry. Previously only the first matching rule's versions were honored, so a config like[form-data@4.0.6, form-data@2.5.6]could still flagform-data@2.5.6as violatingminimumReleaseAge, while[form-data@4.0.6 || 2.5.6]worked as expected #12463. -
fa7004b: The in-memory package metadata cache is now populated on the exact-version disk fast path, so repeated resolutions of the same package within one install no longer re-read and re-parse the on-disk metadata. In large monorepos this brings the time for adding a new package down from minutes to seconds. The in-memory cache key now also includes the registry, so a package of the same name served by two different registries in a single install can no longer share a cache slot and resolve the wrong tarball. -
0a154b1: Fixedpnpm patchdropping the package name (and leaking internal option fields) when the patched dependency resolves to a single git-hosted version. -
4d3fe4b: The pnpr resolver endpoints moved under the reserved/-/pnprnamespace:POST /v1/resolveis nowPOST /-/pnpr/v0/resolveandPOST /v1/verify-lockfileis nowPOST /-/pnpr/v0/verify-lockfile. The capability handshake atGET /-/pnpradvertises protocol version0to match. This keeps every pnpr-proprietary route in npm's reserved namespace, so it can never collide with a package path. -
0ec878d: Removing a runtime dependency now removes the matchingdevEngines.runtimeorengines.runtimeentry that was materialized from it. Blank runtime selectors are normalized tolatest. -
17e7f2c:pnpm sbomnow emits a CycloneDXissue-trackerexternal reference for components (and the root) whosepackage.jsondeclares abugsURL. Email-onlybugsentries are skipped, since the reference requires a URL. -
a84d2a1: Add@pnpm/resolving.tarball-url, which builds and recognizes the canonical npm tarball URL of a package. It vendorsgetNpmTarballUrl(previously the externalget-npm-tarball-urlpackage) and addsisCanonicalRegistryTarballUrl, the predicate the lockfile writer uses to decide whether a tarball URL is derivable from name+version+registry (and can therefore be omitted frompnpm-lock.yaml).Exposing
isCanonicalRegistryTarballUrllets a custom resolver (pnpmfileresolvers) fronting a proxy that serves tarballs on a non-canonical path (e.g. an ephemerallocalhost:<port>) rewrite the resolved tarball to the canonical form, so nothing host-specific is persisted to the lockfile. Previously this logic was private to@pnpm/lockfile.utils.Two correctness fixes are included while consolidating the logic: the scoped-package unescape now handles uppercase
%2Fas well as%2f(percent-encoding is case-insensitive), and protocol-insensitive comparison strips only a leadinghttp(s)://scheme instead of splitting on the first://(which could truncate URLs containing a later://). -
852d537: Lockfile verification no longer reports a registry metadata fetch failure (for example a403/401on a private registry, or a network error) asERR_PNPM_TARBALL_URL_MISMATCH. When the registry can't be reached to verify an entry, the install now aborts with the registry's own fetch error (such asERR_PNPM_FETCH_403, which already explains the authentication situation) instead of mislabeling a transport failure as lockfile tampering. Registry fetch errors no longer leak basic-auth credentials embedded in the registry URL (https://user:pass@host/) into their message.
v11.8.0
Minor Changes
c112b61: Added a--dry-runoption topnpm install. It runs a full dependency resolution and
✂ Note
PR body was truncated to here.
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.

Changes
- Replaces
os.cpus().lengthwithos.availableParallelism()when sizing the image optimization queue ingenerate.ts.os.cpus().lengthreturns the host CPU count and ignores cgroup CPU quotas — in a--cpus=2container on a 4-core host it returns 4, so Astro starts 4 concurrent image pipelines instead of 2. Each pipeline holds a fully decoded source image in sharp/libvips native memory, directly multiplying peak memory and causing OOM kills in memory-limited containers. os.availableParallelism()respects cgroup quotas and CPU affinity masks. On unconstrained hosts it returns the same value asos.cpus().length, so the change is behavior-neutral outside containers.
Testing
- No new tests added. The fix is a single-line API swap; all 111 existing image tests pass unchanged.
Docs
- No docs update needed. This is an internal queue-sizing detail with no user-facing API change.
Closes #17425

Other pnpm concepts are also linked so I figured it would make sense to have a link for pnpm workspaces as well
Changes
- changes CONTRIBUTING.md by adding a link for pnpm workspaces.
Testing
not relevant only markdown file was changed
Docs
not relevant only markdown file was changed

Closes #17381
Changes
- Fixes
create-astrosilently producing an empty project on Linux when the target directory path contains non-ASCII characters (Turkish, German, French, Scandinavian, etc.).modern-tar(a transitive dep via@bluwy/giget-core) unconditionally rewrites destination paths to NFD (decomposed Unicode) form. On byte-preserving filesystems like ext4, NFD and NFC are distinct byte sequences, somodern-tarextracts template files into a separate NFD-encoded sibling directory while the CLI still exits 0 — leaving the user's NFC directory with onlyAGENTS.md/CLAUDE.mdand nopackage.json,src/, orastro.config.mjs. - Adds
relocateNFDFiles()intemplate.ts, called immediately afterdownloadTemplate. It computes the NFD variant of the resolved working directory, checks whether a separate NFD-encoded directory was created (by comparing inodes — same inode means macOS/APFS normalized them to one entry, so no action needed), moves all entries from the NFD directory to the correct NFC directory viafs.renameSync, and removes the now-empty NFD directory tree.
Note: The ideal long-term fix is upstream in
modern-tar— NFD-normalizing tar entry names for comparison is defensible, but rewriting the destination path's Unicode form is not. This PR is a targeted workaround increate-astroto unblock affected users.
Testing
- Adds
packages/create-astro/test/units/relocate-nfd.test.tswith 3 unit tests: moves files from the NFD directory to the NFC directory (the main fix path); no-op for ASCII-only paths where NFC and NFD are identical; no-op when no NFD sibling directory exists. All three tests auto-skip on macOS where HFS+/APFS treats NFC and NFD as the same filesystem entry.
Docs
- No docs update needed; this is a transparent fix to CLI scaffolding behavior with no user-facing API changes.

Changes
Fixes #17398
<my-element popover> rendered as popover="true" (and popover={false} as popover="false"), because handleBooleanAttribute stringifies boolean attributes on custom elements. The Popover API only accepts "auto", "manual", or the attribute being absent — and popover="false" actually enables a manual popover, inverting the author's intent.
popover is now always rendered as a bare boolean attribute (or omitted) regardless of tag name, matching the existing behavior for built-in elements. Explicit string values like popover="auto" are unaffected, as is the custom-element stringification for download/hidden.
Testing
Added regression cases to test/units/app/astro-attrs.test.ts covering popover={true} (bare attribute), popover={false} (omitted), and popover="auto" (kept) on a custom element. The true-case fails before the fix ('true' !== '') and passes after. 150 tests across the attribute/custom-element rendering suites pass locally.
Docs
No docs change needed — this aligns rendering with documented Popover API semantics. Changeset included.
Note: this fix was developed with AI assistance (Claude) and verified locally as described above.

Related Issue
Closes #17420
Changes
This PR fixes two issues in the AstroSession runtime (packages/astro/src/core/session/runtime.ts).
Route session diagnostics through AstroLogger
Session runtime warnings were previously emitted using console.error, bypassing Astro's structured logging system. This prevented these diagnostics from respecting the configured logger and log level and made them unavailable to custom log sinks.
This PR updates the session runtime to use AstroLogger for these warning paths, ensuring logging behavior is consistent with the rest of the Astro runtime.
Reset partial session state after regeneration failures
When #ensureData() throws during regenerate(), the session falls back to a new empty data store but leaves the internal #partial flag unchanged.
This PR resets the partial state after the recovery path completes, ensuring the regenerated session is internally consistent and preventing unnecessary storage access on subsequent session operations.
Summary
- Route session runtime diagnostics through
AstroLogger. - Ensure regenerated sessions are no longer marked as partial after recovery.
- Keep session state consistent after regeneration failures.
- Preserve existing public API behavior.
Testing
The implementation has been verified against the reported issue and resolves both behaviors described in #17420.
Docs
No documentation changes are required.
These changes affect only the internal session runtime implementation and do not introduce any changes to Astro's public API or user-facing behavior.

Summary
This is the adapter half of #17227, split out as promised, and it is a minor because it adds one public function.
An adapter almost always needs a request's URL before it hands the request to app.match() and app.render(): to check the static-asset manifest, to read a routing param, to tag a cache entry. Every one of those steps parses the same immutable string over again. On main a typical SSR request parses request.url three or four times: once in the adapter, twice inside match() (once for the asset check, once for the domain-i18n probe), and once in FetchState.
getRequestURL(request) parses request.url on first use and keeps the result on the request, so everything that asks for it afterwards gets the same object for the cost of a symbol lookup (~6 ns versus ~186 ns).
import { getRequestURL } from 'astro/app';
const url = getRequestURL(request);The returned URL is shared with the rest of the request, so it is read-only by contract. Code that needs to rewrite a URL builds its own with new URL(request.url).
Why not the RenderOptions.url from the original PR
The first revision of #17227 threaded a pre-parsed URL through RenderOptions, added a third parameter to match(), and changed createRequestFromNodeRequest to return { request, url }. Three API changes, and the doc comment had to ask callers to honor two rules that nothing enforced: the URL must equal new URL(request.url), and it must not be reused after render, because the pipeline normalizes it in place. TrailingSlashHandler derives redirects from that object, so a caller that passed a mismatched URL would have silently changed redirect behavior.
Deriving the URL from request.url on demand removes both rules rather than documenting them:
- It cannot disagree with the request.
request.urlis immutable per the Fetch spec, and a rewritten request is a different object with its own entry. - It needs no signature changes, so
createRequestFromNodeRequestkeeps returning aRequestand hands over the URL it had already parsed. (Changing that return type would have been a breaking change to a published export, not aminor.) - It is one concept instead of three, and community adapters get it too.
Why FetchState still parses its own
Deliberately, and this is the part that makes the shared object safe. FetchState rewrites url.pathname and hands the result to user code as Astro.url / context.url, so it cannot use a read-only object.
Cloning the shared URL instead is not an option: constructing a URL from another URL serializes and re-parses it, so it costs more than parsing request.url again (255 ns vs 186 ns, measured below). There is no cheap defensive copy. A shared URL can only go to readers, which is why FetchState keeps its own parse.
What changed
- Adds
packages/astro/src/core/app/request-url.tsand exportsgetRequestURLfromastro/app. It follows the existingrender-options.ts, which already carries per-request data on aRequestbehind aSymbol.for. BaseApp.match(),getPathnameFromRequest()andcomputePathnameFromDomain()use it.createRequestFromNodeRequestandcreateRequesthand over the URL they already parsed to build theRequest, guarded onrequest.url === url.hrefso aRequest-constructor normalization can never seed a mismatched URL. Signatures unchanged.- Cloudflare:
matchStaticAssettakes theRequestinstead of a URL string; the cache provider uses the shared URL. - Vercel: the serverless entrypoint uses the shared URL for its param reads, and rewrites a copy of it for the
realPathoverride rather than mutating the shared object. - Netlify: only the cache provider. Its SSR function never parsed a URL of its own, so there was nothing else to share and it stays as it is.
- Node: no adapter change at all; it benefits from the seeding above.
Follow-up
Astro core still re-parses request.url in a couple of spots this PR does not touch: the in-memory cache provider and the trailing-slash handler. A follow-up can route those through getRequestURL() too, so core matches the adapters on one convention. #17227 already does the equivalent by other means and can be reconciled onto the helper once this lands.
Testing
astro unit suite 3174 pass / 0 fail (1 skipped). @astrojs/cloudflare cf-helpers 18/18 and fetch-lazy-init 4/4. @astrojs/vercel path-override-security 2/2 and isr 6/6; the latter covers the honored realPath rewrite that this touches. astro and the three adapters (@astrojs/cloudflare, @astrojs/netlify, @astrojs/vercel) build and typecheck clean.
The benchmark harness asserts the cache's semantics against the real shipped function: one object per request, always matching request.url, never shared across requests.
Benchmarks
Median of 7 runs, local (WSL2, Node 24):
| Operation | ns/op |
|---|---|
getRequestURL(request) (cached) |
6 |
new URL(request.url) (what it replaces) |
186 |
new URL(urlObject) (why nothing clones) |
255 |
On one request, the adapter plus match() do 3 parses before and 1 after. With FetchState's own parse, a typical SSR request goes from three or four down to two.
This is small next to HTML rendering. It shows up on light responses (endpoints, redirects, 304s) and on short-lived serverless instances, where per-request setup is a bigger slice of the work and CPU time is billed directly.
Investigated and measured with help from Claude Opus 4.8.

Summary
This is a follow-up to #17227, which set out to reduce how often the request pipeline parses request.url. Profiling that path turned up a cost bigger than the parse it was trying to save, and this addresses it.
normalizeUrl rewrites url.pathname twice on every SSR request. For an ordinary path like /about both writes are no-ops, since the pathname is already decoded and has no duplicate slashes. They are not free, though: assigning url.pathname re-parses and re-serializes the whole URL, which costs more than parsing a URL from scratch.
Skipping each write when it would not change the value takes the normalization of a plain request path from ~858 ns to ~421 ns. Behavior is unchanged: a write whose target already equals the current pathname is a no-op by definition.
normalizeUrl is on the hot path of every SSR request: FetchState calls it to build state.url, which is what user code sees as Astro.url. createNormalizedUrl covers the rewrite path.
This is internal to astro core, adds no API, and is a patch.
What changed
normalizeUrlguards eachurl.pathnameassignment on the value actually changing (packages/astro/src/core/util/normalized-url.ts). That is the whole change.- Adds unit tests for
normalizeUrl, which had none.
The ordering subtlety
The collapse has to stay after the decode is written back, rather than being folded into it. The pathname setter rewrites \ to /, so a decoded backslash only becomes a duplicate slash once it has been assigned:
/a%5C/b -> decode -> /a\/b -> assign -> /a//b -> collapse -> /a/b
Collapsing the decoded string before assigning it would leave /a//b. Nothing pinned that ordering before, so the new tests do.
Testing
astro unit suite: 3185 pass, 0 fail (1 skipped); tsc -b clean. The security tests that cover this normalization (test/units/app/encoded-backslash-bypass, double-slash-bypass, double-encoding-bypass, malformed-uri, trailing-slash) pass unchanged.
The benchmark harness asserts, on 12 inputs covering plain, encoded, multi-encoded, duplicate-slash, backslash, reserved-character and query/hash paths, that the old and new forms produce identical output, and that both match the real shipped normalizeUrl.
Benchmarks
Median of 7 runs, local (WSL2, Node 24). Both sides call the real validateAndDecodePathname and collapseDuplicateSlashes:
| Request path | Before | After |
|---|---|---|
/blog/post-1 (ordinary) |
858 ns | 421 ns |
/ (root) |
754 ns | 340 ns |
/a%20b (encoded) |
949 ns | 742 ns |
/a//b (duplicate slash) |
832 ns | 659 ns |
The parse itself is ~190 ns of each row; the rest is the two writes. Encoded and duplicate-slash paths still need the first write, so they only save the second.
For context on the primitives (same machine):
| Operation | ns/op |
|---|---|
new URL(string) |
186 |
new URL(urlObject) (clone) |
255 |
url.pathname = x (one write) |
212 |
On a render-heavy page this is lost in the noise, since HTML rendering dominates. It shows up on light responses (endpoints, redirects, 304s), where per-request setup is a bigger slice of the work.
Where this matters more
A long-lived Node server amortizes JIT warmup over millions of requests and only ever sees the steady-state figure above. A serverless instance is short-lived and serves few requests, so much of its traffic runs while the isolate is still warming, where the url.pathname setter is interpreted with cold inline caches and the saving is larger in absolute terms:
| Regime | Before | After | Saving |
|---|---|---|---|
| Steady state (200k warmup) | 858 ns | 421 ns | 437 ns (51%) |
| Warming (first 100 requests of a fresh process) | 3386 ns | 2702 ns | 684 ns (20%) |
That also makes it billable: Cloudflare Workers bills CPU-ms and enforces a CPU limit, and Lambda-based hosts bill GB-ms of wall clock.
It does not, however, improve a true cold start. The very first invocation in a fresh process is dominated by ~85 µs of one-time URL-machinery init, where the difference is indistinguishable from noise (the sign flips between runs).
Investigated and measured with help from Claude Opus 4.8.

Changes
While loading the manifest, deserializeManifest builds every route twice. This is the loop it runs at startup:
for (const serializedRoute of serializedManifest.routes) {
routes.push({
...serializedRoute,
routeData: deserializeRouteData(serializedRoute.routeData),
});
// redundant: recomputes the same route and writes it back onto the
// input object, which is never read again
const route = serializedRoute as unknown as RouteInfo;
route.routeData = deserializeRouteData(serializedRoute.routeData);
}Only the first deserializeRouteData matters. Its result goes into the routes array the function returns. The second call does the exact same work again (including recompiling the route's regex), then assigns it onto serializedRoute, which nothing touches after this. So it's wasted on every startup.
This PR deletes those two lines. Nothing else changes.
Why it's safe
The removed line wrote to the input, so the one thing to rule out is a caller reading serializedManifest.routes[i].routeData back after the call. There are two callers, and neither does:
loadManifest(core/app/node.ts) passes a freshJSON.parseresult and returns right away.- The generated
virtual:astro:manifestmodule passes an inline literal and only ever uses the returned manifest'sroutes.
The returned value is fine too: its routes is a separate array and each kept route is its own copy, so nothing downstream points at the field that was being mutated. The only way to notice the difference would be third-party code that calls the exported deserializeManifest and depends on it mutating the object you handed it, which was never intended (that as unknown as RouteInfo write was a leftover).
Impact
Small, but free, and it lands on the cold-start path: every server adapter (Node, Vercel, Netlify, Cloudflare) runs this when it calls createApp(). It helps most on apps with a lot of routes on short-lived serverless instances, where startup CPU is billed.
Testing
astrounit tests: 3174 pass, 0 fail. Covers building anAppfrom a manifest and running match and render.@astrojs/nodeintegration tests: 178 pass, 0 fail. A real build and serve, so it exercises the whole thing end to end.- Cold-start micro-benchmark (fresh process per sample, no warmup): about 13% off the manifest route loop at 200 routes, and positive but within noise at 50 and 1000. It's modest because V8 caches regex compilation, so compiling the same source again right after the first time is nearly free. What you save is the extra allocation, not a real recompile.
Docs
None. This is internal and changes no behavior or public API.
Investigated and measured with Claude Opus 4.8.

Changes
- Prevent aborted request bodies from being reported as unhandled rejections.
- Share one process-level rejection listener across app handlers while keeping request context isolated with
AsyncLocalStorage. - Log genuine unhandled rejections once, including the request URL, through the configured adapter logger.
- Add a standalone regression fixture covering interrupted JSON requests and JSON logging.
Testing
- Added a regression test that sends an incomplete JSON request and closes the socket, verifying that the server remains running without logging
ECONNRESETor an unhandled rejection. - Added coverage verifying that a genuine unhandled rejection is logged exactly once with its request URL through the JSON logger.
Docs
No documentation changes are needed. This corrects internal request-abort handling and does not change the public API or configuration.

Changes
- Updates
@cloudflare/vite-pluginto^1.45.1so generated wrangler config no longer includes the removedlegacy_envfield - Updates the wrangler peer and development dependency to
^4.112.0 - Adds regression coverage for the generated
wrangler.json
Testing
pnpm exec turbo run build --filter=@astrojs/cloudflarepnpm --filter @astrojs/cloudflare testpnpm peers check- Added a regression test confirming that generated config file does not contain
legacy_env
Docs
No documentation changes are needed

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.1.2
Patch Changes
-
#17445
a5f7230Thanks @ocavue! - Updates dependencycookieto v2. Cookie values made entirely of URL-safe characters are no longer percent-encoded inSet-Cookieheaders; encoded values round-trip exactly as before. -
#17402
a89c137Thanks @farrosfr! - Fixes a bug where mutatedAstro.localsduring the request lifecycle are lost and not passed to custom error pages (404.astro/500.astro) -
#17405
91992efThanks @Araluma! - Prevents an unhandled promise rejection from the prefetchfetchfallback. In WebKit (Safari),<link rel="prefetch">is unsupported, so prefetch uses thefetch()fallback; on a flaky connection that fetch rejects withTypeError: Load failed, and because the promise was not awaited or caught, it surfaced as an unhandled rejection to the page's global error handlers. The best-effort prefetch now swallows the failure with.catch().

What
The prefetch fetch() fallback is a floating promise with no .catch(). This PR adds .catch(() => {}) so a failed prefetch can't surface as an unhandled rejection.
Why
In packages/astro/src/prefetch/index.ts, prefetch() uses <link rel="prefetch"> when supported and otherwise falls back to fetch(url, { priority: 'low', headers }). WebKit (Safari) does not support <link rel="prefetch">, so Safari always takes the fetch fallback. On a flaky connection that fetch rejects with TypeError: Load failed, and because the promise is neither awaited nor caught, it bubbles up as an unhandled promise rejection to the page's global error handlers.
For sites with production error monitoring, this shows up as recurring noise (window.onunhandledrejection → "Load failed") on Safari/iOS, with no filename/stack to diagnose it — it is not an application error, just a best-effort prefetch that didn't complete.
The fallback was added in #10464 (prefetch on Firefox/Safari); the missing .catch() slipped past no-floating-promises (enabled in #11089) because it's in this fallback branch.
Change
- fetch(url, { priority: 'low', headers });
+ // best-effort hint — swallow network failures (WebKit fetch fallback)
+ fetch(url, { priority: 'low', headers }).catch(() => {});A prefetch is a best-effort hint; swallowing the failure is the correct behavior (the real navigation will surface any genuine error). Changeset included (patch).

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

Fixes #17382
Changes
- Ensures request-scoped
Astro.localsmutations made during the request lifecycle are preserved and passed to custom error pages (404.astro/500.astro). - Implemented by defining
localsas a dynamic getter onrenderOptionswithinFetchState, rather than copying the static reference at constructor time.
Testing
- Adds a new unit test in
packages/astro/test/units/app/locals.test.tsto assert that error pages can still access mutated request-scopedlocalseven if the middleware is skipped during the error render fallback.
Docs
- No docs update is needed as this is a bug fix aligning behavior with existing expectations.

Summary
- Return 404 for unknown parameters matching prerendered dynamic endpoints.
- Add a Node adapter regression test and an @astrojs/node patch changeset.
Root cause
The adapter skipped prerendered pages before SSR but still rendered prerendered endpoints. Unknown endpoint parameters therefore returned 500 instead of 404.
Fixes #17392
Validation
- Node adapter build
- prerender integration test
- git diff --check
What is this?
Adds starlight-agentready to the community plugins list.
After each astro build, this plugin submits your docs site to AgentReady — a hosted MCP server that crawls your docs and makes them instantly queryable by AI agents (Claude, Cursor, Windsurf, and any MCP client) with cited, multi-page answers.
npm: https://www.npmjs.com/package/starlight-agentready
GitHub: https://github.com/AshutoshRaj97/agentready-mcp/tree/main/starlight-plugin
Usage
import agentready from 'starlight-agentready'
export default defineConfig({
site: 'https://docs.yoursite.com',
integrations: [
starlight({
plugins: [agentready()],
}),
],
})
Changes
- Preserves the pre-existing behavior of configured script and style resources when element-specific hashes are used.
- When generic resources are explicitly configured, enabling a
-elemdirective with a hash no longer implicitly addsself. Users who want same-origin resources on the element directive can still configureselfexplicitly. - Keeps the implicit
selffallback when no generic resources are configured.
Example case, you have this config:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
security: {
csp: {
scriptDirective: {
resources: ["'none'"],
hashes: [
{
hash: 'sha256-TRUSTED_INLINE_SCRIPT_HASH',
kind: 'element',
},
],
},
},
},
});Prior to this change you get this:
Content-Security-Policy:
script-src 'none';
script-src-elem 'self' 'sha256-TRUSTED_INLINE_SCRIPT_HASH';
With 'self' overriding the more strict hash.
Testing
- Adds renderer coverage for script and style element directives with configured generic resources.
Docs
- No docs update needed; this restores the documented resource behavior.

Changes
- Escapes generated transition styles for inline CSS contexts.
- Serializes dev toolbar metadata
- Serializes server island URLs
Testing
- Adds unit coverage for style, script, attribute, and URL serialization.
Docs
- No docs update needed because public APIs are unchanged.

Changes
- If an integration sets the logger in
updateConfig()it currently isn't actually updated - This PR makes it so that when there's a change, the destination is loaded and updated (required a little refactoring of the logger loading)
Testing
Manually + test
Docs
Changeset

Changes
- That was a review I made at some point but it got forgotten
- This PR removes the unused generic from
AstroLoggerDestinationsince the chunk is always anAstroLoggerMessage - Some people may consider this breaking but I don't think it is, especially we don't document it anywhere (e.g. in https://docs.astro.build/en/reference/logger-reference)
Testing
Build should pass
Docs
- Changeset
- withastro/docs#14245

Changes
- When trying to use loggers in a project, I found this inconsistency
Testing
Adds a unit test (help from Claude)
Docs
- Changeset
- No docs PR, updating the types jsdocs is enough

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.1.0
Minor Changes
-
#17302
5f4dc03Thanks @astrobot-houston! - Adds a newdeferRenderoption to theglob()content loaderWhen set to
true, renderable entries (such as Markdown) are not rendered during content sync. Instead, rendering is deferred until the entry is actually rendered in a page, using the same on-demand path that.mdxfiles already use.This reduces memory usage during
astro buildfor large collections whose rendered output is much larger than the source — for example, Markdown that uses heavy rehype plugins likerehype-katex. Such builds could previously run out of memory while storing the eagerly-rendered HTML for every entry.// src/content.config.ts import { defineCollection } from 'astro:content'; import { glob } from 'astro/loaders'; const docs = defineCollection({ loader: glob({ pattern: '**/*.md', base: 'src/content/docs', deferRender: true }), });
By default
deferRenderisfalse, preserving the existing behavior of rendering entries eagerly during sync so their rendered HTML can be cached across builds. -
#17296
30698a2Thanks @ematipico! - Adds a new experimentalcollectionStorageoption for controlling how the content layer persists its data storeBy default, Astro serializes the entire content layer data store to a single file (
.astro/data-store.json). For very large content collections, this file can grow large enough to hit platform file-size limits.Set
experimental.collectionStorage: 'chunked'to instead split the data store across many smaller, content-addressed files inside a.astro/data-store/directory, described by a manifest:// astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ experimental: { collectionStorage: 'chunked', }, });
Because each part file is named by a hash of its contents, unchanged parts keep the same name across builds and are not rewritten, and identical parts are deduplicated. The default value is
'single-file', which preserves the current behavior. -
#17214
44c4989Thanks @ematipico! - Adds support for the more specific CSP directivesscript-src-elem,script-src-attr,style-src-elem, andstyle-src-attrthrough a newkindoption.Previously,
CSPwas only scoped to genericscript-src/style-srcdirectives. Now each source or hash can be scoped to a narrower directive — for example, to allow inlinestyleattributes (such as those fromdefine:varsor Shiki) without loosening the policy for your<style>and<link>elements.Scoping sources and hashes in your config
Each entry in
resourcesandhashescan be an object with akindproperty. Depending on whether you usescriptDirectiveorstyleDirective,"element"targetsscript-src-elemorstyle-src-elem,"attribute"targetsscript-src-attrorstyle-src-attr, and"default"(the same as a bare string or hash) targetsscript-srcorstyle-src.// astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ security: { csp: { scriptDirective: { resources: [{ resource: 'https://cdn.example.com', kind: 'element' }], }, styleDirective: { resources: [{ resource: "'unsafe-inline'", kind: 'attribute' }], }, }, }, });
Scoping at runtime
The same
kindoption is available on the runtime CSP API, where the existing methods now also accept an object:ctx.csp.insertScriptResource({ resource: 'https://cdn.example.com', kind: 'element' }); ctx.csp.insertStyleResource({ resource: "'unsafe-inline'", kind: 'attribute' });
-
#17258
84814d4Thanks @astrobot-houston! - Adds a newformat()option to thepaginateutility. Theformat()option is a function that accepts the current URL of the page, and returns a new URL.For example, when your host only supports URLs using the
.htmlextension, you can useformat()to add it to the generated URLs:--- export async function getStaticPaths({ paginate }) { // Load your data with fetch(), getCollection(), etc. const response = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=150`); const result = await response.json(); const allPokemon = result.results; // Return a paginated collection of paths for all items return paginate(allPokemon, { pageSize: 10, format: (url) => `${url}.html`, }); } const { page } = Astro.props; ---
-
#17331
7db6420Thanks @matthewp! - Adds a--ignore-lockflag toastro devfor starting a dev server without checking or writing the lock file, so it can run alongside an already-running dev server for the same project.The new instance is not tracked by
astro dev stop,astro dev status, orastro dev logs.--ignore-lockcannot be combined with--background(or an auto-detected AI agent environment, which runs dev servers in the background automatically) or--force, since those rely on the lock file.astro dev --ignore-lock
-
#17389
16de021Thanks @florian-lefebvre! - Allows passing URL entrypoints when configuring the loggerMatching other APIs like session drivers or font providers, the logger entrypoint can now be a URL:
import { defineConfig } from 'astro/config'; export default defineConfig({ logger: { entrypoint: new URL('./logger.js', import.meta.url), }, });
Patch Changes
-
#17332
4407483Thanks @astrobot-houston! - Fixes the JSON logger crashing withprocess is not definedin non-Node runtimes like Cloudflare's workerd. The JSON logger now usesconsole.log/console.errorinstead ofprocess.stdout/process.stderr, matching the pattern already used by the console logger. -
#17391
186a1e7Thanks @florian-lefebvre! - Fixes a case where an integration could not update the logger withupdateConfig() -
#17394
d9f99e1Thanks @matthewp! - Fixes element-specific CSP directives to preserve the existing behavior of configured script and style resources -
#17374
b2d1b3eThanks @astrobot-houston! - Fixes dev server returning 404 for?urlimported assets when accessed via browser navigation -
#17390
ed71eafThanks @florian-lefebvre! - Removes an unused and undocumented generic from theAstroLoggerDestinationtype -
#17393
092da56Thanks @matthewp! - Hardens generated transition styles, development metadata, and server island URLs when embedding dynamic values

Changes
Fixes #17381.
pnpm create astro silently scaffolds into the wrong directory on Linux when the project path contains non-ASCII characters (e.g. /home/user/Masaüstü/). The root cause is in modern-tar (transitive via @bluwy/giget-core): its normalizeUnicode helper NFD-normalizes any non-ASCII string, and validateBounds applies that to the resolved destination path — not just tar entry names. On byte-preserving filesystems (ext4 and friends) NFC and NFD are distinct paths, so the template lands in a parallel decomposed-form directory tree while the CLI exits 0. Details in my triage comment on the issue.
This PR adds relocateNFDFiles(), called after downloadTemplate(): if an NFD-variant of the target directory exists and is a distinct directory (inode check, so normalization-transparent filesystems like APFS/HFS+ are a no-op), its contents are moved back to the intended NFC path and the empty NFD directory chain is removed. The upward rmdir walk stops at the first shared ancestor, which is guaranteed non-empty because the real NFC target lives inside it.
The fix and test originate from the triage bot's exploration branch (triagebot/fix-17381, cherry-picked with authorship preserved); I verified the root cause independently against modern-tar's source, reviewed the relocation/cleanup logic, and added the missing changeset.
A create-astro-side relocation is a mitigation — the durable fix belongs upstream in modern-tar (NFD-normalizing tar entry names for comparison is defensible; rewriting the destination path's Unicode form is not). Happy to open an upstream issue there as a follow-up, but create-astro users are broken today.
Testing
New unit test packages/create-astro/test/units/relocate-nfd.test.ts covers the move-and-cleanup path, the ASCII no-op, and the missing-NFD-dir no-op. Note the suite self-skips on normalization-transparent filesystems (macOS), so the meaningful assertions execute on the Linux CI runners — which is also the only platform where the bug reproduces.
Docs
n/a — bug fix, no behavior change for correctly-scaffolded projects. Changeset included.


Changes
- Local font filenames (
/_astro/fonts/<hash>.<ext>) are now deterministic regardless of where the project is checked out. Previously,FsFontFileContentResolver.resolve()returnedabsolutePath + fileContent, so the same font produced different hashes on different machines or CI workspace paths. Now only the file content is hashed, matching how all other bundled assets (JS, CSS, images) are identified.
Testing
- Updated the existing unit test in
packages/astro/test/units/assets/fonts/infra.test.ts: the assertion for the absolute-path case now expects'content'instead ofurl + 'content', reflecting that the path is no longer included in the resolved value.
Docs
- No docs update needed — this is a bug fix restoring expected deterministic behavior with no API changes.
Closes #17377

Changes
- Builds on #16957 by adding the missing component
<style>block cases to dev CSS HMR handling.
Fixes stale dev CSS after editing component style blocks that are compiled into virtual CSS modules.
Astro’s dev CSS collector already treats style block requests like these as CSS:
*.astro?astro&type=style...*.svelte?svelte&type=style...*.vue?vue&type=style...
But astro:hmr-reload only recognized real CSS file paths, so these style block modules could skip the per-route virtual:astro:dev-css:* invalidation path. This caused stale SSR/dev CSS after HMR edits.
This PR classifies Astro, Svelte, and Vue style block requests as style modules, invalidates per-route dev CSS when client CSS HMR can handle the update, and preserves SSR invalidation/full reload when no client module exists to apply the update.
Also addresses #17083
Testing
-
Added unit coverage for Astro, Svelte, and Vue style block virtual modules invalidating per-route dev CSS modules.
-
Added coverage for SSR-only style block modules using SSR invalidation/full reload.
-
Added negative coverage for non-style component queries, raw CSS imports, unrelated
type=stylerequests, and component-style-looking requests without the matching compiler marker.
Docs
- No docs changes. This fixes dev-server HMR behaviour without changing user-facing APIs.

To speed up the test suite, I proceeded with migrating the eligible test cases from E2E to integration tests.
Changes
Regarding to packages/astro/e2e/core-image-styles.test.ts test file.
// Wait for client-side CSS injection
await page.waitForLoadState('networkidle');
I had concerns about this comment. however, I verified it by disabling JavaScript in my browser and [data-astro-image] CSS is already present within the <style> tag in the initial HTML element.
Since I migrated the test from an E2E test to an integration test.
Testing
I migrated the commits to reproduce the test error and verified the migration process.
reproduce issue
Solved issue


Summary
Fixes a bug where cloudflareConfigCustomizer would silently override an explicit cache: { enabled: false } in the user's wrangler config when cacheCloudflare() was configured as a cache provider.
Root Cause
In packages/integrations/cloudflare/src/wrangler.ts, the cache auto-enable condition used a falsy check:
// Before
cache: needsWorkerCache && !config.cache?.enabled ? { enabled: true } : undefined,!config.cache?.enabled evaluates to true for both:
config.cacheisundefined— cache not configured at all (correct: should auto-enable)config.cache.enabledisfalse— user explicitly opted out (incorrect: should be respected)
Fix
Changed to a strict === undefined check so auto-enabling only applies when the user hasn't set the cache field at all:
// After
cache: needsWorkerCache && config.cache?.enabled === undefined ? { enabled: true } : undefined,An explicit cache: { enabled: false } is now preserved through the build. This is consistent with how other fields in the same function (main, compatibility_date, assets) handle their defaults using nullish coalescing.
Why this matters
Workers Cache supports per-entrypoint enablement via the exports block. A valid pattern is to leave the top-level enabled: false while enabling cache on specific named entrypoints — e.g., an uncached gateway entry that routes into a cached worker export. Previously the only workaround was post-processing dist/server/wrangler.json after the build.
Testing
Four new unit tests added under a worker cache describe block in packages/integrations/cloudflare/test/wrangler.test.ts:
- Auto-enables cache when
needsWorkerCache: trueand cache is unconfigured - Does not enable cache when
needsWorkerCache: false - Does not override when cache is already
enabled: true - Does not override explicit
cache.enabled: false(regression test for this issue)
Confirmed working by issue reporter @skezo against the preview build.
Closes #17375

Closes #17177
Changes
- Files imported with
?url(e.g.import pdf from '../downloads/a.pdf?url') now serve correctly in the dev server when accessed via browser navigation. Previously, browsers received a 404 because they sendAccept: text/htmlon navigation, which triggered the route guard — but the guard was computingexistsInSrcincorrectly. - The bug was in
route-guard.ts: resolvingnew URL('.' + pathname, config.srcDir)for a path like/src/downloads/a.pdfproduced<root>/src/src/downloads/a.pdf(double-nested), which doesn't exist. The fix resolves fromconfig.rootinstead and checks containment usingstartsWith, so the path is correctly identified as insidesrcDir.
Testing
- Added
packages/astro/test/units/dev/route-guard-middleware.test.tswith two cases: one that confirmssrc/files pass through the guard on browser navigation (previously failing), and one that confirms root-level files (e.g.README.md) are still blocked.
Docs
- No docs update needed — this restores expected Vite
?urlimport behavior with no API changes.

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.9
Patch Changes
-
#17286
a249317Thanks @astrobot-houston! - Fixes the first browser visit afterastro devstarts triggering an immediate full page reload -
#17369
a94d4a5Thanks @adamchal! - Fixes an issue where a client island could permanently fail to hydrate if the first attempt to load its component failed. Islands now reliably recover from transient import failures, which previously did not work for React components duringastro dev.
@astrojs/vercel@11.0.3
Patch Changes

Changes
- Adds a build-generated token to the internal ISR route rewrite, so the
_isrfunction only applies thex_astro_pathpath override when the token matches the current build's route table. This replaces the previousx-vercel-isrheader check. - Strips the internal
x_astro_pathandx_astro_path_tokenparams from the URL before rendering, so they never reach user code.
Testing
- Updates the ISR route-generation assertions to expect the new token param (normalized to a placeholder, since it's a random per-build value).
- Adds
_isrentrypoint tests covering the override being ignored without a valid token, ignored when onlyx-vercel-isris set, and applied when the valid token is present.
Docs
- No docs update needed. The token and ISR routing params are internal implementation details with no public API change.

Changes
Fixes astro-island's hydration import retry (added in #16412) so it actually recovers in dev, and hardens its e2e test so a page reload can no longer satisfy it.
This unblocks #17286, whose e2e failures are not a false positive: they expose that the retry never worked in dev for React components. Details below.
Why the retry was broken in dev
importWithRetry retried with a URL fragment cache buster (#astro-retry=<ts>). A fragment only changes the module map key of the top-level module and never reaches the server. In dev, @vitejs/plugin-react emits a self-import of the module's own bare URL for react-refresh:
import * as __vite_react_currentExports from "/src/components/Counter.jsx";Once the first import attempt fails, Chrome caches the failed fetch for the bare URL in its module map. The fragment-busted retry still depends on that poisoned bare URL, so the retry rejects with Failed to fetch dynamically imported module even though the network request returns 200.
A query parameter reaches the server, so Vite propagates it into the transformed module's self-import and every module map key in the retried graph is fresh. Verified with in-page probes: after a failed import, a fragment-busted re-import rejects while a query-busted re-import resolves.
Why this was never caught
The e2e test recovers hydration after first failed component import has been passing by accident. Since #12938, buildStart invalidates the content data store and sends a full-reload that Vite buffers and flushes to the first client that connects (the bug reported in #17283). That reload recovered the test page before the island's retry was ever exercised. #17286 removes the spurious reload, which is why its e2e run fails deterministically on both runners.
Test changes
The test now exercises the retry regardless of server reload behavior:
beforeAlldrains startup-buffered HMR messages with a throwaway page, so a bufferedfull-reloadcannot recover the page mid-test.- The test asserts hydration recovers without any main-frame navigation, so recovery via reload now fails the test instead of passing it.
Verification
| island retry | startup reload (pre-#17286) | result |
|---|---|---|
| query (this PR) | present | pass |
| query (this PR) | removed (#17286 applied) | pass |
| hash (current main) | present | fail (test now correctly detects the broken retry) |
Testing
Ran playwright test astro-island-hydration-error locally (Chrome) in all three configurations above.
Docs
N/A, bug fix. Changeset included.

Changes
- Escapes regex metacharacters in
image.remotePatterns(hostname,pathname) andimage.domainswhen generating Netlify Image CDNremote_imagespatterns, so characters like.match literally instead of as wildcards. Previously only.was escaped, and only in some positions. This makes the generated patterns consistent with how Astro matches these values elsewhere.
Testing
- Adds cases covering metacharacters in literal and wildcard (
/*,/**) pathnames and in the hostname.
Docs
- No docs update needed; the documented
remotePatterns/domainsbehavior is unchanged.

Changes
astro preview --opennow correctly opens a browser when using adapters with a custom preview entrypoint (such as@astrojs/cloudflare). Previously, theopenoption was never forwarded from core preview to adapter preview entrypoints.- Adds
open?: string | booleanto thePreviewServerParamsinterface so adapters can receive and act on the value. - Passes
settings.config.server.openfrompackages/astro/src/core/preview/index.tsto the adapter's preview function, matching howheadersandallowedHostsare already forwarded. - Updates the Cloudflare adapter's preview entrypoint to use the received
openvalue instead of hardcodingfalse.
Testing
- No new automated tests — browser-opening behavior cannot be observed in a headless CI environment. TypeScript compilation validates the type contract. Confirmed working by the issue reporter (@kitschpatrol).
Docs
- No docs update needed;
open/--openis an existing CLI flag with no change in user-facing behavior or configuration surface.
Closes #17362

Changes
e2e tests are heavy and down the developer experience when run e2e test in local or CI/CD. To improve this, I want to migrate these tests from e2e to integration.
packages/astro/e2e/vite-virtual-modules.test.ts
This e2e test depends on only HTML element, no need to e2e test for this. So, it moved to integration test.
Testing
Reverted PR (#13902 ) changes temporarily to reproduce the issue in the tests, and then migrated the tests to integration.
Reproduce issue on test
Reflect #13902 in code testing
Related PR

Closes #17322
Regression from the Zod 4 upgrade (#14956). reference() used to validate that the referenced entry existed; afterwards it blindly wraps any string into { id, collection }, so e.g. author: "John-Doe" passes validation even though the glob loader slugified the real id to john-doe — producing a broken reference at render time with no build-time signal.
This re-adds validation: a new #validateReferences(logger) runs after the parallel collection load in #doSync, walks every synced entry's data, and logger.warns when a reference points to a missing entry in an otherwise-loaded collection. References into un-loaded / selective-sync collections are skipped to avoid false positives.
- Added unit tests (warns on dangling ref / silent on valid ref).
- content-layer suite → 66 passed;
tsc -bclean. Changeset included.
Note: I went with a build-time warn (non-breaking) rather than a hard error — happy to switch to throwing if you'd prefer to restore the pre-regression behavior.

Changes
- Building from a symlinked (or junction-linked) directory dropped all CSS from the output.
resolveRoot()usedpath.resolve(), which does not follow symlinks, soconfig.rootkept the symlink path while Vite/Rollup resolve module IDs to the real path (resolve.preserveSymlinksisfalse). ThepagesByViteIDkeys then never matched the Rollup module graph IDs, sogetPageDataByViteID()returnedundefinedand CSS was never associated with any page. resolveRoot()now runsfs.realpathSync()on the resolved path soconfig.rootmatches Vite's resolved IDs, with a try/catch fallback for a root that doesn't exist yet.
Fixes #17319
Testing
- Adds
test/symlink-css.test.ts, which builds the same fixture from its real directory and from a symlink to it and asserts the symlinked build has the same CSS as the real one. It fails onmain(the symlinked build has no CSS) and passes with the fix.
Docs
- No docs needed — this restores the documented build behavior for symlinked roots.

When a navigate() was a sameDocument switch, prevent back/forward navigation from triggering a view transition.
This change enables users to replaceState after a navigate() if they want to remove an anchor from the URL without Astro triggering view transitions later, during back/forward navigation.
await navigate("#top", {
history: "push",
...
});
history.replaceState(
history.state,
"",
window.location.pathname + window.location.search
);Testing
- My own site, after backporting to 6.4.8. It used the
navigate -> replaceStatetrick above - new e2e test
Docs
Possibly needs to change https://docs.astro.build/en/reference/modules/astro-transitions/? but these history.replaceState tricks are not documented there to begin with.

Changes
Bumps the compiler to the latest
Testing
N/A
Docs
N/A
Loved working on this documentation, thank you for making Starlight!
Site added: https://bonjourr.fr/docs/

Closes #17348
Changes
- When the prerender handler processes a request (e.g.
/_image),matchRoutenow skips routes withprerender: falsebefore importing their component modules. Previously,/_image's component (@astrojs/cloudflare/image-transform-endpoint) was eagerly imported in the Node prerender environment, where its top-levelimport { env } from 'cloudflare:workers'fails. A prerendered catch-all like[...slug].astrowas enough to trigger this path becausematchAllRoutesreturns it as a candidate for/_image, causing the broad prerender gate inplugin.tsto invoke the prerender handler for the request. - Threads the existing
prerenderOnlyflag fromhandleRequestthroughdevMatchintomatchRouteto carry the filtering down to the right place.
Testing
- No new test added — a dedicated fixture would need to combine
prerenderEnvironment: 'node', a catch-all prerendered route, and the Cloudflare image endpoint together. Existing tests (dev-image-endpoint.test.ts,prerender-node-env.test.ts) continue to pass. The fix was verified manually by the issue reporter.
Docs
- No docs update needed; this restores documented behavior with no API changes.

Summary
Fixes a bug where imageService: 'compile' produced unoptimized images (byte-for-byte source copies with incorrect extensions, e.g. PNG bytes in a .webp file) when prerenderEnvironment was set to 'node'. No warning or error was emitted — the build completed silently.
Closes #17346
Root Cause
Two interacting issues:
-
collectStaticImagesonly existed on the workerd prerenderer. This method installs sharp (or a user-configured image service) intoglobalThis.astroAsset.imageServicebefore the image generation pipeline runs. WhenprerenderEnvironment: 'node', the default Node prerenderer was used instead — which had nocollectStaticImages, so image transforms fell back to the workerd passthrough stub, returning input buffers unchanged. -
The default prerender entrypoint was skipped when
setPrerendererwas called. Astro skips setting the prerender entrypoint whensettings.prerendereris truthy, so restoring it manually was also required.
Fix
In packages/integrations/cloudflare/src/index.ts:
-
astro:build:starthook — Added anelse if (hasBuildImageService)branch forprerenderEnvironment: 'node'. Wraps the default prerenderer with acollectStaticImagesmethod that installs sharp (or the user's custom service) before image generation runs, mirroring the existing workerd prerenderer behavior. -
astro:build:setuphook — Restores the default prerender entrypoint whenprerenderEnvironment: 'node'andhasBuildImageServiceare both true, since it gets skipped whensetPrerendereris called.
Before: (before: 12kB, after: 12kB) — output .webp is actually PNG bytes
After: (before: 12kB, after: 0kB) — proper WebP output (~494 bytes)
Testing
Added packages/integrations/cloudflare/test/compile-image-service.test.ts with tests covering:
imageService: 'compile'withprerenderEnvironment: 'node'(the bug scenario)- User-configured custom image service with
prerenderEnvironment: 'node'
All existing tests continue to pass.

Changes
Running astro check (or the @astrojs/language-server CLI checker) against the TypeScript 7 native compiler currently crashes with an opaque error:
Cannot read properties of undefined (reading 'fileExists')
at AstroCheck.getTsconfig (.../@astrojs/language-server/dist/check.js)
Root cause: the checker is built on Volar and TypeScript's programmatic Language Service API (ts.sys, ts.findConfigFile, LanguageServiceHost, …). The TypeScript 7 native compiler doesn't ship that API yet — require('typescript') resolves to a module that only exposes version and versionMajorMinor:
const ts = require('typescript'); // typescript@7.0.2
Object.keys(ts); // -> ['version', 'versionMajorMinor']
ts.sys; // -> undefined
ts.findConfigFile; // -> undefinedSo getTsconfig() dereferences this.ts.sys.fileExists on undefined and throws before the user gets any hint about what went wrong.
This PR adds an early guard in AstroCheck.initialize() that detects a TypeScript build lacking the programmatic API and throws an actionable error instead:
The TypeScript module loaded (found 7.0.2) does not expose the programmatic API that
astro checkrelies on. This is expected with the TypeScript 7 native compiler, which does not ship that API yet. Use TypeScript 6.x forastro checkfor now — see withastro/roadmap#1321 for the tracking issue.
This does not add TypeScript 7 support (that's blocked on TypeScript's new API, tracked in withastro/roadmap#1321) — it just turns a confusing crash into a clear, actionable message.
Testing
- With a TS7-shaped module (
{ version, versionMajorMinor }): the new error is thrown instead of thefileExistscrash. - With a real TypeScript (
ts.sys/ts.findConfigFilepresent): the guard is a no-op and existing behavior is unchanged. pnpm --filter @astrojs/language-server buildpasses; the file matches Prettier config.
Docs
Not needed — no public API or documented behavior changes; only the failure message for an already-unsupported configuration.

Changes
- Replaces the restart-per-propagator head collection scan with a single traversal of the live
Set, restoring linear rendering performance on pages with many component instances. - Fully drains pending async slot evaluations before advancing the iterator, preserving discovery order and ensuring propagators registered after an
awaitare still collected.
Testing
- Adds a unit test that counts
Setiterator steps and fails if collection returns to the previous quadratic scan pattern. - Adds a unit test covering async slot work queued during
init(), including the late propagator registration and complete queue drain.
Docs
- No documentation update is needed because this restores expected rendering performance without changing public APIs or behavior.
Closes #17343

Changes
- What does this change?
This updates the news box in the basic example to promote the new features for Astro 7 and link to the Astro 7 blog post
OLD:

I did not run pnpm changeset because this only changes files in /examples/
Testing
Besides verifying the change using pnpm run dev, I did not test anything because it is a copy change
Docs
No docs changes needed because this simply updates copy and link to the new blog post

Changes
Fixes #17340
This PR makes it so the Sätteri processor uses HAST for code blocks like the Unified pipeline does, leading the final MDX to render it using normal components instead of raw strings.
Testing
Added new tests and current ones should pass
Docs
N/A

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.8
Patch Changes
-
#17363
3f4efc5Thanks @astrobot-houston! - Fixesastro preview --opennot opening a browser when using an adapter with a custom preview entrypoint, such as@astrojs/cloudflare -
#17313
e2e319dThanks @ronits2407! - Exposes theAstroRuntimeLoggerinterface to allow users to properly type the logger functions at runtime. -
#17328
025cc74Thanks @matthewp! - Fixesastro dev --forcenot replacing an already-running dev server -
#17353
2bba277Thanks @ematipico! - Updates the Astro compiler to the latest version, which fixes many regressions. Refer to the changelog for more details. -
#17344
79a41e0Thanks @adamchal! - Improves rendering performance for pages with many component instances, such as repeated MDX<Content />components. -
Updated dependencies [
64b0d66]:- @astrojs/markdown-satteri@0.3.4
@astrojs/cloudflare@14.1.3
Patch Changes
-
#17363
3f4efc5Thanks @astrobot-houston! - Fixesastro preview --opennot opening a browser when using an adapter with a custom preview entrypoint, such as@astrojs/cloudflare -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/mdx@7.0.3
Patch Changes
- #17341
64b0d66Thanks @Princesseuh! - Fixes customprecomponents not applying to syntax-highlighted code blocks when using the Sätteri Markdown processor with MDX.
@astrojs/netlify@8.1.2
Patch Changes
-
#17368
ee74c28Thanks @matthewp! - Fixes the generated Netlify Image CDNremote_imagespatterns so that regex metacharacters (such as.) inimage.remotePatterns(hostname,pathname) andimage.domainsare matched literally instead of behaving like wildcards. This makes the generated patterns consistent with how Astro matches these values elsewhere. -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/language-server@2.16.12
Patch Changes
- #17345
5196fb4Thanks @kkhys! - Fixes an opaqueCannot read properties of undefined (reading 'fileExists')crash whenastro checkruns against the TypeScript 7 native compiler. The native compiler does not ship the programmatic API the checker relies on yet, soastro checknow fails early with a clear message pointing to the tracking issue instead.
@astrojs/markdown-satteri@0.3.4
Patch Changes
- #17341
64b0d66Thanks @Princesseuh! - Fixes customprecomponents not applying to syntax-highlighted code blocks when using the Sätteri Markdown processor with MDX.

Fixes #17329.
Bug
When Astro renders a custom error page (`404.astro` / `500.astro`), any cookies set on the error page via `Astro.cookies.set()` never reached the final response. If the original page also set a cookie, the error page's cookies vanished entirely.
Root cause
In `mergeResponses` (packages/astro/src/core/errors/default-handler.ts):
- The merged response's headers were built by appending every header from `originalResponse.headers` (including any set-cookie entries) into a fresh `newHeaders` Headers object, then skipping any header name from `newResponse.headers` that was already in the seen set. `set-cookie` matches that seen set the moment the original response contributed its own set-cookie entries, so `newResponse`'s set-cookie entries were silently dropped.
- The new cookies were appended to `originalResponse.headers` via `originalResponse.headers.append('set-cookie', cookieValue)`, but the merged response was built from `newHeaders`, which is a separate Headers object copied from the originals before the append. The append went to a Headers object no one else held a reference to.
Repro from #17329
Original page sets `sid=abc` then throws; `500.astro` sets `flash=boom`.
```
Observed final Set-Cookie: ["sid=abc; Path=/"]
Expected: ["sid=abc; Path=/", "flash=boom; Path=/"]
```
Fix
When both responses carry an `AstroCookies` instance, call `originalCookies.merge(newCookies)` to fold the new entries into the original AstroCookies object, then `attachCookiesToResponse` below emits the combined set on the merged response. Appending to `originalResponse.headers` is removed because it never reached the merged response.
`AstroCookies#merge` already exists in `packages/astro/src/core/cookies/cookies.ts` (added in #1522 / similar) and overwrites on name collision, so a same-named cookie from the error page intentionally wins over the original.
Behavior is unchanged when only one response carries cookies (the `else if` branches).
1 file changed, +9/-7.

Changes
- Fixes intermittent CI failures in the
Test (integrations)job (most often on macos-14) that surface as a whole test file failing withError: Unable to deserialize cloned data due to invalid or unsupported versionwhile every individual test in it passes. - Fix is to pass
isolation: 'none'in the non-parallel path so tests run in the runner process itself with no child-process IPC channel to corrupt. This matches the existing "single process" intent of that code path. The parallel path keeps default per-fileprocessisolation.
Testing
N/A
Docs
N/A

Summary
Fixes a crash where astro dev --json (or any project using the JSON logger) would return HTTP 500 for every request when using the Cloudflare adapter.
Closes #17280
Root Cause
The JSON logger's write() method referenced process.stderr and process.stdout directly. When the Cloudflare adapter handles requests inside workerd — Cloudflare's local dev runtime — the process global doesn't exist, causing a ReferenceError on every log call. The error then cascaded: the error handler tried to log via the same broken logger, swallowing the original message and returning a 500.
This is especially easy to hit without explicitly passing --json, because Astro automatically enables JSON logging when it detects it is running inside a coding agent.
Fix
packages/astro/src/core/logger/impls/json.ts now uses console.error/console.log instead of process.stderr.write/process.stdout.write. The console API is available in all JS runtimes including workerd. This matches the pattern already used by the sibling console logger.
The node logger (node.ts) was intentionally not changed — it only ever runs in Node.js where process is guaranteed, and it relies on process.stdout.write to support newLine: false for composing multi-part build output lines (e.g. filename + render timing on the same line). Changing it would break that formatting.
As a side effect, the JSON logger no longer emits a partial (non-newline-terminated) write for newLine: false events, which previously concatenated two JSON objects without a separator — producing unparseable output. Each event now unconditionally gets its own line, which is correct JSONL format.
Changes
packages/astro/src/core/logger/impls/json.ts— replaceprocess.stdout/process.stderrwithconsole.log/console.error; remove now-unusedConsoleStreamtype andnode:streamimportpackages/astro/test/units/logger/destination.test.ts— update tests to spy onconsole.log/console.errorinstead ofprocess.stdout.write/process.stderr.write; add a cross-runtime compatibility test assertingwrite()contains noprocess.reference.changeset/cool-rice-exist.md— patch changeset forastro

Closes #17324
Changes
- Adds
--ignore-locktoastro dev, which skips checking and writing the lock file so a new dev server can run alongside an already-running one instead of erroring. - The
--ignore-lockinstance is intentionally untracked —astro dev stop/status/logscontinue to only ever see the one canonical, lock-tracked server. - Rejects
--ignore-lockcombined with--background(including when background mode is only implied by AI agent detection) with a clear error, since background servers rely on the lock file to be discoverable. Also rejects--force+--ignore-lock(contradictory: replace vs. coexist).
Testing
- Adds unit tests for
isIgnoreLock,getBackgroundIgnoreLockConflict, andgetForceIgnoreLockConflictcovering the flag parsing and both conflict messages.
Docs

Changes
astro dev --forcenow actually replaces an already-running dev server instead of always erroring, matching the behavior already promised by the error message.- Extracts the SIGTERM → wait → SIGKILL → remove-lock-file sequence into a shared
killDevServerhelper incore/dev/lockfile.ts, and reuses it instop.tsandbackground.ts(previously duplicated in both).
Testing
- Adds
killDevServerunit tests intest/units/dev/lockfile.test.ts: killing a live spawned process and cleaning up the lock file, and cleaning up the lock file when the recorded PID is already dead.
Docs
- No docs update needed —
--forceis already documented forastro dev; this fixes it to match existing documented behavior.

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.7
Patch Changes
-
#17318
23a4120Thanks @astrobot-houston! - Fixes CSS module scoped-name hash mismatch inastro devwhen usingvite.css.transformer: 'lightningcss'with content collections. Previously, a component importing a CSS module and rendered via content collectionrender()would get different class name hashes in the element and the injected<style>tag, causing styles not to apply. -
#17323
4298883Thanks @ematipico! - Fixes a dev server memory leak which caused Node.js to emit warnings in the console. -
#17323
4298883Thanks @ematipico! - Fixes a dev server crash when a.htmlor/index.htmlsuffixed request (such as thosenetlify devprobes as pretty-URL fallbacks) matched a dynamic endpoint route, causing aTypeError: Missing parametererror -
#17325
cebc404Thanks @astrobot-houston! - Fixes a bug where CSS@importrules could end up mid-stylesheet after inline CSS chunks were merged during build, causing browsers to silently ignore them -
#17323
4298883Thanks @ematipico! - Fixes a build regression that could leave unresolved preload markers in inlined scripts with external dynamic imports -
Updated dependencies [
4298883,4298883]:- @astrojs/telemetry@3.3.3
@astrojs/cloudflare@14.1.2
Patch Changes
-
#17323
4298883Thanks @ematipico! - Fixes build-time image optimization ignoring a custom image service registered by an integrationPreviously, when using
imageService: 'compile'orimageService: 'custom', a custom image service was only respected if it was set directly in theimage.serviceoption ofastro.config. If an integration registered the service instead, images were silently optimized with the default Sharp service at build time. A custom image service now transforms your images at build time no matter how it was configured. -
#17323
4298883Thanks @ematipico! - Prebundlesastro/componentsand the<ClientRouter />transition runtime modules in the dev server environment so pages using them no longer trigger a mid-session dep optimizer reload, which caused React "Invalid hook call" errors in islands on the first request after a cold cache -
#17323
4298883Thanks @ematipico! - Fixes an issue wherevarsweren't available at build time. Now the adapter loadsvarsfrom the Wrangler config soastro:envpublic variables resolve at build time -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/telemetry@3.3.3
Patch Changes
-
#17323
4298883Thanks @ematipico! - Refactors internal WSL detection by removing theis-wsldependency. -
#17323
4298883Thanks @ematipico! - Replacedwhich-pm-runsdependency withpackage-manager-detector

Changes
- CSS
@importrules that end up mid-stylesheet aftermergeInlineCssconcatenates inline chunks are silently ignored by browsers, breaking font loading and other imported resources in production builds. mergeInlineCssnow skips merging any CSS chunk that contains@import. Those chunks remain as their own<style>tags, keeping@importat the top of its own stylesheet — which is required by the CSS spec.
Closes #11950
Testing
- Added
packages/astro/test/units/build/css-order.test.tswith 6 cases covering: normal merging, skipping merge when current or previous chunk has@import, two adjacent@importchunks, external stylesheet boundaries, and the mixed scenario where non-import chunks still get merged around an isolated import chunk.
Docs
- No docs update needed — this is a build correctness fix with no user-facing API changes.

Changes
- Dynamic endpoint routes with a file extension (e.g.
[...slug].png.ts) no longer fail withNoMatchingStaticPathFoundduringastro buildwhentrailingSlash: "always"is set.stringifyParams()now mirrors thetrailingSlashForPath()logic already used in route pattern generation: when a route is an endpoint with a file extension, trailing slash is forced to'never', so generated paths (e.g./og/foo.png) match the route pattern (e.g./^\/og\/(.*?)\.png$/). The mismatch affected both ASCII and non-ASCII params.
Closes #17306
Testing
- Added three unit tests in
packages/astro/test/units/routing/get-params.test.tscovering:- Spread file-extension endpoint with non-ASCII params +
trailingSlash: 'always'— path must not get a trailing slash and must match the route pattern. - Single dynamic file-extension endpoint (
[name].json) with the same config. - Endpoint without a file extension still gets a trailing slash, confirming the fix is scoped correctly.
- Spread file-extension endpoint with non-ASCII params +
Docs
- No docs update needed — this is a bug fix for existing static build behavior with no API surface change.

Changes
- Fixes CSS module class names not matching between the element and the injected
<style>tag inastro devwhen usingvite.css.transformer: 'lightningcss'with content collections. Production builds were unaffected. - Root cause:
getStylesForURL()re-imports CSS modules with?inlineappended to get the raw CSS string. Lightning CSS uses the full module ID (including?inline) as the filename for scoped-name hashing, producing a different hash than the original import. The fix shares the dev-css plugin's existingcssContentCache(which holds already-processed CSS with correct hashes) with the content asset propagation plugin, so?inlinere-imports are avoided when the cache has the result.
Testing
- Adds
packages/astro/test/lightningcss-css-modules-content.test.ts— regression test that starts a dev server withlightningcssenabled, renders a content collection page, and asserts the scoped class name on the element appears in the injected<style>tag.
Docs
- No docs update needed — this is a bug fix with no API or configuration changes.
Closes #17312

Changes
This PR fixes a memory leak in the dev server. The listener was never cleaned at the end of the request.
Testing
Manually tested. I tried an integration test, but it was ugly and weird.
Docs
N/A

Fixes #17265
Changes
- Prevents Astro from inlining script chunks when module metadata reports dynamic imports, including external dynamic imports omitted from Rolldown chunk metadata.
- Extracts the script inlining decision into testable helpers.
Testing
- Adds unit coverage for external dynamic imports reported through module info while
chunk.dynamicImportsis empty.
Docs
- No docs update needed; this restores expected build output behavior.

## Changes
- Adds dependency crawling using `vitefu` in the `@astrojs/solid-js` integration to dynamically discover Solid packages and populate `noExternal` and `external` in the Vite configuration. This ensures that SolidJS package exports (e.g. packages declaring `"solid"` fields in their `exports`) are bundled/resolved correctly during SSR and client build.
- Casts `originalEmit` to `any` in `solid-component.test.ts` when forwarding calls from the monkey-patched `process.emit`. This bypasses TypeScript's overload resolution error where the union of all Node event names is not assignable to the specific `"worker"` signature.
## Testing
- Re-enabled and updated `describe('Solid component build')` test suite in [solid-component.test.ts](file:///packages/astro/test/solid-component.test.ts) (reverted `.skip`, removed `@ts-expect-error` annotations, and replaced outdated assertions).
- Verified that all 10 Solid component build tests successfully compile and pass.
- Verified that all doctype tests in [astro-doctype.test.ts](file:///packages/astro/test/astro-doctype.test.ts) pass successfully.
## Docs
- No documentation changes needed since these are internal dependency crawling improvements and test suite fixes.
Summary
- add an Astro-specific
SatteriFeaturestype that overrides the upstreamsmartPunctuationJSDoc - export the type so consumers see the corrected default in
satteri({ features })
Fixes #17305.
Testing
corepack pnpm --filter @astrojs/prism buildcorepack pnpm --filter @astrojs/internal-helpers buildcorepack pnpm --filter @astrojs/markdown-satteri buildcorepack pnpm --filter @astrojs/markdown-satteri testcorepack pnpm exec prettier packages/markdown/satteri/src/processor.ts packages/markdown/satteri/src/index.ts --checkgit diff --check
Notes
AI-assisted. I manually reviewed the final diff and verification output.

Changes
- Extracts the
loggertype fromAPIContextto an exportedAstroComponentLoggerinterface. - This allows developers to easily type their custom middleware or integration functions that use the logger.
- Added a minor changeset.
Testing
- The change is type-only and relies on TypeScript's compilation.
- Verified by running
pnpm run typecheckandpnpm run test:match "exports". - No runtime behavioral changes were made.
Docs
- Updated docs in PR withastro/docs#14203

Changes
Follow-up to #17187, which pre-bundled astro/virtual-modules/transitions.js in the Cloudflare adapter's server dev environment to stop a mid-request dep optimizer reload. The same class of bug (#17166 / #16853 / #16529) still fires for pages that use the astro:components virtual module (e.g. import { Code } from "astro:components").
- The adapter's server
optimizeDepsexcludesastro:*virtual modules, so the real module behind that virtual,astro/components, is never in the boot-time include list. - It is therefore discovered by the SSR dep optimizer only at first render. On the first request after a cold optimizer cache, that late discovery triggers
optimized dependencies changed. reloadingmid-render, which the workerd module runner does not survive cleanly: components resolvereactfromnode_moduleswhilereact-dom/servercomes from a freshly re-optimizeddeps_ssrchunk. Two copies of React in one render throwInvalid hook call/Cannot read properties of null (reading 'useState')in every island, and the page returns 200 with the islands empty. - Fix: add
astro/componentsto the server-environmentoptimizeDeps.includelist, right after the existingastro/virtual-modules/transitions.jsentry, so it is pre-bundled at boot and the mid-render reload never happens.
This is the same one-line shape as #17187, for a different late-discovered virtual-module target.
Testing
Reproduced on a real Astro 7 + @astrojs/cloudflare 14.1.1 + @astrojs/react site (React 19) that renders astro:components:
- Stock adapter (bug): cold
astro dev(rm -rf node_modules/.vite), then requesting routes across the reload window. The dev log showsdependency optimized: astro/components→optimized dependencies changed. reloading→ repeatedInvalid hook callandTypeError: Cannot read properties of null (reading 'useState'). Every route still returns 200 but React islands render empty. - Patched adapter (this PR): identical cold-cache procedure. Zero optimizer reloads after startup, zero hook errors, all islands render, and warm-cache reboots stay clean.
I verified this by packing the patched adapter and pinning it into the site via a pnpm override; the bug reproduces reliably without the patch and disappears with it.
Docs
No docs needed — this is an internal dev-server dependency-optimization detail with no user-facing API change.
Add Hostgrid help center site to the showcase.
Removed broken link to ecoping.earth. The domain appears to be expired/inactive, which poses a potential security risk for documentation users.
Changes
Adds HitKeep to the Starlight showcase with an 800x450 PNG thumbnail captured from a 1280x720 desktop viewport and resized per the contributing guide.
Validation
- pnpm --filter starlight-docs build
- pnpm exec prettier --check docs/src/components/showcase-sites.astro
- pnpm --filter starlight-docs typecheck

Changes
- Adds a
deferRender?: booleanoption to theglob()loader. Whentrue, renderable content entries (e.g. Markdown) skip eager rendering during content sync and instead use the same deferred-render path that.mdxfiles already use — rendering happens on demand at page build time via the Vite markdown plugin. - This resolves OOM crashes during
astro buildfor large Markdown collections that use heavy rehype plugins likerehype-katex, where rendered HTML can be 70× larger than source. Closes #17301. - Removes the
// todo: add an explicit way to opt in to deferred renderingcomment that tracked this gap since the Content Layer was introduced.
Testing
- Unit tests in
glob-loader.test.tscover eager rendering (default), deferred rendering whendeferRender: true, and that non-renderable data entries are unaffected. - Also verified by running
node --max-old-space-size=256 astro buildagainst a 200-file KaTeX-heavy fixture. The build previously OOM'd; withdeferRender: trueit succeeds. Confirmed working by the issue reporter (@integrable).
Docs
- Docs PR: withastro/docs#14208

Closes #17297
Changes
- A
.htmlor/index.htmlsuffixed request to a dynamic endpoint route (e.g.GET /api/items/123/status.htmlforsrc/pages/api/items/[id]/status.ts) no longer crashes dev withTypeError: Missing parameter: id. - The dev route matcher strips
.html//index.htmlwhen retrying unmatched requests, butgetParams()only stripped.htmlforroute.type === 'page'. For a matched endpoint, params came back{}andstringifyParamsthrew.getParamsnow applies the same fallback for non-page routes: if the pattern doesn't match the original pathname, retry with the suffix stripped. Endpoints that legitimately capture.htmlin a param (e.g.[path]matching/file.html) match on the original pathname and are unaffected. - Especially impactful under
netlify dev, which probes.html//index.htmlvariants on any 404 — firing the crash for every dynamic API endpoint.
Testing
- Added
getParamsunit tests covering.htmland/index.htmlrequests to a dynamic endpoint route, plus a guard asserting endpoints that capture.htmlin a param keep the suffix.
Docs
No docs update needed — this restores the routing behavior users already expect.

Changes
This PR adds a new experimental option called dataStore. It allows to split the data into multiple chunks.
I also did some refactor, so that we can prepare different data sources e.g. sqlite. For this reason, the functions of the interface are also async.
Chunks are created when:
- a chunk is bigger than 10Mb in weight
- every 1000 entries
Testing
Added various tests for:
- string chucking
- data store chunking
- integration
- e2e
Docs
/cc @withastro/maintainers-docs for feedback!

Changes
- CSS
url('data:image/...')data URIs no longer crashastro buildwithENAMETOOLONGwhentsconfig.jsonhascompilerOptions.baseUrlset. Thevite-plugin-config-aliasCSS transform now skips anyurl()reference that starts withdata:, since data URIs are inline content and never valid file paths to resolve. - The regression was introduced in v7 by the
cssUrlREmatching added to the CSS transform handler (commit4766f3716d). The baseUrl alias regex/^(?!\.*\/|\.*$|\w:)(.+)$/was designed to exclude Windows drive letters but\w:only matches a single character before the colon, sodata:(4 chars) slipped through and was passed tofs.statSync().
Closes #17293
Testing
- Adds
packages/astro/test/alias-css-url-data-uri.test.tswith a fixture that combinestsconfig.jsonbaseUrl: "."and a CSSurl()data URI — verifies the build completes without error and the data URI is preserved in output.
Docs
- No docs update needed; this restores previously working behavior from v6.

Closes #16119
Changes
- Fixes a v6 regression where scoped CSS from components nested inside a
client:onlyisland was silently dropped in production builds. In dev mode everything worked fine, making the bug particularly hard to spot. - Root cause: the CSS deduplication logic in
plugin-css.tscorrectly marks a child component's CSS for deletion (it was already bundled during SSR for another page), but theclient:onlypage never participated in SSR so it never received that CSS. ThegetParentClientOnlyswalk would add the CSS topagesToCss, but the deleted asset was already gone by the timeinlineStylesheetsPluginran. - Fix: when the
getParentClientOnlyswalk finds a CSS entry that was deleted, inline the CSS content directly intopageData.stylesfromdeletedCssAssets, bypassing the need for the asset to survive toinlineStylesheetsPlugin. Content-based deduplication prevents double-injecting styles that survived deletion normally.
Testing
- Added
packages/astro/test/client-only-child-styles.test.tswith a fixture covering two scenarios: a page usingclient:only(where the child's scoped styles must be recovered fromdeletedCssAssets) and a page usingclient:loaddirectly (to confirm no regression there).
Docs
- No docs update needed — this restores behavior that already worked in v5 and is expected to work per existing docs.

Changes
- Prevents prototype pollution in config merge by filtering proto/constructor/prototype keys
- Fixes XSS in <script> and <style> elements by escaping </script> and </style> sequences in raw string children before marking as HTML-safe
- Blocks cross-origin POST regardless of Content-Type to prevent CSRF bypass (defense-in-depth; CORS preflight already protects fetch)
Testing
- Updated 2 CSRF test assertions to expect 403 instead of 200 for cross-origin JSON/octet-stream POSTs
Docs
- No docs update needed; all changes are internal hardening with no user-facing API changes

Changes
Both "Advanced Routing" and "Blog" Example Template pages presented missing spaces between the HTML tags, the file locations are respectively examples/advanced-routing/src/pages/index.astro and examples/blog/src/pages/index.astro.
Image provided below for Before and After.
Testing
Testing was made by editing the files and opening the URL in both Firefox and Chromium-based browsers and seeing if:
- The problem was present and persistent between different Browser engines.
- The patch worked.
Both showed to be true.
Docs
Unneeded, very small text changes.
Images
"examples/blog/src/pages/index.astro":
| Before | After |
|---|---|
![]() |
![]() |
"examples/advanced-routing/src/pages/index.astro":
| Before | After |
|---|---|
![]() |
![]() |

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.7
Patch Changes
-
#17317
437401eThanks @ematipico! - Fixes a dev server memory leak which caused Node.js to emit warnings in the console. -
#17299
1170b6dThanks @astrobot-houston! - Fixes a dev server crash when a.htmlor/index.htmlsuffixed request (such as thosenetlify devprobes as pretty-URL fallbacks) matched a dynamic endpoint route, causing aTypeError: Missing parametererror -
#17316
ed92e31Thanks @matthewp! - Fixes a build regression that could leave unresolved preload markers in inlined scripts with external dynamic imports -
Updated dependencies [
a77af9d,4aa78d8]:- @astrojs/telemetry@3.3.3
@astrojs/cloudflare@14.1.2
Patch Changes
-
#17285
6929e40Thanks @adamchal! - Fixes build-time image optimization ignoring a custom image service registered by an integrationPreviously, when using
imageService: 'compile'orimageService: 'custom', a custom image service was only respected if it was set directly in theimage.serviceoption ofastro.config. If an integration registered the service instead, images were silently optimized with the default Sharp service at build time. A custom image service now transforms your images at build time no matter how it was configured. -
#17303
464c46eThanks @jkomyno! - Prebundlesastro/componentsand the<ClientRouter />transition runtime modules in the dev server environment so pages using them no longer trigger a mid-session dep optimizer reload, which caused React "Invalid hook call" errors in islands on the first request after a cold cache -
#17275
6a99600Thanks @matthewp! - Fixes an issue wherevarsweren't available at build time. Now the adapter loadsvarsfrom the Wrangler config soastro:envpublic variables resolve at build time -
Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/telemetry@3.3.3
Patch Changes
-
#17311
a77af9dThanks @gameroman! - Refactors internal WSL detection by removing theis-wsldependency. -
#17304
4aa78d8Thanks @gameroman! - Replacedwhich-pm-runsdependency withpackage-manager-detector

Closes #17283
Changes
- The first browser to connect after
astro devstarts no longer receives an immediate, unprompted full-reload. Previously,invalidateDataStore()unconditionally sent afull-reloadHMR signal during thebuildStarthook — before any client connected — causing the signal to queue and fire the moment the first WebSocket client arrived. - Adds a
notifyClientoption toinvalidateDataStore()(defaulttrue). ThebuildStartcall now passes{ notifyClient: false }, preserving the module invalidation that prevents the content layer race condition (#12866) while skipping the client reload during startup.
Testing
- Added
packages/astro/test/units/content-layer/content-virtual-mod.test.ts— verifies thatbuildStartinvalidates the data store module but does not send afull-reloadto the client HMR channel.
Docs
- No docs update needed; this is a dev-server behavior fix with no API or config changes.

Changes
Follow-up to #17099: a custom image.service was only respected during build-time image generation (imageService: 'compile' and 'custom') when set directly in the user config. A service registered by an integration via updateConfig() was silently replaced with Sharp, because the adapter's astro:config:setup runs before all integrations. The service is now resolved against the final config in astro:config:done.
Testing
- New
integration-defined-image-service.test.tscovers both modes with the service registered by an integration. - Existing image service tests pass unchanged.
Docs
Not required. Existing docs assumes this works.

Changes
<Picture inferSize>with remote URLs no longer fails withFailedToFetchRemoteImageDimensionson rate-limiting servers (e.g. Wikimedia HTTP 429). ThePicturecomponent callsgetImage()once per output format, and each call was independently invokinginferRemoteSize()— firing 2–4 HTTP requests to the same URL in rapid succession.- Fix: resolve remote dimensions once in
Picture.astrobefore thegetImage()loop, then pass explicitwidth/height(droppinginferSize) to each call. No global caches — the result is scoped to the single component render, matching the approach recommended by @matthewp.
Closes #17263
Testing
- Covered by the existing
core-image-infersize.test.tssuite, which includes aPicture component workscase withinferSizein dev mode and passes without changes. - The specific failure mode (HTTP 429 from a rate-limiting server) is not easily reproducible in CI without a live server, and was verified manually by the reporter.
Docs
- No docs update needed —
inferSizebehavior on<Picture>is unchanged from the user's perspective; this fixes a reliability regression.

Changes
- Fixes
ImageandPicturecomponent layout styles being missing inastro devwhen JavaScript is disabled. The dev CSS pipeline was caching content by raw module ID (with\0prefix) in thetransformhook, but looking it up viacollected.id— thewrapId()-transformed version (/@id/__x00__...) — causing a cache miss for virtual CSS modules and producing empty<style>tags. Switching the lookup tocollected.idKey(the raw ID) aligns both sides of the cache.
Testing
- Adds
packages/astro/test/units/dev/dev-css-virtual-modules.test.tswith three unit tests covering:wrapId()transformation behavior for virtual module IDs, the no-op behavior for filesystem paths, and the cache key mismatch that the fix resolves.
Docs
- No docs update needed — this is a dev-mode bug fix with no API surface change.
Closes #17267
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.3
Patch Changes
- #3911
1686eccThanks @timothyjordan! - Keeps keyboard focus inside the mobile menu while it is open, preventing focus moving to hidden interactive elements in page content.

Fixes #16790
Changes
astro:envpublic variables defined undervarsin a Wrangler config (wrangler.toml/.json/.jsonc) now resolve when imported fromastro:env/clientandastro:env/server. Previously only.dev.varswas loaded into the environment, so public vars from the Wrangler config were inlined asundefinedat build time (onlygetSecretworked).- The adapter now resolves the effective env in
astro:config:donevia wrangler'sunstable_readConfig+unstable_getVarsForDev, which merges configvarswith.dev.vars/.dev.vars.<env>/.envoverrides exactly aswrangler devdoes (respectingCLOUDFLARE_ENV), and assigns the result toprocess.env. This replaces the previous hand-rolled.dev.varsreader, so there's a single source of truth that matches Wrangler's own precedence.
Testing
- Removed the manual
process.env.API_URL/process.env.PORToverrides from theastro-envSSR tests. Those overrides were masking the bug; the tests now rely on the adapter loadingvarsfrom the fixture'swrangler.jsonc, exercising the real code path for build and dev. The existingsecret/action secretcases continue to cover.dev.varsloading through the unified path.
Docs
- No docs update needed; this makes the adapter match the already-documented
astro:envbehavior.

Changes
- Adds a
render()overload forLiveDataEntry<LiveLoaderDataType<C>>topackages/astro/templates/content/types.d.ts. Previously, the only overload covered entries fromDataEntryMap(regular content collections). When a project only useslive.config.ts,DataEntryMapis empty, sokeyof DataEntryMapresolves toneverandrender()rejects any live entry with a TypeScript error. - The new overload mirrors the pattern already used by
getLiveEntryandgetLiveCollection. Projects with onlycontent.config.tsare unaffected —LiveContentConfigresolves toneverso the overload is inert.
Closes #16688
Testing
- No new test added — the bug is in a
.d.tstype template; integration-level verification (astro sync+astro check) is covered by the existing live loaders and content collection type test suites, all of which continue to pass.
Docs
- No docs update needed. This fixes a missing type overload to match already-documented runtime behavior.

Changes
- Island component paths (
client:component-path→metadata.componentUrl) now resolve extensionless relative imports (e.g.import { Counter } from '../components/Counter') to the real file on disk, probing Vite's default extension order and then directoryindexfiles. Previously the path stayed extensionless, so theinclude/excludeglobs of JSX renderer integrations (e.g.react({ include: ['**/react/*.tsx'] })) could never match it. - This is what fixes the "Invalid hook call" warning. Because the extensionless path never matched the
includeglob, the React renderer declined the component, and Astro asked the MDX renderer next — which tests a candidate by calling it as a plain function, so anyuseStateinside triggered React 19's warning. Now the glob matches, React claims the component, and MDX never probes it. (With multiple JSX frameworks configured, the same mismatch instead hard-failed with "Unable to render" — also fixed.) - Fixes #16767
Testing
- New
test/units/util/resolve-path.test.tscovering extension probing in Vite's order, directoryindexresolution, the existing.jsx→.tsxremap, and pass-through of extension-ful, bare, and#subpath specifiers (all fail without the fix) - New
multiple-jsx-renderersfixture page using an extensionless import matched by anincludeglob — the build hard-fails on it without the fix
Docs
- No docs update needed — this makes behavior match what the integration docs already show (
includeglobs written with file extensions).

Svelte 5 renamed its internal SSR prop from $$payload to $$renderer starting in a newer patch (tracked in fix(svelte): detect Svelte components with renamed renderer prop #14433, which updated the Svelte renderer). That PR correctly updated @astrojs/svelte, but the Solid renderer has its own copy of the same Svelte-exclusion check and was not updated at the same time.
Impact: users who have both @astrojs/solid-js and @astrojs/svelte installed, and whose Svelte version compiles components with $$renderer, will see those Svelte components rendered as empty strings by the Solid renderer instead of being handed off to the Svelte renderer.
Fix: mirror the same two-prop check already used in @astrojs/svelte:
if (componentStr.includes('$$payload') || componentStr.includes('$$renderer')) return false; 
Fixes #16078
Changes
- "Go To References" invoked from a
.tsfile now finds usages inside.astrofiles that are reached throughAstro.locals.*. Previously only.astrofiles with an explicit frontmatter import were discovered; a page usingAstro.locals.utils.toUpper()was silently missed. - The
@astrojs/ts-pluginnow injects the installed Astro package'senv.d.tsandastro-jsx.d.tsinto the TypeScript program, mirroring the language server'saddAstroTypes(). Without these theAstroglobal is undeclared, so the type chain throughAstro.localscan't resolve and references aren't found. (.astrofiles themselves already enter the program via Volar's external-files mechanism, so no file-discovery change is needed.) - Passes
includeScripts: false/includeStyles: falsetoconvertToTSX()(matching the language server) so<script>/<style>bodies are no longer wrapped in{() => { ... }}arrow functions, whereimportdeclarations are syntactically invalid and pollute the virtual file.
Known limitation
References to symbols imported inside <script> tags still won't appear via the ts-plugin. Making script content reference-searchable requires getExtraServiceScripts(), which Volar explicitly does not support in the TS-plugin path (decorateLanguageServiceHost.js logs getExtraServiceScripts() is not available in TS plugin.). The Astro language server handles that case; the ts-plugin cannot without upstream Volar changes.
Note on verification
This fix ships in @astrojs/ts-plugin, which the Astro VS Code extension bundles (astro-ts-plugin-bundle, registered via typescriptServerPlugins). When the extension is installed, tsserver loads the bundled plugin, so installing a preview of the npm package into a project's node_modules does not exercise the fix. End-to-end verification requires an extension build (or disabling the extension and relying solely on the tsconfig plugins entry with the workspace TypeScript).
Testing
- Adds
test/units/astro-types.test.mts: builds the.astro→.tsxoutput for a template that only usesAstro.locals.utils.toUpper(), then runsfindReferencesthrough the raw TypeScript language service. Asserts the reference is missed without type injection and found onceaddAstroTypesruns — a direct regression test for the reported behavior.
Docs
- No docs update needed; this is an editor-tooling bug fix with no user-facing API change.

Closes #17206
Changes
- The Container API no longer emits a false deprecation warning for
markdown.gfmandmarkdown.smartypantswhen neither option was set by the user. - Root cause:
AstroContainer.create()andcreateFromManifest()passedASTRO_CONFIG_DEFAULTSdirectly tovalidateConfig(). BecauseASTRO_CONFIG_DEFAULTSincludesgfm: trueandsmartypants: true, thewarnDeprecatedMarkdownOptionscheck saw them as user-specified values. Normal builds pass raw user config (without defaults pre-applied), so the warning never fires there. The fix strips those two keys from the markdown defaults before passing tovalidateConfig().
Testing
- Added
packages/astro/test/units/render/container-deprecation.test.ts: asserts thatAstroContainer.create()with default config does not log themarkdown.gfm/smartypantsdeprecation warning.
Docs
- No docs update needed — this is a false-positive warning fix with no API or behavior change for users.

Fixes #15627
Changes
- A
<script>inside a component rendered throughAstro.slots.render()now stays at its original position instead of being hoisted to the start of the slot output (sometimes escaping its parent element entirely). This regressed in #15147 and broke Starlight components (withastro/starlight#3712) as well as any CSS relying on:first-child/sibling selectors. SlotStringnow keeps its content as an orderedchunksstream with scripts inline, resolved and deduplicated lazily at stringify time — matching how the main render path already handles instructions.
Testing
- Adds "Scripts rendered via
Astro.slots.render()preserve their position", asserting the script renders inside its<li>/<ol>rather than before them. - Verified the existing #13847 test ("Scripts in Fragment slots are processed when another slot is unused") still passes alongside the new positional behavior.
Docs
- No docs update needed; this restores previously documented
<script>positioning behavior.

Changes
create-astronow respectsHTTP_PROXY/HTTPS_PROXYenvironment variables when downloading templates. When proxy env vars are set, the CLI re-execs itself with Node.js's--use-env-proxyflag so that nativefetch()(used by@bluwy/giget-core) routes requests through the configured proxy.- Requires no new dependencies. Degrades gracefully on Node.js < v22.21.0 — older versions get the same behavior as before. The flag is available in Node.js v22.21.0+ and v24.5.0+.
Closes #13684
Testing
- Added
packages/create-astro/test/units/proxy.test.tswith two cases: one verifying thatHTTPS_PROXYis respected (a non-existent proxy causes a connection error, proving the proxy was used), and one verifying normal operation is unaffected when no proxy env vars are set.
Docs
- No docs update needed; this restores previously expected behavior with no new APIs or config options.

Changes
- Adds a
formatoption toPaginateOptionsthat accepts a(url: string) => stringcallback. When provided, it is applied to all pagination URLs (current,next,prev,first,last) after they are constructed. - This is a non-breaking, opt-in addition — default behavior is completely unchanged. Users deploying to static file servers without URL rewrite rules can now use this to append
.html(or apply any other transformation) to pagination URLs generated bypaginate().
paginate(items, {
pageSize: 10,
format: (url) => `${url}.html`,
})Closes #13604
Testing
- Added 3 new
describeblocks (8 test cases) topackages/astro/test/units/render/paginate.test.tscovering:formatapplied to all URL properties,formatskipped forundefinedURLs,formatapplied after base path is prepended, and no regression whenformatis omitted.
Docs
- The new
formatoption is documented inline via JSDoc onPaginateOptions. A docs-site update to thepaginate()reference may be warranted to surface this option for users hitting 404s on static file servers.
@withastro/maintainers-docs for feedback

Closes #17135
Changes
getPackagenow imports packages using the absolute path returned byrequire.resolve()(converted to a file URL viapathToFileURL) instead of the bare package name. Previously,require.resolve()was correctly scoped to the project'scwd, but the subsequentawait import(packageName)resolved from astro's own install location — causingastro checkto fail when astro lives in a virtual store (e.g. pnpm) outside the project directory tree.- Affects both import sites in
getPackage: the initial load and the post-install load.
Testing
- Added
packages/astro/test/units/cli/install-package.test.ts: creates a temporary project directory with a fake package in itsnode_modules, then verifiesgetPackagecan find and load it when passed that directory ascwd.
Docs
- No docs update needed — this is an internal resolution fix with no user-facing API change.

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@7.0.6
Patch Changes
-
#17261
79aa99cThanks @astrobot-houston! - Fixes a false deprecation warning formarkdown.gfmandmarkdown.smartypantswhen using the Container API -
#17247
f94280dThanks @chatman-media! - Fixes route generation throwing "Missing parameter" (or silently dropping the segment) when a dynamic param's value is0. The generator used truthy checks instead of checking forundefined, sopaginate(posts, { params: { categoryId: 0 } })would crash even though0is a perfectly valid param value. -
#17278
6f11739Thanks @astrobot-houston! - Fixes missing CSS for virtual style modules (e.g., responsive image layout styles) in dev mode when JavaScript is disabled -
#17250
0b30b35Thanks @matthewp! - Fixes thesecurity.checkOrigincheck so it is applied consistently to Astro Actions and on-demand endpoints, regardless of how the request pipeline is composed. Previously, the origin check could be skipped in the composableastro/honopipeline depending on the order of themiddleware()primitive (or when it was omitted). -
#17274
8c3579bThanks @astrobot-houston! - Fixes missingrender()type overload for live collection entries. Previously, callingrender()on aLiveDataEntryproduced a TypeScript error when using onlylive.config.tswithout acontent.config.ts. -
#17257
4208297Thanks @astrobot-houston! - Fixesastro checkfailing to find@astrojs/checkandtypescriptwhen astro is installed in a directory outside the project tree (e.g. pnpm virtual store) -
#17272
b428648Thanks @matthewp! - Fixes island component paths so that extensionless imports (e.g.import { Counter } from '../components/Counter') resolve to the real file on disk, matching Vite's extension order and directoryindexresolution. This makes theinclude/excludeoptions of JSX renderer integrations (React, Preact, Solid) match components imported without a file extension, and removes the spurious React 19 "Invalid hook call" warning logged on every request in dev whenincludewas set alongside another JSX renderer -
#17279
2aeaa44Thanks @astrobot-houston! - Fixes a bug where<Picture inferSize>with a remote image could fail withFailedToFetchRemoteImageDimensionswhen the image server rate-limits requests (e.g. HTTP 429). Remote dimensions are now resolved once per render instead of once per output format. -
#17251
5240e26Thanks @matthewp! - Hardens the handling of attribute rendering when using with custom elements. -
#17248
429bd62Thanks @astrobot-houston! - Fixes a crash when using Astro'sgetViteConfigwith Vitest browser mode (e.g., Storybook vitest runner). Astro now skips dev server setup inside Vitest, preventing errors. -
#17260
14524c0Thanks @matthewp! - Fixes a regression where a<script>inside a component rendered throughAstro.slots.render()was hoisted out of its original position instead of staying next to its component content -
Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
- @astrojs/markdown-remark@7.2.1
- @astrojs/markdown-satteri@0.3.3
create-astro@5.2.2
Patch Changes
- #17259
ed6bea5Thanks @astrobot-houston! - Fixes proxy support by respectingHTTP_PROXYandHTTPS_PROXYenvironment variables when downloading templates. On Node.js v22.21.0+ and v24.5.0+,create-astronow automatically enables the--use-env-proxyflag so that nativefetch()routes requests through the configured proxy.
@astrojs/cloudflare@14.1.1
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
- @astrojs/underscore-redirects@1.0.3
@astrojs/markdoc@2.0.3
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
@astrojs/mdx@7.0.2
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
- @astrojs/markdown-remark@7.2.1
@astrojs/netlify@8.1.1
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
- @astrojs/underscore-redirects@1.0.3
@astrojs/node@11.0.2
Patch Changes
-
#17252
eb6f97eThanks @matthewp! - Fixes trailing-slash handling for request paths that begin with a backslashWith
trailingSlash: 'always', the standalone Node server could append a trailing slash to a request path that begins with a backslash (for example/\example.com/foo) and echo that path back in theLocationheader of a301response. Because browsers resolve a leading\the same way as/, the resultingLocationcould point off-site.Such paths are now recognized as internal paths, matching the existing handling for paths that begin with
//, so they are no longer rewritten with a trailing slash. -
Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
@astrojs/preact@6.0.1
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
@astrojs/react@6.0.1
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
@astrojs/solid-js@7.0.1
Patch Changes
- #17270
0142964Thanks @FrancoKaddour! - Fix@astrojs/solid-jsincorrectly claiming Svelte 5 components compiled with the newer$$rendererprop (instead of the legacy$$payload). Projects mixing Solid and Svelte could see Svelte components silently rendered as empty strings by the Solid renderer.
@astrojs/vercel@11.0.2
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
@astrojs/internal-helpers@0.10.1
Patch Changes
-
#17252
eb6f97eThanks @matthewp! - Fixes trailing-slash handling for request paths that begin with a backslashWith
trailingSlash: 'always', the standalone Node server could append a trailing slash to a request path that begins with a backslash (for example/\example.com/foo) and echo that path back in theLocationheader of a301response. Because browsers resolve a leading\the same way as/, the resultingLocationcould point off-site.Such paths are now recognized as internal paths, matching the existing handling for paths that begin with
//, so they are no longer rewritten with a trailing slash.
@astrojs/language-server@2.16.11
Patch Changes
- #17059
60cb289Thanks @dupontcyborg! - Update volar-service-* dependencies from 0.0.70 to 0.0.71 to pull in yaml-language-server 1.23.0 and yaml 2.8.3, resolving CVE-2026-33532 (GHSA-48c2-rrv3-qjmp), a denial-of-service vulnerability in yaml <2.8.3.
@astrojs/ts-plugin@1.10.10
Patch Changes
- #17269
c72d4f2Thanks @matthewp! - Fixes "Go To References" from.tsfiles missing usages inside.astrofiles that are reached throughAstro.locals. The plugin now injects Astro's ambient types so type chains likeAstro.locals.utils.toUpper()resolve, matching the language server.
@astrojs/markdown-remark@7.2.1
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1
@astrojs/markdown-satteri@0.3.3
Patch Changes
- Updated dependencies [
eb6f97e]:- @astrojs/internal-helpers@0.10.1

Changes
- Prefetch hover listeners now attach to links injected by
server:defercomponents. Previously,onPageLoad()scanned for<a>tags once on page load, but server islands resolve asynchronously after that scan completes — so their links were never registered. - Adds a
MutationObserverinsideonPageLoad()that watches for dynamically added DOM nodes. When a new anchor (or element containing one) is detected, the prefetch callback re-runs. The existinglistenedAnchorsWeakSet prevents duplicate listener attachment.
Closes #13297
Testing
- No automated tests added — the prefetch module is entirely client-side browser code (
document,MutationObserver, event listeners) with no existing test infrastructure in the repo. Fix was verified manually and confirmed by the reporter.
Docs
- No docs update needed; this is a bug fix restoring expected behavior for an existing feature combination.

Changes
- Fixes VS Code syntax highlighting breaking when
@property(or any CSS construct with angle brackets likesyntax: "<color>") is used inside a<style>block. Previously, the</style>closing tag and all subsequent<style>and<script>blocks would be incorrectly tokenized. - Updates all 7 style injection patterns in
astro.tmLanguage.src.yamlfrom a singlebegin/endapproach to the same two-patternbegin/end(single-line) +begin/while(multi-line) approach already used by script injection patterns since PR #15109. Thewhilepattern is evaluated at the start of each line before child patterns run, so</style>is always detected regardless of the embedded CSS grammar's internal state.
Testing
- Adds
packages/language-tools/vscode/test/grammar/fixtures/style/at-property.astro— a new grammar snapshot fixture verifying that all blocks are correctly scoped when@propertywith asyntax: "<color>"descriptor is present. - Updates the dummy CSS grammar (
css.tmLanguage-dummy.json) to include selector patterns that simulate VS Code's real CSS grammar behavior, making the existing snapshot tests more realistic. - Updates
expression.astro.snapandstyle.astro.snapsnapshots to reflect that leading whitespace before</style>is no longer scoped as CSS content (matches the existing script behavior).
Docs
No docs update needed — this is a VS Code extension grammar fix with no user-facing API changes.
Closes #16751

Summary
With trailingSlash: 'always', the standalone @astrojs/node server could append a trailing slash to a request path beginning with a backslash (e.g. /\example.com/foo) and echo it back in the Location header of a 301. Since browsers resolve a leading \ like /, that Location could point off-site.
isInternalPath now folds backslashes to forward slashes before comparing the prefix, so /\host is recognized as an internal path just like //host and is no longer rewritten with a trailing slash (it falls through to a 404). The core trailing-slash handlers already normalized this via URL parsing; this brings the raw-request path in the Node adapter in line.
Testing
- Unit tests for
isInternalPathcovering backslash prefixes. - Integration test in the Node adapter using a raw request line (a literal backslash, which URL parsers would otherwise normalize) asserting a
404with noLocationheader.

Changes
- Applies the existing attribute name validation to custom HTML element rendering during SSR. Attribute names containing characters invalid per the HTML spec (
" ' > / =or whitespace) are now dropped instead of interpolated raw, preventing unsafe HTML output.
Testing
- Adds
renderHTMLElement rejects invalid attribute keystest suite covering malicious keys (event handler injection, script tag injection) and valid keys (namespaced,data-*).
Docs
- No docs update needed — this is an internal hardening fix with no user-facing API change.

Changes
- Applies the
security.checkOrigincheck at the request dispatch points (Astro Actions and on-demand endpoints/pages), so it holds consistently regardless of how the composableastro/honopipeline is ordered. Previously the check only ran inside themiddleware()primitive, so it could be skipped depending on primitive order — or whenmiddleware()was omitted entirely. - Extracts the shared origin-check logic into
core/app/origin-check.ts(a predicate + response builder) consumed by the pipeline middleware, the actions dispatch, and thepages()endpoint dispatch. No behavior change to the classic pipeline: the check remains a no-op at the dispatch sinks when the middleware already ran, for prerendered/static routes, for safe methods, and whencheckOriginis disabled.
Testing
- Adds
action-origin-check.test.ts: composes a Hono app withactions()beforemiddleware()and asserts a cross-origin action request is rejected before the handler runs, while same-origin succeeds. - Adds
pages-origin-check.test.ts: composes a Hono app withpages()and nomiddleware()and asserts a cross-originPOSTto an on-demand endpoint is rejected before the handler runs, while same-origin succeeds.
Docs
- No docs update needed; existing
security.checkOriginbehavior is unchanged from the user's perspective.

Changes
Our packages still had the alpha versions in their peerDependencies fields. This PR fixes the problem by using the non-alpha packages.
Testing
Green CI.
Docs
N/A

Closes #16275
Changes
- When
process.env.VITESTis set,configureServerinvite-plugin-astro-servernow returns early instead of setting up Astro's dev server middleware. Vitest's browser mode (used by the Storybook vitest runner) boots its own Vite server, which triggers this hook — but its module evaluator doesn't implementwrapDynamicImport, causing aTypeErrorwhen Astro's SSR runner tries to loadcreateAstroServerAppvia dynamic imports. - The dev server middleware (SSR handler, prerender handler, trailing-slash redirects) is only meaningful for
astro dev, so skipping it in Vitest contexts is safe.
Testing
- Added
packages/astro/test/units/vite-plugin-astro-server/vitest-guard.test.tswith two cases: one confirming the early return whenVITESTis set (using a bare fake server with no environments), and one confirming normal execution proceeds whenVITESTis absent.
Docs
- No docs update needed — this is a bug fix with no user-facing API changes.
Last fetched: | Scheduled refresh: Every Saturday
See Customizing GitHub Activity Pages to configure your own
Inspired by prs.atinux.com







