UNPKG

@worker-tools/request-cookie-store

Version:

An implementation of the Cookie Store API for request handlers.

97 lines 4.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseCookieHeader = exports.setCookie = exports.attrsToSetCookie = void 0; const attrsToSetCookie = (attrs) => attrs.map(att => att.join('=')).join('; '); exports.attrsToSetCookie = attrsToSetCookie; /** * RegExp to match field-content in RFC 7230 sec 3.2 * * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] * field-vchar = VCHAR / obs-text * obs-text = %x80-FF */ const RE_FIELD_CONTENT = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; /** * Implements <https://wicg.github.io/cookie-store/#set-a-cookie> * with some additional behaviors taken from Chrome's implementation. */ function setCookie(options, value, origin, encode = (x) => { var _a; return (_a = x === null || x === void 0 ? void 0 : x.toString()) !== null && _a !== void 0 ? _a : ''; }) { const [name, val] = (typeof options === 'string' ? [options, value] : [options.name, options.value]).map(encode); const opts = typeof options === 'string' ? {} : options; if (name == null || val == null) throw TypeError("required value(s) missing"); if (!name.length && val.includes('=')) throw TypeError("Cookie value cannot contain '=' if the name is empty"); if (!name.length && !val.length) throw TypeError("Cookie name and value both cannot be empty"); // Unspecified, emulating Chrome's current behavior if (!RE_FIELD_CONTENT.test(name + val) || name.includes('=') || val.includes(';')) return null; if (val.includes(', ')) { throw TypeError("The cookie value must not contain sequence: ', '."); } const attrs = [[name, val]]; const { domain, path, sameSite } = opts; if (domain) { // Unspecified, emulating Chrome's current behavior if (!RE_FIELD_CONTENT.test(domain) || domain.includes(';')) return null; if (domain.startsWith('.')) throw TypeError('Cookie domain cannot start with "."'); const host = origin === null || origin === void 0 ? void 0 : origin.host; if (host && domain !== host && !domain.endsWith(`.${host}`)) throw TypeError('Cookie domain must match current host'); attrs.push(['Domain', domain]); } let expires = null; if (opts.expires) { expires = opts.expires instanceof Date ? opts.expires : new Date(opts.expires); attrs.push(['Expires', expires.toUTCString()]); } if (path) { if (!path.toString().startsWith('/')) throw TypeError('Cookie path must start with "/"'); // Unspecified, emulating Chrome's current behavior if (!RE_FIELD_CONTENT.test(path) || path.includes(';')) return null; attrs.push(['Path', path]); } // Always secure, except for localhost if (origin && origin.hostname !== 'localhost') attrs.push(['Secure']); if (opts.httpOnly) attrs.push(['HttpOnly']); switch (sameSite) { case undefined: break; case 'none': attrs.push(['SameSite', 'None']); break; case 'lax': attrs.push(['SameSite', 'Lax']); break; case 'strict': attrs.push(['SameSite', 'Strict']); break; default: throw TypeError(`The provided value '${sameSite}' is not a valid enum value of type CookieSameSite.`); } return [attrs, expires]; } exports.setCookie = setCookie; /** * A not-so-strict parser for cookie headers. * - Allows pretty much everything in the value, including `=` * - Trims keys and values * - Ignores when both name and value are empty (but either empty allowed) * * For more on the state of allowed cookie characters, * see <https://stackoverflow.com/a/1969339/870615>. */ function parseCookieHeader(cookie) { 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 === ''))); } exports.parseCookieHeader = parseCookieHeader; //# sourceMappingURL=set-cookie.js.map