AstroEco is Contributing…
Display your GitHub pull requests using astro-loader-github-prs
Description
- Fixes #3890 (review) & #3890 (comment)
Tweak a comment: "non-Firefox" → "Chromium-based"
Safari has already supported the syntax :lang(zh, ja).
The position of Opera is the culprit.
This change is so trivial that we should ship it with other changes.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| pnpm/action-setup | action | patch | v6.0.5 → v6.0.6 |
Release Notes
pnpm/action-setup (pnpm/action-setup)
v6.0.6
What's Changed
Full Changelog: pnpm/action-setup@v6.0.5...v6.0.6
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
- Adds a new "Write a Unit Test" step to the triage bot's fix skill, so it produces a regression test alongside every fix attempt.
- Creates
reference/unit-testing.mddocumenting unit test conventions, file placement, and the shared test utilities/mocks available in the repo. Referenced fromAGENTS.mdso any agent (not just triage) can use it. - Adds a Unit Test line to the triage bot's GitHub comment template.
Testing
- No test changes — this PR only modifies agent instructions and reference docs.
Docs
- No user-facing docs needed.
reference/unit-testing.mdis agent-facing only.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.39.2
Patch Changes
Description
- Fixes #3872 (comment)
:lang(zh, ja) used in #3872 shamefully has not been supported Chromium and Safari. As a result, text-autospace has been enabled only in Firefox. Replaced it with the combination of :lang(zh) and :lang(ja).
Screenshot in Chrome (Pay attention to the slight presence of whitespace around "Astro" and "Starlight"):
Before (https://starlight.astro.build/ja/getting-started/):
After:

Changes
Updates the attribute escaping logic to use named entities (& and ") instead of numeric entities (& and ").
Currently, <title> tags render named entities, while other meta tags render numeric entities. Although both formats are semantically equivalent, this results in inconsistent raw HTML output.
This change aligns the escaping behavior across all head and meta tags so they consistently render named entities.
Closes #16657
Testing
Updates the test cases that rendered & and " to instead render & and ".
Docs
N/A
Changes
- When
imageServiceuses compile-time behavior (compileor{ build: 'compile', ... }), the adapter no longer always replaces the user’simage.servicewith the workerd stub. It only swaps in@astrojs/cloudflare/image-service-workerdwhen the project is on Astro’s default Sharp service; a customentrypointis left intact. - The Vite plugin now receives the resolved image service
entrypoint(so static image handling matches the configured service instead of hard-coding the workerd stub). - The Cloudflare workerd prerenderer only temporarily swaps in Sharp for the Node-side static image pass when that workerd stub is in use; with a custom service it leaves
globalThis.astroAsset.imageServiceunset so the custom transform runs.
Testing
- Adds
compile-custom-image-service.test.tswith a fixture that setsimageService: { build: 'compile' }and a custom image service; asserts prerendered HTML uses the customgetURL()output and custom image attributes.
Docs
- No docs update: behavior now matches the existing
imageServicedocs expectation that a customimage.serviceis respected when compatible with the adapter.
Changes
- Fixes #16201 — the Cloudflare adapter silently replaced a user-defined
image.servicewith@astrojs/cloudflare/image-service-workerdwheneverimageServicewas unset (default'cloudflare-binding') or set to'compile'/'cloudflare-binding'/'cloudflare'/'passthrough'. Custom services (e.g., a third-party CDN service) are now preserved across all modes. - Added a guard at the top of
setImageConfigthat detects a non-defaultimage.service(entrypoint ≠astro/assets/services/sharp) and returns the config untouched, mirroring the precedent in the existing fallbackdefault:branch and the explicit'custom'mode. - Emits a single
logger.infoline when this preservation kicks in, telling the user the override was skipped and pointing them atimageService: 'custom'to silence the notice. - Added a
.changeset(@astrojs/cloudflarepatch).
Testing
- New unit test:
packages/integrations/cloudflare/test/image-config.test.ts(10 cases) covering preservation acrossundefined,'passthrough','cloudflare','cloudflare-binding','compile', and the compound{ build: 'compile' }/{ build: 'compile', runtime: 'cloudflare-binding' }configs. Also asserts the existing override of default sharp in'cloudflare-binding'and'compile'is preserved (no regression) and that explicit'custom'continues to work. - Existing image-related fixture tests run clean:
external-image-service.test.ts(2/2),binding-image-service.test.ts(7/7),compile-image-service.test.ts(5/5). pnpm run buildsucceeds;tsc -bclean.
Docs
No user-facing docs changes needed. Behavior aligns with what the existing docs already imply (a custom image.service should be respected); this PR brings the Cloudflare adapter into line. Worth a brief note in the Cloudflare adapter README that custom services are preserved alongside imageService modes — happy to follow up in withastro/docs if maintainers prefer.
Closes #16201
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.2
Patch Changes
-
#16638
272185bThanks @ematipico! - Fixes a bug where the Astro compiler wasn't freed at the end of the build. After the fix, the memory used by the compiler is now correctly freed at the end of the build. -
#16544
d365c97Thanks @matthewp! - TightensisRemotePath()to reject control characters after a leading slash and fixes the dev image endpoint origin check -
Updated dependencies [
d365c97,9256345]:- @astrojs/internal-helpers@0.9.1
- @astrojs/markdown-remark@7.1.2
@astrojs/prism@4.0.2
Patch Changes
- #15723
9256345Thanks @rururux! - Fixes an issue where the<Prism />component failed to work in Cloudflare Workers.
@astrojs/cloudflare@13.5.1
Patch Changes
-
#16652
98c32ccThanks @greatjourney589! - Fixes user-declared KV namespace bindings being duplicated in the generateddist/server/wrangler.json, which caused wrangler validation to fail with " assigned to multiple KV Namespace bindings." The Astro Cloudflare config customizer now returns only the auto-injectedSESSIONbinding and lets@cloudflare/vite-pluginmerge it with the user's wrangler config, instead of pre-merging the user's bindings into the output. -
#15723
9256345Thanks @rururux! - Fixes an issue where the<Prism />component failed to work in Cloudflare Workers. -
Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
- @astrojs/underscore-redirects@1.0.3
@astrojs/markdoc@1.0.5
Patch Changes
-
#15723
9256345Thanks @rururux! - Updates internal type usage from@astrojs/prism. -
Updated dependencies [
d365c97,9256345,9256345]:- @astrojs/internal-helpers@0.9.1
- @astrojs/markdown-remark@7.1.2
- @astrojs/prism@4.0.2
@astrojs/mdx@5.0.5
Patch Changes
- Updated dependencies [
9256345]:- @astrojs/markdown-remark@7.1.2
@astrojs/netlify@7.0.9
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
- @astrojs/underscore-redirects@1.0.3
@astrojs/node@10.1.1
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
@astrojs/preact@5.1.3
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
@astrojs/react@5.0.5
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
@astrojs/vercel@10.0.7
Patch Changes
- Updated dependencies [
d365c97]:- @astrojs/internal-helpers@0.9.1
@astrojs/internal-helpers@0.9.1
Patch Changes
- #16544
d365c97Thanks @matthewp! - TightensisRemotePath()to reject control characters after a leading slash and fixes the dev image endpoint origin check
@astrojs/markdown-remark@7.1.2
Patch Changes
Closes #16590
Changes
- Fixes a regression introduced in #16555 where user-declared KV namespace bindings (e.g.
RATE_LIMIT,CACHE) appear twice in the generateddist/server/wrangler.json, causing wrangler to fail with"<binding> assigned to multiple KV Namespace bindings.". - The Astro Cloudflare config customizer now returns only the auto-injected
SESSIONbinding fromkv_namespaces, instead of pre-merging the user's existing bindings into the output. @cloudflare/vite-pluginalready merges the customizer's output with the user's wrangler config, so echoing the user's bindings caused the duplication.- Removed the now-unused
withSessionKVBinding()helper. - Added a changeset (
@astrojs/cloudflarepatch).
Affected versions reported in the issue: @astrojs/cloudflare@13.3.0–13.3.1 + astro@6.2.x. Last working combination was astro@6.1.9 + @astrojs/cloudflare@13.2.1, which matches the pre-#16555 behavior this PR restores.
Testing
- Updated two existing assertions in
packages/integrations/cloudflare/test/wrangler.test.tsthat locked in the buggy merged-output shape. - Added a dedicated regression test
does not include user-declared KV bindings in the output (regression #16590)covering the multi-binding scenario from the issue (RATE_LIMIT+CACHEdeclared by the user) and asserting the customizer returns only[{ binding: 'SESSION' }]. - All 25 tests in
wrangler.test.tspass locally.
Docs
No documentation changes needed. The fix restores documented and previously-shipped behavior — the public API and configuration surface (sessionKVBindingName, automatic SESSION binding provisioning) are unchanged.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.1
Patch Changes
Changes
- Restores the
FREDKBOT_GITHUB_TOKENenv var mapping that was accidentally dropped in the flue 0.3 upgrade (#16441). Without it,postGitHubCommentfails at the end of every triage run.
Testing
- No test changes. Verified by inspecting the failing run.
Docs
- No docs needed — CI-only change.
Description
Adds generic clock, desktop, mobile phone, app window, database, server, git branch, notes, question mark, analytics, and padlock icons. Also adds a new logo icon for SolidJS. The mobile icon has a bit of a funny name — mobile-android — but I kept it to match the source library’s name.
Which icons to include built-in is always a bit subjective, but I went through our icon library and tried to add a handful that might meet some more common needs on docs sites given I was already adding a couple based on user requests.
For the SolidJS logo, I asked on their Discord server to confirm which style they preferred for a monochrome icon (as it deviates from their more common blue gradient style).
Changes
After #16402 migrated every test under packages/astro/test to TypeScript, two scripts in packages/astro/package.json were left pointing at non-existent .test.js globs:
"test:match": "astro-scripts test \"test/**/*.test.js\" --match",
"test:cli": "astro-scripts test \"test/**/cli.test.js\"",Because astro-scripts test wraps the matched files in a temp module before invoking node:test (see scripts/cmd/test.js:90-102), an empty match still produces a passing run — node:test counts the empty wrapper as a single passing "test" — so the breakage was silent. pnpm test:cli reports tests 1, pass 1, suites 0 and exits in ~100ms without ever executing the real CLI tests in test/cli.test.ts.
This PR:
- Updates the globs to
.test.tsso they match real files (test/cli.test.ts,test/units/**/*.test.ts,test/*.test.ts). - Adds
--strip-typessonode:testcan load the TypeScript files, matching the existingtest:unitandtest:integrationscripts on the surrounding lines. - Restores
test:matchas the documented "run a subset of tests by name pattern" command referenced inCONTRIBUTING.mdlines 130-132.
Testing
Verified locally before/after the change:
Before — pnpm test:cli (phantom pass, no real tests run):
> astro-scripts test "test/**/cli.test.js"
✔ /workspaces/astro/packages/astro/node_modules/.astro/test.mjs (90ms)
ℹ tests 1 ℹ suites 0 ℹ pass 1
After — pnpm test:cli --match "astro --version" (real cli.test.ts test):
> astro-scripts test "test/**/cli.test.ts" --strip-types --match 'astro --version'
▶ astro cli
✔ astro --version (338ms)
✔ astro cli (339ms)
ℹ tests 1 ℹ suites 1 ℹ pass 1
Note the suite count flips from 0 → 1, confirming cli.test.ts is actually loaded and the describe('astro cli') suite runs.
Docs
No docs changes needed — CONTRIBUTING.md:130-132 already documents pnpm test:match "<pattern>"; this PR makes that documented behaviour work again.
No changeset added — internal-only package.json script change, no published-package surface affected.
Changes
- Renames
packages/astro/e2e/astro-island-hydration-error.test.jsto.test.tsand adds minimal type annotations to align with the e2e test convention established in #16402. - This file was introduced in #16412 (after #16402 had already migrated every other e2e test to TypeScript), so it landed using the prior
.jsconvention. It is the only remaining.test.jsfile inpackages/astro/e2e/. - As a side effect, the file is now matched by
playwright.config.js(which usestestMatch: 'e2e/*.test.ts'), so its two regression tests forastro:hydration-errorrecovery (added in #16412) now actually execute in CI. They were silently skipped before this rename.
Testing
pnpm exec tsc --noEmit -p tsconfig.test.jsonpasses against the converted file.- The diff is a near-mechanical port of the existing test (12 insertions / 6 deletions), preserving identical test semantics.
gitrecognises the change as a rename (83% similarity). - The
Window.__hydrationErrorEventstyping follows the samedeclare global { interface Window {...} }pattern already used bye2e/view-transitions.test.ts.
Docs
No docs changes required — internal test refactor with no public-API surface.
No changeset added — internal-only test file rename, no published-package behaviour changes.
Changes
- The generic image endpoint (
assets/endpoint/generic.ts) self-fetches local images from the same origin. #16519 added anisRemoteAllowedcheck on the response URL, but that check rejects local URLs (e.g.http://host/_astro/image.png) since they aren't inimage.domainsorimage.remotePatterns. This caused local images on non-prerendered pages to 404. - Extracted the fetch logic into
loadImage.tswith anisRemoteflag that gates theisRemoteAllowedcheck. Local images skip it — they're already protected by the same-origin guard in the caller.
Fixes #16644
Testing
- Added
test/units/assets/endpoint-load-image.test.tswith 4 cases: local image succeeds, unauthorized remote rejected, allowed remote succeeds, fetch failure handled. The local image test fails without the fix.
Docs
- No docs needed — this is a regression fix restoring prior behavior.
Changes
A flaky test was detected in astro-assets-prefix.test.ts in CI.
https://github.com/withastro/astro/actions/runs/25502409719/job/74839082587
The root cause is likely that tests are now running in parallel, and both astro-assets-prefix.test.ts and astro-assets-prefix-multi-cdn.test.ts share the same fixture while also calling fixture.clean() in their after() hooks, which deletes the cache folder.
When the test fails, the log shows "The collection "blog" does not exist or is empty. Please check your content config file for errors." and as shown here, the getDataStoreFile function references the .astro folder, which is exactly what clean() removes.
astro/packages/astro/src/content/content-layer.ts
Lines 468 to 470 in 45e50e4
| export function getDataStoreFile(settings: AstroSettings, isDev: boolean) { | |
| return new URL(DATA_STORE_FILE, isDev ? settings.dotAstroDir : settings.config.cacheDir); | |
| } |
astro/packages/astro/test/test-utils.ts
Lines 270 to 284 in 45e50e4
| clean: async () => { | |
| await fs.promises.rm(config.outDir, { | |
| maxRetries: 10, | |
| recursive: true, | |
| force: true, | |
| }); | |
| const astroCache = new URL('./node_modules/.astro', config.root); | |
| if (fs.existsSync(astroCache)) { | |
| await fs.promises.rm(astroCache, { | |
| maxRetries: 10, | |
| recursive: true, | |
| force: true, | |
| }); | |
| } | |
| }, |
To fix this, I merged the two test files into one.
An alternative would have been to give each test its own dedicated fixture, but since the two test cases are similar in nature, I went with the merge approach instead.
Testing
Green CI
Docs
N/A
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.39.1
Patch Changes
-
#3885
010eed1Thanks @ArmandPhilippot! - Fixes the version mentioned in an error message related to autogenerated sidebar groups support. -
#3887
b3c6990Thanks @delucis! - Adds 13 new icons:clock,desktop,mobile-android,window,database,server,code-branch,notes,question,question-circle,analytics,padlock, andsolidjs.
Description
The Starlight version mentioned in the error related to autogenerated sidebar groups support is wrong:
Support for autogenerated sidebar groups was removed in Starlight v0.38.0.
#3618 has been released earlier in 0.39.0, not 0.38.0.
I added a changeset because we need a new version for such a small change. 😅
Changes
Migrate a js test file to ts.
Testing
CI should pass.
Docs
N/A
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| changesets/action | action | minor | v1.7.0 → v1.8.0 |
Release Notes
changesets/action (changesets/action)
v1.8.0
Minor Changes
- #258
f5dbf72Thanks @tom-sherman! - Support draft version PR modes with a newprDraftinput. Usecreateto create new version PRs as drafts, oralwaysto also convert existing version PRs back to draft when updating them.
Patch Changes
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
A follow-up to #16471 and #16472
svelte2tsx@0.7.55 supports typescript v6, so we can also update the typescript peer dependency range in @astrojs/svelte.
Testing
CI should pass
Docs
A changeset file is added because we need to release a new version for @astrojs/svelte.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/cloudflare@13.5.0
Minor Changes
- #16639
4d72482Thanks @ematipico! - The adapter now depends on Astro 6.3.0.
@astrojs/node@10.1.0
Minor Changes
- #16639
4d72482Thanks @ematipico! - The adapter now depends on Astro 6.3.0.
Changes
The compiler was never torn down at the end of the build. This PR fixes it.
Testing
Manually tested.
Docs
N/A
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@astrojs/starlight@0.39.0
Minor Changes
-
#3618
dcf6d09Thanks @HiDeoo! -⚠️ BREAKING CHANGE: This release changes how autogenerated links work in Starlight’s sidebar configuration.If you have sidebar groups using the
autogeneratekey, you must now wrap that configuration in anitemsarray:{ label: 'My group', - autogenerate: { directory: 'some-dir' }, + items: [{ autogenerate: { directory: 'some-dir' } }], }This change unlocks the possibility to mix autogenerated links and other links in a single group, for example:
{ label: 'Mixed group', items: [ 'example-page', { autogenerate: { directory: 'examples' } }, { label: 'More examples', link: 'https://example.com' }, ], }
This release also updates the shape of autogenerated sidebar entries in route data. Autogenerated links and groups in
Astro.locals.starlightRoute.sidebarnow include anautogenerateobject with the configureddirectoryvalue:{ type: 'link', label: 'Example', href: '/examples/example/', isCurrent: false, autogenerate: { directory: 'examples' } }
-
#3618
dcf6d09Thanks @HiDeoo! -⚠️ BREAKING CHANGE: This release changes the default collapsed state of autogenerated sidebar subgroups.Autogenerated subgroups no longer inherit the
collapsedvalue from their parent group. They are now expanded by default unless explicitly configured withautogenerate.collapsed.If your sidebar configuration relies on a collapsed parent group to also collapse its autogenerated subgroups, update your configuration to set
autogenerate.collapsedtotrue:{ label: 'Reference', collapsed: true, items: [ - { autogenerate: { directory: 'reference' } }, + { autogenerate: { directory: 'reference', collapsed: true } }, ], } -
#3845
4d755f5Thanks @delucis! - Adds a<link rel="alternate" hreflang="x-default" href="...">tag pointing to the default locale in multilingual sites. Thex-defaultalternate is used as a signal of which language to fall back to if no other is available. Learn more in Google’s SEO localization docs. -
#3862
ec70630Thanks @itrew! - Makes spacing of items in nested lists more consistent -
#3872
417a66cThanks @tats-u! - Enables the CSS propertytext-autospacein Chinese and Japanese documents.If you would prefer to disable autospacing in Chinese and Japanese pages, you can add the following custom CSS to your site:
[lang]:where(:lang(zh, ja)) { text-autospace: initial; }
-
#3797
9764ebdThanks @delucis! - Avoids the risk of layout shift when users expand and collapse sidebar groupsThis release can introduce additional padding to the site sidebar on certain devices to reserve space for scrollbars. You may wish to inspect your site sidebar visually when upgrading.
If you would prefer to keep the previous styling, you can add the following custom CSS to your site:
.sidebar-pane { scrollbar-gutter: auto; }
-
#3858
6672c35Thanks @delucis! - Updatesi18next, used for Starlight’s localization APIs, from v23 to v26There should not be any user-facing changes from this update
Description
Adds flush() and close() methods to AstroIntegrationLogger, mirroring AstroLogger. Both delegate to the underlying destination's optional flush / close hooks.
AstroLogger already exposes flush() and close() (added in #16404). AstroIntegrationLogger did not, so any code path that placed an AstroIntegrationLogger where an AstroLogger was expected — e.g. App.#prepareResponse calling this.logger.flush() — would crash with TypeError: this.logger.flush is not a function. The two logger classes now have a symmetric public surface.
Closes #16622.
Changes
packages/astro/src/core/logger/core.ts: addflush()andclose()toAstroIntegrationLogger, identical shape to the methods onAstroLogger.packages/astro/test/units/logger/logger.test.ts: coverAstroIntegrationLogger.flush()/.close()(delegation, no-throw, fork inheritance).- changeset:
patch.
Testing
pnpm --filter astro build:ci
pnpm --filter astro exec astro-scripts test test/units/logger/logger.test.ts --strip-types --teardown ./test/units/teardown.ts
# 10 pass, 0 fail
Docs
No public API docs change required — AstroIntegrationLogger.flush() / .close() mirror the existing AstroLogger methods (no behaviour change for destinations that don't define flush/close).
Changes
Fixes #16612
Testing
Should pass! We unfortunately cannot test this in the monorepo because TypeScript is of course always available
Docs
N/A
Changes
This PR improves integration tests:
- remove large-array, as we already use that in our benchmarks
- moves some fixtures at higher level so that they aren't built multiple times
- removes the custom rollup thing because it's already tested elsewhere
Testing
Green CI
Docs
N/A
Changes
Some tests in core-image.test.ts have been flaky in Astro's CI.
https://github.com/withastro/astro/actions/runs/25425893932/job/74579340641?pr=16579
https://github.com/withastro/astro/actions/runs/25407064617/job/74520525804?pr=16614
After investigating, I found that the removeDir function, which is used to clean up the cache directory before tests, was not being awaited despite returning a Promise.
This PR adds the missing await to address the potential race condition.
Testing
Green CI
Docs
N/A
Changes
One of TypeScript's biggest strengths is static analysis. In practice, we rely on it in three places:
- In editors (e.g. vscode / TypeScript language service)
- During local and CI builds via
tsc - During local and CI linting via
eslintwith type-aware linting
All three ultimately depend on TypeScript project configuration through tsconfig.
Before #16241, Astro used three different tsconfig setups for these workflows:
- Editors used an effectively empty root
tsconfig.json, so the language service fell back to its default behavior. tscused per-package configs that only includedsrc/, excluding files likebin/andtest/.eslintused a separatetsconfig.eslint.json.
This inconsistency created a poor developer experience. For example, vscode could report type errors while CI remained green, making editor diagnostics unreliable.
This PR completes the TypeScript project references work introduced in previous PRs. After this PR, all three workflows share the same source of truth: the root tsconfig.json.
The root tsconfig.json now includes all src/, test/, scripts, and configuration files (e.g. prettier.config.mjs). It doesn't include SFC files (.astro, .svelte, etc.) and test fixtures, though.
Also, note that if a JS/TS file is included in eslint but not in the root tsconfig.json, eslint will show the following errors. This is beneficial because it ensures that we have all files added to tsconfig.json, thus these files are covered by tsc.
/astro/packages/astro/e2e/astro-island-hydration-error.test.js
0:0 error Parsing error: /astro/packages/astro/e2e/astro-island-hydration-error.test.js was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject
Testing
CI should pass
Docs
N/A
Changes
Migrate some tests from JS to TS.
These tests come from an old pull request, which was merged into main after the TypeScript migration was completed.
Testing
N/A
Docs
N/A
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| actions/labeler | action | minor | v6.0.1 → v6.1.0 |
Release Notes
actions/labeler (actions/labeler)
v6.1.0
Enhancements
- Add changed-files-labels-limit and max-files-changed configuration options to cap the number of labels added by @bluca in #923
Bug Fixes
- Improve Labeler Action documentation and permission error handling by @chiranjib-swain in #897
- Preserve manually added labels during workflow runs and refine label synchronization logic by @chiranjib-swain in #917
Dependency Updates
- Upgrade brace-expansion from 1.1.11 to 1.1.12 and document breaking changes in v6 by @dependabot in #877
- Upgrade minimatch from 10.0.1 to 10.2.3 by @dependabot in #926
- Upgrade dependencies (@actions/core, @actions/github, js-yaml, minimatch, @typescript-eslint) by @Copilot in #934
New Contributors
- @chiranjib-swain made their first contribution in #897
- @bluca made their first contribution in #923
- @Copilot made their first contribution in #934
Full Changelog: actions/labeler@v6...v6.1.0
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
- Fixes
ModuleLoader.getSSREnvironment()to return thessrEnvironmentparameter the loader was constructed with, instead of always looking upviteServer.environments.ssr. - With adapters that register a separate
prerenderVite dev environment (notably@astrojs/cloudflarewithprerenderEnvironment: 'node'),createViteLoaderbuilds a dedicated loader per environment. Every other method already uses the constructor parameter - onlygetSSREnvironment()ignored it, so the prerender handler's loader returned the unrelatedssrenvironment andgetComponentMetadata()walked the wrong module graph in dev. With Cloudflare's default workerdssr, that env is also non-runnable, making theas RunnableDevEnvironmentcast unsafe. - Removes the now-unused
ASTRO_VITE_ENVIRONMENT_NAMESimport. - Changeset:
astro: patch(.changeset/fix-module-loader-prerender-env.md). - Closes #16436. Follow-up to #16292, which fixed the visible style-into-
<template>symptom inastro:head-metadatabut did not touch this separate correctness bug in the per-environment module loader.
Testing
tsc --noEmitonpackages/astropasses cleanly.- Existing regression test
packages/astro/test/head-propagation-prerender-env.test.jsstill passes (1/1) - confirms no regression on theastro:head-metadatapath that #16292 fixed. - No new test added:
loader.getSSREnvironment()has a single caller (getComponentMetadata), and when only thessrenv existsssrEnvironment === viteServer.environments.ssr, so the new behaviour is a strict superset of the old correct behaviour. Happy to extend thehead-propagation-prerender-envfixture with a prerendered route to exercise the prerender handler's loader directly if maintainers prefer.
Docs
No docs change needed - this is a dev-server-internal correctness fix with no user-facing API or behaviour change. Users on adapters with a separate prerender env (e.g. @astrojs/cloudflare with prerenderEnvironment: 'node') will simply see correct head metadata for prerendered routes in dev.
Changes
The Houston bot for the triage workflow is currently not working.
https://github.com/withastro/astro/actions/runs/25424727887/job/74575139340
The log shows the following:
pnpm exec flue run issue-triage \
--target node \
--session-id "issue-triage-$ISSUE_NUMBER" \
--payload "{\"issueNumber\": $ISSUE_NUMBER}"
Unknown argument: --session-id
Looking into the flue repository, I couldn't find the --session-id argument anywhere.
Instead, I found that --id is defined as the correct argument.
https://github.com/withastro/flue/blob/808b34ee154b94025d5cb2b45288d88b7766ae0f/packages/cli/bin/flue.ts#L95-L108
I also confirmed that commit 8388ed1 in the flue repository changed --session-id to --id.
Based on this, I concluded that --id should be used instead of --session-id, and updated it accordingly.
Testing
I don't know how to do it 😔
Docs
N/A
Changes
issue : #15609
- Added a fallback path in
handleActionfor contexts that lack a pipeline. - If neither a pipeline nor
ACTION_API_CONTEXT_SYMBOLis present on the contextActionCalledFromServerErroris still thrown as before.
Testing
Before implementation
After implementation
Docs
Changes
Right now, types for entry.data field returned by getLiveEntry() are not working.
---
import { getLiveEntry } from "astro:content";
const { entry } = await getLiveEntry("example", "hello-world");
const data = entry?.data; // type `any` instead of defined type from loader.
---The cause is that the utility type ExtractDataType used here is missing, so entry.data is always of type any.
This pr adds the missing utility type to the file.
Testing
Tested by manually adding the missing utility type to the generated .astro/content.d.ts in my projects and verifing the expected types to work.
Docs
It adds missing typing.
Fixes the smoke test failure caused by astro-expressive-code@0.42.0 being too recently published to pass the 3-day minimumReleaseAge gate. Since this is a Starlight dependency used by our docs smoke test, it should always be installable regardless of release age.
Another PR updating depedencies. Expressive Code is the main motivator as the latest release updates to Shiki v4 (thanks again @ocavue!) so Starlight will share this with Astro instead of having v3 and v4 in the tree.
Prod dependencies
- Updates
astro-expressive-codein Starlight (0.41.7 => 0.42.0)
Dev depdendencies
- Updates
astroacross monorepo (6.1.9 => 6.2.2) - Updates
@astrojs/check(0.9.8 => 0.9.9) - Updates
pnpm(10.33.2 => 10.33.3) - Updates linters
eslint(10.2.1 => 10.3.0)globals(17.5.0 => 17.6.0)typescript-eslint(8.59.0 => 8.59.2)
Haven’t bothered with a changeset as we already have an unreleased “update deps” patch changeset in the pipeline. Should see no user-facing change in any case.
Changes
This PR adds the missing * line from a code sample in JSDoc, which basically removed the whitespace between the imports and the code.
This doesn't need a changeset, AFAIK
Testing
Docs
That's all it is.
Changes
Fixes a regression from #16277 where the generated wrangler.json sets
assets.directory to include the base prefix (e.g. "../client/blog"
instead of "../client").
Cloudflare's asset binding resolves the full request URL path (including
the base) against the directory. With the prefixed path, requests would
resolve to a double-nested location (e.g. client/blog/blog/file.js),
causing 404s for all static assets.
Related: #16276
Testing
Added a test to with-base.test.ts verifying the assets directory in
the generated wrangler.json points to the un-prefixed client root.
Docs
fixes: #16604
Changes
As reported in the issue, the migration guide and deprecation comments recommended using build.assetsPrefix from astro:config/server as a replacement for the deprecated import.meta.env.ASSETS_PREFIX, but astro:config/server was not actually exporting build.assetsPrefix.
astro/packages/astro/client.d.ts
Lines 11 to 15 in 3740b24
| /** | |
| * The prefix for Astro-generated asset links if the build.assetsPrefix config option is set. This can be used to create asset links not handled by Astro. | |
| * @deprecated This will be removed in a future major version of Astro. Use `build.assetsPrefix` from `astro:config/server` instead. | |
| */ | |
| readonly ASSETS_PREFIX: string | Record<string, string>; |
This PR fixes the implementation so that build.assetsPrefix is correctly exported from astro:config/server.
Testing
Added tests to verify that build.assetsPrefix is properly exported and that its value can be read correctly.
Docs
The "Upgrade to Astro v6" page documents this under "Deprecated: import.meta.env.ASSETS_PREFIX" and recommends using build.assetsPrefix instead, but the "Imports from astro:config/server" reference page does not yet list build.assetsPrefix.
A follow-up PR to update the docs will be submitted separately.
Fixes #16491
Context: In Vite 6, top-level vite.optimizeDeps only applies to the client environment. The Cloudflare adapter owns SSR/prerender dep optimization via its configEnvironment hook, so user-configured optimizeDeps.exclude and optimizeDeps.esbuildOptions.loader were silently ignored for server environments.
Changes
- Fix: capture the user's
config.vite.optimizeDepsinastro:config:setupand merge it into the adapter'sconfigEnvironmentreturn value for server environments. Added integration test: (test/custom-loader.test.ts) with a fixture containing a fake package that imports a.datafile, reproduces the esbuild "No loader is configured for .data files" error when the fix is absent.- Extended
test/ssr-deps.test.tswith a dynamically-created fake-data-pkg that imports a .data file, reproduces the esbuild "No loader is configured for .data files" error when the fix is absent.
Testing
- Run
NODE_OPTIONS="--experimental-strip-types --no-warnings"pnpm test in packages/integrations/cloudflare. - If the new test fails locally with a miniflare compatibility date error, that is a pre-existing local env issue (same as
ssr-deps.test.ts) CI is the source of truth
Docs
Fix doesn't introduce any new config options or change any public API. No docs update needed.
Changes
- Moves conflict resolution and changeset cleanup into the
merge-main-to-nextaction so the branch is clean and buildable before CI runs. AI only runs when there are actual conflicts; clean merges skip it entirely. - Strips the
merge-fixaction down to just test fixing — no more conflict stripping, no--no-frozen-lockfile. Uses--frozen-lockfilesince the lockfile is already correct. - Adds critical rules to the
fix-testsskill based on analysis of a 1-hour timeout: never runpnpm install, time-box investigation to 5 min, diff before deep-reading source, batch bash commands.
Testing
- No test changes — these are CI workflow and skill instruction changes.
Docs
- No docs needed — internal CI automation only.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
astro@6.3.0
Minor Changes
-
#16366
d69f858Thanks @matthewp! - Adds a newexperimental.advancedRoutingoption that lets you take full control of Astro's request handling pipeline by creating asrc/app.tsfile in your project.Today, Astro handles every incoming request through a fixed internal pipeline: trailing slash normalization, redirects, actions, middleware, page rendering, i18n, and so on. That pipeline works great for most sites, but as projects grow you often want to run your own logic between those steps — an auth check before rendering, a rate limiter before actions, custom logging around the whole stack. Advanced routing gives you that control.
When enabled, Astro looks for a
src/app.tsfile in your project. If it finds one, that file becomes the entrypoint for all server-rendered requests. You compose the pipeline yourself using the handlers Astro provides, and you can slot your own logic anywhere in the chain.Enabling advanced routing
// astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ experimental: { advancedRouting: true, }, });
Two ways to build your pipeline
Astro ships two entrypoints for advanced routing:
astro/fetchandastro/hono.astro/fetchis a low-level, framework-free API built on the Web Fetch standard. You create aFetchStatefrom the incoming request, then call handler functions in sequence. Each handler takes the state, does its work, and returns aResponse(orundefinedto pass through). This is the core primitive that everything else is built on:// src/app.ts import { FetchState, trailingSlash, redirects, actions, middleware, pages, i18n, } from 'astro/fetch'; export default { async fetch(request: Request) { const state = new FetchState(request); // Early exits — these return a Response only when they apply. const slash = trailingSlash(state); if (slash) return slash; const redirect = redirects(state); if (redirect) return redirect; const action = await actions(state); if (action) return action; // Middleware wraps page rendering; i18n post-processes the response. const response = await middleware(state, () => pages(state)); return i18n(state, response); }, };
astro/honowraps the same handlers as Hono middleware, so you can mix Astro's pipeline with Hono's ecosystem of middleware (logger, CORS, JWT, rate limiting, etc.) using theapp.use()pattern you already know:// src/app.ts import { Hono } from 'hono'; import { getCookie } from 'hono/cookie'; import { logger } from 'hono/logger'; import { actions, middleware, pages, i18n } from 'astro/hono'; const app = new Hono(); app.use(logger()); // Auth gate — only runs for /dashboard routes. app.use('/dashboard/*', async (c, next) => { const session = getCookie(c, 'session'); if (!session) return c.redirect('/login'); return next(); }); app.use(actions()); app.use(middleware()); app.use(pages()); app.use(i18n()); export default app;
Both approaches give you the same power — pick whichever fits your project. If you don't need a framework,
astro/fetchkeeps things minimal. If you want a rich middleware ecosystem,astro/honogets you there with one import.For more information on enabling and using this feature in your project, see the experimental advanced routing docs. To give feedback, or to keep up with its development, see the advanced routing RFC for more information and discussion.
-
#16366
d69f858Thanks @matthewp! - Adds aconsume()instance method toAstroCookies. This method marks the cookies as consumed and returns theSet-Cookieheader values. After consumption, any subsequentset()calls will log a warning, since the headers have already been sent.Previously this was only available as a static method
AstroCookies.consume(cookies). The static method is now deprecated but kept for backward compatibility with existing adapters. -
#16412
ba2d2e3Thanks @0xbejaxer! - Add retry and error event handling forastro-islandhydration import failures to reduce unrecoverable hydration errors on transient network failures. -
#16582
885cd31Thanks @Princesseuh! - Adds a newimage.dangerouslyProcessSVGflag to optionally enable processing SVG inputs. For security reasons, Astro will no longer rasterizes SVG image sources by default in its default image service and endpoint.Set
image.dangerouslyProcessSVG: trueto opt back into processing SVG inputs.// astro.config.mjs import { defineConfig } from 'astro/config'; export default defineConfig({ // ... image: { dangerouslyProcessSVG: true, }, });
Note that this is a breaking change for users who were previously relying on Astro's default image service to rasterize SVG inputs, but it is a necessary change to improve security and prevent potential vulnerabilities.
-
#16519
1b1c218Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in
image.remotePatternsor a domain inimage.domains, Astro will fail with a helpful error message.In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:
export default defineConfig({ image: { domains: ['example.com', 'cdn.example.com'], }, });
{ /* Redirects to https://cdn.example.com/assets/image.png: */ } <Image src="https://example.com/assets/image.png" width="1920" height="1080" alt="An example image." />; { /* Redirects to https://malicious.com/image.png: */ } <Image src="https://example.com/bad-image.png" width="1920" height="1080" alt="An example image." />;
In cases where all redirects to HTTPS hosts should be trusted, the following configuration for
image.remotePatternscan be used:export default defineConfig({ image: { remotePatterns: [ { protocol: 'https', }, ], }, });
Patch Changes
-
#16592
9c6efc5Thanks @matthewp! - Escapes interpolated values in the dev server redirect HTML template, consistent with how the 404 template already handles them -
#16585
78f305eThanks @web-dev0521! - Fixesz.array(z.boolean())in form actions incorrectly coercing the string"false"totrue. Boolean array elements now use the same'true'/'false'string comparison as singlez.boolean()fields, so submitting["false", "true", "false"]correctly parses as[false, true, false]. -
#16567
12a03f2Thanks @matthewp! - Fixes deleted content collection entries persisting ingetCollection()results during dev -
#16595
ce9b25cThanks @web-dev0521! - FixespushDirectivein the CSP runtime duplicating the new directive once per existing non-matching directive. CallinginsertDirective()(or otherwise pushing a directive whose name is not yet in the list) now appends it exactly once, and a directive that merges with a later existing entry no longer leaves an unmerged copy behind. -
#16600
94e4b7cThanks @web-dev0521! - FixesAstro.preferredLocalereturning the wrong value wheni18n.localesmixes object-form entries ({ path, codes }) with string entries that normalize to the same locale. The first matching code in the configuredlocalesorder is now selected, matching the documented behavior. -
#16591
cce20f7Thanks @matthewp! - Uses a consistent generic error message in the image endpoint across all adapters -
#16629
f54be80Thanks @g-taki! - Fixes a bug where SSR responses inastro devcould crash withTypeError: this.logger.flush is not a function. -
#16589
3740b24Thanks @ArmandPhilippot! - Fixes an outdated code snippet in the documentation for session storage configuration. -
Updated dependencies [
354e231]:- @astrojs/telemetry@3.3.2
@astrojs/cloudflare@13.4.0
Minor Changes
-
#16519
1b1c218Thanks @louisescher! - Adds support for redirecting URLs in remote image optimization.Previously, when a remote image URL meant to be optimized by Astro led to a redirect, Astro would fail silently and ignore the redirect. Now, Astro tracks up to 10 redirects for these images. If any of the redirects are not covered by a pattern in
image.remotePatternsor a domain inimage.domains, Astro will fail with a helpful error message.In the following example, the first image would be loaded successfully, while the second would lead to Astro throwing an error:
export default defineConfig({ image: { domains: ['example.com', 'cdn.example.com'], }, });
{ /* Redirects to https://cdn.example.com/assets/image.png: */ } <Image src="https://example.com/assets/image.png" width="1920" height="1080" alt="An example image." />; { /* Redirects to https://malicious.com/image.png: */ } <Image src="https://example.com/bad-image.png" width="1920" height="1080" alt="An example image." />;
In cases where all redirects to HTTPS hosts should be trusted, the following configuration for
image.remotePatternscan be used:export default defineConfig({ image: { remotePatterns: [ { protocol: 'https', }, ], }, });
Patch Changes
- Updated dependencies []:
- @astrojs/underscore-redirects@1.0.3
@astrojs/language-server@2.16.8
Patch Changes
- #16627
5778cb7Thanks @Princesseuh! - Fixes unintended dependency on thetypescriptpackage being available to the language server
@astrojs/telemetry@3.3.2
Patch Changes
- #16260
354e231Thanks @gameroman! - Refactors internal config logic to remove thedlvdependency in favor of native logic
Changes
- Fix
pushDirectivein the CSP runtime duplicating the new directive once per existing non-matching directive. - Root cause: the
elsebranch inside the loop pushednewDirectiveon every non-match instead of once after the loop. Replaced thededuplicatedflag with amatchedflag and moved thenewDirectivepush outside the loop, guarded by!matched. - Closes #16594.
Before: pushDirective(["img-src 'self'", "style-src 'self'"], "script-src 'self'") → ["img-src 'self'", "script-src 'self'", "style-src 'self'", "script-src 'self'"] (duplicated + interleaved)
After: → ["img-src 'self'", "style-src 'self'", "script-src 'self'"]
Also fixes Mode 2: a directive that merges with a later existing entry no longer leaves an unmerged copy in front of the merged result.
Don't forget a changeset! Run
pnpm changeset. — Added:.changeset/fix-csp-push-directive-duplicate.md
Testing
- Added a
regression: issue #16594suite inpackages/astro/test/units/csp/runtime.test.tscovering:- Mode 1 — non-matching new directive appended exactly once.
- Mode 2 — merge with a later match, no unmerged copy left behind.
- Empty input list edge case.
- Many non-matches: directive-name uniqueness asserted via a
Mapcount. - Table-driven sweep across "non-match", "match in middle", "match at end" shapes, asserting both length and
Set-based name uniqueness.
- All 11 tests in
csp/runtime.test.tspass locally (node --test test/units/csp/runtime.test.ts).
Docs
No docs change required — pushDirective is internal CSP runtime; public insertDirective() semantics are unchanged (this restores them to the documented behavior).
Changes
This PR does an overhaul of all error messages to make them more consistent with each other, namely:
- fixing missing full stop at the end of messages.
- adding missing backticks to code excerpts.
- adding () to differentiate functions from simple properties/objects.
- improving error titles/messages with more context.
Testing
I am not sure if adding backticks in a few places could break rendering, we'll be sure once we get the automated PR on docs.
Docs
That's all it is.
Changes
- The redirect template in
3xx.tsinterpolated URL values directly into HTML without escaping. The sibling4xx.tstemplate already escapes its values usinghtml-escaper. This applies the same pattern to the redirect template for consistency.
Testing
- No new tests. The change adds
escape()calls around existing interpolations — the same approach4xx.tsalready uses.
Docs
- No docs update needed.
Changes
- The Node image endpoint (
node.ts) returns a generic'Internal Server Error'string on failure, but the generic adapter endpoint (generic.ts) was interpolating the error object into the response. This alignsgeneric.tswithnode.tsso all adapters use the same consistent error response.
Testing
- No new tests. Single-line string replacement in an error path — existing image endpoint tests cover the surrounding behavior.
Docs
- No docs update needed.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| withastro/automation | action | digest | e27ec6d → c8a2e8b |
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 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.
Description
- Closes #3871
Especially in Chinese and Japanese, the CSS property text-autospace: normal is preferred for better looking like books by automatically inserting spaces with the moderate width.
- https://developer.chrome.com/blog/css-i18n-features#inter-script_spacing_text-autospace
- https://developer.mozilla.org/docs/Web/CSS/text-autospace
- https://webkit.org/blog/16574/webkit-features-in-safari-18-4/#:~:text=Text%20auto%20space
- vuejs/vitepress#4996
- web-infra-dev/rspress#2920
- https://github.com/web-infra-dev/rspress/releases/tag/v2.0.0-rc.3
Without this, some users and AIs try to insert such spaces manually as workarounds but they are too wide and prevent keywords from searched.
Japanese:
Before:
After:
Note: the current Chinese content of Starlight translation is not affected because it took the workarounds like manually inserting spaces.
The other languages are not affected.
Changes
- During
astro build,createTempViteServer(inpackages/astro/src/core/sync/index.ts) creates a Vite dev server purely to resolve virtual modules for type generation. The server never handles HTTP requests, but every plugin'sconfigureServerhook still fires — including adapter plugins that start heavy runtimes (notably@astrojs/cloudflare→@cloudflare/vite-plugin→ miniflare/workerd, ~1–3.5 s of overhead). - Additionally, top-level
optimizeDeps: { noDiscovery: true }only applies to theclientenvironment under Vite 7's Environment API, so adapters'configEnvironmenthooks re-injected theiroptimizeDeps.includelists intossr/astro/prerenderand forced unnecessary pre-bundling. - Two changes in
createTempViteServer:- Per-environment
optimizeDeps: { noDiscovery: true, include: [] }for all four environments. - Inline
astro:sync:strip-server-hooksplugin that removesconfigureServerfrom non-astro:plugins inconfigResolved.
- Per-environment
- Net effect on a representative project:
Types Generateddrops from ~3.6 s to ~125 ms; total build from ~13 s to ~9.2 s. - Closes #16332.
- Changeset added (
patchforastro).
This is the fix astrobot-houston suggested on the issue, applied verbatim.
Testing
pnpm exec astro-scripts test test/astro-sync.test.ts --strip-types— all 9 existing sync tests pass.- Verified end-to-end against the MRE from #16332 (https://github.com/adamchal/astro-sync-perf): with
@astrojs/cloudflareon the adapter,Types Generateddrops from ~2.3 s to ~100 ms and miniflare no longer starts during sync. - I have been running this same patch (applied as a
postinstallmonkey-patch) on a production Astro/Cloudflare site for the past few weeks with no regressions in dev or build.
Docs
No user-facing API changes — this is a build-time perf fix. Adapter authors who relied on configureServer firing during sync would notice, but that path was unintentional and has no documented contract.
Changes
- Fix
z.array(z.boolean())in form actions incorrectly coercing the string"false"totrue. handleFormDataGetAllwas usingentries.map(Boolean), butFormDatavalues are always strings andBoolean("false") === true. Replaced with the same'true'/'false'comparison already used for singlez.boolean()fields inhandleFormDataGet.- Closes #16584.
Before: ["false", "true", "false"] → [true, true, true]
After: ["false", "true", "false"] → [false, true, false]
Testing
- Added a new
boolean arrayssuite inpackages/astro/test/units/actions/form-data-to-object.test.ts:should preserve "false" string values in boolean arrays— direct regression test for the reported case.should coerce mixed boolean array values correctly— table-driven coverage across all-true / all-false / mixed inputs.
- All 25 tests in
formDataToObjectpass locally (node --test test/units/actions/form-data-to-object.test.ts).
Docs
No docs change required — this is a behavior fix bringing array-element parsing in line with the already-documented single-boolean coercion behavior.
fixes #13297
Changes
In the current implementation, enabling prefetch settings causes links to be collected via document.getElementsByTagName('a') on the initial page load.
While this approach works for most cases, it fails to cover certain scenarios, such as the one reported in issue #13297.
The root cause of issue #13297 is that when a component with the server:defer attribute uses top-level await, its links are rendered after the Promise resolves, meaning they are not captured by the initial document.getElementsByTagName('a') call.
More broadly, the same issue occurs whenever links are dynamically added to the DOM after the initial page load.
To address this, this pull request adds the observeDynamicLinks option, which uses a MutationObserver to watch for dynamically added link elements.
This ensures that links added after the initial render are properly captured.
This feature is opt-in.
Why MutationObserver?
Investigation process
- First, I checked the code around
server:deferto see if it used any event notifications. - It did not, but I found that the prefetch code [uses the
astro:page-loadevent].
astro/packages/astro/src/prefetch/index.ts
Line 316 in 7711e47
document.addEventListener('astro:page-load', () => { - When I looked into whether
astro:page-loadcould be used here, I found that the constant defining this event name is marked as@deprecated, so I decided to look for a different approach.
astro/packages/astro/src/transitions/events.ts
Lines 12 to 13 in 7711e47
/** @deprecated This will be removed in Astro 7 */ export const TRANSITION_PAGE_LOAD = 'astro:page-load'; - I also considered that the root cause of issue #13297 is not specific to
server:defer, the same problem can occur in other situations where links are added to the DOM dynamically. For this reason, I wanted an approach that resolves the issue within the prefetch code itself, rather than adding prefetch-dependent logic to theserver:deferside. - As a result, I concluded that
MutationObserveris the best approach, as it is well-suited for observing DOM changes and allows the fix to be fully self-contained within the prefetch implementation.
Testing
Added tests to verify that the observeDynamicLinks option works correctly in the following scenarios:
- Links dynamically added via
server:defer, as reported in issue #13297 - Links dynamically added by a toggle component (
ToggleButton.jsx)
Docs
/cc @withastro/maintainers-docs for feedback!
Changes
SVGs are always troublesome in the end and wanting to actually process them is so uncommon that it's best to leave it to the user.
I decided to put it on a global image option so that other services could also use it, but I'd be okay with it being a service config as well, it's ok
Testing
Added a test
Docs
WIP
This PR contains the following updates:
| Package | Change | Age | Confidence |
|---|---|---|---|
| @markdoc/markdoc (source) | ^0.5.4 → ^0.5.7 |
||
| devalue | ^5.6.3 → ^5.8.0 |
||
| preact (source) | ^10.28.4 → ^10.29.1 |
Release Notes
markdoc/markdoc (@markdoc/markdoc)
v0.5.7
What's Changed
- Make table-syntax validation error location more precise by @yue-stripe in #605
- the
validate()method now returns more location/line info than before - Build output now includes ES2022 syntax. The esbuild version used to build markdoc was upgraded from 0.13 to 0.25, which raises the effective output target from ~ES2020 to ES2022+. If your build pipeline transpiles @markdoc/markdoc, you may need to ensure your toolchain supports ES2022 features such as static class blocks.
- the
Full Changelog: markdoc/markdoc@0.5.6...0.5.7
v0.5.6
What's Changed
- Add validation for invalid Markdoc table syntax by @yue-stripe in #603
Markdoc.validatewill now raise errors for invalid syntax within{% table %}tags.
Full Changelog: markdoc/markdoc@0.5.5...0.5.6
v0.5.5
Added support for providing a list of conditional tags in addition to if. (['if'] is set by default). No breaking changes.
sveltejs/devalue (devalue)
v5.8.0
Minor Changes
c5115b0: feat: addstringifyAsyncfor async serialization
v5.7.1
Patch Changes
8becc7c: fix: handle regexes consistently in uneval's value and reference formats
v5.7.0
Minor Changes
df2e284: feat: use native alternatives to encode/decode base64498656e: feat: addDataViewsupporta210130: feat: whitelistFloat16Arraydf2e284: feat: simplify TypedArray slices
Patch Changes
preactjs/preact (preact)
v10.29.1
Fixes
- Create a unique event-clock for each Preact instance on a page. (#5068, thanks @JoviDeCroock)
- Fix incorrect DOM order with conditional ContextProvider and inner keys (#5067, thanks @JoviDeCroock)
Maintenance
- fix: Remove postinstall script for playwright setup (#5063, thanks @rschristian)
- chore: speed up tests by using playwright instead of webdriverio (#5060, thanks @marvinhagemeister)
- chore: migrate remaining .js -> .jsx files (#5059, thanks @marvinhagemeister)
- chore: rename
.test.js->.test.jsxwhen JSX is used (#5058, thanks @marvinhagemeister) - Migrate from biome to oxfmt (#5033, thanks @JoviDeCroock)
Configuration
📅 Schedule: (UTC)
- Branch creation
- Between 12:00 AM and 03:59 AM, only on Monday (
* 0-3 * * 1)
- Between 12:00 AM and 03:59 AM, only on Monday (
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
Minor adjustment to @astrojs/mdx so smartypants object options are passed to remark-smartypants, matching Astro's markdown behavior since v6.1.
Testing
Added a regression test that verifies smartypants object options (e.g. dashes: "oldschool") are applied in MDX. The test fails without this change (e.g., for dashes: "oldschool" an em-dash is expected while the default configuration produces an en-dash) and passes with it.
Changes
issue : #16273
Fixed issue where custome elements in MDX bypass the renderer pipline. By detecting hyphens in tag names within the JSX runtime, custom elements are now correctly routed to the renderer for SSR, ensuring parity with .astro files.
Testing
Before fixing bug
Test result after implement all test.
After fixing bug
Docs
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| pnpm/action-setup | action | patch | v6.0.4 → v6.0.5 |
Release Notes
pnpm/action-setup (pnpm/action-setup)
v6.0.5
What's Changed
Full Changelog: pnpm/action-setup@v6.0.4...v6.0.5
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
Fixes prerendered pages returning 404 when build.format: 'file' is set in the Node standalone adapter.
When build.format is 'file', pages are emitted as flat .html files (about.html) instead of about/index.html. The send library's extensions option tells it to try appending .html before giving up, so a request to /about resolves correctly on disk instead of falling through to the SSR handler as a 404.
One-line change in serve-static.ts — app.manifest.buildFormat is already available in scope (same as app.manifest.trailingSlash used just above).
Testing
Added two cases to the existing prerender.test.ts under a new "build.format: 'file'" describe block:
- prerendered page is served correctly via its clean URL
- SSR route still works alongside it
Docs
No user-facing behavior change beyond bug fix — no docs update needed.
Fixes #16570.
Changes
Fixes the Vite build warning reported in #15957:
[WARN] Plugin vite:reporter: Unused imports from "@astrojs/internal-helpers/remote":
– "matchHostname", "matchPathname", "matchPort", "matchProtocol"
plugin-internals.ts sets noExternal: ['astro'] for the prerender environment, which bundles astro but leaves @astrojs/internal-helpers as an external. Rollup then sees that those four helpers are imported by the bundled astro code but not consumed inside the bundle boundary, which triggers the unused-import warning.
Adding @astrojs/internal-helpers to noExternal alongside astro co-bundles it in the prerender environment, eliminating the warning. This is a first-party workspace package and the same pattern is already used elsewhere.
Testing
The warning is emitted by Vite/Rollup during the build phase and is not directly observable in the integration test suite without capturing stderr. Verified manually by running a project build with output: 'hybrid' before and after the change — the warning disappears with the fix applied.
Docs
No user-facing behavior change — no docs update needed.
Fixes #15957.
Changes
Automatically injects Cache-Control: public, max-age=31536000, immutable into Cloudflare's _headers file for hashed Astro assets (/_astro/*), skipping when build.assetsPrefix is set or when the user already has a matching Cache-Control rule.
Testing
Unit tests.
Docs
/cc @withastro/maintainers-docs for feedback!
Changes
- Invalidates the
astro:data-layer-contentvirtual module in the SSR module runner'sevaluatedModulescache when the data store changes, not just in the server-side module graph. The existinginvalidateModulecall only clears the server'stransformResult, but a subsequenttransformRequest(triggered during module resolution) re-populates it before the runner'sfetchModulecheck, causing a false cache hit that returns stale data.
Closes #16561
Testing
- No new tests added. Manually verified with a content collection setup: deleting entries correctly updates
getCollection()results on the next page load in dev.
Docs
- No docs update needed; this is a bug fix with no API change.
Changes
- Fixes #16564 —
data-astro-prefetch="tap"silently failing when the user clicks a nested child element (e.g.<span>,<img>,<svg>, Astro<Image />) inside an anchor e.targetontouchstart/mousedownis the deepest element under the pointer, not necessarily the<a>— useclosest('a')to walk up to the anchor before checking the strategy- Particularly impactful on slow connections / data-saver mode, where all strategies collapse to
tap— meaning zero prefetching was happening for any link with nested content for the users who would benefit most
Testing
- Added e2e fixture link
<a data-astro-prefetch="tap"><span>tap nested</span></a>and a corresponding page - Added e2e test in
prefetch.test.tsthat clicks the inner<span>and asserts the prefetch request fires
Docs
No docs change needed — this is a bug fix restoring already-documented behavior, not a new feature or API change.
Closes #16563
Summary
Calling session.delete(key) as the first mutation in a request (no prior get, set, has, keys, etc.) did not write back to session storage. The session stayed dirty in memory, but [PERSIST_SYMBOL]() skipped the save path because #data was still undefined, so the backing store kept the old value and the next request could still read the “deleted” key.
Root cause
set()initializes the in-memory map withthis.#data ??= new Map();delete()only didthis.#data?.delete(key), so#datacould remainundefined.- Persistence gated saves on
if (this.#dirty && this.#data), so delete-only flows never calledsetItemand#toDeletewas never applied to storage.
Fix
- Initialize the map in
delete()the same way as inset():this.#data ??= new Map()before removing the key, so the persist path runs and#ensureData()can merge, apply deletions, and serialize to the driver.
Testing
- Added a unit test that pre-seeds backing storage, calls only
delete()then persist, and asserts a newAstroSessionwith the same storage/session id no longer returns the deleted key. - Adjusted an existing persistence test so the follow-up session’s storage
getreturns parsed JSON (matching real unstorage behavior) aftersetItemwrites a devalue JSON string.
Docs
No documentation change required; behavior now matches what the session API already promises.
Contributor checklist (see CONTRIBUTING.md)
-
pnpm exec changesetfor theastropackage (user-facing bugfix) - Tests:
pnpm -C packages/astro exec astro-scripts test "test/units/sessions/astro-session.test.ts"
Fixes #16553
Changes
- Fixes non-prerendered routes (e.g. SSR endpoints using
cloudflare:workers) failing when a dynamic prerendered route like[page].astroexists in the same project withprerenderEnvironment: 'node'. - The dev prerender middleware was using
matchAllRoutes(), which returns every route matching a pathname. A request to/ssrwould match bothssr.astro(non-prerendered) and[page].astro(prerendered), and since one match was prerendered, the request was incorrectly routed to the Node handler. Replaced withmatchRoute(), which returns only the highest-priority match.
Testing
- Added a dynamic prerendered route (
[page].astrowithgetStaticPaths) to the existingprerender-node-envfixture. The existing "renders SSR page through workerd" test now exercises this scenario.
Docs
- No docs needed.
This PR contains the following updates:
| Package | Type | Update | Change |
|---|---|---|---|
| withastro/automation | action | digest | 497c926 → e27ec6d |
Configuration
📅 Schedule: (UTC)
- Branch creation
- At any time (no schedule defined)
- Automerge
- At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- If you want to rebase/retry this PR, check this box
This PR was generated by Mend Renovate. View the repository job log.
Changes
- Updates
./test-utils.jsimports to./test-utils.tsin three.test.jsfiles that were missed when #16492 renamedtest-utils.js→test-utils.ts.
Testing
- No new tests. This fixes the existing
test:integration:jssuite which fails immediately withERR_MODULE_NOT_FOUND.
Docs
- No docs needed — test-only change.
Changes
Simplify db's tsconfig by removing tsconfig.virtual.json.
Its only purpose is to prevent incorrect auto-imports from editor hints and doesn’t affect the runtime output, so it should be relatively safe to remove it.
This file makes it tricky for the TypeScript project references refactor.
Testing
Green CI
Docs
N/A
Changes
- Double-encoded URL paths like
/api/%2561dmin/userscould bypass middleware auth checks because the normalization fallback leftctx.url.pathnamehalf-decoded. - Adds
MultiLevelEncodingErroras a distinct error type so callers can distinguish it from generic decode failures. #createNormalizedUrlnow re-throwsMultiLevelEncodingErrorinstead of falling back. Other decode errors (truly malformed URLs) still fall back gracefully.BaseApp.render()catchesMultiLevelEncodingErrorand returns400 Bad Request.
Testing
- Added
test/units/app/double-encoding-bypass.test.ts— 7 tests using a catch-all/api/[...path]endpoint behind auth middleware. Covers the direct path (401), single-encoded (401), double-encoded (400), multiple encoded segments (400), public routes (200), and non-protected API routes (200).
Docs
- No docs update needed — no user-facing API changes.
Changes
- Fixes a bug in
@astrojs/cloudflarewhere custom KV namespace bindings (e.g.MY_KV,CACHE) were silently removed when Astro's session functionality was enabled. - The root cause was in
packages/integrations/cloudflare/src/wrangler.ts: when injecting theSESSIONKV binding, the code returned a fresh single-item array instead of merging with the user's existingkv_namespaces. - Extracted a
withSessionKVBindinghelper that copies existing namespaces and appends the SESSION binding, preserving all user-defined bindings. - Fix applies to both the top-level config and the
previewsconfig (same code path). - Updated the existing test that was asserting the broken behavior, and added a new test covering the
previewscase.
Closes #16554
Testing
- Updated
packages/integrations/cloudflare/test/wrangler.test.ts: fixed the assertion in "adds SESSION binding when other KV bindings exist but not the session one" to verifyOTHER_KVis preserved alongsideSESSION. - Added a new test "preserves existing previews KV bindings when adding SESSION binding" covering the same scenario in the
previewsconfig. - No integration test needed: the bug and fix are fully exercised by the unit test suite for
cloudflareConfigCustomizer.
Docs
No docs change needed. This is a bug fix restoring behavior that users already rely on — their existing kv_namespaces entries in wrangler.toml now survive when sessions are enabled.
Changes
- Fixes a regex bug where
returnwas incorrectly rewritten inside string literals, template literals, and comments during esbuild's dep-scan / frontmatter error-check phase - Replaces the two-pass
\breturn\bregex with a single-pass state-machine regex that skips over all string/comment tokens before rewriting barereturnstatements - Fixes the primary report case:
gen.return(value)→ was producinggen.throw (value)(syntax error); now preserved correctly - Extracts a shared
replaceTopLevelReturns()helper intoutils.tsto deduplicate logic between the cloudflare esbuild plugin andcompile.ts
Affected files:
packages/integrations/cloudflare/src/esbuild-plugin-astro-frontmatter.tspackages/astro/src/vite-plugin-astro/utils.tspackages/astro/src/vite-plugin-astro/compile.ts
Closes #16551
Testing
The fix is a pure regex change with no new dependencies. Verified manually against the cases from the issue:
| Input | Before | After |
|---|---|---|
gen.return(val) |
gen.throw (val) ← syntax error |
unchanged ✓ |
"return value" |
"throw value" |
unchanged ✓ |
// return foo |
// throw foo |
unchanged ✓ |
`return ${x}` |
`throw ${x}` |
unchanged ✓ |
return foo |
return foo |
throw foo ✓ |
return; |
return; |
throw 0; ✓ |
No new tests added — the two affected code paths are internal-only (esbuild dep scan and compile-error enhancement), exercised by the existing integration test suite.
Docs
No user-facing behavior change — this only affects internal dep scanning and error reporting during compilation. No docs update needed.
Changes
fixes #16524.
with vite.css.transformer: 'lightningcss', scoped styles using a nested & selector inside :where(...) silently produce css where the scope attribute lands on the matched child instead of the intended parent. tailwind v4's space-x-*, space-y-*, and divide-* all expand to this shape and tailwind v4 ships with lightningcss in the loop, so any astro 6 + tailwind v4 project using these utilities in scoped <style> blocks targeting children from another component gets broken spacing in production while the css rule still looks present in the bundle.
what's happening: lightningcss runs inside vite's preprocessCSS() and lowers nesting before @astrojs/compiler injects scope attributes. by the time the compiler sees the css, .parent is buried inside :where(...) and isn't a top-level compound anymore, so the injector prepends [data-astro-cid-X] as a new leading compound — which constrains the wrong element.
fix in packages/astro/src/core/compile/style.ts: when the user has css.transformer: 'lightningcss', call preprocessCSS() with a shallow-cloned vite config whose css.lightningcss.exclude ORs in Features.Nesting, so lightningcss skips its nesting-lowering pass for this preprocess call. vite's final pipeline still lowers nesting for the bundle so output stays compatible with the user's targets. clone is non-mutating per call so it's safe under parallel .astro compiles. lightningcss is resolved from the user's project root via createRequire — same instance vite uses, no new dep added to packages/astro. falls back to the original config if lightningcss can't be resolved.
i think a better fix would live in @astrojs/compiler itself, teach the scope injector to recognize :where(<simple-compound>, ...) as a leading-compound wrapper and attach the cid to the inner compound instead of prepending a new one. similar in spirit to withastro/compiler#1153 but extended to cover the :where(...) case, and it'd cover any future css transform that produces a similarly shaped selector not just lightningcss.
Testing
added a regression fixture under packages/astro/test/:
lightningcss-scoped-nesting.test.ts— compiles the reporter's exact shape withvite.css.transformer: 'lightningcss'and asserts the scope id binds to.parent, not as a leading compound on the matched child.- fixture under
packages/astro/test/fixtures/lightningcss-scoped-nesting/— single page using the reporter's:where(& > :not(:last-child))selector inside a scoped<style>block.
reporter's reproduction repo: https://github.com/rklos/astro-css-bug-repro
Docs
no docs changes. silent regression in css output; behavior after the fix matches what the docs already describe for scoped styles + nested selectors.
Changes
upgrade/package.json has "build": "astro-scripts build \"src/index.ts\" --bundle && tsc". The output dist/index.js should be emitted by astro-scripts, while tsc should only emit .d.ts files.
In #16493, tsc emits both .js and .d.ts files, which overrides the bundled dist/index.js file that was just emitted by astro-scripts.
This PR updates upgrade/tsconfig.json to ensure that tsc won't emit any .js files.
Testing
Before this PR:
$ pnpm -w build
$ tree ./packages/upgrade/dist/
./packages/upgrade/dist/
├── actions
│ ├── context.d.ts
│ ├── context.js
│ ├── help.d.ts
│ ├── help.js
│ ├── install.d.ts
│ ├── install.js
│ ├── verify.d.ts
│ └── verify.js
├── index.d.ts
├── index.js
├── messages.d.ts
├── messages.js
├── shell.d.ts
└── shell.js
2 directories, 14 filesAfter this PR:
$ pnpm -w build
$ tree ./packages/upgrade/dist/
./packages/upgrade/dist/
├── actions
│ ├── context.d.ts
│ ├── help.d.ts
│ ├── install.d.ts
│ └── verify.d.ts
├── index.d.ts
├── index.js
├── messages.d.ts
└── shell.d.ts
2 directories, 8 filesAfter this PR, only one bundled dist/index.js file is emitted in dist/.
Docs
The previous and afterward outputs can both work, so there is no behavior change. No docs are needed.
Last fetched: | Scheduled refresh: Every Saturday
See Customizing GitHub Activity Pages to configure your own
Inspired by prs.atinux.com