diff --git a/.changeset/empty-spoons-kiss.md b/.changeset/empty-spoons-kiss.md
deleted file mode 100644
index 8bb114ef6ff9e..0000000000000
--- a/.changeset/empty-spoons-kiss.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@astrojs/db': patch
----
-
-Fixes mixed environment variable for app token when using DB commands with libSQL remote.
diff --git a/.changeset/forty-spies-train.md b/.changeset/forty-spies-train.md
deleted file mode 100644
index 5df78b648fb33..0000000000000
--- a/.changeset/forty-spies-train.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'astro': patch
----
-
-Fixes case where content layer did not update during clean dev builds on Linux and Windows
diff --git a/.changeset/mighty-stingrays-press.md b/.changeset/mighty-stingrays-press.md
new file mode 100644
index 0000000000000..12c353dcd928b
--- /dev/null
+++ b/.changeset/mighty-stingrays-press.md
@@ -0,0 +1,63 @@
+---
+'astro': patch
+---
+
+Adds support for Zod discriminated unions on Action form inputs. This allows forms with different inputs to be submitted to the same action, using a given input to decide which object should be used for validation.
+
+This example accepts either a `create` or `update` form submission, and uses the `type` field to determine which object to validate against.
+
+```ts
+import { defineAction } from 'astro:actions';
+import { z } from 'astro:schema';
+
+export const server = {
+ changeUser: defineAction({
+ accept: 'form',
+ input: z.discriminatedUnion('type', [
+ z.object({
+ type: z.literal('create'),
+ name: z.string(),
+ email: z.string().email(),
+ }),
+ z.object({
+ type: z.literal('update'),
+ id: z.number(),
+ name: z.string(),
+ email: z.string().email(),
+ }),
+ ]),
+ async handler(input) {
+ if (input.type === 'create') {
+ // input is { type: 'create', name: string, email: string }
+ } else {
+ // input is { type: 'update', id: number, name: string, email: string }
+ }
+ },
+ }),
+}
+```
+
+The corresponding `create` and `update` forms may look like this:
+
+```astro
+---
+import { actions } from 'astro:actions';
+---
+
+
+
+
+
+
+```
diff --git a/examples/framework-multiple/package.json b/examples/framework-multiple/package.json
index e408e6679633e..568c1f72ceeb5 100644
--- a/examples/framework-multiple/package.json
+++ b/examples/framework-multiple/package.json
@@ -11,7 +11,7 @@
"astro": "astro"
},
"dependencies": {
- "@astrojs/preact": "^3.5.2",
+ "@astrojs/preact": "^3.5.3",
"@astrojs/react": "^3.6.2",
"@astrojs/solid-js": "^4.4.1",
"@astrojs/svelte": "^6.0.0-alpha.0",
@@ -24,6 +24,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/examples/framework-preact/package.json b/examples/framework-preact/package.json
index 68acefdabf522..fe4c2e27c9576 100644
--- a/examples/framework-preact/package.json
+++ b/examples/framework-preact/package.json
@@ -11,7 +11,7 @@
"astro": "astro"
},
"dependencies": {
- "@astrojs/preact": "^3.5.2",
+ "@astrojs/preact": "^3.5.3",
"@preact/signals": "^1.3.0",
"astro": "^5.0.0-alpha.6",
"preact": "^10.23.2"
diff --git a/examples/framework-vue/package.json b/examples/framework-vue/package.json
index 7b797e7a32fcc..961a6eac1f160 100644
--- a/examples/framework-vue/package.json
+++ b/examples/framework-vue/package.json
@@ -13,6 +13,6 @@
"dependencies": {
"@astrojs/vue": "^5.0.0-alpha.0",
"astro": "^5.0.0-alpha.6",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/examples/server-islands/package.json b/examples/server-islands/package.json
index 721b0ec2ce8be..8447bb4f3b8a2 100644
--- a/examples/server-islands/package.json
+++ b/examples/server-islands/package.json
@@ -14,11 +14,11 @@
"@astrojs/react": "^3.6.2",
"@astrojs/tailwind": "^6.0.0-alpha.0",
"@fortawesome/fontawesome-free": "^6.6.0",
- "@tailwindcss/forms": "^0.5.8",
+ "@tailwindcss/forms": "^0.5.9",
"@types/react": "^18.3.5",
"@types/react-dom": "^18.3.0",
"astro": "^5.0.0-alpha.6",
- "postcss": "^8.4.43",
+ "postcss": "^8.4.45",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwindcss": "^3.4.10"
diff --git a/examples/starlog/package.json b/examples/starlog/package.json
index f4f8605e230ab..03eae731fbcf2 100644
--- a/examples/starlog/package.json
+++ b/examples/starlog/package.json
@@ -11,7 +11,7 @@
},
"dependencies": {
"astro": "^5.0.0-alpha.6",
- "sass": "^1.77.8",
+ "sass": "^1.78.0",
"sharp": "^0.33.3"
}
}
diff --git a/examples/with-mdx/package.json b/examples/with-mdx/package.json
index c1e3586c92a3b..1c71b47ee6d7d 100644
--- a/examples/with-mdx/package.json
+++ b/examples/with-mdx/package.json
@@ -12,7 +12,7 @@
},
"dependencies": {
"@astrojs/mdx": "^4.0.0-alpha.2",
- "@astrojs/preact": "^3.5.2",
+ "@astrojs/preact": "^3.5.3",
"astro": "^5.0.0-alpha.6",
"preact": "^10.23.2"
}
diff --git a/examples/with-nanostores/package.json b/examples/with-nanostores/package.json
index 13c013b0fda7a..060c952004859 100644
--- a/examples/with-nanostores/package.json
+++ b/examples/with-nanostores/package.json
@@ -11,7 +11,7 @@
"astro": "astro"
},
"dependencies": {
- "@astrojs/preact": "^3.5.2",
+ "@astrojs/preact": "^3.5.3",
"@nanostores/preact": "^0.5.2",
"astro": "^5.0.0-alpha.6",
"nanostores": "^0.11.3",
diff --git a/examples/with-tailwindcss/package.json b/examples/with-tailwindcss/package.json
index dc54c6df28fe1..0e295fa7c5116 100644
--- a/examples/with-tailwindcss/package.json
+++ b/examples/with-tailwindcss/package.json
@@ -17,7 +17,7 @@
"astro": "^5.0.0-alpha.6",
"autoprefixer": "^10.4.20",
"canvas-confetti": "^1.9.3",
- "postcss": "^8.4.43",
+ "postcss": "^8.4.45",
"tailwindcss": "^3.4.10"
}
}
diff --git a/package.json b/package.json
index b863ea1f99045..456956981ad71 100644
--- a/package.json
+++ b/package.json
@@ -55,10 +55,10 @@
"@astrojs/check": "^0.9.3",
"@biomejs/biome": "1.8.3",
"@changesets/changelog-github": "^0.5.0",
- "@changesets/cli": "^2.27.7",
+ "@changesets/cli": "^2.27.8",
"@types/node": "^18.17.8",
"esbuild": "^0.21.5",
- "eslint": "^9.9.1",
+ "eslint": "^9.10.0",
"eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-regexp": "^2.6.0",
"globby": "^14.0.2",
@@ -67,7 +67,7 @@
"prettier-plugin-astro": "^0.14.1",
"turbo": "^2.1.1",
"typescript": "~5.5.4",
- "typescript-eslint": "^8.3.0"
+ "typescript-eslint": "^8.4.0"
},
"pnpm": {
"peerDependencyRules": {
diff --git a/packages/astro-rss/package.json b/packages/astro-rss/package.json
index ba07de25bc250..d65b71c6dc330 100644
--- a/packages/astro-rss/package.json
+++ b/packages/astro-rss/package.json
@@ -33,7 +33,7 @@
"xml2js": "0.6.2"
},
"dependencies": {
- "fast-xml-parser": "^4.4.1",
+ "fast-xml-parser": "^4.5.0",
"kleur": "^4.1.5"
}
}
diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md
index c54dcc01e38cd..ff43c9ac5491b 100644
--- a/packages/astro/CHANGELOG.md
+++ b/packages/astro/CHANGELOG.md
@@ -69,6 +69,42 @@
});
```
+## 4.15.4
+
+### Patch Changes
+
+- [#11879](https://github.com/withastro/astro/pull/11879) [`bd1d4aa`](https://github.com/withastro/astro/commit/bd1d4aaf8262187b4f132d7fe0365902131ddf1a) Thanks [@matthewp](https://github.com/matthewp)! - Allow passing a cryptography key via ASTRO_KEY
+
+ For Server islands Astro creates a cryptography key in order to hash props for the islands, preventing accidental leakage of secrets.
+
+ If you deploy to an environment with rolling updates then there could be multiple instances of your app with different keys, causing potential key mismatches.
+
+ To fix this you can now pass the `ASTRO_KEY` environment variable to your build in order to reuse the same key.
+
+ To generate a key use:
+
+ ```
+ astro create-key
+ ```
+
+ This will print out an environment variable to set like:
+
+ ```
+ ASTRO_KEY=PIAuyPNn2aKU/bviapEuc/nVzdzZPizKNo3OqF/5PmQ=
+ ```
+
+- [#11935](https://github.com/withastro/astro/pull/11935) [`c58193a`](https://github.com/withastro/astro/commit/c58193a691775af5c568e461c63040a42e2471f7) Thanks [@Princesseuh](https://github.com/Princesseuh)! - Fixes `astro add` not using the proper export point when adding certain adapters
+
+## 4.15.3
+
+### Patch Changes
+
+- [#11902](https://github.com/withastro/astro/pull/11902) [`d63bc50`](https://github.com/withastro/astro/commit/d63bc50d9940c1107e0fee7687e5c332549a0eff) Thanks [@ascorbic](https://github.com/ascorbic)! - Fixes case where content layer did not update during clean dev builds on Linux and Windows
+
+- [#11886](https://github.com/withastro/astro/pull/11886) [`7ff7134`](https://github.com/withastro/astro/commit/7ff7134b8038a3b798293b2218bbf6dd02d2ac32) Thanks [@matthewp](https://github.com/matthewp)! - Fixes a missing error message when actions throws during `astro sync`
+
+- [#11904](https://github.com/withastro/astro/pull/11904) [`ca54e3f`](https://github.com/withastro/astro/commit/ca54e3f819fad009ac3c3c8b57a26014a2652a73) Thanks [@wtchnm](https://github.com/wtchnm)! - perf(assets): avoid downloading original image when using cache
+
## 5.0.0-alpha.5
### Major Changes
diff --git a/packages/astro/e2e/fixtures/astro-envs/package.json b/packages/astro/e2e/fixtures/astro-envs/package.json
index 7bb5ecfd0f3e6..271277f785a63 100644
--- a/packages/astro/e2e/fixtures/astro-envs/package.json
+++ b/packages/astro/e2e/fixtures/astro-envs/package.json
@@ -5,6 +5,6 @@
"dependencies": {
"@astrojs/vue": "workspace:*",
"astro": "workspace:*",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/client-only/package.json b/packages/astro/e2e/fixtures/client-only/package.json
index d89fe78983519..dc634e91f622f 100644
--- a/packages/astro/e2e/fixtures/client-only/package.json
+++ b/packages/astro/e2e/fixtures/client-only/package.json
@@ -16,6 +16,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/error-sass/package.json b/packages/astro/e2e/fixtures/error-sass/package.json
index 9658b550a1f6e..d59b5322c4f8f 100644
--- a/packages/astro/e2e/fixtures/error-sass/package.json
+++ b/packages/astro/e2e/fixtures/error-sass/package.json
@@ -4,6 +4,6 @@
"private": true,
"dependencies": {
"astro": "workspace:*",
- "sass": "^1.77.8"
+ "sass": "^1.78.0"
}
}
diff --git a/packages/astro/e2e/fixtures/errors/package.json b/packages/astro/e2e/fixtures/errors/package.json
index ab47d7da80c4a..e65edebd9bb62 100644
--- a/packages/astro/e2e/fixtures/errors/package.json
+++ b/packages/astro/e2e/fixtures/errors/package.json
@@ -12,9 +12,9 @@
"preact": "^10.23.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "sass": "^1.77.8",
+ "sass": "^1.78.0",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/hmr/package.json b/packages/astro/e2e/fixtures/hmr/package.json
index 87d59ab7f6e26..ad10713835530 100644
--- a/packages/astro/e2e/fixtures/hmr/package.json
+++ b/packages/astro/e2e/fixtures/hmr/package.json
@@ -4,6 +4,6 @@
"private": true,
"devDependencies": {
"astro": "workspace:*",
- "sass": "^1.77.8"
+ "sass": "^1.78.0"
}
}
diff --git a/packages/astro/e2e/fixtures/multiple-frameworks/package.json b/packages/astro/e2e/fixtures/multiple-frameworks/package.json
index f90d9725d43d6..24a68ca22eb58 100644
--- a/packages/astro/e2e/fixtures/multiple-frameworks/package.json
+++ b/packages/astro/e2e/fixtures/multiple-frameworks/package.json
@@ -18,6 +18,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/nested-in-preact/package.json b/packages/astro/e2e/fixtures/nested-in-preact/package.json
index c5f10cd437f89..e32875c4d7a84 100644
--- a/packages/astro/e2e/fixtures/nested-in-preact/package.json
+++ b/packages/astro/e2e/fixtures/nested-in-preact/package.json
@@ -16,6 +16,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/nested-in-react/package.json b/packages/astro/e2e/fixtures/nested-in-react/package.json
index bac0d1bae4912..146de6ea4dd7c 100644
--- a/packages/astro/e2e/fixtures/nested-in-react/package.json
+++ b/packages/astro/e2e/fixtures/nested-in-react/package.json
@@ -16,6 +16,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/nested-in-solid/package.json b/packages/astro/e2e/fixtures/nested-in-solid/package.json
index 4a2748eb52a2f..9c47f29e6c223 100644
--- a/packages/astro/e2e/fixtures/nested-in-solid/package.json
+++ b/packages/astro/e2e/fixtures/nested-in-solid/package.json
@@ -16,6 +16,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/nested-in-svelte/package.json b/packages/astro/e2e/fixtures/nested-in-svelte/package.json
index a98e441f53284..58915a0d66a8e 100644
--- a/packages/astro/e2e/fixtures/nested-in-svelte/package.json
+++ b/packages/astro/e2e/fixtures/nested-in-svelte/package.json
@@ -16,6 +16,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/nested-in-vue/package.json b/packages/astro/e2e/fixtures/nested-in-vue/package.json
index 1702f98bf872f..d41f8e86f7e04 100644
--- a/packages/astro/e2e/fixtures/nested-in-vue/package.json
+++ b/packages/astro/e2e/fixtures/nested-in-vue/package.json
@@ -16,6 +16,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/nested-recursive/package.json b/packages/astro/e2e/fixtures/nested-recursive/package.json
index d35cf0eb5a220..d3d0cb6b576e9 100644
--- a/packages/astro/e2e/fixtures/nested-recursive/package.json
+++ b/packages/astro/e2e/fixtures/nested-recursive/package.json
@@ -16,7 +16,7 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
},
"scripts": {
"dev": "astro dev"
diff --git a/packages/astro/e2e/fixtures/server-islands-key/astro.config.mjs b/packages/astro/e2e/fixtures/server-islands-key/astro.config.mjs
new file mode 100644
index 0000000000000..db1a7b45243ea
--- /dev/null
+++ b/packages/astro/e2e/fixtures/server-islands-key/astro.config.mjs
@@ -0,0 +1,12 @@
+import { defineConfig } from 'astro/config';
+import node from '@astrojs/node';
+
+// https://astro.build/config
+export default defineConfig({
+ output: 'server',
+ adapter: node({ mode: 'standalone' }),
+ integrations: [],
+ experimental: {
+ serverIslands: true,
+ }
+});
diff --git a/packages/astro/e2e/fixtures/server-islands-key/package.json b/packages/astro/e2e/fixtures/server-islands-key/package.json
new file mode 100644
index 0000000000000..b03c575c7e544
--- /dev/null
+++ b/packages/astro/e2e/fixtures/server-islands-key/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@e2e/server-islands-key",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "astro dev"
+ },
+ "dependencies": {
+ "astro": "workspace:*",
+ "@astrojs/node": "^8.3.3"
+ }
+}
diff --git a/packages/astro/e2e/fixtures/server-islands-key/src/components/Island.astro b/packages/astro/e2e/fixtures/server-islands-key/src/components/Island.astro
new file mode 100644
index 0000000000000..5eab0dc4dfc36
--- /dev/null
+++ b/packages/astro/e2e/fixtures/server-islands-key/src/components/Island.astro
@@ -0,0 +1,6 @@
+---
+const { secret } = Astro.props;
+---
+I am an island
+
+{secret}
diff --git a/packages/astro/e2e/fixtures/server-islands-key/src/pages/index.astro b/packages/astro/e2e/fixtures/server-islands-key/src/pages/index.astro
new file mode 100644
index 0000000000000..a19aa8a27518b
--- /dev/null
+++ b/packages/astro/e2e/fixtures/server-islands-key/src/pages/index.astro
@@ -0,0 +1,14 @@
+---
+import Island from '../components/Island.astro';
+---
+
+
+
+
+
+
+
+ children
+
+
+
diff --git a/packages/astro/e2e/fixtures/tailwindcss/package.json b/packages/astro/e2e/fixtures/tailwindcss/package.json
index 14d4c210b2031..1f856578131be 100644
--- a/packages/astro/e2e/fixtures/tailwindcss/package.json
+++ b/packages/astro/e2e/fixtures/tailwindcss/package.json
@@ -6,7 +6,7 @@
"@astrojs/tailwind": "workspace:*",
"astro": "workspace:*",
"autoprefixer": "^10.4.20",
- "postcss": "^8.4.43",
+ "postcss": "^8.4.45",
"tailwindcss": "^3.4.10"
}
}
diff --git a/packages/astro/e2e/fixtures/view-transitions/package.json b/packages/astro/e2e/fixtures/view-transitions/package.json
index e40816ffd8fd8..48cf30a9ae4b2 100644
--- a/packages/astro/e2e/fixtures/view-transitions/package.json
+++ b/packages/astro/e2e/fixtures/view-transitions/package.json
@@ -11,6 +11,6 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/fixtures/vue-component/package.json b/packages/astro/e2e/fixtures/vue-component/package.json
index 98e9e12394cd5..f53a1479fc2f2 100644
--- a/packages/astro/e2e/fixtures/vue-component/package.json
+++ b/packages/astro/e2e/fixtures/vue-component/package.json
@@ -6,6 +6,6 @@
"@astrojs/mdx": "workspace:*",
"@astrojs/vue": "workspace:*",
"astro": "workspace:*",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/e2e/server-islands-key.test.js b/packages/astro/e2e/server-islands-key.test.js
new file mode 100644
index 0000000000000..2120d1368facd
--- /dev/null
+++ b/packages/astro/e2e/server-islands-key.test.js
@@ -0,0 +1,33 @@
+import { expect } from '@playwright/test';
+import { testFactory } from './test-utils.js';
+
+const test = testFactory(import.meta.url, { root: './fixtures/server-islands-key/' });
+
+test.describe('Server islands - Key reuse', () => {
+ test.describe('Production', () => {
+ let previewServer;
+
+ test.beforeAll(async ({ astro }) => {
+ // Playwright's Node version doesn't have these functions, so stub them.
+ process.stdout.clearLine = () => {};
+ process.stdout.cursorTo = () => {};
+ process.env.ASTRO_KEY = 'eKBaVEuI7YjfanEXHuJe/pwZKKt3LkAHeMxvTU7aR0M=';
+ await astro.build();
+ previewServer = await astro.preview();
+ });
+
+ test.afterAll(async () => {
+ await previewServer.stop();
+ delete process.env.ASTRO_KEY;
+ });
+
+ test('Components render properly', async ({ page, astro }) => {
+ await page.goto(astro.resolveUrl('/'));
+
+ let el = page.locator('#island');
+
+ await expect(el, 'element rendered').toBeVisible();
+ await expect(el, 'should have content').toHaveText('I am an island');
+ });
+ });
+});
diff --git a/packages/astro/e2e/test-utils.js b/packages/astro/e2e/test-utils.js
index 933186a7181eb..a2728f9627c2c 100644
--- a/packages/astro/e2e/test-utils.js
+++ b/packages/astro/e2e/test-utils.js
@@ -14,6 +14,10 @@ const testFileToPort = new Map();
for (let i = 0; i < testFiles.length; i++) {
const file = testFiles[i];
if (file.endsWith('.test.js')) {
+ // Port 4045 is an unsafe port in Chrome, so skip it.
+ if (4000 + i === 4045) {
+ i++;
+ }
testFileToPort.set(file, 4000 + i);
}
}
diff --git a/packages/astro/package.json b/packages/astro/package.json
index 59c9c9025e56e..1e7c8f4af5a8b 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -139,7 +139,7 @@
"common-ancestor-path": "^1.0.1",
"cookie": "^0.6.0",
"cssesc": "^3.0.0",
- "debug": "^4.3.6",
+ "debug": "^4.3.7",
"deterministic-object-hash": "^2.0.2",
"devalue": "^5.0.0",
"diff": "^5.2.0",
@@ -170,14 +170,14 @@
"prompts": "^2.4.2",
"rehype": "^13.0.1",
"semver": "^7.6.3",
- "shiki": "^1.16.1",
+ "shiki": "^1.16.2",
"string-width": "^7.2.0",
"strip-ansi": "^7.1.0",
"tinyexec": "^0.3.0",
"tsconfck": "^3.1.3",
"unist-util-visit": "^5.0.0",
"vfile": "^6.0.3",
- "vite": "^5.4.2",
+ "vite": "^5.4.3",
"vitefu": "^1.0.2",
"which-pm": "^3.0.0",
"xxhash-wasm": "^1.0.2",
@@ -191,7 +191,7 @@
},
"devDependencies": {
"@astrojs/check": "^0.9.3",
- "@playwright/test": "^1.46.1",
+ "@playwright/test": "^1.47.0",
"@types/aria-query": "^5.0.4",
"@types/common-ancestor-path": "^1.0.2",
"@types/cssesc": "^3.0.2",
@@ -222,7 +222,7 @@
"rehype-toc": "^3.0.2",
"remark-code-titles": "^0.1.2",
"rollup": "^4.21.2",
- "sass": "^1.77.8",
+ "sass": "^1.78.0",
"undici": "^6.19.8",
"unified": "^11.0.5"
},
diff --git a/packages/astro/src/actions/runtime/virtual/server.ts b/packages/astro/src/actions/runtime/virtual/server.ts
index cd1b4269ed385..8e5e6bb4f1a5f 100644
--- a/packages/astro/src/actions/runtime/virtual/server.ts
+++ b/packages/astro/src/actions/runtime/virtual/server.ts
@@ -92,7 +92,7 @@ function getFormServerHandler(
if (!inputSchema) return await handler(unparsedInput, context);
- const baseSchema = unwrapSchemaEffects(inputSchema);
+ const baseSchema = unwrapBaseObjectSchema(inputSchema, unparsedInput);
const parsed = await inputSchema.safeParseAsync(
baseSchema instanceof z.ZodObject
? formDataToObject(unparsedInput, baseSchema)
@@ -191,7 +191,7 @@ function handleFormDataGet(
return validator instanceof z.ZodNumber ? Number(value) : value;
}
-function unwrapSchemaEffects(schema: z.ZodType) {
+function unwrapBaseObjectSchema(schema: z.ZodType, unparsedInput: FormData) {
while (schema instanceof z.ZodEffects || schema instanceof z.ZodPipeline) {
if (schema instanceof z.ZodEffects) {
schema = schema._def.schema;
@@ -200,5 +200,15 @@ function unwrapSchemaEffects(schema: z.ZodType) {
schema = schema._def.in;
}
}
+ if (schema instanceof z.ZodDiscriminatedUnion) {
+ const typeKey = schema._def.discriminator;
+ const typeValue = unparsedInput.get(typeKey);
+ if (typeof typeValue !== 'string') return schema;
+
+ const objSchema = schema._def.optionsMap.get(typeValue);
+ if (!objSchema) return schema;
+
+ return objSchema;
+ }
return schema;
}
diff --git a/packages/astro/src/assets/build/generate.ts b/packages/astro/src/assets/build/generate.ts
index c38ebb75a3598..00261d5b7f88a 100644
--- a/packages/astro/src/assets/build/generate.ts
+++ b/packages/astro/src/assets/build/generate.ts
@@ -98,11 +98,11 @@ export async function generateImagesForPath(
env: AssetEnv,
queue: PQueue,
) {
- const originalImageData = await loadImage(originalFilePath, env);
+ let originalImage: ImageData;
for (const [_, transform] of transformsAndPath.transforms) {
await queue
- .add(async () => generateImage(originalImageData, transform.finalPath, transform.transform))
+ .add(async () => generateImage(transform.finalPath, transform.transform))
.catch((e) => {
throw e;
});
@@ -128,13 +128,9 @@ export async function generateImagesForPath(
}
}
- async function generateImage(
- originalImage: ImageData,
- filepath: string,
- options: ImageTransform,
- ) {
+ async function generateImage(filepath: string, options: ImageTransform) {
const timeStart = performance.now();
- const generationData = await generateImageInternal(originalImage, filepath, options);
+ const generationData = await generateImageInternal(filepath, options);
const timeEnd = performance.now();
const timeChange = getTimeStat(timeStart, timeEnd);
@@ -151,7 +147,6 @@ export async function generateImagesForPath(
}
async function generateImageInternal(
- originalImage: ImageData,
filepath: string,
options: ImageTransform,
): Promise {
@@ -207,6 +202,10 @@ export async function generateImagesForPath(
? (options.src as ImageMetadata).src
: (options.src as string);
+ if (!originalImage) {
+ originalImage = await loadImage(originalFilePath, env);
+ }
+
let resultData: Partial = {
data: undefined,
expires: originalImage.expires,
diff --git a/packages/astro/src/cli/add/index.ts b/packages/astro/src/cli/add/index.ts
index f263904cbb4d7..7866f5a093ab9 100644
--- a/packages/astro/src/cli/add/index.ts
+++ b/packages/astro/src/cli/add/index.ts
@@ -279,7 +279,7 @@ export async function add(names: string[], { flags }: AddOptions) {
if (isAdapter(integration)) {
const officialExportName = OFFICIAL_ADAPTER_TO_IMPORT_MAP[integration.id];
if (officialExportName) {
- setAdapter(mod, integration);
+ setAdapter(mod, integration, officialExportName);
} else {
logger.info(
'SKIP_FORMAT',
@@ -447,7 +447,11 @@ function addIntegration(mod: ProxifiedModule, integration: IntegrationInfo)
}
}
-export function setAdapter(mod: ProxifiedModule, adapter: IntegrationInfo) {
+export function setAdapter(
+ mod: ProxifiedModule,
+ adapter: IntegrationInfo,
+ exportName: string,
+) {
const config = getDefaultExportOptions(mod);
const adapterId = toIdent(adapter.id);
@@ -455,7 +459,7 @@ export function setAdapter(mod: ProxifiedModule, adapter: IntegrationInfo)
mod.imports.$append({
imported: 'default',
local: adapterId,
- from: adapter.packageName,
+ from: exportName,
});
}
diff --git a/packages/astro/src/cli/create-key/index.ts b/packages/astro/src/cli/create-key/index.ts
new file mode 100644
index 0000000000000..d9b9f08ffd526
--- /dev/null
+++ b/packages/astro/src/cli/create-key/index.ts
@@ -0,0 +1,33 @@
+import { createNodeLogger } from '../../core/config/logging.js';
+import { createKey as createCryptoKey, encodeKey } from '../../core/encryption.js';
+import { type Flags, flagsToAstroInlineConfig } from '../flags.js';
+
+interface CreateKeyOptions {
+ flags: Flags;
+}
+
+export async function createKey({ flags }: CreateKeyOptions): Promise<0 | 1> {
+ try {
+ const inlineConfig = flagsToAstroInlineConfig(flags);
+ const logger = createNodeLogger(inlineConfig);
+
+ const keyPromise = createCryptoKey();
+ const key = await keyPromise;
+ const encoded = await encodeKey(key);
+
+ logger.info(
+ 'crypto',
+ `Generated a key to encrypt props passed to Server islands. To reuse the same key across builds, set this value as ASTRO_KEY in an environment variable on your build server.
+
+ASTRO_KEY=${encoded}`,
+ );
+ } catch (err: unknown) {
+ if (err != null) {
+ // eslint-disable-next-line no-console
+ console.error(err.toString());
+ }
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts
index c767569fde51d..23486f938a3b8 100644
--- a/packages/astro/src/cli/index.ts
+++ b/packages/astro/src/cli/index.ts
@@ -7,6 +7,7 @@ type CLICommand =
| 'help'
| 'version'
| 'add'
+ | 'create-key'
| 'docs'
| 'dev'
| 'build'
@@ -30,6 +31,7 @@ async function printAstroHelp() {
['add', 'Add an integration.'],
['build', 'Build your project and write it to disk.'],
['check', 'Check your project for errors.'],
+ ['create-key', 'Create a cryptography key'],
['db', 'Manage your Astro database.'],
['dev', 'Start the development server.'],
['docs', 'Open documentation in your web browser.'],
@@ -78,6 +80,7 @@ function resolveCommand(flags: yargs.Arguments): CLICommand {
'build',
'preview',
'check',
+ 'create-key',
'docs',
'db',
'info',
@@ -111,6 +114,11 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
await printInfo({ flags });
return;
}
+ case 'create-key': {
+ const { createKey } = await import('./create-key/index.js');
+ const exitCode = await createKey({ flags });
+ return process.exit(exitCode);
+ }
case 'docs': {
const { docs } = await import('./docs/index.js');
await docs({ flags });
diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts
index e93bb36eae886..440ed6028fb8c 100644
--- a/packages/astro/src/core/build/index.ts
+++ b/packages/astro/src/core/build/index.ts
@@ -18,7 +18,7 @@ import { resolveConfig } from '../config/config.js';
import { createNodeLogger } from '../config/logging.js';
import { createSettings } from '../config/settings.js';
import { createVite } from '../create-vite.js';
-import { createKey } from '../encryption.js';
+import { createKey, getEnvironmentKey, hasEnvironmentKey } from '../encryption.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import type { Logger } from '../logger/core.js';
import { levels, timerMessage } from '../logger/core.js';
@@ -193,6 +193,9 @@ class AstroBuilder {
green(`✓ Completed in ${getTimeStat(this.timer.init, performance.now())}.`),
);
+ const hasKey = hasEnvironmentKey();
+ const keyPromise = hasKey ? getEnvironmentKey() : createKey();
+
const opts: StaticBuildOptions = {
allPages,
settings: this.settings,
@@ -203,7 +206,7 @@ class AstroBuilder {
pageNames,
teardownCompiler: this.teardownCompiler,
viteConfig,
- key: createKey(),
+ key: keyPromise,
};
const { internals, ssrOutputChunkNames, contentFileNames } = await viteBuild(opts);
diff --git a/packages/astro/src/core/encryption.ts b/packages/astro/src/core/encryption.ts
index ccfc9bdd274f8..253e5f3c97bda 100644
--- a/packages/astro/src/core/encryption.ts
+++ b/packages/astro/src/core/encryption.ts
@@ -20,6 +20,37 @@ export async function createKey() {
return key;
}
+// The environment variable name that can be used to provide the encrypted key.
+const ENVIRONMENT_KEY_NAME = 'ASTRO_KEY' as const;
+
+/**
+ * Get the encoded value of the ASTRO_KEY env var.
+ */
+export function getEncodedEnvironmentKey(): string {
+ return process.env[ENVIRONMENT_KEY_NAME] || '';
+}
+
+/**
+ * See if the environment variable key ASTRO_KEY is set.
+ */
+export function hasEnvironmentKey(): boolean {
+ return getEncodedEnvironmentKey() !== '';
+}
+
+/**
+ * Get the environment variable key and decode it into a CryptoKey.
+ */
+export async function getEnvironmentKey(): Promise {
+ // This should never happen, because we always check `hasEnvironmentKey` before this is called.
+ if (!hasEnvironmentKey()) {
+ throw new Error(
+ `There is no environment key defined. If you see this error there is a bug in Astro.`,
+ );
+ }
+ const encodedKey = getEncodedEnvironmentKey();
+ return decodeKey(encodedKey);
+}
+
/**
* Takes a key that has been serialized to an array of bytes and returns a CryptoKey
*/
diff --git a/packages/astro/src/core/logger/core.ts b/packages/astro/src/core/logger/core.ts
index 530399ac42490..51ebd9325b063 100644
--- a/packages/astro/src/core/logger/core.ts
+++ b/packages/astro/src/core/logger/core.ts
@@ -18,6 +18,7 @@ export type LoggerLabel =
| 'check'
| 'config'
| 'content'
+ | 'crypto'
| 'deprecated'
| 'markdown'
| 'router'
@@ -27,6 +28,7 @@ export type LoggerLabel =
| 'middleware'
| 'preferences'
| 'redirects'
+ | 'sync'
| 'toolbar'
| 'assets'
| 'env'
diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts
index f50bea9d062b3..c5bc540e9b28b 100644
--- a/packages/astro/src/core/sync/index.ts
+++ b/packages/astro/src/core/sync/index.ts
@@ -66,7 +66,19 @@ export default async function sync(
logger,
});
const manifest = await createRouteManifest({ settings, fsMod: fs }, logger);
- await runHookConfigDone({ settings, logger, command: 'sync' });
+
+ // Run `astro:config:done`
+ // Actions will throw if there is misconfiguration, so catch here.
+ try {
+ await runHookConfigDone({ settings, logger });
+ } catch (err) {
+ if (err instanceof Error) {
+ const errorMessage = err.toString();
+ logger.error('sync', errorMessage);
+ }
+ throw err;
+ }
+
return await syncInternal({ settings, logger, fs, force: inlineConfig.force, manifest });
}
diff --git a/packages/astro/src/vite-plugin-astro-server/css.ts b/packages/astro/src/vite-plugin-astro-server/css.ts
index c2dd5f6d7a6b1..af147048a0b97 100644
--- a/packages/astro/src/vite-plugin-astro-server/css.ts
+++ b/packages/astro/src/vite-plugin-astro-server/css.ts
@@ -9,6 +9,8 @@ interface ImportedStyle {
content: string;
}
+const inlineQueryRE = /(?:\?|&)inline(?:$|&)/;
+
/** Given a filePath URL, crawl Vite’s module graph to find all style imports. */
export async function getStylesForURL(
filePath: URL,
@@ -32,21 +34,20 @@ export async function getStylesForURL(
}
// Else try to load it
else {
- const url = new URL(importedModule.url, 'http://localhost');
+ let modId = importedModule.url;
// Mark url with ?inline so Vite will return the CSS as plain string, even for CSS modules
- url.searchParams.set('inline', '');
- const modId = `${decodeURI(url.pathname)}${url.search}`;
-
+ if (!inlineQueryRE.test(importedModule.url)) {
+ if (importedModule.url.includes('?')) {
+ modId = importedModule.url.replace('?', '?inline&');
+ } else {
+ modId += '?inline';
+ }
+ }
try {
// The SSR module is possibly not loaded. Load it if it's null.
const ssrModule = await loader.import(modId);
css = ssrModule.default;
} catch {
- // Some CSS modules, e.g. from Vue files, may not work with the ?inline query.
- // If so, we fallback to a url instead
- if (modId.includes('.module.')) {
- importedCssUrls.add(importedModule.url);
- }
// The module may not be inline-able, e.g. SCSS partials. Skip it as it may already
// be inlined into other modules if it happens to be in the graph.
continue;
diff --git a/packages/astro/test/0-css.test.js b/packages/astro/test/0-css.test.js
index 6c2ee0966cdc9..a582f9e7196d6 100644
--- a/packages/astro/test/0-css.test.js
+++ b/packages/astro/test/0-css.test.js
@@ -225,7 +225,7 @@ describe('CSS', function () {
it('
- Vue CSS Modules
+ Vue CSS Modules
diff --git a/packages/astro/test/fixtures/actions/src/actions/index.ts b/packages/astro/test/fixtures/actions/src/actions/index.ts
index ed76927993533..4e6120309fd6c 100644
--- a/packages/astro/test/fixtures/actions/src/actions/index.ts
+++ b/packages/astro/test/fixtures/actions/src/actions/index.ts
@@ -7,6 +7,29 @@ const passwordSchema = z
.max(128, 'Password length exceeded. Max 128 chars.');
export const server = {
+ imageUploadInChunks: defineAction({
+ accept: 'form',
+ input: z.discriminatedUnion('type', [
+ z.object({
+ type: z.literal('first-chunk'),
+ image: z.instanceof(File),
+ alt: z.string(),
+ }),
+ z.object({ type: z.literal('rest-chunk'), image: z.instanceof(File), uploadId: z.string() }),
+ ]),
+ handler: async (data) => {
+ if (data.type === 'first-chunk') {
+ const uploadId = Math.random().toString(36).slice(2);
+ return {
+ uploadId,
+ };
+ } else {
+ return {
+ uploadId: data.uploadId,
+ };
+ }
+ },
+ }),
subscribe: defineAction({
input: z.object({ channel: z.string() }),
handler: async ({ channel }) => {
diff --git a/packages/astro/test/fixtures/astro-children/package.json b/packages/astro/test/fixtures/astro-children/package.json
index 038487d67d089..1eca53bfa5f96 100644
--- a/packages/astro/test/fixtures/astro-children/package.json
+++ b/packages/astro/test/fixtures/astro-children/package.json
@@ -9,6 +9,6 @@
"astro": "workspace:*",
"preact": "^10.23.2",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/test/fixtures/astro-envs/package.json b/packages/astro/test/fixtures/astro-envs/package.json
index ececd485cf316..264d4d23af190 100644
--- a/packages/astro/test/fixtures/astro-envs/package.json
+++ b/packages/astro/test/fixtures/astro-envs/package.json
@@ -5,6 +5,6 @@
"dependencies": {
"@astrojs/vue": "workspace:*",
"astro": "workspace:*",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/test/fixtures/astro-slots-nested/package.json b/packages/astro/test/fixtures/astro-slots-nested/package.json
index 4f3ed29e02f52..db5d8edd128a4 100644
--- a/packages/astro/test/fixtures/astro-slots-nested/package.json
+++ b/packages/astro/test/fixtures/astro-slots-nested/package.json
@@ -14,6 +14,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/test/fixtures/container-custom-renderers/package.json b/packages/astro/test/fixtures/container-custom-renderers/package.json
index 6ffebecb957cf..fd7740fd3e001 100644
--- a/packages/astro/test/fixtures/container-custom-renderers/package.json
+++ b/packages/astro/test/fixtures/container-custom-renderers/package.json
@@ -9,6 +9,6 @@
"astro": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/test/fixtures/fetch/package.json b/packages/astro/test/fixtures/fetch/package.json
index 97aa25a78396b..8ef26a016997c 100644
--- a/packages/astro/test/fixtures/fetch/package.json
+++ b/packages/astro/test/fixtures/fetch/package.json
@@ -9,6 +9,6 @@
"astro": "workspace:*",
"preact": "^10.23.2",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/test/fixtures/fontsource-package/package.json b/packages/astro/test/fixtures/fontsource-package/package.json
index 558a54d16b51e..14316760f6cbc 100644
--- a/packages/astro/test/fixtures/fontsource-package/package.json
+++ b/packages/astro/test/fixtures/fontsource-package/package.json
@@ -3,8 +3,8 @@
"version": "0.0.0",
"private": true,
"dependencies": {
- "@fontsource/monofett": "5.0.21",
- "@fontsource/montserrat": "5.0.19",
+ "@fontsource/monofett": "5.0.22",
+ "@fontsource/montserrat": "5.0.20",
"astro": "workspace:*"
}
}
diff --git a/packages/astro/test/fixtures/jsx/package.json b/packages/astro/test/fixtures/jsx/package.json
index 6d32dffe4d292..152caf935381f 100644
--- a/packages/astro/test/fixtures/jsx/package.json
+++ b/packages/astro/test/fixtures/jsx/package.json
@@ -17,6 +17,6 @@
"react-dom": "^18.3.1",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
}
}
diff --git a/packages/astro/test/fixtures/postcss/package.json b/packages/astro/test/fixtures/postcss/package.json
index 45438302bd8fa..1cd556f4c94b6 100644
--- a/packages/astro/test/fixtures/postcss/package.json
+++ b/packages/astro/test/fixtures/postcss/package.json
@@ -8,10 +8,10 @@
"@astrojs/vue": "workspace:*",
"astro": "workspace:*",
"autoprefixer": "^10.4.20",
- "postcss": "^8.4.43",
+ "postcss": "^8.4.45",
"solid-js": "^1.8.22",
"svelte": "^4.2.19",
- "vue": "^3.4.38"
+ "vue": "^3.5.3"
},
"devDependencies": {
"postcss-preset-env": "^10.0.2"
diff --git a/packages/astro/test/fixtures/preact-component/src/components/ComponentWithNullProp.jsx b/packages/astro/test/fixtures/preact-component/src/components/ComponentWithNullProp.jsx
new file mode 100644
index 0000000000000..53856ce902751
--- /dev/null
+++ b/packages/astro/test/fixtures/preact-component/src/components/ComponentWithNullProp.jsx
@@ -0,0 +1,7 @@
+import { h } from 'preact';
+
+export default ({ nullProp }) => {
+ return
+
I have a null prop: {nullProp}
+
+}
\ No newline at end of file
diff --git a/packages/astro/test/fixtures/preact-component/src/pages/signals.astro b/packages/astro/test/fixtures/preact-component/src/pages/signals.astro
index 37b43a73c7eca..8abfe8f025dbb 100644
--- a/packages/astro/test/fixtures/preact-component/src/pages/signals.astro
+++ b/packages/astro/test/fixtures/preact-component/src/pages/signals.astro
@@ -3,6 +3,7 @@ import { signal } from '@preact/signals';
import Signals from '../components/Signals';
import SignalsInArray from '../components/SignalsInArray';
import SignalsInObject from '../components/SignalsInObject';
+import ComponentWithNullProp from '../components/ComponentWithNullProp';
const count = signal(1);
const secondCount = signal(2);
---
@@ -14,6 +15,7 @@ const secondCount = signal(2);
-
+
+