@@ -16,7 +16,6 @@ import type {
1616 EventBuffer ,
1717 InternalEventContext ,
1818 PopEventContext ,
19- RecordingEvent ,
2019 RecordingOptions ,
2120 ReplayContainer as ReplayContainerInterface ,
2221 ReplayPluginOptions ,
@@ -30,6 +29,7 @@ import { createBreadcrumb } from './util/createBreadcrumb';
3029import { createPerformanceEntries } from './util/createPerformanceEntries' ;
3130import { createPerformanceSpans } from './util/createPerformanceSpans' ;
3231import { debounce } from './util/debounce' ;
32+ import { getHandleRecordingEmit } from './util/handleRecordingEmit' ;
3333import { isExpired } from './util/isExpired' ;
3434import { isSessionExpired } from './util/isSessionExpired' ;
3535import { overwriteRecordDroppedEvent , restoreRecordDroppedEvent } from './util/monkeyPatchRecordDroppedEvent' ;
@@ -155,7 +155,7 @@ export class ReplayContainer implements ReplayContainerInterface {
155155 * _performanceObserver, Recording, Sentry SDK, etc)
156156 */
157157 public start ( ) : void {
158- this . _setInitialState ( ) ;
158+ this . setInitialState ( ) ;
159159
160160 if ( ! this . _loadAndCheckSession ( ) ) {
161161 return ;
@@ -207,7 +207,7 @@ export class ReplayContainer implements ReplayContainerInterface {
207207 // Without this, it would record forever, until an error happens, which we don't want
208208 // instead, we'll always keep the last 60 seconds of replay before an error happened
209209 ...( this . recordingMode === 'error' && { checkoutEveryNms : ERROR_CHECKOUT_TIME } ) ,
210- emit : this . _handleRecordingEmit ,
210+ emit : getHandleRecordingEmit ( this ) ,
211211 } ) ;
212212 } catch ( err ) {
213213 this . _handleException ( err ) ;
@@ -403,6 +403,25 @@ export class ReplayContainer implements ReplayContainerInterface {
403403 return false ;
404404 }
405405
406+ /**
407+ * Capture some initial state that can change throughout the lifespan of the
408+ * replay. This is required because otherwise they would be captured at the
409+ * first flush.
410+ */
411+ public setInitialState ( ) : void {
412+ const urlPath = `${ WINDOW . location . pathname } ${ WINDOW . location . hash } ${ WINDOW . location . search } ` ;
413+ const url = `${ WINDOW . location . origin } ${ urlPath } ` ;
414+
415+ this . performanceEvents = [ ] ;
416+
417+ // Reset _context as well
418+ this . _clearContext ( ) ;
419+
420+ this . _context . initialUrl = url ;
421+ this . _context . initialTimestamp = new Date ( ) . getTime ( ) ;
422+ this . _context . urls . push ( url ) ;
423+ }
424+
406425 /** A wrapper to conditionally capture exceptions. */
407426 private _handleException ( error : unknown ) : void {
408427 __DEBUG_BUILD__ && logger . error ( '[Replay]' , error ) ;
@@ -428,7 +447,7 @@ export class ReplayContainer implements ReplayContainerInterface {
428447 // If session was newly created (i.e. was not loaded from storage), then
429448 // enable flag to create the root replay
430449 if ( type === 'new' ) {
431- this . _setInitialState ( ) ;
450+ this . setInitialState ( ) ;
432451 }
433452
434453 const currentSessionId = this . getSessionId ( ) ;
@@ -446,25 +465,6 @@ export class ReplayContainer implements ReplayContainerInterface {
446465 return true ;
447466 }
448467
449- /**
450- * Capture some initial state that can change throughout the lifespan of the
451- * replay. This is required because otherwise they would be captured at the
452- * first flush.
453- */
454- private _setInitialState ( ) : void {
455- const urlPath = `${ WINDOW . location . pathname } ${ WINDOW . location . hash } ${ WINDOW . location . search } ` ;
456- const url = `${ WINDOW . location . origin } ${ urlPath } ` ;
457-
458- this . performanceEvents = [ ] ;
459-
460- // Reset _context as well
461- this . _clearContext ( ) ;
462-
463- this . _context . initialUrl = url ;
464- this . _context . initialTimestamp = new Date ( ) . getTime ( ) ;
465- this . _context . urls . push ( url ) ;
466- }
467-
468468 /**
469469 * Adds listeners to record events for the replay
470470 */
@@ -516,72 +516,6 @@ export class ReplayContainer implements ReplayContainerInterface {
516516 }
517517 }
518518
519- /**
520- * Handler for recording events.
521- *
522- * Adds to event buffer, and has varying flushing behaviors if the event was a checkout.
523- */
524- private _handleRecordingEmit : ( event : RecordingEvent , isCheckout ?: boolean ) => void = (
525- event : RecordingEvent ,
526- isCheckout ?: boolean ,
527- ) => {
528- // If this is false, it means session is expired, create and a new session and wait for checkout
529- if ( ! this . checkAndHandleExpiredSession ( ) ) {
530- __DEBUG_BUILD__ && logger . warn ( '[Replay] Received replay event after session expired.' ) ;
531-
532- return ;
533- }
534-
535- this . addUpdate ( ( ) => {
536- // The session is always started immediately on pageload/init, but for
537- // error-only replays, it should reflect the most recent checkout
538- // when an error occurs. Clear any state that happens before this current
539- // checkout. This needs to happen before `addEvent()` which updates state
540- // dependent on this reset.
541- if ( this . recordingMode === 'error' && event . type === 2 ) {
542- this . _setInitialState ( ) ;
543- }
544-
545- // We need to clear existing events on a checkout, otherwise they are
546- // incremental event updates and should be appended
547- void addEvent ( this , event , isCheckout ) ;
548-
549- // Different behavior for full snapshots (type=2), ignore other event types
550- // See https://github.com/rrweb-io/rrweb/blob/d8f9290ca496712aa1e7d472549480c4e7876594/packages/rrweb/src/types.ts#L16
551- if ( event . type !== 2 ) {
552- return false ;
553- }
554-
555- // If there is a previousSessionId after a full snapshot occurs, then
556- // the replay session was started due to session expiration. The new session
557- // is started before triggering a new checkout and contains the id
558- // of the previous session. Do not immediately flush in this case
559- // to avoid capturing only the checkout and instead the replay will
560- // be captured if they perform any follow-up actions.
561- if ( this . session && this . session . previousSessionId ) {
562- return true ;
563- }
564-
565- // See note above re: session start needs to reflect the most recent
566- // checkout.
567- if ( this . recordingMode === 'error' && this . session && this . _context . earliestEvent ) {
568- this . session . started = this . _context . earliestEvent ;
569- this . _maybeSaveSession ( ) ;
570- }
571-
572- // Flush immediately so that we do not miss the first segment, otherwise
573- // it can prevent loading on the UI. This will cause an increase in short
574- // replays (e.g. opening and closing a tab quickly), but these can be
575- // filtered on the UI.
576- if ( this . recordingMode === 'session' ) {
577- // We want to ensure the worker is ready, as otherwise we'd always send the first event uncompressed
578- void this . flushImmediate ( ) ;
579- }
580-
581- return true ;
582- } ) ;
583- } ;
584-
585519 /**
586520 * Handle when visibility of the page content changes. Opening a new tab will
587521 * cause the state to change to hidden because of content of current page will
0 commit comments