11/* eslint-disable max-lines */
22import { Hub } from '@sentry/core' ;
3- import { EventProcessor , Integration , Transaction , TransactionContext } from '@sentry/types' ;
3+ import { EventProcessor , Integration , Transaction , TransactionContext , TransactionSource } from '@sentry/types' ;
44import { baggageHeaderToDynamicSamplingContext , getDomElement , logger } from '@sentry/utils' ;
55
66import { startIdleTransaction } from '../hubextensions' ;
7- import { DEFAULT_FINAL_TIMEOUT , DEFAULT_HEARTBEAT_INTERVAL , DEFAULT_IDLE_TIMEOUT } from '../idletransaction' ;
7+ import {
8+ DEFAULT_FINAL_TIMEOUT ,
9+ DEFAULT_HEARTBEAT_INTERVAL ,
10+ DEFAULT_IDLE_TIMEOUT ,
11+ IdleTransaction ,
12+ } from '../idletransaction' ;
813import { extractTraceparentData } from '../utils' ;
914import { registerBackgroundTabDetection } from './backgroundtab' ;
1015import { addPerformanceEntries , startTrackingLongTasks , startTrackingWebVitals } from './metrics' ;
@@ -87,7 +92,7 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
8792 *
8893 * Default: undefined
8994 */
90- _experiments ?: Partial < { enableLongTask : boolean } > ;
95+ _experiments ?: Partial < { enableLongTask : boolean ; interactionSampleRate : number } > ;
9196
9297 /**
9398 * beforeNavigate is called before a pageload/navigation transaction is created and allows users to modify transaction
@@ -120,7 +125,7 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = {
120125 routingInstrumentation : instrumentRoutingWithDefaults ,
121126 startTransactionOnLocationChange : true ,
122127 startTransactionOnPageLoad : true ,
123- _experiments : { enableLongTask : true } ,
128+ _experiments : { enableLongTask : true , interactionSampleRate : 0.0 } ,
124129 ...defaultRequestInstrumentationOptions ,
125130} ;
126131
@@ -147,6 +152,9 @@ export class BrowserTracing implements Integration {
147152
148153 private _getCurrentHub ?: ( ) => Hub ;
149154
155+ private _latestRouteName ?: string ;
156+ private _latestRouteSource ?: TransactionSource ;
157+
150158 public constructor ( _options ?: Partial < BrowserTracingOptions > ) {
151159 this . options = {
152160 ...DEFAULT_BROWSER_TRACING_OPTIONS ,
@@ -185,6 +193,7 @@ export class BrowserTracing implements Integration {
185193 traceXHR,
186194 tracePropagationTargets,
187195 shouldCreateSpanForRequest,
196+ _experiments,
188197 } = this . options ;
189198
190199 instrumentRouting (
@@ -197,6 +206,10 @@ export class BrowserTracing implements Integration {
197206 registerBackgroundTabDetection ( ) ;
198207 }
199208
209+ if ( _experiments ?. interactionSampleRate ?? 0 > 0 ) {
210+ this . _registerInteractionListener ( _experiments ?. interactionSampleRate ?? 0 ) ;
211+ }
212+
200213 instrumentOutgoingRequests ( {
201214 traceFetch,
202215 traceXHR,
@@ -248,6 +261,9 @@ export class BrowserTracing implements Integration {
248261 ? { ...finalContext . metadata , source : 'custom' }
249262 : finalContext . metadata ;
250263
264+ this . _latestRouteName = finalContext . name ;
265+ this . _latestRouteSource = finalContext . metadata ?. source ;
266+
251267 if ( finalContext . sampled === false ) {
252268 __DEBUG_BUILD__ &&
253269 logger . log ( `[Tracing] Will not send ${ finalContext . op } transaction because of beforeNavigate.` ) ;
@@ -273,6 +289,60 @@ export class BrowserTracing implements Integration {
273289
274290 return idleTransaction as Transaction ;
275291 }
292+
293+ /** Start listener for interaction transactions */
294+ private _registerInteractionListener ( interactionSampleRate : number ) : void {
295+ let inflightInteractionTransaction : IdleTransaction | undefined ;
296+ const registerInteractionTransaction = ( ) : void => {
297+ const { idleTimeout, finalTimeout, heartbeatInterval } = this . options ;
298+ if ( Math . random ( ) > interactionSampleRate ) {
299+ return ;
300+ }
301+
302+ const op = 'ui.action.click' ;
303+ if ( inflightInteractionTransaction ) {
304+ inflightInteractionTransaction . finish ( ) ;
305+ inflightInteractionTransaction = undefined ;
306+ }
307+
308+ if ( ! this . _getCurrentHub ) {
309+ __DEBUG_BUILD__ && logger . warn ( `[Tracing] Did not create ${ op } transaction because _getCurrentHub is invalid.` ) ;
310+ return undefined ;
311+ }
312+
313+ if ( ! this . _latestRouteName ) {
314+ __DEBUG_BUILD__ &&
315+ logger . warn ( `[Tracing] Did not create ${ op } transaction because _latestRouteName is missing.` ) ;
316+ return undefined ;
317+ }
318+
319+ const hub = this . _getCurrentHub ( ) ;
320+ const { location } = WINDOW ;
321+
322+ const context : TransactionContext = {
323+ name : this . _latestRouteName ,
324+ op,
325+ trimEnd : true ,
326+ metadata : {
327+ source : this . _latestRouteSource ?? 'url' ,
328+ } ,
329+ } ;
330+
331+ inflightInteractionTransaction = startIdleTransaction (
332+ hub ,
333+ context ,
334+ idleTimeout ,
335+ finalTimeout ,
336+ true ,
337+ { location } , // for use in the tracesSampler
338+ heartbeatInterval ,
339+ ) ;
340+ } ;
341+
342+ [ 'click' ] . forEach ( type => {
343+ addEventListener ( type , registerInteractionTransaction , { once : false , capture : true } ) ;
344+ } ) ;
345+ }
276346}
277347
278348/** Returns the value of a meta tag */
0 commit comments