UNPKG

mauss

Version:

lightweight, modular, type-safe utilities

139 lines (138 loc) 5.25 kB
export const cookies = { parse(source = '') { if (!source && typeof window !== 'undefined') source = document.cookie; const jar = Object.create(null); for (const cookie of source ? source.split(';') : []) { const trimmed = cookie.trim(); const delimiter = trimmed.indexOf('='); if (!trimmed || delimiter === trimmed.length - 1) continue; const name = trimmed.slice(0, delimiter).trim(); let value = trimmed.slice(delimiter + 1).trim(); const quotes = value[0] === value[value.length - 1]; if (value[0] === '"' && quotes) value = value.slice(1, -1); try { jar[name] = decodeURIComponent(value); } catch (error) { jar[name] = value; // assign value as-is } } return { has: (key) => key in jar, get: (key) => jar[key], keys: () => Object.keys(jar), values: () => Object.values(jar), entries: () => Object.entries(jar), }; }, /** * Locates a cookie and immediately returns the raw value * @param source cookie source to parse * @param name cookie name to search for * @param trim remove quotes in cookie value, if there is any * @returns the value of cookie name and empty string if it doesn't exist */ raw(source, name, trim = false) { if (!source || !name) return void 0; for (let i = 0, c = 0; i < source.length; i++, c = 0) { if (source[i] !== name[0]) continue; let matched = true; while (matched && source[i] !== '=') { matched = source[i++] === name[c++]; if (c === name.length) { while (source[i] === ' ') i++; matched = source[i] === '='; } } if (!matched) continue; let end = i + 1; while (source[end] !== ';' && end < source.length) end++; const quoted = source[i + 1] === '"' && source[end] === '"'; i += trim && quoted ? 2 : 1; if (trim && quoted) end -= 1; return source.slice(i, end); } return void 0; }, /** * @param options cookie settings * @returns a set-cookie function */ create({ path = '/', domain, maxAge, expires = 6, sameSite = 'Lax', secure = false, httpOnly = true, } = {}) { let crumbs = `; Path=${path}`; crumbs += `; SameSite=${sameSite}`; if (domain) crumbs += `; Domain=${domain}`; if (httpOnly) crumbs += `; HttpOnly`; if (secure || sameSite === 'None') crumbs += `; Secure`; const HOUR = 36e5; // ONE HOUR (60 * 60 * 1e3) /** * @param name name for cookie * @param value value to be saved as cookie name * @returns the complete 'Set-Cookie' value */ return function set(name, value) { if (/[\s\t()<>@,;:\\"/\[\]?={}\u0080-\u00ff]/.test(name)) { name = name.replace(/[\s\t]/g, '-').replace(/-+/g, '-'); name = name.replace(/[()<>@,;:\\"/\[\]?={}\u0080-\u00ff]/g, ''); console.warn(`Illegal cookie name, creating "${name}" instead`); } let biscuit = `${name}=${encodeURIComponent(value)}`; const date = new Date(); if (maxAge /** Max-Age has precedence over Expires */) { date.setTime(date.getTime() + maxAge * 24 * HOUR); biscuit = `${biscuit}; Max-Age=${date}`; } else if (expires) { date.setTime(date.getTime() + expires * 24 * HOUR); biscuit = `${biscuit}; Expires=${date.toUTCString()}`; } /** Defaults to expiring in 6 hours */ else { date.setTime(date.getTime() + 6 * HOUR); biscuit = `${biscuit}; Expires=${date.toUTCString()}`; } return `${biscuit}${crumbs}`; }; }, /** * A convenience function for the default `create()` * * @param name name for cookie * @param value value to be saved as cookie name * @returns the complete 'Set-Cookie' value */ get set() { return this.create(); }, /** * @param values object of string pair as name and value for cookies * @param options cookie settings * @returns array of the complete 'Set-Cookie' values */ bulk(values, options = {}) { const printer = this.create(options); return Object.entries(values).map(([name, value]) => printer(name, value)); }, /** * Automatically remove cookie in browser * @param name cookie to expire * @returns expiring 'Set-Cookie' value */ remove(name) { const expire = `${name}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT`; if (typeof window !== 'undefined') document.cookie = expire; return expire; }, };