{"version":3,"file":"EmbraceUserSessionManager.cjs","names":["DEFAULT_ACTIVITY_THROTTLE_MS","DEFAULT_ACTIVITY_EVENTS","diag","readPermanentProperties","trace","throttle","clampNumber","DEFAULT_USER_SESSION_MAX_DURATION_SECONDS","MIN_USER_SESSION_MAX_DURATION_SECONDS","MAX_USER_SESSION_MAX_DURATION_SECONDS","DEFAULT_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS","MAX_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS","DEFAULT_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS","MAX_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS","KEY_EMB_USER_SESSION_ID","ATTR_SESSION_ID","KEY_EMB_USER_SESSION_PREVIOUS_ID","ATTR_SESSION_PREVIOUS_ID","KEY_EMB_USER_SESSION_NUMBER","KEY_EMB_USER_SESSION_PART_INDEX","KEY_EMB_SESSION_PART_NUMBER","KEY_EMB_USER_SESSION_START_TS","KEY_EMB_USER_SESSION_MAX_DURATION_SECONDS","KEY_EMB_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS","KEY_EMB_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS","EMBRACE_LAST_END_USER_SESSION_TS_KEY","getVisibilityState","generateUUID","KEY_EMB_TYPE","KEY_EMB_STATE","KEY_EMB_SESSION_PART_ID","KEY_EMB_SESSION_PART_START_REASON","KEY_EMB_COLD_START","EmbraceExtendedSpan","SESSION_PART_SPAN_NAME","FINAL_SESSION_PART_END_REASONS","KEY_EMB_SESSION_PART_END_REASON","KEY_EMB_SDK_STARTUP_DURATION","KEY_EMB_PAGE_LOAD","KEY_EMB_IS_FINAL_SESSION_PART","KEY_EMB_USER_SESSION_TERMINATION_REASON","KEY_PREFIX_EMB_PROPERTIES","EMBRACE_USER_SESSION_STATE_KEY","storePermanentProperties","readUserSessionState","isUserSessionExpired","getIncrementedCount","EMBRACE_USER_SESSION_NUMBER_KEY","createUserSessionState","EMBRACE_SESSION_PART_NUMBER_KEY","isTabEngaged"],"sources":["../../../src/managers/EmbraceUserSessionManager/EmbraceUserSessionManager.ts"],"sourcesContent":["import type {\n  Attributes,\n  DiagLogger,\n  Tracer,\n  TracerProvider,\n} from '@opentelemetry/api';\nimport { diag, trace } from '@opentelemetry/api';\nimport {\n  ATTR_SESSION_ID,\n  ATTR_SESSION_PREVIOUS_ID,\n} from '@opentelemetry/semantic-conventions/incubating';\nimport type {\n  PropertyOptions,\n  SessionPartStartedEvent,\n  UserSessionEndReason,\n} from '../../api-sessions/manager/types.ts';\nimport type { VisibilityStateDocument } from '../../common/index.ts';\nimport {\n  EMB_STATES,\n  EMB_TYPES,\n  KEY_EMB_COLD_START,\n  KEY_EMB_IS_FINAL_SESSION_PART,\n  KEY_EMB_PAGE_LOAD,\n  KEY_EMB_SDK_STARTUP_DURATION,\n  KEY_EMB_SESSION_PART_END_REASON,\n  KEY_EMB_SESSION_PART_ID,\n  KEY_EMB_SESSION_PART_NUMBER,\n  KEY_EMB_SESSION_PART_START_REASON,\n  KEY_EMB_STATE,\n  KEY_EMB_TYPE,\n  KEY_EMB_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,\n  KEY_EMB_USER_SESSION_ID,\n  KEY_EMB_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,\n  KEY_EMB_USER_SESSION_MAX_DURATION_SECONDS,\n  KEY_EMB_USER_SESSION_NUMBER,\n  KEY_EMB_USER_SESSION_PART_INDEX,\n  KEY_EMB_USER_SESSION_PREVIOUS_ID,\n  KEY_EMB_USER_SESSION_START_TS,\n  KEY_EMB_USER_SESSION_TERMINATION_REASON,\n  KEY_PREFIX_EMB_PROPERTIES,\n} from '../../constants/index.ts';\nimport type { ExtendedSpan } from '../../index.ts';\nimport type { DynamicConfigManager } from '../../sdk/index.ts';\nimport type {\n  NamespacedStorage,\n  PerformanceManager,\n  TimeoutRef,\n} from '../../utils/index.ts';\nimport {\n  clampNumber,\n  generateUUID,\n  getIncrementedCount,\n  getVisibilityState,\n  throttle,\n} from '../../utils/index.ts';\nimport type { LimitManagerInternal } from '../EmbraceLimitManager/index.ts';\nimport { EmbraceExtendedSpan } from '../EmbraceTraceManager/EmbraceExtendedSpan.ts';\nimport {\n  DEFAULT_ACTIVITY_EVENTS,\n  DEFAULT_ACTIVITY_THROTTLE_MS,\n  DEFAULT_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,\n  DEFAULT_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,\n  DEFAULT_USER_SESSION_MAX_DURATION_SECONDS,\n  EMBRACE_LAST_END_USER_SESSION_TS_KEY,\n  EMBRACE_SESSION_PART_NUMBER_KEY,\n  EMBRACE_USER_SESSION_NUMBER_KEY,\n  EMBRACE_USER_SESSION_STATE_KEY,\n  END_USER_SESSION_COOLDOWN_MS,\n  FINAL_SESSION_PART_END_REASONS,\n  MAX_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,\n  MAX_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,\n  MAX_USER_SESSION_MAX_DURATION_SECONDS,\n  MIN_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,\n  MIN_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,\n  MIN_USER_SESSION_MAX_DURATION_SECONDS,\n  SESSION_PART_SPAN_NAME,\n} from './constants.ts';\nimport type {\n  EmbraceUserSessionManagerArgs,\n  EndSessionPartOptions,\n  RolloverSessionPartOptions,\n  StartSessionPartOptions,\n  UserSessionAttributes,\n  UserSessionManagerInternal,\n  UserSessionState,\n} from './types.ts';\nimport {\n  addActivityListeners,\n  createUserSessionState,\n  isTabEngaged,\n  isUserSessionExpired,\n  readPermanentProperties,\n  readUserSessionState,\n  removeActivityListeners,\n  storePermanentProperties,\n} from './utils/index.ts';\n\n/**\n * Parts are engagement-gated (visible AND focused), so only one part can\n * be active at a time. Engagement exclusivity lets us treat storage as\n * a plain shared row read on engagement and written on changes.\n */\nexport class EmbraceUserSessionManager implements UserSessionManagerInternal {\n  private _state: UserSessionState | null = null;\n  private _previousUserSessionId: string | null = null;\n  private _maxDurationTimeout: ReturnType<typeof setTimeout> | null = null;\n  // Bare keys; the wire-format prefix is applied at stamp time.\n  private _permanentProperties: Record<string, string> = {};\n  // Distinguishes \"another tab wiped storage after we wrote\" from \"we\n  // never managed to write in the first place\"; see\n  // _continueUserSessionAfterPartEnd.\n  private _hasStoredState = false;\n\n  private _activeSessionPartId: string | null = null;\n  // Held for the part's lifetime so getUserSessionAttributes() reads\n  // during the part return its starting value, even if another tab bumps\n  // the shared counter while this tab is mid-disengagement.\n  private _currentSessionPartNumber: number | null = null;\n  private _sessionPartSpan: ExtendedSpan | null = null;\n  private _activeSessionPartCounts: Record<string, number> | null = null;\n  // Marks the first part of the page lifetime so it can be reported as a\n  // cold start.\n  private _coldStart = true;\n  private _nextSessionPartCounts: Record<string, number> = {};\n  private _sdkStartupDuration = 0;\n  private readonly _sessionPartStartedListeners: Array<\n    (event: SessionPartStartedEvent) => void\n  > = [];\n  private readonly _sessionPartEndedListeners: Array<() => void> = [];\n  private _tracer: Tracer;\n\n  private readonly _diag: DiagLogger;\n  private readonly _perf: PerformanceManager;\n  private readonly _storage: NamespacedStorage;\n  private readonly _visibilityDoc: VisibilityStateDocument;\n  private readonly _limitManager: LimitManagerInternal;\n  private readonly _dynamicConfigManager: DynamicConfigManager;\n  private readonly _target: EventTarget;\n  private readonly _activityEvents: ReadonlyArray<string>;\n  private readonly _onActivityThrottled: (event: Event) => void;\n  private _sessionPartInactivityTimer: TimeoutRef | null = null;\n\n  public constructor({\n    dynamicConfigManager,\n    diag: diagParam,\n    perf,\n    visibilityDoc,\n    storage,\n    limitManager,\n    target = window,\n    activityThrottleMs = DEFAULT_ACTIVITY_THROTTLE_MS,\n    activityEvents = DEFAULT_ACTIVITY_EVENTS,\n  }: EmbraceUserSessionManagerArgs) {\n    this._diag =\n      diagParam ??\n      diag.createComponentLogger({\n        namespace: 'EmbraceUserSessionManager',\n      });\n    this._perf = perf;\n    this._visibilityDoc = visibilityDoc;\n    this._storage = storage;\n    this._limitManager = limitManager;\n    this._dynamicConfigManager = dynamicConfigManager;\n    this._permanentProperties = readPermanentProperties(\n      this._storage,\n      this._diag,\n    );\n    this._tracer = trace.getTracer('embrace-web-sdk-session-parts');\n    this._target = target;\n    this._activityEvents = activityEvents;\n    this._onActivityThrottled = throttle(this._onActivity, activityThrottleMs);\n  }\n\n  /**\n   * Reads the user-session durations from remote config, clamps each to its\n   * allowed range, and enforces inactivity <= max duration. Called once per\n   * user-session creation; the resolved values are frozen into that session's\n   * state.\n   */\n  private _resolveUserSessionDurations(): {\n    userSessionMaxDurationSeconds: number;\n    userSessionInactivityTimeoutSeconds: number;\n    userSessionForegroundInactivityTimeoutSeconds: number;\n  } {\n    const config = this._dynamicConfigManager.getConfig();\n\n    const userSessionMaxDurationSeconds = clampNumber({\n      diag: this._diag,\n      value: config.userSessionMaxDurationSeconds,\n      defaultValue: DEFAULT_USER_SESSION_MAX_DURATION_SECONDS,\n      min: MIN_USER_SESSION_MAX_DURATION_SECONDS,\n      max: MAX_USER_SESSION_MAX_DURATION_SECONDS,\n    });\n    const userSessionInactivityTimeoutSeconds = clampNumber({\n      diag: this._diag,\n      value: config.userSessionInactivityTimeoutSeconds,\n      defaultValue: DEFAULT_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,\n      min: MIN_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,\n      max: MAX_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,\n    });\n    const userSessionForegroundInactivityTimeoutSeconds = clampNumber({\n      diag: this._diag,\n      value: config.userSessionForegroundInactivityTimeoutSeconds,\n      defaultValue: DEFAULT_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,\n      min: MIN_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,\n      max: MAX_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,\n    });\n\n    // Each inactivity timeout must fit inside the max duration; if remote\n    // config violates that, the offending value falls back to its own default\n    // rather than to the max-duration value.\n    const inactivityWithinMax =\n      userSessionInactivityTimeoutSeconds <= userSessionMaxDurationSeconds;\n    const foregroundWithinMax =\n      userSessionForegroundInactivityTimeoutSeconds <=\n      userSessionMaxDurationSeconds;\n\n    if (!inactivityWithinMax) {\n      this._diag.warn(\n        `userSessionInactivityTimeoutSeconds (${userSessionInactivityTimeoutSeconds.toString()}s) ` +\n          `exceeds userSessionMaxDurationSeconds (${userSessionMaxDurationSeconds.toString()}s); ` +\n          `falling back to default inactivity timeout (${DEFAULT_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS.toString()}s).`,\n      );\n    }\n    if (!foregroundWithinMax) {\n      this._diag.warn(\n        `userSessionForegroundInactivityTimeoutSeconds (${userSessionForegroundInactivityTimeoutSeconds.toString()}s) ` +\n          `exceeds userSessionMaxDurationSeconds (${userSessionMaxDurationSeconds.toString()}s); ` +\n          `falling back to default foreground inactivity timeout (${DEFAULT_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS.toString()}s).`,\n      );\n    }\n\n    return {\n      userSessionMaxDurationSeconds,\n      userSessionInactivityTimeoutSeconds: inactivityWithinMax\n        ? userSessionInactivityTimeoutSeconds\n        : DEFAULT_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS,\n      userSessionForegroundInactivityTimeoutSeconds: foregroundWithinMax\n        ? userSessionForegroundInactivityTimeoutSeconds\n        : DEFAULT_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS,\n    };\n  }\n\n  public setTracerProvider(tracerProvider: TracerProvider): void {\n    this._tracer = tracerProvider.getTracer('embrace-web-sdk-session-parts');\n    // Listeners attach here rather than in the constructor so that tests\n    // that construct a manager without going through SDK init don't\n    // pollute window with listeners, and so that browser-activity-driven\n    // parts only start once a real tracer is wired.\n    addActivityListeners({\n      target: this._target,\n      visibilityDoc: this._visibilityDoc,\n      activityEvents: this._activityEvents,\n      onActivity: this._onActivityThrottled,\n      onVisibilityChange: this._onVisibilityChange,\n      onFocus: this._onFocus,\n      onBlur: this._onBlur,\n    });\n    // Eager state init so getUserSessionId() returns a real id before the\n    // first part starts, and so other tabs see the row when we create a\n    // fresh session here.\n    const { state, created } = this._loadOrCreateUserSessionState(\n      this._perf.getNowMillis(),\n    );\n    this._state = state;\n    if (created) {\n      this._storeState();\n    }\n  }\n\n  public recordSDKStartupDuration(duration: number): void {\n    this._sdkStartupDuration = Math.ceil(duration);\n  }\n\n  public getUserSessionId(): string | null {\n    return this._state?.userSessionId ?? null;\n  }\n\n  public getPreviousUserSessionId(): string | null {\n    // Fall back to in-memory for the brief window during user-session end\n    // where storage is cleared but the just-ended id is still held.\n    return this._state?.previousUserSessionId ?? this._previousUserSessionId;\n  }\n\n  public getUserSessionStartTime(): number | null {\n    return this._state?.userSessionStartTs ?? null;\n  }\n\n  public getUserSessionAttributes(): UserSessionAttributes | null {\n    if (!this._state) {\n      return null;\n    }\n    const previousUserSessionId = this.getPreviousUserSessionId() ?? '';\n    return {\n      [KEY_EMB_USER_SESSION_ID]: this._state.userSessionId,\n      [ATTR_SESSION_ID]: this._state.userSessionId,\n      [KEY_EMB_USER_SESSION_PREVIOUS_ID]: previousUserSessionId,\n      [ATTR_SESSION_PREVIOUS_ID]: previousUserSessionId,\n      [KEY_EMB_USER_SESSION_NUMBER]: this._state.userSessionNumber,\n      [KEY_EMB_USER_SESSION_PART_INDEX]: this._state.userSessionPartIndex,\n      [KEY_EMB_SESSION_PART_NUMBER]: this._currentSessionPartNumber ?? 0,\n      [KEY_EMB_USER_SESSION_START_TS]: this._state.userSessionStartTs,\n      [KEY_EMB_USER_SESSION_MAX_DURATION_SECONDS]:\n        this._state.userSessionMaxDurationSeconds,\n      [KEY_EMB_USER_SESSION_INACTIVITY_TIMEOUT_SECONDS]:\n        this._state.userSessionInactivityTimeoutSeconds,\n      [KEY_EMB_USER_SESSION_FOREGROUND_INACTIVITY_TIMEOUT_SECONDS]:\n        this._state.userSessionForegroundInactivityTimeoutSeconds,\n    };\n  }\n\n  public endUserSession(): void {\n    if (!this._state) {\n      this._diag.debug(\n        'Trying to end user session, but there is no active session. This is a no-op.',\n      );\n      return;\n    }\n\n    const now = this._perf.getNowMillis();\n    const lastEndRaw = this._storage.getItem(\n      EMBRACE_LAST_END_USER_SESSION_TS_KEY,\n    );\n    const lastEndTs = lastEndRaw === null ? NaN : Number(lastEndRaw);\n    if (\n      Number.isFinite(lastEndTs) &&\n      now - lastEndTs < END_USER_SESSION_COOLDOWN_MS\n    ) {\n      this._diag.warn(\n        'endUserSession called within cooldown period, ignoring.',\n      );\n      return;\n    }\n\n    this._rolloverUserSession('manual');\n    this._storage.setItem(EMBRACE_LAST_END_USER_SESSION_TS_KEY, String(now));\n  }\n\n  public getSessionPartId(): string | null {\n    return this._activeSessionPartId;\n  }\n\n  public getSessionPartSpan(): ExtendedSpan | null {\n    return this._sessionPartSpan;\n  }\n\n  public startSessionPartInternal({\n    reason,\n    timestamp,\n  }: StartSessionPartOptions): void {\n    if (this._sessionPartSpan) {\n      this._diag.warn(\n        `startSessionPartInternal called while a part is active (reason: ${reason}); ignoring`,\n      );\n      return;\n    }\n\n    // Parts are foreground-only: a part corresponds to a tab that is both\n    // visible AND focused.\n    if (\n      getVisibilityState(this._visibilityDoc) === EMB_STATES.Background ||\n      !this._visibilityDoc.hasFocus()\n    ) {\n      this._diag.debug(\n        `skipping session part start (reason: ${reason}) because the tab is not engaged`,\n      );\n      return;\n    }\n\n    const activeSessionPartId = generateUUID();\n    const previouslyRecordedCounts = this._nextSessionPartCounts;\n\n    this._beginUserSessionForPartStart();\n\n    const attributes: Attributes = {\n      ...this.getUserSessionAttributes(),\n      [KEY_EMB_TYPE]: EMB_TYPES.SessionPart,\n      [KEY_EMB_STATE]: EMB_STATES.Foreground,\n      [KEY_EMB_SESSION_PART_ID]: activeSessionPartId,\n      [KEY_EMB_SESSION_PART_START_REASON]: reason,\n      [KEY_EMB_COLD_START]: this._coldStart,\n      ...previouslyRecordedCounts,\n    };\n\n    this._activeSessionPartId = activeSessionPartId;\n    let span: EmbraceExtendedSpan;\n    try {\n      span = new EmbraceExtendedSpan(\n        this._tracer.startSpan(SESSION_PART_SPAN_NAME, {\n          attributes,\n          startTime: timestamp,\n        }),\n      );\n    } catch (error) {\n      this._activeSessionPartId = null;\n      this._diag.error('Error starting session part span', error);\n      return;\n    }\n\n    this._activeSessionPartCounts = {};\n    this._nextSessionPartCounts = {};\n    this._sessionPartSpan = span;\n\n    this._startSessionPartInactivityTimer();\n\n    this._fireListeners(this._sessionPartStartedListeners, 'started', {\n      reason,\n    });\n  }\n\n  public endSessionPartInternal({\n    reason,\n    userSessionEndReason,\n    timestamp,\n  }: EndSessionPartOptions): void {\n    if (!this._sessionPartSpan) {\n      this._diag.debug(\n        'trying to end a session part, but there is no session part in progress. This is a no-op.',\n      );\n      return;\n    }\n\n    this._clearSessionPartInactivityTimer();\n\n    const isFinalSessionPart = FINAL_SESSION_PART_END_REASONS.has(reason);\n\n    // Two stamps, captured upfront so an unbounded SpanProcessor.onEnd\n    // cannot push them forward: the caller's timestamp (when given) becomes\n    // the span end time, while the inactivity deadline is computed from the\n    // actual call time so an anchored end cannot shorten the expiry window.\n    const now = this._perf.getNowMillis();\n    const partEndTs = timestamp ?? now;\n    const span = this._sessionPartSpan;\n    try {\n      const endAttrs: Attributes = {\n        [KEY_EMB_SESSION_PART_END_REASON]: reason,\n        ...this._activeSessionPartCounts,\n        ...this._limitManager.getDiagnosticCounts(),\n        [KEY_EMB_SDK_STARTUP_DURATION]: this._sdkStartupDuration,\n      };\n      if (this._coldStart) {\n        endAttrs[KEY_EMB_PAGE_LOAD] =\n          this._visibilityDoc.readyState === 'complete';\n      }\n      if (isFinalSessionPart) {\n        endAttrs[KEY_EMB_IS_FINAL_SESSION_PART] = 1;\n        if (userSessionEndReason) {\n          endAttrs[KEY_EMB_USER_SESSION_TERMINATION_REASON] =\n            userSessionEndReason;\n        }\n      }\n\n      const propertyAttrs: Attributes = {};\n      for (const [bareKey, value] of Object.entries(\n        this.getSessionPartProperties(),\n      )) {\n        propertyAttrs[KEY_PREFIX_EMB_PROPERTIES + bareKey] = value;\n      }\n\n      try {\n        span.setAttributes({\n          ...propertyAttrs,\n          ...endAttrs,\n        });\n      } catch (error) {\n        this._diag.warn(\n          'Error setting end attributes on session part span; ending span without them',\n          error,\n        );\n      }\n    } finally {\n      // Fire listeners after end attributes are stamped (so subscribers can\n      // read them) and before span.end() (so anything they emit still\n      // attaches to the part that is conceptually still active).\n      this._fireListeners(this._sessionPartEndedListeners, 'ended', undefined);\n      try {\n        span.end(partEndTs);\n      } catch (error) {\n        this._diag.warn('Error ending session part span', error);\n      }\n      this._sessionPartSpan = null;\n      this._coldStart = false;\n      this._activeSessionPartId = null;\n      this._activeSessionPartCounts = null;\n      this._currentSessionPartNumber = null;\n      this._limitManager.reset();\n    }\n\n    if (isFinalSessionPart) {\n      this._previousUserSessionId = this._state?.userSessionId ?? null;\n      this._state = null;\n      this._storage.removeItem(EMBRACE_USER_SESSION_STATE_KEY);\n      this._clearMaxDurationTimer();\n    } else {\n      this._continueUserSessionAfterPartEnd(now);\n    }\n  }\n\n  public addBreadcrumb(name: string): void {\n    if (!this._sessionPartSpan) {\n      this._diag.debug(\n        'trying to add breadcrumb, but there is no session part in progress. This is a no-op.',\n      );\n      return;\n    }\n\n    const limitedBreadcrumb = this._limitManager.limitBreadcrumb(name);\n    if (limitedBreadcrumb === 'dropped') {\n      return;\n    }\n\n    this._sessionPartSpan.addEvent(\n      'emb-breadcrumb',\n      {\n        message: limitedBreadcrumb.name,\n      },\n      this._perf.getNowMillis(),\n    );\n  }\n\n  public addProperty(\n    propertyKey: string,\n    value: string,\n    options?: PropertyOptions,\n  ): void {\n    const limitedSessionProperty = this._limitManager.limitUserSessionProperty(\n      propertyKey,\n      value,\n    );\n\n    if (limitedSessionProperty === 'dropped') {\n      return;\n    }\n\n    const bareKey = limitedSessionProperty.key;\n    const bareValue = limitedSessionProperty.value;\n\n    if (options?.lifespan === 'permanent') {\n      const candidate = {\n        ...this._permanentProperties,\n        [bareKey]: bareValue,\n      };\n      // Reject the write if storage is unavailable: a property that can't\n      // persist would silently diverge from what other tabs (and the next\n      // page load) see, which is worse than no property at all.\n      if (!storePermanentProperties(this._storage, candidate)) {\n        this._diag.warn(\n          'Storage unavailable; rejecting permanent property write.',\n        );\n        return;\n      }\n      this._permanentProperties = candidate;\n      this._removeFromUserSessionProperties(bareKey);\n      return;\n    }\n\n    // Ensure a state row exists so the write has somewhere to land;\n    // addProperty can fire before any part has started.\n    const { state } = this._loadOrCreateUserSessionState(\n      this._perf.getNowMillis(),\n    );\n    this._state = {\n      ...state,\n      userSessionProperties: {\n        ...state.userSessionProperties,\n        [bareKey]: bareValue,\n      },\n    };\n    if (!this._storeState()) {\n      // Roll back so callers don't observe a value that won't persist.\n      // The user session itself stays in memory.\n      this._state = state;\n      this._diag.warn(\n        'Storage unavailable; rejecting user-session property write.',\n      );\n    }\n  }\n\n  public removeProperty(propertyKey: string): void {\n    const bareKey = this._limitManager.truncateString(\n      'session_property_key',\n      propertyKey,\n    );\n\n    // Try both stores so a key that was flipped between scopes doesn't\n    // linger in the other one.\n    this._removeFromPermanentProperties(bareKey);\n    this._removeFromUserSessionProperties(bareKey);\n  }\n\n  /**\n   * Bare-keyed view of properties for the active part. User-session-scoped\n   * entries shadow permanent ones, matching the cross-scope flip rule\n   * enforced in `addProperty`. Callers apply the wire-format prefix at\n   * stamp time.\n   */\n  public getSessionPartProperties(): Record<string, string> {\n    return {\n      ...this._permanentProperties,\n      ...(this._state?.userSessionProperties ?? {}),\n    };\n  }\n\n  public incrSessionPartCountForKey(key: string): void {\n    if (!this._sessionPartSpan || !this._activeSessionPartCounts) {\n      this._diag.debug(\n        'trying to increment a count for the active session part, but there is no session part in progress. This is a no-op.',\n      );\n      return;\n    }\n    this._activeSessionPartCounts[key] =\n      (this._activeSessionPartCounts[key] || 0) + 1;\n  }\n\n  public incrNextSessionPartCountForKey(key: string): void {\n    this._nextSessionPartCounts[key] =\n      (this._nextSessionPartCounts[key] || 0) + 1;\n  }\n\n  public addSessionPartStartedListener(\n    listener: (event: SessionPartStartedEvent) => void,\n  ): () => void {\n    return this._addListener(this._sessionPartStartedListeners, listener);\n  }\n\n  public addSessionPartEndedListener(listener: () => void): () => void {\n    return this._addListener(this._sessionPartEndedListeners, listener);\n  }\n\n  public getSessionId(): string | null {\n    return this.getUserSessionId();\n  }\n\n  public getPreviousSessionId(): null {\n    return null;\n  }\n\n  public getSessionStartTime(): number | null {\n    return this.getUserSessionStartTime();\n  }\n\n  public endSessionSpan(): void {\n    this.endUserSession();\n  }\n\n  public startSessionSpan(): void {}\n\n  public getSessionSpan(): ExtendedSpan | null {\n    return this.getSessionPartSpan();\n  }\n\n  public currentSessionAsReadableSpan(): null {\n    return null;\n  }\n\n  public addSessionStartedListener(_listener: () => void): () => void {\n    return () => {};\n  }\n\n  public addSessionEndedListener(_listener: () => void): () => void {\n    return () => {};\n  }\n\n  /**\n   * Returns the persisted user-session state, rotating to a fresh one if\n   * the persisted row is missing or expired. Arms the max-duration timer\n   * when needed. The caller is responsible for assigning the result to\n   * `_state` and for persisting (the helper does not write to storage).\n   */\n  private _loadOrCreateUserSessionState(now: number): {\n    state: UserSessionState;\n    created: boolean;\n  } {\n    let state = readUserSessionState(this._storage, this._diag);\n    // Never persisted (writes disabled): keep the in-memory session rather than\n    // mint a fresh one per part. After a persist, an empty read is a real clear.\n    if (!state && !this._hasStoredState && this._state) {\n      state = this._state;\n    }\n    let created = false;\n    if (!state || isUserSessionExpired(state, now)) {\n      if (state) {\n        this._previousUserSessionId = state.userSessionId;\n      }\n      const userSessionNumber = getIncrementedCount(\n        this._storage,\n        EMBRACE_USER_SESSION_NUMBER_KEY,\n        this._diag,\n      );\n      // Refresh remote config for the next user session. The fetch is async,\n      // so it lands in the cache after this session's durations are frozen\n      // below; this session uses the currently cached config. Skipped on cold\n      // start, where initSDK already refreshes at startup.\n      if (!this._coldStart) {\n        void this._dynamicConfigManager.refreshRemoteConfig();\n      }\n      const {\n        userSessionMaxDurationSeconds,\n        userSessionInactivityTimeoutSeconds,\n        userSessionForegroundInactivityTimeoutSeconds,\n      } = this._resolveUserSessionDurations();\n      state = createUserSessionState({\n        now,\n        previousUserSessionId: this._previousUserSessionId,\n        userSessionMaxDurationSeconds,\n        userSessionInactivityTimeoutSeconds,\n        userSessionForegroundInactivityTimeoutSeconds,\n        userSessionNumber,\n      });\n      created = true;\n    }\n    // Arm on a freshly created state, and on the page-reload case where a\n    // new manager loads existing state and has no timer running yet.\n    // userSessionMaxEndTs is fixed at creation, so re-arming the same value\n    // on every load/start call is wasteful.\n    if (created || this._maxDurationTimeout === null) {\n      this._setupMaxDurationTimer(state, now);\n    }\n    return { state, created };\n  }\n\n  /**\n   * Bumps the part counter and arms the max-duration timer. Relies on\n   * `_loadOrCreateUserSessionState` to provide a non-null, non-expired state,\n   * so `getUserSessionAttributes()` is guaranteed non-null after this.\n   */\n  private _beginUserSessionForPartStart(): void {\n    const now = this._perf.getNowMillis();\n    const { state } = this._loadOrCreateUserSessionState(now);\n\n    this._currentSessionPartNumber = getIncrementedCount(\n      this._storage,\n      EMBRACE_SESSION_PART_NUMBER_KEY,\n      this._diag,\n    );\n\n    this._state = {\n      ...state,\n      userSessionPartIndex: state.userSessionPartIndex + 1,\n      // A part is active, so any pending inactivity deadline no longer\n      // applies; it gets re-armed when the part ends.\n      inactivityDeadlineTs: null,\n    };\n    this._storeState();\n  }\n\n  /**\n   * Records the inactivity deadline on the user-session row so the next\n   * part start can detect lazy expiry. The max-duration timer stays valid\n   * because userSessionMaxEndTs doesn't change between parts.\n   */\n  private _continueUserSessionAfterPartEnd(now: number): void {\n    if (!this._state) {\n      this._clearMaxDurationTimer();\n      return;\n    }\n\n    // If the state row was present at part start but is gone now (user\n    // wiped site data, quota eviction, sibling tab cleared), don't\n    // resurrect it from memory. Drop in-memory state so the next part\n    // start creates a fresh user session; carry the just-ended id\n    // forward so the next session can back-link to it.\n    // _hasStoredState gates this: if we never persisted in the first\n    // place, the in-memory copy stays authoritative.\n    if (\n      this._hasStoredState &&\n      this._storage.getItem(EMBRACE_USER_SESSION_STATE_KEY) === null\n    ) {\n      this._previousUserSessionId = this._state.userSessionId;\n      this._state = null;\n      this._clearMaxDurationTimer();\n      return;\n    }\n\n    this._state = {\n      ...this._state,\n      inactivityDeadlineTs:\n        now + this._state.userSessionInactivityTimeoutSeconds * 1000,\n    };\n    this._storeState();\n  }\n\n  /**\n   * Ends the active part as final and starts a fresh part for the next\n   * user session. State clearing for the engaged path is handled by\n   * endSessionPartInternal's isFinal branch. When no part is active\n   * (tab disengaged), the same state-clearing is performed inline so\n   * the next engagement creates a fresh user session rather than\n   * resuming the just-ended one.\n   */\n  private _rolloverUserSession(\n    userSessionEndReason: UserSessionEndReason,\n  ): void {\n    if (this._sessionPartSpan) {\n      // The paired end/start share one boundary timestamp so the consecutive\n      // part spans tile without an artificial gap. Real gaps in the part\n      // timeline (page load, hidden tab) stay meaningful because those paths\n      // start a part without passing a timestamp. The timestamp anchors the\n      // spans only; user-session state and timers always use the actual call\n      // time.\n      const boundaryTimestamp = this._perf.getNowMillis();\n      this.endSessionPartInternal({\n        reason: 'user_session_ended',\n        userSessionEndReason,\n        timestamp: boundaryTimestamp,\n      });\n      this.startSessionPartInternal({\n        reason: 'user_session_rollover',\n        timestamp: boundaryTimestamp,\n      });\n      return;\n    }\n\n    this._previousUserSessionId = this._state?.userSessionId ?? null;\n    this._state = null;\n    this._storage.removeItem(EMBRACE_USER_SESSION_STATE_KEY);\n    this._clearMaxDurationTimer();\n  }\n\n  /**\n   * Ends the active part and starts a new one within the same user session.\n   * The user session is preserved while the part counters advance, so the\n   * caller's `endReason`/`startReason` must both be non-final. Both spans share\n   * one boundary timestamp so they tile without a gap; the caller may supply it\n   * (for example the timestamp of the triggering event), defaulting to now. A\n   * no-op when no part is active: there is nothing to roll over and the next\n   * engagement starts one.\n   */\n\n  public rolloverSessionPartInternal({\n    endReason,\n    startReason,\n    timestamp,\n  }: RolloverSessionPartOptions): void {\n    if (!this._sessionPartSpan) {\n      return;\n    }\n    const boundaryTimestamp = timestamp ?? this._perf.getNowMillis();\n    this.endSessionPartInternal({\n      reason: endReason,\n      timestamp: boundaryTimestamp,\n    });\n    this.startSessionPartInternal({\n      reason: startReason,\n      timestamp: boundaryTimestamp,\n    });\n  }\n\n  private _setupMaxDurationTimer(state: UserSessionState, now: number): void {\n    this._clearMaxDurationTimer();\n\n    const remaining = state.userSessionMaxEndTs - now;\n    if (remaining <= 0) {\n      return;\n    }\n\n    this._maxDurationTimeout = setTimeout(() => {\n      this._maxDurationTimeout = null;\n      this._rolloverUserSession('max_duration_reached');\n    }, remaining);\n  }\n\n  /**\n   * @internal Would be private but exposed for tests to simulate a page reload\n   */\n  public _clearMaxDurationTimer(): void {\n    if (this._maxDurationTimeout !== null) {\n      clearTimeout(this._maxDurationTimeout);\n      this._maxDurationTimeout = null;\n    }\n  }\n\n  /**\n   * Detaches browser-activity listeners and clears the part-inactivity\n   * timer. Intended for SDK teardown and tests; in production the manager\n   * lives for the page's lifetime.\n   */\n  public _shutdown(): void {\n    removeActivityListeners({\n      target: this._target,\n      visibilityDoc: this._visibilityDoc,\n      activityEvents: this._activityEvents,\n      onActivity: this._onActivityThrottled,\n      onVisibilityChange: this._onVisibilityChange,\n      onFocus: this._onFocus,\n      onBlur: this._onBlur,\n    });\n    this._clearSessionPartInactivityTimer();\n    this._clearMaxDurationTimer();\n  }\n\n  private readonly _onActivity = (): void => {\n    try {\n      if (!isTabEngaged(this._visibilityDoc)) {\n        return;\n      }\n      if (this._activeSessionPartId === null) {\n        this.startSessionPartInternal({ reason: 'web_activity' });\n        return;\n      }\n      this._startSessionPartInactivityTimer();\n    } catch (e) {\n      this._diag.warn('Error handling activity event', e);\n    }\n  };\n\n  // Tab visibility flipped (switch tabs, minimize, OS app switch). Drives\n  // the engagement transition off the current visibilityState/hasFocus pair.\n  private readonly _onVisibilityChange = (): void => {\n    this._handleEngagementTransition('visibilitychange');\n  };\n\n  // Window focus arrived (alt-tab back, clicked into the document). hasFocus\n  // is now true; engagement transition resolves whether to start a part.\n  private readonly _onFocus = (): void => {\n    this._handleEngagementTransition('focus');\n  };\n\n  // Window focus lost (alt-tab, DevTools, another window). Engagement\n  // transition ends the active part as background. pagehide/pageshow\n  // are not listened to: blur or visibilitychange-to-hidden end the part\n  // first (see README \"Unload and BFCache handling\" for the verified\n  // cross-engine ordering). The part-end doubles as the unload flush via\n  // EmbraceSessionPartBatchedSpanProcessor.onEnd's synchronous keepalive export.\n  // https://developer.chrome.com/docs/web-platform/page-lifecycle-api\n  private readonly _onBlur = (): void => {\n    this._handleEngagementTransition('blur');\n  };\n\n  private _handleEngagementTransition(source: string): void {\n    try {\n      const engaged = isTabEngaged(this._visibilityDoc);\n      const active = this._activeSessionPartId !== null;\n      if (!engaged && active) {\n        this._diag.debug(`tab disengaged via ${source}; ending current part`);\n        this.endSessionPartInternal({ reason: 'background' });\n        return;\n      }\n      if (engaged && !active) {\n        this._diag.debug(`tab engaged via ${source}; starting new part`);\n        this.startSessionPartInternal({ reason: 'foreground' });\n        return;\n      }\n      if (engaged && active) {\n        // Redundant engagement event while a part is already active (browser\n        // quirk: an extra focus/visibility fire that isn't a real transition).\n        // Re-arm the inactivity timer since the event still implies presence.\n        this._startSessionPartInactivityTimer();\n      }\n    } catch (e) {\n      this._diag.warn('Error handling engagement change', e);\n    }\n  }\n\n  private readonly _onSessionPartInactivity = (): void => {\n    this._sessionPartInactivityTimer = null;\n    try {\n      if (this._activeSessionPartId === null) {\n        return;\n      }\n      this._diag.debug(\n        'inactivity timer fired; ending current part and user session',\n      );\n      this.endSessionPartInternal({\n        reason: 'web_foreground_inactivity',\n        userSessionEndReason: 'inactivity',\n      });\n    } catch (e) {\n      this._diag.warn('Error handling inactivity timer', e);\n    }\n  };\n\n  private _startSessionPartInactivityTimer(): void {\n    this._clearSessionPartInactivityTimer();\n    // Callers only arm this with an active part, so the early return is a type\n    // guard rather than a live path.\n    if (!this._state) {\n      return;\n    }\n    this._sessionPartInactivityTimer = setTimeout(\n      this._onSessionPartInactivity,\n      this._state.userSessionForegroundInactivityTimeoutSeconds * 1000,\n    );\n  }\n\n  private _clearSessionPartInactivityTimer(): void {\n    if (this._sessionPartInactivityTimer !== null) {\n      clearTimeout(this._sessionPartInactivityTimer);\n      this._sessionPartInactivityTimer = null;\n    }\n  }\n\n  private _addListener<T>(list: T[], listener: T): () => void {\n    list.push(listener);\n    return () => {\n      const i = list.indexOf(listener);\n      if (i !== -1) {\n        list.splice(i, 1);\n      }\n    };\n  }\n\n  private _fireListeners<T>(\n    list: Array<(arg: T) => void>,\n    kind: string,\n    arg: T,\n  ): void {\n    for (const listener of list) {\n      try {\n        listener(arg);\n      } catch (error) {\n        this._diag.warn(\n          `Error while executing session part ${kind} listener`,\n          error,\n        );\n      }\n    }\n  }\n\n  private _storeState(): boolean {\n    if (!this._state) {\n      return false;\n    }\n    const stored = this._storage.setItem(\n      EMBRACE_USER_SESSION_STATE_KEY,\n      JSON.stringify(this._state),\n    );\n    if (stored) {\n      this._hasStoredState = true;\n    }\n    return stored;\n  }\n\n  private _removeFromPermanentProperties(bareKey: string): void {\n    if (!(bareKey in this._permanentProperties)) {\n      return;\n    }\n    const pruned = { ...this._permanentProperties };\n    delete pruned[bareKey];\n    this._permanentProperties = pruned;\n    storePermanentProperties(this._storage, pruned);\n  }\n\n  private _removeFromUserSessionProperties(bareKey: string): void {\n    if (!this._state || !(bareKey in this._state.userSessionProperties)) {\n      return;\n    }\n    const pruned = { ...this._state.userSessionProperties };\n    delete pruned[bareKey];\n    this._state = {\n      ...this._state,\n      userSessionProperties: pruned,\n    };\n    this._storeState();\n  }\n}\n"],"mappings":"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"}