UNPKG

fexios

Version:

Fetch based HTTP client with similar API to axios for browser and Node.js

279 lines (275 loc) 7.96 kB
'use strict'; /** * Cookie Jar * * @author dragon-fish <dragon-fish@qq.com> * @license MIT */ class CookieJar { cookies = /* @__PURE__ */ new Map(); /** * Set a cookie */ setCookie(cookie, domain, path) { const effectiveDomain = cookie.domain ?? domain; const effectivePath = cookie.path ?? path ?? "/"; const hostOnly = cookie.domain === void 0 && domain !== void 0; const key = this.getCookieKey(cookie.name, effectiveDomain, effectivePath); const cookieWithTime = { ...cookie, domain: effectiveDomain, path: effectivePath, _hostOnly: cookie._hostOnly ?? hostOnly, _createdAt: cookie.maxAge !== void 0 ? /* @__PURE__ */ new Date() : cookie._createdAt }; this.cookies.set(key, cookieWithTime); } /** * Get a cookie */ getCookie(name, domain, path) { const exactKey = this.getCookieKey(name, domain, path); let cookie = this.cookies.get(exactKey); if (cookie && !this.isCookieExpired(cookie)) { return cookie; } for (const storedCookie of this.cookies.values()) { if (storedCookie.name === name && this.isCookieMatch(storedCookie, domain, path) && !this.isCookieExpired(storedCookie)) { return storedCookie; } } return void 0; } /** * Get all matching cookies */ getCookies(domain, path) { const result = []; for (const cookie of this.cookies.values()) { if (this.isCookieMatch(cookie, domain, path) && !this.isCookieExpired(cookie)) { result.push(cookie); } } return result; } /** * Delete a cookie */ deleteCookie(name, domain, path) { const key = this.getCookieKey(name, domain, path); return this.cookies.delete(key); } /** * Clear all cookies */ clear() { this.cookies.clear(); } /** * Clean expired cookies */ cleanExpiredCookies() { let cleanedCount = 0; for (const [key, cookie] of this.cookies.entries()) { if (this.isCookieExpired(cookie)) { this.cookies.delete(key); cleanedCount++; } } return cleanedCount; } /** * Get all cookies (including expired ones) */ getAllCookies(domain, path) { const result = []; for (const cookie of this.cookies.values()) { if (this.isCookieMatch(cookie, domain, path)) { result.push(cookie); } } return result; } /** * Parse cookies from Set-Cookie header */ parseSetCookieHeader(setCookieHeader, domain, path) { const cookieStrings = this.splitSetCookieHeader(setCookieHeader); for (const cookieStr of cookieStrings) { const cookie = this.parseCookieString(cookieStr); if (cookie) { this.setCookie(cookie, domain, path); } } } /** * Generate Cookie header string */ getCookieHeader(domain, path) { const cookies = this.getCookies(domain, path); return cookies.filter((cookie) => !this.isCookieExpired(cookie)).map((cookie) => `${cookie.name}=${cookie.value}`).join("; "); } /** * Get the unique key for a cookie */ getCookieKey(name, domain, path) { return `${name}:${domain || "*"}:${path || "/"}`; } /** * Check if cookie matches the given domain and path */ isCookieMatch(cookie, domain, path) { if (domain) { if (cookie.domain) { if (cookie._hostOnly) { if (cookie.domain !== domain) return false; } else { if (!this.isDomainMatch(cookie.domain, domain)) return false; } } else { return false; } } if (path && cookie.path && !this.isPathMatch(cookie.path, path)) { return false; } return true; } /** * Check if domain matches */ isDomainMatch(cookieDomain, requestDomain) { const cd = cookieDomain.startsWith(".") ? cookieDomain.substring(1) : cookieDomain; if (requestDomain === cd) { return true; } return requestDomain.endsWith("." + cd); } /** * Check if path matches */ isPathMatch(cookiePath, requestPath) { if (cookiePath === requestPath) { return true; } if (requestPath.startsWith(cookiePath)) { return cookiePath.endsWith("/") || requestPath[cookiePath.length] === "/"; } return false; } /** * Check if cookie is expired */ isCookieExpired(cookie) { const now = /* @__PURE__ */ new Date(); if (cookie.expires) { return now > cookie.expires; } if (cookie.maxAge !== void 0 && cookie._createdAt) { const expirationTime = new Date( cookie._createdAt.getTime() + cookie.maxAge * 1e3 ); return now > expirationTime; } return false; } /** * Parse cookie string */ parseCookieString(cookieStr) { const parts = cookieStr.split(";").map((part) => part.trim()); if (parts.length === 0) return null; const [nameValue] = parts; const equalIndex = nameValue.indexOf("="); if (equalIndex === -1) return null; const name = nameValue.substring(0, equalIndex).trim(); const value = nameValue.substring(equalIndex + 1).trim(); const cookie = { name, value }; for (let i = 1; i < parts.length; i++) { const part = parts[i]; const equalIndex2 = part.indexOf("="); if (equalIndex2 === -1) { const attr = part.toLowerCase(); if (attr === "secure") { cookie.secure = true; } else if (attr === "httponly") { cookie.httpOnly = true; } } else { const attrName = part.substring(0, equalIndex2).trim().toLowerCase(); const attrValue = part.substring(equalIndex2 + 1).trim(); switch (attrName) { case "domain": cookie.domain = attrValue; break; case "path": cookie.path = attrValue; break; case "expires": cookie.expires = new Date(attrValue); break; case "max-age": cookie.maxAge = parseInt(attrValue, 10); break; case "samesite": cookie.sameSite = attrValue; break; } } } return cookie; } /** * Safely split a possibly-combined Set-Cookie header string into individual cookie strings. * Split on commas that are followed by the start of a new cookie-pair (name=), * avoiding commas inside Expires attribute values. */ splitSetCookieHeader(header) { if (!header) return []; const parts = header.split(/,(?=\s*[^;=]+=)/); return parts.map((p) => p.trim()).filter((p) => p.length > 0); } } const pluginCookieJar = { name: "fexios-plugin-cookie-jar", install(app) { const cookieJar = new CookieJar(); app.interceptors.request.use((ctx) => { const url = new URL(ctx.url); const cookieHeader = cookieJar.getCookieHeader(url.hostname, url.pathname); if (cookieHeader) { ctx.headers = { ...ctx.headers, Cookie: cookieHeader }; } return ctx; }); app.interceptors.response.use((ctx) => { const url = new URL(ctx.url); const headersAny = ctx.rawResponse?.headers; const host = url.hostname; const reqPath = url.pathname; const getSetCookie = typeof headersAny?.getSetCookie === "function" ? headersAny.getSetCookie.bind(headersAny) : void 0; if (getSetCookie) { const list = getSetCookie(); if (Array.isArray(list) && list.length > 0) { for (const sc of list) { cookieJar.parseSetCookieHeader(sc, host, reqPath); } } } else { const setCookieHeader = ctx.rawResponse?.headers?.get("set-cookie"); if (setCookieHeader) { cookieJar.parseSetCookieHeader(setCookieHeader, host, reqPath); } } return ctx; }); app.cookieJar = cookieJar; return app; } }; exports.CookieJar = CookieJar; exports.pluginCookieJar = pluginCookieJar; //# sourceMappingURL=index.cjs.map