import { ONE_SECOND } from '@openobserve/js-core/time' import { findAllCommaSeparatedValues, findCommaSeparatedValue, findCommaSeparatedValues, generateUUID, } from '../tools/utils/stringUtils' import { buildUrl } from '../tools/utils/urlPolyfill' import { globalObject } from '../tools/globalObject' export interface CookieOptions { secure?: boolean crossSite?: boolean partitioned?: boolean domain?: string } export function setCookie(name: string, value: string, expireDelay: number = 0, options?: CookieOptions) { const date = new Date() date.setTime(date.getTime() + expireDelay) const expires = `expires=${date.toUTCString()}` const sameSite = options?.crossSite ? 'none' : 'strict' const domain = options?.domain ? `;domain=${options.domain}` : '' const secure = options?.secure ? ';secure' : '' const partitioned = options?.partitioned ? ';partitioned' : '' document.cookie = `${name}=${value};${expires};path=/;samesite=${sameSite}${domain}${secure}${partitioned}` } /** * Returns the value of the cookie with the given name * If there are multiple cookies with the same name, returns the first one */ export function getCookie(name: string) { const value = findCommaSeparatedValue(document.cookie, name) if (value === '') { return } return value } /** * Returns all the values of the cookies with the given name */ export function getCookies(name: string): string[] { return findAllCommaSeparatedValues(document.cookie).get(name) || [] } let initCookieParsed: Map | undefined /** * Returns a cached value of the cookie. Use this during SDK initialization (and whenever possible) * to avoid accessing document.cookie multiple times. * * ⚠️ If there are multiple cookies with the same name, returns the LAST one (unlike `getCookie()`) */ export function getInitCookie(name: string) { if (!initCookieParsed) { initCookieParsed = findCommaSeparatedValues(document.cookie) } return initCookieParsed.get(name) } export function resetInitCookies() { initCookieParsed = undefined } export function deleteCookie(name: string, options?: CookieOptions) { setCookie(name, '', 0, options) } /** * No API to retrieve it, number of levels for subdomain and suffix are unknown * strategy: find the minimal domain on which cookies are allowed to be set * https://web.dev/same-site-same-origin/#site */ let getCurrentSiteCache: string | undefined export function getCurrentSite( hostname = globalObject.location?.hostname ?? '', referrer = typeof document !== 'undefined' ? document.referrer : '' ): string | undefined { // Cookie probing requires document.cookie, which is not available in Web Workers or Node.js if (typeof document === 'undefined') { return undefined } if (getCurrentSiteCache === undefined) { const defaultHostName = getCookieDefaultHostName(hostname, referrer) if (defaultHostName) { // Use a unique cookie name to avoid issues when the SDK is initialized multiple times during // the test cookie lifetime const testCookieName = `oo_site_test_${generateUUID()}` const testCookieValue = 'test' const domainLevels = defaultHostName.split('.') let candidateDomain = domainLevels.pop()! while (domainLevels.length && !getCookie(testCookieName)) { candidateDomain = `${domainLevels.pop()!}.${candidateDomain}` setCookie(testCookieName, testCookieValue, ONE_SECOND, { domain: candidateDomain }) } deleteCookie(testCookieName, { domain: candidateDomain }) getCurrentSiteCache = candidateDomain } } return getCurrentSiteCache } function getCookieDefaultHostName(hostname: string, referrer: string) { try { return hostname || buildUrl(referrer).hostname } catch { // Ignore } } export function resetGetCurrentSite() { getCurrentSiteCache = undefined }