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' ;
@@ -92,7 +97,7 @@ export interface BrowserTracingOptions extends RequestInstrumentationOptions {
9297 *
9398 * Default: undefined
9499 */
95- _experiments ?: Partial < { enableLongTask : boolean } > ;
100+ _experiments ?: Partial < { enableLongTask : boolean ; enableInteractions : boolean } > ;
96101
97102 /**
98103 * beforeNavigate is called before a pageload/navigation transaction is created and allows users to modify transaction
@@ -125,7 +130,7 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = {
125130 routingInstrumentation : instrumentRoutingWithDefaults ,
126131 startTransactionOnLocationChange : true ,
127132 startTransactionOnPageLoad : true ,
128- _experiments : { enableLongTask : true } ,
133+ _experiments : { enableLongTask : true , enableInteractions : false } ,
129134 ...defaultRequestInstrumentationOptions ,
130135} ;
131136
@@ -152,6 +157,9 @@ export class BrowserTracing implements Integration {
152157
153158 private _getCurrentHub ?: ( ) => Hub ;
154159
160+ private _latestRouteName ?: string ;
161+ private _latestRouteSource ?: TransactionSource ;
162+
155163 public constructor ( _options ?: Partial < BrowserTracingOptions > ) {
156164 this . options = {
157165 ...DEFAULT_BROWSER_TRACING_OPTIONS ,
@@ -189,6 +197,7 @@ export class BrowserTracing implements Integration {
189197 traceXHR,
190198 tracePropagationTargets,
191199 shouldCreateSpanForRequest,
200+ _experiments,
192201 } = this . options ;
193202
194203 instrumentRouting (
@@ -201,6 +210,10 @@ export class BrowserTracing implements Integration {
201210 registerBackgroundTabDetection ( ) ;
202211 }
203212
213+ if ( _experiments ?. enableInteractions ) {
214+ this . _registerInteractionListener ( ) ;
215+ }
216+
204217 instrumentOutgoingRequests ( {
205218 traceFetch,
206219 traceXHR,
@@ -252,6 +265,9 @@ export class BrowserTracing implements Integration {
252265 ? { ...finalContext . metadata , source : 'custom' }
253266 : finalContext . metadata ;
254267
268+ this . _latestRouteName = finalContext . name ;
269+ this . _latestRouteSource = finalContext . metadata ?. source ;
270+
255271 if ( finalContext . sampled === false ) {
256272 __DEBUG_BUILD__ &&
257273 logger . log ( `[Tracing] Will not send ${ finalContext . op } transaction because of beforeNavigate.` ) ;
@@ -277,6 +293,57 @@ export class BrowserTracing implements Integration {
277293
278294 return idleTransaction as Transaction ;
279295 }
296+
297+ /** Start listener for interaction transactions */
298+ private _registerInteractionListener ( ) : void {
299+ let inflightInteractionTransaction : IdleTransaction | undefined ;
300+ const registerInteractionTransaction = ( ) : void => {
301+ const { idleTimeout, finalTimeout, heartbeatInterval } = this . options ;
302+
303+ const op = 'ui.action.click' ;
304+ if ( inflightInteractionTransaction ) {
305+ inflightInteractionTransaction . finish ( ) ;
306+ inflightInteractionTransaction = undefined ;
307+ }
308+
309+ if ( ! this . _getCurrentHub ) {
310+ __DEBUG_BUILD__ && logger . warn ( `[Tracing] Did not create ${ op } transaction because _getCurrentHub is invalid.` ) ;
311+ return undefined ;
312+ }
313+
314+ if ( ! this . _latestRouteName ) {
315+ __DEBUG_BUILD__ &&
316+ logger . warn ( `[Tracing] Did not create ${ op } transaction because _latestRouteName is missing.` ) ;
317+ return undefined ;
318+ }
319+
320+ const hub = this . _getCurrentHub ( ) ;
321+ const { location } = WINDOW ;
322+
323+ const context : TransactionContext = {
324+ name : this . _latestRouteName ,
325+ op,
326+ trimEnd : true ,
327+ metadata : {
328+ source : this . _latestRouteSource ?? 'url' ,
329+ } ,
330+ } ;
331+
332+ inflightInteractionTransaction = startIdleTransaction (
333+ hub ,
334+ context ,
335+ idleTimeout ,
336+ finalTimeout ,
337+ true ,
338+ { location } , // for use in the tracesSampler
339+ heartbeatInterval ,
340+ ) ;
341+ } ;
342+
343+ [ 'click' ] . forEach ( type => {
344+ addEventListener ( type , registerInteractionTransaction , { once : false , capture : true } ) ;
345+ } ) ;
346+ }
280347}
281348
282349/** Returns the value of a meta tag */
0 commit comments