{"version":3,"file":"sessionInCookie.mjs","names":[],"sources":["../../../../src/domain/session/storeStrategies/sessionInCookie.ts"],"sourcesContent":["import { isEmptyObject } from '../../../tools/utils/objectUtils'\nimport type { CookieOptions } from '../../../browser/cookie'\nimport { getCookies } from '../../../browser/cookie'\nimport type { Configuration } from '../../configuration'\nimport { SESSION_COOKIE_EXPIRATION_DELAY, SESSION_TIME_OUT_DELAY, SessionPersistence } from '../sessionConstants'\nimport type { SessionState } from '../sessionState'\nimport { toSessionString, toSessionState } from '../sessionState'\nimport { Observable } from '../../../tools/observable'\nimport { mockable } from '../../../tools/mockable'\nimport { monitorError } from '../../../tools/monitor'\nimport type { CookieAccess } from '../../../browser/cookieAccess'\nimport {\n  areCookiesAuthorized,\n  createCookieStoreAccess,\n  createDocumentCookieAccess,\n  isCookieStoreSupported,\n} from '../../../browser/cookieAccess'\nimport { CookieApi, LEGACY_SESSION_STORE_KEY, SESSION_STORE_KEY } from './sessionStoreStrategy'\nimport type {\n  SessionStoreStrategy,\n  SessionStoreStrategyType,\n  CookieSessionStoreStrategyType,\n  SessionStateOperation,\n} from './sessionStoreStrategy'\n\nconst SESSION_COOKIE_VERSION = 0\n\nexport async function selectCookieStrategy(\n  configuration: Configuration\n): Promise<SessionStoreStrategyType | undefined> {\n  const { cookieOptions } = configuration\n  if (!cookieOptions) {\n    return undefined\n  }\n\n  if (isCookieStoreSupported() && (await areCookiesAuthorized(createCookieStoreAccess, cookieOptions))) {\n    return { type: SessionPersistence.COOKIE, cookieOptions, cookieApi: CookieApi.COOKIE_STORE }\n  }\n\n  if (await areCookiesAuthorized(createDocumentCookieAccess, cookieOptions)) {\n    return { type: SessionPersistence.COOKIE, cookieOptions, cookieApi: CookieApi.DOCUMENT_COOKIE }\n  }\n\n  return undefined\n}\n\n// Promise chain serializes calls when Web Locks are unavailable\nlet pendingChain: Promise<void> | undefined\n\nexport function initCookieStrategy(\n  sessionStoreStrategyType: CookieSessionStoreStrategyType,\n  configuration: Configuration\n): SessionStoreStrategy {\n  const { cookieOptions, cookieApi } = sessionStoreStrategyType\n  const sessionObservable = new Observable<SessionState>()\n  const trackAnonymousUser = !!configuration.trackAnonymousUser\n  const opts = encodeCookieOptions(cookieOptions)\n  const cookieAccess = mockable(createCookieAccess)(cookieApi, cookieOptions)\n  let isFirstCall = true\n\n  cookieAccess.observable.subscribe(() => {\n    cookieAccess\n      .getAll()\n      .then((cookieValues) => {\n        sessionObservable.notify(findMatchingSessionState(cookieValues, opts))\n      })\n      .catch((error) => monitorError(new Error(`Error while reading session cookies on change: ${error}`)))\n  })\n\n  function applyAndWrite(fn: (state: SessionState) => SessionState) {\n    return cookieAccess.getAllAndSet((cookieValues) => {\n      let currentState = findMatchingSessionState(cookieValues, opts)\n\n      if (isFirstCall && isEmptyObject(currentState)) {\n        currentState = findMatchingSessionState(getCookies(LEGACY_SESSION_STORE_KEY), opts)\n      }\n      isFirstCall = false\n\n      const newState = fn(currentState)\n      const sessionString = buildSessionString(newState, cookieOptions)\n      const expireDelay = trackAnonymousUser ? SESSION_COOKIE_EXPIRATION_DELAY : SESSION_TIME_OUT_DELAY\n\n      return { value: sessionString, expireDelay }\n    })\n  }\n\n  return {\n    async setSessionState(\n      fn: (sessionState: SessionState) => SessionState,\n      _operation: SessionStateOperation\n    ): Promise<void> {\n      if (typeof navigator !== 'undefined' && navigator.locks) {\n        await navigator.locks\n          .request(SESSION_STORE_KEY, () => applyAndWrite(fn))\n          .catch((error: unknown) => {\n            if (isContextGoingAwayError(error)) {\n              return\n            }\n            throw error\n          })\n      } else {\n        pendingChain = (pendingChain ?? Promise.resolve()).then(() => applyAndWrite(fn))\n        await pendingChain\n      }\n    },\n    sessionObservable,\n  }\n}\n\n// Thrown when the browsing context tears down mid-lock-request.\n//   - \"AbortError: Promise was rejected because the browsing context is going away\" (Webkit)\n//   - \"Error: Failed to execute 'request' on 'LockManager': The provided callback is no longer runnable.\" (Chromium)\nfunction isContextGoingAwayError(error: unknown): boolean {\n  if (!(error instanceof Error)) {\n    return false\n  }\n  return error.name === 'AbortError' || error.message.includes('no longer runnable')\n}\n\nexport function createCookieAccess(cookieApi: CookieApi, cookieOptions: CookieOptions): CookieAccess {\n  return cookieApi === CookieApi.COOKIE_STORE\n    ? createCookieStoreAccess(SESSION_STORE_KEY, cookieOptions)\n    : createDocumentCookieAccess(SESSION_STORE_KEY, cookieOptions)\n}\n\nfunction findMatchingSessionState(items: string[], opts: string): SessionState {\n  let sessionState: SessionState | undefined\n\n  // reverse the cookies so that if there is no match, the cookie returned is the first one\n  for (const item of items.slice().reverse()) {\n    sessionState = toSessionState(item)\n    if (sessionState.c === opts) {\n      break\n    }\n  }\n\n  // remove the cookie options from the session state as this is not part of the session state\n  delete sessionState?.c\n\n  return sessionState ?? {}\n}\n\nfunction buildSessionString(sessionState: SessionState, cookieOptions: CookieOptions): string {\n  return toSessionString(\n    isEmptyObject(sessionState) ? sessionState : { ...sessionState, c: encodeCookieOptions(cookieOptions) }\n  )\n}\n\nfunction encodeCookieOptions(cookieOptions: CookieOptions): string {\n  const domainCount = cookieOptions.domain ? cookieOptions.domain.split('.').length - 1 : 0\n\n  /* eslint-disable no-bitwise */\n  let byte = 0\n  byte |= SESSION_COOKIE_VERSION << 5 // Store version in upper 3 bits\n  byte |= domainCount << 1 // Store domain count in next 4 bits\n  byte |= cookieOptions.crossSite ? 1 : 0 // Store useCrossSiteScripting in next bit\n  /* eslint-enable no-bitwise */\n\n  return byte.toString(16) // Convert to hex string\n}\n"],"mappings":";;;;;;;;;;AAyBA,MAAM,yBAAyB;AAE/B,eAAsB,qBACpB,eAC+C;CAC/C,MAAM,EAAE,kBAAkB;CAC1B,IAAI,CAAC,eACH;CAGF,IAAI,uBAAuB,KAAM,MAAM,qBAAqB,yBAAyB,aAAa,GAChG,OAAO;EAAE,MAAM,mBAAmB;EAAQ;EAAe,WAAA;CAAkC;CAG7F,IAAI,MAAM,qBAAqB,4BAA4B,aAAa,GACtE,OAAO;EAAE,MAAM,mBAAmB;EAAQ;EAAe,WAAA;CAAqC;AAIlG;AAGA,IAAI;AAEJ,SAAgB,mBACd,0BACA,eACsB;CACtB,MAAM,EAAE,eAAe,cAAc;CACrC,MAAM,oBAAoB,IAAI,WAAyB;CACvD,MAAM,qBAAqB,CAAC,CAAC,cAAc;CAC3C,MAAM,OAAO,oBAAoB,aAAa;CAC9C,MAAM,eAAe,SAAS,kBAAkB,CAAC,CAAC,WAAW,aAAa;CAC1E,IAAI,cAAc;CAElB,aAAa,WAAW,gBAAgB;EACtC,aACG,OAAO,CAAC,CACR,MAAM,iBAAiB;GACtB,kBAAkB,OAAO,yBAAyB,cAAc,IAAI,CAAC;EACvE,CAAC,CAAC,CACD,OAAO,UAAU,6BAAa,IAAI,MAAM,kDAAkD,OAAO,CAAC,CAAC;CACxG,CAAC;CAED,SAAS,cAAc,IAA2C;EAChE,OAAO,aAAa,cAAc,iBAAiB;GACjD,IAAI,eAAe,yBAAyB,cAAc,IAAI;GAE9D,IAAI,eAAe,cAAc,YAAY,GAC3C,eAAe,yBAAyB,WAAW,wBAAwB,GAAG,IAAI;GAEpF,cAAc;GAMd,OAAO;IAAE,OAHa,mBADL,GAAG,YAC4B,GAAG,aAGvB;IAAG,aAFX,qBAAqB,kCAAkC;GAEhC;EAC7C,CAAC;CACH;CAEA,OAAO;EACL,MAAM,gBACJ,IACA,YACe;GACf,IAAI,OAAO,cAAc,eAAe,UAAU,OAChD,MAAM,UAAU,MACb,QAAQ,yBAAyB,cAAc,EAAE,CAAC,CAAC,CACnD,OAAO,UAAmB;IACzB,IAAI,wBAAwB,KAAK,GAC/B;IAEF,MAAM;GACR,CAAC;QACE;IACL,gBAAgB,gBAAgB,QAAQ,QAAQ,EAAA,CAAG,WAAW,cAAc,EAAE,CAAC;IAC/E,MAAM;GACR;EACF;EACA;CACF;AACF;AAKA,SAAS,wBAAwB,OAAyB;CACxD,IAAI,EAAE,iBAAiB,QACrB,OAAO;CAET,OAAO,MAAM,SAAS,gBAAgB,MAAM,QAAQ,SAAS,oBAAoB;AACnF;AAEA,SAAgB,mBAAmB,WAAsB,eAA4C;CACnG,OAAO,cAAA,IACH,wBAAwB,mBAAmB,aAAa,IACxD,2BAA2B,mBAAmB,aAAa;AACjE;AAEA,SAAS,yBAAyB,OAAiB,MAA4B;CAC7E,IAAI;CAGJ,KAAK,MAAM,QAAQ,MAAM,MAAM,CAAC,CAAC,QAAQ,GAAG;EAC1C,eAAe,eAAe,IAAI;EAClC,IAAI,aAAa,MAAM,MACrB;CAEJ;CAGA,OAAO,cAAc;CAErB,OAAO,gBAAgB,CAAC;AAC1B;AAEA,SAAS,mBAAmB,cAA4B,eAAsC;CAC5F,OAAO,gBACL,cAAc,YAAY,IAAI,eAAe;EAAE,GAAG;EAAc,GAAG,oBAAoB,aAAa;CAAE,CACxG;AACF;AAEA,SAAS,oBAAoB,eAAsC;CACjE,MAAM,cAAc,cAAc,SAAS,cAAc,OAAO,MAAM,GAAG,CAAC,CAAC,SAAS,IAAI;CAGxF,IAAI,OAAO;CACX,QAAQ,0BAA0B;CAClC,QAAQ,eAAe;CACvB,QAAQ,cAAc,YAAY,IAAI;CAGtC,OAAO,KAAK,SAAS,EAAE;AACzB"}