'use strict'; const { parseSetCookie } = require('./parse'); const { stringify } = require('./util'); const { webidl } = require('../fetch/webidl'); const { Headers } = require('../fetch/headers'); const brandChecks = webidl.brandCheckMultiple( [Headers, globalThis.Headers].filter(Boolean) ); /** * @typedef {Object} Cookie * @property {string} name * @property {string} value * @property {Date|number} [expires] * @property {number} [maxAge] * @property {string} [domain] * @property {string} [path] * @property {boolean} [secure] * @property {boolean} [httpOnly] * @property {'Strict'|'Lax'|'None'} [sameSite] * @property {string[]} [unparsed] */ /** * @param {Headers} headers * @returns {Record} */ function getCookies(headers) { webidl.argumentLengthCheck(arguments, 1, 'getCookies'); brandChecks(headers); const cookie = headers.get('cookie'); /** @type {Record} */ const out = {}; if (!cookie) { return out; } for (const piece of cookie.split(';')) { const [name, ...value] = piece.split('='); out[name.trim()] = value.join('='); } return out; } /** * @param {Headers} headers * @param {string} name * @param {{ path?: string, domain?: string }|undefined} attributes * @returns {void} */ function deleteCookie(headers, name, attributes) { brandChecks(headers); const prefix = 'deleteCookie'; webidl.argumentLengthCheck(arguments, 2, prefix); name = webidl.converters.DOMString(name, prefix, 'name'); attributes = webidl.converters.DeleteCookieAttributes(attributes); // Matches behavior of // https://github.com/denoland/deno_std/blob/63827b16330b82489a04614027c33b7904e08be5/http/cookie.ts#L278 setCookie(headers, { name, value: '', expires: new Date(0), ...attributes, }); } /** * @param {Headers} headers * @returns {Cookie[]} */ function getSetCookies(headers) { webidl.argumentLengthCheck(arguments, 1, 'getSetCookies'); brandChecks(headers); const cookies = headers.getSetCookie(); if (!cookies) { return []; } return cookies.map((pair) => parseSetCookie(pair)); } /** * Parses a cookie string * @param {string} cookie */ function parseCookie(cookie) { cookie = webidl.converters.DOMString(cookie); return parseSetCookie(cookie); } /** * @param {Headers} headers * @param {Cookie} cookie * @returns {void} */ function setCookie(headers, cookie) { webidl.argumentLengthCheck(arguments, 2, 'setCookie'); brandChecks(headers); cookie = webidl.converters.Cookie(cookie); const str = stringify(cookie); if (str) { headers.append('set-cookie', str, true); } } webidl.converters.DeleteCookieAttributes = webidl.dictionaryConverter([ { converter: webidl.nullableConverter(webidl.converters.DOMString), key: 'path', defaultValue: () => null, }, { converter: webidl.nullableConverter(webidl.converters.DOMString), key: 'domain', defaultValue: () => null, }, ]); webidl.converters.Cookie = webidl.dictionaryConverter([ { converter: webidl.converters.DOMString, key: 'name', }, { converter: webidl.converters.DOMString, key: 'value', }, { converter: webidl.nullableConverter((value) => { if (typeof value === 'number') { return webidl.converters['unsigned long long'](value); } return new Date(value); }), key: 'expires', defaultValue: () => null, }, { converter: webidl.nullableConverter(webidl.converters['long long']), key: 'maxAge', defaultValue: () => null, }, { converter: webidl.nullableConverter(webidl.converters.DOMString), key: 'domain', defaultValue: () => null, }, { converter: webidl.nullableConverter(webidl.converters.DOMString), key: 'path', defaultValue: () => null, }, { converter: webidl.nullableConverter(webidl.converters.boolean), key: 'secure', defaultValue: () => null, }, { converter: webidl.nullableConverter(webidl.converters.boolean), key: 'httpOnly', defaultValue: () => null, }, { converter: webidl.converters.USVString, key: 'sameSite', allowedValues: ['Strict', 'Lax', 'None'], }, { converter: webidl.sequenceConverter(webidl.converters.DOMString), key: 'unparsed', defaultValue: () => new Array(0), }, ]); module.exports = { getCookies, deleteCookie, getSetCookies, setCookie, parseCookie, };