@@ -7,6 +7,7 @@ import { logger } from '@sentry/utils';
77import { ERROR_CHECKOUT_TIME , MAX_SESSION_LIFE , SESSION_IDLE_DURATION , WINDOW } from './constants' ;
88import { setupPerformanceObserver } from './coreHandlers/performanceObserver' ;
99import { createEventBuffer } from './eventBuffer' ;
10+ import { clearSession } from './session/clearSession' ;
1011import { getSession } from './session/getSession' ;
1112import { saveSession } from './session/saveSession' ;
1213import type {
@@ -85,6 +86,11 @@ export class ReplayContainer implements ReplayContainerInterface {
8586 */
8687 private _isEnabled : boolean = false ;
8788
89+ /**
90+ * If true, will flush regardless of `_isEnabled` property
91+ */
92+ private _shouldFinalFlush : boolean = false ;
93+
8894 /**
8995 * Paused is a state where:
9096 * - DOM Recording is not listening at all
@@ -237,7 +243,7 @@ export class ReplayContainer implements ReplayContainerInterface {
237243 * Currently, this needs to be manually called (e.g. for tests). Sentry SDK
238244 * does not support a teardown
239245 */
240- public stop ( reason ?: string ) : void {
246+ public async stop ( reason ?: string ) : Promise < void > {
241247 if ( ! this . _isEnabled ) {
242248 return ;
243249 }
@@ -253,14 +259,31 @@ export class ReplayContainer implements ReplayContainerInterface {
253259 log ( msg ) ;
254260 }
255261
262+ // Set this property so that it ignores `_isEnabled` = false
263+ // We can't move `_isEnabled` after awaiting a flush, otherwise we can
264+ // enter into an infinite loop when `stop()` is called while flushing.
265+ this . _shouldFinalFlush = true ;
256266 this . _isEnabled = false ;
257267 this . _removeListeners ( ) ;
258268 this . stopRecording ( ) ;
269+
270+ // Flush event buffer before stopping
271+ await this . flushImmediate ( )
272+
273+ this . _shouldFinalFlush = false ;
274+
275+ // After flush, destroy event buffer
259276 this . eventBuffer && this . eventBuffer . destroy ( ) ;
260277 this . eventBuffer = null ;
261- this . _debouncedFlush . cancel ( ) ;
278+
279+ // Clear session from session storage, note this means if a new session
280+ // is started after, it will not have `previousSessionId`
281+ clearSession ( this ) ;
282+
262283 } catch ( err ) {
263284 this . _handleException ( err ) ;
285+ } finally {
286+ this . _shouldFinalFlush = false ;
264287 }
265288 }
266289
@@ -760,7 +783,7 @@ export class ReplayContainer implements ReplayContainerInterface {
760783 // This means we retried 3 times and all of them failed,
761784 // or we ran into a problem we don't want to retry, like rate limiting.
762785 // In this case, we want to completely stop the replay - otherwise, we may get inconsistent segments
763- this . stop ( 'sendReplay' ) ;
786+ void this . stop ( 'sendReplay' ) ;
764787
765788 const client = getCurrentHub ( ) . getClient ( ) ;
766789
@@ -775,7 +798,7 @@ export class ReplayContainer implements ReplayContainerInterface {
775798 * can be active at a time. Do not call this directly.
776799 */
777800 private _flush : ( ) => Promise < void > = async ( ) => {
778- if ( ! this . _isEnabled ) {
801+ if ( ! this . _isEnabled && ! this . _shouldFinalFlush ) {
779802 // This can happen if e.g. the replay was stopped because of exceeding the retry limit
780803 return ;
781804 }
0 commit comments