Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/vinext/src/shims/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ export class NextRequest extends Request {
// NextResponse
// ---------------------------------------------------------------------------

/** Valid HTTP redirect status codes, matching Next.js's REDIRECTS set. */
const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);

export class NextResponse<_Body = unknown> extends Response {
private _cookies: ResponseCookies;

Expand Down Expand Up @@ -192,6 +195,9 @@ export class NextResponse<_Body = unknown> extends Response {
*/
static redirect(url: string | URL, init?: number | ResponseInit): NextResponse {
const status = typeof init === "number" ? init : (init?.status ?? 307);
if (!REDIRECT_STATUSES.has(status)) {
throw new RangeError(`Failed to execute "redirect" on "response": Invalid status code`);
}
const destination = typeof url === "string" ? url : url.toString();
const headers = new Headers(typeof init === "object" ? init?.headers : undefined);
headers.set("Location", destination);
Expand Down
25 changes: 25 additions & 0 deletions tests/shims.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4748,6 +4748,31 @@ describe("NextResponse.redirect() status codes", () => {
const res = NextResponse.redirect(url);
expect(res.headers.get("Location")).toBe("https://example.com/target");
});

it("supports 303 See Other", async () => {
const { NextResponse } = await import("../packages/vinext/src/shims/server.js");
const res = NextResponse.redirect("https://example.com", 303);
expect(res.status).toBe(303);
});

// Ported from Next.js: packages/next/src/server/web/spec-extension/response.ts
// Next.js validates redirect status codes and throws RangeError for invalid ones.
it("throws RangeError for non-redirect status code 200", async () => {
const { NextResponse } = await import("../packages/vinext/src/shims/server.js");
expect(() => NextResponse.redirect("https://example.com", 200)).toThrow(RangeError);
});

it("throws RangeError for non-redirect status code 418", async () => {
const { NextResponse } = await import("../packages/vinext/src/shims/server.js");
expect(() => NextResponse.redirect("https://example.com", 418)).toThrow(RangeError);
});

it("throws RangeError with descriptive message for invalid status", async () => {
const { NextResponse } = await import("../packages/vinext/src/shims/server.js");
expect(() => NextResponse.redirect("https://example.com", 200)).toThrow(
/Failed to execute "redirect" on "response": Invalid status code/,
);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the current tests only cover the init: number overload. It would be worth adding a case for the init: ResponseInit overload with an invalid status, since that's a separate code path through the ternary on line 197 of server.ts:

Suggested change
});
});
it("throws RangeError when status is passed via ResponseInit object", async () => {
const { NextResponse } = await import("../packages/vinext/src/shims/server.js");
expect(() => NextResponse.redirect("https://example.com", { status: 200 })).toThrow(RangeError);
});

});

// ---------------------------------------------------------------------------
Expand Down
Loading