diff --git a/.changeset/four-lamps-walk.md b/.changeset/four-lamps-walk.md new file mode 100644 index 000000000000..d2abb0ef2b2a --- /dev/null +++ b/.changeset/four-lamps-walk.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[breaking] Disallow error status codes outside 400-599 diff --git a/packages/kit/src/exports/index.js b/packages/kit/src/exports/index.js index b0ae3243e0aa..0ad15b8d92fa 100644 --- a/packages/kit/src/exports/index.js +++ b/packages/kit/src/exports/index.js @@ -8,12 +8,22 @@ import { HttpError, Redirect, ValidationError } from '../runtime/control.js'; * @param {any} message */ export function error(status, message) { + if ( + (!__SVELTEKIT_BROWSER__ || __SVELTEKIT_DEV__) && + (isNaN(status) || status < 400 || status > 599) + ) { + throw new Error(`HTTP error status codes must be between 400 and 599 — ${status} is invalid`); + } + return new HttpError(status, message); } /** @type {import('@sveltejs/kit').redirect} */ export function redirect(status, location) { - if (isNaN(status) || status < 300 || status > 308) { + if ( + (!__SVELTEKIT_BROWSER__ || __SVELTEKIT_DEV__) && + (isNaN(status) || status < 300 || status > 308) + ) { throw new Error('Invalid status code'); } diff --git a/packages/kit/src/runtime/control.js b/packages/kit/src/runtime/control.js index 2bcf963985a2..16ea65416416 100644 --- a/packages/kit/src/runtime/control.js +++ b/packages/kit/src/runtime/control.js @@ -43,56 +43,3 @@ export class ValidationError { this.data = data; } } - -/** - * Creates an `HttpError` object with an HTTP status code and an optional message. - * This object, if thrown during request handling, will cause SvelteKit to - * return an error response without invoking `handleError` - * @param {number} status - * @param {string | undefined} [message] - */ -export function error(status, message) { - return new HttpError(status, message); -} - -/** - * Creates a `Redirect` object. If thrown during request handling, SvelteKit will - * return a redirect response. - * @param {300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308} status - * @param {string} location - */ -export function redirect(status, location) { - if (isNaN(status) || status < 300 || status > 308) { - throw new Error('Invalid status code'); - } - - return new Redirect(status, location); -} - -/** - * Generates a JSON `Response` object from the supplied data. - * @param {any} data - * @param {ResponseInit} [init] - */ -export function json(data, init) { - // TODO deprecate this in favour of `Response.json` when it's - // more widely supported - const headers = new Headers(init?.headers); - if (!headers.has('content-type')) { - headers.set('content-type', 'application/json'); - } - - return new Response(JSON.stringify(data), { - ...init, - headers - }); -} - -/** - * Generates a `ValidationError` object. - * @param {number} status - * @param {Record | undefined} [data] - */ -export function invalid(status, data) { - return new ValidationError(status, data); -} diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index be47fcb68a93..8678d1b1a6b6 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1295,28 +1295,32 @@ test.describe('Redirects', () => { await clicknav('[href="/redirect/missing-status/a"]'); + const message = process.env.DEV || !javaScriptEnabled ? 'Invalid status code' : 'Redirect loop'; + expect(page.url()).toBe(`${baseURL}/redirect/missing-status/a`); expect(await page.textContent('h1')).toBe('500'); expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: "Invalid status code"' + `This is your custom error page saying: "${message}"` ); if (!javaScriptEnabled) { // handleError is not invoked for client-side navigation const lines = read_errors('/redirect/missing-status/a').stack.split('\n'); - expect(lines[0]).toBe('Error: Invalid status code'); + expect(lines[0]).toBe(`Error: ${message}`); } }); - test('errors on invalid status', async ({ baseURL, page, clicknav }) => { + test('errors on invalid status', async ({ baseURL, page, clicknav, javaScriptEnabled }) => { await page.goto('/redirect'); await clicknav('[href="/redirect/missing-status/b"]'); + const message = process.env.DEV || !javaScriptEnabled ? 'Invalid status code' : 'Redirect loop'; + expect(page.url()).toBe(`${baseURL}/redirect/missing-status/b`); expect(await page.textContent('h1')).toBe('500'); expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: "Invalid status code"' + `This is your custom error page saying: "${message}"` ); });