Repository: sveltejs/kit (js)

Issue:
+layout.server.js not correctly passing data down during client-side hydration
### Describe the bug

After client side navigation, calling `await parent()` from within a +page.server.js exported load function returns `{"data":null}` instead of the value returned from the +layout.server.js exported load function

### Reproduction

- Load https://stackblitz.com/edit/sveltejs-kit-template-default-8az3ew?file=src/routes/+page.server.js
- Note that layout data, and page data are correct initially
- Click on 'Another page' link, to do a client side navigation to a new page
- Note that layout data is now incorrect (as is the layout data that was merged into page data)

### Logs

_No response_

### System Info

```shell
stackblitz default template fork
```


### Severity

serious, but I can work around it

### Additional Information

_No response_

Pull request:
[fix] correctly provide server parent data
Fixes #5904

- the data property was not unwrapped on the server, resulting in wrong results on client side navigations
- the root layout data was ommitted because of a wrong undefined check

### 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.
- [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


Patch:
diff --git a/.changeset/cool-birds-attack.md b/.changeset/cool-birds-attack.md
new file mode 100644
index 000000000..b36514a98
--- /dev/null
+++ b/.changeset/cool-birds-attack.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Correctly provide server parent data
diff --git a/.changeset/lovely-boxes-give.md b/.changeset/lovely-boxes-give.md
new file mode 100644
index 000000000..4f4218c15
--- /dev/null
+++ b/.changeset/lovely-boxes-give.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[feat] `$env/static/*` are now virtual to prevent writing sensitive values to disk
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 49501cca8..21659e46e 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -499,6 +499,7 @@
     "loud-shrimps-drive",
     "lovely-apricots-fail",
     "lovely-baboons-suffer",
+    "lovely-boxes-give",
     "lovely-flies-care",
     "lovely-foxes-visit",
     "lovely-hairs-applaud",
@@ -984,6 +985,7 @@
     "ten-mice-kneel",
     "ten-plants-sleep",
     "tender-actors-tease",
+    "tender-bottles-cheer",
     "tender-buckets-turn",
     "tender-buses-talk",
     "tender-geckos-agree",
diff --git a/.changeset/tender-bottles-cheer.md b/.changeset/tender-bottles-cheer.md
new file mode 100644
index 000000000..fd86682f2
--- /dev/null
+++ b/.changeset/tender-bottles-cheer.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Lazy load Svelte components to reenable no-ssr use cases
diff --git a/documentation/docs/01-project-structure.md b/documentation/docs/01-project-structure.md
index dff7f3d21..2eac580f2 100644
--- a/documentation/docs/01-project-structure.md
+++ b/documentation/docs/01-project-structure.md
@@ -17,6 +17,8 @@ my-project/
 │ └ hooks.js
 ├ static/
 │ └ [your static assets]
+├ tests/
+│ └ [your tests]
 ├ package.json
 ├ svelte.config.js
 ├ tsconfig.json
@@ -48,6 +50,10 @@ You can use `.ts` files instead of `.js` files, if using TypeScript.
 
 Any static assets that should be served as-is, like `robots.txt` or `favicon.png`, go in here.
 
+#### tests
+
+If you chose to add tests to your project during `npm create svelte@latest`, they will live in this directory.
+
 #### package.json
 
 Your `package.json` file must include `@sveltejs/kit`, `svelte` and `vite` as `devDependencies`.
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index ad4d76ca7..dc5d2fc4a 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,17 @@
 # @sveltejs/kit
 
+## 1.0.0-next.413
+
+### Patch Changes
+
+- [feat] `$env/static/*` are now virtual to prevent writing sensitive values to disk ([#5825](https://github.com/sveltejs/kit/pull/5825))
+
+## 1.0.0-next.412
+
+### Patch Changes
+
+- Lazy load Svelte components to reenable no-ssr use cases ([#5930](https://github.com/sveltejs/kit/pull/5930))
+
 ## 1.0.0-next.411
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index a58cc1041..a51221444 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.411",
+	"version": "1.0.0-next.413",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/src/core/constants.js b/packages/kit/src/core/constants.js
index 75388913b..8dc9aab27 100644
--- a/packages/kit/src/core/constants.js
+++ b/packages/kit/src/core/constants.js
@@ -1,3 +1,5 @@
 // in `vite dev` and `vite preview`, we use a fake asset path so that we can
 // serve local assets while verifying that requests are correctly prefixed
 export const SVELTE_KIT_ASSETS = '/_svelte_kit_assets';
+
+export const GENERATED_COMMENT = '// this file is generated — do not edit it\n';
diff --git a/packages/kit/src/core/env.js b/packages/kit/src/core/env.js
new file mode 100644
index 000000000..f18f1c598
--- /dev/null
+++ b/packages/kit/src/core/env.js
@@ -0,0 +1,91 @@
+import { GENERATED_COMMENT } from './constants.js';
+
+/**
+ * @param {string} id
+ * @param {Record<string, string>} env
+ * @returns {string}
+ */
+export function create_module(id, env) {
+	/** @type {string[]} */
+	const declarations = [];
+
+	for (const key in env) {
+		if (!valid_identifier.test(key) || reserved.has(key)) {
+			continue;
+		}
+
+		const comment = `/** @type {import('${id}').${key}} */`;
+		const declaration = `export const ${key} = ${JSON.stringify(env[key])};`;
+
+		declarations.push(`${comment}\n${declaration}`);
+	}
+
+	return GENERATED_COMMENT + declarations.join('\n\n');
+}
+
+/**
+ * @param {string} id
+ * @param {Record<string, string>} env
+ * @returns {string}
+ */
+export function create_types(id, env) {
+	const declarations = Object.keys(env)
+		.filter((k) => valid_identifier.test(k))
+		.map((k) => `\texport const ${k}: string;`)
+		.join('\n');
+
+	return `declare module '${id}' {\n${declarations}\n}`;
+}
+
+export const reserved = new Set([
+	'do',
+	'if',
+	'in',
+	'for',
+	'let',
+	'new',
+	'try',
+	'var',
+	'case',
+	'else',
+	'enum',
+	'eval',
+	'null',
+	'this',
+	'true',
+	'void',
+	'with',
+	'await',
+	'break',
+	'catch',
+	'class',
+	'const',
+	'false',
+	'super',
+	'throw',
+	'while',
+	'yield',
+	'delete',
+	'export',
+	'import',
+	'public',
+	'return',
+	'static',
+	'switch',
+	'typeof',
+	'default',
+	'extends',
+	'finally',
+	'package',
+	'private',
+	'continue',
+	'debugger',
+	'function',
+	'arguments',
+	'interface',
+	'protected',
+	'implements',
+	'instanceof'
+]);
+
+export const valid_identifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
diff --git a/packages/kit/src/core/sync/utils.js b/packages/kit/src/core/sync/utils.js
index dafb6350f..c0ee2f2cd 100644
--- a/packages/kit/src/core/sync/utils.js
+++ b/packages/kit/src/core/sync/utils.js
@@ -42,56 +42,3 @@ export function trim(str) {
 	const pattern = new RegExp(`^${indentation}`, 'gm');
 	return str.replace(pattern, '').trim();
 }
-
-export const reserved = new Set([
-	'do',
-	'if',
-	'in',
-	'for',
-	'let',
-	'new',
-	'try',
-	'var',
-	'case',
-	'else',
-	'enum',
-	'eval',
-	'null',
-	'this',
-	'true',
-	'void',
-	'with',
-	'await',
-	'break',
-	'catch',
-	'class',
-	'const',
-	'false',
-	'super',
-	'throw',
-	'while',
-	'yield',
-	'delete',
-	'export',
-	'import',
-	'public',
-	'return',
-	'static',
-	'switch',
-	'typeof',
-	'default',
-	'extends',
-	'finally',
-	'package',
-	'private',
-	'continue',
-	'debugger',
-	'function',
-	'arguments',
-	'interface',
-	'protected',
-	'implements',
-	'instanceof'
-]);
-
-export const valid_identifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
diff --git a/packages/kit/src/core/sync/write_ambient.js b/packages/kit/src/core/sync/write_ambient.js
index 800e1ccce..28d6b919f 100644
--- a/packages/kit/src/core/sync/write_ambient.js
+++ b/packages/kit/src/core/sync/write_ambient.js
@@ -1,9 +1,9 @@
 import path from 'path';
-import colors from 'kleur';
 import { get_env } from '../../vite/utils.js';
-import { write_if_changed, reserved, valid_identifier } from './utils.js';
+import { GENERATED_COMMENT } from '../constants.js';
+import { create_types } from '../env.js';
+import { write_if_changed } from './utils.js';
 
-const autogen_comment = '// this file is generated — do not edit it\n';
 const types_reference = '/// <reference types="@sveltejs/kit" />\n\n';
 
 /**
@@ -16,72 +16,12 @@ const types_reference = '/// <reference types="@sveltejs/kit" />\n\n';
 export function write_ambient(config, mode) {
 	const env = get_env(mode, config.env.publicPrefix);
 
-	// TODO when testing src, `$app` points at `src/runtime/app`... will
-	// probably need to fiddle with aliases
-	write_if_changed(
-		path.join(config.outDir, 'runtime/env/static/public.js'),
-		create_env_module('$env/static/public', env.public)
-	);
-
-	write_if_changed(
-		path.join(config.outDir, 'runtime/env/static/private.js'),
-		create_env_module('$env/static/private', env.private)
-	);
-
 	write_if_changed(
 		path.join(config.outDir, 'ambient.d.ts'),
-		autogen_comment +
+		GENERATED_COMMENT +
 			types_reference +
-			create_env_types('$env/static/public', env.public) +
+			create_types('$env/static/public', env.public) +
 			'\n\n' +
-			create_env_types('$env/static/private', env.private)
+			create_types('$env/static/private', env.private)
 	);
 }
-
-/**
- * @param {string} id
- * @param {Record<string, string>} env
- * @returns {string}
- */
-function create_env_module(id, env) {
-	/** @type {string[]} */
-	const declarations = [];
-
-	for (const key in env) {
-		const warning = !valid_identifier.test(key)
-			? 'not a valid identifier'
-			: reserved.has(key)
-			? 'a reserved word'
-			: null;
-
-		if (warning) {
-			console.error(
-				colors
-					.bold()
-					.yellow(`Omitting environment variable "${key}" from ${id} as it is ${warning}`)
-			);
-			continue;
-		}
-
-		const comment = `/** @type {import('${id}').${key}} */`;
-		const declaration = `export const ${key} = ${JSON.stringify(env[key])};`;
-
-		declarations.push(`${comment}\n${declaration}`);
-	}
-
-	return autogen_comment + declarations.join('\n\n');
-}
-
-/**
- * @param {string} id
- * @param {Record<string, string>} env
- * @returns {string}
- */
-function create_env_types(id, env) {
-	const declarations = Object.keys(env)
-		.filter((k) => valid_identifier.test(k))
-		.map((k) => `\texport const ${k}: string;`)
-		.join('\n');
-
-	return `declare module '${id}' {\n${declarations}\n}`;
-}
diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js
index c35a45c6a..d780c5458 100644
--- a/packages/kit/src/runtime/server/index.js
+++ b/packages/kit/src/runtime/server/index.js
@@ -250,7 +250,8 @@ export async function respond(request, options, state) {
 								try {
 									if (error) return;
 
-									const node = n ? await options.manifest._.nodes[n]() : undefined;
+									// == because it could be undefined (in dev) or null (in build, because of JSON.stringify)
+									const node = n == undefined ? n : await options.manifest._.nodes[n]();
 									return {
 										// TODO return `uses`, so we can reuse server data effectively
 										data: await load_server_data({
@@ -260,7 +261,11 @@ export async function respond(request, options, state) {
 												/** @type {import('types').JSONObject} */
 												const data = {};
 												for (let j = 0; j < i; j += 1) {
-													Object.assign(data, await promises[j]);
+													const parent = await promises[j];
+													if (!parent || parent instanceof HttpError || 'error' in parent) {
+														return data;
+													}
+													Object.assign(data, parent.data);
 												}
 												return data;
 											}
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index ea4969da9..1e1fc04b8 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -79,20 +79,6 @@ export async function render_response({
 	}
 
 	if (resolve_opts.ssr) {
-		for (const { node } of branch) {
-			if (node.imports) {
-				node.imports.forEach((url) => modulepreloads.add(url));
-			}
-
-			if (node.stylesheets) {
-				node.stylesheets.forEach((url) => stylesheets.add(url));
-			}
-
-			if (node.inline_styles) {
-				Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
-			}
-		}
-
 		const session = writable($session);
 
 		/** @type {Record<string, any>} */
@@ -112,7 +98,7 @@ export async function render_response({
 				url: state.prerendering ? new PrerenderingURL(event.url) : event.url,
 				data: branch.reduce((acc, { data }) => (Object.assign(acc, data), acc), {})
 			},
-			components: branch.map(({ node }) => node.component)
+			components: await Promise.all(branch.map(({ node }) => node.component()))
 		};
 
 		// TODO remove this for 1.0
@@ -143,6 +129,20 @@ export async function render_response({
 		}
 
 		rendered = options.root.render(props);
+
+		for (const { node } of branch) {
+			if (node.imports) {
+				node.imports.forEach((url) => modulepreloads.add(url));
+			}
+
+			if (node.stylesheets) {
+				node.stylesheets.forEach((url) => stylesheets.add(url));
+			}
+
+			if (node.inline_styles) {
+				Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
+			}
+		}
 	} else {
 		rendered = { head: '', html: '', css: { code: '', map: null } };
 	}
diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js
index 7e3df1a9d..e7c358fda 100644
--- a/packages/kit/src/vite/build/build_server.js
+++ b/packages/kit/src/vite/build/build_server.js
@@ -239,6 +239,8 @@ export async function build_server(options, client) {
 		/** @type {string[]} */
 		const imports = [];
 
+		// String representation of
+		/** @type {import('types').SSRNode} */
 		/** @type {string[]} */
 		const exports = [`export const index = ${i};`];
 
@@ -255,7 +257,9 @@ export async function build_server(options, client) {
 			stylesheets.push(...entry.stylesheets);
 
 			exports.push(
-				`export { default as component } from '../${vite_manifest[node.component].file}';`,
+				`export const component = async () => (await import('../${
+					vite_manifest[node.component].file
+				}')).default;`,
 				`export const file = '${entry.file}';` // TODO what is this?
 			);
 		}
diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js
index 5b1968236..438c94fb0 100644
--- a/packages/kit/src/vite/dev/index.js
+++ b/packages/kit/src/vite/dev/index.js
@@ -82,19 +82,23 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) {
 						result.stylesheets = [];
 
 						if (node.component) {
-							const { module, module_node, url } = await resolve(node.component);
-
-							module_nodes.push(module_node);
-
-							result.component = module.default;
-							result.file = url.endsWith('.svelte') ? url : url + '?import'; // TODO what is this for?
-
-							prevent_illegal_vite_imports(
-								module_node,
-								illegal_imports,
-								extensions,
-								svelte_config.kit.outDir
-							);
+							result.component = async () => {
+								const { module_node, module, url } = await resolve(
+									/** @type {string} */ (node.component)
+								);
+
+								module_nodes.push(module_node);
+								result.file = url.endsWith('.svelte') ? url : url + '?import'; // TODO what is this for?
+
+								prevent_illegal_vite_imports(
+									module_node,
+									illegal_imports,
+									extensions,
+									svelte_config.kit.outDir
+								);
+
+								return module.default;
+							};
 						}
 
 						if (node.shared) {
diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js
index 9f2f3a196..70a9cf482 100644
--- a/packages/kit/src/vite/index.js
+++ b/packages/kit/src/vite/index.js
@@ -14,8 +14,9 @@ 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 { preview } from './preview/index.js';
-import { get_aliases, resolve_entry, prevent_illegal_rollup_imports } from './utils.js';
+import { get_aliases, resolve_entry, prevent_illegal_rollup_imports, get_env } from './utils.js';
 import { fileURLToPath } from 'node:url';
+import { create_module } from '../core/env.js';
 
 const cwd = process.cwd();
 
@@ -107,6 +108,9 @@ function kit() {
 	/** @type {string | undefined} */
 	let deferred_warning;
 
+	/** @type {{ public: Record<string, string>; private: Record<string, string> }} */
+	let env;
+
 	/**
 	 * @type {{
 	 *   build_dir: string;
@@ -191,13 +195,13 @@ function kit() {
 		 * @see https://vitejs.dev/guide/api-plugin.html#config
 		 */
 		async config(config, config_env) {
-			// The config is created in build_server for SSR mode and passed inline
-			if (config.build?.ssr) {
-				return;
-			}
-
 			vite_config_env = config_env;
 			svelte_config = await load_config();
+			env = get_env(vite_config_env.mode, svelte_config.kit.env.publicPrefix);
+
+			// The config is created in build_server for SSR mode and passed inline
+			if (config.build?.ssr) return;
+
 			is_build = config_env.command === 'build';
 
 			paths = {
@@ -269,6 +273,20 @@ function kit() {
 			return result;
 		},
 
+		async resolveId(id) {
+			// treat $env/static/[public|private] as virtual
+			if (id.startsWith('$env/static/')) return `\0${id}`;
+		},
+
+		async load(id) {
+			switch (id) {
+				case '\0$env/static/private':
+					return create_module('$env/static/private', env.private);
+				case '\0$env/static/public':
+					return create_module('$env/static/public', env.public);
+			}
+		},
+
 		/**
 		 * Stores the final config.
 		 */
@@ -432,9 +450,10 @@ function kit() {
 				await adapt(svelte_config, build_data, prerendered, { log });
 			} else {
 				console.log(colors.bold().yellow('\nNo adapter specified'));
-				// prettier-ignore
+
+				const link = colors.bold().cyan('https://kit.svelte.dev/docs/adapters');
 				console.log(
-					`See ${colors.bold().cyan('https://kit.svelte.dev/docs/adapters')} to learn how to configure your app to run on the platform of your choosing`
+					`See ${link} to learn how to configure your app to run on the platform of your choosing`
 				);
 			}
 
diff --git a/packages/kit/src/vite/utils.js b/packages/kit/src/vite/utils.js
index 1856c48a6..6c62d2636 100644
--- a/packages/kit/src/vite/utils.js
+++ b/packages/kit/src/vite/utils.js
@@ -105,6 +105,8 @@ export function get_aliases(config) {
 	const alias = [
 		{ find: '__GENERATED__', replacement: path.posix.join(config.outDir, 'generated') },
 		{ find: '$app', replacement: `${runtime_directory}/app` },
+		{ find: '$env/dynamic/public', replacement: `${runtime_directory}/env/dynamic/public.js` },
+		{ find: '$env/dynamic/private', replacement: `${runtime_directory}/env/dynamic/private.js` },
 		// For now, we handle `$lib` specially here rather than make it a default value for
 		// `config.kit.alias` since it has special meaning for packaging, etc.
 		{ find: '$lib', replacement: config.files.lib }
@@ -128,21 +130,6 @@ export function get_aliases(config) {
 		}
 	}
 
-	alias.push(
-		{
-			find: '$env/static/public',
-			replacement: path.posix.join(config.outDir, 'runtime/env/static/public.js')
-		},
-		{
-			find: '$env/static/private',
-			replacement: path.posix.join(config.outDir, 'runtime/env/static/private.js')
-		},
-		{
-			find: '$env',
-			replacement: `${runtime_directory}/env`
-		}
-	);
-
 	return alias;
 }
 
diff --git a/packages/kit/src/vite/utils.spec.js b/packages/kit/src/vite/utils.spec.js
index fa960d8c4..a566434f5 100644
--- a/packages/kit/src/vite/utils.spec.js
+++ b/packages/kit/src/vite/utils.spec.js
@@ -210,39 +210,24 @@ test('transform kit.alias to resolve.alias', () => {
 		kit: { alias: { simpleKey: 'simple/value', key: 'value', 'key/*': 'value/*' } }
 	});
 
-	const prefix = path.resolve('.');
+	const transformed = get_aliases(config.kit).map((entry) => {
+		const replacement = posixify(path.relative('.', entry.replacement));
 
-	const transformed = get_aliases(config.kit)
-		.map((entry) => {
-			const replacement = posixify(
-				entry.replacement.startsWith(prefix)
-					? entry.replacement.slice(prefix.length + 1)
-					: entry.replacement
-			);
-
-			return {
-				find: entry.find.toString(), // else assertion fails
-				replacement
-			};
-		})
-		.filter(
-			(entry) => entry.find !== '$app' && entry.find !== '$env' // testing this would mean to reimplement the logic in the test
-		);
+		return {
+			find: entry.find.toString(), // else assertion fails
+			replacement
+		};
+	});
 
 	assert.equal(transformed, [
 		{ find: '__GENERATED__', replacement: '.svelte-kit/generated' },
+		{ find: '$app', replacement: 'src/runtime/app' },
+		{ find: '$env/dynamic/public', replacement: 'src/runtime/env/dynamic/public.js' },
+		{ find: '$env/dynamic/private', replacement: 'src/runtime/env/dynamic/private.js' },
 		{ find: '$lib', replacement: 'src/lib' },
 		{ find: 'simpleKey', replacement: 'simple/value' },
 		{ find: /^key$/.toString(), replacement: 'value' },
-		{ find: /^key\/(.+)$/.toString(), replacement: 'value/$1' },
-		{
-			find: '$env/static/public',
-			replacement: '.svelte-kit/runtime/env/static/public.js'
-		},
-		{
-			find: '$env/static/private',
-			replacement: '.svelte-kit/runtime/env/static/private.js'
-		}
+		{ find: /^key\/(.+)$/.toString(), replacement: 'value/$1' }
 	]);
 });
 
diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts
index 059e8e078..c6f2d141e 100644
--- a/packages/kit/types/internal.d.ts
+++ b/packages/kit/types/internal.d.ts
@@ -196,7 +196,7 @@ export interface SSREndpoint {
 }
 
 export interface SSRNode {
-	component: SSRComponent;
+	component: SSRComponentLoader;
 	/** index into the `components` array in client-manifest.js */
 	index: number;
 	/** client-side module URL for this component */
@@ -281,11 +281,6 @@ export interface SSRErrorPage {
 	id: '__error';
 }
 
-export interface SSRPagePart {
-	id: string;
-	load: SSRComponentLoader;
-}
-
 export type SSRRoute = SSREndpoint | SSRPage;
 
 export interface SSRState {


Test patch:
diff --git a/packages/create-svelte/test/check.js b/packages/create-svelte/test/check.js
index 5e9fdd8bb..2fa477952 100644
--- a/packages/create-svelte/test/check.js
+++ b/packages/create-svelte/test/check.js
@@ -5,22 +5,25 @@ import { test } from 'uvu';
 import * as assert from 'uvu/assert';
 import { create } from '../index.js';
 import { fileURLToPath } from 'url';
+import glob from 'tiny-glob';
 // use a directory outside of packages to ensure it isn't added to the pnpm workspace
 const test_workspace_dir = fileURLToPath(
 	new URL('../../../.test-tmp/create-svelte/', import.meta.url)
 );
-const overrides = {};
-['kit', 'adapter-auto', 'adapter-cloudflare', 'adapter-netlify', 'adapter-vercel'].forEach(
-	(pkg) => {
-		overrides[`@sveltejs/${pkg}`] = `${path.resolve(
-			test_workspace_dir,
-			'..',
-			'..',
-			'packages',
-			pkg
-		)}`; //'workspace:*';
-	}
-);
+
+const existing_workspace_overrides = JSON.parse(
+	fs.readFileSync(fileURLToPath(new URL('../../../package.json', import.meta.url)), 'utf-8')
+).pnpm?.overrides;
+
+const overrides = { ...existing_workspace_overrides };
+
+(
+	await glob(fileURLToPath(new URL('../../../packages', import.meta.url)) + '/*/package.json')
+).forEach((pkgPath) => {
+	const name = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).name;
+	overrides[name] = path.dirname(path.resolve(pkgPath));
+});
+
 test.before(() => {
 	try {
 		// prepare test pnpm workspace
@@ -69,6 +72,13 @@ for (const template of fs.readdirSync('templates')) {
 				if (pkg.dependencies?.[key]) {
 					pkg.dependencies[key] = value;
 				}
+				if (!pkg.pnpm) {
+					pkg.pnpm = {};
+				}
+				if (!pkg.pnpm.overrides) {
+					pkg.pnpm.overrides = {};
+				}
+				pkg.pnpm.overrides = { ...pkg.pnpm.overrides, ...overrides };
 			});
 			pkg.private = true;
 			fs.writeFileSync(path.join(cwd, 'package.json'), JSON.stringify(pkg, null, '\t') + '\n');
diff --git a/packages/kit/test/apps/basics/src/routes/+layout.server.js b/packages/kit/test/apps/basics/src/routes/+layout.server.js
new file mode 100644
index 000000000..a57ef430c
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/+layout.server.js
@@ -0,0 +1,5 @@
+export async function load() {
+	return {
+		rootlayout: 'rootlayout'
+	};
+}
diff --git a/packages/kit/test/apps/basics/src/routes/no-ssr/browser-only-global/+page.svelte b/packages/kit/test/apps/basics/src/routes/no-ssr/browser-only-global/+page.svelte
new file mode 100644
index 000000000..2e48a4f8e
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/no-ssr/browser-only-global/+page.svelte
@@ -0,0 +1,5 @@
+<script context="module">
+	document;
+</script>
+
+<p>Works</p>
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/parent/+layout.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/parent/+layout.server.js
new file mode 100644
index 000000000..030a760ac
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/parent/+layout.server.js
@@ -0,0 +1,3 @@
+export function load() {
+	return { layout: 'layout' };
+}
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/parent/+layout.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/parent/+layout.svelte
new file mode 100644
index 000000000..5a30dcf36
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/parent/+layout.svelte
@@ -0,0 +1,9 @@
+<script>
+    export let data;
+</script>
+
+<h2>Layout data: {JSON.stringify(data)}</h2>
+<a href="/shadowed/parent?test">Reload</a>
+<a href="/shadowed/parent/sub">Sub</a>
+
+<slot />
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/parent/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/parent/+page.server.js
new file mode 100644
index 000000000..02ed02f61
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/parent/+page.server.js
@@ -0,0 +1,4 @@
+export async function load({ parent }) {
+	const data = await parent();
+	return { page: 'page', data };
+}
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/parent/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/parent/+page.svelte
new file mode 100644
index 000000000..10ec5756a
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/parent/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+    export let data;
+</script>
+
+<p>Page data: {JSON.stringify(data)}</p>
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/parent/sub/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/parent/sub/+page.server.js
new file mode 100644
index 000000000..a47fdcc46
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/parent/sub/+page.server.js
@@ -0,0 +1,4 @@
+export async function load({ parent }) {
+	const data = await parent();
+	return { sub: 'sub', data };
+}
diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/parent/sub/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/parent/sub/+page.svelte
new file mode 100644
index 000000000..10ec5756a
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/shadowed/parent/sub/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+    export let data;
+</script>
+
+<p>Page data: {JSON.stringify(data)}</p>
diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js
index c8a017c36..cef1f3721 100644
--- a/packages/kit/test/apps/basics/test/client.test.js
+++ b/packages/kit/test/apps/basics/test/client.test.js
@@ -620,3 +620,9 @@ test.describe('Page Store', () => {
 		);
 	});
 });
+
+test('Can use browser-only global on client-only page', async ({ page, read_errors }) => {
+	await page.goto('/no-ssr/browser-only-global');
+	await expect(page.locator('p')).toHaveText('Works');
+	expect(read_errors('/no-ssr/browser-only-global')).toBe(undefined);
+});
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index 75f6c7a8a..32e2903aa 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -251,6 +251,26 @@ test.describe('Shadowed pages', () => {
 			expect(requests).not.toContain(`${baseURL}/shadowed/missing-get`);
 		}
 	});
+
+	test('Parent data is present', async ({ page, clicknav }) => {
+		await page.goto('/shadowed/parent');
+		await expect(page.locator('h2')).toHaveText('Layout data: {"layout":"layout"}');
+		await expect(page.locator('p')).toHaveText(
+			'Page data: {"page":"page","data":{"rootlayout":"rootlayout","layout":"layout"}}'
+		);
+
+		await clicknav('[href="/shadowed/parent?test"]');
+		await expect(page.locator('h2')).toHaveText('Layout data: {"layout":"layout"}');
+		await expect(page.locator('p')).toHaveText(
+			'Page data: {"page":"page","data":{"rootlayout":"rootlayout","layout":"layout"}}'
+		);
+
+		await clicknav('[href="/shadowed/parent/sub"]');
+		await expect(page.locator('h2')).toHaveText('Layout data: {"layout":"layout"}');
+		await expect(page.locator('p')).toHaveText(
+			'Page data: {"sub":"sub","data":{"rootlayout":"rootlayout","layout":"layout"}}'
+		);
+	});
 });
 
 test.describe('Encoded paths', () => {
@@ -1154,7 +1174,7 @@ test.describe('$app/stores', () => {
 			await page.waitForTimeout(100); // gross, but necessary since no navigation occurs
 			await page.click('a[href="/store/navigating/a"]');
 
-			await page.waitForSelector('#not-navigating', { timeout: 500 });
+			await page.waitForSelector('#not-navigating', { timeout: 5000 });
 			expect(await page.textContent('#nav-status')).toBe('not currently navigating');
 		}
 	});