Skip to content

AstroEco is Contributing…

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

withastro/astro

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

Fixes #17484

Changes

  • Fixes the dev server watcher's matchesGlob function 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 ignore option to align the watcher's filtering semantics with tinyglobby's set-subtraction behavior

Testing

  • The existing handles negative matches in glob pattern unit test verifies that tinyglobby-based initial sync correctly excludes negated patterns; the watcher fix changes picomatch.isMatch to match those same semantics

Docs

  • No docs update needed — this is a bug fix that aligns runtime behavior with documented glob semantics
withastro/astro

Fix Cloudflare pre-optimizing for internal helpers' picomatch dependency

Issue & Repro

  • See my repro
  • pnpm why picomatch shows that @astrojs/internal-helpers has its own copy of picomatch which isn't in the optimizeDeps list
  • This copy can then cause the dev server to fail a la #15796 because picomatch uses CJS require() internally
  • Running pnpm dev --force on 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 the optimizeDeps list in packages/integrations/cloudflare/src/index.ts

https://github.com/jimpala/astro/blob/0dfbee95759900b33deeb6498e127a262bc4e5f9/packages/integrations/cloudflare/src/index.ts#L331-L332

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

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-17298 used 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/.italic to strings in astro's bundled server runtime and .relative/.container/.lowercase to 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.fileName in place instead of deleting and re-adding the bundle entry: the generateBundle bundle object is a Rolldown proxy that silently ignores direct key assignment (verified empirically — bundle[newName] = asset leaves bundle[newName] undefined). Mutating fileName re-keys the proxy and the writer emits by fileName, so the Vite manifest, ssrAssetsPerEnvironment tracking, and pagesToCss all 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 on main (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, plus test/units/build/** → 0 fail.
  • Verified against the original reproduction shape (Tailwind v4 + output: 'server' + node adapter + prerendered index + 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 in dist/server/entry.mjs.

Docs

N/A — build output bugfix, changeset included.

🤖 Generated with Claude Code

https://claude.ai/code/session_01U5TQ1y3yBEDx73tYTHoUBj

withastro/astro

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 — the commandMap had no nub entry, so nub users fell back to npm run dev. Added nub: 'nub run dev'.
  • template.ts (processTemplateReadme) — the non-npm path replaced npm run with the bare package-manager name, turning npm run dev into nub dev. nub has no implicit script shortcut, so that is invalid. It now maps npm run <script> to nub run <script>, keeping the explicit run.

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.

withastro/astro

Changes

  • Restores getFallback as a named export from astro: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 from export * to an explicit list. Users following the official docs would hit both a ts(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

Closes #17482

withastro/astro

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 default Cloudflare-CDN-Cache-Control: no-store header.
  • 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-Cookie headers before the rebuild, because setCookieHeaders looks 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.

withastro/astro

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

withastro/astro

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

Before package json After package json

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.

withastro/starlight

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 like vscode:// 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:bar is a valid relative link, but foo:bar is treated as a protocol foo:). 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 there and it isn’t performance sensitive in the same way the sidebar is.
withastro/astro

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:

  1. 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.
  2. 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.
  3. 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).
  4. Merge-key limits could theoretically reject documents with pathological numbers of << merges, though anything hitting those limits was likely a DoS vector anyway.
withastro/astro

Changes

  • Adds background?: string to ImageSharedProps in packages/astro/src/assets/types.ts, making the background prop visible to TypeScript when using <Image /> and <Picture />. The prop already worked at runtime (props are spread into getImage()), but was missing from the type, causing astro check to report Property '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 check on a repro project reports 0 errors.

Docs

  • No docs update needed. The background prop is already documented for getImage() and <Image /> on docs.astro.build; the component just lacked the corresponding type.

Closes #17471

withastro/astro

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

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.1.4

Patch Changes

  • #17472 4dc590c Thanks @astrobot-houston! - Adds the missing background prop to the <Image /> and <Picture /> component types. The prop already worked at runtime, but was absent from the types, causing astro check to report that background does not exist on the component props

  • #17421 f1448de Thanks @iamkaleemsajjad-hue! - Fixes session runtime errors being silently swallowed by console.error instead of routing through Astro's logger

  • #17421 f1448de Thanks @iamkaleemsajjad-hue! - Fixes a session being left in a partial state after a storage failure during session.regenerate(), preventing unnecessary storage reads on subsequent operations

create-astro@5.2.3

Patch Changes

  • #17423 08e8adb Thanks @astrobot-houston! - Fixes create-astro silently writing template files to the wrong directory on Linux when the path contains non-ASCII characters.

@astrojs/markdoc@2.0.5

Patch Changes

  • #17460 3b93a1a Thanks @astrobot-houston! - Fixes custom transform functions being dropped when a tag or node also specifies a custom render component. User-written transforms are now always preserved; only Markdoc's built-in transforms are removed so the custom component wins.

  • #17191 fc3fb2b Thanks @eldardada! - Fixes custom transform functions 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 custom render component now recognizes bracket notation, optional chaining and whitespace, not only dot notation.

@astrojs/check@0.9.10

Patch Changes

withastro/astro

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:

  1. Changed \G(?:\\G|\\s+) in all three lang detection patterns so they can match on subsequent lines
  2. Added #tags-lang-fallback-start-attributes with a restricted begin that only matches lines containing > or /> (the last line of the tag open)
  3. Used this restricted fallback instead of the original #tags-lang-start-attributes which 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.

withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.41.4

Patch Changes

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/markdoc@2.0.4

Patch Changes

@astrojs/language-server@2.16.13

Patch Changes

withastro/starlight

Description

  • Closes #4083
  • Closes #4084
  • This PR removes our use of the pagehide event in our sidebar state persistence script.
  • As spotted in #4083 this has never been working as intended due to a typo as pageHide in the event name.
  • We could fix the casing, but AFAICT we do not need the event at all:
    1. It hasn’t been firing for 1 year without anyone noticing.
    2. The main reason for pagehide was due to Safari’s partial visibilitychange support. According to MDN’s data Safari fixed this in April 2021 (v14.1 on desktop, v14.5 on mobile), so we no longer need pagehide to cover those older browsers which fall outside of our browser support matrix.
    3. 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.
withastro/starlight

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.

withastro/astro

Changes

  • Enables includeProjectReference in the Volar checker so that astro check follows tsconfig references and checks files from all referenced projects
  • It required updating how astro-check is run for the astro package. I tried a bunch of things and the current state is the only one I managed to make work
  • Fixes #17464

Testing

Docs

Changeset

withastro/astro

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

withastro/astro

Changes

  • Fixes user-written transform functions being silently deleted when a custom render component is specified. The previous transformRespectsRender heuristic 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 using this.render.
  • Replaces the string-matching heuristic with reference equality via isBuiltinMarkdocTransform, which compares the transform function against the built-in Markdoc.nodes/Markdoc.tags entries. Only actual Markdoc built-in transforms are removed; all user-written transforms are preserved.

Testing

  • Adds render-this-context.test.ts with a fixture that uses this.render in a tag transform function, 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 transform behavior.

Closes #17458

withastro/astro

Fixes #17456

Changes

  • Pre-bundles the Astro Actions server entrypoints and astro/zod in the server environment's optimizeDeps.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.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.1.3

Patch Changes

  • #17427 630b382 Thanks @astrobot-houston! - Fixes image optimization during astro build using 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

  • #17457 d46ecd8 Thanks @matthewp! - Fixes a dev server crash when using Astro Actions with the Cloudflare adapter

  • Updated dependencies []:

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

Closes #17404

Changes

  • Fixes build failure caused by satteri being bundled into the workerd prerender environment. A static import { satteri } in config/schemas/base.ts was pulled into the prerender bundle via the Container API's import chain (container → ASTRO_CONFIG_DEFAULTS → base.ts → satteri). Cloudflare's browser resolve condition resolved satteri to its WASM browser entry, which requires @napi-rs/wasm-runtime (not installed). The fix extracts ASTRO_CONFIG_DEFAULTS into a new defaults.ts module (no satteri import) and defers satteri initialization to config resolution time in validate.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.ts imported node:path directly and pulled in esbuild (→ node:url) via the client-directive/index.ts barrel re-export. Neither is available in workerd without nodejs_compat. Fixed by replacing posix.sep with '/' and importing getDefaultClientDirectives from the direct client-directive/default.ts instead 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 value and 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.ts to reflect that markdown.processor is now undefined after validateConfig (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.

withastro/astro

Changes

Since this test validated by checking code directly for "/blog", migrated it from E2E to an integration test.

Testing

Reproduce past issue on e2e test

スクリーンショット 2026-07-18 19 37 37

Reproduce same issue via integration test

スクリーンショット 2026-07-18 22 45 04

After fix logs
スクリーンショット 2026-07-20 8 04 05

Docs

withastro/astro

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.

withastro/astro

Changes

  1. Updates cookie to v2
  2. Migrates AstroCookies to the new parseCookie / stringifySetCookie API.
  3. 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.

withastro/astro

This PR contains the following updates:

Package Change Age Confidence
js-yaml ^4.1.1^5.2.1 age confidence

Release Notes

nodeca/js-yaml (js-yaml)

v5.2.1

Compare Source

Fixed
  • Add Map support to !!omap (should work when realMapTag used)
Security
  • Remove quadratic complexity from !!omap addItem. Regression from v5
    (usually not critical, because YAML11_SCHEMA is not default anymore).

v5.2.0

Compare Source

Added
  • Added maxTotalMergeKeys (10000) loader option to limit the total number of
    keys processed by YAML merge (<<) across one load() / loadAll() call.
  • Added maxAliases (-1) loader option to limit the number of YAML aliases per
    document.
Removed
  • maxMergeSeqLength replaced with maxTotalMergeKeys for limiting YAML merge
    processing.
Fixed
  • Round-trip of integers with exponential form (>= 1e21)

v5.1.0

Compare Source

Added
  • Collection tags can finalize an incrementally populated carrier into a
    different result value.
Changed
  • [breaking] quoteStyle now selects the preferred quote style; use the
    restored forceQuotes option to force quoting non-key strings.

v5.0.0

Compare Source

Added
  • Added named exports for schemas, tags, parser events and AST utilities.
  • Reworked JSON_SCHEMA and CORE_SCHEMA with spec-compliant scalar resolution
    rules, and added YAML11_SCHEMA.
  • Added realMapTag for lossless mappings with non-string and complex keys.
    Object-based mappings now reject complex keys instead of stringifying them.
  • Added dump() transform option for changing the generated AST before
    rendering.
  • Added dump() options seqInlineFirst, flowBracketPadding,
    flowSkipCommaSpace, flowSkipColonSpace, quoteFlowKeys, quoteStyle and
    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").
  • load/dump default behaviour is now specified exactly via schemas:
    • load uses CORE_SCHEMA, without !!merge by default.
    • dump uses YAML11_SCHEMA + CORE_SCHEMA for the quoting check, to
      guarantee backward compatibility by default.
  • !!set is now loaded as a JavaScript Set.
  • Replaced the Type API with a tags API. Similar, but more precise and
    simpler. See examples for details. Tags can be defined via
    defineScalarTag(), defineSequenceTag() and defineMappingTag(), or as a
    spread + override of an existing tag.
  • Renamed Schema.extend() to Schema.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 returning undefined.
  • Moved browser builds to the js-yaml/browser export.
  • Deprecated the loadAll signature with an iterator (still works, but is a
    candidate for removal).
Removed
  • Removed deprecated safeLoad(), safeLoadAll() and safeDump() exports.
  • Removed DEFAULT_SCHEMA and the nested types export.
  • Removed loader options onWarning, legacy and listener.
  • Removed dumper options styles, replacer, noCompatMode, condenseFlow,
    quotingType and forceQuotes. Renamed noArrayIndent to seqNoIndent.
    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

Compare Source

v4.2.0

Compare Source

Added
  • Added docs/safety.md with 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 after merge fix,
    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)
  • 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.

withastro/astro

This PR contains the following updates:

Package Change Age Confidence
htmlparser2 ^10.1.0^12.0.0 age confidence

Release Notes

fb55/htmlparser2 (htmlparser2)

v12.0.0

Compare Source

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) unless recognizeSelfClosing is 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, <?…, <![CDATA[… at EOF emit the correct token type

Implicit open/close

  • <h1><h6> implicitly close other headings
  • <a> closes a previous <a>
  • Nested <form> is ignored when one is already open
  • <image> is rewritten to <img> outside foreign content
  • </> is silently ignored instead of emitted as text

Other fixes

  • Fixed reset() not clearing attribute state, which could leak data across parseComplete() calls

#​2387

Full Changelog: fb55/htmlparser2@v11.0.0...v12.0.0

v11.0.0

Compare Source

Breaking Changes

  • The module is now ESM only #​2381
    • CommonJS require() is not supported in legacy environment anymore. Use import instead.
    • The minimum Node.js version is now 20.19.0.
  • Dependencies have been bumped to their latest major versions: domhandler v6, domutils v4, domelementtype v3, entities v8.
  • The deprecated parseDOM function was removed.

Features

  • Added WebWritableStream for the Web Streams API, enabling direct piping from fetch() response bodies into the parser #​2376

Bug Fixes

  • Comments now accept --!> as a closing sequence per the HTML spec, and <!--> is recognized as an empty comment in HTML mode #​2382
  • XML processing instructions (<?xml ... ?>) now require the full ?> closing sequence instead of just > #​2382
  • Fixed reset() not clearing isSpecial and sequenceIndex state, which could cause incorrect parsing after reuse #​2382
  • Fixed XML comment parsing: <!--> is no longer treated as a complete comment in xmlMode #​2383

Other Changes

  • Expanded README with full API reference, parser options, events, and practical examples #​2384

New Contributors

Full Changelog: fb55/htmlparser2@v10.1.0...v11.0.0


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • Between 12:00 AM and 03:59 AM, only on Monday (* 0-3 * * 1)
  • 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.

withastro/astro

This PR contains the following updates:

Package Change Age Confidence
diff ^8.0.3^9.0.0 age confidence

Release Notes

kpdecker/jsdiff (diff)

v9.0.0

Compare Source

(All changes part of PR #​672.)

  • ES5 support is dropped. parsePatch now uses TextDecoder and Uint8Array, 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 diff and 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; parsePatch would 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, parsePatch parses all the possible escape sequences that GNU diff (or Git) ever output, and formatPatch quotes and escapes filenames containing special characters in the same way GNU diff does.

  • formatPatch now omits file headers when oldFileName or newFileName in the provided patch object are undefined, regardless of the headerOptions parameter. (Previously, it would treat the absence of oldFileName or newFileName as indicating the filename was the word "undefined" and emit headers --- undefined / +++ undefined.)

  • formatPatch no longer outputs trailing tab characters at the end of ---/+++ headers.

    Previously, if formatPatch was passed a patch object to serialize that had empty strings for the oldHeader or newHeader property, it would include a trailing tab character after the filename in the --- and/or +++ file header. Now, this scenario is treated the same as when oldHeader/newHeader is undefined - i.e. the trailing tab is omitted.

  • formatPatch no 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.) Previously formatPatch would provide the correct output but also mutate the oldLines or newLines property on the hunk, changing the meaning of the underlying patch.

  • Git-style patches are now supported by parsePatch, formatPatch, and reversePatch.

    Patches output by git diff can include some features that are unlike those output by GNU diff, 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 parsePatch parses 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 by parsePatch. See isGit and subsequent properties in the docs in the README.md file.

    formatPatch now outputs extended headers based on these new Git-specific properties, and reversePatch respects them as far as possible (with one unavoidable caveat noted in the README.md file).

  • Unpaired file headers now cause parsePatch to throw.

    It remains acceptable to have a patch with no file headers whatsoever (e.g. one that begins with a @@&#8203; hunk header on the very first line), but a patch with only a --- header or only a +++ header is now considered an error.

  • parsePatch is 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 +, -, or and 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 oldFileName and newFileName fields of StructuredPatch are now typed as string | undefined instead of string. This type change reflects the (pre-existing) reality that parsePatch can produce patches without filenames (e.g. when parsing a patch that simply contains hunks with no file headers).

v8.0.4

Compare Source

  • #​667 - fix another bug in diffWords when used with an Intl.Segmenter. If the text to be diffed included a combining mark after a whitespace character (i.e. roughly speaking, an accented space), diffWords would 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)
  • 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.

withastro/astro

This PR contains the following updates:

Package Change Age Confidence
@vercel/analytics (source) ^1.6.1^2.0.1 age confidence

Release Notes

vercel/analytics (@​vercel/analytics)

v2.0.1

Compare Source

What's Changed

New Contributors

Full Changelog: vercel/analytics@v2.0.0...v2.0.1

v2.0.0

Compare Source

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)
  • 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.

withastro/astro

ℹ️ 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 age confidence

Release Notes

nodejs/undici (undici)

v8.8.0

Compare Source

v8.7.0

Compare Source

What's Changed
New Contributors

Full Changelog: nodejs/undici@v8.6.0...v8.7.0

v8.6.0

Compare Source

v8.5.0

Compare Source

⚠️ 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(...) or WebSocketStream
    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 / Socks5ProxyAgent over SOCKS5 that rely on
    requestTls.
  • Workaround: route traffic through an HTTP-proxy ProxyAgent, where
    requestTls functions 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 Socks5ProxyAgent across 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
    Authorization upstream and receive non-canonical qualified directives.
  • Workaround: disable shared-cache mode for authenticated traffic, avoid
    caching authenticated responses, or add Vary: Authorization upstream.
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: 0 on 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: #5408 don't rewind kPendingIdx past in-flight requests · #5391 allow h2 POST request multiplexing · #5406 reap idle HTTP/2 sessions · #5410 preserve h2 queue on out-of-order completion
  • Features: #5416 add bodyMixin.textStream() · #5418 align EventSource with spec
  • Docs / CI / tests: #5413 document request header validation · #5383 absorb h2 stream timeout resets (test) · #5420 remove stale repro + lint · #5426 extend Windows CI timeout · #5427 detect available python in WPT runner

Full changelog: v8.4.1...v8.5.0.


Credits

Per-advisory credits (as recorded in each GHSA):

v8.4.1

Compare Source

What's Changed

New Contributors

Full Changelog: nodejs/undici@v8.4.0...v8.4.1

v8.4.0

Compare Source

What's Changed

New Contributors

Full Changelog: nodejs/undici@v8.3.0...v8.4.0

v8.3.0

Compare Source

What's Changed

New Contributors

Full Changelog: nodejs/undici@v8.2.0...v8.3.0

v8.2.0

Compare Source

What's Changed

New Contributors

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)
  • 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.

withastro/astro

This PR contains the following updates:

Package Change Age Confidence
@fastify/static ^9.0.0^10.1.0 age confidence

Release Notes

fastify/fastify-static (@​fastify/static)

v10.1.0

Compare Source

What's Changed

New Contributors

Full Changelog: fastify/fastify-static@v10.0.0...v10.1.0

v10.0.0

Compare Source

Breaking Changes

  • setHeaders now using FastifyReply instead of Response.

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('@&#8203;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('@&#8203;fastify/static'), {
  root: path.join(__dirname, 'public'),
  prefix: '/public/', // optional: default '/',
  setHeaders(reply) {
    reply.header('X-Test', 'Foo')
  }
})

What's Changed

New Contributors

Full Changelog: fastify/fastify-static@v9.3.0...v10.0.0

v9.3.0

Compare Source

What's Changed

  • chore: update fastify-plugin dependency to version 6.0.0 by @​Puppo in #​594

New Contributors

Full Changelog: fastify/fastify-static@v9.2.0...v9.3.0

v9.2.0

Compare Source

What's Changed

New Contributors

Full Changelog: fastify/fastify-static@v9.1.3...v9.2.0

v9.1.3

Compare Source

What's Changed

Full Changelog: fastify/fastify-static@v9.1.2...v9.1.3

v9.1.2

Compare Source

What's Changed

Full Changelog: fastify/fastify-static@v9.1.1...v9.1.2

v9.1.1

Compare Source

⚠️ 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

Compare Source

What's Changed

New Contributors

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)
  • 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.

withastro/astro

This PR contains the following updates:

Package Change Age Confidence
@cloudflare/workers-types ^4.20260526.1^5.20260722.1 age confidence

Release Notes

cloudflare/workerd (@​cloudflare/workers-types)

v5.20260722.1

Compare Source

v5.20260721.1

Compare Source

v5.20260719.1

Compare Source

v5.20260718.1

Compare Source

v5.20260717.1

Compare Source

v5.20260716.1

Compare Source

v5.20260715.1

Compare Source

v5.20260714.1

Compare Source

v5.20260713.1

Compare Source

v5.20260712.1

Compare Source

v5.20260711.1

Compare Source

v5.20260710.1

Compare Source

v5.20260708.1

Compare Source

v5.20260707.1

Compare Source

v5.20260706.1

Compare Source

v5.20260705.1

Compare Source

v5.20260704.1

Compare Source

v5.20260703.1

Compare Source

v4.20260702.1

Compare Source

v4.20260701.1

Compare Source

v4.20260630.1

Compare Source

v4.20260629.1

Compare Source

v4.20260628.1

Compare Source

v4.20260627.1

Compare Source

v4.20260626.1

Compare Source

v4.20260625.1

Compare Source

v4.20260624.1

Compare Source

v4.20260623.1

Compare Source

v4.20260621.1

Compare Source

v4.20260620.1

Compare Source

v4.20260619.1

Compare Source

v4.20260617.1

Compare Source

v4.20260616.1

Compare Source

v4.20260615.1

Compare Source

v4.20260613.1

Compare Source

v4.20260612.1

Compare Source

v4.20260611.1

Compare Source

v4.20260610.1

Compare Source

v4.20260609.1

Compare Source

v4.20260608.1

Compare Source

v4.20260607.1

Compare Source

v4.20260606.1

Compare Source

v4.20260605.1

Compare Source

v4.20260604.1

Compare Source

v4.20260603.1

Compare Source

v4.20260602.1

Compare Source

v4.20260601.1

Compare Source

v4.20260531.1

Compare Source

v4.20260530.1

Compare Source

v4.20260529.1

Compare Source

v4.20260528.1

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • Between 12:00 AM and 03:59 AM, only on Monday (* 0-3 * * 1)
  • 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.

withastro/astro

AI discourse: most works are done by Claude Fable 5

Changes

  • astro:data-layer-content now always emits export default JSON.parse("...") instead of a dataToEsm() 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/pluginutils dependency from astro.

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.parse is 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 emits JSON.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.

withastro/astro

ℹ️ Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
pnpm (source) 11.5.011.13.1 age confidence

Release Notes

pnpm/pnpm (pnpm)

v11.13.1: pnpm 11.13.1

Compare Source

Patch Changes

  • Fixed pnpm pack applying workspace-root ignore rules when a workspace package has its own .npmignore file.
  • Keep the interactive minimumReleaseAge approval prompt visible during pnpm 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-update failing to link native platform binaries stored in sibling global virtual store slots.

v11.13.0: pnpm 11.13

Compare Source

Minor Changes

  • Added versioning.epics to pnpm-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 major M, members live in M*100 … M*100+99. Members move independently inside the band (patch, minor, and a major intent 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 team command 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 change command records change intents as changesets-compatible .changeset/*.md files (pnpm change status shows the pending release plan), and the bare pnpm version -r consumes them — bumping versions across the workspace with dependent propagation through workspace: ranges, fixed groups, a maxBump cap, --filter narrowing, 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 new pnpm lane <name> --filter <pkg> command and back with pnpm lane main --filter <pkg> (pnpm lane shows the membership), releasing X.Y.Z-lane.N prereleases from the same runs that release stable versions of the packages on the main lane. Configuration lives under the new versioning key of pnpm-workspace.yaml (fixed, ignore, maxBump, lanes, changelog). When two workspace projects publish the same name, intent files, versioning.lanes, and versioning.fixed/ignore may reference a project by its workspace-relative directory path (e.g. "./pnpm/npm/pnpm") — the one additive extension to the changesets format, applied automatically by pnpm change.

    Release changelogs default to registry storage (versioning.changelog.storage): no CHANGELOG.md is 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 later pnpm version -r only once the registry confirms the version is published with its section. Set versioning.changelog.storage: repository to keep committed CHANGELOG.md files 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 tokenHelper set in the global pnpm auth.ini is no longer rejected as project-level configuration. The guard that blocks tokenHelper from a project .npmrc only treated ~/.npmrc as a trusted source, so a helper written to auth.ini (for example by pnpm config set) failed on every command and could not even be removed with pnpm config delete. A tokenHelper in a workspace or project .npmrc is still rejected.

  • pnpm cache delete now removes a package's metadata from every metadata cache directory (metadata, metadata-full, and metadata-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 as file: instead of deduping back to link: 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 deploy now supports workspaces that use catalogs.

  • Fixed pnpm deploy with a shared lockfile so local file: tarball dependencies keep their package name in the generated deploy lockfile. This prevents warm-store deploys from failing with ERR_PNPM_UNEXPECTED_PKG_CONTENT_IN_STORE when the tarball filename includes the version.

  • Options that follow create, exec, or test appearing as a subcommand of another command are now parsed instead of being silently treated as positional parameters. For example, pnpm team create @&#8203;org:team --registry <url> previously ignored the --registry option and sent the request to the default registry.

  • pnpm add -g, pnpm update -g, pnpm setup, and the self-updater no longer fail with ERR_PNPM_MISSING_TIME when trustPolicy: no-downgrade or resolutionMode: time-based is set in the global config #​12883. The decision to fetch full registry metadata now lives in one place, and the no-downgrade trust 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 the time field.

  • pnpm list and pnpm why no longer crash with EMFILE: too many open files when a project has a large number of unsaved dependencies (packages present in node_modules but not in the lockfile). The reads of those packages are now concurrency-limited.

  • The published pnpm package no longer declares dependencies or devDependencies. Because the CLI bundles its runtime dependencies into dist/node_modules, those fields are dropped when packing, so npm install of the tarball no longer tries to resolve internal-only packages such as @pnpm/test-ipc-server. Closes #​12955.

  • Fixed pnpm publish --otp and pnpm publish --batch --otp to send the configured OTP to the registry.

  • pnpm publish again 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 the embed-readme setting continues to control only whether the readme is written into the package.json inside 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 and verify-deps-before-run compared 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 Modified to an unconditional request, preventing false ERR_PNPM_CACHE_MISSING_AFTER_304 failures pnpm/pnpm#12882.

    If the retry also returns 304, report ERR_PNPM_META_NOT_MODIFIED_WITHOUT_CACHE instead.

  • Fixed pnpm update removing transitive lockfile entries when dedupePeerDependents is 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 tokenHelper command 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 exec or pnpm dlx are still running (for example, when one project's command fails during pnpm --recursive exec). The PIDs of these commands are now recorded when they are spawned and their whole process trees are terminated with taskkill on 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 pack now respects workspace-root .npmignore and .gitignore files when packing workspace packages.

Platinum Sponsors

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.12.0: pnpm 11.12

Compare Source

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, where cafs and fetchers cannot 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 .git entry, so the filter works in a git worktree checked out inside another repository's tree.

  • 43711ce: pnpm outdated no longer checks the registry for dependencies that are resolved from local link:, file:, or workspace: references in the lockfile #​12827.

  • 3c6718b: Fixed a deadlock in peer dependency resolution: pnpm install hung forever when a peer dependency cycle spanned a project's own dependencies and auto-installed peer providers, for example when installing electron-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 a 2.0.0 resolved 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 list now stops paginating after a fail-safe cap of 1000 pages, so a misbehaving registry cannot keep the command looping forever.

  • eee7c9a: verify-deps-before-run no longer spawns a pnpm install when pnpm is executed in a directory that has no package.json. A mistyped command run outside a project (for example pnpm 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

Bit
OpenAI

Gold Sponsors

Sanity Discord Vite
SerpApi CodeRabbit Stackblitz
Workleap Nx

v11.11.0

Compare Source

Minor Changes
  • 508b8c2: Added the pnpm access command 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: Allow allowBuilds entries 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. with minimumReleaseAge or trustPolicy enabled) this cuts peak RSS by roughly 30%, back in line with pnpm 10. The resolved lockfile is unchanged.
  • 51300fd: Prevent a crafted pnpm-lock.yaml from 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 snapshot version: "../../x") is now rejected at formatGlobalVirtualStorePath, 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 symlinked pnpm-lock.yaml files when reading or writing the env lockfile document.
  • 9318a11: Allow registries and namedRegistries to be configured in the global config.yaml file.
  • 51300fd: Fixed a path traversal vulnerability where a dependency whose manifest name was a scoped path traversal (e.g. @x/../../../<path>) could be written outside node_modules to an attacker-controlled location during pnpm 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 from pnpm-lock.yaml when 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 install and pnpm dedupe silently 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 with autoInstallPeers when 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 the httpProxy, httpsProxy, noProxy, proxy, and noproxy settings are no longer expanded when these settings come from a project's pnpm-workspace.yaml. They now receive the same protection already applied to registry, namedRegistries, and pnprServer.
  • d1da02e: pnpm publish no longer prints credentials when the target registry is configured with inline user: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-update now honors trustPolicy=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 the pn alias 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 be node, deno, or bun, and the version must not contain a comma. Previously these were interpolated straight into a pnpm add selector, 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

Compare Source

Minor Changes
  • e2e3c81: Added the issues command as an alias of bugs, so pnpm issues opens the package's bug tracker URL in the browser.

  • 8491f8e: Added the prefix command which prints the current package prefix directory (or global prefix directory if -g / --global is used).

  • 3425e80: Added an _auth setting 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 the pnpm_config__auth environment variable. The env form sidesteps the GitHub Actions / bash / zsh limitation that broke the existing pnpm_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 http or https and must not include credentials, query strings, or fragments:

    export pnpm_config__auth='{"https://registry.npmjs.org":{"@&#8203;":{"authToken":"npm-token"},"@&#8203;org":{"authToken":"org-token"}}}'

    The equivalent in the global config.yaml:

    _auth:
      https://registry.npmjs.org:
        "@&#8203;":
          authToken: npm-token
        "@&#8203;org":
          authToken: org-token

    Within each registry URL, @ means registry-wide/default credentials and package scopes like @org bind credentials to that scope on the same host. The only supported credential field is authToken (maps to _authToken / bearer auth); the deprecated basicAuth / username + password forms are intentionally not accepted here.

    Each entry also infers a trusted registry route: @ routes the default registry (and pnpm add <pkg> resolves there), and @org routes that scope. Because the credential and destination host arrive in one trusted value, repo-controlled pnpm-workspace.yaml or project .npmrc cannot redirect the token to a different host. _auth is honored only from the env var and the global config — it is ignored in a project pnpm-workspace.yaml / .npmrc, so repo-controlled config can never supply registry auth. Precedence: CLI flags (--registry, --@&#8203;scope:registry) > pnpm_config__auth > global config.yaml _auth > pnpm-workspace.yaml.

    Both pnpm_config__auth (lowercase, documented form) and PNPM_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 global config.yaml _auth on a conflicting key. tokenHelper is 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-update and packageManager version-switching can now install and link pnpm v12 (the Rust port), published with equal content under both the pnpm and @pnpm/exe names on the next-12 dist-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 unscoped pnpm package (the Rust exe) — even when updating from the SEA @pnpm/exe build.

  • 1dd12bd: When resolving through a pnpr install-accelerator server, pnpm no longer forwards its own upstream registry credentials in the resolve request. Only the Authorization header 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 authentication authUrl and doneUrl in 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 whose SHASUMS256.txt is signed by the new releaser verify successfully.

  • acbdb94: Fixed shell tab completion not suggesting workspaces after the -F alias for --filter option.

  • dcabb78: Fixed pnpm up -r <pkg> bumping unrelated packages that have open semver ranges. Previously, any update mutation nullified the lockfile-derived preferredVersions globally, so packages with ^x.y.z ranges could re-resolve to newer compatible versions even though the user only asked to update a specific package. The install layer now always seeds preferredVersions from the lockfile, and caller-supplied preferred versions (such as the vulnerability penalties of pnpm audit --fix) layer on top of the seed instead of replacing it. The targeted package still bumps: the per-resolve updateRequested flag 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.exe flavor of a bin, so a stale node.exe no longer survives on PATH after uninstall, and a new global install no longer silently overwrites an existing node.exe.
    • pnpm add -g pnpm@<version> (and @pnpm/exe@<version>) is now rejected like the bare pnpm form, pointing to pnpm self-update.
    • Dependency aliases read from a global package's manifest are validated before being joined onto node_modules paths, 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.
  • 25c7388: pnpm now rejects jsr: specifiers whose package name is not a valid npm package name — an empty scope or name (e.g. jsr:@&#8203;scope/), path separators inside the name, or any other shape validate-npm-package-name rejects — with ERR_PNPM_INVALID_JSR_PACKAGE_NAME instead 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:@&#8203;/bar), path separators inside the name (e.g. gh:@&#8203;scope/../name), or any other shape validate-npm-package-name rejects — with ERR_PNPM_INVALID_NAMED_REGISTRY_PACKAGE_NAME instead of passing the name through to registry URLs and metadata cache file paths.

  • 96da7c5: node-gyp's gyp_main.py and gyp entrypoints are now packed with the executable bit in the pnpm and @pnpm/exe tarballs. 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-offline resolution 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-app now rejects --entry / pnpm.app.entry and --output-dir / pnpm.app.outputDir values 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-controlled package.json from 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 are ERR_PNPM_PACK_APP_ENTRY_OUTSIDE_PROJECT, ERR_PNPM_PACK_APP_OUTPUT_DIR_OUTSIDE_PROJECT, and ERR_PNPM_PACK_APP_OUTPUT_FILE_NOT_REGULAR.

    When ad-hoc signing macOS targets, pnpm pack-app now runs the system codesign by absolute path and resolves ldid to a location outside the project, so a repository-controlled node_modules/.bin on PATH cannot hijack the signer.

  • ce5d5a5: Relative paths in patchedDependencies are now resolved against the lockfile directory when computing patch file hashes, so running pnpm install from a subdirectory no longer fails with ENOENT looking for the patch file in the wrong location #​12762.

  • ebb4096: pnpm peers no longer reports a conflict for a missing peer dependency that is ignored via pnpm.peerDependencyRules.ignoreMissing.

  • dcabb78: Fixed a prototype-pollution hazard when seeding preferred versions: a dependency named __proto__ in a manifest or in pnpm-lock.yaml could write through Object.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: Hardened pnpm deploy --force so 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 when PNPM_CONFIG_NPMRC_AUTH_FILE points 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 --filter with --filter-prod #​8335.

  • d539172: pnpm pack and pnpm publish no longer follow a symlinked workspace LICENSE file 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: Fixed pnpm up <pkg> producing a different result than a fresh install of the same manifests would. The resolver now distinguishes updateRequested (true only for packages that match the user's update target) from the broader update flag, 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 and pnpm install ran. Preferred versions a fresh install applies (manifest pins, versions propagated down the dependency chain, and the vulnerability-avoidance penalties of pnpm 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>@&#8203;<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 to pnpm.overrides instead, 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

Compare Source

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 a ERR_PNPM_MISSING_TARBALL_INTEGRITY lockfile verification violation (the install fails closed) rather than being silently re-fetched.
  • 6c35a43: Added --exclude-peers to pnpm sbom. With auto-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 matches pnpm list --exclude-peers; note the SBOM flag prunes a peer's exclusive subtree, which is stricter than pnpm list (which only hides leaf peers).
Patch Changes
  • 25a829e: pnpm audit --fix now writes a single combined minimumReleaseAgeExclude entry 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 in pnpm-workspace.yaml are merged into the combined form rather than left as duplicates. Installs that auto-collect immature versions into minimumReleaseAgeExclude now 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 through styled-jsx — from a package's peer-dependency suffix across otherwise identical installs, churning the lockfile and causing intermittent pnpm dedupe --check failures 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 in pnpm dlx where a failed install could surface a spurious EBUSY: resource busy or locked error. The cleanup of a partially-populated dlx cache is now best-effort with retries and no longer masks the original error.

  • ec7cf70: Shortened the pnpm dlx cache path so deep dependency trees no longer overflow Windows' MAX_PATH, which could make a dependency's lifecycle script fail with spawn cmd.exe ENOENT.

  • 05b95ab: Fixed pnpm hanging (and crashing with an unhandled promise rejection) when a non-retryable network error such as SELF_SIGNED_CERT_IN_CHAIN occurs 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 a pnpm audit performance 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: When enableGlobalVirtualStore is toggled on for a project that was previously installed without it, stale hoisted symlinks under node_modules/.pnpm/node_modules are now replaced instead of being left pointing at the old per-project virtual store location #​9739.

  • 6545793: Fixed pnpm install --ignore-workspace overwriting the allowBuilds map in pnpm-workspace.yaml. The ignored builds of a package with a build script were auto-populated into allowBuilds even though --ignore-workspace was passed, clobbering committed true/false values with the set this to true or false placeholder #​12469.

  • fbdc0eb: Fixed minimumReleaseAgeExclude and trustPolicyExclude so 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 flag form-data@2.5.6 as violating minimumReleaseAge, 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: Fixed pnpm patch dropping 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 /-/pnpr namespace: POST /v1/resolve is now POST /-/pnpr/v0/resolve and POST /v1/verify-lockfile is now POST /-/pnpr/v0/verify-lockfile. The capability handshake at GET /-/pnpr advertises protocol version 0 to 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 matching devEngines.runtime or engines.runtime entry that was materialized from it. Blank runtime selectors are normalized to latest.

  • 17e7f2c: pnpm sbom now emits a CycloneDX issue-tracker external reference for components (and the root) whose package.json declares a bugs URL. Email-only bugs entries 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 vendors getNpmTarballUrl (previously the external get-npm-tarball-url package) and adds isCanonicalRegistryTarballUrl, the predicate the lockfile writer uses to decide whether a tarball URL is derivable from name+version+registry (and can therefore be omitted from pnpm-lock.yaml).

    Exposing isCanonicalRegistryTarballUrl lets a custom resolver (pnpmfile resolvers) fronting a proxy that serves tarballs on a non-canonical path (e.g. an ephemeral localhost:<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 %2F as well as %2f (percent-encoding is case-insensitive), and protocol-insensitive comparison strips only a leading http(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 a 403/401 on a private registry, or a network error) as ERR_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 as ERR_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

Compare Source

Minor Changes
  • c112b61: Added a --dry-run option to pnpm 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)
  • 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.

withastro/astro

Changes

  • Replaces os.cpus().length with os.availableParallelism() when sizing the image optimization queue in generate.ts. os.cpus().length returns the host CPU count and ignores cgroup CPU quotas — in a --cpus=2 container 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 as os.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

withastro/astro

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

withastro/astro

Closes #17381

Changes

  • Fixes create-astro silently 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, so modern-tar extracts template files into a separate NFD-encoded sibling directory while the CLI still exits 0 — leaving the user's NFC directory with only AGENTS.md/CLAUDE.md and no package.json, src/, or astro.config.mjs.
  • Adds relocateNFDFiles() in template.ts, called immediately after downloadTemplate. 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 via fs.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 in create-astro to unblock affected users.

Testing

  • Adds packages/create-astro/test/units/relocate-nfd.test.ts with 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.
withastro/astro

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.

withastro/astro

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.

withastro/astro

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.url is immutable per the Fetch spec, and a rewritten request is a different object with its own entry.
  • It needs no signature changes, so createRequestFromNodeRequest keeps returning a Request and hands over the URL it had already parsed. (Changing that return type would have been a breaking change to a published export, not a minor.)
  • 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.ts and exports getRequestURL from astro/app. It follows the existing render-options.ts, which already carries per-request data on a Request behind a Symbol.for.
  • BaseApp.match(), getPathnameFromRequest() and computePathnameFromDomain() use it.
  • createRequestFromNodeRequest and createRequest hand over the URL they already parsed to build the Request, guarded on request.url === url.href so a Request-constructor normalization can never seed a mismatched URL. Signatures unchanged.
  • Cloudflare: matchStaticAsset takes the Request instead 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 realPath override 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.

withastro/astro

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

  • normalizeUrl guards each url.pathname assignment 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.

withastro/astro

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 fresh JSON.parse result and returns right away.
  • The generated virtual:astro:manifest module passes an inline literal and only ever uses the returned manifest's routes.

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

  • astro unit tests: 3174 pass, 0 fail. Covers building an App from a manifest and running match and render.
  • @astrojs/node integration 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.

withastro/astro

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 ECONNRESET or 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.

withastro/astro

Changes

  • Updates @cloudflare/vite-plugin to ^1.45.1 so generated wrangler config no longer includes the removed legacy_env field
  • 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/cloudflare
  • pnpm --filter @astrojs/cloudflare test
  • pnpm peers check
  • Added a regression test confirming that generated config file does not contain legacy_env

Docs

No documentation changes are needed

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.1.2

Patch Changes

  • #17445 a5f7230 Thanks @ocavue! - Updates dependency cookie to v2. Cookie values made entirely of URL-safe characters are no longer percent-encoded in Set-Cookie headers; encoded values round-trip exactly as before.

  • #17402 a89c137 Thanks @farrosfr! - Fixes a bug where mutated Astro.locals during the request lifecycle are lost and not passed to custom error pages (404.astro/500.astro)

  • #17405 91992ef Thanks @Araluma! - Prevents an unhandled promise rejection from the prefetch fetch fallback. In WebKit (Safari), <link rel="prefetch"> is unsupported, so prefetch uses the fetch() fallback; on a flaky connection that fetch rejects with TypeError: 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().

withastro/astro

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

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.1.1

Patch Changes

  • #17399 4b03702 Thanks @matthewp! - Fixes encoded request paths being routed incorrectly when using domain-based i18n
withastro/astro

Fixes #17382

Changes

  • Ensures request-scoped Astro.locals mutations made during the request lifecycle are preserved and passed to custom error pages (404.astro/500.astro).
  • Implemented by defining locals as a dynamic getter on renderOptions within FetchState, rather than copying the static reference at constructor time.

Testing

  • Adds a new unit test in packages/astro/test/units/app/locals.test.ts to assert that error pages can still access mutated request-scoped locals even 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.
withastro/astro

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

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()],
    }),
  ],
})
withastro/astro

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 -elem directive with a hash no longer implicitly adds self. Users who want same-origin resources on the element directive can still configure self explicitly.
  • Keeps the implicit self fallback 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.
withastro/astro

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

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

withastro/astro

Changes

  • That was a review I made at some point but it got forgotten
  • This PR removes the unused generic from AstroLoggerDestination since the chunk is always an AstroLoggerMessage
  • 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

withastro/astro

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

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.1.0

Minor Changes

  • #17302 5f4dc03 Thanks @astrobot-houston! - Adds a new deferRender option to the glob() content loader

    When 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 .mdx files already use.

    This reduces memory usage during astro build for large collections whose rendered output is much larger than the source — for example, Markdown that uses heavy rehype plugins like rehype-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 deferRender is false, preserving the existing behavior of rendering entries eagerly during sync so their rendered HTML can be cached across builds.

  • #17296 30698a2 Thanks @ematipico! - Adds a new experimental collectionStorage option for controlling how the content layer persists its data store

    By 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 44c4989 Thanks @ematipico! - Adds support for the more specific CSP directives script-src-elem, script-src-attr, style-src-elem, and style-src-attr through a new kind option.

    Previously, CSP was only scoped to generic script-src/style-src directives. Now each source or hash can be scoped to a narrower directive — for example, to allow inline style attributes (such as those from define:vars or Shiki) without loosening the policy for your <style> and <link> elements.

    Scoping sources and hashes in your config

    Each entry in resources and hashes can be an object with a kind property. Depending on whether you use scriptDirective or styleDirective, "element" targets script-src-elem or style-src-elem, "attribute" targets script-src-attr or style-src-attr, and "default" (the same as a bare string or hash) targets script-src or style-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 kind option 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 84814d4 Thanks @astrobot-houston! - Adds a new format() option to the paginate utility. The format() 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 .html extension, you can use format() 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 7db6420 Thanks @matthewp! - Adds a --ignore-lock flag to astro dev for 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, or astro dev logs. --ignore-lock cannot 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 16de021 Thanks @florian-lefebvre! - Allows passing URL entrypoints when configuring the logger

    Matching 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 4407483 Thanks @astrobot-houston! - Fixes the JSON logger crashing with process is not defined in non-Node runtimes like Cloudflare's workerd. The JSON logger now uses console.log/console.error instead of process.stdout/process.stderr, matching the pattern already used by the console logger.

  • #17391 186a1e7 Thanks @florian-lefebvre! - Fixes a case where an integration could not update the logger with updateConfig()

  • #17394 d9f99e1 Thanks @matthewp! - Fixes element-specific CSP directives to preserve the existing behavior of configured script and style resources

  • #17374 b2d1b3e Thanks @astrobot-houston! - Fixes dev server returning 404 for ?url imported assets when accessed via browser navigation

  • #17390 ed71eaf Thanks @florian-lefebvre! - Removes an unused and undocumented generic from the AstroLoggerDestination type

  • #17393 092da56 Thanks @matthewp! - Hardens generated transition styles, development metadata, and server island URLs when embedding dynamic values

withastro/astro

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.

withastro/astro

Changes

#17384 (the fix for #17377) was merged without a changeset — changeset-bot flagged it — so the fix currently won't appear in the changelog. This adds the missing changeset (astro patch) describing the fix.

Testing

N/A — changeset only.

Docs

N/A

withastro/astro

Changes

  • Local font filenames (/_astro/fonts/<hash>.<ext>) are now deterministic regardless of where the project is checked out. Previously, FsFontFileContentResolver.resolve() returned absolutePath + 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 of url + '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

withastro/astro

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=style requests, 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.
withastro/astro

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.

スクリーンショット 2026-07-16 0 56 42

Testing

I migrated the commits to reproduce the test error and verified the migration process.

reproduce issue

スクリーンショット 2026-07-14 22 53 52

Solved issue

スクリーンショット 2026-07-14 23 10 57
withastro/astro

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.cache is undefined — cache not configured at all (correct: should auto-enable)
  • config.cache.enabled is false — 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:

  1. Auto-enables cache when needsWorkerCache: true and cache is unconfigured
  2. Does not enable cache when needsWorkerCache: false
  3. Does not override when cache is already enabled: true
  4. Does not override explicit cache.enabled: false (regression test for this issue)

Confirmed working by issue reporter @skezo against the preview build.

Closes #17375

withastro/astro

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 send Accept: text/html on navigation, which triggered the route guard — but the guard was computing existsInSrc incorrectly.
  • The bug was in route-guard.ts: resolving new URL('.' + pathname, config.srcDir) for a path like /src/downloads/a.pdf produced <root>/src/src/downloads/a.pdf (double-nested), which doesn't exist. The fix resolves from config.root instead and checks containment using startsWith, so the path is correctly identified as inside srcDir.

Testing

  • Added packages/astro/test/units/dev/route-guard-middleware.test.ts with two cases: one that confirms src/ 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 ?url import behavior with no API changes.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.9

Patch Changes

  • #17286 a249317 Thanks @astrobot-houston! - Fixes the first browser visit after astro dev starts triggering an immediate full page reload

  • #17369 a94d4a5 Thanks @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 during astro dev.

@astrojs/vercel@11.0.3

Patch Changes

withastro/astro

Changes

  • Adds a build-generated token to the internal ISR route rewrite, so the _isr function only applies the x_astro_path path override when the token matches the current build's route table. This replaces the previous x-vercel-isr header check.
  • Strips the internal x_astro_path and x_astro_path_token params 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 _isr entrypoint tests covering the override being ignored without a valid token, ignored when only x-vercel-isr is 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.
withastro/astro

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:

  • beforeAll drains startup-buffered HMR messages with a throwaway page, so a buffered full-reload cannot 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.

withastro/astro

Changes

  • Escapes regex metacharacters in image.remotePatterns (hostname, pathname) and image.domains when generating Netlify Image CDN remote_images patterns, 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/domains behavior is unchanged.
withastro/astro

Changes

  • astro preview --open now correctly opens a browser when using adapters with a custom preview entrypoint (such as @astrojs/cloudflare). Previously, the open option was never forwarded from core preview to adapter preview entrypoints.
  • Adds open?: string | boolean to the PreviewServerParams interface so adapters can receive and act on the value.
  • Passes settings.config.server.open from packages/astro/src/core/preview/index.ts to the adapter's preview function, matching how headers and allowedHosts are already forwarded.
  • Updates the Cloudflare adapter's preview entrypoint to use the received open value instead of hardcoding false.

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 / --open is an existing CLI flag with no change in user-facing behavior or configuration surface.

Closes #17362

withastro/astro

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

スクリーンショット 2026-07-11 22 07 07

Reflect #13902 in code testing

スクリーンショット 2026-07-11 23 14 00

Related PR

#13902

withastro/astro

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 -b clean. 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.

withastro/astro

Changes

  • Building from a symlinked (or junction-linked) directory dropped all CSS from the output. resolveRoot() used path.resolve(), which does not follow symlinks, so config.root kept the symlink path while Vite/Rollup resolve module IDs to the real path (resolve.preserveSymlinks is false). The pagesByViteID keys then never matched the Rollup module graph IDs, so getPageDataByViteID() returned undefined and CSS was never associated with any page.
  • resolveRoot() now runs fs.realpathSync() on the resolved path so config.root matches 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 on main (the symlinked build has no CSS) and passes with the fix.

Docs

  • No docs needed — this restores the documented build behavior for symlinked roots.
withastro/astro

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 -> replaceState trick 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.

withastro/astro

Changes

Bumps the compiler to the latest

Testing

N/A

Docs

N/A

withastro/starlight

Loved working on this documentation, thank you for making Starlight!

Site added: https://bonjourr.fr/docs/

withastro/astro

Closes #17348

Changes

  • When the prerender handler processes a request (e.g. /_image), matchRoute now skips routes with prerender: false before importing their component modules. Previously, /_image's component (@astrojs/cloudflare/image-transform-endpoint) was eagerly imported in the Node prerender environment, where its top-level import { env } from 'cloudflare:workers' fails. A prerendered catch-all like [...slug].astro was enough to trigger this path because matchAllRoutes returns it as a candidate for /_image, causing the broad prerender gate in plugin.ts to invoke the prerender handler for the request.
  • Threads the existing prerenderOnly flag from handleRequest through devMatch into matchRoute to 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.
withastro/astro

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:

  1. collectStaticImages only existed on the workerd prerenderer. This method installs sharp (or a user-configured image service) into globalThis.astroAsset.imageService before the image generation pipeline runs. When prerenderEnvironment: 'node', the default Node prerenderer was used instead — which had no collectStaticImages, so image transforms fell back to the workerd passthrough stub, returning input buffers unchanged.

  2. The default prerender entrypoint was skipped when setPrerenderer was called. Astro skips setting the prerender entrypoint when settings.prerenderer is truthy, so restoring it manually was also required.

Fix

In packages/integrations/cloudflare/src/index.ts:

  • astro:build:start hook — Added an else if (hasBuildImageService) branch for prerenderEnvironment: 'node'. Wraps the default prerenderer with a collectStaticImages method that installs sharp (or the user's custom service) before image generation runs, mirroring the existing workerd prerenderer behavior.

  • astro:build:setup hook — Restores the default prerender entrypoint when prerenderEnvironment: 'node' and hasBuildImageService are both true, since it gets skipped when setPrerenderer is 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' with prerenderEnvironment: 'node' (the bug scenario)
  • User-configured custom image service with prerenderEnvironment: 'node'

All existing tests continue to pass.

withastro/astro

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

So 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 check relies on. This is expected with the TypeScript 7 native compiler, which does not ship that API yet. Use TypeScript 6.x for astro check for 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 the fileExists crash.
  • With a real TypeScript (ts.sys / ts.findConfigFile present): the guard is a no-op and existing behavior is unchanged.
  • pnpm --filter @astrojs/language-server build passes; the file matches Prettier config.

Docs

Not needed — no public API or documented behavior changes; only the failure message for an already-unsupported configuration.

withastro/astro

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 await are still collected.

Testing

  • Adds a unit test that counts Set iterator 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

withastro/astro

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:
image

NEW:
image

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

withastro/astro

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

withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.8

Patch Changes

  • #17363 3f4efc5 Thanks @astrobot-houston! - Fixes astro preview --open not opening a browser when using an adapter with a custom preview entrypoint, such as @astrojs/cloudflare

  • #17313 e2e319d Thanks @ronits2407! - Exposes the AstroRuntimeLogger interface to allow users to properly type the logger functions at runtime.

  • #17328 025cc74 Thanks @matthewp! - Fixes astro dev --force not replacing an already-running dev server

  • #17353 2bba277 Thanks @ematipico! - Updates the Astro compiler to the latest version, which fixes many regressions. Refer to the changelog for more details.

  • #17344 79a41e0 Thanks @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 3f4efc5 Thanks @astrobot-houston! - Fixes astro preview --open not 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 64b0d66 Thanks @Princesseuh! - Fixes custom pre components not applying to syntax-highlighted code blocks when using the Sätteri Markdown processor with MDX.

@astrojs/netlify@8.1.2

Patch Changes

  • #17368 ee74c28 Thanks @matthewp! - Fixes the generated Netlify Image CDN remote_images patterns so that regex metacharacters (such as .) in image.remotePatterns (hostname, pathname) and image.domains are 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 5196fb4 Thanks @kkhys! - Fixes an opaque Cannot read properties of undefined (reading 'fileExists') crash when astro check runs against the TypeScript 7 native compiler. The native compiler does not ship the programmatic API the checker relies on yet, so astro check now fails early with a clear message pointing to the tracking issue instead.

@astrojs/markdown-satteri@0.3.4

Patch Changes

  • #17341 64b0d66 Thanks @Princesseuh! - Fixes custom pre components not applying to syntax-highlighted code blocks when using the Sätteri Markdown processor with MDX.
withastro/astro

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

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

withastro/astro

Changes

  • Fixes intermittent CI failures in the Test (integrations) job (most often on macos-14) that surface as a whole test file failing with Error: Unable to deserialize cloned data due to invalid or unsupported version while 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-file process isolation.

Testing

N/A

Docs

N/A

withastro/astro

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 — replace process.stdout/process.stderr with console.log/console.error; remove now-unused ConsoleStream type and node:stream import
  • packages/astro/test/units/logger/destination.test.ts — update tests to spy on console.log/console.error instead of process.stdout.write/process.stderr.write; add a cross-runtime compatibility test asserting write() contains no process. reference
  • .changeset/cool-rice-exist.md — patch changeset for astro
withastro/astro

Closes #17324

Changes

  • Adds --ignore-lock to astro 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-lock instance is intentionally untracked — astro dev stop/status/logs continue to only ever see the one canonical, lock-tracked server.
  • Rejects --ignore-lock combined 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, and getForceIgnoreLockConflict covering the flag parsing and both conflict messages.

Docs

withastro/astro

Changes

  • astro dev --force now 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 killDevServer helper in core/dev/lockfile.ts, and reuses it in stop.ts and background.ts (previously duplicated in both).

Testing

  • Adds killDevServer unit tests in test/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 — --force is already documented for astro dev; this fixes it to match existing documented behavior.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.7

Patch Changes

  • #17318 23a4120 Thanks @astrobot-houston! - Fixes CSS module scoped-name hash mismatch in astro dev when using vite.css.transformer: 'lightningcss' with content collections. Previously, a component importing a CSS module and rendered via content collection render() would get different class name hashes in the element and the injected <style> tag, causing styles not to apply.

  • #17323 4298883 Thanks @ematipico! - Fixes a dev server memory leak which caused Node.js to emit warnings in the console.

  • #17323 4298883 Thanks @ematipico! - Fixes a dev server crash when a .html or /index.html suffixed request (such as those netlify dev probes as pretty-URL fallbacks) matched a dynamic endpoint route, causing a TypeError: Missing parameter error

  • #17325 cebc404 Thanks @astrobot-houston! - Fixes a bug where CSS @import rules could end up mid-stylesheet after inline CSS chunks were merged during build, causing browsers to silently ignore them

  • #17323 4298883 Thanks @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 4298883 Thanks @ematipico! - Fixes build-time image optimization ignoring a custom image service registered by an integration

    Previously, when using imageService: 'compile' or imageService: 'custom', a custom image service was only respected if it was set directly in the image.service option of astro.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 4298883 Thanks @ematipico! - Prebundles astro/components and 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 4298883 Thanks @ematipico! - Fixes an issue where vars weren't available at build time. Now the adapter loads vars from the Wrangler config so astro:env public variables resolve at build time

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/telemetry@3.3.3

Patch Changes

withastro/astro

Changes

  • CSS @import rules that end up mid-stylesheet after mergeInlineCss concatenates inline chunks are silently ignored by browsers, breaking font loading and other imported resources in production builds.
  • mergeInlineCss now skips merging any CSS chunk that contains @import. Those chunks remain as their own <style> tags, keeping @import at 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.ts with 6 cases covering: normal merging, skipping merge when current or previous chunk has @import, two adjacent @import chunks, 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.
withastro/astro

Reverts #17287

withastro/astro

Changes

  • Dynamic endpoint routes with a file extension (e.g. [...slug].png.ts) no longer fail with NoMatchingStaticPathFound during astro build when trailingSlash: "always" is set. stringifyParams() now mirrors the trailingSlashForPath() 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.ts covering:
    • 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.

Docs

  • No docs update needed — this is a bug fix for existing static build behavior with no API surface change.
withastro/astro

Changes

  • Fixes CSS module class names not matching between the element and the injected <style> tag in astro dev when using vite.css.transformer: 'lightningcss' with content collections. Production builds were unaffected.
  • Root cause: getStylesForURL() re-imports CSS modules with ?inline appended 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 existing cssContentCache (which holds already-processed CSS with correct hashes) with the content asset propagation plugin, so ?inline re-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 with lightningcss enabled, 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

withastro/astro

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

withastro/astro

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.dynamicImports is empty.

Docs

  • No docs update needed; this restores expected build output behavior.
withastro/astro
## 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.
withastro/astro

Summary

  • add an Astro-specific SatteriFeatures type that overrides the upstream smartPunctuation JSDoc
  • export the type so consumers see the corrected default in satteri({ features })

Fixes #17305.

Testing

  • corepack pnpm --filter @astrojs/prism build
  • corepack pnpm --filter @astrojs/internal-helpers build
  • corepack pnpm --filter @astrojs/markdown-satteri build
  • corepack pnpm --filter @astrojs/markdown-satteri test
  • corepack pnpm exec prettier packages/markdown/satteri/src/processor.ts packages/markdown/satteri/src/index.ts --check
  • git diff --check

Notes

AI-assisted. I manually reviewed the final diff and verification output.

withastro/astro

Changes

  • Extracts the logger type from APIContext to an exported AstroComponentLogger interface.
  • 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 typecheck and pnpm run test:match "exports".
  • No runtime behavioral changes were made.

Docs

withastro/astro

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 optimizeDeps excludes astro:* 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. reloading mid-render, which the workerd module runner does not survive cleanly: components resolve react from node_modules while react-dom/server comes from a freshly re-optimized deps_ssr chunk. Two copies of React in one render throw Invalid hook call / Cannot read properties of null (reading 'useState') in every island, and the page returns 200 with the islands empty.
  • Fix: add astro/components to the server-environment optimizeDeps.include list, right after the existing astro/virtual-modules/transitions.js entry, 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 shows dependency optimized: astro/componentsoptimized dependencies changed. reloading → repeated Invalid hook call and TypeError: 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.

withastro/starlight

Add Hostgrid help center site to the showcase.

Site: https://hostgrid.dev/help/

withastro/starlight

Removed broken link to ⁠ecoping.earth⁠. The domain appears to be expired/inactive, which poses a potential security risk for documentation users.

withastro/starlight

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

Changes

  • Adds a deferRender?: boolean option to the glob() loader. When true, renderable content entries (e.g. Markdown) skip eager rendering during content sync and instead use the same deferred-render path that .mdx files already use — rendering happens on demand at page build time via the Vite markdown plugin.
  • This resolves OOM crashes during astro build for large Markdown collections that use heavy rehype plugins like rehype-katex, where rendered HTML can be 70× larger than source. Closes #17301.
  • Removes the // todo: add an explicit way to opt in to deferred rendering comment that tracked this gap since the Content Layer was introduced.

Testing

  • Unit tests in glob-loader.test.ts cover eager rendering (default), deferred rendering when deferRender: true, and that non-renderable data entries are unaffected.
  • Also verified by running node --max-old-space-size=256 astro build against a 200-file KaTeX-heavy fixture. The build previously OOM'd; with deferRender: true it succeeds. Confirmed working by the issue reporter (@integrable).

Docs

withastro/astro

Closes #17297

Changes

  • A .html or /index.html suffixed request to a dynamic endpoint route (e.g. GET /api/items/123/status.html for src/pages/api/items/[id]/status.ts) no longer crashes dev with TypeError: Missing parameter: id.
  • The dev route matcher strips .html / /index.html when retrying unmatched requests, but getParams() only stripped .html for route.type === 'page'. For a matched endpoint, params came back {} and stringifyParams threw. getParams now 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 .html in 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.html variants on any 404 — firing the crash for every dynamic API endpoint.

Testing

  • Added getParams unit tests covering .html and /index.html requests to a dynamic endpoint route, plus a guard asserting endpoints that capture .html in a param keep the suffix.

Docs

No docs update needed — this restores the routing behavior users already expect.

withastro/astro

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

withastro/docs#14210

/cc @withastro/maintainers-docs for feedback!

withastro/astro

Changes

  • CSS url('data:image/...') data URIs no longer crash astro build with ENAMETOOLONG when tsconfig.json has compilerOptions.baseUrl set. The vite-plugin-config-alias CSS transform now skips any url() reference that starts with data:, since data URIs are inline content and never valid file paths to resolve.
  • The regression was introduced in v7 by the cssUrlRE matching added to the CSS transform handler (commit 4766f3716d). The baseUrl alias regex /^(?!\.*\/|\.*$|\w:)(.+)$/ was designed to exclude Windows drive letters but \w: only matches a single character before the colon, so data: (4 chars) slipped through and was passed to fs.statSync().

Closes #17293

Testing

  • Adds packages/astro/test/alias-css-url-data-uri.test.ts with a fixture that combines tsconfig.json baseUrl: "." and a CSS url() 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.
withastro/astro

Closes #16119

Changes

  • Fixes a v6 regression where scoped CSS from components nested inside a client:only island 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.ts correctly marks a child component's CSS for deletion (it was already bundled during SSR for another page), but the client:only page never participated in SSR so it never received that CSS. The getParentClientOnlys walk would add the CSS to pagesToCss, but the deleted asset was already gone by the time inlineStylesheetsPlugin ran.
  • Fix: when the getParentClientOnlys walk finds a CSS entry that was deleted, inline the CSS content directly into pageData.styles from deletedCssAssets, bypassing the need for the asset to survive to inlineStylesheetsPlugin. Content-based deduplication prevents double-injecting styles that survived deletion normally.

Testing

  • Added packages/astro/test/client-only-child-styles.test.ts with a fixture covering two scenarios: a page using client:only (where the child's scoped styles must be recovered from deletedCssAssets) and a page using client:load directly (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.
withastro/astro

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

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:

  1. The problem was present and persistent between different Browser engines.
  2. The patch worked.

Both showed to be true.

Docs

Unneeded, very small text changes.

Images

"‎examples/blog/src/pages/index.astro":

Before After
Blog Before Blog After

"examples/advanced-routing/src/pages/index.astro":

Before After
Routing Before Routing After
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.7

Patch Changes

  • #17317 437401e Thanks @ematipico! - Fixes a dev server memory leak which caused Node.js to emit warnings in the console.

  • #17299 1170b6d Thanks @astrobot-houston! - Fixes a dev server crash when a .html or /index.html suffixed request (such as those netlify dev probes as pretty-URL fallbacks) matched a dynamic endpoint route, causing a TypeError: Missing parameter error

  • #17316 ed92e31 Thanks @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 6929e40 Thanks @adamchal! - Fixes build-time image optimization ignoring a custom image service registered by an integration

    Previously, when using imageService: 'compile' or imageService: 'custom', a custom image service was only respected if it was set directly in the image.service option of astro.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 464c46e Thanks @jkomyno! - Prebundles astro/components and 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 6a99600 Thanks @matthewp! - Fixes an issue where vars weren't available at build time. Now the adapter loads vars from the Wrangler config so astro:env public variables resolve at build time

  • Updated dependencies []:

    • @astrojs/underscore-redirects@1.0.3

@astrojs/telemetry@3.3.3

Patch Changes

withastro/astro

Closes #17283

Changes

  • The first browser to connect after astro dev starts no longer receives an immediate, unprompted full-reload. Previously, invalidateDataStore() unconditionally sent a full-reload HMR signal during the buildStart hook — before any client connected — causing the signal to queue and fire the moment the first WebSocket client arrived.
  • Adds a notifyClient option to invalidateDataStore() (default true). The buildStart call 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 that buildStart invalidates the data store module but does not send a full-reload to the client HMR channel.

Docs

  • No docs update needed; this is a dev-server behavior fix with no API or config changes.
withastro/astro

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.ts covers both modes with the service registered by an integration.
  • Existing image service tests pass unchanged.

Docs

Not required. Existing docs assumes this works.

withastro/astro

Changes

  • <Picture inferSize> with remote URLs no longer fails with FailedToFetchRemoteImageDimensions on rate-limiting servers (e.g. Wikimedia HTTP 429). The Picture component calls getImage() once per output format, and each call was independently invoking inferRemoteSize() — firing 2–4 HTTP requests to the same URL in rapid succession.
  • Fix: resolve remote dimensions once in Picture.astro before the getImage() loop, then pass explicit width/height (dropping inferSize) 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.ts suite, which includes a Picture component works case with inferSize in 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 — inferSize behavior on <Picture> is unchanged from the user's perspective; this fixes a reliability regression.
withastro/astro

Changes

  • Fixes Image and Picture component layout styles being missing in astro dev when JavaScript is disabled. The dev CSS pipeline was caching content by raw module ID (with \0 prefix) in the transform hook, but looking it up via collected.id — the wrapId()-transformed version (/@id/__x00__...) — causing a cache miss for virtual CSS modules and producing empty <style> tags. Switching the lookup to collected.idKey (the raw ID) aligns both sides of the cache.

Testing

  • Adds packages/astro/test/units/dev/dev-css-virtual-modules.test.ts with 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

withastro/starlight

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

@astrojs/starlight@0.41.3

Patch Changes

  • #3911 1686ecc Thanks @timothyjordan! - Keeps keyboard focus inside the mobile menu while it is open, preventing focus moving to hidden interactive elements in page content.
withastro/astro

Fixes #16790

Changes

  • astro:env public variables defined under vars in a Wrangler config (wrangler.toml/.json/.jsonc) now resolve when imported from astro:env/client and astro:env/server. Previously only .dev.vars was loaded into the environment, so public vars from the Wrangler config were inlined as undefined at build time (only getSecret worked).
  • The adapter now resolves the effective env in astro:config:done via wrangler's unstable_readConfig + unstable_getVarsForDev, which merges config vars with .dev.vars/.dev.vars.<env>/.env overrides exactly as wrangler dev does (respecting CLOUDFLARE_ENV), and assigns the result to process.env. This replaces the previous hand-rolled .dev.vars reader, so there's a single source of truth that matches Wrangler's own precedence.

Testing

  • Removed the manual process.env.API_URL/process.env.PORT overrides from the astro-env SSR tests. Those overrides were masking the bug; the tests now rely on the adapter loading vars from the fixture's wrangler.jsonc, exercising the real code path for build and dev. The existing secret/action secret cases continue to cover .dev.vars loading through the unified path.

Docs

  • No docs update needed; this makes the adapter match the already-documented astro:env behavior.
withastro/astro

Changes

  • Adds a render() overload for LiveDataEntry<LiveLoaderDataType<C>> to packages/astro/templates/content/types.d.ts. Previously, the only overload covered entries from DataEntryMap (regular content collections). When a project only uses live.config.ts, DataEntryMap is empty, so keyof DataEntryMap resolves to never and render() rejects any live entry with a TypeScript error.
  • The new overload mirrors the pattern already used by getLiveEntry and getLiveCollection. Projects with only content.config.ts are unaffected — LiveContentConfig resolves to never so the overload is inert.

Closes #16688

Testing

  • No new test added — the bug is in a .d.ts type 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.
withastro/astro

Changes

  • Island component paths (client:component-pathmetadata.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 directory index files. Previously the path stayed extensionless, so the include/exclude globs 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 include glob, 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 any useState inside 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.ts covering extension probing in Vite's order, directory index resolution, the existing .jsx.tsx remap, and pass-through of extension-ful, bare, and # subpath specifiers (all fail without the fix)
  • New multiple-jsx-renderers fixture page using an extensionless import matched by an include glob — the build hard-fails on it without the fix

Docs

  • No docs update needed — this makes behavior match what the integration docs already show (include globs written with file extensions).
withastro/astro

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

Fixes #16078

Changes

  • "Go To References" invoked from a .ts file now finds usages inside .astro files that are reached through Astro.locals.*. Previously only .astro files with an explicit frontmatter import were discovered; a page using Astro.locals.utils.toUpper() was silently missed.
  • The @astrojs/ts-plugin now injects the installed Astro package's env.d.ts and astro-jsx.d.ts into the TypeScript program, mirroring the language server's addAstroTypes(). Without these the Astro global is undeclared, so the type chain through Astro.locals can't resolve and references aren't found. (.astro files themselves already enter the program via Volar's external-files mechanism, so no file-discovery change is needed.)
  • Passes includeScripts: false / includeStyles: false to convertToTSX() (matching the language server) so <script>/<style> bodies are no longer wrapped in {() => { ... }} arrow functions, where import declarations 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.tsx output for a template that only uses Astro.locals.utils.toUpper(), then runs findReferences through the raw TypeScript language service. Asserts the reference is missed without type injection and found once addAstroTypes runs — 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.
withastro/astro

Closes #17206

Changes

  • The Container API no longer emits a false deprecation warning for markdown.gfm and markdown.smartypants when neither option was set by the user.
  • Root cause: AstroContainer.create() and createFromManifest() passed ASTRO_CONFIG_DEFAULTS directly to validateConfig(). Because ASTRO_CONFIG_DEFAULTS includes gfm: true and smartypants: true, the warnDeprecatedMarkdownOptions check 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 to validateConfig().

Testing

  • Added packages/astro/test/units/render/container-deprecation.test.ts: asserts that AstroContainer.create() with default config does not log the markdown.gfm/smartypants deprecation warning.

Docs

  • No docs update needed — this is a false-positive warning fix with no API or behavior change for users.
withastro/astro

Fixes #15627

Changes

  • A <script> inside a component rendered through Astro.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.
  • SlotString now keeps its content as an ordered chunks stream 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.
withastro/astro

Changes

  • create-astro now respects HTTP_PROXY/HTTPS_PROXY environment variables when downloading templates. When proxy env vars are set, the CLI re-execs itself with Node.js's --use-env-proxy flag so that native fetch() (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.ts with two cases: one verifying that HTTPS_PROXY is 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.
withastro/astro

Changes

  • Adds a format option to PaginateOptions that accepts a (url: string) => string callback. 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 by paginate().
paginate(items, {
  pageSize: 10,
  format: (url) => `${url}.html`,
})

Closes #13604

Testing

  • Added 3 new describe blocks (8 test cases) to packages/astro/test/units/render/paginate.test.ts covering: format applied to all URL properties, format skipped for undefined URLs, format applied after base path is prepended, and no regression when format is omitted.

Docs

  • The new format option is documented inline via JSDoc on PaginateOptions. A docs-site update to the paginate() reference may be warranted to surface this option for users hitting 404s on static file servers.

withastro/docs#14201

@withastro/maintainers-docs for feedback

withastro/astro

Closes #17135

Changes

  • getPackage now imports packages using the absolute path returned by require.resolve() (converted to a file URL via pathToFileURL) instead of the bare package name. Previously, require.resolve() was correctly scoped to the project's cwd, but the subsequent await import(packageName) resolved from astro's own install location — causing astro check to 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 its node_modules, then verifies getPackage can find and load it when passed that directory as cwd.

Docs

  • No docs update needed — this is an internal resolution fix with no user-facing API change.
withastro/astro

This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.

Releases

astro@7.0.6

Patch Changes

  • #17261 79aa99c Thanks @astrobot-houston! - Fixes a false deprecation warning for markdown.gfm and markdown.smartypants when using the Container API

  • #17247 f94280d Thanks @chatman-media! - Fixes route generation throwing "Missing parameter" (or silently dropping the segment) when a dynamic param's value is 0. The generator used truthy checks instead of checking for undefined, so paginate(posts, { params: { categoryId: 0 } }) would crash even though 0 is a perfectly valid param value.

  • #17278 6f11739 Thanks @astrobot-houston! - Fixes missing CSS for virtual style modules (e.g., responsive image layout styles) in dev mode when JavaScript is disabled

  • #17250 0b30b35 Thanks @matthewp! - Fixes the security.checkOrigin check 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 composable astro/hono pipeline depending on the order of the middleware() primitive (or when it was omitted).

  • #17274 8c3579b Thanks @astrobot-houston! - Fixes missing render() type overload for live collection entries. Previously, calling render() on a LiveDataEntry produced a TypeScript error when using only live.config.ts without a content.config.ts.

  • #17257 4208297 Thanks @astrobot-houston! - Fixes astro check failing to find @astrojs/check and typescript when astro is installed in a directory outside the project tree (e.g. pnpm virtual store)

  • #17272 b428648 Thanks @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 directory index resolution. This makes the include/exclude options 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 when include was set alongside another JSX renderer

  • #17279 2aeaa44 Thanks @astrobot-houston! - Fixes a bug where <Picture inferSize> with a remote image could fail with FailedToFetchRemoteImageDimensions when 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 5240e26 Thanks @matthewp! - Hardens the handling of attribute rendering when using with custom elements.

  • #17248 429bd62 Thanks @astrobot-houston! - Fixes a crash when using Astro's getViteConfig with Vitest browser mode (e.g., Storybook vitest runner). Astro now skips dev server setup inside Vitest, preventing errors.

  • #17260 14524c0 Thanks @matthewp! - Fixes a regression where a <script> inside a component rendered through Astro.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 ed6bea5 Thanks @astrobot-houston! - Fixes proxy support by respecting HTTP_PROXY and HTTPS_PROXY environment variables when downloading templates. On Node.js v22.21.0+ and v24.5.0+, create-astro now automatically enables the --use-env-proxy flag so that native fetch() 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 eb6f97e Thanks @matthewp! - Fixes trailing-slash handling for request paths that begin with a backslash

    With 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 the Location header of a 301 response. Because browsers resolve a leading \ the same way as /, the resulting Location could 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 0142964 Thanks @FrancoKaddour! - Fix @astrojs/solid-js incorrectly claiming Svelte 5 components compiled with the newer $$renderer prop (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 eb6f97e Thanks @matthewp! - Fixes trailing-slash handling for request paths that begin with a backslash

    With 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 the Location header of a 301 response. Because browsers resolve a leading \ the same way as /, the resulting Location could 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

@astrojs/ts-plugin@1.10.10

Patch Changes

  • #17269 c72d4f2 Thanks @matthewp! - Fixes "Go To References" from .ts files missing usages inside .astro files that are reached through Astro.locals. The plugin now injects Astro's ambient types so type chains like Astro.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
withastro/astro

Changes

  • Prefetch hover listeners now attach to links injected by server:defer components. 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 MutationObserver inside onPageLoad() that watches for dynamically added DOM nodes. When a new anchor (or element containing one) is detected, the prefetch callback re-runs. The existing listenedAnchors WeakSet 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.
withastro/astro

Changes

  • Fixes VS Code syntax highlighting breaking when @property (or any CSS construct with angle brackets like syntax: "<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.yaml from a single begin/end approach to the same two-pattern begin/end (single-line) + begin/while (multi-line) approach already used by script injection patterns since PR #15109. The while pattern 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 @property with a syntax: "<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.snap and style.astro.snap snapshots 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

withastro/astro

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 isInternalPath covering backslash prefixes.
  • Integration test in the Node adapter using a raw request line (a literal backslash, which URL parsers would otherwise normalize) asserting a 404 with no Location header.
withastro/astro

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

Changes

  • Applies the security.checkOrigin check at the request dispatch points (Astro Actions and on-demand endpoints/pages), so it holds consistently regardless of how the composable astro/hono pipeline is ordered. Previously the check only ran inside the middleware() primitive, so it could be skipped depending on primitive order — or when middleware() 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 the pages() 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 when checkOrigin is disabled.

Testing

  • Adds action-origin-check.test.ts: composes a Hono app with actions() before middleware() 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 with pages() and no middleware() and asserts a cross-origin POST to an on-demand endpoint is rejected before the handler runs, while same-origin succeeds.

Docs

  • No docs update needed; existing security.checkOrigin behavior is unchanged from the user's perspective.
withastro/astro

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

withastro/astro

Closes #16275

Changes

  • When process.env.VITEST is set, configureServer in vite-plugin-astro-server now 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 implement wrapDynamicImport, causing a TypeError when Astro's SSR runner tries to load createAstroServerApp via 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.ts with two cases: one confirming the early return when VITEST is set (using a bare fake server with no environments), and one confirming normal execution proceeds when VITEST is 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