{"version":3,"file":"SpanSessionVisibilityInstrumentation.cjs","names":["EmbraceInstrumentationBase","throttle"],"sources":["../../../../src/instrumentations/session/SpanSessionVisibilityInstrumentation/SpanSessionVisibilityInstrumentation.ts"],"sourcesContent":["import type { SDKFeaturesManager } from '../../../managers/index.ts';\nimport type { EmbraceSessionBatchedSpanProcessor } from '../../../processors/index.ts';\nimport type { TimeoutRef } from '../../../utils/index.ts';\nimport {\n  bulkAddEventListener,\n  bulkRemoveEventListener,\n  throttle,\n} from '../../../utils/index.ts';\nimport { EmbraceInstrumentationBase } from '../../EmbraceInstrumentationBase/index.ts';\nimport type { SpanSessionVisibilityInstrumentationArgs } from './types.ts';\n\nconst SESSION_INTERACTION_EVENTS = ['mousedown'];\n\nexport class SpanSessionVisibilityInstrumentation extends EmbraceInstrumentationBase {\n  private _currentVisibilityState: DocumentVisibilityState;\n  private _checkVisibilityTimeout: TimeoutRef | null;\n  private _interactionSinceLastVisibilityChange: boolean;\n  private readonly _avoidEndingLimitedSessions: boolean;\n  private readonly _embraceSpanProcessor?: EmbraceSessionBatchedSpanProcessor;\n  private readonly _checkVisibilityChange: () => void;\n  private readonly _onVisibilityChange: () => void;\n  private readonly _onInteractionThrottled: () => void;\n\n  public constructor(\n    {\n      diag,\n      perf,\n      visibilityWaitTimeMs = 0,\n      limitedSessionMaxDurationMs = 5000,\n      backgroundSessions = false,\n      maxPendingSpanCount = 5,\n      visibilityDoc = window.document,\n    }: SpanSessionVisibilityInstrumentationArgs = {},\n    featureManager: SDKFeaturesManager,\n    embraceSpanProcessor?: EmbraceSessionBatchedSpanProcessor,\n  ) {\n    super({\n      instrumentationName: 'SpanSessionVisibilityInstrumentation',\n      instrumentationVersion: '1.0.0',\n      diag,\n      perf,\n      config: {},\n    });\n\n    this._currentVisibilityState = visibilityDoc.visibilityState;\n    this._checkVisibilityTimeout = null;\n    this._interactionSinceLastVisibilityChange = false;\n    this._avoidEndingLimitedSessions =\n      limitedSessionMaxDurationMs > 0 &&\n      featureManager.isEmptySessionAvoidanceEnabled();\n    this._embraceSpanProcessor = embraceSpanProcessor;\n\n    this._checkVisibilityChange = () => {\n      if (visibilityWaitTimeMs <= 0) {\n        // If no timeout configured, events are forwarded directly.\n        this._currentVisibilityState = visibilityDoc.visibilityState;\n        this._onVisibilityChange();\n        return;\n      }\n      if (this._checkVisibilityTimeout) {\n        clearTimeout(this._checkVisibilityTimeout);\n      }\n\n      // When switching to visible, we want to trigger the event immediately\n      if (\n        visibilityDoc.visibilityState === 'visible' &&\n        this._currentVisibilityState !== visibilityDoc.visibilityState\n      ) {\n        this._currentVisibilityState = visibilityDoc.visibilityState;\n        this._onVisibilityChange();\n        return;\n      }\n\n      this._diag.debug(\n        `Visibility changed to ${visibilityDoc.visibilityState}. Will wait ${(visibilityWaitTimeMs / 1000).toString()}s, and check if visibility changed`,\n      );\n      this._checkVisibilityTimeout = setTimeout(() => {\n        if (this._currentVisibilityState !== visibilityDoc.visibilityState) {\n          this._currentVisibilityState = visibilityDoc.visibilityState;\n          this._onVisibilityChange();\n        } else {\n          this._diag.debug(\n            `Visibility was not changed after timeout happened: ${visibilityDoc.visibilityState}`,\n          );\n        }\n      }, visibilityWaitTimeMs);\n    };\n\n    this._onVisibilityChange = () => {\n      this._diag.debug(\n        `Visibility change detected: ${visibilityDoc.visibilityState}`,\n      );\n\n      const currentSessionStartTime = this.sessionManager.getSessionStartTime();\n\n      // A limited session is one that is:\n      // - shorter than a specified duration threshold\n      // - contains no user interactions\n      // - Embrace is enabled and the amount of pending spans is less than MAX_PENDING_SPAN_COUNT\n      const isLimitedSession =\n        this._avoidEndingLimitedSessions &&\n        currentSessionStartTime !== null &&\n        this.perf.millisSinceHRTime(currentSessionStartTime) <\n          limitedSessionMaxDurationMs &&\n        !this._interactionSinceLastVisibilityChange &&\n        !!this._embraceSpanProcessor &&\n        this._embraceSpanProcessor.getPendingSpansCount() < maxPendingSpanCount;\n\n      if (isLimitedSession) {\n        this._diag.debug(\n          'Not ending the session since it is considered limited',\n        );\n        // If this session still meets the definition of a limited session don't yet end it but instead just record\n        // the visibility change as a breadcrumb\n        this.sessionManager.addBreadcrumb(\n          `Tab visibility changed to ${visibilityDoc.visibilityState}`,\n        );\n\n        const sessionId = this.sessionManager.getSessionId();\n        if (sessionId) {\n          const sessionSpan =\n            this.sessionManager.currentSessionAsReadableSpan('state_changed');\n          if (sessionSpan) {\n            this._embraceSpanProcessor.storePendingSpans(\n              sessionId,\n              sessionSpan,\n            );\n          }\n        }\n      } else {\n        // If there was a session in progress that we didn't end because we considered it limited, then drop the stored spans in storage:\n        const sessionId = this.sessionManager.getSessionId();\n        if (this._embraceSpanProcessor && sessionId) {\n          this._embraceSpanProcessor.clearStoredSpans(sessionId);\n        }\n\n        this.sessionManager.endSessionSpanInternal('state_changed');\n\n        if (visibilityDoc.visibilityState === 'hidden' && backgroundSessions) {\n          this._diag.debug(\n            'Starting a session since document visibility switched to hidden and `backgroundSessions` is enabled',\n          );\n          this.sessionManager.startSessionSpan({ reason: 'hidden' });\n        } else if (visibilityDoc.visibilityState === 'visible') {\n          this._diag.debug(\n            'Starting a session since document visibility switched to visible',\n          );\n          this.sessionManager.startSessionSpan({ reason: 'visible' });\n        }\n      }\n\n      this._interactionSinceLastVisibilityChange = false;\n    };\n\n    this._onInteractionThrottled = throttle(() => {\n      this._interactionSinceLastVisibilityChange = true;\n    }, 1000);\n\n    if (this._config.enabled) {\n      this.enable();\n    }\n  }\n\n  public disable(): void {\n    window.removeEventListener('visibilitychange', this._checkVisibilityChange);\n\n    if (this._avoidEndingLimitedSessions) {\n      bulkRemoveEventListener({\n        target: window,\n        events: SESSION_INTERACTION_EVENTS,\n        callback: this._onInteractionThrottled,\n      });\n    }\n  }\n\n  public enable(): void {\n    window.addEventListener('visibilitychange', this._checkVisibilityChange);\n\n    if (this._avoidEndingLimitedSessions) {\n      bulkAddEventListener({\n        target: window,\n        events: SESSION_INTERACTION_EVENTS,\n        callback: this._onInteractionThrottled,\n      });\n    }\n  }\n}\n"],"mappings":";;;;;;AAWA,MAAM,6BAA6B,CAAC,YAAY;AAEhD,IAAa,uCAAb,cAA0DA,+EAAAA,2BAA2B;CACnF;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YACE,EACE,MACA,MACA,uBAAuB,GACvB,8BAA8B,KAC9B,qBAAqB,OACrB,sBAAsB,GACtB,gBAAgB,OAAO,aACqB,EAAE,EAChD,gBACA,sBACA;AACA,QAAM;GACJ,qBAAqB;GACrB,wBAAwB;GACxB;GACA;GACA,QAAQ,EAAE;GACX,CAAC;AAEF,OAAK,0BAA0B,cAAc;AAC7C,OAAK,0BAA0B;AAC/B,OAAK,wCAAwC;AAC7C,OAAK,8BACH,8BAA8B,KAC9B,eAAe,gCAAgC;AACjD,OAAK,wBAAwB;AAE7B,OAAK,+BAA+B;AAClC,OAAI,wBAAwB,GAAG;AAE7B,SAAK,0BAA0B,cAAc;AAC7C,SAAK,qBAAqB;AAC1B;;AAEF,OAAI,KAAK,wBACP,cAAa,KAAK,wBAAwB;AAI5C,OACE,cAAc,oBAAoB,aAClC,KAAK,4BAA4B,cAAc,iBAC/C;AACA,SAAK,0BAA0B,cAAc;AAC7C,SAAK,qBAAqB;AAC1B;;AAGF,QAAK,MAAM,MACT,yBAAyB,cAAc,gBAAgB,eAAe,uBAAuB,KAAM,UAAU,CAAC,oCAC/G;AACD,QAAK,0BAA0B,iBAAiB;AAC9C,QAAI,KAAK,4BAA4B,cAAc,iBAAiB;AAClE,UAAK,0BAA0B,cAAc;AAC7C,UAAK,qBAAqB;UAE1B,MAAK,MAAM,MACT,sDAAsD,cAAc,kBACrE;MAEF,qBAAqB;;AAG1B,OAAK,4BAA4B;AAC/B,QAAK,MAAM,MACT,+BAA+B,cAAc,kBAC9C;GAED,MAAM,0BAA0B,KAAK,eAAe,qBAAqB;AAezE,OARE,KAAK,+BACL,4BAA4B,QAC5B,KAAK,KAAK,kBAAkB,wBAAwB,GAClD,+BACF,CAAC,KAAK,yCACN,CAAC,CAAC,KAAK,yBACP,KAAK,sBAAsB,sBAAsB,GAAG,qBAEhC;AACpB,SAAK,MAAM,MACT,wDACD;AAGD,SAAK,eAAe,cAClB,6BAA6B,cAAc,kBAC5C;IAED,MAAM,YAAY,KAAK,eAAe,cAAc;AACpD,QAAI,WAAW;KACb,MAAM,cACJ,KAAK,eAAe,6BAA6B,gBAAgB;AACnE,SAAI,YACF,MAAK,sBAAsB,kBACzB,WACA,YACD;;UAGA;IAEL,MAAM,YAAY,KAAK,eAAe,cAAc;AACpD,QAAI,KAAK,yBAAyB,UAChC,MAAK,sBAAsB,iBAAiB,UAAU;AAGxD,SAAK,eAAe,uBAAuB,gBAAgB;AAE3D,QAAI,cAAc,oBAAoB,YAAY,oBAAoB;AACpE,UAAK,MAAM,MACT,sGACD;AACD,UAAK,eAAe,iBAAiB,EAAE,QAAQ,UAAU,CAAC;eACjD,cAAc,oBAAoB,WAAW;AACtD,UAAK,MAAM,MACT,mEACD;AACD,UAAK,eAAe,iBAAiB,EAAE,QAAQ,WAAW,CAAC;;;AAI/D,QAAK,wCAAwC;;AAG/C,OAAK,0BAA0BC,uBAAAA,eAAe;AAC5C,QAAK,wCAAwC;KAC5C,IAAK;AAER,MAAI,KAAK,QAAQ,QACf,MAAK,QAAQ;;CAIjB,UAAuB;AACrB,SAAO,oBAAoB,oBAAoB,KAAK,uBAAuB;AAE3E,MAAI,KAAK,4BACP,+DAAA,wBAAwB;GACtB,QAAQ;GACR,QAAQ;GACR,UAAU,KAAK;GAChB,CAAC;;CAIN,SAAsB;AACpB,SAAO,iBAAiB,oBAAoB,KAAK,uBAAuB;AAExE,MAAI,KAAK,4BACP,yDAAA,qBAAqB;GACnB,QAAQ;GACR,QAAQ;GACR,UAAU,KAAK;GAChB,CAAC"}