import { dateNow } from '@openobserve/js-core/time' import type { TimeStamp } from '@openobserve/js-core/time' import { isEmptyObject } from '../../tools/utils/objectUtils' import { objectEntries } from '../../tools/utils/polyfills' import { generateUUID } from '../../tools/utils/stringUtils' import type { Configuration } from '../configuration' import { SESSION_EXPIRATION_DELAY, SESSION_TIME_OUT_DELAY } from './sessionConstants' import { isValidSessionString, SESSION_ENTRY_REGEXP, SESSION_ENTRY_SEPARATOR } from './sessionStateValidation' export const EXPIRED = '1' export interface SessionState { id?: string created?: string expire?: string isExpired?: typeof EXPIRED anonymousId?: string [key: string]: string | undefined } export function getCreatedDate(state: SessionState): TimeStamp | undefined { return state.created ? (Number(state.created) as TimeStamp) : undefined } export function getExpireDate(state: SessionState): TimeStamp | undefined { const expireDate = state.expire && (Number(state.expire) as TimeStamp) const createdDate = getCreatedDate(state) if (expireDate && createdDate) { return Math.min(expireDate, createdDate + SESSION_TIME_OUT_DELAY) as TimeStamp } } export function getExpiredSessionState( previousSessionState: { anonymousId?: string } | undefined, configuration: Configuration ): SessionState { const expiredSessionState: SessionState = { isExpired: EXPIRED, } if (configuration.trackAnonymousUser && previousSessionState?.anonymousId) { expiredSessionState.anonymousId = previousSessionState?.anonymousId } return expiredSessionState } export function isSessionInNotStartedState(session: SessionState) { return isEmptyObject(session) } export function isSessionStarted(session: SessionState) { return !isSessionInNotStartedState(session) } export function isSessionInExpiredState(session: SessionState) { return session.isExpired !== undefined || !isActiveSession(session) } // An active session is a session in either `Tracked` or `NotTracked` state function isActiveSession(sessionState: SessionState) { const expireDate = getExpireDate(sessionState) return expireDate ? dateNow() < expireDate : false } export function expandSessionState(session: SessionState) { session.expire = String(dateNow() + SESSION_EXPIRATION_DELAY) } export function toSessionString(session: SessionState) { return ( objectEntries(session) // we use `aid` as a key for anonymousId .map(([key, value]) => (key === 'anonymousId' ? `aid=${value}` : `${key}=${value}`)) .join(SESSION_ENTRY_SEPARATOR) ) } export function toSessionState(sessionString: string | undefined | null) { const session: SessionState = {} if (isValidSessionString(sessionString)) { sessionString.split(SESSION_ENTRY_SEPARATOR).forEach((entry) => { const matches = SESSION_ENTRY_REGEXP.exec(entry) if (matches !== null) { const [, key, value] = matches if (key === 'aid') { // we use `aid` as a key for anonymousId session.anonymousId = value } else { session[key] = value } } }) } return session } export function initializeSession(state: SessionState, configuration: Configuration): SessionState { if (isSessionInNotStartedState(state)) { if (configuration.trackAnonymousUser) { state.anonymousId = generateUUID() } return getExpiredSessionState(state, configuration) } return state } export function expandOrRenew(state: SessionState, configuration: Configuration): SessionState { // prevent renewing if state is altered by a 3rd party (e.g. adblocker deleting the cookie) if (isSessionInNotStartedState(state)) { return state } let newState = state if (isSessionInExpiredState(state)) { newState = getExpiredSessionState(state, configuration) } if (!newState.id) { newState.id = generateUUID() newState.created = String(dateNow()) } if (configuration.trackAnonymousUser && !newState.anonymousId) { newState.anonymousId = generateUUID() } delete newState.isExpired expandSessionState(newState) return newState } export function expandOnly(state: SessionState): SessionState { if (isSessionInExpiredState(state) || isSessionInNotStartedState(state) || !state.id) { return state } expandSessionState(state) return state }