Repository: sveltejs/kit
Pull request #7177 (2022-10-07 14:17:43)

[fix] load data through regular json request
using devalue's stringify/parse methods
Part of #7125 and #6477

Not sure exactly what's left for the above issues to get closed besides allowing setting cache headers again.

If we change to this we could use the same methods for interacting with form actions, allowing them to also return `Date` objects for example.

Marked as draft, @Rich-Harris feel free to take over/close.

### 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


changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 82d403d8483da380d6ff28618a8b7a19414f236c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/sveltejs/kit/new/data-json?filename=.changeset/metal-rules-rush.md&value=---%0A%22%40sveltejs%2Fadapter-netlify%22%3A%20patch%0A%22%40sveltejs%2Fadapter-vercel%22%3A%20patch%0A%22%40sveltejs%2Fkit%22%3A%20patch%0A%22test-basics%22%3A%20patch%0A%22test-options%22%3A%20patch%0A%22prerendering-test-basics%22%3A%20patch%0A%22prerendering-test-trailing-slash%22%3A%20patch%0A---%0A%0A%5Bfix%5D%20load%20data%20through%20regular%20json%20request%0A)



Patch:
diff --git a/.changeset/famous-humans-march.md b/.changeset/famous-humans-march.md
new file mode 100644
index 000000000..f50d32bd9
--- /dev/null
+++ b/.changeset/famous-humans-march.md
@@ -0,0 +1,7 @@
+---
+'@sveltejs/adapter-netlify': patch
+'@sveltejs/adapter-vercel': patch
+'@sveltejs/kit': patch
+---
+
+Transfer server data as devalue-encoded JSON
diff --git a/.changeset/hot-shoes-bow.md b/.changeset/hot-shoes-bow.md
new file mode 100644
index 000000000..5a3a6794a
--- /dev/null
+++ b/.changeset/hot-shoes-bow.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Fix parsing content-type header for actions
diff --git a/.changeset/light-melons-collect.md b/.changeset/light-melons-collect.md
new file mode 100644
index 000000000..6d9680ea5
--- /dev/null
+++ b/.changeset/light-melons-collect.md
@@ -0,0 +1,5 @@
+---
+"@sveltejs/adapter-node": patch
+---
+
+[chore] update dependency @rollup/plugin-json to v5
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 3f67b2dc2..9a9929333 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -516,6 +516,7 @@
     "hot-lions-cough",
     "hot-mayflies-visit",
     "hot-planes-shout",
+    "hot-shoes-bow",
     "hot-steaks-hang",
     "hot-taxis-jam",
     "hungry-coats-sort",
@@ -933,8 +934,10 @@
     "rotten-rocks-mate",
     "rotten-singers-taste",
     "rotten-tips-teach",
+    "rotten-trees-brush",
     "rotten-wasps-beam",
     "rude-balloons-return",
+    "rude-berries-rush",
     "rude-geese-roll",
     "rude-grapes-perform",
     "rude-lies-laugh",
@@ -987,6 +990,7 @@
     "sharp-geckos-beam",
     "sharp-glasses-mix",
     "sharp-hornets-beam",
+    "sharp-lies-grin",
     "sharp-mangos-tickle",
     "sharp-moles-confess",
     "sharp-moles-hammer",
@@ -1189,6 +1193,7 @@
     "tame-kangaroos-rush",
     "tame-lions-jump",
     "tame-lobsters-beam",
+    "tame-mugs-teach",
     "tame-needles-march",
     "tame-pants-raise",
     "tame-radios-build",
diff --git a/.changeset/rotten-trees-brush.md b/.changeset/rotten-trees-brush.md
new file mode 100644
index 000000000..5265a930e
--- /dev/null
+++ b/.changeset/rotten-trees-brush.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] reset form in use:enhance after successful submit
diff --git a/.changeset/rude-berries-rush.md b/.changeset/rude-berries-rush.md
new file mode 100644
index 000000000..c0ef80487
--- /dev/null
+++ b/.changeset/rude-berries-rush.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/amp': patch
+---
+
+Amend boilerplate to match current recommendations
diff --git a/.changeset/sharp-lies-grin.md b/.changeset/sharp-lies-grin.md
new file mode 100644
index 000000000..a75c5f53b
--- /dev/null
+++ b/.changeset/sharp-lies-grin.md
@@ -0,0 +1,5 @@
+---
+"@sveltejs/adapter-netlify": patch
+---
+
+[fix] clear out build directory when running `adapter-netlify`
diff --git a/.changeset/tame-mugs-teach.md b/.changeset/tame-mugs-teach.md
new file mode 100644
index 000000000..ef7f5bf67
--- /dev/null
+++ b/.changeset/tame-mugs-teach.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] auto-update commented PageError
diff --git a/documentation/docs/02-web-standards.md b/documentation/docs/02-web-standards.md
index 1d68b36fc..937f1e65d 100644
--- a/documentation/docs/02-web-standards.md
+++ b/documentation/docs/02-web-standards.md
@@ -18,7 +18,7 @@ Besides `fetch` itself, the [Fetch API](https://developer.mozilla.org/en-US/docs
 
 #### Request
 
-An instance of [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) is accessible in [hooks](/docs/hooks) and [server routes](/docs/routing#server) as `event.request`. It contains useful methods like `request.json()` and `request.formData()` for e.g. getting data that was posted to an endpoint.
+An instance of [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) is accessible in [hooks](/docs/hooks) and [server routes](/docs/routing#server) as `event.request`. It contains useful methods like `request.json()` and `request.formData()` for getting data that was posted to an endpoint.
 
 #### Response
 
diff --git a/documentation/docs/06-form-actions.md b/documentation/docs/06-form-actions.md
index d87effa24..401e09f14 100644
--- a/documentation/docs/06-form-actions.md
+++ b/documentation/docs/06-form-actions.md
@@ -134,7 +134,7 @@ export const actions = {
 
 #### Validation errors
 
-If the request couldn't be processed because of invalid data, you can return validation errors — along with the previously submitted form values — back to the user so that they can try again. The `invalid` function lets you return an HTTP status code (typically 400, in the case of validation errors) along with the data:
+If the request couldn't be processed because of invalid data, you can return validation errors — along with the previously submitted form values — back to the user so that they can try again. The `invalid` function lets you return an HTTP status code (typically 400 or 422, in the case of validation errors) along with the data:
 
 ```diff
 // @errors: 2339 2304
@@ -366,4 +366,4 @@ const response = await fetch(this.action, {
 
 ### Alternatives
 
-Form actions are the preferred way to send data to the server, since they can be progressively enhanced, but you can also use [`+server.js`](/docs/routing#server) files to expose (for example) a JSON API.
\ No newline at end of file
+Form actions are the preferred way to send data to the server, since they can be progressively enhanced, but you can also use [`+server.js`](/docs/routing#server) files to expose (for example) a JSON API.
diff --git a/package.json b/package.json
index 382da2469..814a803aa 100644
--- a/package.json
+++ b/package.json
@@ -24,9 +24,9 @@
 	"homepage": "https://github.com/sveltejs/kit#readme",
 	"devDependencies": {
 		"@changesets/cli": "^2.22.0",
-		"@rollup/plugin-commonjs": "^22.0.1",
-		"@rollup/plugin-json": "^4.1.0",
-		"@rollup/plugin-node-resolve": "^14.1.0",
+		"@rollup/plugin-commonjs": "^23.0.0",
+		"@rollup/plugin-json": "^5.0.0",
+		"@rollup/plugin-node-resolve": "^15.0.0",
 		"@svitejs/changesets-changelog-github-compact": "^0.1.1",
 		"playwright": "^1.25.0",
 		"prettier": "^2.6.2",
@@ -36,7 +36,7 @@
 		"turbo": "^1.3.4",
 		"typescript": "^4.8.2"
 	},
-	"packageManager": "pnpm@7.13.2",
+	"packageManager": "pnpm@7.13.3",
 	"engines": {
 		"pnpm": "^7.0.0"
 	},
diff --git a/packages/adapter-auto/CHANGELOG.md b/packages/adapter-auto/CHANGELOG.md
index ca546206b..33b702303 100644
--- a/packages/adapter-auto/CHANGELOG.md
+++ b/packages/adapter-auto/CHANGELOG.md
@@ -1,5 +1,13 @@
 # @sveltejs/adapter-auto
 
+## 1.0.0-next.81
+
+### Patch Changes
+
+- Updated dependencies [[`2555e64b`](https://github.com/sveltejs/kit/commit/2555e64be5ca8433c623e7838dc9c570ad024aed)]:
+  - @sveltejs/adapter-netlify@1.0.0-next.79
+  - @sveltejs/adapter-vercel@1.0.0-next.77
+
 ## 1.0.0-next.80
 
 ### Patch Changes
diff --git a/packages/adapter-auto/package.json b/packages/adapter-auto/package.json
index 859537000..7967f78cf 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.80",
+	"version": "1.0.0-next.81",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/adapter-netlify/CHANGELOG.md b/packages/adapter-netlify/CHANGELOG.md
index 25523f4f8..9b0373d36 100644
--- a/packages/adapter-netlify/CHANGELOG.md
+++ b/packages/adapter-netlify/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @sveltejs/adapter-netlify
 
+## 1.0.0-next.79
+
+### Patch Changes
+
+- [fix] clear out build directory when running `adapter-netlify` ([#7159](https://github.com/sveltejs/kit/pull/7159))
+
 ## 1.0.0-next.78
 
 ### Patch Changes
diff --git a/packages/adapter-netlify/index.js b/packages/adapter-netlify/index.js
index 6d77b17a7..613122752 100644
--- a/packages/adapter-netlify/index.js
+++ b/packages/adapter-netlify/index.js
@@ -45,6 +45,7 @@ export default function ({ split = false, edge = edge_set_in_env_var } = {}) {
 			const publish = get_publish_directory(netlify_config, builder) || 'build';
 
 			// empty out existing build directories
+			builder.rimraf(publish);
 			builder.rimraf('.netlify/edge-functions');
 			builder.rimraf('.netlify/functions-internal');
 			builder.rimraf('.netlify/server');
@@ -196,7 +197,7 @@ async function generate_lambda_functions({ builder, publish, split }) {
 					writeFileSync(`.netlify/functions-internal/${name}.json`, fn_config);
 
 					redirects.push(`${pattern} /.netlify/functions/${name} 200`);
-					redirects.push(`${pattern}/__data.js /.netlify/functions/${name} 200`);
+					redirects.push(`${pattern}/__data.json /.netlify/functions/${name} 200`);
 				}
 			};
 		});
diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json
index 759948d15..427e8df32 100644
--- a/packages/adapter-netlify/package.json
+++ b/packages/adapter-netlify/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/adapter-netlify",
-	"version": "1.0.0-next.78",
+	"version": "1.0.0-next.79",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -38,9 +38,9 @@
 	},
 	"devDependencies": {
 		"@netlify/functions": "^1.0.0",
-		"@rollup/plugin-commonjs": "^22.0.1",
-		"@rollup/plugin-json": "^4.1.0",
-		"@rollup/plugin-node-resolve": "^14.1.0",
+		"@rollup/plugin-commonjs": "^23.0.0",
+		"@rollup/plugin-json": "^5.0.0",
+		"@rollup/plugin-node-resolve": "^15.0.0",
 		"@sveltejs/kit": "workspace:*",
 		"@types/node": "^16.11.36",
 		"@types/set-cookie-parser": "^2.4.2",
diff --git a/packages/adapter-node/package.json b/packages/adapter-node/package.json
index cf1c2dfb3..5a346d5c9 100644
--- a/packages/adapter-node/package.json
+++ b/packages/adapter-node/package.json
@@ -42,9 +42,9 @@
 		"uvu": "^0.5.3"
 	},
 	"dependencies": {
-		"@rollup/plugin-commonjs": "^22.0.1",
-		"@rollup/plugin-json": "^4.1.0",
-		"@rollup/plugin-node-resolve": "^14.1.0",
+		"@rollup/plugin-commonjs": "^23.0.0",
+		"@rollup/plugin-json": "^5.0.0",
+		"@rollup/plugin-node-resolve": "^15.0.0",
 		"rollup": "^2.78.1"
 	}
 }
diff --git a/packages/adapter-vercel/index.js b/packages/adapter-vercel/index.js
index 071d025e7..05148b117 100644
--- a/packages/adapter-vercel/index.js
+++ b/packages/adapter-vercel/index.js
@@ -225,7 +225,7 @@ export default function ({ external = [], edge, split } = {}) {
 								sliced_pattern = '^/?';
 							}
 
-							const src = `${sliced_pattern}(?:/__data.js)?$`; // TODO adding /__data.js is a temporary workaround — those endpoints should be treated as distinct routes
+							const src = `${sliced_pattern}(?:/__data.json)?$`; // TODO adding /__data.json is a temporary workaround — those endpoints should be treated as distinct routes
 
 							await generate_function(route.id || 'index', src, entry.generateManifest);
 						}
diff --git a/packages/amp/CHANGELOG.md b/packages/amp/CHANGELOG.md
index e41c72d40..f3fccf20a 100644
--- a/packages/amp/CHANGELOG.md
+++ b/packages/amp/CHANGELOG.md
@@ -1,8 +1,13 @@
 # @sveltejs/amp
 
-## 1.0.0-next.1
+## 1.0.0-next.2
+
 ### Patch Changes
 
+- Amend boilerplate to match current recommendations ([#7184](https://github.com/sveltejs/kit/pull/7184))
 
+## 1.0.0-next.1
+
+### Patch Changes
 
 - Add types to pkg.exports ([#5045](https://github.com/sveltejs/kit/pull/5045))
diff --git a/packages/amp/index.js b/packages/amp/index.js
index 5b54cb891..41fb0c55a 100644
--- a/packages/amp/index.js
+++ b/packages/amp/index.js
@@ -1,8 +1,8 @@
 // https://amp.dev/documentation/guides-and-tutorials/learn/spec/amp-boilerplate/
 const boilerplate = `
-	<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
-	<link rel="preload" as="script" href="https://cdn.ampproject.org/v0.js">
+	<link rel="preload" as="script" href="https://cdn.ampproject.org/v0.js" />
 	<script async src="https://cdn.ampproject.org/v0.js"></script>
+	<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
 `;
 
 /** @param {string} html */
diff --git a/packages/amp/package.json b/packages/amp/package.json
index e07db9e35..9bf148010 100644
--- a/packages/amp/package.json
+++ b/packages/amp/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/amp",
-	"version": "1.0.0-next.1",
+	"version": "1.0.0-next.2",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 4ee8fabb1..61ac14abd 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,15 @@
 # @sveltejs/kit
 
+## 1.0.0-next.512
+
+### Patch Changes
+
+- Fix parsing content-type header for actions ([#7195](https://github.com/sveltejs/kit/pull/7195))
+
+* [fix] reset form in use:enhance after successful submit ([#7207](https://github.com/sveltejs/kit/pull/7207))
+
+- [fix] auto-update commented PageError ([#7189](https://github.com/sveltejs/kit/pull/7189))
+
 ## 1.0.0-next.511
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index b5c719581..646b2fac9 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.511",
+	"version": "1.0.0-next.512",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/src/constants.js b/packages/kit/src/constants.js
index 7d50f1524..ca14db293 100644
--- a/packages/kit/src/constants.js
+++ b/packages/kit/src/constants.js
@@ -4,4 +4,4 @@ export const SVELTE_KIT_ASSETS = '/_svelte_kit_assets';
 
 export const GENERATED_COMMENT = '// this file is generated — do not edit it\n';
 
-export const DATA_SUFFIX = '/__data.js';
+export const DATA_SUFFIX = '/__data.json';
diff --git a/packages/kit/src/core/sync/sync.js b/packages/kit/src/core/sync/sync.js
index ce7c8dccf..b1ba0fbb5 100644
--- a/packages/kit/src/core/sync/sync.js
+++ b/packages/kit/src/core/sync/sync.js
@@ -18,9 +18,17 @@ export function init(config, mode) {
 	if (fs.existsSync('src/app.d.ts')) {
 		const content = fs.readFileSync('src/app.d.ts', 'utf-8');
 		if (content.includes('PageError')) {
-			throw new Error(
-				'App.PageError has been renamed to App.Error — please update your src/app.d.ts'
-			);
+			if (content.includes('// interface PageError')) {
+				fs.writeFileSync(
+					'src/app.d.ts',
+					content.replace(/\/\/ interface PageError/g, '// interface Error')
+				);
+				console.warn('App.PageError has been renamed to App.Error — we updated your src/app.d.ts');
+			} else {
+				throw new Error(
+					'App.PageError has been renamed to App.Error — please update your src/app.d.ts'
+				);
+			}
 		}
 	}
 
diff --git a/packages/kit/src/core/sync/write_types/index.js b/packages/kit/src/core/sync/write_types/index.js
index de91ace4d..a55679811 100644
--- a/packages/kit/src/core/sync/write_types/index.js
+++ b/packages/kit/src/core/sync/write_types/index.js
@@ -617,7 +617,7 @@ export function tweak_types(content, is_server) {
 					}
 				}
 			}
-			modified ||= _modified;
+			modified = modified || _modified;
 			return _modified;
 		}
 
diff --git a/packages/kit/src/runtime/app/forms.js b/packages/kit/src/runtime/app/forms.js
index b77770b96..4a93d633d 100644
--- a/packages/kit/src/runtime/app/forms.js
+++ b/packages/kit/src/runtime/app/forms.js
@@ -25,6 +25,7 @@ export function enhance(form, submit = () => {}) {
 	 */
 	const fallback_callback = async ({ action, result }) => {
 		if (result.type === 'success') {
+			form.reset();
 			await invalidateAll();
 		}
 
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index 664cf80f7..bb90f1b08 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -1,7 +1,13 @@
 import { onMount, tick } from 'svelte';
 import { make_trackable, decode_params, normalize_path } from '../../utils/url.js';
 import { find_anchor, get_base_uri, scroll_state } from './utils.js';
-import { lock_fetch, unlock_fetch, initial_fetch, subsequent_fetch } from './fetcher.js';
+import {
+	lock_fetch,
+	unlock_fetch,
+	initial_fetch,
+	subsequent_fetch,
+	native_fetch
+} from './fetcher.js';
 import { parse } from './parse.js';
 
 import Root from '__GENERATED__/root.svelte';
@@ -10,6 +16,7 @@ import { HttpError, Redirect } from '../control.js';
 import { stores } from './singletons.js';
 import { DATA_SUFFIX } from '../../constants.js';
 import { unwrap_promises } from '../../utils/promises.js';
+import * as devalue from 'devalue';
 
 const SCROLL_KEY = 'sveltekit:scroll';
 const INDEX_KEY = 'sveltekit:index';
@@ -1479,8 +1486,6 @@ export function create_client({ target, base, trailing_slash }) {
 	};
 }
 
-let data_id = 1;
-
 /**
  * @param {URL} url
  * @param {boolean[]} invalid
@@ -1489,25 +1494,20 @@ let data_id = 1;
 async function load_data(url, invalid) {
 	const data_url = new URL(url);
 	data_url.pathname = url.pathname.replace(/\/$/, '') + DATA_SUFFIX;
-	data_url.searchParams.set('__invalid', invalid.map((x) => (x ? 'y' : 'n')).join(''));
-	data_url.searchParams.set('__id', String(data_id++));
-
-	// The __data.js file is generated by the server and looks like
-	// `window.__sveltekit_data = ${devalue.uneval(data)}`. We do this instead
-	// of `export const data` because modules are cached indefinitely,
-	// and that would cause memory leaks.
-	//
-	// The data is read and deleted in the same tick as the promise
-	// resolves, so it's not vulnerable to race conditions
-	await import(/* @vite-ignore */ data_url.href);
 
-	// @ts-expect-error
-	const server_data = window.__sveltekit_data;
+	const res = await native_fetch(data_url.href, {
+		headers: {
+			'x-sveltekit-invalidated': invalid.map((x) => (x ? '1' : '')).join(',')
+		}
+	});
+	const server_data = await res.text();
 
-	// @ts-expect-error
-	delete window.__sveltekit_data;
+	if (!res.ok) {
+		// error message is a JSON-stringified string which devalue can't handle at the top level
+		throw new Error(JSON.parse(server_data));
+	}
 
-	return server_data;
+	return devalue.parse(server_data);
 }
 
 /**
diff --git a/packages/kit/src/runtime/client/fetcher.js b/packages/kit/src/runtime/client/fetcher.js
index 2e43af217..8ae2515bd 100644
--- a/packages/kit/src/runtime/client/fetcher.js
+++ b/packages/kit/src/runtime/client/fetcher.js
@@ -2,7 +2,7 @@ import { hash } from '../hash.js';
 
 let loading = 0;
 
-const native_fetch = window.fetch;
+export const native_fetch = window.fetch;
 
 export function lock_fetch() {
 	loading += 1;
diff --git a/packages/kit/src/runtime/server/data/index.js b/packages/kit/src/runtime/server/data/index.js
index 3f2c3654a..3e0ea3890 100644
--- a/packages/kit/src/runtime/server/data/index.js
+++ b/packages/kit/src/runtime/server/data/index.js
@@ -15,7 +15,7 @@ import { DATA_SUFFIX } from '../../../constants.js';
  */
 export async function render_data(event, route, options, state) {
 	if (!route.page) {
-		// requesting /__data.js should fail for a +server.js
+		// requesting /__data.json should fail for a +server.js
 		return new Response(undefined, {
 			status: 404
 		});
@@ -25,10 +25,8 @@ export async function render_data(event, route, options, state) {
 		const node_ids = [...route.page.layouts, route.page.leaf];
 
 		const invalidated =
-			event.url.searchParams
-				.get('__invalid')
-				?.split('')
-				.map((x) => x === 'y') ?? node_ids.map(() => true);
+			event.request.headers.get('x-sveltekit-invalidated')?.split(',').map(Boolean) ??
+			node_ids.map(() => true);
 
 		let aborted = false;
 
@@ -38,9 +36,6 @@ export async function render_data(event, route, options, state) {
 			options.trailing_slash
 		);
 
-		url.searchParams.delete('__invalid');
-		url.searchParams.delete('__id');
-
 		const new_event = { ...event, url };
 
 		const functions = node_ids.map((n, i) => {
diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js
index 6f08b62f1..8c1ec9d1f 100644
--- a/packages/kit/src/runtime/server/index.js
+++ b/packages/kit/src/runtime/server/index.js
@@ -3,6 +3,7 @@ import { render_page } from './page/index.js';
 import { render_response } from './page/render.js';
 import { respond_with_error } from './page/respond_with_error.js';
 import { coalesce_to_error } from '../../utils/error.js';
+import { is_form_content_type } from '../../utils/http.js';
 import { GENERIC_ERROR, handle_fatal_error } from './utils.js';
 import { decode_params, disable_search, normalize_path } from '../../utils/url.js';
 import { exec } from '../../utils/routing.js';
@@ -24,12 +25,10 @@ export async function respond(request, options, state) {
 	let url = new URL(request.url);
 
 	if (options.csrf.check_origin) {
-		const type = request.headers.get('content-type')?.split(';')[0];
-
 		const forbidden =
 			request.method === 'POST' &&
 			request.headers.get('origin') !== url.origin &&
-			(type === 'application/x-www-form-urlencoded' || type === 'multipart/form-data');
+			is_form_content_type(request);
 
 		if (forbidden) {
 			return new Response(`Cross-site ${request.method} form submissions are forbidden`, {
@@ -292,7 +291,7 @@ export async function respond(request, options, state) {
 					// 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
+						// 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];
diff --git a/packages/kit/src/runtime/server/page/actions.js b/packages/kit/src/runtime/server/page/actions.js
index 6f9507e87..02c038d5e 100644
--- a/packages/kit/src/runtime/server/page/actions.js
+++ b/packages/kit/src/runtime/server/page/actions.js
@@ -1,6 +1,6 @@
 import { error, json } from '../../../exports/index.js';
 import { normalize_error } from '../../../utils/error.js';
-import { negotiate } from '../../../utils/http.js';
+import { is_form_content_type, negotiate } from '../../../utils/http.js';
 import { HttpError, Redirect, ValidationError } from '../../control.js';
 import { handle_error_and_jsonify } from '../utils.js';
 
@@ -180,9 +180,10 @@ export async function call_action(event, actions) {
 		throw new Error(`No action with name '${name}' found`);
 	}
 
-	const type = event.request.headers.get('content-type')?.split('; ')[0];
-	if (type !== 'application/x-www-form-urlencoded' && type !== 'multipart/form-data') {
-		throw new Error(`Actions expect form-encoded data (received ${type})`);
+	if (!is_form_content_type(event.request)) {
+		throw new Error(
+			`Actions expect form-encoded data (received ${event.request.headers.get('content-type')}`
+		);
 	}
 
 	return action(event);
diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js
index d9ebd7ae6..954b4e6b7 100644
--- a/packages/kit/src/runtime/server/page/index.js
+++ b/packages/kit/src/runtime/server/page/index.js
@@ -205,10 +205,10 @@ export async function render_page(event, route, page, options, state, resolve_op
 
 					if (err instanceof Redirect) {
 						if (state.prerendering && should_prerender_data) {
-							const body = `window.__sveltekit_data = ${JSON.stringify({
+							const body = devalue.stringify({
 								type: 'redirect',
 								location: err.location
-							})}`;
+							});
 
 							state.prerendering.dependencies.set(data_pathname, {
 								response: new Response(body),
@@ -260,10 +260,10 @@ export async function render_page(event, route, page, options, state, resolve_op
 		}
 
 		if (state.prerendering && should_prerender_data) {
-			const body = `window.__sveltekit_data = ${devalue.uneval({
+			const body = devalue.stringify({
 				type: 'data',
 				nodes: branch.map((branch_node) => branch_node?.server_data)
-			})}`;
+			});
 
 			state.prerendering.dependencies.set(data_pathname, {
 				response: new Response(body),
diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js
index 640e46d8e..08bc5437d 100644
--- a/packages/kit/src/runtime/server/utils.js
+++ b/packages/kit/src/runtime/server/utils.js
@@ -70,17 +70,17 @@ export function allowed_methods(mod) {
 /** @param {any} data */
 export function data_response(data) {
 	const headers = {
-		'content-type': 'application/javascript',
+		'content-type': 'application/json',
 		'cache-control': 'private, no-store'
 	};
 
 	try {
-		return new Response(`window.__sveltekit_data = ${devalue.uneval(data)}`, { headers });
+		return new Response(devalue.stringify(data), { headers });
 	} catch (e) {
 		const error = /** @type {any} */ (e);
 		const match = /\[(\d+)\]\.data\.(.+)/.exec(error.path);
 		const message = match ? `${error.message} (data.${match[2]})` : error.message;
-		return new Response(`throw new Error(${JSON.stringify(message)})`, { headers });
+		return new Response(JSON.stringify(message), { headers, status: 500 });
 	}
 }
 
diff --git a/packages/kit/src/utils/http.js b/packages/kit/src/utils/http.js
index 434a0f1f8..31721d10c 100644
--- a/packages/kit/src/utils/http.js
+++ b/packages/kit/src/utils/http.js
@@ -53,3 +53,20 @@ export function negotiate(accept, types) {
 
 	return accepted;
 }
+
+/**
+ * Returns `true` if the request contains a `content-type` header with the given type
+ * @param {Request} request
+ * @param  {...string} types
+ */
+export function is_content_type(request, ...types) {
+	const type = request.headers.get('content-type')?.split(';', 1)[0].trim() ?? '';
+	return types.includes(type);
+}
+
+/**
+ * @param {Request} request
+ */
+export function is_form_content_type(request) {
+	return is_content_type(request, 'application/x-www-form-urlencoded', 'multipart/form-data');
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 31cb20af9..ae49ab11d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -5,9 +5,9 @@ importers:
   .:
     specifiers:
       '@changesets/cli': ^2.22.0
-      '@rollup/plugin-commonjs': ^22.0.1
-      '@rollup/plugin-json': ^4.1.0
-      '@rollup/plugin-node-resolve': ^14.1.0
+      '@rollup/plugin-commonjs': ^23.0.0
+      '@rollup/plugin-json': ^5.0.0
+      '@rollup/plugin-node-resolve': ^15.0.0
       '@svitejs/changesets-changelog-github-compact': ^0.1.1
       playwright: ^1.25.0
       prettier: ^2.6.2
@@ -18,9 +18,9 @@ importers:
       typescript: ^4.8.2
     devDependencies:
       '@changesets/cli': 2.23.0
-      '@rollup/plugin-commonjs': 22.0.1_rollup@2.78.1
-      '@rollup/plugin-json': 4.1.0_rollup@2.78.1
-      '@rollup/plugin-node-resolve': 14.1.0_rollup@2.78.1
+      '@rollup/plugin-commonjs': 23.0.0_rollup@2.78.1
+      '@rollup/plugin-json': 5.0.0_rollup@2.78.1
+      '@rollup/plugin-node-resolve': 15.0.0_rollup@2.78.1
       '@svitejs/changesets-changelog-github-compact': 0.1.1
       playwright: 1.25.0
       prettier: 2.7.1
@@ -83,9 +83,9 @@ importers:
     specifiers:
       '@iarna/toml': ^2.2.5
       '@netlify/functions': ^1.0.0
-      '@rollup/plugin-commonjs': ^22.0.1
-      '@rollup/plugin-json': ^4.1.0
-      '@rollup/plugin-node-resolve': ^14.1.0
+      '@rollup/plugin-commonjs': ^23.0.0
+      '@rollup/plugin-json': ^5.0.0
+      '@rollup/plugin-node-resolve': ^15.0.0
       '@sveltejs/kit': workspace:*
       '@types/node': ^16.11.36
       '@types/set-cookie-parser': ^2.4.2
@@ -101,9 +101,9 @@ importers:
       set-cookie-parser: 2.5.0
     devDependencies:
       '@netlify/functions': 1.0.0
-      '@rollup/plugin-commonjs': 22.0.1_rollup@2.78.1
-      '@rollup/plugin-json': 4.1.0_rollup@2.78.1
-      '@rollup/plugin-node-resolve': 14.1.0_rollup@2.78.1
+      '@rollup/plugin-commonjs': 23.0.0_rollup@2.78.1
+      '@rollup/plugin-json': 5.0.0_rollup@2.78.1
+      '@rollup/plugin-node-resolve': 15.0.0_rollup@2.78.1
       '@sveltejs/kit': link:../kit
       '@types/node': 16.11.42
       '@types/set-cookie-parser': 2.4.2
@@ -114,9 +114,9 @@ importers:
 
   packages/adapter-node:
     specifiers:
-      '@rollup/plugin-commonjs': ^22.0.1
-      '@rollup/plugin-json': ^4.1.0
-      '@rollup/plugin-node-resolve': ^14.1.0
+      '@rollup/plugin-commonjs': ^23.0.0
+      '@rollup/plugin-json': ^5.0.0
+      '@rollup/plugin-node-resolve': ^15.0.0
       '@sveltejs/kit': workspace:*
       '@types/node': ^16.11.36
       c8: ^7.11.3
@@ -127,9 +127,9 @@ importers:
       typescript: ^4.8.2
       uvu: ^0.5.3
     dependencies:
-      '@rollup/plugin-commonjs': 22.0.1_rollup@2.78.1
-      '@rollup/plugin-json': 4.1.0_rollup@2.78.1
-      '@rollup/plugin-node-resolve': 14.1.0_rollup@2.78.1
+      '@rollup/plugin-commonjs': 23.0.0_rollup@2.78.1
+      '@rollup/plugin-json': 5.0.0_rollup@2.78.1
+      '@rollup/plugin-node-resolve': 15.0.0_rollup@2.78.1
       rollup: 2.78.1
     devDependencies:
       '@sveltejs/kit': link:../kit
@@ -1077,54 +1077,52 @@ packages:
   /@polka/url/1.0.0-next.21:
     resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
 
-  /@rollup/plugin-commonjs/22.0.1_rollup@2.78.1:
-    resolution: {integrity: sha512-dGfEZvdjDHObBiP5IvwTKMVeq/tBZGMBHZFMdIV1ClMM/YoWS34xrHFGfag9SN2ZtMgNZRFruqvxZQEa70O6nQ==}
-    engines: {node: '>= 12.0.0'}
+  /@rollup/plugin-commonjs/23.0.0_rollup@2.78.1:
+    resolution: {integrity: sha512-JbrTRyDNtLQj/rhl7RFUuYXwQ2fac+33oLDAu2k++WD95zweyo28UAomLVA0JMGx4vmCa7Nw4T6k/1F6lelExg==}
+    engines: {node: '>=14.0.0'}
     peerDependencies:
-      rollup: ^2.68.0
+      rollup: ^2.68.0||^3.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
     dependencies:
-      '@rollup/pluginutils': 3.1.0_rollup@2.78.1
+      '@rollup/pluginutils': 4.2.1
       commondir: 1.0.1
       estree-walker: 2.0.2
-      glob: 7.2.3
+      glob: 8.0.3
       is-reference: 1.2.1
-      magic-string: 0.25.9
-      resolve: 1.22.1
+      magic-string: 0.26.6
       rollup: 2.78.1
 
-  /@rollup/plugin-json/4.1.0_rollup@2.78.1:
-    resolution: {integrity: sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw==}
+  /@rollup/plugin-json/5.0.0_rollup@2.78.1:
+    resolution: {integrity: sha512-LsWDA5wJs/ggzakVuKQhZo7HPRcQZgBa3jWIVxQSFxaRToUGNi8ZBh3+k/gQ+1eInVYJgn4WBRCUkmoDrmmGzw==}
+    engines: {node: '>=14.0.0'}
     peerDependencies:
-      rollup: ^1.20.0 || ^2.0.0
+      rollup: ^1.20.0||^2.0.0||^3.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
     dependencies:
-      '@rollup/pluginutils': 3.1.0_rollup@2.78.1
+      '@rollup/pluginutils': 4.2.1
       rollup: 2.78.1
 
-  /@rollup/plugin-node-resolve/14.1.0_rollup@2.78.1:
-    resolution: {integrity: sha512-5G2niJroNCz/1zqwXtk0t9+twOSDlG00k1Wfd7bkbbXmwg8H8dvgHdIWAun53Ps/rckfvOC7scDBjuGFg5OaWw==}
-    engines: {node: '>= 10.0.0'}
+  /@rollup/plugin-node-resolve/15.0.0_rollup@2.78.1:
+    resolution: {integrity: sha512-iwJbzfTzlzDDQcGmkS7EkCKwe2kSkdBrjX87Fy/KrNjr6UNnLpod0t6X66e502LRe5JJCA4FFqrEscWPnZAkig==}
+    engines: {node: '>=14.0.0'}
     peerDependencies:
-      rollup: ^2.78.0
+      rollup: ^2.78.0||^3.0.0
+    peerDependenciesMeta:
+      rollup:
+        optional: true
     dependencies:
-      '@rollup/pluginutils': 3.1.0_rollup@2.78.1
-      '@types/resolve': 1.17.1
+      '@rollup/pluginutils': 4.2.1
+      '@types/resolve': 1.20.2
       deepmerge: 4.2.2
-      is-builtin-module: 3.1.0
+      is-builtin-module: 3.2.0
       is-module: 1.0.0
       resolve: 1.22.1
       rollup: 2.78.1
 
-  /@rollup/pluginutils/3.1.0_rollup@2.78.1:
-    resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
-    engines: {node: '>= 8.0.0'}
-    peerDependencies:
-      rollup: ^1.20.0||^2.0.0
-    dependencies:
-      '@types/estree': 0.0.39
-      estree-walker: 1.0.1
-      picomatch: 2.3.1
-      rollup: 2.78.1
-
   /@rollup/pluginutils/4.2.1:
     resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
     engines: {node: '>= 8.0.0'}
@@ -1181,9 +1179,6 @@ packages:
     resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==}
     dev: false
 
-  /@types/estree/0.0.39:
-    resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
-
   /@types/estree/0.0.52:
     resolution: {integrity: sha512-BZWrtCU0bMVAIliIV+HJO1f1PR41M7NKjfxrFJwwhKI1KwhwOxYw1SXg9ao+CIMt774nFuGiG6eU+udtbEI9oQ==}
 
@@ -1227,6 +1222,7 @@ packages:
 
   /@types/node/18.0.0:
     resolution: {integrity: sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==}
+    dev: true
 
   /@types/normalize-package-data/2.4.1:
     resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
@@ -1246,10 +1242,8 @@ packages:
     resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
     dev: true
 
-  /@types/resolve/1.17.1:
-    resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
-    dependencies:
-      '@types/node': 18.0.0
+  /@types/resolve/1.20.2:
+    resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
 
   /@types/sade/1.7.4:
     resolution: {integrity: sha512-6ys13kmtlY0aIOz4KtMdeBD9BHs6vSE3aRcj4vAZqXjypT2el8WZt6799CMjElVgh1cbOH/t3vrpQ4IpwytcPA==}
@@ -1474,6 +1468,11 @@ packages:
       balanced-match: 1.0.2
       concat-map: 0.0.1
 
+  /brace-expansion/2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+    dependencies:
+      balanced-match: 1.0.2
+
   /braces/3.0.2:
     resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
     engines: {node: '>=8'}
@@ -2163,9 +2162,6 @@ packages:
     resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==}
     dev: false
 
-  /estree-walker/1.0.1:
-    resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
-
   /estree-walker/2.0.2:
     resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
 
@@ -2411,6 +2407,16 @@ packages:
       once: 1.4.0
       path-is-absolute: 1.0.1
 
+  /glob/8.0.3:
+    resolution: {integrity: sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 5.1.0
+      once: 1.4.0
+
   /globalyzer/0.1.0:
     resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==}
 
@@ -2600,8 +2606,8 @@ packages:
       has-tostringtag: 1.0.0
     dev: true
 
-  /is-builtin-module/3.1.0:
-    resolution: {integrity: sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==}
+  /is-builtin-module/3.2.0:
+    resolution: {integrity: sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==}
     engines: {node: '>=6'}
     dependencies:
       builtin-modules: 3.3.0
@@ -2896,6 +2902,7 @@ packages:
     resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
     dependencies:
       sourcemap-codec: 1.4.8
+    dev: true
 
   /magic-string/0.26.2:
     resolution: {integrity: sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==}
@@ -2910,6 +2917,12 @@ packages:
     dependencies:
       sourcemap-codec: 1.4.8
 
+  /magic-string/0.26.6:
+    resolution: {integrity: sha512-6d+3bFybzyQFJYSoRsl9ZC0wheze8M1LrQC7tNMRqXR4izUTDOLMd9BtSuExK9iAukFh+s5K0WAhc/dlQ+HKYA==}
+    engines: {node: '>=12'}
+    dependencies:
+      sourcemap-codec: 1.4.8
+
   /make-dir/3.1.0:
     resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
     engines: {node: '>=8'}
@@ -2993,6 +3006,12 @@ packages:
     dependencies:
       brace-expansion: 1.1.11
 
+  /minimatch/5.1.0:
+    resolution: {integrity: sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==}
+    engines: {node: '>=10'}
+    dependencies:
+      brace-expansion: 2.0.1
+
   /minimist-options/4.1.0:
     resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
     engines: {node: '>= 6'}
diff --git a/sites/kit.svelte.dev/src/lib/search/SearchResultList.svelte b/sites/kit.svelte.dev/src/lib/search/SearchResultList.svelte
index 70b5bc611..cd752798c 100644
--- a/sites/kit.svelte.dev/src/lib/search/SearchResultList.svelte
+++ b/sites/kit.svelte.dev/src/lib/search/SearchResultList.svelte
@@ -103,6 +103,8 @@
 		display: block;
 		white-space: nowrap;
 		line-height: 1;
+		overflow: hidden;
+		text-overflow: ellipsis;
 	}
 
 	a strong {
@@ -113,8 +115,6 @@
 	a span {
 		font-size: 1.2rem;
 		color: #737373;
-		overflow: hidden;
-		text-overflow: ellipsis;
 		margin: 0.4rem 0 0 0;
 	}
 


Test patch:
diff --git a/packages/kit/test/apps/basics/src/routes/actions/success-data/+page.svelte b/packages/kit/test/apps/basics/src/routes/actions/success-data/+page.svelte
index e3699100e..e784d2b20 100644
--- a/packages/kit/test/apps/basics/src/routes/actions/success-data/+page.svelte
+++ b/packages/kit/test/apps/basics/src/routes/actions/success-data/+page.svelte
@@ -2,10 +2,12 @@
 	/** @type {import('./$types').ActionData} */
 	export let form;
 
-	async function submit() {
+	async function submit({ submitter }) {
 		const res = await fetch(this.action, {
 			method: 'POST',
-			body: new FormData(this),
+			body: submitter.getAttribute('formenctype') === 'multipart/form-data'
+				? new FormData(this)
+				: new URLSearchParams({ username: this['username'].value }),
 			headers: {
 				accept: 'application/json'
 			}
@@ -19,5 +21,6 @@
 
 <form method="post" on:submit|preventDefault={submit}>
 	<input name="username" type="text" />
-	<button>Submit</button>
+	<button formenctype="multipart/form-data">Submit</button>
+	<button formenctype="application/x-www-form-urlencoded">Submit</button>
 </form>
diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js
index 205e420a0..b847d6dfd 100644
--- a/packages/kit/test/apps/basics/test/client.test.js
+++ b/packages/kit/test/apps/basics/test/client.test.js
@@ -436,7 +436,7 @@ test.describe('Load', () => {
 		await expect(page.locator('p')).toHaveText('Count is 2');
 	});
 
-	test('__data.js has cache-control: private, no-store', async ({ page, clicknav }) => {
+	test('__data.json has cache-control: private, no-store', async ({ page, clicknav }) => {
 		await page.goto('/load/server-data-nostore?x=1');
 
 		const [response] = await Promise.all([
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index 93816939b..0588cdc6b 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -576,7 +576,7 @@ test.describe('Errors', () => {
 		);
 
 		const { status, name, message, stack, fancy } = read_errors(
-			'/errors/page-endpoint/get-implicit/__data.js'
+			'/errors/page-endpoint/get-implicit/__data.json'
 		);
 		expect(status).toBe(undefined);
 		expect(name).toBe('FancyError');
@@ -1773,7 +1773,7 @@ test.describe('Actions', () => {
 		}
 	});
 
-	test('Success data is returned', async ({ page }) => {
+	test('Success data as form-data is returned', async ({ page }) => {
 		await page.goto('/actions/success-data');
 
 		expect(await page.textContent('pre')).toBe(JSON.stringify(null));
@@ -1781,12 +1781,26 @@ test.describe('Actions', () => {
 		await page.type('input[name="username"]', 'foo');
 		await Promise.all([
 			page.waitForRequest((request) => request.url().includes('/actions/success-data')),
-			page.click('button')
+			page.click('button[formenctype="multipart/form-data"]')
 		]);
 
 		await expect(page.locator('pre')).toHaveText(JSON.stringify({ result: 'foo' }));
 	});
 
+	test('Success data as form-urlencoded is returned', async ({ page }) => {
+		await page.goto('/actions/success-data');
+
+		expect(await page.textContent('pre')).toBe(JSON.stringify(null));
+
+		await page.type('input[name="username"]', 'bar');
+		await Promise.all([
+			page.waitForRequest((request) => request.url().includes('/actions/success-data')),
+			page.click('button[formenctype="application/x-www-form-urlencoded"]')
+		]);
+
+		await expect(page.locator('pre')).toHaveText(JSON.stringify({ result: 'bar' }));
+	});
+
 	test('applyAction updates form prop', async ({ page, javaScriptEnabled }) => {
 		await page.goto('/actions/update-form');
 		expect(await page.textContent('pre')).toBe(JSON.stringify(null));
@@ -1859,6 +1873,7 @@ test.describe('Actions', () => {
 
 		await expect(page.locator('pre.formdata1')).toHaveText(JSON.stringify({ result: 'foo' }));
 		await expect(page.locator('pre.formdata2')).toHaveText(JSON.stringify({ result: 'foo' }));
+		await expect(page.locator('input[name=username]')).toHaveValue('');
 	});
 
 	test('use:enhance abort controller', async ({ page, javaScriptEnabled }) => {
diff --git a/packages/kit/test/apps/options/test/test.js b/packages/kit/test/apps/options/test/test.js
index 855daab6c..f5ebe352c 100644
--- a/packages/kit/test/apps/options/test/test.js
+++ b/packages/kit/test/apps/options/test/test.js
@@ -1,3 +1,4 @@
+import { parse } from 'devalue';
 import { expect } from '@playwright/test';
 import { start_server, test } from '../../../utils.js';
 
@@ -162,13 +163,10 @@ test.describe('trailingSlash', () => {
 	});
 
 	test('can fetch data from page-endpoint', async ({ request }) => {
-		const r = await request.get('/path-base/page-endpoint/__data.js');
-		const code = await r.text();
+		const r = await request.get('/path-base/page-endpoint/__data.json');
+		const data = parse(await r.text());
 
-		const window = {};
-		new Function('window', code)(window);
-
-		expect(window.__sveltekit_data).toEqual({
+		expect(data).toEqual({
 			type: 'data',
 			nodes: [null, { type: 'data', data: { message: 'hi' }, uses: {} }]
 		});
@@ -199,7 +197,7 @@ test.describe('trailingSlash', () => {
 			expect(requests.filter((req) => req.endsWith('.js')).length).toBeGreaterThan(0);
 		}
 
-		expect(requests.includes(`/path-base/prefetching/prefetched/__data.js`)).toBe(true);
+		expect(requests.includes(`/path-base/prefetching/prefetched/__data.json`)).toBe(true);
 
 		requests = [];
 		await app.goto('/path-base/prefetching/prefetched');
diff --git a/packages/kit/test/prerendering/basics/test/test.js b/packages/kit/test/prerendering/basics/test/test.js
index f3ef0a1dc..b6a9cfe22 100644
--- a/packages/kit/test/prerendering/basics/test/test.js
+++ b/packages/kit/test/prerendering/basics/test/test.js
@@ -1,5 +1,6 @@
 import * as fs from 'fs';
 import { fileURLToPath } from 'url';
+import { parse } from 'devalue';
 import { test } from 'uvu';
 import * as assert from 'uvu/assert';
 
@@ -25,11 +26,9 @@ 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 code = read('redirect-server/__data.js');
-	const window = {};
-	new Function('window', code)(window);
+	const data = parse(read('redirect-server/__data.json'));
 
-	assert.equal(window.__sveltekit_data, {
+	assert.equal(data, {
 		type: 'redirect',
 		location: 'https://example.com/redirected'
 	});
@@ -77,11 +76,9 @@ test('loads a file with spaces in the filename', () => {
 	assert.ok(content.includes('<h1>answer: 42</h1>'), content);
 });
 
-test('generates __data.js file for shadow endpoints', () => {
-	const window = {};
-
-	new Function('window', read('__data.js'))(window);
-	assert.equal(window.__sveltekit_data, {
+test('generates __data.json file for shadow endpoints', () => {
+	let data = parse(read('__data.json'));
+	assert.equal(data, {
 		type: 'data',
 		nodes: [
 			null,
@@ -93,8 +90,8 @@ test('generates __data.js file for shadow endpoints', () => {
 		]
 	});
 
-	new Function('window', read('shadowed-get/__data.js'))(window);
-	assert.equal(window.__sveltekit_data, {
+	data = parse(read('shadowed-get/__data.json'));
+	assert.equal(data, {
 		type: 'data',
 		nodes: [
 			null,
@@ -109,7 +106,7 @@ test('generates __data.js file for shadow endpoints', () => {
 
 test('does not prerender page with shadow endpoint with non-load handler', () => {
 	assert.ok(!fs.existsSync(`${build}/shadowed-post.html`));
-	assert.ok(!fs.existsSync(`${build}/shadowed-post/__data.js`));
+	assert.ok(!fs.existsSync(`${build}/shadowed-post/__data.json`));
 });
 
 test('decodes paths when writing files', () => {
@@ -183,10 +180,9 @@ test('prerenders binary data', async () => {
 });
 
 test('fetches data from local endpoint', () => {
-	const window = {};
-	new Function('window', read('origin/__data.js'))(window);
+	const data = parse(read('origin/__data.json'));
 
-	assert.equal(window.__sveltekit_data, {
+	assert.equal(data, {
 		type: 'data',
 		nodes: [
 			null,
diff --git a/packages/kit/test/prerendering/trailing-slash/test/test.js b/packages/kit/test/prerendering/trailing-slash/test/test.js
index 0344b6129..8a7c6550a 100644
--- a/packages/kit/test/prerendering/trailing-slash/test/test.js
+++ b/packages/kit/test/prerendering/trailing-slash/test/test.js
@@ -11,7 +11,7 @@ const read = (file) => fs.readFileSync(`${build}/${file}`, 'utf-8');
 test('prerendered.paths omits trailing slashes for endpoints', () => {
 	const content = read('service-worker.js');
 
-	for (const path of ['/page/', '/page/__data.js', '/standalone-endpoint.json']) {
+	for (const path of ['/page/', '/page/__data.json', '/standalone-endpoint.json']) {
 		assert.ok(content.includes(`"${path}"`), `Missing ${path}`);
 	}
 });