Repository: sveltejs/kit (js)

Issue:
Incorrect response `status code` when throwing error from `+server.ts`
### Describe the bug

When throwing an error from a `+server.ts` route, the `status code` in the `response header` is set to `500` instead of the status code specified in the `error()` helper. The nearest Error Boundary does render and the `error` property in the `$page` store does have the correct status code, however the response header doesn't match.


### Reproduction

### **+server.ts**

```
import { error, redirect } from '@sveltejs/kit';
import type { RequestHandler } from './$types';

export const GET: RequestHandler = ({ params }) => {
	if (true) {
		throw error(404, 'Shortcode Not Found');
	}

	return redirect(301, 'https://kit.svelte.dev');
};
```

### **Response Header**

```
Status Code: 500 Internal Server Error
```

### **$page store**

```
{
  error: HttpError {
    name: 'HttpError',
    stack: undefined,
    status: 404,
    message: 'Shortcode Not Found'
  },
  params: { shortcode: 'test' },
  routeId: '[shortcode]',
  status: 500,
  url: URL {
    href: 'http://localhost:5173/test',
    origin: 'http://localhost:5173',
    protocol: 'http:',
    username: '',
    password: '',
    host: 'localhost:5173',
    hostname: 'localhost',
    port: '5173',
    pathname: '/test',
    search: '',
    searchParams: URLSearchParams {},
    hash: ''
  },
  data: {}
}
```



### Logs

_No response_

### System Info

```shell
System:
    OS: macOS 12.5
    CPU: (10) arm64 Apple M1 Max
    Memory: 5.49 GB / 32.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 18.7.0 - /opt/homebrew/bin/node
    npm: 8.16.0 - /opt/homebrew/bin/npm
  Browsers:
    Brave Browser: 104.1.42.95
    Chrome: 104.0.5112.79
    Firefox Developer Edition: 104.0
    Safari: 15.6
    Safari Technology Preview: 16.0
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.64
    @sveltejs/kit: next => 1.0.0-next.417
    svelte: ^3.44.0 => 3.49.0
    vite: ^3.0.0 => 3.0.8
```


### Severity

serious, but I can work around it

### Additional Information

_No response_

Pull request description:
[fix] Handle `throw error/redirect` in `+server.js`
Fixes #5993

This results in a (in my opinion correct) change in behavior: If you `throw` an `error`, you will no longer have the message rendered in the nearest `+error.svelte`, instead it's just blank text, which is what I'd expect from querying a regular endpoint.

### 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/fresh-pigs-tease.md b/.changeset/fresh-pigs-tease.md
new file mode 100644
index 000000000..9af327627
--- /dev/null
+++ b/.changeset/fresh-pigs-tease.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[breaking] remove ability for `+page.server.js` to respond to `GET` requests with JSON
diff --git a/.changeset/honest-parrots-compare.md b/.changeset/honest-parrots-compare.md
new file mode 100644
index 000000000..4c8442e71
--- /dev/null
+++ b/.changeset/honest-parrots-compare.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Handle `throw error/redirect` in `+server.js`
diff --git a/.changeset/mighty-students-develop.md b/.changeset/mighty-students-develop.md
new file mode 100644
index 000000000..cbffb0dea
--- /dev/null
+++ b/.changeset/mighty-students-develop.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Handle windows paths and regexp chars in kit.alias
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 58258eebe..ea7fe6e55 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -256,6 +256,7 @@
     "few-buses-argue",
     "few-carrots-sell",
     "few-cobras-switch",
+    "few-hornets-joke",
     "few-points-taste",
     "few-rockets-sort",
     "few-rockets-think",
@@ -335,6 +336,7 @@
     "gold-carrots-sniff",
     "gold-colts-train",
     "gold-moles-hunt",
+    "gold-rocks-hang",
     "gold-spies-fail",
     "gold-trains-dream",
     "good-beans-invite",
diff --git a/.changeset/selfish-tips-divide.md b/.changeset/selfish-tips-divide.md
new file mode 100644
index 000000000..ac7e76767
--- /dev/null
+++ b/.changeset/selfish-tips-divide.md
@@ -0,0 +1,5 @@
+---
+'create-svelte': patch
+---
+
+Run svelte-kit sync before svelte-check in check scripts
diff --git a/packages/create-svelte/CHANGELOG.md b/packages/create-svelte/CHANGELOG.md
index dbdb840a5..f03c4fa56 100644
--- a/packages/create-svelte/CHANGELOG.md
+++ b/packages/create-svelte/CHANGELOG.md
@@ -1,5 +1,11 @@
 # create-svelte
 
+## 2.0.0-next.156
+
+### Patch Changes
+
+- update `app.d.ts` for library skeleton template ([#6020](https://github.com/sveltejs/kit/pull/6020))
+
 ## 2.0.0-next.155
 
 ### Patch Changes
diff --git a/packages/create-svelte/package.json b/packages/create-svelte/package.json
index ca8f89dce..56e941bcc 100644
--- a/packages/create-svelte/package.json
+++ b/packages/create-svelte/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "create-svelte",
-	"version": "2.0.0-next.155",
+	"version": "2.0.0-next.156",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/create-svelte/shared/+typescript/package.json b/packages/create-svelte/shared/+typescript/package.json
index 465f674b8..7b5612f97 100644
--- a/packages/create-svelte/shared/+typescript/package.json
+++ b/packages/create-svelte/shared/+typescript/package.json
@@ -1,7 +1,7 @@
 {
 	"scripts": {
-		"check": "svelte-check --tsconfig ./tsconfig.json",
-		"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch"
+		"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+		"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
 	},
 	"devDependencies": {
 		"typescript": "^4.7.4",
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 7b859cf14..fdef39d3d 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @sveltejs/kit
 
+## 1.0.0-next.421
+
+### Patch Changes
+
+- Respect `export const prerender = false` in `+page.server.js` ([#6012](https://github.com/sveltejs/kit/pull/6012))
+
 ## 1.0.0-next.420
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 664069a66..3645e8c6a 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.0-next.420",
+	"version": "1.0.0-next.421",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js
index 338791b4d..9d112214a 100644
--- a/packages/kit/src/runtime/server/endpoint.js
+++ b/packages/kit/src/runtime/server/endpoint.js
@@ -1,3 +1,4 @@
+import { HttpError, Redirect } from '../../index/private.js';
 import { check_method_names, method_not_allowed } from './utils.js';
 
 /**
@@ -29,16 +30,29 @@ export async function render_endpoint(event, route) {
 		return method_not_allowed(mod, method);
 	}
 
-	const response = await handler(
-		/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
-	);
-
-	if (!(response instanceof Response)) {
-		return new Response(
-			`Invalid response from route ${event.url.pathname}: handler should return a Response object`,
-			{ status: 500 }
+	try {
+		const response = await handler(
+			/** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
 		);
-	}
 
-	return response;
+		if (!(response instanceof Response)) {
+			return new Response(
+				`Invalid response from route ${event.url.pathname}: handler should return a Response object`,
+				{ status: 500 }
+			);
+		}
+
+		return response;
+	} catch (error) {
+		if (error instanceof HttpError) {
+			return new Response(error.message, { status: error.status });
+		} else if (error instanceof Redirect) {
+			return new Response(undefined, {
+				status: error.status,
+				headers: { Location: error.location }
+			});
+		} else {
+			throw error;
+		}
+	}
 }
diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js
index edaa42016..91f0a6470 100644
--- a/packages/kit/src/runtime/server/page/index.js
+++ b/packages/kit/src/runtime/server/page/index.js
@@ -36,7 +36,11 @@ export async function render_page(event, route, options, state, resolve_opts) {
 		'application/json'
 	]);
 
-	if (accept === 'application/json') {
+	if (
+		accept === 'application/json' &&
+		event.request.method !== 'GET' &&
+		event.request.method !== 'HEAD'
+	) {
 		const node = await options.manifest._.nodes[route.leaf]();
 		if (node.server) {
 			return handle_json_request(event, options, node.server);
@@ -346,8 +350,8 @@ function get_page_config(leaf, options) {
  * @param {import('types').SSRNode['server']} mod
  */
 export async function handle_json_request(event, options, mod) {
-	const method = /** @type {import('types').HttpMethod} */ (event.request.method);
-	const handler = mod[method === 'HEAD' || method === 'GET' ? 'load' : method];
+	const method = /** @type {'POST' | 'PUT' | 'PATCH' | 'DELETE'} */ (event.request.method);
+	const handler = mod[method];
 
 	if (!handler) {
 		return method_not_allowed(mod, method);
@@ -357,14 +361,6 @@ export async function handle_json_request(event, options, mod) {
 		// @ts-ignore
 		const result = await handler.call(null, event);
 
-		if (method === 'HEAD') {
-			return new Response();
-		}
-
-		if (method === 'GET') {
-			return json(result);
-		}
-
 		if (result?.errors) {
 			// @ts-ignore
 			return json({ errors: result.errors }, { status: result.status || 400 });
diff --git a/packages/kit/src/vite/utils.js b/packages/kit/src/vite/utils.js
index 6c62d2636..4e9ae7543 100644
--- a/packages/kit/src/vite/utils.js
+++ b/packages/kit/src/vite/utils.js
@@ -2,6 +2,7 @@ import fs from 'fs';
 import path from 'path';
 import { loadConfigFromFile, loadEnv, normalizePath } from 'vite';
 import { runtime_directory } from '../core/utils.js';
+import { posixify } from '../utils/filesystem.js';
 
 /**
  * @param {import('vite').ResolvedConfig} config
@@ -113,18 +114,22 @@ export function get_aliases(config) {
 	];
 
 	for (let [key, value] of Object.entries(config.alias)) {
+		value = posixify(value);
 		if (value.endsWith('/*')) {
 			value = value.slice(0, -2);
 		}
 		if (key.endsWith('/*')) {
 			// Doing just `{ find: key.slice(0, -2) ,..}` would mean `import .. from "key"` would also be matched, which we don't want
 			alias.push({
-				find: new RegExp(`^${key.slice(0, -2)}\\/(.+)$`),
+				find: new RegExp(`^${escape_for_regexp(key.slice(0, -2))}\\/(.+)$`),
 				replacement: `${path.resolve(value)}/$1`
 			});
 		} else if (key + '/*' in config.alias) {
 			// key and key/* both exist -> the replacement for key needs to happen _only_ on import .. from "key"
-			alias.push({ find: new RegExp(`^${key}$`), replacement: path.resolve(value) });
+			alias.push({
+				find: new RegExp(`^${escape_for_regexp(key)}$`),
+				replacement: path.resolve(value)
+			});
 		} else {
 			alias.push({ find: key, replacement: path.resolve(value) });
 		}
@@ -133,6 +138,13 @@ export function get_aliases(config) {
 	return alias;
 }
 
+/**
+ * @param {string} str
+ */
+function escape_for_regexp(str) {
+	return str.replace(/[.*+?^${}()|[\]\\]/g, (match) => '\\' + match);
+}
+
 /**
  * Given an entry point like [cwd]/src/hooks, returns a filename like [cwd]/src/hooks.js or [cwd]/src/hooks/index.js
  * @param {string} entry
diff --git a/packages/kit/src/vite/utils.spec.js b/packages/kit/src/vite/utils.spec.js
index a566434f5..6fa338f64 100644
--- a/packages/kit/src/vite/utils.spec.js
+++ b/packages/kit/src/vite/utils.spec.js
@@ -207,7 +207,15 @@ test('merge resolve.alias', () => {
 
 test('transform kit.alias to resolve.alias', () => {
 	const config = validate_config({
-		kit: { alias: { simpleKey: 'simple/value', key: 'value', 'key/*': 'value/*' } }
+		kit: {
+			alias: {
+				simpleKey: 'simple/value',
+				key: 'value',
+				'key/*': 'value/*',
+				$regexChar: 'windows\\path',
+				'$regexChar/*': 'windows\\path\\*'
+			}
+		}
 	});
 
 	const transformed = get_aliases(config.kit).map((entry) => {
@@ -227,7 +235,9 @@ test('transform kit.alias to resolve.alias', () => {
 		{ find: '$lib', replacement: 'src/lib' },
 		{ find: 'simpleKey', replacement: 'simple/value' },
 		{ find: /^key$/.toString(), replacement: 'value' },
-		{ find: /^key\/(.+)$/.toString(), replacement: 'value/$1' }
+		{ find: /^key\/(.+)$/.toString(), replacement: 'value/$1' },
+		{ find: /^\$regexChar$/.toString(), replacement: 'windows/path' },
+		{ find: /^\$regexChar\/(.+)$/.toString(), replacement: 'windows/path/$1' }
 	]);
 });
 


Test patch:
diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-error/+server.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-error/+server.js
new file mode 100644
index 000000000..14c9aec1c
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-error/+server.js
@@ -0,0 +1,5 @@
+import { error } from '@sveltejs/kit';
+
+export function GET() {
+	throw error(401, 'You shall not pass');
+}
diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-redirect/+server.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-redirect/+server.js
new file mode 100644
index 000000000..26300742e
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-throw-redirect/+server.js
@@ -0,0 +1,5 @@
+import { redirect } from '@sveltejs/kit';
+
+export function GET() {
+	throw redirect(302, '/');
+}
diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js
index 45615bcb5..5ddcc2d3f 100644
--- a/packages/kit/test/apps/basics/test/server.test.js
+++ b/packages/kit/test/apps/basics/test/server.test.js
@@ -124,40 +124,6 @@ test.describe('Errors', () => {
 		expect(await response.text()).toMatch('thisvariableisnotdefined is not defined');
 	});
 
-	test('page endpoint thrown error respects `accept: application/json`', async ({ request }) => {
-		const response = await request.get('/errors/page-endpoint/get-implicit', {
-			headers: {
-				accept: 'application/json'
-			}
-		});
-
-		const { message, name, stack, fancy } = await response.json();
-
-		expect(response.status()).toBe(500);
-		expect(name).toBe('FancyError');
-		expect(message).toBe('oops');
-		expect(fancy).toBe(true);
-
-		if (process.env.DEV) {
-			expect(stack.split('\n').length).toBeGreaterThan(1);
-		} else {
-			expect(stack.split('\n').length).toBe(1);
-		}
-	});
-
-	test('page endpoint returned error respects `accept: application/json`', async ({ request }) => {
-		const response = await request.get('/errors/page-endpoint/get-explicit', {
-			headers: {
-				accept: 'application/json'
-			}
-		});
-
-		const { message } = await response.json();
-
-		expect(response.status()).toBe(400);
-		expect(message).toBe('oops');
-	});
-
 	test('returns 400 when accessing a malformed URI', async ({ page }) => {
 		const response = await page.goto('/%c0%ae%c0%ae/etc/passwd');
 		if (process.env.DEV) {
@@ -181,6 +147,26 @@ test.describe('Errors', () => {
 			'This is your custom error page saying: "Cannot read properties of undefined (reading \'toUpperCase\')"'
 		);
 	});
+
+	test('throw error(..) in endpoint', async ({ page, read_errors }) => {
+		const res = await page.goto('/errors/endpoint-throw-error');
+
+		const error = read_errors('/errors/endpoint-throw-error');
+		expect(error).toBe(undefined);
+
+		expect(await res?.text()).toBe('You shall not pass');
+		expect(res?.status()).toBe(401);
+	});
+
+	test('throw redirect(..) in endpoint', async ({ page, read_errors }) => {
+		const res = await page.goto('/errors/endpoint-throw-redirect');
+		expect(res?.status()).toBe(200); // redirects are opaque to the browser
+
+		const error = read_errors('/errors/endpoint-throw-redirect');
+		expect(error).toBe(undefined);
+
+		expect(await page.textContent('h1')).toBe('the answer is 42');
+	});
 });
 
 test.describe('Load', () => {
@@ -211,53 +197,6 @@ test.describe('Routing', () => {
 });
 
 test.describe('Shadowed pages', () => {
-	test('responds to HEAD requests from endpoint', async ({ request }) => {
-		const url = '/shadowed/simple';
-
-		const opts = {
-			headers: {
-				accept: 'application/json'
-			}
-		};
-
-		const responses = {
-			head: await request.head(url, opts),
-			get: await request.get(url, opts)
-		};
-
-		const headers = {
-			head: responses.head.headers(),
-			get: responses.get.headers()
-		};
-
-		expect(responses.head.status()).toBe(200);
-		expect(responses.get.status()).toBe(200);
-		expect(await responses.head.text()).toBe('');
-		expect(await responses.get.json()).toEqual({ answer: 42 });
-
-		['date', 'transfer-encoding'].forEach((name) => {
-			delete headers.head[name];
-			delete headers.get[name];
-		});
-
-		expect(headers.get).toEqual({
-			...headers.head,
-			'content-type': 'application/json'
-		});
-	});
-
-	test('Responds from endpoint if Accept includes application/json but not text/html', async ({
-		request
-	}) => {
-		const response = await request.get('/shadowed/simple', {
-			headers: {
-				accept: 'application/json'
-			}
-		});
-
-		expect(await response.json()).toEqual({ answer: 42 });
-	});
-
 	test('Action can return undefined', async ({ request }) => {
 		const response = await request.post('/shadowed/simple/post', {
 			headers: {