UNPKG

elysia

Version:

Ergonomic Framework for Human

331 lines (327 loc) 8.83 kB
// src/cookies.ts import { parse, serialize } from "cookie"; import decode from "fast-decode-uri-component"; // src/utils.ts var hasHeaderShorthand = "toJSON" in new Headers(); var primitiveHooks = [ "start", "request", "parse", "transform", "resolve", "beforeHandle", "afterHandle", "mapResponse", "afterResponse", "trace", "error", "stop", "body", "headers", "params", "query", "response", "type", "detail" ], primitiveHookMap = primitiveHooks.reduce( (acc, x) => (acc[x] = !0, acc), {} ); var isBun2 = typeof Bun < "u", hasBunHash = isBun2 && typeof Bun.hash == "function"; var StatusMap = { Continue: 100, "Switching Protocols": 101, Processing: 102, "Early Hints": 103, OK: 200, Created: 201, Accepted: 202, "Non-Authoritative Information": 203, "No Content": 204, "Reset Content": 205, "Partial Content": 206, "Multi-Status": 207, "Already Reported": 208, "Multiple Choices": 300, "Moved Permanently": 301, Found: 302, "See Other": 303, "Not Modified": 304, "Temporary Redirect": 307, "Permanent Redirect": 308, "Bad Request": 400, Unauthorized: 401, "Payment Required": 402, Forbidden: 403, "Not Found": 404, "Method Not Allowed": 405, "Not Acceptable": 406, "Proxy Authentication Required": 407, "Request Timeout": 408, Conflict: 409, Gone: 410, "Length Required": 411, "Precondition Failed": 412, "Payload Too Large": 413, "URI Too Long": 414, "Unsupported Media Type": 415, "Range Not Satisfiable": 416, "Expectation Failed": 417, "I'm a teapot": 418, "Misdirected Request": 421, "Unprocessable Content": 422, Locked: 423, "Failed Dependency": 424, "Too Early": 425, "Upgrade Required": 426, "Precondition Required": 428, "Too Many Requests": 429, "Request Header Fields Too Large": 431, "Unavailable For Legal Reasons": 451, "Internal Server Error": 500, "Not Implemented": 501, "Bad Gateway": 502, "Service Unavailable": 503, "Gateway Timeout": 504, "HTTP Version Not Supported": 505, "Variant Also Negotiates": 506, "Insufficient Storage": 507, "Loop Detected": 508, "Not Extended": 510, "Network Authentication Required": 511 }, InvertedStatusMap = Object.fromEntries( Object.entries(StatusMap).map(([k, v]) => [v, k]) ); function removeTrailingEquals(digest) { let trimmedDigest = digest; for (; trimmedDigest.endsWith("="); ) trimmedDigest = trimmedDigest.slice(0, -1); return trimmedDigest; } var encoder = new TextEncoder(), signCookie = async (val, secret) => { if (typeof val != "string") throw new TypeError("Cookie value must be provided as a string."); if (secret === null) throw new TypeError("Secret key must be provided."); let secretKey = await crypto.subtle.importKey( "raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, !1, ["sign"] ), hmacBuffer = await crypto.subtle.sign( "HMAC", secretKey, encoder.encode(val) ); return val + "." + removeTrailingEquals(Buffer.from(hmacBuffer).toString("base64")); }, unsignCookie = async (input, secret) => { if (typeof input != "string") throw new TypeError("Signed cookie string must be provided."); if (secret === null) throw new TypeError("Secret key must be provided."); let tentativeValue = input.slice(0, input.lastIndexOf(".")); return await signCookie(tentativeValue, secret) === input ? tentativeValue : !1; }; var ELYSIA_FORM_DATA = Symbol("ElysiaFormData"), ELYSIA_REQUEST_ID = Symbol("ElysiaRequestId"); var isNotEmpty = (obj) => { if (!obj) return !1; for (let _ in obj) return !0; return !1; }; var supportPerMethodInlineHandler = (() => { if (typeof Bun > "u") return !0; let semver = Bun.version.split("."); return !(+semver[0] < 1 || +semver[1] < 2 || +semver[2] < 14); })(); // src/error.ts import { Value } from "@sinclair/typebox/value"; var env = typeof Bun < "u" ? Bun.env : typeof process < "u" ? process?.env : void 0, ERROR_CODE = Symbol("ElysiaErrorCode"), isProduction = (env?.NODE_ENV ?? env?.ENV) === "production"; var InvalidCookieSignature = class extends Error { constructor(key, message) { super(message ?? `"${key}" has invalid cookie signature`); this.key = key; this.code = "INVALID_COOKIE_SIGNATURE"; this.status = 400; } }; // src/cookies.ts var Cookie = class { 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; } 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) { this.setCookie.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() ?? ""; } }, 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); let isStringKey = typeof secrets == "string"; sign && sign !== !0 && !Array.isArray(sign) && (sign = [sign]); let jar = {}, cookies = parse(cookieString); for (let [name, v] of Object.entries(cookies)) { if (v === void 0) continue; let value = decode(v); if (sign === !0 || sign?.includes(name)) { if (!secrets) throw new Error("No secret is provided to cookie plugin"); if (isStringKey) { let temp = await unsignCookie(value, secrets); if (temp === !1) throw new InvalidCookieSignature(name); value = temp; } else { let decoded = !0; for (let i = 0; i < secrets.length; i++) { let 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; let set = []; for (let [key, property] of Object.entries(cookies)) { if (!key || !property) continue; let 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 };