UNPKG

elysia

Version:

Ergonomic Framework for Human

221 lines (220 loc) 6.05 kB
import { parse, serialize } from "cookie"; import decode from "fast-decode-uri-component"; import { isNotEmpty, unsignCookie } from "./utils.mjs"; import { InvalidCookieSignature } from "./error.mjs"; const hashString = (str) => { let hash = 2166136261; const len = str.length; for (let i = 0; i < len; i++) hash ^= str.charCodeAt(i), hash = Math.imul(hash, 16777619); return hash >>> 0; }; class Cookie { constructor(name, jar, initial = {}) { this.name = name; this.jar = jar; this.initial = initial; } get cookie() { return this.jar[this.name] ?? this.initial; } set cookie(jar) { this.name in this.jar || (this.jar[this.name] = this.initial), this.jar[this.name] = jar, this.valueHash = void 0; } get setCookie() { return this.name in this.jar || (this.jar[this.name] = this.initial), this.jar[this.name]; } set setCookie(jar) { this.cookie = jar; } get value() { return this.cookie.value; } set value(value) { const current = this.cookie.value; if (current !== value) { if (typeof current == "object" && current !== null && typeof value == "object" && value !== null) try { const valueStr = JSON.stringify(value), newHash = hashString(valueStr); if (this.valueHash !== void 0 && this.valueHash !== newHash) this.valueHash = newHash; else { if (JSON.stringify(current) === valueStr) { this.valueHash = newHash; return; } this.valueHash = newHash; } } catch { } this.name in this.jar || (this.jar[this.name] = { ...this.initial }), this.jar[this.name].value = value; } } get expires() { return this.cookie.expires; } set expires(expires) { this.setCookie.expires = expires; } get maxAge() { return this.cookie.maxAge; } set maxAge(maxAge) { this.setCookie.maxAge = maxAge; } get domain() { return this.cookie.domain; } set domain(domain) { this.setCookie.domain = domain; } get path() { return this.cookie.path; } set path(path) { this.setCookie.path = path; } get secure() { return this.cookie.secure; } set secure(secure) { this.setCookie.secure = secure; } get httpOnly() { return this.cookie.httpOnly; } set httpOnly(httpOnly) { this.setCookie.httpOnly = httpOnly; } get sameSite() { return this.cookie.sameSite; } set sameSite(sameSite) { this.setCookie.sameSite = sameSite; } get priority() { return this.cookie.priority; } set priority(priority) { this.setCookie.priority = priority; } get partitioned() { return this.cookie.partitioned; } set partitioned(partitioned) { this.setCookie.partitioned = partitioned; } get secrets() { return this.cookie.secrets; } set secrets(secrets) { this.setCookie.secrets = secrets; } update(config) { return this.setCookie = Object.assign( this.cookie, typeof config == "function" ? config(this.cookie) : config ), this; } set(config) { return this.setCookie = Object.assign( { ...this.initial, value: this.value }, typeof config == "function" ? config(this.cookie) : config ), this; } remove() { if (this.value !== void 0) return this.set({ expires: /* @__PURE__ */ new Date(0), maxAge: 0, value: "" }), this; } toString() { return typeof this.value == "object" ? JSON.stringify(this.value) : this.value?.toString() ?? ""; } } const createCookieJar = (set, store, initial) => (set.cookie || (set.cookie = {}), new Proxy(store, { get(_, key) { return key in store ? new Cookie( key, set.cookie, Object.assign({}, initial ?? {}, store[key]) ) : new Cookie( key, set.cookie, Object.assign({}, initial) ); } })), parseCookie = async (set, cookieString, { secrets, sign, ...initial } = {}) => { if (!cookieString) return createCookieJar(set, {}, initial); const isStringKey = typeof secrets == "string"; sign && sign !== !0 && !Array.isArray(sign) && (sign = [sign]); const jar = {}, cookies = parse(cookieString); for (const [name, v] of Object.entries(cookies)) { if (v === void 0) continue; let value = decode(v); if (value) { const starts = value.charCodeAt(0), ends = value.charCodeAt(value.length - 1); if (starts === 123 && ends === 125 || starts === 91 && ends === 93) try { value = JSON.parse(value); } catch { } } if (sign === !0 || sign?.includes(name)) { if (!secrets) throw new Error("No secret is provided to cookie plugin"); if (isStringKey) { if (typeof value != "string") throw new InvalidCookieSignature(name); const temp = await unsignCookie(value, secrets); if (temp === !1) throw new InvalidCookieSignature(name); value = temp; } else { let decoded = !1; for (let i = 0; i < secrets.length; i++) { if (typeof value != "string") throw new InvalidCookieSignature(name); const temp = await unsignCookie(value, secrets[i]); if (temp !== !1) { decoded = !0, value = temp; break; } } if (!decoded) throw new InvalidCookieSignature(name); } } jar[name] = { value }; } return createCookieJar(set, jar, initial); }, serializeCookie = (cookies) => { if (!cookies || !isNotEmpty(cookies)) return; const set = []; for (const [key, property] of Object.entries(cookies)) { if (!key || !property) continue; const value = property.value; value != null && set.push( serialize( key, typeof value == "object" ? JSON.stringify(value) : value + "", property ) ); } if (set.length !== 0) return set.length === 1 ? set[0] : set; }; export { Cookie, createCookieJar, parseCookie, serializeCookie };