@@ -9,7 +9,7 @@ import { getInfo } from '../../../common/config/info'
9
9
import { getConfiguration } from '../../../common/config/init'
10
10
import { getRuntime } from '../../../common/config/runtime'
11
11
import { FEATURE_NAME } from '../constants'
12
- import { isBrowserScope } from '../../../common/constants/runtime'
12
+ import { initialLocation , isBrowserScope } from '../../../common/constants/runtime'
13
13
import { AggregateBase } from '../../utils/aggregate-base'
14
14
import { warn } from '../../../common/util/console'
15
15
import { now } from '../../../common/timing/now'
@@ -19,6 +19,8 @@ import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants'
19
19
import { EventBuffer } from '../../utils/event-buffer'
20
20
import { applyFnToProps } from '../../../common/util/traverse'
21
21
import { IDEAL_PAYLOAD_SIZE } from '../../../common/constants/agent-constants'
22
+ import { UserActionsAggregator } from './user-actions/user-actions-aggregator'
23
+ import { isIFrameWindow } from '../../../common/dom/iframe'
22
24
23
25
export class Aggregate extends AggregateBase {
24
26
#agentRuntime
@@ -43,6 +45,8 @@ export class Aggregate extends AggregateBase {
43
45
return
44
46
}
45
47
48
+ const preHarvestMethods = [ ]
49
+
46
50
if ( agentInit . page_action . enabled ) {
47
51
registerHandler ( 'api-addPageAction' , ( timestamp , name , attributes ) => {
48
52
this . addEvent ( {
@@ -52,7 +56,6 @@ export class Aggregate extends AggregateBase {
52
56
timeSinceLoad : timestamp / 1000 ,
53
57
actionName : name ,
54
58
referrerUrl : this . referrerUrl ,
55
- currentUrl : cleanURL ( '' + location ) ,
56
59
...( isBrowserScope && {
57
60
browserWidth : window . document . documentElement ?. clientWidth ,
58
61
browserHeight : window . document . documentElement ?. clientHeight
@@ -61,22 +64,53 @@ export class Aggregate extends AggregateBase {
61
64
} , this . featureName , this . ee )
62
65
}
63
66
64
- if ( agentInit . user_actions . enabled ) {
67
+ if ( isBrowserScope && agentInit . user_actions . enabled ) {
68
+ this . userActionAggregator = new UserActionsAggregator ( )
69
+
70
+ this . addUserAction = ( aggregatedUserAction ) => {
71
+ try {
72
+ /** The aggregator process only returns an event when it is "done" aggregating -
73
+ * so we still need to validate that an event was given to this method before we try to add */
74
+ if ( aggregatedUserAction ?. event ) {
75
+ const { target, timeStamp, type } = aggregatedUserAction . event
76
+ this . addEvent ( {
77
+ eventType : 'UserAction' ,
78
+ timestamp : Math . floor ( this . #agentRuntime. timeKeeper . correctRelativeTimestamp ( timeStamp ) ) ,
79
+ action : type ,
80
+ actionCount : aggregatedUserAction . count ,
81
+ duration : aggregatedUserAction . relativeMs [ aggregatedUserAction . relativeMs . length - 1 ] ,
82
+ rageClick : aggregatedUserAction . rageClick ,
83
+ relativeMs : aggregatedUserAction . relativeMs ,
84
+ target : aggregatedUserAction . selectorPath ,
85
+ ...( isIFrameWindow ( window ) && { iframe : true } ) ,
86
+ ...( target ?. id && { targetId : target . id } ) ,
87
+ ...( target ?. tagName && { targetTag : target . tagName } ) ,
88
+ ...( target ?. type && { targetType : target . type } ) ,
89
+ ...( target ?. className && { targetClass : target . className } )
90
+ } )
91
+ }
92
+ } catch ( e ) {
93
+ // do nothing for now
94
+ }
95
+ }
96
+
65
97
registerHandler ( 'ua' , ( evt ) => {
66
- this . addEvent ( {
67
- eventType : 'UserAction' ,
68
- timestamp : Math . floor ( this . #agentRuntime. timeKeeper . correctRelativeTimestamp ( evt . timeStamp ) ) ,
69
- action : evt . type ,
70
- targetId : evt . target ?. id ,
71
- targetTag : evt . target ?. tagName ,
72
- targetType : evt . target ?. type ,
73
- targetClass : evt . target ?. className
74
- } )
98
+ /** the processor will return the previously aggregated event if it has been completed by processing the current event */
99
+ this . addUserAction ( this . userActionAggregator . process ( evt ) )
75
100
} , this . featureName , this . ee )
101
+
102
+ preHarvestMethods . push ( ( options = { } ) => {
103
+ /** send whatever UserActions have been aggregated up to this point
104
+ * if we are in a final harvest. By accessing the aggregationEvent, the aggregation is then force-cleared */
105
+ if ( options . isFinalHarvest ) this . addUserAction ( this . userActionAggregator . aggregationEvent )
106
+ } )
76
107
}
77
108
78
109
this . harvestScheduler = new HarvestScheduler ( 'ins' , { onFinished : ( ...args ) => this . onHarvestFinished ( ...args ) } , this )
79
- this . harvestScheduler . harvest . on ( 'ins' , ( ...args ) => this . onHarvestStarted ( ...args ) )
110
+ this . harvestScheduler . harvest . on ( 'ins' , ( ...args ) => {
111
+ preHarvestMethods . forEach ( fn => fn ( ...args ) )
112
+ return this . onHarvestStarted ( ...args )
113
+ } )
80
114
this . harvestScheduler . startTimer ( this . harvestTimeSeconds , 0 )
81
115
82
116
this . drain ( )
@@ -99,8 +133,9 @@ export class Aggregate extends AggregateBase {
99
133
const defaultEventAttributes = {
100
134
/** should be overridden by the event-specific attributes, but just in case -- set it to now() */
101
135
timestamp : Math . floor ( this . #agentRuntime. timeKeeper . correctRelativeTimestamp ( now ( ) ) ) ,
102
- /** all generic events require a pageUrl */
103
- pageUrl : cleanURL ( getRuntime ( this . agentIdentifier ) . origin )
136
+ /** all generic events require pageUrl(s) */
137
+ pageUrl : cleanURL ( '' + initialLocation ) ,
138
+ currentUrl : cleanURL ( '' + location )
104
139
}
105
140
106
141
const eventAttributes = {
0 commit comments