UNPKG

sfn-cookie

Version:

Simple Friendly Cookie parser and serializer for Node.js and browsers.

171 lines (140 loc) 4.58 kB
import assign = require("object-assign"); export interface BaseCookieOptions { /** How many seconds that this cookie should last. */ maxAge?: number; /** Keep alive to a specified date or time. */ expires?: string | number | Date; /** Honor same-site principle. */ sameSite?: "Strict" | "Lax"; /** Set cookie for a specified domain name. */ domain?: string; /** Set cookie for a specified pathname. */ path?: string; /** Only HTTP(s), not JavaScript, can access this cookie. */ httpOnly?: boolean; /** The cookie won't be sent if not using HTTPS protocol. */ secure?: boolean; } export interface CookieOptions extends BaseCookieOptions { name?: string; value?: string; } export class Cookie implements CookieOptions { name: string; value: string; maxAge: number; expires: string | number | Date; sameSite: "Strict" | "Lax"; domain: string; path: string; httpOnly: boolean; secure: boolean; /** * @example new Cookie("username=Luna") * @example new Cookie("username=Luna; Max-Age=120; HttpOnly") * @example new Cookie("username", "Luna") * @example new Cookie("username", "Luna", { maxAge: 120, httpOnly: true }) * @example new Cookie({ name: "username", value: "Luna", maxAge: 120, httpOnly: true }) */ constructor(options: CookieOptions); constructor(cookieStr: string); constructor(name: string, value: string, options?: BaseCookieOptions); constructor(input, value = null, options = null) { if (typeof input === "string") { if (value === null) { assign(this, parse(input)); } else { assign(this, { name: input, value }, options); } } else { assign(this, input); } } /** Gets the serialized cookie string of the current instance. */ toString(): string { return serialize(this); } } /** Serializes an object or Cookie instance to a valid cookie string. */ export function serialize(data: Cookie | CookieOptions): string { let { name, value, maxAge, expires, sameSite, domain, path, httpOnly, secure } = data; if (!name) return ""; let str = encodeURIComponent(name) + "=" + encodeURIComponent(value); if (maxAge) str += "; Max-Age=" + maxAge; if (expires) { let err = "Expires must be a valid date string or timestamp, or a Date instance."; if (expires instanceof Date) { expires = expires.toUTCString(); } else if (typeof expires == "number" || typeof expires == "string") { try { expires = new Date(<any>expires).toUTCString(); } catch (e) { throw new TypeError(err); } } else { throw new TypeError(err); } str += "; Expires=" + expires; } if (sameSite) str += "; SameSite=" + sameSite; if (domain) str += "; Domain=" + domain; if (path) str += "; Path=" + path; if (httpOnly) str += "; HttpOnly"; if (secure) str += "; Secure"; return str; } /** Parses a cookie string to a Cookie instance. */ export function parse(cookieStr: string): Cookie { if (!cookieStr || typeof cookieStr !== "string") return null; let pairs = cookieStr.split(/\s*;\s*/), data: CookieOptions = { name: undefined, value: undefined }; for (let i in pairs) { let pair = pairs[i].split("="); if (i == "0") { data.name = decodeURIComponent(pair[0]); data.value = decodeURIComponent(pair[1]); } else { let key = pair[0] == "Max-Age" ? "maxAge" : pair[0][0].toLowerCase() + pair[0].slice(1); if (!isNaN(<any>pair[1])) { data[key] = parseInt(pair[1]); } else if (pair[1] === undefined) { data[key] = true; } else { data[key] = pair[1]; } } } return new Cookie(data); } /** * Parses a string as multiple cookies, useful for parsing * `document.cookie` and `req.headers.cookie`. */ export function parseMany(str: string): Cookie[] { if (!str || typeof str !== "string") return []; let pairs = str.split(/\s*;\s*/), cookies = []; for (let pair of pairs) { cookies.push(new Cookie(pair)); } return cookies; } export default Cookie;