Repository: sveltejs/kit (js)

Issue:
return fail() fails with Error: Data returned from action inside / is not serializable: Cannot stringify arbitrary non-POJOs
### Describe the bug

As of sveltekit 1.0.13, returning a fail() object from an Action fails with the following error when using `npm run dev`

```
Error: Data returned from action inside / is not serializable: Cannot stringify arbitrary non-POJOs
```

Everything worked fine in 1.0.12.  Also works as expected using `npm run build` / `npm run preview`

### Reproduction

`+page.server.js`
```javascript
import { fail } from '@sveltejs/kit';

/** @type {import('./$types').Actions} */
export const actions = {
	test: async () => {
		return fail(500);
	},
};
```



`+page.svelte`
```html
<form method="POST" action="?/test">
	<button>Submit</button>
</form>
```


### Logs

```Shell
Error: Data returned from action inside / is not serializable: Cannot stringify arbitrary non-POJOs
    at try_deserialize (/node_modules/@sveltejs/kit/src/runtime/server/page/actions.js:262:10)
    at stringify_action_response (/node_modules/@sveltejs/kit/src/runtime/server/page/actions.js:244:9)
    at Module.handle_action_json_request (/node_modules/@sveltejs/kit/src/runtime/server/page/actions.js:67:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async resolve (/node_modules/@sveltejs/kit/src/runtime/server/respond.js:358:17)
    at async Module.respond (/node_modules/@sveltejs/kit/src/runtime/server/respond.js:229:20)
    at async file:///C:/Users/geoff/slingshot/slingshot-webui/frontend/node_modules/@sveltejs/kit/src/exports/vite/dev/index.js:468:22
```


### System Info

```Shell
System:
    OS: Windows 10 10.0.19045
    CPU: (12) x64 Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz
    Memory: 33.87 GB / 63.95 GB
  Binaries:
    Node: 19.3.0 - C:\Program Files\nodejs\node.EXE
    npm: 9.3.0 - C:\Program Files\nodejs\npm.CMD
  Browsers:
    Chrome: 109.0.5414.74
    Edge: Spartan (44.19041.1266.0), Chromium (108.0.1462.76)
  npmPackages:
    @sveltejs/adapter-auto: ^1.0.0 => 1.0.0
    @sveltejs/adapter-node: ^1.0.0 => 1.1.1
    @sveltejs/kit: ^1.0.12 => 1.1.0
    svelte: ^3.46.0 => 3.55.1
    vite: ^4.0.0 => 4.0.4
```


### Severity

blocking an upgrade

### Additional Information

_No response_

Pull request description:
fix: reapply exports alignment
...after Vite dependency optimizations
fixes #8523

### 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
- [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. Changesets that add features should be `minor` and those that fix bugs should be `patch`. Please prefix changeset messages with `feat:`, `fix:`, or `chore:`.


Patch:
diff --git a/.changeset/pink-games-perform.md b/.changeset/pink-games-perform.md
new file mode 100644
index 000000000..9668f2693
--- /dev/null
+++ b/.changeset/pink-games-perform.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+fix: reapply exports alignment after Vite dependency optimizations
diff --git a/documentation/docs/10-getting-started/30-project-structure.md b/documentation/docs/10-getting-started/30-project-structure.md
index 91ae38473..3252ac943 100644
--- a/documentation/docs/10-getting-started/30-project-structure.md
+++ b/documentation/docs/10-getting-started/30-project-structure.md
@@ -17,7 +17,8 @@ my-project/
 │ │ └ [your routes]
 │ ├ app.html
 │ ├ error.html
-│ └ hooks.js
+│ ├ hooks.client.js
+│ └ hooks.server.js
 ├ static/
 │ └ [your static assets]
 ├ tests/
@@ -34,7 +35,7 @@ You'll also find common files like `.gitignore` and `.npmrc` (and `.prettierrc`
 
 ### src
 
-The `src` directory contains the meat of your project.
+The `src` directory contains the meat of your project. Everything except `src/routes` and `src/app.html` is optional.
 
 - `lib` contains your library code (utilities and components), which can be imported via the [`$lib`](modules#$lib) alias, or packaged up for distribution using [`svelte-package`](packaging)
   - `server` contains your server-only library code. It can be imported by using the [`$lib/server`](server-only-modules) alias. SvelteKit will prevent you from importing these in client code.
@@ -46,11 +47,12 @@ The `src` directory contains the meat of your project.
   - `%sveltekit.assets%` — either [`paths.assets`](configuration#paths), if specified, or a relative path to [`paths.base`](configuration#paths)
   - `%sveltekit.nonce%` — a [CSP](configuration#csp) nonce for manually included links and scripts, if used
   - `%sveltekit.env.[NAME]%` - this will be replaced at render time with the `[NAME]` environment variable, which must begin with the [`publicPrefix`](https://kit.svelte.dev/docs/configuration#env) (usually `PUBLIC_`). It will fallback to `''` if not matched.
-- `error.html` (optional) is the page that is rendered when everything else fails. It can contain the following placeholders:
+- `error.html` is the page that is rendered when everything else fails. It can contain the following placeholders:
   - `%sveltekit.status%` — the HTTP status
   - `%sveltekit.error.message%` — the error message
-- `hooks.js` (optional) contains your application's [hooks](hooks)
-- `service-worker.js` (optional) contains your [service worker](service-workers)
+- `hooks.client.js` contains your client [hooks](/docs/hooks)
+- `hooks.server.js` contains your server [hooks](/docs/hooks)
+- `service-worker.js` contains your [service worker](/docs/service-workers)
 
 You can use `.ts` files instead of `.js` files, if using TypeScript.
 
diff --git a/package.json b/package.json
index 53fd55a0b..1c28198cc 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
 		"turbo": "^1.6.3",
 		"typescript": "^4.9.4"
 	},
-	"packageManager": "pnpm@7.25.1",
+	"packageManager": "pnpm@7.26.0",
 	"engines": {
 		"pnpm": "^7.0.0"
 	},
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index fadfd155e..f52769f12 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,17 @@
 # @sveltejs/kit
 
+## 1.2.8
+
+### Patch Changes
+
+- fix: include base path in path to start script ([#8651](https://github.com/sveltejs/kit/pull/8651))
+
+## 1.2.7
+
+### Patch Changes
+
+- fix: set headers when throwing redirect in handle ([#8648](https://github.com/sveltejs/kit/pull/8648))
+
 ## 1.2.6
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 52059400c..832a9ed7a 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.2.6",
+	"version": "1.2.8",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js
index a5bf770a0..8537a77c1 100644
--- a/packages/kit/src/exports/vite/dev/index.js
+++ b/packages/kit/src/exports/vite/dev/index.js
@@ -315,17 +315,30 @@ export async function dev(vite, vite_config, svelte_config) {
 		}
 	});
 
-	// This shameful hack allows us to load runtime server code via Vite
-	// while apps load `HttpError` and `Redirect` in Node, without
-	// causing `instanceof` checks to fail
-	const control_module_node = await import(`../../../runtime/control.js`);
-	const control_module_vite = await vite.ssrLoadModule(`${runtime_base}/control.js`);
-
-	control_module_node.replace_implementations({
-		ActionFailure: control_module_vite.ActionFailure,
-		HttpError: control_module_vite.HttpError,
-		Redirect: control_module_vite.Redirect
-	});
+	async function align_exports() {
+		// This shameful hack allows us to load runtime server code via Vite
+		// while apps load `HttpError` and `Redirect` in Node, without
+		// causing `instanceof` checks to fail
+		const control_module_node = await import(`../../../runtime/control.js`);
+		const control_module_vite = await vite.ssrLoadModule(`${runtime_base}/control.js`);
+
+		control_module_node.replace_implementations({
+			ActionFailure: control_module_vite.ActionFailure,
+			HttpError: control_module_vite.HttpError,
+			Redirect: control_module_vite.Redirect
+		});
+	}
+	align_exports();
+	const ws_send = vite.ws.send;
+	/** @param {any} args */
+	vite.ws.send = function (...args) {
+		// We need to reapply the patch after Vite did dependency optimizations
+		// because that clears the module resolutions
+		if (args[0]?.type === 'full-reload' && args[0].path === '*') {
+			align_exports();
+		}
+		return ws_send.apply(vite.ws, args);
+	};
 
 	vite.middlewares.use(async (req, res, next) => {
 		try {
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index 9c911ebb1..215ca2b3f 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -164,7 +164,15 @@ export async function render_response({
 	}
 
 	/** @param {string} path */
-	const prefixed = (path) => (path.startsWith('/') ? path : `${resolved_assets}/${path}`);
+	const prefixed = (path) => {
+		if (path.startsWith('/')) {
+			// Vite makes the start script available through the base path and without it.
+			// We load it via the base path in order to support remote IDE environments which proxy
+			// all URLs under the base path during development.
+			return base + path;
+		}
+		return `${resolved_assets}/${path}`;
+	};
 
 	const serialized = { data: '', form: 'null', error: 'null' };
 
diff --git a/packages/kit/src/runtime/server/respond.js b/packages/kit/src/runtime/server/respond.js
index 07b7eab1d..1cd4047b0 100644
--- a/packages/kit/src/runtime/server/respond.js
+++ b/packages/kit/src/runtime/server/respond.js
@@ -110,6 +110,9 @@ export async function respond(request, options, manifest, state) {
 	/** @type {Record<string, string>} */
 	const headers = {};
 
+	/** @type {Record<string, import('./page/types').Cookie>} */
+	let cookies_to_add = {};
+
 	/** @type {import('types').RequestEvent} */
 	const event = {
 		// @ts-expect-error `cookies` and `fetch` need to be created after the `event` itself
@@ -221,6 +224,7 @@ export async function respond(request, options, manifest, state) {
 			trailing_slash ?? 'never'
 		);
 
+		cookies_to_add = new_cookies;
 		event.cookies = cookies;
 		event.fetch = create_fetch({ event, options, manifest, state, get_cookie_header });
 
@@ -237,7 +241,7 @@ export async function respond(request, options, manifest, state) {
 						response.headers.set(key, /** @type {string} */ (value));
 					}
 
-					add_cookies_to_headers(response.headers, Object.values(new_cookies));
+					add_cookies_to_headers(response.headers, Object.values(cookies_to_add));
 
 					if (state.prerendering && event.route.id !== null) {
 						response.headers.set('x-sveltekit-routeid', encodeURI(event.route.id));
@@ -293,11 +297,11 @@ export async function respond(request, options, manifest, state) {
 		return response;
 	} catch (e) {
 		if (e instanceof Redirect) {
-			if (is_data_request) {
-				return redirect_json_response(e);
-			} else {
-				return redirect_response(e.status, e.location);
-			}
+			const response = is_data_request
+				? redirect_json_response(e)
+				: redirect_response(e.status, e.location);
+			add_cookies_to_headers(response.headers, Object.values(cookies_to_add));
+			return response;
 		}
 		return await handle_fatal_error(event, options, e);
 	}
diff --git a/turbo.json b/turbo.json
index e07e1e8cf..7a422b757 100644
--- a/turbo.json
+++ b/turbo.json
@@ -44,7 +44,7 @@
 		},
 		"test": {
 			"dependsOn": ["^build"],
-			"inputs": ["test/**"],
+			"inputs": ["src/**", "scripts/**", "shared/**", "templates/**", "test/**"],
 			"outputs": ["coverage/", "test-results/**"],
 			"outputMode": "new-only",
 			"env": ["CI", "TURBO_CACHE_KEY"]


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 c32054b45..a909520da 100644
--- a/packages/kit/test/apps/basics/src/hooks.server.js
+++ b/packages/kit/test/apps/basics/src/hooks.server.js
@@ -2,6 +2,7 @@ import fs from 'fs';
 import { sequence } from '@sveltejs/kit/hooks';
 import { HttpError } from '../../../../src/runtime/control';
 import { error, redirect } from '@sveltejs/kit';
+import { COOKIE_NAME } from './routes/cookies/shared';
 
 /**
  * Transform an error into a POJO, by copying its `name`, `message`
@@ -96,6 +97,9 @@ export const handle = sequence(
 		if (event.url.pathname.includes('/redirect/in-handle')) {
 			if (event.url.search === '?throw') {
 				throw redirect(307, event.url.origin + '/redirect/c');
+			} else if (event.url.search.includes('cookies')) {
+				event.cookies.delete(COOKIE_NAME, { path: '/cookies' });
+				throw redirect(307, event.url.origin + '/cookies');
 			} else {
 				return new Response(undefined, { status: 307, headers: { location: '/redirect/c' } });
 			}
diff --git a/packages/kit/test/apps/basics/test/cross-platform/test.js b/packages/kit/test/apps/basics/test/cross-platform/test.js
index dd9d47466..fa2e3dd73 100644
--- a/packages/kit/test/apps/basics/test/cross-platform/test.js
+++ b/packages/kit/test/apps/basics/test/cross-platform/test.js
@@ -581,6 +581,30 @@ test.describe('Redirects', () => {
 		await page.goBack();
 		expect(page.url()).toBe(`${baseURL}/redirect`);
 	});
+
+	test('sets cookies when throw redirect in handle hook', async ({
+		page,
+		app,
+		javaScriptEnabled
+	}) => {
+		await page.goto('/cookies/set');
+		let span = page.locator('#cookie-value');
+		expect(await span.innerText()).toContain('teapot');
+
+		if (javaScriptEnabled) {
+			const [, response] = await Promise.all([
+				app.goto('/redirect/in-handle?throw&cookies'),
+				page.waitForResponse((request) =>
+					request.url().endsWith('in-handle/__data.json?throw=&cookies=&x-sveltekit-invalidated=_1')
+				)
+			]);
+			expect((await response.allHeaders())['set-cookie']).toBeDefined();
+		}
+
+		await page.goto('/redirect/in-handle?throw&cookies');
+		span = page.locator('#cookie-value');
+		expect(await span.innerText()).toContain('undefined');
+	});
 });
 
 test.describe('Routing', () => {