Repository: sveltejs/kit
Pull request #5987 (2022-08-17 17:31:35)

check if data is serializable
This throws an error if a `load` function in `+page.server.js` or `+layout.server.js` returns data that can't be serialized as JSON, so things like `/hello/` don't become `{}`.

Supersedes #5635.

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [ ] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it solves.
- [x] Ideally, include a test that fails without this PR but passes with it.

### Tests
- [x] Run the tests with `pnpm test` and lint the project with `pnpm lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. All changesets should be `patch` until SvelteKit 1.0


changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: eb1c1ebea84675641e1510feb28c4b6d4162cd81

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name          | Type  |
| ------------- | ----- |
| @sveltejs/kit | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/sveltejs/kit/new/disallow-non-json?filename=.changeset/olive-tigers-hammer.md&value=---%0A%22%40sveltejs%2Fkit%22%3A%20patch%0A%22test-basics%22%3A%20patch%0A---%0A%0Acheck%20data%20is%20serializable%0A)



Patch:
diff --git a/.changeset/few-hornets-joke.md b/.changeset/few-hornets-joke.md
new file mode 100644
index 000000000..e1d391fa9
--- /dev/null
+++ b/.changeset/few-hornets-joke.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Respect `export const prerender = false` in `+page.server.js`
diff --git a/.changeset/fresh-pigs-tease.md b/.changeset/fresh-pigs-tease.md
new file mode 100644
index 000000000..9af327627
--- /dev/null
+++ b/.changeset/fresh-pigs-tease.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[breaking] remove ability for `+page.server.js` to respond to `GET` requests with JSON
diff --git a/.changeset/gold-rocks-hang.md b/.changeset/gold-rocks-hang.md
new file mode 100644
index 000000000..03fd306e9
--- /dev/null
+++ b/.changeset/gold-rocks-hang.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+update `app.d.ts` for library skeleton template
diff --git a/.changeset/gorgeous-snakes-pretend.md b/.changeset/gorgeous-snakes-pretend.md
new file mode 100644
index 000000000..44581b129
--- /dev/null
+++ b/.changeset/gorgeous-snakes-pretend.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Generate `__data.json` files for server-side redirects when prerendering
diff --git a/.changeset/honest-parrots-compare.md b/.changeset/honest-parrots-compare.md
new file mode 100644
index 000000000..4c8442e71
--- /dev/null
+++ b/.changeset/honest-parrots-compare.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Handle `throw error/redirect` in `+server.js`
diff --git a/.changeset/lemon-kids-double.md b/.changeset/lemon-kids-double.md
new file mode 100644
index 000000000..e98b9d4d2
--- /dev/null
+++ b/.changeset/lemon-kids-double.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+handle `set-cookie` in `setHeaders`
diff --git a/.changeset/mighty-students-develop.md b/.changeset/mighty-students-develop.md
new file mode 100644
index 000000000..cbffb0dea
--- /dev/null
+++ b/.changeset/mighty-students-develop.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Handle windows paths and regexp chars in kit.alias
diff --git a/.changeset/ninety-fans-cover.md b/.changeset/ninety-fans-cover.md
new file mode 100644
index 000000000..aba7c9ea3
--- /dev/null
+++ b/.changeset/ninety-fans-cover.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Make errors type in Action type less restrictive
diff --git a/.changeset/pink-gorillas-watch.md b/.changeset/pink-gorillas-watch.md
new file mode 100644
index 000000000..199a76170
--- /dev/null
+++ b/.changeset/pink-gorillas-watch.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] support linked `@sveltejs/kit` project in Vite 3.0.3+
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 1dce3f4af..ea7fe6e55 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -256,6 +256,7 @@
     "few-buses-argue",
     "few-carrots-sell",
     "few-cobras-switch",
+    "few-hornets-joke",
     "few-points-taste",
     "few-rockets-sort",
     "few-rockets-think",
@@ -326,6 +327,7 @@
     "fuzzy-spoons-report",
     "gentle-apes-tease",
     "gentle-dogs-heal",
+    "gentle-news-shout",
     "gentle-rules-camp",
     "giant-ants-hang",
     "giant-ghosts-give",
@@ -334,6 +336,7 @@
     "gold-carrots-sniff",
     "gold-colts-train",
     "gold-moles-hunt",
+    "gold-rocks-hang",
     "gold-spies-fail",
     "gold-trains-dream",
     "good-beans-invite",
@@ -348,6 +351,7 @@
     "gorgeous-planes-rhyme",
     "gorgeous-radios-matter",
     "gorgeous-radios-tan",
+    "gorgeous-snakes-pretend",
     "gorgeous-taxis-smash",
     "gorgeous-toys-rule",
     "great-bags-worry",
@@ -655,6 +659,7 @@
     "perfect-socks-sparkle",
     "pink-ducks-press",
     "pink-flies-mix",
+    "pink-gorillas-watch",
     "pink-poets-begin",
     "plenty-cougars-invite",
     "plenty-cups-watch",
@@ -1078,6 +1083,7 @@
     "twenty-dogs-shout",
     "twenty-dryers-hope",
     "twenty-hairs-shave",
+    "twenty-numbers-breathe",
     "twenty-numbers-destroy",
     "twenty-papayas-compare",
     "twenty-pigs-attend",
@@ -1132,6 +1138,7 @@
     "wicked-oranges-share",
     "wicked-plums-count",
     "wicked-spies-invite",
+    "wicked-spoons-leave",
     "wicked-terms-agree",
     "wild-badgers-brake",
     "wild-beans-happen",
diff --git a/.changeset/selfish-tips-divide.md b/.changeset/selfish-tips-divide.md
new file mode 100644
index 000000000..ac7e76767
--- /dev/null
+++ b/.changeset/selfish-tips-divide.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Run svelte-kit sync before svelte-check in check scripts
diff --git a/.changeset/shiny-scissors-listen.md b/.changeset/shiny-scissors-listen.md
new file mode 100644
index 000000000..9565d88e2
--- /dev/null
+++ b/.changeset/shiny-scissors-listen.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Check that data is serializable
diff --git a/.changeset/wise-berries-pay.md b/.changeset/wise-berries-pay.md
new file mode 100644
index 000000000..457972dc1
--- /dev/null
+++ b/.changeset/wise-berries-pay.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Fix parent data type for layouts referencing named layouts in the same folder
diff --git a/documentation/docs/05-load.md b/documentation/docs/05-load.md
index 8631e2139..2f956a7cf 100644
--- a/documentation/docs/05-load.md
+++ b/documentation/docs/05-load.md
@@ -188,6 +188,28 @@ export async function load({ parent }) {
 
 In `+page.js` or `+layout.js` it will return data from `load` functions in parent `+layout.js` files. Implicitly, a missing `+layout.js` is treated as a `({ data }) => data` function, meaning that it will also return data from parent `+layout.server.js` files.
 
+Be careful not to introduce accidental waterfalls when using `await parent()`. If for example you only want to merge parent data into the returned output, call it _after_ fetching your other data.
+
+```diff
+/// file: src/routes/foo/+page.server.js
+// @filename: $types.d.ts
+export type PageLoad = import('@sveltejs/kit').Load<{}, null, { a: number, b: number }>;
+
+// @filename: index.js
+// ---cut---
+/** @type {import('./$types').PageLoad} */
+export async function load({ parent, fetch }) {
+-	const parentData = await parent();
+	const data = await fetch('./some-api');
++	const parentData = await parent();
+
+	return {
+		...data
+		meta: { ...parentData.meta, ...data.meta }
+	};
+}
+```
+
 #### setHeaders
 
 If you need to set headers for the response, you can do so using the `setHeaders` method. This is useful if you want the page to be cached, for example:
diff --git a/documentation/docs/15-configuration.md b/documentation/docs/15-configuration.md
index 844ba60b3..2f99bd282 100644
--- a/documentation/docs/15-configuration.md
+++ b/documentation/docs/15-configuration.md
@@ -72,9 +72,6 @@ const config = {
 		}
 	},
 
-	// options passed to svelte.preprocess (https://svelte.dev/docs#compile-time-svelte-preprocess)
-	preprocess: null,
-
 	// options passed to @sveltejs/package
 	package: {
 		source: 'value of kit.files.lib, if available, else src/lib',
@@ -83,7 +80,10 @@ const config = {
 		// excludes all .d.ts and files starting with _ as the name
 		exports: (filepath) => !/^_|\/_|\.d\.ts$/.test(filepath),
 		files: () => true
-	}
+	},
+
+	// options passed to svelte.preprocess (https://svelte.dev/docs#compile-time-svelte-preprocess)
+	preprocess: null
 };
 
 export default config;
diff --git a/documentation/docs/17-seo.md b/documentation/docs/17-seo.md
index afd79d00f..64208d7c4 100644
--- a/documentation/docs/17-seo.md
+++ b/documentation/docs/17-seo.md
@@ -26,7 +26,7 @@ SvelteKit redirects pathnames with trailing slashes to ones without (or vice ver
 
 Every page should have well-written and unique `<title>` and `<meta name="description">` elements inside a [`<svelte:head>`](https://svelte.dev/docs#template-syntax-svelte-head). Guidance on how to write descriptive titles and descriptions, along with other suggestions on making content understandable by search engines, can be found on Google's [Lighthouse SEO audits](https://web.dev/lighthouse-seo/) documentation.
 
-> A common pattern is to return SEO-related [`stuff`](/docs/load#output-stuff) from page `load` functions, then use it (as [`$page.stuff`](/docs/modules#$app-stores)) in a `<svelte:head>` in your root [layout](/docs/routing#layout).
+> A common pattern is to return SEO-related `data` from page [`load`](/docs/load) functions, then use it (as [`$page.data`](/docs/modules#$app-stores)) in a `<svelte:head>` in your root [layout](/docs/routing#layout).
 
 #### Structured data
 
@@ -59,7 +59,8 @@ export default config;
 ```js
 /// file: src/routes/sitemap.xml/+server.js
 export async function GET() {
-	return new Response(`
+	return new Response(
+		`
 		<?xml version="1.0" encoding="UTF-8" ?>
 		<urlset
 			xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
diff --git a/package.json b/package.json
index 3aa8f7a12..3f3b9d671 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,7 @@
 		"turbo": "^1.3.4",
 		"typescript": "^4.7.4"
 	},
-	"packageManager": "pnpm@7.9.0",
+	"packageManager": "pnpm@7.9.3",
 	"engines": {
 		"pnpm": "^7.0.0"
 	},
diff --git a/packages/adapter-static/package.json b/packages/adapter-static/package.json
index d3a0c7a25..9c0a100da 100644
--- a/packages/adapter-static/package.json
+++ b/packages/adapter-static/package.json
@@ -32,6 +32,6 @@
 		"svelte": "^3.48.0",
 		"typescript": "^4.7.4",
 		"uvu": "^0.5.3",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	}
 }
diff --git a/packages/create-svelte/CHANGELOG.md b/packages/create-svelte/CHANGELOG.md
index dbdb840a5..f03c4fa56 100644
--- a/packages/create-svelte/CHANGELOG.md
+++ b/packages/create-svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
 # create-svelte
 
+## 2.0.0-next.156
+
+### Patch Changes
+
+- update `app.d.ts` for library skeleton template ([#6020](https://github.com/sveltejs/kit/pull/6020))
+
 ## 2.0.0-next.155
 
 ### Patch Changes
diff --git a/packages/create-svelte/package.json b/packages/create-svelte/package.json
index ca8f89dce..56e941bcc 100644
--- a/packages/create-svelte/package.json
+++ b/packages/create-svelte/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "create-svelte",
-	"version": "2.0.0-next.155",
+	"version": "2.0.0-next.156",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/create-svelte/shared/+typescript/package.json b/packages/create-svelte/shared/+typescript/package.json
index 465f674b8..7b5612f97 100644
--- a/packages/create-svelte/shared/+typescript/package.json
+++ b/packages/create-svelte/shared/+typescript/package.json
@@ -1,7 +1,7 @@
 {
 	"scripts": {
-		"check": "svelte-check --tsconfig ./tsconfig.json",
-		"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch"
+		"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+		"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
 	},
 	"devDependencies": {
 		"typescript": "^4.7.4",
diff --git a/packages/create-svelte/templates/default/package.template.json b/packages/create-svelte/templates/default/package.template.json
index d4cc74df2..98557d778 100644
--- a/packages/create-svelte/templates/default/package.template.json
+++ b/packages/create-svelte/templates/default/package.template.json
@@ -11,7 +11,7 @@
 		"@sveltejs/adapter-auto": "next",
 		"@sveltejs/kit": "next",
 		"svelte": "^3.46.0",
-		"vite": "^3.0.4"
+		"vite": "^3.0.8"
 	},
 	"type": "module",
 	"dependencies": {
diff --git a/packages/create-svelte/templates/libskeleton/src/app.d.ts b/packages/create-svelte/templates/libskeleton/src/app.d.ts
index b28d84056..66327cbb1 100644
--- a/packages/create-svelte/templates/libskeleton/src/app.d.ts
+++ b/packages/create-svelte/templates/libskeleton/src/app.d.ts
@@ -6,6 +6,6 @@
 declare namespace App {
 	// interface Locals {}
 	// interface Platform {}
-	// interface Session {}
-	// interface Stuff {}
+	// interface PrivateEnv {}
+	// interface PublicEnv {}
 }
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index ec4774ada..fdef39d3d 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,29 @@
 # @sveltejs/kit
 
+## 1.0.0-next.421
+
+### Patch Changes
+
+- Respect `export const prerender = false` in `+page.server.js` ([#6012](https://github.com/sveltejs/kit/pull/6012))
+
+## 1.0.0-next.420
+
+### Patch Changes
+
+- [fix] support linked `@sveltejs/kit` project in Vite 3.0.3+ ([#5861](https://github.com/sveltejs/kit/pull/5861))
+
+## 1.0.0-next.419
+
+### Patch Changes
+
+- Return a 303 response when a `POST` handler provides a `location` ([#5989](https://github.com/sveltejs/kit/pull/5989))
+
+* Generate `__data.json` files for server-side redirects when prerendering ([#5997](https://github.com/sveltejs/kit/pull/5997))
+
+- [chore] remove chokidar as dependency of kit ([#5996](https://github.com/sveltejs/kit/pull/5996))
+
+* Use relative asset paths where possible ([#4250](https://github.com/sveltejs/kit/pull/4250))
+
 ## 1.0.0-next.418
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index f6ce39789..3645e8c6a 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.418",
+	"version": "1.0.0-next.421",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -39,11 +39,11 @@
 		"tiny-glob": "^0.2.9",
 		"typescript": "^4.7.4",
 		"uvu": "^0.5.3",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"peerDependencies": {
 		"svelte": "^3.44.0",
-		"vite": "3.0.2"
+		"vite": "^3.0.0"
 	},
 	"bin": {
 		"svelte-kit": "svelte-kit.js"
diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js
index 34ec6f33e..c5cfe1ac5 100644
--- a/packages/kit/src/core/sync/write_types.js
+++ b/packages/kit/src/core/sync/write_types.js
@@ -139,12 +139,23 @@ function get_groups(manifest_data, routes_dir) {
 		return group;
 	}
 
-	// first, sort nodes by path length (necessary for finding the nearest layout more efficiently)...
-	const nodes = [...manifest_data.nodes].sort(
-		(n1, n2) =>
+	// first, sort nodes (necessary for finding the nearest layout more efficiently)...
+	const nodes = [...manifest_data.nodes].sort((n1, n2) => {
+		// Sort by path length first...
+		const path_length_diff =
 			/** @type {string} */ (n1.component ?? n1.shared ?? n1.server).split('/').length -
-			/** @type {string} */ (n2.component ?? n2.shared ?? n2.server).split('/').length
-	);
+			/** @type {string} */ (n2.component ?? n2.shared ?? n2.server).split('/').length;
+
+		return (
+			path_length_diff ||
+			// ...on ties, sort named layouts first
+			(path.basename(n1.component || '').includes('-')
+				? -1
+				: path.basename(n2.component || '').includes('-')
+				? 1
+				: 0)
+		);
+	});
 
 	// ...then, populate `directories` with +page/+layout files...
 	for (let i = 0; i < nodes.length; i += 1) {
@@ -713,12 +724,12 @@ export function find_nearest_layout(routes_dir, nodes, start_idx) {
 	}
 
 	let common_path = path.dirname(start_file);
-	if (match[1] === 'layout' && !name) {
+	if (match[1] === 'layout' && !match[2] && !name) {
 		// We are a default layout, so we skip the current level
 		common_path = path.dirname(common_path);
 	}
 
-	for (let i = start_idx; i >= 0; i -= 1) {
+	for (let i = start_idx - 1; i >= 0; i -= 1) {
 		const node = nodes[i];
 		const file = /** @type {string} */ (node.component || node.shared || node.server);
 
diff --git a/packages/kit/src/core/sync/write_types.spec.js b/packages/kit/src/core/sync/write_types.spec.js
index 2191e7421..4f0a85d14 100644
--- a/packages/kit/src/core/sync/write_types.spec.js
+++ b/packages/kit/src/core/sync/write_types.spec.js
@@ -160,6 +160,14 @@ test('Finds nearest layout (named)', () => {
 	});
 });
 
+test('Finds nearest named layout from layout', () => {
+	assert.equal(find_nearest_layout('src/routes', nodes, 1), {
+		key: '',
+		folder_depth_diff: 0,
+		name: ''
+	});
+});
+
 test('Finds nearest layout (recursively named)', () => {
 	assert.equal(find_nearest_layout('src/routes', nodes, 3), {
 		key: '',
diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js
index 338791b4d..9d112214a 100644
--- a/packages/kit/src/runtime/server/endpoint.js
+++ b/packages/kit/src/runtime/server/endpoint.js
@@ -1,3 +1,4 @@
+import { HttpError, Redirect } from '../../index/private.js';
 import { check_method_names, method_not_allowed } from './utils.js';
 
 /**
@@ -29,16 +30,29 @@ export async function render_endpoint(event, route) {
 		return method_not_allowed(mod, method);
 	}
 
-	const response = await handler(
-		/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
-	);
-
-	if (!(response instanceof Response)) {
-		return new Response(
-			`Invalid response from route ${event.url.pathname}: handler should return a Response object`,
-			{ status: 500 }
+	try {
+		const response = await handler(
+			/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
 		);
-	}
 
-	return response;
+		if (!(response instanceof Response)) {
+			return new Response(
+				`Invalid response from route ${event.url.pathname}: handler should return a Response object`,
+				{ status: 500 }
+			);
+		}
+
+		return response;
+	} catch (error) {
+		if (error instanceof HttpError) {
+			return new Response(error.message, { status: error.status });
+		} else if (error instanceof Redirect) {
+			return new Response(undefined, {
+				status: error.status,
+				headers: { Location: error.location }
+			});
+		} else {
+			throw error;
+		}
+	}
 }
diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js
index 625fc0539..e4a8bd422 100644
--- a/packages/kit/src/runtime/server/index.js
+++ b/packages/kit/src/runtime/server/index.js
@@ -118,6 +118,9 @@ export async function respond(request, options, state) {
 	/** @type {import('types').ResponseHeaders} */
 	const headers = {};
 
+	/** @type {string[]} */
+	const cookies = [];
+
 	/** @type {import('types').RequestEvent} */
 	const event = {
 		get clientAddress() {
@@ -141,16 +144,26 @@ export async function respond(request, options, state) {
 		setHeaders: (new_headers) => {
 			for (const key in new_headers) {
 				const lower = key.toLowerCase();
+				const value = new_headers[key];
 
-				if (lower in headers) {
-					throw new Error(`"${key}" header is already set`);
-				}
+				if (lower === 'set-cookie') {
+					const new_cookies = /** @type {string[]} */ (Array.isArray(value) ? value : [value]);
 
-				// TODO apply these headers to the response
-				headers[lower] = new_headers[key];
+					for (const cookie of new_cookies) {
+						if (cookies.includes(cookie)) {
+							throw new Error(`"${key}" header already has cookie with same value`);
+						}
 
-				if (state.prerendering && lower === 'cache-control') {
-					state.prerendering.cache = /** @type {string} */ (new_headers[key]);
+						cookies.push(cookie);
+					}
+				} else if (lower in headers) {
+					throw new Error(`"${key}" header is already set`);
+				} else {
+					headers[lower] = value;
+
+					if (state.prerendering && lower === 'cache-control') {
+						state.prerendering.cache = /** @type {string} */ (value);
+					}
 				}
 			}
 		},
@@ -254,6 +267,7 @@ export async function respond(request, options, state) {
 									return {
 										// TODO return `uses`, so we can reuse server data effectively
 										data: await load_server_data({
+											dev: options.dev,
 											event,
 											node,
 											parent: async () => {
@@ -312,19 +326,19 @@ export async function respond(request, options, state) {
 								: await render_page(event, route, options, state, resolve_opts);
 					}
 
-					for (const key in headers) {
-						const value = headers[key];
-						if (key === 'set-cookie') {
-							for (const cookie of Array.isArray(value) ? value : [value]) {
-								response.headers.append(key, /** @type {string} */ (cookie));
-							}
-						} else if (!is_data_request) {
-							// we only want to set cookies on __data.json requests, we don't
-							// want to cache stuff erroneously etc
+					if (!is_data_request) {
+						// we only want to set cookies on __data.json requests, we don't
+						// want to cache stuff erroneously etc
+						for (const key in headers) {
+							const value = headers[key];
 							response.headers.set(key, /** @type {string} */ (value));
 						}
 					}
 
+					for (const cookie of cookies) {
+						response.headers.append('set-cookie', cookie);
+					}
+
 					// respond with 304 if etag matches
 					if (response.status === 200 && response.headers.has('etag')) {
 						let if_none_match_value = request.headers.get('if-none-match');
diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js
index 9a1769bb0..07ccefbe7 100644
--- a/packages/kit/src/runtime/server/page/index.js
+++ b/packages/kit/src/runtime/server/page/index.js
@@ -36,7 +36,11 @@ export async function render_page(event, route, options, state, resolve_opts) {
 		'application/json'
 	]);
 
-	if (accept === 'application/json') {
+	if (
+		accept === 'application/json' &&
+		event.request.method !== 'GET' &&
+		event.request.method !== 'HEAD'
+	) {
 		const node = await options.manifest._.nodes[route.leaf]();
 		if (node.server) {
 			return handle_json_request(event, options, node.server);
@@ -95,6 +99,9 @@ export async function render_page(event, route, options, state, resolve_opts) {
 			}
 		}
 
+		const should_prerender_data = nodes.some((node) => node?.server);
+		const data_pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`;
+
 		if (!resolve_opts.ssr) {
 			return await render_response({
 				branch: [],
@@ -114,7 +121,8 @@ export async function render_page(event, route, options, state, resolve_opts) {
 			});
 		}
 
-		const should_prerender = leaf_node.shared?.prerender ?? options.prerender.default;
+		const should_prerender =
+			leaf_node.shared?.prerender ?? leaf_node.server?.prerender ?? options.prerender.default;
 		if (should_prerender) {
 			const mod = leaf_node.server;
 			if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) {
@@ -153,6 +161,7 @@ export async function render_page(event, route, options, state, resolve_opts) {
 					}
 
 					return await load_server_data({
+						dev: options.dev,
 						event,
 						node,
 						parent: async () => {
@@ -214,6 +223,16 @@ export async function render_page(event, route, options, state, resolve_opts) {
 					const error = normalize_error(e);
 
 					if (error instanceof Redirect) {
+						if (state.prerendering && should_prerender_data) {
+							state.prerendering.dependencies.set(data_pathname, {
+								response: new Response(undefined),
+								body: JSON.stringify({
+									type: 'redirect',
+									location: error.location
+								})
+							});
+						}
+
 						return redirect_response(error.status, error.location);
 					}
 
@@ -267,19 +286,14 @@ export async function render_page(event, route, options, state, resolve_opts) {
 			}
 		}
 
-		// generate __data.json files when prerendering
-		if (state.prerendering && nodes.some((node) => node?.server)) {
-			const pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`;
-
-			const dependency = {
+		if (state.prerendering && should_prerender_data) {
+			state.prerendering.dependencies.set(data_pathname, {
 				response: new Response(undefined),
 				body: JSON.stringify({
 					type: 'data',
 					nodes: branch.map((branch_node) => ({ data: branch_node?.server_data }))
 				})
-			};
-
-			state.prerendering.dependencies.set(pathname, dependency);
+			});
 		}
 
 		// TODO use validation_errors
@@ -337,8 +351,8 @@ function get_page_config(leaf, options) {
  * @param {import('types').SSRNode['server']} mod
  */
 export async function handle_json_request(event, options, mod) {
-	const method = /** @type {import('types').HttpMethod} */ (event.request.method);
-	const handler = mod[method === 'HEAD' || method === 'GET' ? 'load' : method];
+	const method = /** @type {'POST' | 'PUT' | 'PATCH' | 'DELETE'} */ (event.request.method);
+	const handler = mod[method];
 
 	if (!handler) {
 		return method_not_allowed(mod, method);
@@ -348,14 +362,6 @@ export async function handle_json_request(event, options, mod) {
 		// @ts-ignore
 		const result = await handler.call(null, event);
 
-		if (method === 'HEAD') {
-			return new Response();
-		}
-
-		if (method === 'GET') {
-			return json(result);
-		}
-
 		if (result?.errors) {
 			// @ts-ignore
 			return json({ errors: result.errors }, { status: result.status || 400 });
diff --git a/packages/kit/src/runtime/server/page/load_data.js b/packages/kit/src/runtime/server/page/load_data.js
index ef73137fc..2562356fd 100644
--- a/packages/kit/src/runtime/server/page/load_data.js
+++ b/packages/kit/src/runtime/server/page/load_data.js
@@ -3,12 +3,13 @@ import { LoadURL, PrerenderingURL } from '../../../utils/url.js';
 /**
  * Calls the user's `load` function.
  * @param {{
+ *   dev: boolean;
  *   event: import('types').RequestEvent;
  *   node: import('types').SSRNode | undefined;
  *   parent: () => Promise<Record<string, any>>;
  * }} opts
  */
-export async function load_server_data({ event, node, parent }) {
+export async function load_server_data({ dev, event, node, parent }) {
 	if (!node?.server) return null;
 
 	const server_data = await node.server.load?.call(null, {
@@ -27,7 +28,13 @@ export async function load_server_data({ event, node, parent }) {
 		url: event.url
 	});
 
-	return server_data ? unwrap_promises(server_data) : null;
+	const result = server_data ? await unwrap_promises(server_data) : null;
+
+	if (dev) {
+		check_serializability(result, /** @type {string} */ (node.server_id), 'data');
+	}
+
+	return result;
 }
 
 /**
@@ -79,3 +86,42 @@ async function unwrap_promises(object) {
 
 	return unwrapped;
 }
+
+/**
+ * Check that the data can safely be serialized to JSON
+ * @param {any} value
+ * @param {string} id
+ * @param {string} path
+ */
+function check_serializability(value, id, path) {
+	const type = typeof value;
+
+	if (type === 'string' || type === 'boolean' || type === 'number' || type === 'undefined') {
+		// primitives are fine
+		return;
+	}
+
+	if (type === 'object') {
+		// nulls are fine...
+		if (!value) return;
+
+		// ...so are plain arrays...
+		if (Array.isArray(value)) {
+			value.forEach((child, i) => {
+				check_serializability(child, id, `${path}[${i}]`);
+			});
+			return;
+		}
+
+		// ...and objects
+		const tag = Object.prototype.toString.call(value);
+		if (tag === '[object Object]') {
+			for (const key in value) {
+				check_serializability(value[key], id, `${path}.${key}`);
+			}
+			return;
+		}
+	}
+
+	throw new Error(`${path} returned from 'load' in ${id} cannot be serialized as JSON`);
+}
diff --git a/packages/kit/src/runtime/server/page/respond_with_error.js b/packages/kit/src/runtime/server/page/respond_with_error.js
index 13e285e09..1802881f2 100644
--- a/packages/kit/src/runtime/server/page/respond_with_error.js
+++ b/packages/kit/src/runtime/server/page/respond_with_error.js
@@ -35,6 +35,7 @@ export async function respond_with_error({ event, options, state, status, error,
 			const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
 
 			const server_data_promise = load_server_data({
+				dev: options.dev,
 				event,
 				node: default_layout,
 				parent: async () => ({})
diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js
index 7414463b8..cb126b83c 100644
--- a/packages/kit/src/vite/build/build_server.js
+++ b/packages/kit/src/vite/build/build_server.js
@@ -4,7 +4,7 @@ import { mkdirp, posixify } from '../../utils/filesystem.js';
 import { get_vite_config, merge_vite_configs, resolve_entry } from '../utils.js';
 import { load_template } from '../../core/config/index.js';
 import { runtime_directory } from '../../core/utils.js';
-import { create_build, find_deps, get_default_config, is_http_method } from './utils.js';
+import { create_build, find_deps, get_default_build_config, is_http_method } from './utils.js';
 import { s } from '../../utils/misc.js';
 
 /**
@@ -205,7 +205,7 @@ export async function build_server(options, client) {
 	);
 
 	const merged_config = merge_vite_configs(
-		get_default_config({ config, input, ssr: true, outDir: `${output_dir}/server` }),
+		get_default_build_config({ config, input, ssr: true, outDir: `${output_dir}/server` }),
 		await get_vite_config(vite_config, vite_config_env)
 	);
 
diff --git a/packages/kit/src/vite/build/utils.js b/packages/kit/src/vite/build/utils.js
index a1b3da9f8..14cd04a36 100644
--- a/packages/kit/src/vite/build/utils.js
+++ b/packages/kit/src/vite/build/utils.js
@@ -87,7 +87,7 @@ export function find_deps(manifest, entry, add_dynamic_css) {
  * }} options
  * @return {import('vite').UserConfig}
  */
-export const get_default_config = function ({ config, input, ssr, outDir }) {
+export function get_default_build_config({ config, input, ssr, outDir }) {
 	const prefix = `${config.kit.appDir}/immutable`;
 
 	return {
@@ -135,7 +135,7 @@ export const get_default_config = function ({ config, input, ssr, outDir }) {
 			}
 		}
 	};
-};
+}
 
 /**
  * @param {import('types').ValidatedKitConfig} config
diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js
index c67f11fe2..a3dfa4273 100644
--- a/packages/kit/src/vite/dev/index.js
+++ b/packages/kit/src/vite/dev/index.js
@@ -119,6 +119,7 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) {
 						if (node.server) {
 							const { module } = await resolve(node.server);
 							result.server = module;
+							result.server_id = node.server;
 						}
 
 						// in dev we inline all styles to avoid FOUC. this gets populated lazily so that
diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js
index a5ef0f32e..33a8f324b 100644
--- a/packages/kit/src/vite/index.js
+++ b/packages/kit/src/vite/index.js
@@ -12,7 +12,7 @@ import { load_config } from '../core/config/index.js';
 import { dev } from './dev/index.js';
 import { generate_manifest } from '../core/generate_manifest/index.js';
 import { runtime_directory, logger } from '../core/utils.js';
-import { find_deps, get_default_config as get_default_build_config } from './build/utils.js';
+import { find_deps, get_default_build_config } from './build/utils.js';
 import { preview } from './preview/index.js';
 import { get_aliases, resolve_entry, prevent_illegal_rollup_imports, get_env } from './utils.js';
 import { fileURLToPath } from 'node:url';
@@ -266,6 +266,13 @@ function kit() {
 							`${posixify(svelte_config.kit.outDir)}/!(generated)`
 						]
 					}
+				},
+				ssr: {
+					// Without this, Vite will treat `@sveltejs/kit` as noExternal if it's
+					// a linked dependency, and that causes modules to be imported twice
+					// under different IDs, which breaks a bunch of stuff
+					// https://github.com/vitejs/vite/pull/9296
+					external: ['@sveltejs/kit']
 				}
 			};
 
diff --git a/packages/kit/src/vite/utils.js b/packages/kit/src/vite/utils.js
index 6c62d2636..4e9ae7543 100644
--- a/packages/kit/src/vite/utils.js
+++ b/packages/kit/src/vite/utils.js
@@ -2,6 +2,7 @@ import fs from 'fs';
 import path from 'path';
 import { loadConfigFromFile, loadEnv, normalizePath } from 'vite';
 import { runtime_directory } from '../core/utils.js';
+import { posixify } from '../utils/filesystem.js';
 
 /**
  * @param {import('vite').ResolvedConfig} config
@@ -113,18 +114,22 @@ export function get_aliases(config) {
 	];
 
 	for (let [key, value] of Object.entries(config.alias)) {
+		value = posixify(value);
 		if (value.endsWith('/*')) {
 			value = value.slice(0, -2);
 		}
 		if (key.endsWith('/*')) {
 			// Doing just `{ find: key.slice(0, -2) ,..}` would mean `import .. from "key"` would also be matched, which we don't want
 			alias.push({
-				find: new RegExp(`^${key.slice(0, -2)}\\/(.+)$`),
+				find: new RegExp(`^${escape_for_regexp(key.slice(0, -2))}\\/(.+)$`),
 				replacement: `${path.resolve(value)}/$1`
 			});
 		} else if (key + '/*' in config.alias) {
 			// key and key/* both exist -> the replacement for key needs to happen _only_ on import .. from "key"
-			alias.push({ find: new RegExp(`^${key}$`), replacement: path.resolve(value) });
+			alias.push({
+				find: new RegExp(`^${escape_for_regexp(key)}$`),
+				replacement: path.resolve(value)
+			});
 		} else {
 			alias.push({ find: key, replacement: path.resolve(value) });
 		}
@@ -133,6 +138,13 @@ export function get_aliases(config) {
 	return alias;
 }
 
+/**
+ * @param {string} str
+ */
+function escape_for_regexp(str) {
+	return str.replace(/[.*+?^${}()|[\]\\]/g, (match) => '\\' + match);
+}
+
 /**
  * Given an entry point like [cwd]/src/hooks, returns a filename like [cwd]/src/hooks.js or [cwd]/src/hooks/index.js
  * @param {string} entry
diff --git a/packages/kit/src/vite/utils.spec.js b/packages/kit/src/vite/utils.spec.js
index a566434f5..6fa338f64 100644
--- a/packages/kit/src/vite/utils.spec.js
+++ b/packages/kit/src/vite/utils.spec.js
@@ -207,7 +207,15 @@ test('merge resolve.alias', () => {
 
 test('transform kit.alias to resolve.alias', () => {
 	const config = validate_config({
-		kit: { alias: { simpleKey: 'simple/value', key: 'value', 'key/*': 'value/*' } }
+		kit: {
+			alias: {
+				simpleKey: 'simple/value',
+				key: 'value',
+				'key/*': 'value/*',
+				$regexChar: 'windows\\path',
+				'$regexChar/*': 'windows\\path\\*'
+			}
+		}
 	});
 
 	const transformed = get_aliases(config.kit).map((entry) => {
@@ -227,7 +235,9 @@ test('transform kit.alias to resolve.alias', () => {
 		{ find: '$lib', replacement: 'src/lib' },
 		{ find: 'simpleKey', replacement: 'simple/value' },
 		{ find: /^key$/.toString(), replacement: 'value' },
-		{ find: /^key\/(.+)$/.toString(), replacement: 'value/$1' }
+		{ find: /^key\/(.+)$/.toString(), replacement: 'value/$1' },
+		{ find: /^\$regexChar$/.toString(), replacement: 'windows/path' },
+		{ find: /^\$regexChar\/(.+)$/.toString(), replacement: 'windows/path/$1' }
 	]);
 });
 
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index 4e63561ca..294330e19 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -106,7 +106,6 @@ export interface Config {
 	compilerOptions?: CompileOptions;
 	extensions?: string[];
 	kit?: KitConfig;
-	preprocess?: any;
 	package?: {
 		source?: string;
 		dir?: string;
@@ -114,6 +113,7 @@ export interface Config {
 		exports?: (filepath: string) => boolean;
 		files?: (filepath: string) => boolean;
 	};
+	preprocess?: any;
 	[key: string]: any;
 }
 
@@ -312,7 +312,7 @@ export interface Action<
 	Params extends Partial<Record<string, string>> = Partial<Record<string, string>>
 > {
 	(event: RequestEvent<Params>): MaybePromise<
-		| { status?: number; errors: Record<string, string>; location?: never }
+		| { status?: number; errors: Record<string, any>; location?: never }
 		| { status?: never; errors?: never; location: string }
 		| void
 	>;
diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts
index 9738ab5a0..b3677941b 100644
--- a/packages/kit/types/internal.d.ts
+++ b/packages/kit/types/internal.d.ts
@@ -211,17 +211,19 @@ export interface SSRNode {
 		hydrate?: boolean;
 		prerender?: boolean;
 		router?: boolean;
-		ssr?: boolean;
 	};
 
 	server: {
 		load?: ServerLoad;
-		HEAD?: ServerLoad;
+		prerender?: boolean;
 		POST?: Action;
 		PATCH?: Action;
 		PUT?: Action;
 		DELETE?: Action;
 	};
+
+	// store this in dev so we can print serialization errors
+	server_id?: string;
 }
 
 export type SSRNodeLoader = () => Promise<SSRNode>;
diff --git a/packages/package/src/types.d.ts b/packages/package/src/types.d.ts
index f08999f8c..2350e43f3 100644
--- a/packages/package/src/types.d.ts
+++ b/packages/package/src/types.d.ts
@@ -18,10 +18,10 @@ export type RecursiveRequired<T> = {
 };
 
 export interface ValidatedConfig {
-	preprocess?: any;
 	extensions: string[];
-	package: RecursiveRequired<PackageConfig>;
 	kit?: any;
+	package: RecursiveRequired<PackageConfig>;
+	preprocess?: any;
 }
 
 export type Validator<T = any> = (input: T, keypath: string) => T;
diff --git a/packages/package/types/index.d.ts b/packages/package/types/index.d.ts
index 07670d36b..0e1ba39b6 100644
--- a/packages/package/types/index.d.ts
+++ b/packages/package/types/index.d.ts
@@ -29,6 +29,6 @@ export interface Config {
 	compilerOptions?: CompileOptions;
 	extensions?: string[];
 	kit?: any;
-	preprocess?: any;
 	package?: PackageConfig;
+	preprocess?: any;
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3b958fc87..141b10e1d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -55,7 +55,7 @@ importers:
       worktop: 0.8.0-next.14
     dependencies:
       '@cloudflare/workers-types': 3.14.0
-      esbuild: 0.14.48
+      esbuild: 0.14.54
       worktop: 0.8.0-next.14
     devDependencies:
       '@types/node': 16.11.42
@@ -73,7 +73,7 @@ importers:
     dependencies:
       '@cloudflare/workers-types': 3.14.0
       '@iarna/toml': 2.2.5
-      esbuild: 0.14.48
+      esbuild: 0.14.54
     devDependencies:
       '@cloudflare/kv-asset-handler': 0.2.0
       '@types/node': 16.11.42
@@ -98,7 +98,7 @@ importers:
       uvu: ^0.5.3
     dependencies:
       '@iarna/toml': 2.2.5
-      esbuild: 0.14.48
+      esbuild: 0.14.54
       set-cookie-parser: 2.5.0
       tiny-glob: 0.2.9
     devDependencies:
@@ -149,7 +149,7 @@ importers:
       svelte: ^3.48.0
       typescript: ^4.7.4
       uvu: ^0.5.3
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../kit
       '@types/node': 16.11.42
@@ -158,17 +158,17 @@ importers:
       svelte: 3.48.0
       typescript: 4.7.4
       uvu: 0.5.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/adapter-static/test/apps/prerendered:
     specifiers:
       '@sveltejs/kit': workspace:*
       svelte: ^3.48.0
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../../../kit
       svelte: 3.48.0
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/adapter-static/test/apps/spa:
     specifiers:
@@ -176,13 +176,13 @@ importers:
       '@sveltejs/kit': workspace:*
       sirv-cli: ^2.0.2
       svelte: ^3.48.0
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/adapter-node': link:../../../../adapter-node
       '@sveltejs/kit': link:../../../../kit
       sirv-cli: 2.0.2
       svelte: 3.48.0
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/adapter-vercel:
     specifiers:
@@ -193,7 +193,7 @@ importers:
       typescript: ^4.7.4
     dependencies:
       '@vercel/nft': 0.21.0
-      esbuild: 0.14.48
+      esbuild: 0.14.54
     devDependencies:
       '@sveltejs/kit': link:../kit
       '@types/node': 16.11.42
@@ -294,9 +294,9 @@ importers:
       typescript: ^4.7.4
       undici: ^5.8.1
       uvu: ^0.5.3
-      vite: 3.0.2
+      vite: ^3.0.8
     dependencies:
-      '@sveltejs/vite-plugin-svelte': 1.0.1_svelte@3.48.0+vite@3.0.2
+      '@sveltejs/vite-plugin-svelte': 1.0.1_svelte@3.48.0+vite@3.0.8
       cookie: 0.5.0
       devalue: 2.0.1
       kleur: 4.1.5
@@ -323,7 +323,7 @@ importers:
       svelte-preprocess: 4.10.7_lvfi2wesz6u4l5rfbnetbucfmm
       typescript: 4.7.4
       uvu: 0.5.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/apps/amp:
     specifiers:
@@ -334,7 +334,7 @@ importers:
       svelte: ^3.48.0
       svelte-check: ^2.7.1
       typescript: ^4.7.4
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/amp': link:../../../../amp
       '@sveltejs/kit': link:../../..
@@ -343,7 +343,7 @@ importers:
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/apps/basics:
     specifiers:
@@ -353,7 +353,7 @@ importers:
       svelte: ^3.48.0
       svelte-check: ^2.7.1
       typescript: ^4.7.4
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../..
       cross-env: 7.0.3
@@ -361,7 +361,7 @@ importers:
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/apps/options:
     specifiers:
@@ -370,14 +370,14 @@ importers:
       svelte: ^3.48.0
       svelte-check: ^2.7.1
       typescript: ^4.7.4
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../..
       cross-env: 7.0.3
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/apps/options-2:
     specifiers:
@@ -387,7 +387,7 @@ importers:
       svelte: ^3.48.0
       svelte-check: ^2.7.1
       typescript: ^4.7.4
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/adapter-node': link:../../../../adapter-node
       '@sveltejs/kit': link:../../..
@@ -395,7 +395,7 @@ importers:
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/apps/writes:
     specifiers:
@@ -405,7 +405,7 @@ importers:
       svelte: ^3.48.0
       svelte-check: ^2.7.1
       typescript: ^4.7.4
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../..
       cross-env: 7.0.3
@@ -413,7 +413,7 @@ importers:
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/prerendering/basics:
     specifiers:
@@ -422,14 +422,14 @@ importers:
       svelte-check: ^2.7.1
       typescript: ^4.7.4
       uvu: ^0.5.3
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../..
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
       uvu: 0.5.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/prerendering/fallback:
     specifiers:
@@ -438,14 +438,14 @@ importers:
       svelte-check: ^2.7.1
       typescript: ^4.7.4
       uvu: ^0.5.3
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../..
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
       uvu: 0.5.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/prerendering/options:
     specifiers:
@@ -454,14 +454,14 @@ importers:
       svelte-check: ^2.7.1
       typescript: ^4.7.4
       uvu: ^0.5.3
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../..
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
       uvu: 0.5.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/prerendering/paths-base:
     specifiers:
@@ -470,14 +470,14 @@ importers:
       svelte-check: ^2.7.1
       typescript: ^4.7.4
       uvu: ^0.5.3
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../..
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
       uvu: 0.5.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/kit/test/prerendering/trailing-slash:
     specifiers:
@@ -486,14 +486,14 @@ importers:
       svelte-check: ^2.7.1
       typescript: ^4.7.4
       uvu: ^0.5.4
-      vite: 3.0.2
+      vite: ^3.0.8
     devDependencies:
       '@sveltejs/kit': link:../../..
       svelte: 3.48.0
       svelte-check: 2.8.0_svelte@3.48.0
       typescript: 4.7.4
       uvu: 0.5.4
-      vite: 3.0.2
+      vite: 3.0.8
 
   packages/migrate:
     specifiers:
@@ -554,7 +554,7 @@ importers:
       shiki-twoslash: ^3.0.2
       svelte: ^3.48.0
       typescript: ^4.7.4
-      vite: ^3.0.4
+      vite: ^3.0.8
       vite-imagetools: ^4.0.3
     devDependencies:
       '@sveltejs/adapter-auto': link:../../packages/adapter-auto
@@ -809,6 +809,14 @@ packages:
     resolution: {integrity: sha512-F6lZuzu/vNl9AxOq1U3x2UCi68bcE1O59cKGQ5ntRh3lGQfd3vers6NUag02T7P+qFCHaAp78QN6fRmq12Y3iA==}
     dev: false
 
+  /@esbuild/linux-loong64/0.14.54:
+    resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
   /@fontsource/fira-mono/4.5.8:
     resolution: {integrity: sha512-sFuSPB/Km8B1fy3CH0NqO5Nb4GmVMzp3XFaw6MwK293xhm3OnB68QJawwTTjLewcrS78wOTAhTUB058qxurJoQ==}
     dev: false
@@ -993,7 +1001,7 @@ packages:
       golden-fleece: 1.0.9
     dev: true
 
-  /@sveltejs/vite-plugin-svelte/1.0.1_svelte@3.48.0+vite@3.0.2:
+  /@sveltejs/vite-plugin-svelte/1.0.1_svelte@3.48.0+vite@3.0.8:
     resolution: {integrity: sha512-PorCgUounn0VXcpeJu+hOweZODKmGuLHsLomwqSj+p26IwjjGffmYQfVHtiTWq+NqaUuuHWWG7vPge6UFw4Aeg==}
     engines: {node: ^14.18.0 || >= 16}
     peerDependencies:
@@ -1011,7 +1019,7 @@ packages:
       magic-string: 0.26.2
       svelte: 3.48.0
       svelte-hmr: 0.14.12_svelte@3.48.0
-      vite: 3.0.2
+      vite: 3.0.8
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -1813,192 +1821,193 @@ packages:
     resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==}
     dev: true
 
-  /esbuild-android-64/0.14.48:
-    resolution: {integrity: sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==}
+  /esbuild-android-64/0.14.54:
+    resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /esbuild-android-arm64/0.14.48:
-    resolution: {integrity: sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==}
+  /esbuild-android-arm64/0.14.54:
+    resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /esbuild-darwin-64/0.14.48:
-    resolution: {integrity: sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==}
+  /esbuild-darwin-64/0.14.54:
+    resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /esbuild-darwin-arm64/0.14.48:
-    resolution: {integrity: sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==}
+  /esbuild-darwin-arm64/0.14.54:
+    resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /esbuild-freebsd-64/0.14.48:
-    resolution: {integrity: sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==}
+  /esbuild-freebsd-64/0.14.54:
+    resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [freebsd]
     requiresBuild: true
     optional: true
 
-  /esbuild-freebsd-arm64/0.14.48:
-    resolution: {integrity: sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==}
+  /esbuild-freebsd-arm64/0.14.54:
+    resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [freebsd]
     requiresBuild: true
     optional: true
 
-  /esbuild-linux-32/0.14.48:
-    resolution: {integrity: sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==}
+  /esbuild-linux-32/0.14.54:
+    resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /esbuild-linux-64/0.14.48:
-    resolution: {integrity: sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==}
+  /esbuild-linux-64/0.14.54:
+    resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /esbuild-linux-arm/0.14.48:
-    resolution: {integrity: sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==}
+  /esbuild-linux-arm/0.14.54:
+    resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /esbuild-linux-arm64/0.14.48:
-    resolution: {integrity: sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==}
+  /esbuild-linux-arm64/0.14.54:
+    resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /esbuild-linux-mips64le/0.14.48:
-    resolution: {integrity: sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==}
+  /esbuild-linux-mips64le/0.14.54:
+    resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==}
     engines: {node: '>=12'}
     cpu: [mips64el]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /esbuild-linux-ppc64le/0.14.48:
-    resolution: {integrity: sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==}
+  /esbuild-linux-ppc64le/0.14.54:
+    resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /esbuild-linux-riscv64/0.14.48:
-    resolution: {integrity: sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==}
+  /esbuild-linux-riscv64/0.14.54:
+    resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==}
     engines: {node: '>=12'}
     cpu: [riscv64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /esbuild-linux-s390x/0.14.48:
-    resolution: {integrity: sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==}
+  /esbuild-linux-s390x/0.14.54:
+    resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==}
     engines: {node: '>=12'}
     cpu: [s390x]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /esbuild-netbsd-64/0.14.48:
-    resolution: {integrity: sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==}
+  /esbuild-netbsd-64/0.14.54:
+    resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [netbsd]
     requiresBuild: true
     optional: true
 
-  /esbuild-openbsd-64/0.14.48:
-    resolution: {integrity: sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==}
+  /esbuild-openbsd-64/0.14.54:
+    resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [openbsd]
     requiresBuild: true
     optional: true
 
-  /esbuild-sunos-64/0.14.48:
-    resolution: {integrity: sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==}
+  /esbuild-sunos-64/0.14.54:
+    resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [sunos]
     requiresBuild: true
     optional: true
 
-  /esbuild-windows-32/0.14.48:
-    resolution: {integrity: sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==}
+  /esbuild-windows-32/0.14.54:
+    resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /esbuild-windows-64/0.14.48:
-    resolution: {integrity: sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==}
+  /esbuild-windows-64/0.14.54:
+    resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /esbuild-windows-arm64/0.14.48:
-    resolution: {integrity: sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==}
+  /esbuild-windows-arm64/0.14.54:
+    resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /esbuild/0.14.48:
-    resolution: {integrity: sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==}
+  /esbuild/0.14.54:
+    resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==}
     engines: {node: '>=12'}
     hasBin: true
     requiresBuild: true
     optionalDependencies:
-      esbuild-android-64: 0.14.48
-      esbuild-android-arm64: 0.14.48
-      esbuild-darwin-64: 0.14.48
-      esbuild-darwin-arm64: 0.14.48
-      esbuild-freebsd-64: 0.14.48
-      esbuild-freebsd-arm64: 0.14.48
-      esbuild-linux-32: 0.14.48
-      esbuild-linux-64: 0.14.48
-      esbuild-linux-arm: 0.14.48
-      esbuild-linux-arm64: 0.14.48
-      esbuild-linux-mips64le: 0.14.48
-      esbuild-linux-ppc64le: 0.14.48
-      esbuild-linux-riscv64: 0.14.48
-      esbuild-linux-s390x: 0.14.48
-      esbuild-netbsd-64: 0.14.48
-      esbuild-openbsd-64: 0.14.48
-      esbuild-sunos-64: 0.14.48
-      esbuild-windows-32: 0.14.48
-      esbuild-windows-64: 0.14.48
-      esbuild-windows-arm64: 0.14.48
+      '@esbuild/linux-loong64': 0.14.54
+      esbuild-android-64: 0.14.54
+      esbuild-android-arm64: 0.14.54
+      esbuild-darwin-64: 0.14.54
+      esbuild-darwin-arm64: 0.14.54
+      esbuild-freebsd-64: 0.14.54
+      esbuild-freebsd-arm64: 0.14.54
+      esbuild-linux-32: 0.14.54
+      esbuild-linux-64: 0.14.54
+      esbuild-linux-arm: 0.14.54
+      esbuild-linux-arm64: 0.14.54
+      esbuild-linux-mips64le: 0.14.54
+      esbuild-linux-ppc64le: 0.14.54
+      esbuild-linux-riscv64: 0.14.54
+      esbuild-linux-s390x: 0.14.54
+      esbuild-netbsd-64: 0.14.54
+      esbuild-openbsd-64: 0.14.54
+      esbuild-sunos-64: 0.14.54
+      esbuild-windows-32: 0.14.54
+      esbuild-windows-64: 0.14.54
+      esbuild-windows-arm64: 0.14.54
 
   /escalade/3.1.1:
     resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
@@ -4468,32 +4477,6 @@ packages:
       magic-string: 0.26.2
     dev: true
 
-  /vite/3.0.2:
-    resolution: {integrity: sha512-TAqydxW/w0U5AoL5AsD9DApTvGb2iNbGs3sN4u2VdT1GFkQVUfgUldt+t08TZgi23uIauh1TUOQJALduo9GXqw==}
-    engines: {node: ^14.18.0 || >=16.0.0}
-    hasBin: true
-    peerDependencies:
-      less: '*'
-      sass: '*'
-      stylus: '*'
-      terser: ^5.4.0
-    peerDependenciesMeta:
-      less:
-        optional: true
-      sass:
-        optional: true
-      stylus:
-        optional: true
-      terser:
-        optional: true
-    dependencies:
-      esbuild: 0.14.48
-      postcss: 8.4.16
-      resolve: 1.22.1
-      rollup: 2.75.7
-    optionalDependencies:
-      fsevents: 2.3.2
-
   /vite/3.0.8:
     resolution: {integrity: sha512-AOZ4eN7mrkJiOLuw8IA7piS4IdOQyQCA81GxGsAQvAZzMRi9ZwGB3TOaYsj4uLAWK46T5L4AfQ6InNGlxX30IQ==}
     engines: {node: ^14.18.0 || >=16.0.0}
@@ -4513,13 +4496,12 @@ packages:
       terser:
         optional: true
     dependencies:
-      esbuild: 0.14.48
+      esbuild: 0.14.54
       postcss: 8.4.16
       resolve: 1.22.1
       rollup: 2.75.7
     optionalDependencies:
       fsevents: 2.3.2
-    dev: true
 
   /vscode-oniguruma/1.6.2:
     resolution: {integrity: sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA==}
diff --git a/sites/kit.svelte.dev/package.json b/sites/kit.svelte.dev/package.json
index 5be7b5e44..eeeb76b29 100644
--- a/sites/kit.svelte.dev/package.json
+++ b/sites/kit.svelte.dev/package.json
@@ -21,7 +21,7 @@
 		"shiki-twoslash": "^3.0.2",
 		"svelte": "^3.48.0",
 		"typescript": "^4.7.4",
-		"vite": "^3.0.4",
+		"vite": "^3.0.8",
 		"vite-imagetools": "^4.0.3"
 	},
 	"type": "module"


Test patch:
diff --git a/packages/adapter-static/test/apps/prerendered/package.json b/packages/adapter-static/test/apps/prerendered/package.json
index 3d8844d99..b38399527 100644
--- a/packages/adapter-static/test/apps/prerendered/package.json
+++ b/packages/adapter-static/test/apps/prerendered/package.json
@@ -10,7 +10,7 @@
 	"devDependencies": {
 		"@sveltejs/kit": "workspace:*",
 		"svelte": "^3.48.0",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/adapter-static/test/apps/spa/package.json b/packages/adapter-static/test/apps/spa/package.json
index 585a543a2..a3144238b 100644
--- a/packages/adapter-static/test/apps/spa/package.json
+++ b/packages/adapter-static/test/apps/spa/package.json
@@ -12,7 +12,7 @@
 		"@sveltejs/kit": "workspace:*",
 		"sirv-cli": "^2.0.2",
 		"svelte": "^3.48.0",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/apps/amp/package.json b/packages/kit/test/apps/amp/package.json
index fdc85fc83..1576cdbfc 100644
--- a/packages/kit/test/apps/amp/package.json
+++ b/packages/kit/test/apps/amp/package.json
@@ -19,7 +19,7 @@
 		"svelte": "^3.48.0",
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/apps/basics/package.json b/packages/kit/test/apps/basics/package.json
index 567a8a1be..a58156c56 100644
--- a/packages/kit/test/apps/basics/package.json
+++ b/packages/kit/test/apps/basics/package.json
@@ -18,7 +18,7 @@
 		"svelte": "^3.48.0",
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-error/+server.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-error/+server.js
new file mode 100644
index 000000000..14c9aec1c
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-error/+server.js
@@ -0,0 +1,5 @@
+import { error } from '@sveltejs/kit';
+
+export function GET() {
+	throw error(401, 'You shall not pass');
+}
diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-redirect/+server.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-redirect/+server.js
new file mode 100644
index 000000000..26300742e
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-redirect/+server.js
@@ -0,0 +1,5 @@
+import { redirect } from '@sveltejs/kit';
+
+export function GET() {
+	throw redirect(302, '/');
+}
diff --git a/packages/kit/test/apps/basics/src/routes/headers/set-cookie/+layout.server.js b/packages/kit/test/apps/basics/src/routes/headers/set-cookie/+layout.server.js
new file mode 100644
index 000000000..21fd24121
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/headers/set-cookie/+layout.server.js
@@ -0,0 +1,5 @@
+export function load({ setHeaders }) {
+	setHeaders({
+		'set-cookie': 'cookie1=value1'
+	});
+}
diff --git a/packages/kit/test/apps/basics/src/routes/headers/set-cookie/+layout.svelte b/packages/kit/test/apps/basics/src/routes/headers/set-cookie/+layout.svelte
new file mode 100644
index 000000000..0385342ce
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/headers/set-cookie/+layout.svelte
@@ -0,0 +1,1 @@
+<slot />
\ No newline at end of file
diff --git a/packages/kit/test/apps/basics/src/routes/headers/set-cookie/sub/+page.server.js b/packages/kit/test/apps/basics/src/routes/headers/set-cookie/sub/+page.server.js
new file mode 100644
index 000000000..1d7acff07
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/headers/set-cookie/sub/+page.server.js
@@ -0,0 +1,5 @@
+export function load({ setHeaders }) {
+	setHeaders({
+		'set-cookie': 'cookie2=value2'
+	});
+}
diff --git a/packages/kit/test/apps/basics/src/routes/headers/set-cookie/sub/+page.svelte b/packages/kit/test/apps/basics/src/routes/headers/set-cookie/sub/+page.svelte
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/+page.svelte
index 3d37d71a7..27884f041 100644
--- a/packages/kit/test/apps/basics/src/routes/shadowed/+page.svelte
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/+page.svelte
@@ -5,6 +5,7 @@
 <a href="/shadowed/no-get">no-get</a>
 <a href="/shadowed/dynamic/foo">dynamic/foo</a>
 <a href="/shadowed/missing-get">missing-get</a>
+<a href="/shadowed/serialization">serialization</a>
 
 <form action="/shadowed/redirect-post" method="post">
 	<button type="submit" id="redirect-post">redirect</button>
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/serialization/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/serialization/+page.server.js
new file mode 100644
index 000000000..ceb16a887
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/serialization/+page.server.js
@@ -0,0 +1,5 @@
+export function load() {
+	return {
+		regex: /nope/
+	};
+}
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/serialization/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/serialization/+page.svelte
new file mode 100644
index 000000000..a5a2d66e7
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/serialization/+page.svelte
@@ -0,0 +1,6 @@
+<script>
+	/** @type {import('./$types').PageData} */
+	export let data;
+</script>
+
+<h1>{data.regex.test('nope')}</h1>
diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js
index 45615bcb5..1cafac3ba 100644
--- a/packages/kit/test/apps/basics/test/server.test.js
+++ b/packages/kit/test/apps/basics/test/server.test.js
@@ -124,40 +124,6 @@ test.describe('Errors', () => {
 		expect(await response.text()).toMatch('thisvariableisnotdefined is not defined');
 	});
 
-	test('page endpoint thrown error respects `accept: application/json`', async ({ request }) => {
-		const response = await request.get('/errors/page-endpoint/get-implicit', {
-			headers: {
-				accept: 'application/json'
-			}
-		});
-
-		const { message, name, stack, fancy } = await response.json();
-
-		expect(response.status()).toBe(500);
-		expect(name).toBe('FancyError');
-		expect(message).toBe('oops');
-		expect(fancy).toBe(true);
-
-		if (process.env.DEV) {
-			expect(stack.split('\n').length).toBeGreaterThan(1);
-		} else {
-			expect(stack.split('\n').length).toBe(1);
-		}
-	});
-
-	test('page endpoint returned error respects `accept: application/json`', async ({ request }) => {
-		const response = await request.get('/errors/page-endpoint/get-explicit', {
-			headers: {
-				accept: 'application/json'
-			}
-		});
-
-		const { message } = await response.json();
-
-		expect(response.status()).toBe(400);
-		expect(message).toBe('oops');
-	});
-
 	test('returns 400 when accessing a malformed URI', async ({ page }) => {
 		const response = await page.goto('/%c0%ae%c0%ae/etc/passwd');
 		if (process.env.DEV) {
@@ -181,6 +147,26 @@ test.describe('Errors', () => {
 			'This is your custom error page saying: "Cannot read properties of undefined (reading \'toUpperCase\')"'
 		);
 	});
+
+	test('throw error(..) in endpoint', async ({ page, read_errors }) => {
+		const res = await page.goto('/errors/endpoint-throw-error');
+
+		const error = read_errors('/errors/endpoint-throw-error');
+		expect(error).toBe(undefined);
+
+		expect(await res?.text()).toBe('You shall not pass');
+		expect(res?.status()).toBe(401);
+	});
+
+	test('throw redirect(..) in endpoint', async ({ page, read_errors }) => {
+		const res = await page.goto('/errors/endpoint-throw-redirect');
+		expect(res?.status()).toBe(200); // redirects are opaque to the browser
+
+		const error = read_errors('/errors/endpoint-throw-redirect');
+		expect(error).toBe(undefined);
+
+		expect(await page.textContent('h1')).toBe('the answer is 42');
+	});
 });
 
 test.describe('Load', () => {
@@ -211,53 +197,6 @@ test.describe('Routing', () => {
 });
 
 test.describe('Shadowed pages', () => {
-	test('responds to HEAD requests from endpoint', async ({ request }) => {
-		const url = '/shadowed/simple';
-
-		const opts = {
-			headers: {
-				accept: 'application/json'
-			}
-		};
-
-		const responses = {
-			head: await request.head(url, opts),
-			get: await request.get(url, opts)
-		};
-
-		const headers = {
-			head: responses.head.headers(),
-			get: responses.get.headers()
-		};
-
-		expect(responses.head.status()).toBe(200);
-		expect(responses.get.status()).toBe(200);
-		expect(await responses.head.text()).toBe('');
-		expect(await responses.get.json()).toEqual({ answer: 42 });
-
-		['date', 'transfer-encoding'].forEach((name) => {
-			delete headers.head[name];
-			delete headers.get[name];
-		});
-
-		expect(headers.get).toEqual({
-			...headers.head,
-			'content-type': 'application/json'
-		});
-	});
-
-	test('Responds from endpoint if Accept includes application/json but not text/html', async ({
-		request
-	}) => {
-		const response = await request.get('/shadowed/simple', {
-			headers: {
-				accept: 'application/json'
-			}
-		});
-
-		expect(await response.json()).toEqual({ answer: 42 });
-	});
-
 	test('Action can return undefined', async ({ request }) => {
 		const response = await request.post('/shadowed/simple/post', {
 			headers: {
@@ -314,6 +253,14 @@ test.describe('Static files', () => {
 	});
 });
 
+test.describe('setHeaders', () => {
+	test('allows multiple set-cookie headers with different values', async ({ page }) => {
+		const response = await page.goto('/headers/set-cookie/sub');
+		const cookies = (await response?.allHeaders())['set-cookie'];
+		expect(cookies.includes('cookie1=value1') && cookies.includes('cookie2=value2')).toBe(true);
+	});
+});
+
 test.describe('Miscellaneous', () => {
 	test('does not serve version.json with an immutable cache header', async ({ request }) => {
 		// this isn't actually a great test, because caching behaviour is down to adapters.
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index e54c9ec49..1a0f1a553 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -277,6 +277,18 @@ test.describe('Shadowed pages', () => {
 			'Page data: {"sub":"sub","data":{"rootlayout":"rootlayout","layout":"layout"}}'
 		);
 	});
+
+	if (process.env.DEV) {
+		test('Data must be serializable', async ({ page, clicknav }) => {
+			await page.goto('/shadowed');
+			await clicknav('[href="/shadowed/serialization"]');
+
+			expect(await page.textContent('h1')).toBe('500');
+			expect(await page.textContent('#message')).toBe(
+				'This is your custom error page saying: "data.regex returned from \'load\' in src/routes/shadowed/serialization/+page.server.js cannot be serialized as JSON"'
+			);
+		});
+	}
 });
 
 test.describe('Encoded paths', () => {
diff --git a/packages/kit/test/apps/options-2/package.json b/packages/kit/test/apps/options-2/package.json
index 63cd01ba2..747b2bef6 100644
--- a/packages/kit/test/apps/options-2/package.json
+++ b/packages/kit/test/apps/options-2/package.json
@@ -18,7 +18,7 @@
 		"svelte": "^3.48.0",
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/apps/options/package.json b/packages/kit/test/apps/options/package.json
index 6850d6f4c..51e96620d 100644
--- a/packages/kit/test/apps/options/package.json
+++ b/packages/kit/test/apps/options/package.json
@@ -17,7 +17,7 @@
 		"svelte": "^3.48.0",
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/apps/writes/package.json b/packages/kit/test/apps/writes/package.json
index 6ea72a979..3aeed6b2b 100644
--- a/packages/kit/test/apps/writes/package.json
+++ b/packages/kit/test/apps/writes/package.json
@@ -18,7 +18,7 @@
 		"svelte": "^3.48.0",
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/prerendering/basics/package.json b/packages/kit/test/prerendering/basics/package.json
index 95f04e742..92c4a6bfa 100644
--- a/packages/kit/test/prerendering/basics/package.json
+++ b/packages/kit/test/prerendering/basics/package.json
@@ -15,7 +15,7 @@
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
 		"uvu": "^0.5.3",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/prerendering/basics/src/routes/page-server-options/+page.server.js b/packages/kit/test/prerendering/basics/src/routes/page-server-options/+page.server.js
new file mode 100644
index 000000000..d43d0cd2a
--- /dev/null
+++ b/packages/kit/test/prerendering/basics/src/routes/page-server-options/+page.server.js
@@ -0,0 +1,1 @@
+export const prerender = false;
diff --git a/packages/kit/test/prerendering/basics/src/routes/page-server-options/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/page-server-options/+page.svelte
new file mode 100644
index 000000000..3c3d6df7a
--- /dev/null
+++ b/packages/kit/test/prerendering/basics/src/routes/page-server-options/+page.svelte
@@ -0,0 +1,1 @@
+<h1>This should not be visible</h1>
diff --git a/packages/kit/test/prerendering/basics/src/routes/redirect-server/+page.server.js b/packages/kit/test/prerendering/basics/src/routes/redirect-server/+page.server.js
new file mode 100644
index 000000000..0ed002001
--- /dev/null
+++ b/packages/kit/test/prerendering/basics/src/routes/redirect-server/+page.server.js
@@ -0,0 +1,6 @@
+import { redirect } from '@sveltejs/kit';
+
+/** @type {import('@sveltejs/kit').Load} */
+export function load() {
+	throw redirect(301, 'https://example.com/redirected');
+}
diff --git a/packages/kit/test/prerendering/basics/src/routes/redirect-server/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/redirect-server/+page.svelte
new file mode 100644
index 000000000..e69de29bb
diff --git a/packages/kit/test/prerendering/basics/test/test.js b/packages/kit/test/prerendering/basics/test/test.js
index 7afc184ac..e79573f0f 100644
--- a/packages/kit/test/prerendering/basics/test/test.js
+++ b/packages/kit/test/prerendering/basics/test/test.js
@@ -21,6 +21,20 @@ test('renders a redirect', () => {
 	);
 });
 
+test('renders a server-side redirect', () => {
+	const html = read('redirect-server.html');
+	assert.equal(html, '<meta http-equiv="refresh" content="0;url=https://example.com/redirected">');
+
+	const json = read('redirect-server/__data.json');
+	assert.equal(
+		json,
+		JSON.stringify({
+			type: 'redirect',
+			location: 'https://example.com/redirected'
+		})
+	);
+});
+
 test('does not double-encode redirect locations', () => {
 	const content = read('redirect-encoded.html');
 	assert.equal(
@@ -85,6 +99,11 @@ test('does not prerender page with shadow endpoint with non-load handler', () =>
 	assert.ok(!fs.existsSync(`${build}/shadowed-post/__data.json`));
 });
 
+test('does not prerender page with prerender = false in +page.server.js', () => {
+	assert.ok(!fs.existsSync(`${build}/page-server-options.html`));
+	assert.ok(!fs.existsSync(`${build}/page-server-options/__data.json`));
+});
+
 test('decodes paths when writing files', () => {
 	let content = read('encoding/path with spaces.html');
 	assert.ok(content.includes('<p id="a">path with spaces</p>'));
diff --git a/packages/kit/test/prerendering/fallback/package.json b/packages/kit/test/prerendering/fallback/package.json
index 3c3e7aa38..8b6a09282 100644
--- a/packages/kit/test/prerendering/fallback/package.json
+++ b/packages/kit/test/prerendering/fallback/package.json
@@ -15,7 +15,7 @@
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
 		"uvu": "^0.5.3",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/prerendering/options/package.json b/packages/kit/test/prerendering/options/package.json
index b3a9e2fea..1fba5f302 100644
--- a/packages/kit/test/prerendering/options/package.json
+++ b/packages/kit/test/prerendering/options/package.json
@@ -15,7 +15,7 @@
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
 		"uvu": "^0.5.3",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/prerendering/paths-base/package.json b/packages/kit/test/prerendering/paths-base/package.json
index 6c94d7c1f..1f743ca22 100644
--- a/packages/kit/test/prerendering/paths-base/package.json
+++ b/packages/kit/test/prerendering/paths-base/package.json
@@ -15,7 +15,7 @@
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
 		"uvu": "^0.5.3",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }
diff --git a/packages/kit/test/prerendering/trailing-slash/package.json b/packages/kit/test/prerendering/trailing-slash/package.json
index 77123a00a..a2c589586 100644
--- a/packages/kit/test/prerendering/trailing-slash/package.json
+++ b/packages/kit/test/prerendering/trailing-slash/package.json
@@ -15,7 +15,7 @@
 		"svelte-check": "^2.7.1",
 		"typescript": "^4.7.4",
 		"uvu": "^0.5.4",
-		"vite": "3.0.2"
+		"vite": "^3.0.8"
 	},
 	"type": "module"
 }