Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const require_utils_url = require("./url.js"); //#region src/utils/cookie.ts /** * @module * Cookie utility. */ const algorithm = { name: "HMAC", hash: "SHA-256" }; const getCryptoKey = async (secret) => { const secretBuf = typeof secret === "string" ? new TextEncoder().encode(secret) : secret; return await crypto.subtle.importKey("raw", secretBuf, algorithm, false, ["sign", "verify"]); }; const makeSignature = async (value, secret) => { const key = await getCryptoKey(secret); const signature = await crypto.subtle.sign(algorithm.name, key, new TextEncoder().encode(value)); return btoa(String.fromCharCode(...new Uint8Array(signature))); }; const verifySignature = async (base64Signature, value, secret) => { try { const signatureBinStr = atob(base64Signature); const signature = new Uint8Array(signatureBinStr.length); for (let i = 0, len = signatureBinStr.length; i < len; i++) signature[i] = signatureBinStr.charCodeAt(i); return await crypto.subtle.verify(algorithm, secret, signature, new TextEncoder().encode(value)); } catch { return false; } }; const validCookieNameRegEx = /^[\w!#$%&'*.^`|~+-]+$/; const relaxedCookieNameRegEx = /^[!#-:<>-[\]-~]+$/; const validCookieValueRegEx = /^[ !#-:<-[\]-~]*$/; const trimCookieWhitespace = (value) => { let start = 0; let end = value.length; while (start < end) { const charCode = value.charCodeAt(start); if (charCode !== 32 && charCode !== 9) break; start++; } while (end > start) { const charCode = value.charCodeAt(end - 1); if (charCode !== 32 && charCode !== 9) break; end--; } return start === 0 && end === value.length ? value : value.slice(start, end); }; const parse = (cookie, name) => { if (name && cookie.indexOf(name) === -1) return {}; const pairs = cookie.split(";"); const parsedCookie = Object.create(null); for (const pairStr of pairs) { const valueStartPos = pairStr.indexOf("="); if (valueStartPos === -1) continue; const cookieName = trimCookieWhitespace(pairStr.substring(0, valueStartPos)); if (name && name !== cookieName || !relaxedCookieNameRegEx.test(cookieName) || cookieName in parsedCookie) continue; let cookieValue = trimCookieWhitespace(pairStr.substring(valueStartPos + 1)); if (cookieValue.startsWith("\"") && cookieValue.endsWith("\"")) cookieValue = cookieValue.slice(1, -1); if (validCookieValueRegEx.test(cookieValue)) { parsedCookie[cookieName] = require_utils_url.tryDecodeURIComponent(cookieValue); if (name) break; } } return parsedCookie; }; const parseSigned = async (cookie, secret, name) => { const parsedCookie = Object.create(null); const secretKey = await getCryptoKey(secret); for (const [key, value] of Object.entries(parse(cookie, name))) { const signatureStartPos = value.lastIndexOf("."); if (signatureStartPos < 0) continue; const signedValue = value.substring(0, signatureStartPos); const signature = value.substring(signatureStartPos + 1); if (signature.length !== 44 || !signature.endsWith("=")) continue; parsedCookie[key] = await verifySignature(signature, signedValue, secretKey) ? signedValue : false; } return parsedCookie; }; const _serialize = (name, value, opt = {}) => { if (!validCookieNameRegEx.test(name)) throw new Error("Invalid cookie name"); let cookie = `${name}=${value}`; if (name.startsWith("__Secure-") && !opt.secure) throw new Error("__Secure- Cookie must have Secure attributes"); if (name.startsWith("__Host-")) { if (!opt.secure) throw new Error("__Host- Cookie must have Secure attributes"); if (opt.path !== "/") throw new Error("__Host- Cookie must have Path attributes with \"/\""); if (opt.domain) throw new Error("__Host- Cookie must not have Domain attributes"); } for (const key of [ "domain", "path", "sameSite", "priority" ]) if (opt[key] && /[;\r\n]/.test(opt[key])) throw new Error(`${key} must not contain ";", "\\r", or "\\n"`); if (opt && typeof opt.maxAge === "number" && opt.maxAge >= 0) { if (opt.maxAge > 3456e4) throw new Error("Cookies Max-Age SHOULD NOT be greater than 400 days (34560000 seconds) in duration."); cookie += `; Max-Age=${opt.maxAge | 0}`; } if (opt.domain && opt.prefix !== "host") cookie += `; Domain=${opt.domain}`; if (opt.path) cookie += `; Path=${opt.path}`; if (opt.expires) { if (opt.expires.getTime() - Date.now() > 3456e7) throw new Error("Cookies Expires SHOULD NOT be greater than 400 days (34560000 seconds) in the future."); cookie += `; Expires=${opt.expires.toUTCString()}`; } if (opt.httpOnly) cookie += "; HttpOnly"; if (opt.secure) cookie += "; Secure"; if (opt.sameSite) cookie += `; SameSite=${opt.sameSite.charAt(0).toUpperCase() + opt.sameSite.slice(1)}`; if (opt.priority) cookie += `; Priority=${opt.priority.charAt(0).toUpperCase() + opt.priority.slice(1)}`; if (opt.partitioned) { if (!opt.secure) throw new Error("Partitioned Cookie must have Secure attributes"); cookie += "; Partitioned"; } return cookie; }; const serialize = (name, value, opt) => { value = encodeURIComponent(value); return _serialize(name, value, opt); }; const serializeSigned = async (name, value, secret, opt = {}) => { const signature = await makeSignature(value, secret); value = `${value}.${signature}`; value = encodeURIComponent(value); return _serialize(name, value, opt); }; //#endregion exports.parse = parse; exports.parseSigned = parseSigned; exports.serialize = serialize; exports.serializeSigned = serializeSigned;