Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/red-bikes-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': minor
---

feat: add `cookie.setSerialized()` to set a cookie from a string
11 changes: 11 additions & 0 deletions packages/kit/src/exports/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,17 @@ export interface Cookies {
opts: import('cookie').CookieSerializeOptions & { path: string }
) => void;

/**
* Sets a cookie from a string representing the value of the `set-cookie` header. This will add the `set-cookie` header to the response, and also make the cookie available via `cookies.get` or `cookies.getAll` during the current request.
*
* No default values. It will set only properties you specified in a cookie.
*
* If you do not specify name, value and path, it will throw an error.
* @param cookie the serialized cookie
* @since 2.21.0
*/
setSerialized: (cookie: string) => void;

/**
* Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
*
Expand Down
39 changes: 39 additions & 0 deletions packages/kit/src/runtime/server/cookie.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { parse, serialize } from 'cookie';
import { DEV } from 'esm-env';
import * as set_cookie_parser from 'set-cookie-parser';
import { normalize_path, resolve } from '../../utils/url.js';
import { add_data_suffix } from '../pathname.js';
import { text_encoder } from '../utils.js';
Expand Down Expand Up @@ -165,6 +166,44 @@ export function get_cookies(request, url) {
set_internal(name, value, { ...defaults, ...options });
},

/**
* @param {string} cookie
*/
setSerialized(cookie) {
if (cookie === '') {
throw new Error('Cannot pass empty string');
}

const parsed = set_cookie_parser.parseString(cookie);
const { name, value, path, sameSite, secure, httpOnly, ...opts } = parsed;

if (name === undefined || value === undefined || path === undefined) {
throw new Error('The name, value and path must be provided to create a cookie.');
}

/**
* @type {true|false|'lax'|'strict'|'none'|undefined}
*/
const normalized_same_site = (() => {
if (sameSite === undefined || typeof sameSite === 'boolean') {
return sameSite;
}
const lower = sameSite.toLowerCase();
if (lower === 'lax' || lower === 'strict' || lower === 'none') {
return /** @type {'lax'|'strict'|'none'} */ (lower);
}
return undefined;
})();

this.set(name, value, {
...opts,
path,
sameSite: normalized_same_site,
secure: secure ?? false,
httpOnly: httpOnly ?? false
});
},

/**
* @param {string} name
* @param {import('./page/types.js').Cookie['options']} options
Expand Down
40 changes: 40 additions & 0 deletions packages/kit/src/runtime/server/cookie.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,3 +280,43 @@
expect(duplicate?.value).toEqual('foobar_value');
});
});

test('no default values when setSerialized is called', () => {
const { cookies, new_cookies } = cookies_setup();
const cookie_string = 'a=b; Path=/;';
cookies.setSerialized(cookie_string);
const opts = new_cookies['a']?.options;
assert.equal(opts?.path, '/');

Check failure on line 289 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit (18, ubuntu-latest, chromium)

src/runtime/server/cookie.spec.js > no default values when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:289:9

Check failure on line 289 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit-server-side-route-resolution (build)

src/runtime/server/cookie.spec.js > no default values when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:289:9

Check failure on line 289 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit (20, ubuntu-latest, chromium)

src/runtime/server/cookie.spec.js > no default values when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:289:9

Check failure on line 289 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit (22, ubuntu-latest, chromium)

src/runtime/server/cookie.spec.js > no default values when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:289:9

Check failure on line 289 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit-cross-browser (18, ubuntu-latest, firefox, build)

src/runtime/server/cookie.spec.js > no default values when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:289:9

Check failure on line 289 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit-cross-browser (18, macOS-latest, webkit, build)

src/runtime/server/cookie.spec.js > no default values when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:289:9

Check failure on line 289 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit-cross-browser (18, windows-latest, chromium, build)

src/runtime/server/cookie.spec.js > no default values when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:289:9
assert.equal(opts?.secure, false);
assert.equal(opts?.httpOnly, false);
assert.equal(opts?.sameSite, undefined);
});

test('set all parameters when setSerialized is called', () => {
const { cookies, new_cookies } = cookies_setup();
const cookie_string =
'a=b; Path=/; Max-Age=3600; Expires=Thu, 03 Apr 2025 00:41:07 GMT; Secure; HttpOnly; SameSite=Strict; domain=example.com';
cookies.setSerialized(cookie_string);
const opts = new_cookies['a']?.options;
assert.equal(opts?.path, '/');

Check failure on line 301 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit (18, ubuntu-latest, chromium)

src/runtime/server/cookie.spec.js > set all parameters when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:301:9

Check failure on line 301 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit-server-side-route-resolution (build)

src/runtime/server/cookie.spec.js > set all parameters when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:301:9

Check failure on line 301 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit (20, ubuntu-latest, chromium)

src/runtime/server/cookie.spec.js > set all parameters when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:301:9

Check failure on line 301 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit (22, ubuntu-latest, chromium)

src/runtime/server/cookie.spec.js > set all parameters when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:301:9

Check failure on line 301 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit-cross-browser (18, ubuntu-latest, firefox, build)

src/runtime/server/cookie.spec.js > set all parameters when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:301:9

Check failure on line 301 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit-cross-browser (18, macOS-latest, webkit, build)

src/runtime/server/cookie.spec.js > set all parameters when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:301:9

Check failure on line 301 in packages/kit/src/runtime/server/cookie.spec.js

View workflow job for this annotation

GitHub Actions / test-kit-cross-browser (18, windows-latest, chromium, build)

src/runtime/server/cookie.spec.js > set all parameters when setSerialized is called

AssertionError: expected undefined to equal '/' - Expected: "/" + Received: undefined ❯ src/runtime/server/cookie.spec.js:301:9
assert.equal(opts?.secure, true);
assert.equal(opts?.httpOnly, true);
assert.equal(opts?.sameSite, 'strict');
assert.equal(opts?.domain, 'example.com');
assert.equal(opts?.maxAge, 3600);
assert.isNotNull(opts.expires);
});

test('throw error when setSerialized is called with empty string', () => {
const { cookies } = cookies_setup();
assert.throws(() => cookies.setSerialized(''), 'Cannot pass empty string');
});

test('throw error when setSerialized is called without name, value and path', () => {
const { cookies } = cookies_setup();
const cookie_string = 'Max-Age=3600; Expires=Thu, 03 Apr 2025 00:41:07 GMT; Secure; HttpOnly;';
assert.throws(
() => cookies.setSerialized(cookie_string),
'The name, value and path must be provided to create a cookie.'
);
});
11 changes: 11 additions & 0 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,17 @@ declare module '@sveltejs/kit' {
opts: import('cookie').CookieSerializeOptions & { path: string }
) => void;

/**
* Sets a cookie from a string representing the value of the `set-cookie` header. This will add the `set-cookie` header to the response, and also make the cookie available via `cookies.get` or `cookies.getAll` during the current request.
*
* No default values. It will set only properties you specified in a cookie.
*
* If you do not specify name, value and path, it will throw an error.
* @param cookie the serialized cookie
* @since 2.21.0
*/
setSerialized: (cookie: string) => void;

/**
* Deletes a cookie by setting its value to an empty string and setting the expiry date in the past.
*
Expand Down
Loading