UNPKG

4.17 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.parseCookieHeader = exports.setCookie = exports.attrsToSetCookie = void 0;
4const attrsToSetCookie = (attrs) => attrs.map(att => att.join('=')).join('; ');
5exports.attrsToSetCookie = attrsToSetCookie;
6/**
7 * RegExp to match field-content in RFC 7230 sec 3.2
8 *
9 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
10 * field-vchar = VCHAR / obs-text
11 * obs-text = %x80-FF
12 */
13const RE_FIELD_CONTENT = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
14/**
15 * Implements <https://wicg.github.io/cookie-store/#set-a-cookie>
16 * with some additional behaviors taken from Chrome's implementation.
17 */
18function setCookie(options, value, origin, encode = (x) => { var _a; return (_a = x === null || x === void 0 ? void 0 : x.toString()) !== null && _a !== void 0 ? _a : ''; }) {
19 const [name, val] = (typeof options === 'string'
20 ? [options, value]
21 : [options.name, options.value]).map(encode);
22 const opts = typeof options === 'string' ? {} : options;
23 if (name == null || val == null)
24 throw TypeError("required value(s) missing");
25 if (!name.length && val.includes('='))
26 throw TypeError("Cookie value cannot contain '=' if the name is empty");
27 if (!name.length && !val.length)
28 throw TypeError("Cookie name and value both cannot be empty");
29 // Unspecified, emulating Chrome's current behavior
30 if (!RE_FIELD_CONTENT.test(name + val) || name.includes('=') || val.includes(';'))
31 return null;
32 if (val.includes(', ')) {
33 throw TypeError("The cookie value must not contain sequence: ', '.");
34 }
35 const attrs = [[name, val]];
36 const { domain, path, sameSite } = opts;
37 if (domain) {
38 // Unspecified, emulating Chrome's current behavior
39 if (!RE_FIELD_CONTENT.test(domain) || domain.includes(';'))
40 return null;
41 if (domain.startsWith('.'))
42 throw TypeError('Cookie domain cannot start with "."');
43 const host = origin === null || origin === void 0 ? void 0 : origin.host;
44 if (host && domain !== host && !domain.endsWith(`.${host}`))
45 throw TypeError('Cookie domain must match current host');
46 attrs.push(['Domain', domain]);
47 }
48 let expires = null;
49 if (opts.expires) {
50 expires = opts.expires instanceof Date
51 ? opts.expires
52 : new Date(opts.expires);
53 attrs.push(['Expires', expires.toUTCString()]);
54 }
55 if (path) {
56 if (!path.toString().startsWith('/'))
57 throw TypeError('Cookie path must start with "/"');
58 // Unspecified, emulating Chrome's current behavior
59 if (!RE_FIELD_CONTENT.test(path) || path.includes(';'))
60 return null;
61 attrs.push(['Path', path]);
62 }
63 // Always secure, except for localhost
64 if (origin && origin.hostname !== 'localhost')
65 attrs.push(['Secure']);
66 if (opts.httpOnly)
67 attrs.push(['HttpOnly']);
68 switch (sameSite) {
69 case undefined: break;
70 case 'none':
71 attrs.push(['SameSite', 'None']);
72 break;
73 case 'lax':
74 attrs.push(['SameSite', 'Lax']);
75 break;
76 case 'strict':
77 attrs.push(['SameSite', 'Strict']);
78 break;
79 default: throw TypeError(`The provided value '${sameSite}' is not a valid enum value of type CookieSameSite.`);
80 }
81 return [attrs, expires];
82}
83exports.setCookie = setCookie;
84/**
85 * A not-so-strict parser for cookie headers.
86 * - Allows pretty much everything in the value, including `=`
87 * - Trims keys and values
88 * - Ignores when both name and value are empty (but either empty allowed)
89 *
90 * For more on the state of allowed cookie characters,
91 * see <https://stackoverflow.com/a/1969339/870615>.
92 */
93function parseCookieHeader(cookie) {
94 return new Map(cookie === null || cookie === void 0 ? void 0 : cookie.split(/;\s+/).map(x => x.split('=')).map(([n, ...vs]) => [n.trim(), vs.join('=').trim()]).filter(([n, v]) => !(n === '' && v === '')));
95}
96exports.parseCookieHeader = parseCookieHeader;
97//# sourceMappingURL=set-cookie.js.map
\No newline at end of file