UNPKG

@omneedia/client-js

Version:

Isomorphic Javascript client for Omneedia

186 lines (157 loc) 4.21 kB
type Cookie = { name: string value: string maxAge: number domain?: string path?: string sameSite?: string } /** * Serialize data into a cookie header. */ function serialize( name: string, val: string, options: { maxAge: number domain: string path: string expires: Date httpOnly: boolean secure: boolean sameSite: string } ) { const opt = options || {} const enc = encodeURIComponent const fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/ if (typeof enc !== 'function') { throw new TypeError('option encode is invalid') } if (!fieldContentRegExp.test(name)) { throw new TypeError('argument name is invalid') } const value = enc(val) if (value && !fieldContentRegExp.test(value)) { throw new TypeError('argument val is invalid') } let str = name + '=' + value if (null != opt.maxAge) { const maxAge = opt.maxAge - 0 if (isNaN(maxAge) || !isFinite(maxAge)) { throw new TypeError('option maxAge is invalid') } str += '; Max-Age=' + Math.floor(maxAge) } if (opt.domain) { if (!fieldContentRegExp.test(opt.domain)) { throw new TypeError('option domain is invalid') } str += '; Domain=' + opt.domain } if (opt.path) { if (!fieldContentRegExp.test(opt.path)) { throw new TypeError('option path is invalid') } str += '; Path=' + opt.path } if (opt.expires) { if (typeof opt.expires.toUTCString !== 'function') { throw new TypeError('option expires is invalid') } str += '; Expires=' + opt.expires.toUTCString() } if (opt.httpOnly) { str += '; HttpOnly' } if (opt.secure) { str += '; Secure' } if (opt.sameSite) { const sameSite = typeof opt.sameSite === 'string' ? opt.sameSite.toLowerCase() : opt.sameSite switch (sameSite) { case 'lax': str += '; SameSite=Lax' break case 'strict': str += '; SameSite=Strict' break case 'none': str += '; SameSite=None' break default: throw new TypeError('option sameSite is invalid') } } return str } /** * Based on the environment and the request we know if a secure cookie can be set. */ function isSecureEnvironment(req: any) { if (!req || !req.headers || !req.headers.host) { throw new Error('The "host" request header is not available') } const host = (req.headers.host.indexOf(':') > -1 && req.headers.host.split(':')[0]) || req.headers.host if (['localhost', '127.0.0.1'].indexOf(host) > -1 || host.endsWith('.local')) { return false } return true } export function getCookie(name: string): string | null { const nameLenPlus = name.length + 1 return ( document.cookie .split(';') .map((c) => c.trim()) .filter((cookie) => { return cookie.substring(0, nameLenPlus) === `${name}=` }) .map((cookie) => { return decodeURIComponent(cookie.substring(nameLenPlus)) })[0] || null ) } /** * Serialize a cookie to a string. */ function serializeCookie(cookie: Cookie, secure: boolean) { return serialize(cookie.name, cookie.value, { maxAge: cookie.maxAge, expires: new Date(Date.now() + cookie.maxAge * 1000), httpOnly: true, secure, path: cookie.path ?? '/', domain: cookie.domain ?? '', sameSite: cookie.sameSite ?? 'lax', }) } /** * Set one or more cookies. */ export function setCookies(req: any, res: any, cookies: Array<Cookie>) { const strCookies = cookies.map((c) => serializeCookie(c, isSecureEnvironment(req))) const previousCookies = res.getHeader('Set-Cookie') if (previousCookies) { if (previousCookies instanceof Array) { Array.prototype.push.apply(strCookies, previousCookies) } else if (typeof previousCookies === 'string') { strCookies.push(previousCookies) } } res.setHeader('Set-Cookie', strCookies) } /** * Set one or more cookies. */ export function setCookie(req: any, res: any, cookie: Cookie) { setCookies(req, res, [cookie]) } export function deleteCookie(req: any, res: any, name: string) { setCookie(req, res, { name, value: '', maxAge: -1, }) }