diff --git a/e2e/react-start/basic/package.json b/e2e/react-start/basic/package.json index 1b81972f0c..ffc2ea99ac 100644 --- a/e2e/react-start/basic/package.json +++ b/e2e/react-start/basic/package.json @@ -26,6 +26,7 @@ "@tanstack/react-start": "workspace:^", "express": "^5.1.0", "http-proxy-middleware": "^3.0.5", + "js-cookie": "^3.0.5", "react": "^19.0.0", "react-dom": "^19.0.0", "redaxios": "^0.5.1", @@ -35,6 +36,7 @@ "@playwright/test": "^1.50.1", "@tailwindcss/postcss": "^4.1.15", "@tanstack/router-e2e-utils": "workspace:^", + "@types/js-cookie": "^3.0.6", "@types/node": "^22.10.2", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", diff --git a/e2e/react-start/basic/src/routeTree.gen.ts b/e2e/react-start/basic/src/routeTree.gen.ts index 1f798d17fa..0675c8b5f2 100644 --- a/e2e/react-start/basic/src/routeTree.gen.ts +++ b/e2e/react-start/basic/src/routeTree.gen.ts @@ -28,6 +28,7 @@ import { Route as SearchParamsIndexRouteImport } from './routes/search-params/in import { Route as RedirectIndexRouteImport } from './routes/redirect/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' +import { Route as MultiCookieRedirectIndexRouteImport } from './routes/multi-cookie-redirect/index' import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect' import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' @@ -36,6 +37,7 @@ import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader' import { Route as NotFoundViaHeadRouteImport } from './routes/not-found/via-head' import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad' +import { Route as MultiCookieRedirectTargetRouteImport } from './routes/multi-cookie-redirect/target' import { Route as ApiUsersRouteImport } from './routes/api.users' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' @@ -139,6 +141,12 @@ const NotFoundIndexRoute = NotFoundIndexRouteImport.update({ path: '/', getParentRoute: () => NotFoundRouteRoute, } as any) +const MultiCookieRedirectIndexRoute = + MultiCookieRedirectIndexRouteImport.update({ + id: '/multi-cookie-redirect/', + path: '/multi-cookie-redirect/', + getParentRoute: () => rootRouteImport, + } as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', @@ -180,6 +188,12 @@ const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({ path: '/via-beforeLoad', getParentRoute: () => NotFoundRouteRoute, } as any) +const MultiCookieRedirectTargetRoute = + MultiCookieRedirectTargetRouteImport.update({ + id: '/multi-cookie-redirect/target', + path: '/multi-cookie-redirect/target', + getParentRoute: () => rootRouteImport, + } as any) const ApiUsersRoute = ApiUsersRouteImport.update({ id: '/api/users', path: '/api/users', @@ -277,6 +291,7 @@ export interface FileRoutesByFullPath { '/users': typeof UsersRouteWithChildren '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/api/users': typeof ApiUsersRouteWithChildren + '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-head': typeof NotFoundViaHeadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute @@ -285,6 +300,7 @@ export interface FileRoutesByFullPath { '/search-params/default': typeof SearchParamsDefaultRoute '/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute '/users/$userId': typeof UsersUserIdRoute + '/multi-cookie-redirect': typeof MultiCookieRedirectIndexRoute '/not-found/': typeof NotFoundIndexRoute '/posts/': typeof PostsIndexRoute '/redirect': typeof RedirectIndexRoute @@ -313,6 +329,7 @@ export interface FileRoutesByTo { '/stream': typeof StreamRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/api/users': typeof ApiUsersRouteWithChildren + '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-head': typeof NotFoundViaHeadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute @@ -320,6 +337,7 @@ export interface FileRoutesByTo { '/search-params/default': typeof SearchParamsDefaultRoute '/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute '/users/$userId': typeof UsersUserIdRoute + '/multi-cookie-redirect': typeof MultiCookieRedirectIndexRoute '/not-found': typeof NotFoundIndexRoute '/posts': typeof PostsIndexRoute '/redirect': typeof RedirectIndexRoute @@ -354,6 +372,7 @@ export interface FileRoutesById { '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren + '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-head': typeof NotFoundViaHeadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute @@ -362,6 +381,7 @@ export interface FileRoutesById { '/search-params/default': typeof SearchParamsDefaultRoute '/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute '/users/$userId': typeof UsersUserIdRoute + '/multi-cookie-redirect/': typeof MultiCookieRedirectIndexRoute '/not-found/': typeof NotFoundIndexRoute '/posts/': typeof PostsIndexRoute '/redirect/': typeof RedirectIndexRoute @@ -397,6 +417,7 @@ export interface FileRouteTypes { | '/users' | '/대한민국' | '/api/users' + | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-head' | '/not-found/via-loader' @@ -405,6 +426,7 @@ export interface FileRouteTypes { | '/search-params/default' | '/search-params/loader-throws-redirect' | '/users/$userId' + | '/multi-cookie-redirect' | '/not-found/' | '/posts/' | '/redirect' @@ -433,6 +455,7 @@ export interface FileRouteTypes { | '/stream' | '/대한민국' | '/api/users' + | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-head' | '/not-found/via-loader' @@ -440,6 +463,7 @@ export interface FileRouteTypes { | '/search-params/default' | '/search-params/loader-throws-redirect' | '/users/$userId' + | '/multi-cookie-redirect' | '/not-found' | '/posts' | '/redirect' @@ -473,6 +497,7 @@ export interface FileRouteTypes { | '/대한민국' | '/_layout/_layout-2' | '/api/users' + | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-head' | '/not-found/via-loader' @@ -481,6 +506,7 @@ export interface FileRouteTypes { | '/search-params/default' | '/search-params/loader-throws-redirect' | '/users/$userId' + | '/multi-cookie-redirect/' | '/not-found/' | '/posts/' | '/redirect/' @@ -516,7 +542,9 @@ export interface RootRouteChildren { UsersRoute: typeof UsersRouteWithChildren Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route ApiUsersRoute: typeof ApiUsersRouteWithChildren + MultiCookieRedirectTargetRoute: typeof MultiCookieRedirectTargetRoute RedirectTargetRoute: typeof RedirectTargetRouteWithChildren + MultiCookieRedirectIndexRoute: typeof MultiCookieRedirectIndexRoute RedirectIndexRoute: typeof RedirectIndexRoute PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute FooBarQuxRoute: typeof FooBarQuxRouteWithChildren @@ -643,6 +671,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NotFoundIndexRouteImport parentRoute: typeof NotFoundRouteRoute } + '/multi-cookie-redirect/': { + id: '/multi-cookie-redirect/' + path: '/multi-cookie-redirect' + fullPath: '/multi-cookie-redirect' + preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport + parentRoute: typeof rootRouteImport + } '/users/$userId': { id: '/users/$userId' path: '/$userId' @@ -699,6 +734,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport parentRoute: typeof NotFoundRouteRoute } + '/multi-cookie-redirect/target': { + id: '/multi-cookie-redirect/target' + path: '/multi-cookie-redirect/target' + fullPath: '/multi-cookie-redirect/target' + preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport + parentRoute: typeof rootRouteImport + } '/api/users': { id: '/api/users' path: '/api/users' @@ -973,7 +1015,9 @@ const rootRouteChildren: RootRouteChildren = { Char45824Char54620Char48124Char44397Route: Char45824Char54620Char48124Char44397Route, ApiUsersRoute: ApiUsersRouteWithChildren, + MultiCookieRedirectTargetRoute: MultiCookieRedirectTargetRoute, RedirectTargetRoute: RedirectTargetRouteWithChildren, + MultiCookieRedirectIndexRoute: MultiCookieRedirectIndexRoute, RedirectIndexRoute: RedirectIndexRoute, PostsPostIdDeepRoute: PostsPostIdDeepRoute, FooBarQuxRoute: FooBarQuxRouteWithChildren, diff --git a/e2e/react-start/basic/src/routes/multi-cookie-redirect/index.tsx b/e2e/react-start/basic/src/routes/multi-cookie-redirect/index.tsx new file mode 100644 index 0000000000..7ae4947aef --- /dev/null +++ b/e2e/react-start/basic/src/routes/multi-cookie-redirect/index.tsx @@ -0,0 +1,18 @@ +import { createFileRoute, redirect } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' +import { setCookie } from '@tanstack/react-start/server' + +const setMultipleCookiesAndRedirect = createServerFn().handler(() => { + // Set multiple cookies before redirecting + // This tests that multiple Set-Cookie headers are preserved during redirect + setCookie('session', 'session-value', { path: '/' }) + setCookie('csrf', 'csrf-token-value', { path: '/' }) + setCookie('theme', 'dark', { path: '/' }) + + throw redirect({ to: '/multi-cookie-redirect/target' }) +}) + +export const Route = createFileRoute('/multi-cookie-redirect/')({ + loader: () => setMultipleCookiesAndRedirect(), + component: () => null, +}) diff --git a/e2e/react-start/basic/src/routes/multi-cookie-redirect/target.tsx b/e2e/react-start/basic/src/routes/multi-cookie-redirect/target.tsx new file mode 100644 index 0000000000..3b0cb20bef --- /dev/null +++ b/e2e/react-start/basic/src/routes/multi-cookie-redirect/target.tsx @@ -0,0 +1,39 @@ +import { createFileRoute } from '@tanstack/react-router' +import Cookies from 'js-cookie' +import React, { useEffect } from 'react' + +export const Route = createFileRoute('/multi-cookie-redirect/target')({ + component: RouteComponent, +}) + +function RouteComponent() { + const [cookies, setCookies] = React.useState>({}) + + useEffect(() => { + setCookies({ + session: Cookies.get('session') || '', + csrf: Cookies.get('csrf') || '', + theme: Cookies.get('theme') || '', + }) + }, []) + + return ( +
+

+ Multi Cookie Redirect Target +

+
+

+ Session cookie:{' '} + {cookies.session} +

+

+ CSRF cookie: {cookies.csrf} +

+

+ Theme cookie: {cookies.theme} +

+
+
+ ) +} diff --git a/e2e/react-start/basic/tests/redirect.spec.ts b/e2e/react-start/basic/tests/redirect.spec.ts index 7fcaed8d73..bc36de9623 100644 --- a/e2e/react-start/basic/tests/redirect.spec.ts +++ b/e2e/react-start/basic/tests/redirect.spec.ts @@ -228,4 +228,23 @@ test.describe('redirects', () => { }) }) }) + + test('multiple Set-Cookie headers are preserved on redirect', async ({ + page, + }) => { + // This test verifies that multiple Set-Cookie headers are not lost during redirect + await page.goto('/multi-cookie-redirect') + + // Wait for redirect to complete + await page.waitForURL(/\/multi-cookie-redirect\/target/) + + // Should redirect to target page + await expect(page.getByTestId('multi-cookie-redirect-target')).toBeVisible() + expect(page.url()).toContain('/multi-cookie-redirect/target') + + // Verify all three cookies were preserved during the redirect + await expect(page.getByTestId('cookie-session')).toHaveText('session-value') + await expect(page.getByTestId('cookie-csrf')).toHaveText('csrf-token-value') + await expect(page.getByTestId('cookie-theme')).toHaveText('dark') + }) }) diff --git a/e2e/solid-start/basic/package.json b/e2e/solid-start/basic/package.json index 60c169339c..05789f8ccb 100644 --- a/e2e/solid-start/basic/package.json +++ b/e2e/solid-start/basic/package.json @@ -26,6 +26,7 @@ "@tanstack/solid-start": "workspace:^", "express": "^5.1.0", "http-proxy-middleware": "^3.0.5", + "js-cookie": "^3.0.5", "redaxios": "^0.5.1", "solid-js": "^1.9.10", "tailwind-merge": "^2.6.0", @@ -36,6 +37,7 @@ "@playwright/test": "^1.50.1", "@tailwindcss/postcss": "^4.1.15", "@tanstack/router-e2e-utils": "workspace:^", + "@types/js-cookie": "^3.0.6", "@types/node": "^22.10.2", "combinate": "^1.1.11", "postcss": "^8.5.1", diff --git a/e2e/solid-start/basic/src/routeTree.gen.ts b/e2e/solid-start/basic/src/routeTree.gen.ts index f1ecc1d149..9919aecb00 100644 --- a/e2e/solid-start/basic/src/routeTree.gen.ts +++ b/e2e/solid-start/basic/src/routeTree.gen.ts @@ -26,6 +26,7 @@ import { Route as SearchParamsIndexRouteImport } from './routes/search-params/in import { Route as RedirectIndexRouteImport } from './routes/redirect/index' import { Route as PostsIndexRouteImport } from './routes/posts.index' import { Route as NotFoundIndexRouteImport } from './routes/not-found/index' +import { Route as MultiCookieRedirectIndexRouteImport } from './routes/multi-cookie-redirect/index' import { Route as UsersUserIdRouteImport } from './routes/users.$userId' import { Route as SearchParamsLoaderThrowsRedirectRouteImport } from './routes/search-params/loader-throws-redirect' import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' @@ -34,6 +35,7 @@ import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' import { Route as NotFoundViaLoaderRouteImport } from './routes/not-found/via-loader' import { Route as NotFoundViaHeadRouteImport } from './routes/not-found/via-head' import { Route as NotFoundViaBeforeLoadRouteImport } from './routes/not-found/via-beforeLoad' +import { Route as MultiCookieRedirectTargetRouteImport } from './routes/multi-cookie-redirect/target' import { Route as ApiUsersRouteImport } from './routes/api/users' import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' @@ -135,6 +137,12 @@ const NotFoundIndexRoute = NotFoundIndexRouteImport.update({ path: '/', getParentRoute: () => NotFoundRouteRoute, } as any) +const MultiCookieRedirectIndexRoute = + MultiCookieRedirectIndexRouteImport.update({ + id: '/multi-cookie-redirect/', + path: '/multi-cookie-redirect/', + getParentRoute: () => rootRouteImport, + } as any) const UsersUserIdRoute = UsersUserIdRouteImport.update({ id: '/$userId', path: '/$userId', @@ -176,6 +184,12 @@ const NotFoundViaBeforeLoadRoute = NotFoundViaBeforeLoadRouteImport.update({ path: '/via-beforeLoad', getParentRoute: () => NotFoundRouteRoute, } as any) +const MultiCookieRedirectTargetRoute = + MultiCookieRedirectTargetRouteImport.update({ + id: '/multi-cookie-redirect/target', + path: '/multi-cookie-redirect/target', + getParentRoute: () => rootRouteImport, + } as any) const ApiUsersRoute = ApiUsersRouteImport.update({ id: '/api/users', path: '/api/users', @@ -271,6 +285,7 @@ export interface FileRoutesByFullPath { '/users': typeof UsersRouteWithChildren '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/api/users': typeof ApiUsersRouteWithChildren + '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-head': typeof NotFoundViaHeadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute @@ -279,6 +294,7 @@ export interface FileRoutesByFullPath { '/search-params/default': typeof SearchParamsDefaultRoute '/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute '/users/$userId': typeof UsersUserIdRoute + '/multi-cookie-redirect': typeof MultiCookieRedirectIndexRoute '/not-found/': typeof NotFoundIndexRoute '/posts/': typeof PostsIndexRoute '/redirect': typeof RedirectIndexRoute @@ -307,6 +323,7 @@ export interface FileRoutesByTo { '/stream': typeof StreamRoute '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/api/users': typeof ApiUsersRouteWithChildren + '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-head': typeof NotFoundViaHeadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute @@ -314,6 +331,7 @@ export interface FileRoutesByTo { '/search-params/default': typeof SearchParamsDefaultRoute '/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute '/users/$userId': typeof UsersUserIdRoute + '/multi-cookie-redirect': typeof MultiCookieRedirectIndexRoute '/not-found': typeof NotFoundIndexRoute '/posts': typeof PostsIndexRoute '/redirect': typeof RedirectIndexRoute @@ -349,6 +367,7 @@ export interface FileRoutesById { '/대한민국': typeof Char45824Char54620Char48124Char44397Route '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren '/api/users': typeof ApiUsersRouteWithChildren + '/multi-cookie-redirect/target': typeof MultiCookieRedirectTargetRoute '/not-found/via-beforeLoad': typeof NotFoundViaBeforeLoadRoute '/not-found/via-head': typeof NotFoundViaHeadRoute '/not-found/via-loader': typeof NotFoundViaLoaderRoute @@ -357,6 +376,7 @@ export interface FileRoutesById { '/search-params/default': typeof SearchParamsDefaultRoute '/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute '/users/$userId': typeof UsersUserIdRoute + '/multi-cookie-redirect/': typeof MultiCookieRedirectIndexRoute '/not-found/': typeof NotFoundIndexRoute '/posts/': typeof PostsIndexRoute '/redirect/': typeof RedirectIndexRoute @@ -391,6 +411,7 @@ export interface FileRouteTypes { | '/users' | '/대한민국' | '/api/users' + | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-head' | '/not-found/via-loader' @@ -399,6 +420,7 @@ export interface FileRouteTypes { | '/search-params/default' | '/search-params/loader-throws-redirect' | '/users/$userId' + | '/multi-cookie-redirect' | '/not-found/' | '/posts/' | '/redirect' @@ -427,6 +449,7 @@ export interface FileRouteTypes { | '/stream' | '/대한민국' | '/api/users' + | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-head' | '/not-found/via-loader' @@ -434,6 +457,7 @@ export interface FileRouteTypes { | '/search-params/default' | '/search-params/loader-throws-redirect' | '/users/$userId' + | '/multi-cookie-redirect' | '/not-found' | '/posts' | '/redirect' @@ -468,6 +492,7 @@ export interface FileRouteTypes { | '/대한민국' | '/_layout/_layout-2' | '/api/users' + | '/multi-cookie-redirect/target' | '/not-found/via-beforeLoad' | '/not-found/via-head' | '/not-found/via-loader' @@ -476,6 +501,7 @@ export interface FileRouteTypes { | '/search-params/default' | '/search-params/loader-throws-redirect' | '/users/$userId' + | '/multi-cookie-redirect/' | '/not-found/' | '/posts/' | '/redirect/' @@ -510,7 +536,9 @@ export interface RootRouteChildren { UsersRoute: typeof UsersRouteWithChildren Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route ApiUsersRoute: typeof ApiUsersRouteWithChildren + MultiCookieRedirectTargetRoute: typeof MultiCookieRedirectTargetRoute RedirectTargetRoute: typeof RedirectTargetRouteWithChildren + MultiCookieRedirectIndexRoute: typeof MultiCookieRedirectIndexRoute RedirectIndexRoute: typeof RedirectIndexRoute PostsPostIdDeepRoute: typeof PostsPostIdDeepRoute TransitionCountCreateResourceRoute: typeof TransitionCountCreateResourceRoute @@ -638,6 +666,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof NotFoundIndexRouteImport parentRoute: typeof NotFoundRouteRoute } + '/multi-cookie-redirect/': { + id: '/multi-cookie-redirect/' + path: '/multi-cookie-redirect' + fullPath: '/multi-cookie-redirect' + preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport + parentRoute: typeof rootRouteImport + } '/users/$userId': { id: '/users/$userId' path: '/$userId' @@ -694,6 +729,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof NotFoundViaBeforeLoadRouteImport parentRoute: typeof NotFoundRouteRoute } + '/multi-cookie-redirect/target': { + id: '/multi-cookie-redirect/target' + path: '/multi-cookie-redirect/target' + fullPath: '/multi-cookie-redirect/target' + preLoaderRoute: typeof MultiCookieRedirectTargetRouteImport + parentRoute: typeof rootRouteImport + } '/api/users': { id: '/api/users' path: '/api/users' @@ -937,7 +979,9 @@ const rootRouteChildren: RootRouteChildren = { Char45824Char54620Char48124Char44397Route: Char45824Char54620Char48124Char44397Route, ApiUsersRoute: ApiUsersRouteWithChildren, + MultiCookieRedirectTargetRoute: MultiCookieRedirectTargetRoute, RedirectTargetRoute: RedirectTargetRouteWithChildren, + MultiCookieRedirectIndexRoute: MultiCookieRedirectIndexRoute, RedirectIndexRoute: RedirectIndexRoute, PostsPostIdDeepRoute: PostsPostIdDeepRoute, TransitionCountCreateResourceRoute: TransitionCountCreateResourceRoute, diff --git a/e2e/solid-start/basic/src/routes/multi-cookie-redirect/index.tsx b/e2e/solid-start/basic/src/routes/multi-cookie-redirect/index.tsx new file mode 100644 index 0000000000..a7abb3b009 --- /dev/null +++ b/e2e/solid-start/basic/src/routes/multi-cookie-redirect/index.tsx @@ -0,0 +1,18 @@ +import { createFileRoute, redirect } from '@tanstack/solid-router' +import { createServerFn } from '@tanstack/solid-start' +import { setCookie } from '@tanstack/solid-start/server' + +const setMultipleCookiesAndRedirect = createServerFn().handler(() => { + // Set multiple cookies before redirecting + // This tests that multiple Set-Cookie headers are preserved during redirect + setCookie('session', 'session-value', { path: '/' }) + setCookie('csrf', 'csrf-token-value', { path: '/' }) + setCookie('theme', 'dark', { path: '/' }) + + throw redirect({ to: '/multi-cookie-redirect/target' }) +}) + +export const Route = createFileRoute('/multi-cookie-redirect/')({ + loader: () => setMultipleCookiesAndRedirect(), + component: () => null, +}) diff --git a/e2e/solid-start/basic/src/routes/multi-cookie-redirect/target.tsx b/e2e/solid-start/basic/src/routes/multi-cookie-redirect/target.tsx new file mode 100644 index 0000000000..1ebd4ecb39 --- /dev/null +++ b/e2e/solid-start/basic/src/routes/multi-cookie-redirect/target.tsx @@ -0,0 +1,40 @@ +import { createFileRoute } from '@tanstack/solid-router' +import Cookies from 'js-cookie' +import * as Solid from 'solid-js' + +export const Route = createFileRoute('/multi-cookie-redirect/target')({ + component: RouteComponent, +}) + +function RouteComponent() { + const [cookies, setCookies] = Solid.createSignal>({}) + + Solid.onMount(() => { + setCookies({ + session: Cookies.get('session') || '', + csrf: Cookies.get('csrf') || '', + theme: Cookies.get('theme') || '', + }) + }) + + return ( +
+

+ Multi Cookie Redirect Target +

+
+

+ Session cookie:{' '} + {cookies().session} +

+

+ CSRF cookie: {cookies().csrf} +

+

+ Theme cookie:{' '} + {cookies().theme} +

+
+
+ ) +} diff --git a/e2e/solid-start/basic/tests/redirect.spec.ts b/e2e/solid-start/basic/tests/redirect.spec.ts index 60fd3df329..b52ce026af 100644 --- a/e2e/solid-start/basic/tests/redirect.spec.ts +++ b/e2e/solid-start/basic/tests/redirect.spec.ts @@ -211,4 +211,23 @@ test.describe('redirects', () => { } }) }) + + test('multiple Set-Cookie headers are preserved on redirect', async ({ + page, + }) => { + // This test verifies that multiple Set-Cookie headers are not lost during redirect + await page.goto('/multi-cookie-redirect') + + // Wait for redirect to complete + await page.waitForURL(/\/multi-cookie-redirect\/target/) + + // Should redirect to target page + await expect(page.getByTestId('multi-cookie-redirect-target')).toBeVisible() + expect(page.url()).toContain('/multi-cookie-redirect/target') + + // Verify all three cookies were preserved during the redirect + await expect(page.getByTestId('cookie-session')).toHaveText('session-value') + await expect(page.getByTestId('cookie-csrf')).toHaveText('csrf-token-value') + await expect(page.getByTestId('cookie-theme')).toHaveText('dark') + }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab8840a37c..b764c63d15 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1054,6 +1054,9 @@ importers: http-proxy-middleware: specifier: ^3.0.5 version: 3.0.5 + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 react: specifier: ^19.2.0 version: 19.2.0 @@ -1076,6 +1079,9 @@ importers: '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils + '@types/js-cookie': + specifier: ^3.0.6 + version: 3.0.6 '@types/node': specifier: 22.10.2 version: 22.10.2 @@ -2802,6 +2808,9 @@ importers: http-proxy-middleware: specifier: ^3.0.5 version: 3.0.5 + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 redaxios: specifier: ^0.5.1 version: 0.5.1 @@ -2827,6 +2836,9 @@ importers: '@tanstack/router-e2e-utils': specifier: workspace:^ version: link:../../e2e-utils + '@types/js-cookie': + specifier: ^3.0.6 + version: 3.0.6 '@types/node': specifier: 22.10.2 version: 22.10.2