@@ -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 onMutation : ( mutations : unknown [ ] ) => {
212212 if ( this . _options . _experiments . captureMutationSize ) {
213213 const count = mutations . length ;
@@ -420,6 +420,25 @@ export class ReplayContainer implements ReplayContainerInterface {
420420 return false ;
421421 }
422422
423+ /**
424+ * Capture some initial state that can change throughout the lifespan of the
425+ * replay. This is required because otherwise they would be captured at the
426+ * first flush.
427+ */
428+ public setInitialState ( ) : void {
429+ const urlPath = `${ WINDOW . location . pathname } ${ WINDOW . location . hash } ${ WINDOW . location . search } ` ;
430+ const url = `${ WINDOW . location . origin } ${ urlPath } ` ;
431+
432+ this . performanceEvents = [ ] ;
433+
434+ // Reset _context as well
435+ this . _clearContext ( ) ;
436+
437+ this . _context . initialUrl = url ;
438+ this . _context . initialTimestamp = new Date ( ) . getTime ( ) ;
439+ this . _context . urls . push ( url ) ;
440+ }
441+
423442 /** A wrapper to conditionally capture exceptions. */
424443 private _handleException ( error : unknown ) : void {
425444 __DEBUG_BUILD__ && logger . error ( '[Replay]' , error ) ;
@@ -445,7 +464,7 @@ export class ReplayContainer implements ReplayContainerInterface {
445464 // If session was newly created (i.e. was not loaded from storage), then
446465 // enable flag to create the root replay
447466 if ( type === 'new' ) {
448- this . _setInitialState ( ) ;
467+ this . setInitialState ( ) ;
449468 }
450469
451470 const currentSessionId = this . getSessionId ( ) ;
@@ -463,25 +482,6 @@ export class ReplayContainer implements ReplayContainerInterface {
463482 return true ;
464483 }
465484
466- /**
467- * Capture some initial state that can change throughout the lifespan of the
468- * replay. This is required because otherwise they would be captured at the
469- * first flush.
470- */
471- private _setInitialState ( ) : void {
472- const urlPath = `${ WINDOW . location . pathname } ${ WINDOW . location . hash } ${ WINDOW . location . search } ` ;
473- const url = `${ WINDOW . location . origin } ${ urlPath } ` ;
474-
475- this . performanceEvents = [ ] ;
476-
477- // Reset _context as well
478- this . _clearContext ( ) ;
479-
480- this . _context . initialUrl = url ;
481- this . _context . initialTimestamp = new Date ( ) . getTime ( ) ;
482- this . _context . urls . push ( url ) ;
483- }
484-
485485 /**
486486 * Adds listeners to record events for the replay
487487 */
@@ -533,72 +533,6 @@ export class ReplayContainer implements ReplayContainerInterface {
533533 }
534534 }
535535
536- /**
537- * Handler for recording events.
538- *
539- * Adds to event buffer, and has varying flushing behaviors if the event was a checkout.
540- */
541- private _handleRecordingEmit : ( event : RecordingEvent , isCheckout ?: boolean ) => void = (
542- event : RecordingEvent ,
543- isCheckout ?: boolean ,
544- ) => {
545- // If this is false, it means session is expired, create and a new session and wait for checkout
546- if ( ! this . checkAndHandleExpiredSession ( ) ) {
547- __DEBUG_BUILD__ && logger . warn ( '[Replay] Received replay event after session expired.' ) ;
548-
549- return ;
550- }
551-
552- this . addUpdate ( ( ) => {
553- // The session is always started immediately on pageload/init, but for
554- // error-only replays, it should reflect the most recent checkout
555- // when an error occurs. Clear any state that happens before this current
556- // checkout. This needs to happen before `addEvent()` which updates state
557- // dependent on this reset.
558- if ( this . recordingMode === 'error' && event . type === 2 ) {
559- this . _setInitialState ( ) ;
560- }
561-
562- // We need to clear existing events on a checkout, otherwise they are
563- // incremental event updates and should be appended
564- void addEvent ( this , event , isCheckout ) ;
565-
566- // Different behavior for full snapshots (type=2), ignore other event types
567- // See https://github.com/rrweb-io/rrweb/blob/d8f9290ca496712aa1e7d472549480c4e7876594/packages/rrweb/src/types.ts#L16
568- if ( event . type !== 2 ) {
569- return false ;
570- }
571-
572- // If there is a previousSessionId after a full snapshot occurs, then
573- // the replay session was started due to session expiration. The new session
574- // is started before triggering a new checkout and contains the id
575- // of the previous session. Do not immediately flush in this case
576- // to avoid capturing only the checkout and instead the replay will
577- // be captured if they perform any follow-up actions.
578- if ( this . session && this . session . previousSessionId ) {
579- return true ;
580- }
581-
582- // See note above re: session start needs to reflect the most recent
583- // checkout.
584- if ( this . recordingMode === 'error' && this . session && this . _context . earliestEvent ) {
585- this . session . started = this . _context . earliestEvent ;
586- this . _maybeSaveSession ( ) ;
587- }
588-
589- // Flush immediately so that we do not miss the first segment, otherwise
590- // it can prevent loading on the UI. This will cause an increase in short
591- // replays (e.g. opening and closing a tab quickly), but these can be
592- // filtered on the UI.
593- if ( this . recordingMode === 'session' ) {
594- // We want to ensure the worker is ready, as otherwise we'd always send the first event uncompressed
595- void this . flushImmediate ( ) ;
596- }
597-
598- return true ;
599- } ) ;
600- } ;
601-
602536 /**
603537 * Handle when visibility of the page content changes. Opening a new tab will
604538 * cause the state to change to hidden because of content of current page will
0 commit comments