Repository: sveltejs/kit (js)

Issue:
Respond with status code 413 if request body is too large
### Describe the bug

Yesterday a few people in the discord tried to help someone who got an error 400 upon requesting an endpoint.
We needed almost an hour to find the problem because he only saw "400 Bad request" and not the message of the thrown `Error` (idk why).

If the response status code had been 413, we would have found the issue much earlier.

### Reproduction

Can not reproduce on stackblitz
https://replit.com/@PehGeh/GreenMutedComma
- click on "Upload a large file"

### Logs

_No response_

### System Info

```shell
System:
    OS: Linux 5.15 Ubuntu 20.04.2 LTS (Focal Fossa)
    CPU: (8) x64 AMD EPYC 7B12
    Memory: 37.87 GB / 62.80 GB
    Container: Yes
    Shell: 5.0.17 - /bin/bash
  Binaries:
    Node: 18.2.0 - /nix/store/q3hh5viyhr3gvfrn6yakbd59gyna0v05-nodejs-18.2.0/bin/node
    Yarn: 1.22.18 - /nix/store/n7d0ibna8751b6kfj6i4fkanbdix8h7a-yarn-1.22.18/bin/yarn
    npm: 8.9.0 - /nix/store/q3hh5viyhr3gvfrn6yakbd59gyna0v05-nodejs-18.2.0/bin/npm
```


### Severity

annoyance

### Additional Information

https://github.com/sveltejs/kit/blob/ba168b0ff57caeb45a7b3185a97892ce745abec4/packages/adapter-node/src/handler.js#L51-L61

It seems like it would be enough to set the `status` property on the thrown errors to 413.

Pull request description:
Respond with status code 413 if request body is too large
Fixes #6920

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [x] 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.
- [ ] 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
- [ ] 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


Patch:
diff --git a/.changeset/dry-snails-remain.md b/.changeset/dry-snails-remain.md
new file mode 100644
index 000000000..448501302
--- /dev/null
+++ b/.changeset/dry-snails-remain.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] add missing depends function to ServerLoadEvent type
diff --git a/.changeset/dull-cows-sneeze.md b/.changeset/dull-cows-sneeze.md
new file mode 100644
index 000000000..fd084fe7d
--- /dev/null
+++ b/.changeset/dull-cows-sneeze.md
@@ -0,0 +1,6 @@
+---
+'@sveltejs/adapter-cloudflare': patch
+'@sveltejs/adapter-cloudflare-workers': patch
+---
+
+Include ambient.d.ts files in adapter packages.
diff --git a/.changeset/five-tools-arrive.md b/.changeset/five-tools-arrive.md
new file mode 100644
index 000000000..139d51a0e
--- /dev/null
+++ b/.changeset/five-tools-arrive.md
@@ -0,0 +1,7 @@
+---
+"@sveltejs/adapter-node": patch
+"@sveltejs/adapter-vercel": patch
+"@sveltejs/kit": patch
+---
+
+Redact error message if `getRequest` fails
diff --git a/.changeset/flat-owls-care.md b/.changeset/flat-owls-care.md
new file mode 100644
index 000000000..484be96f9
--- /dev/null
+++ b/.changeset/flat-owls-care.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Add cookies.serialize method
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 8dccd92ba..3a632e993 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -224,6 +224,7 @@
     "dry-jokes-explain",
     "dry-yaks-clean",
     "dull-cheetahs-clean",
+    "dull-cows-sneeze",
     "dull-ducks-report",
     "dull-emus-tap",
     "dull-lobsters-act",
@@ -1020,6 +1021,7 @@
     "smart-apes-drum",
     "smart-bears-leave",
     "smart-deers-approve",
+    "smart-forks-double",
     "smart-humans-eat",
     "smart-mugs-chew",
     "smart-nails-work",
diff --git a/.changeset/short-spies-relax.md b/.changeset/short-spies-relax.md
new file mode 100644
index 000000000..b358986e8
--- /dev/null
+++ b/.changeset/short-spies-relax.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Always apply cookies, not just for matched routes
diff --git a/.changeset/smart-forks-double.md b/.changeset/smart-forks-double.md
new file mode 100644
index 000000000..e98f3bbab
--- /dev/null
+++ b/.changeset/smart-forks-double.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/package': patch
+---
+
+fix check for undefined on application/ strip
diff --git a/.changeset/strong-baboons-travel.md b/.changeset/strong-baboons-travel.md
new file mode 100644
index 000000000..b25d46176
--- /dev/null
+++ b/.changeset/strong-baboons-travel.md
@@ -0,0 +1,5 @@
+---
+"@sveltejs/kit": patch
+---
+
+Respond with 413 if request body is too large
diff --git a/packages/adapter-auto/CHANGELOG.md b/packages/adapter-auto/CHANGELOG.md
index 8343aa61a..160ad4f61 100644
--- a/packages/adapter-auto/CHANGELOG.md
+++ b/packages/adapter-auto/CHANGELOG.md
@@ -1,5 +1,12 @@
 # @sveltejs/adapter-auto
 
+## 1.0.0-next.77
+
+### Patch Changes
+
+- Updated dependencies [[`e40acb56`](https://github.com/sveltejs/kit/commit/e40acb561e32a2ee5bcb6d289ebd1284d7af247b)]:
+  - @sveltejs/adapter-cloudflare@1.0.0-next.36
+
 ## 1.0.0-next.76
 
 ### Patch Changes
diff --git a/packages/adapter-auto/package.json b/packages/adapter-auto/package.json
index 10e404e4a..bc1fbfa3e 100644
--- a/packages/adapter-auto/package.json
+++ b/packages/adapter-auto/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/adapter-auto",
-	"version": "1.0.0-next.76",
+	"version": "1.0.0-next.77",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/adapter-cloudflare-workers/CHANGELOG.md b/packages/adapter-cloudflare-workers/CHANGELOG.md
index b2322760e..2927372a0 100644
--- a/packages/adapter-cloudflare-workers/CHANGELOG.md
+++ b/packages/adapter-cloudflare-workers/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @sveltejs/adapter-cloudflare-workers
 
+## 1.0.0-next.57
+
+### Patch Changes
+
+- Include ambient.d.ts files in adapter packages. ([#6917](https://github.com/sveltejs/kit/pull/6917))
+
 ## 1.0.0-next.56
 
 ### Patch Changes
diff --git a/packages/adapter-cloudflare-workers/package.json b/packages/adapter-cloudflare-workers/package.json
index f856b8e32..2504210b5 100644
--- a/packages/adapter-cloudflare-workers/package.json
+++ b/packages/adapter-cloudflare-workers/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/adapter-cloudflare-workers",
-	"version": "1.0.0-next.56",
+	"version": "1.0.0-next.57",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -19,6 +19,7 @@
 	"main": "index.js",
 	"types": "index.d.ts",
 	"files": [
+		"ambient.d.ts",
 		"files",
 		"index.d.ts"
 	],
diff --git a/packages/adapter-cloudflare/CHANGELOG.md b/packages/adapter-cloudflare/CHANGELOG.md
index 456216ec9..407113129 100644
--- a/packages/adapter-cloudflare/CHANGELOG.md
+++ b/packages/adapter-cloudflare/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @sveltejs/adapter-cloudflare
 
+## 1.0.0-next.36
+
+### Patch Changes
+
+- Include ambient.d.ts files in adapter packages. ([#6917](https://github.com/sveltejs/kit/pull/6917))
+
 ## 1.0.0-next.35
 
 ### Patch Changes
diff --git a/packages/adapter-cloudflare/package.json b/packages/adapter-cloudflare/package.json
index a27f5eaac..e5c93726c 100644
--- a/packages/adapter-cloudflare/package.json
+++ b/packages/adapter-cloudflare/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/adapter-cloudflare",
-	"version": "1.0.0-next.35",
+	"version": "1.0.0-next.36",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -20,7 +20,8 @@
 	"files": [
 		"files",
 		"index.js",
-		"index.d.ts"
+		"index.d.ts",
+		"ambient.d.ts"
 	],
 	"main": "index.js",
 	"scripts": {
diff --git a/packages/adapter-node/src/handler.js b/packages/adapter-node/src/handler.js
index 94f331f57..7177e836b 100644
--- a/packages/adapter-node/src/handler.js
+++ b/packages/adapter-node/src/handler.js
@@ -56,7 +56,7 @@ const ssr = async (req, res) => {
 		});
 	} catch (err) {
 		res.statusCode = err.status || 400;
-		res.end(err.reason || 'Invalid request body');
+		res.end('Invalid request body');
 		return;
 	}
 
diff --git a/packages/adapter-vercel/files/serverless.js b/packages/adapter-vercel/files/serverless.js
index 36fbae4c7..25db646be 100644
--- a/packages/adapter-vercel/files/serverless.js
+++ b/packages/adapter-vercel/files/serverless.js
@@ -23,7 +23,7 @@ export default async (req, res) => {
 		request = await getRequest({ base: `https://${req.headers.host}`, request: req });
 	} catch (err) {
 		res.statusCode = err.status || 400;
-		return res.end(err.reason || 'Invalid request body');
+		return res.end('Invalid request body');
 	}
 
 	setResponse(
diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js
index b73c5c91b..67b82e63f 100644
--- a/packages/kit/src/exports/node/index.js
+++ b/packages/kit/src/exports/node/index.js
@@ -1,4 +1,5 @@
 import * as set_cookie_parser from 'set-cookie-parser';
+import { error } from '../index.js';
 
 /**
  * @param {import('http').IncomingMessage} req
@@ -27,7 +28,8 @@ function get_raw_body(req, body_size_limit) {
 		if (!length) {
 			length = body_size_limit;
 		} else if (length > body_size_limit) {
-			throw new Error(
+			throw error(
+				413,
 				`Received content-length of ${length}, but only accept up to ${body_size_limit} bytes.`
 			);
 		}
@@ -45,6 +47,7 @@ function get_raw_body(req, body_size_limit) {
 	return new ReadableStream({
 		start(controller) {
 			req.on('error', (error) => {
+				cancelled = true;
 				controller.error(error);
 			});
 
@@ -58,8 +61,10 @@ function get_raw_body(req, body_size_limit) {
 
 				size += chunk.length;
 				if (size > length) {
-					req.destroy(
-						new Error(
+					cancelled = true;
+					controller.error(
+						error(
+							413,
 							`request body size exceeded ${
 								content_length ? "'content-length'" : 'BODY_SIZE_LIMIT'
 							} of ${length}`
diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js
index f9aeb8f3b..36e2fa0f2 100644
--- a/packages/kit/src/exports/vite/dev/index.js
+++ b/packages/kit/src/exports/vite/dev/index.js
@@ -397,7 +397,7 @@ export async function dev(vite, vite_config, svelte_config) {
 					});
 				} catch (/** @type {any} */ err) {
 					res.statusCode = err.status || 400;
-					return res.end(err.message || 'Invalid request body');
+					return res.end('Invalid request body');
 				}
 
 				const template = load_template(cwd, svelte_config);
diff --git a/packages/kit/src/exports/vite/preview/index.js b/packages/kit/src/exports/vite/preview/index.js
index 55a59105b..0a24a0b9c 100644
--- a/packages/kit/src/exports/vite/preview/index.js
+++ b/packages/kit/src/exports/vite/preview/index.js
@@ -137,7 +137,7 @@ export async function preview(vite, vite_config, svelte_config) {
 				});
 			} catch (/** @type {any} */ err) {
 				res.statusCode = err.status || 400;
-				return res.end(err.message || 'Invalid request body');
+				return res.end('Invalid request body');
 			}
 
 			setResponse(
diff --git a/packages/kit/src/runtime/server/cookie.js b/packages/kit/src/runtime/server/cookie.js
index 559dcce3e..69177f213 100644
--- a/packages/kit/src/runtime/server/cookie.js
+++ b/packages/kit/src/runtime/server/cookie.js
@@ -71,6 +71,18 @@ export function get_cookies(request, url) {
 					maxAge: 0
 				}
 			});
+		},
+
+		/**
+		 * @param {string} name
+		 * @param {string} value
+		 * @param {import('cookie').CookieSerializeOptions} opts
+		 */
+		serialize(name, value, opts) {
+			return serialize(name, value, {
+				...DEFAULT_SERIALIZE_OPTIONS,
+				...opts
+			});
 		}
 	};
 
diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js
index 9070608a0..7b1daadde 100644
--- a/packages/kit/src/runtime/server/index.js
+++ b/packages/kit/src/runtime/server/index.js
@@ -236,17 +236,6 @@ export async function respond(request, options, state) {
 					throw new Error('This should never happen');
 				}
 
-				if (!is_data_request) {
-					// we only want to set cookies on __data.js 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));
-					}
-				}
-
-				add_cookies_to_headers(response.headers, Array.from(new_cookies.values()));
-
 				return response;
 			}
 
@@ -294,7 +283,21 @@ export async function respond(request, options, state) {
 	try {
 		const response = await options.hooks.handle({
 			event,
-			resolve,
+			resolve: (event, opts) =>
+				resolve(event, opts).then((response) => {
+					// add headers/cookies here, rather than inside `resolve`, so that we
+					// can do it once for all responses instead of once per `return`
+					if (!is_data_request) {
+						// we only want to set cookies on __data.js 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));
+						}
+					}
+					add_cookies_to_headers(response.headers, Array.from(new_cookies.values()));
+					return response;
+				}),
 			// TODO remove for 1.0
 			// @ts-expect-error
 			get request() {
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index b26a0a932..b2b00d240 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -155,6 +155,19 @@ export interface Cookies {
 	 * Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
 	 */
 	delete(name: string, opts?: import('cookie').CookieSerializeOptions): void;
+
+	/**
+	 * Serialize a cookie name-value pair into a Set-Cookie header string.
+	 *
+	 * The `httpOnly` and `secure` options are `true` by default, and must be explicitly disabled if you want cookies to be readable by client-side JavaScript and/or transmitted over HTTP. The `sameSite` option defaults to `lax`.
+	 *
+	 * By default, the `path` of a cookie is the current pathname. In most cases you should explicitly set `path: '/'` to make the cookie available throughout your app.
+	 *
+	 * @param name the name for the cookie
+	 * @param value value to set the cookie to
+	 * @param options object containing serialization options
+	 */
+	serialize(name: string, value: string, opts?: import('cookie').CookieSerializeOptions): string;
 }
 
 export interface KitConfig {
@@ -368,6 +381,7 @@ export interface ServerLoadEvent<
 	ParentData extends Record<string, any> = Record<string, any>
 > extends RequestEvent<Params> {
 	parent: () => Promise<ParentData>;
+	depends: (...deps: string[]) => void;
 }
 
 export interface Action<
diff --git a/packages/package/CHANGELOG.md b/packages/package/CHANGELOG.md
index 0fbb8b088..2d6c7cb57 100644
--- a/packages/package/CHANGELOG.md
+++ b/packages/package/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @sveltejs/package
 
+## 1.0.0-next.5
+
+### Patch Changes
+
+- fix check for undefined on application/ strip ([#6932](https://github.com/sveltejs/kit/pull/6932))
+
 ## 1.0.0-next.4
 
 ### Patch Changes
diff --git a/packages/package/package.json b/packages/package/package.json
index b5bf86863..731fa5ec7 100644
--- a/packages/package/package.json
+++ b/packages/package/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/package",
-	"version": "1.0.0-next.4",
+	"version": "1.0.0-next.5",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/package/src/utils.js b/packages/package/src/utils.js
index 0b263857b..001970610 100644
--- a/packages/package/src/utils.js
+++ b/packages/package/src/utils.js
@@ -52,7 +52,7 @@ export function strip_lang_tags(content) {
 		.replace(
 			/(<!--[^]*?-->)|(<script[^>]*?)\s(?:type|lang)=(["'])(.*?)\3/g,
 			// things like application/ld+json should be kept as-is. Preprocessed languages are "ts" etc
-			(match, s1, s2, _, s4) => (s4.startsWith('application/') ? match : (s1 ?? '') + (s2 ?? ''))
+			(match, s1, s2, _, s4) => (s4?.startsWith('application/') ? match : (s1 ?? '') + (s2 ?? ''))
 		)
 		.replace(/(<!--[^]*?-->)|(<style[^>]*?)\s(?:type|lang)=(["']).*?\3/g, '$1$2');
 }


Test patch:
diff --git a/packages/kit/test/apps/basics/src/hooks.server.js b/packages/kit/test/apps/basics/src/hooks.server.js
index bf5d6b7fe..18f1c7a03 100644
--- a/packages/kit/test/apps/basics/src/hooks.server.js
+++ b/packages/kit/test/apps/basics/src/hooks.server.js
@@ -47,6 +47,15 @@ export const handle = sequence(
 		event.locals.name = /** @type {string} */ (event.cookies.get('name'));
 		return resolve(event);
 	},
+	async ({ event, resolve }) => {
+		if (event.url.pathname === '/cookies/serialize') {
+			event.cookies.set('before', 'before');
+			const response = await resolve(event);
+			response.headers.append('set-cookie', event.cookies.serialize('after', 'after'));
+			return response;
+		}
+		return resolve(event);
+	},
 	async ({ event, resolve }) => {
 		if (event.url.pathname === '/errors/error-in-handle') {
 			throw new Error('Error in handle');
diff --git a/packages/kit/test/apps/basics/src/routes/cookies/serialize/+server.js b/packages/kit/test/apps/basics/src/routes/cookies/serialize/+server.js
new file mode 100644
index 000000000..f46459823
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/cookies/serialize/+server.js
@@ -0,0 +1,8 @@
+/** @type {import('@sveltejs/kit').RequestHandler} */
+export const GET = ({ cookies }) => {
+	const response = new Response('success', {
+		status: 200
+	});
+	response.headers.append('set-cookie', cookies.serialize('endpoint', 'endpoint'));
+	return response;
+};
diff --git a/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.js b/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.js
new file mode 100644
index 000000000..2d4c6d175
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.js
@@ -0,0 +1,8 @@
+/** @type {import('./$types').PageLoad} */
+export function load({ data, depends }) {
+	depends('invalidate-depends:shared');
+	return {
+		shared: new Date().getTime(),
+		...data
+	};
+}
diff --git a/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.server.js b/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.server.js
new file mode 100644
index 000000000..fb81ddb99
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.server.js
@@ -0,0 +1,7 @@
+/** @type {import('./$types').PageServerLoad} */
+export function load({ depends }) {
+	depends('invalidate-depends:server');
+	return {
+		server: new Date().getTime()
+	};
+}
diff --git a/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.svelte
new file mode 100644
index 000000000..1ce33105c
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/invalidation/depends/+page.svelte
@@ -0,0 +1,24 @@
+<script>
+	import { invalidate } from '$app/navigation';
+
+	/** @type {import('./$types').PageData} */
+	export let data;
+</script>
+
+<p class="shared">{data.shared}</p>
+<button
+	type="button"
+	class="shared"
+	on:click={() => invalidate('invalidate-depends:shared')}
+>
+	invalidate shared
+</button>
+
+<p class="server">{data.server}</p>
+<button
+	type="button"
+	class="server"
+	on:click={() => invalidate('invalidate-depends:server')}
+>
+	invalidate server
+</button>
diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js
index 628e64dd1..185b5e514 100644
--- a/packages/kit/test/apps/basics/test/client.test.js
+++ b/packages/kit/test/apps/basics/test/client.test.js
@@ -760,6 +760,36 @@ test.describe.serial('Invalidation', () => {
 		await page.waitForTimeout(200);
 		expect(await page.textContent('h1')).toBe('a: 4, b: 5');
 	});
+
+	test('+layout(.server).js is re-run when server dep is invalidated', async ({ page }) => {
+		await page.goto('/load/invalidation/depends');
+		const server = await page.textContent('p.server');
+		const shared = await page.textContent('p.shared');
+		expect(server).toBeDefined();
+		expect(shared).toBeDefined();
+
+		await Promise.all([page.click('button.server'), page.waitForLoadState('networkidle')]);
+		await page.waitForTimeout(200);
+		const next_server = await page.textContent('p.server');
+		const next_shared = await page.textContent('p.shared');
+		expect(server).not.toBe(next_server);
+		expect(shared).not.toBe(next_shared);
+	});
+
+	test('+layout.js is re-run when shared dep is invalidated', async ({ page }) => {
+		await page.goto('/load/invalidation/depends');
+		const server = await page.textContent('p.server');
+		const shared = await page.textContent('p.shared');
+		expect(server).toBeDefined();
+		expect(shared).toBeDefined();
+
+		await Promise.all([page.click('button.shared'), page.waitForLoadState('networkidle')]);
+		await page.waitForTimeout(200);
+		const next_server = await page.textContent('p.server');
+		const next_shared = await page.textContent('p.shared');
+		expect(server).toBe(next_server);
+		expect(shared).not.toBe(next_shared);
+	});
 });
 
 test.describe('data-sveltekit attributes', () => {
diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js
index 0404859e5..87bb6ea0b 100644
--- a/packages/kit/test/apps/basics/test/server.test.js
+++ b/packages/kit/test/apps/basics/test/server.test.js
@@ -336,6 +336,18 @@ test.describe('setHeaders', () => {
 	});
 });
 
+test.describe('cookies', () => {
+	test('cookie.serialize created correct cookie header string', async ({ page }) => {
+		const response = await page.goto('/cookies/serialize');
+		const cookies = await response.headerValue('set-cookie');
+		expect(
+			cookies.includes('before=before') &&
+				cookies.includes('after=after') &&
+				cookies.includes('endpoint=endpoint')
+		).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.