diff --git a/.changeset/cool-chefs-search.md b/.changeset/cool-chefs-search.md new file mode 100644 index 000000000000..3440b892ccad --- /dev/null +++ b/.changeset/cool-chefs-search.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +chore: migrate to cookie-es for cookie parsing and splitting diff --git a/packages/adapter-netlify/package.json b/packages/adapter-netlify/package.json index 5d348330663c..19468b5d0fae 100644 --- a/packages/adapter-netlify/package.json +++ b/packages/adapter-netlify/package.json @@ -42,8 +42,8 @@ }, "dependencies": { "@iarna/toml": "^2.2.5", - "esbuild": "^0.24.0", - "set-cookie-parser": "^2.6.0" + "cookie-es": "^2.0.0", + "esbuild": "^0.24.0" }, "devDependencies": { "@netlify/functions": "^3.0.0", @@ -53,7 +53,6 @@ "@sveltejs/kit": "workspace:^", "@sveltejs/vite-plugin-svelte": "^5.0.1", "@types/node": "^18.19.48", - "@types/set-cookie-parser": "^2.4.7", "rollup": "^4.14.2", "typescript": "^5.3.3", "vitest": "^3.0.1" diff --git a/packages/adapter-netlify/src/headers.js b/packages/adapter-netlify/src/headers.js index 8fee7ddb38eb..28946aadd4bd 100644 --- a/packages/adapter-netlify/src/headers.js +++ b/packages/adapter-netlify/src/headers.js @@ -1,4 +1,4 @@ -import * as set_cookie_parser from 'set-cookie-parser'; +import { splitSetCookieString } from 'cookie-es'; /** * Splits headers into two categories: single value and multi value @@ -18,7 +18,7 @@ export function split_headers(headers) { headers.forEach((value, key) => { if (key === 'set-cookie') { if (!m[key]) m[key] = []; - m[key].push(...set_cookie_parser.splitCookiesString(value)); + m[key].push(...splitSetCookieString(value)); } else { h[key] = value; } diff --git a/packages/kit/package.json b/packages/kit/package.json index 56ff5e7219d4..eeb6c81a48ca 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -18,8 +18,7 @@ "homepage": "https://svelte.dev", "type": "module", "dependencies": { - "@types/cookie": "^0.6.0", - "cookie": "^0.6.0", + "cookie-es": "^2.0.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", @@ -27,7 +26,6 @@ "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", - "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "devDependencies": { @@ -35,7 +33,6 @@ "@sveltejs/vite-plugin-svelte": "^5.0.1", "@types/connect": "^3.4.38", "@types/node": "^18.19.48", - "@types/set-cookie-parser": "^2.4.7", "dts-buddy": "^0.5.5", "rollup": "^4.14.2", "svelte": "^5.2.9", diff --git a/packages/kit/src/exports/node/index.js b/packages/kit/src/exports/node/index.js index a69b7ae6d906..ccb352972999 100644 --- a/packages/kit/src/exports/node/index.js +++ b/packages/kit/src/exports/node/index.js @@ -1,6 +1,6 @@ import { createReadStream } from 'node:fs'; import { Readable } from 'node:stream'; -import * as set_cookie_parser from 'set-cookie-parser'; +import { splitSetCookieString } from 'cookie-es'; import { SvelteKitError } from '../../runtime/control.js'; /** @@ -145,7 +145,7 @@ export async function setResponse(res, response) { res.setHeader( key, key === 'set-cookie' - ? set_cookie_parser.splitCookiesString( + ? splitSetCookieString( // This is absurd but necessary, TODO: investigate why /** @type {string}*/ (response.headers.get(key)) ) diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index f25cc225e194..b960c5962e9b 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -215,13 +215,13 @@ export interface Cookies { * @param name the name of the cookie * @param opts the options, passed directly to `cookie.parse`. See documentation [here](https://github.com/jshttp/cookie#cookieparsestr-options) */ - get: (name: string, opts?: import('cookie').CookieParseOptions) => string | undefined; + get: (name: string, opts?: import('cookie-es').CookieParseOptions) => string | undefined; /** * Gets all cookies that were previously set with `cookies.set`, or from the request headers. * @param opts the options, passed directly to `cookie.parse`. See documentation [here](https://github.com/jshttp/cookie#cookieparsestr-options) */ - getAll: (opts?: import('cookie').CookieParseOptions) => Array<{ name: string; value: string }>; + getAll: (opts?: import('cookie-es').CookieParseOptions) => Array<{ name: string; value: string }>; /** * Sets a cookie. This will add a `set-cookie` header to the response, but also make the cookie available via `cookies.get` or `cookies.getAll` during the current request. @@ -236,7 +236,7 @@ export interface Cookies { set: ( name: string, value: string, - opts: import('cookie').CookieSerializeOptions & { path: string } + opts: import('cookie-es').CookieSerializeOptions & { path: string } ) => void; /** @@ -246,7 +246,10 @@ export interface Cookies { * @param name the name of the cookie * @param opts the options, passed directly to `cookie.serialize`. The `path` must match the path of the cookie you want to delete. See documentation [here](https://github.com/jshttp/cookie#cookieserializename-value-options) */ - delete: (name: string, opts: import('cookie').CookieSerializeOptions & { path: string }) => void; + delete: ( + name: string, + opts: import('cookie-es').CookieSerializeOptions & { path: string } + ) => void; /** * Serialize a cookie name-value pair into a `Set-Cookie` header string, but don't apply it to the response. @@ -262,7 +265,7 @@ export interface Cookies { serialize: ( name: string, value: string, - opts: import('cookie').CookieSerializeOptions & { path: string } + opts: import('cookie-es').CookieSerializeOptions & { path: string } ) => string; } diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index bdb37b1f9cff..d79077262dc6 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -334,13 +334,6 @@ async function kit({ svelte_config }) { __SVELTEKIT_EMBEDDED__: kit.embedded ? 'true' : 'false', __SVELTEKIT_CLIENT_ROUTING__: kit.router.resolution === 'client' ? 'true' : 'false' }; - - // These Kit dependencies are packaged as CommonJS, which means they must always be externalized. - // Without this, the tests will still pass but `pnpm dev` will fail in projects that link `@sveltejs/kit`. - /** @type {NonNullable} */ (new_config.ssr).external = [ - 'cookie', - 'set-cookie-parser' - ]; } warn_overridden_config(config, new_config); diff --git a/packages/kit/src/runtime/server/cookie.js b/packages/kit/src/runtime/server/cookie.js index b5bc4d29563b..502e06794a05 100644 --- a/packages/kit/src/runtime/server/cookie.js +++ b/packages/kit/src/runtime/server/cookie.js @@ -1,4 +1,4 @@ -import { parse, serialize } from 'cookie'; +import { parse, serialize } from 'cookie-es'; import { normalize_path, resolve } from '../../utils/url.js'; import { add_data_suffix } from '../pathname.js'; @@ -40,7 +40,7 @@ export function get_cookies(request, url, trailing_slash) { /** @type {Record} */ const new_cookies = {}; - /** @type {import('cookie').CookieSerializeOptions} */ + /** @type {import('cookie-es').CookieSerializeOptions} */ const defaults = { httpOnly: true, sameSite: 'lax', @@ -56,7 +56,7 @@ export function get_cookies(request, url, trailing_slash) { /** * @param {string} name - * @param {import('cookie').CookieParseOptions} [opts] + * @param {import('cookie-es').CookieParseOptions} [opts] */ get(name, opts) { const c = new_cookies[name]; @@ -92,7 +92,7 @@ export function get_cookies(request, url, trailing_slash) { }, /** - * @param {import('cookie').CookieParseOptions} [opts] + * @param {import('cookie-es').CookieParseOptions} [opts] */ getAll(opts) { const cookies = parse(header, { decode: opts?.decode }); diff --git a/packages/kit/src/runtime/server/fetch.js b/packages/kit/src/runtime/server/fetch.js index 81bd5c665d8e..83a9e11e40f6 100644 --- a/packages/kit/src/runtime/server/fetch.js +++ b/packages/kit/src/runtime/server/fetch.js @@ -1,4 +1,4 @@ -import * as set_cookie_parser from 'set-cookie-parser'; +import { splitSetCookieString, parseSetCookie } from 'cookie-es'; import { respond } from './respond.js'; import * as paths from '__sveltekit/paths'; import { read_implementation } from '__sveltekit/server'; @@ -154,10 +154,8 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade const set_cookie = response.headers.get('set-cookie'); if (set_cookie) { - for (const str of set_cookie_parser.splitCookiesString(set_cookie)) { - const { name, value, ...options } = set_cookie_parser.parseString(str, { - decodeValues: false - }); + for (const str of splitSetCookieString(set_cookie)) { + const { name, value, ...options } = parseSetCookie(str, { decode: false }); const path = options.path ?? (url.pathname.split('/').slice(0, -1).join('/') || '/'); @@ -165,7 +163,7 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade set_internal(name, value, { path, encode: (value) => value, - .../** @type {import('cookie').CookieSerializeOptions} */ (options) + .../** @type {import('cookie-es').CookieSerializeOptions} */ (options) }); } } diff --git a/packages/kit/src/runtime/server/page/types.d.ts b/packages/kit/src/runtime/server/page/types.d.ts index 7e501b1ab418..9e859aa2d75a 100644 --- a/packages/kit/src/runtime/server/page/types.d.ts +++ b/packages/kit/src/runtime/server/page/types.d.ts @@ -1,4 +1,4 @@ -import { CookieSerializeOptions } from 'cookie'; +import { CookieSerializeOptions } from 'cookie-es'; import { SSRNode, CspDirectives, ServerDataNode } from 'types'; export interface Fetched { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index f7ca3bce33f9..6e6fd8a2e317 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -197,13 +197,13 @@ declare module '@sveltejs/kit' { * @param name the name of the cookie * @param opts the options, passed directly to `cookie.parse`. See documentation [here](https://github.com/jshttp/cookie#cookieparsestr-options) */ - get: (name: string, opts?: import('cookie').CookieParseOptions) => string | undefined; + get: (name: string, opts?: import('cookie-es').CookieParseOptions) => string | undefined; /** * Gets all cookies that were previously set with `cookies.set`, or from the request headers. * @param opts the options, passed directly to `cookie.parse`. See documentation [here](https://github.com/jshttp/cookie#cookieparsestr-options) */ - getAll: (opts?: import('cookie').CookieParseOptions) => Array<{ name: string; value: string }>; + getAll: (opts?: import('cookie-es').CookieParseOptions) => Array<{ name: string; value: string }>; /** * Sets a cookie. This will add a `set-cookie` header to the response, but also make the cookie available via `cookies.get` or `cookies.getAll` during the current request. @@ -218,7 +218,7 @@ declare module '@sveltejs/kit' { set: ( name: string, value: string, - opts: import('cookie').CookieSerializeOptions & { path: string } + opts: import('cookie-es').CookieSerializeOptions & { path: string } ) => void; /** @@ -228,7 +228,10 @@ declare module '@sveltejs/kit' { * @param name the name of the cookie * @param opts the options, passed directly to `cookie.serialize`. The `path` must match the path of the cookie you want to delete. See documentation [here](https://github.com/jshttp/cookie#cookieserializename-value-options) */ - delete: (name: string, opts: import('cookie').CookieSerializeOptions & { path: string }) => void; + delete: ( + name: string, + opts: import('cookie-es').CookieSerializeOptions & { path: string } + ) => void; /** * Serialize a cookie name-value pair into a `Set-Cookie` header string, but don't apply it to the response. @@ -244,7 +247,7 @@ declare module '@sveltejs/kit' { serialize: ( name: string, value: string, - opts: import('cookie').CookieSerializeOptions & { path: string } + opts: import('cookie-es').CookieSerializeOptions & { path: string } ) => string; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 009f9699b389..085c3080892c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,12 +113,12 @@ importers: '@iarna/toml': specifier: ^2.2.5 version: 2.2.5 + cookie-es: + specifier: ^2.0.0 + version: 2.0.0 esbuild: specifier: ^0.24.0 version: 0.24.2 - set-cookie-parser: - specifier: ^2.6.0 - version: 2.6.0 devDependencies: '@netlify/functions': specifier: ^3.0.0 @@ -141,9 +141,6 @@ importers: '@types/node': specifier: ^18.19.48 version: 18.19.50 - '@types/set-cookie-parser': - specifier: ^2.4.7 - version: 2.4.7 rollup: specifier: ^4.14.2 version: 4.30.1 @@ -339,12 +336,9 @@ importers: packages/kit: dependencies: - '@types/cookie': - specifier: ^0.6.0 - version: 0.6.0 - cookie: - specifier: ^0.6.0 - version: 0.6.0 + cookie-es: + specifier: ^2.0.0 + version: 2.0.0 devalue: specifier: ^5.1.0 version: 5.1.0 @@ -366,9 +360,6 @@ importers: sade: specifier: ^1.8.1 version: 1.8.1 - set-cookie-parser: - specifier: ^2.6.0 - version: 2.6.0 sirv: specifier: ^3.0.0 version: 3.0.0 @@ -385,9 +376,6 @@ importers: '@types/node': specifier: ^18.19.48 version: 18.19.50 - '@types/set-cookie-parser': - specifier: ^2.4.7 - version: 2.4.7 dts-buddy: specifier: ^0.5.5 version: 0.5.5(typescript@5.6.3) @@ -1965,9 +1953,6 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - '@types/eslint@8.56.12': resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} @@ -1989,9 +1974,6 @@ packages: '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - '@types/set-cookie-parser@2.4.7': - resolution: {integrity: sha512-+ge/loa0oTozxip6zmhRIk8Z/boU51wl9Q6QdLZcokIGMzY5lFXYy/x7Htj2HTC6/KZP1hUbZ1ekx8DYXICvWg==} - '@types/ws@8.5.10': resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} @@ -2258,14 +2240,13 @@ packages: resolution: {integrity: sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==} engines: {node: '>=4'} + cookie-es@2.0.0: + resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - cross-env@7.0.3: resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} @@ -3188,9 +3169,6 @@ packages: engines: {node: '>=10'} hasBin: true - set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -4350,8 +4328,6 @@ snapshots: dependencies: '@types/node': 18.19.50 - '@types/cookie@0.6.0': {} - '@types/eslint@8.56.12': dependencies: '@types/estree': 1.0.6 @@ -4371,10 +4347,6 @@ snapshots: '@types/semver@7.5.8': {} - '@types/set-cookie-parser@2.4.7': - dependencies: - '@types/node': 18.19.50 - '@types/ws@8.5.10': dependencies: '@types/node': 18.19.50 @@ -4658,9 +4630,9 @@ snapshots: console-clear@1.1.1: {} - cookie@0.5.0: {} + cookie-es@2.0.0: {} - cookie@0.6.0: {} + cookie@0.5.0: {} cross-env@7.0.3: dependencies: @@ -5571,8 +5543,6 @@ snapshots: semver@7.7.1: {} - set-cookie-parser@2.6.0: {} - sharp@0.33.5: dependencies: color: 4.2.3