11/* eslint-disable max-lines */ // TODO: We might want to split this file up
2- import { addGlobalEventProcessor , captureException , getCurrentHub } from '@sentry/core' ;
2+ import { captureException } from '@sentry/core' ;
33import type { Breadcrumb , ReplayRecordingMode } from '@sentry/types' ;
44import type { RateLimits } from '@sentry/utils' ;
5- import { addInstrumentationHandler , disabledUntil , logger } from '@sentry/utils' ;
5+ import { disabledUntil , logger } from '@sentry/utils' ;
66import { EventType , record } from 'rrweb' ;
77
88import { MAX_SESSION_LIFE , SESSION_IDLE_DURATION , VISIBILITY_CHANGE_TIMEOUT , WINDOW } from './constants' ;
9- import { breadcrumbHandler } from './coreHandlers/breadcrumbHandler' ;
10- import { handleFetchSpanListener } from './coreHandlers/handleFetch' ;
11- import { handleGlobalEventListener } from './coreHandlers/handleGlobalEvent' ;
12- import { handleHistorySpanListener } from './coreHandlers/handleHistory' ;
13- import { handleXhrSpanListener } from './coreHandlers/handleXhr' ;
149import { setupPerformanceObserver } from './coreHandlers/performanceObserver' ;
1510import { createEventBuffer } from './eventBuffer' ;
1611import { getSession } from './session/getSession' ;
@@ -20,7 +15,6 @@ import type {
2015 AddUpdateCallback ,
2116 AllPerformanceEntry ,
2217 EventBuffer ,
23- InstrumentationTypeBreadcrumb ,
2418 InternalEventContext ,
2519 PopEventContext ,
2620 RecordingEvent ,
@@ -30,6 +24,7 @@ import type {
3024 Session ,
3125} from './types' ;
3226import { addEvent } from './util/addEvent' ;
27+ import { addGlobalListeners } from './util/addGlobalListeners' ;
3328import { addMemoryEntry } from './util/addMemoryEntry' ;
3429import { createBreadcrumb } from './util/createBreadcrumb' ;
3530import { createPerformanceEntries } from './util/createPerformanceEntries' ;
@@ -318,7 +313,7 @@ export class ReplayContainer implements ReplayContainerInterface {
318313 }
319314
320315 // Otherwise... recording was never suspended, continue as normalish
321- this . _checkAndHandleExpiredSession ( ) ;
316+ this . checkAndHandleExpiredSession ( ) ;
322317
323318 this . _updateSessionActivity ( ) ;
324319 }
@@ -340,6 +335,44 @@ export class ReplayContainer implements ReplayContainerInterface {
340335 return this . session && this . session . id ;
341336 }
342337
338+ /**
339+ * Checks if recording should be stopped due to user inactivity. Otherwise
340+ * check if session is expired and create a new session if so. Triggers a new
341+ * full snapshot on new session.
342+ *
343+ * Returns true if session is not expired, false otherwise.
344+ * @hidden
345+ */
346+ public checkAndHandleExpiredSession ( { expiry = SESSION_IDLE_DURATION } : { expiry ?: number } = { } ) : boolean | void {
347+ const oldSessionId = this . getSessionId ( ) ;
348+
349+ // Prevent starting a new session if the last user activity is older than
350+ // MAX_SESSION_LIFE. Otherwise non-user activity can trigger a new
351+ // session+recording. This creates noisy replays that do not have much
352+ // content in them.
353+ if ( this . _lastActivity && isExpired ( this . _lastActivity , MAX_SESSION_LIFE ) ) {
354+ // Pause recording
355+ this . pause ( ) ;
356+ return ;
357+ }
358+
359+ // --- There is recent user activity --- //
360+ // This will create a new session if expired, based on expiry length
361+ this . _loadSession ( { expiry } ) ;
362+
363+ // Session was expired if session ids do not match
364+ const expired = oldSessionId !== this . getSessionId ( ) ;
365+
366+ if ( ! expired ) {
367+ return true ;
368+ }
369+
370+ // Session is expired, trigger a full snapshot (which will create a new session)
371+ this . _triggerFullSnapshot ( ) ;
372+
373+ return false ;
374+ }
375+
343376 /** A wrapper to conditionally capture exceptions. */
344377 private _handleException ( error : unknown ) : void {
345378 __DEBUG_BUILD__ && logger . error ( '[Replay]' , error ) ;
@@ -409,19 +442,7 @@ export class ReplayContainer implements ReplayContainerInterface {
409442
410443 // There is no way to remove these listeners, so ensure they are only added once
411444 if ( ! this . _hasInitializedCoreListeners ) {
412- // Listeners from core SDK //
413- const scope = getCurrentHub ( ) . getScope ( ) ;
414- if ( scope ) {
415- scope . addScopeListener ( this . _handleCoreBreadcrumbListener ( 'scope' ) ) ;
416- }
417- addInstrumentationHandler ( 'dom' , this . _handleCoreBreadcrumbListener ( 'dom' ) ) ;
418- addInstrumentationHandler ( 'fetch' , handleFetchSpanListener ( this ) ) ;
419- addInstrumentationHandler ( 'xhr' , handleXhrSpanListener ( this ) ) ;
420- addInstrumentationHandler ( 'history' , handleHistorySpanListener ( this ) ) ;
421-
422- // Tag all (non replay) events that get sent to Sentry with the current
423- // replay ID so that we can reference them later in the UI
424- addGlobalEventProcessor ( handleGlobalEventListener ( this ) ) ;
445+ addGlobalListeners ( this ) ;
425446
426447 this . _hasInitializedCoreListeners = true ;
427448 }
@@ -468,7 +489,7 @@ export class ReplayContainer implements ReplayContainerInterface {
468489 isCheckout ?: boolean ,
469490 ) => {
470491 // If this is false, it means session is expired, create and a new session and wait for checkout
471- if ( ! this . _checkAndHandleExpiredSession ( ) ) {
492+ if ( ! this . checkAndHandleExpiredSession ( ) ) {
472493 __DEBUG_BUILD__ && logger . error ( '[Replay] Received replay event after session expired.' ) ;
473494
474495 return ;
@@ -563,51 +584,6 @@ export class ReplayContainer implements ReplayContainerInterface {
563584 this . _doChangeToForegroundTasks ( breadcrumb ) ;
564585 } ;
565586
566- /**
567- * Handler for Sentry Core SDK events.
568- *
569- * These events will create breadcrumb-like objects in the recording.
570- */
571- private _handleCoreBreadcrumbListener : ( type : InstrumentationTypeBreadcrumb ) => ( handlerData : unknown ) => void =
572- ( type : InstrumentationTypeBreadcrumb ) =>
573- ( handlerData : unknown ) : void => {
574- if ( ! this . _isEnabled ) {
575- return ;
576- }
577-
578- const result = breadcrumbHandler ( type , handlerData ) ;
579-
580- if ( result === null ) {
581- return ;
582- }
583-
584- if ( result . category === 'sentry.transaction' ) {
585- return ;
586- }
587-
588- if ( result . category === 'ui.click' ) {
589- this . triggerUserActivity ( ) ;
590- } else {
591- this . _checkAndHandleExpiredSession ( ) ;
592- }
593-
594- this . addUpdate ( ( ) => {
595- void addEvent ( this , {
596- type : EventType . Custom ,
597- // TODO: We were converting from ms to seconds for breadcrumbs, spans,
598- // but maybe we should just keep them as milliseconds
599- timestamp : ( result . timestamp || 0 ) * 1000 ,
600- data : {
601- tag : 'breadcrumb' ,
602- payload : result ,
603- } ,
604- } ) ;
605-
606- // Do not flush after console log messages
607- return result . category === 'console' ;
608- } ) ;
609- } ;
610-
611587 /**
612588 * Tasks to run when we consider a page to be hidden (via blurring and/or visibility)
613589 */
@@ -636,7 +612,7 @@ export class ReplayContainer implements ReplayContainerInterface {
636612 return ;
637613 }
638614
639- const isSessionActive = this . _checkAndHandleExpiredSession ( {
615+ const isSessionActive = this . checkAndHandleExpiredSession ( {
640616 expiry : VISIBILITY_CHANGE_TIMEOUT ,
641617 } ) ;
642618
@@ -707,43 +683,6 @@ export class ReplayContainer implements ReplayContainerInterface {
707683 return Promise . all ( createPerformanceSpans ( this , createPerformanceEntries ( entries ) ) ) ;
708684 }
709685
710- /**
711- * Checks if recording should be stopped due to user inactivity. Otherwise
712- * check if session is expired and create a new session if so. Triggers a new
713- * full snapshot on new session.
714- *
715- * Returns true if session is not expired, false otherwise.
716- */
717- private _checkAndHandleExpiredSession ( { expiry = SESSION_IDLE_DURATION } : { expiry ?: number } = { } ) : boolean | void {
718- const oldSessionId = this . getSessionId ( ) ;
719-
720- // Prevent starting a new session if the last user activity is older than
721- // MAX_SESSION_LIFE. Otherwise non-user activity can trigger a new
722- // session+recording. This creates noisy replays that do not have much
723- // content in them.
724- if ( this . _lastActivity && isExpired ( this . _lastActivity , MAX_SESSION_LIFE ) ) {
725- // Pause recording
726- this . pause ( ) ;
727- return ;
728- }
729-
730- // --- There is recent user activity --- //
731- // This will create a new session if expired, based on expiry length
732- this . _loadSession ( { expiry } ) ;
733-
734- // Session was expired if session ids do not match
735- const expired = oldSessionId !== this . getSessionId ( ) ;
736-
737- if ( ! expired ) {
738- return true ;
739- }
740-
741- // Session is expired, trigger a full snapshot (which will create a new session)
742- this . _triggerFullSnapshot ( ) ;
743-
744- return false ;
745- }
746-
747686 /**
748687 * Only flush if `this.recordingMode === 'session'`
749688 */
@@ -862,7 +801,7 @@ export class ReplayContainer implements ReplayContainerInterface {
862801 return ;
863802 }
864803
865- if ( ! this . _checkAndHandleExpiredSession ( ) ) {
804+ if ( ! this . checkAndHandleExpiredSession ( ) ) {
866805 __DEBUG_BUILD__ && logger . error ( '[Replay] Attempting to finish replay event after session expired.' ) ;
867806 return ;
868807 }
0 commit comments