@newrelic/browser-agent 1.301.0 → 1.302.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/cjs/common/config/init-types.js +1 -1
- package/dist/cjs/common/config/init.js +7 -1
- package/dist/cjs/common/config/runtime.js +1 -1
- package/dist/cjs/common/constants/agent-constants.js +11 -2
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/drain/drain.js +1 -1
- package/dist/cjs/common/harvest/harvester.js +30 -39
- package/dist/cjs/common/util/mfe.js +45 -0
- package/dist/cjs/features/ajax/aggregate/index.js +5 -1
- package/dist/cjs/features/generic_events/aggregate/index.js +9 -8
- package/dist/cjs/features/generic_events/constants.js +3 -1
- package/dist/cjs/features/generic_events/instrument/index.js +38 -32
- package/dist/cjs/features/jserrors/aggregate/index.js +18 -17
- package/dist/cjs/features/logging/aggregate/index.js +19 -15
- package/dist/cjs/features/logging/shared/utils.js +3 -3
- package/dist/cjs/features/page_view_event/aggregate/index.js +3 -33
- package/dist/cjs/features/session_replay/aggregate/index.js +13 -13
- package/dist/cjs/features/session_replay/shared/recorder.js +3 -2
- package/dist/cjs/features/session_trace/aggregate/index.js +0 -2
- package/dist/cjs/features/soft_navigations/aggregate/index.js +1 -2
- package/dist/cjs/features/utils/aggregate-base.js +45 -47
- package/dist/cjs/loaders/api/addPageAction.js +2 -2
- package/dist/cjs/loaders/api/log.js +2 -2
- package/dist/cjs/loaders/api/noticeError.js +2 -2
- package/dist/cjs/loaders/api/register-api-types.js +5 -5
- package/dist/cjs/loaders/api/register.js +80 -97
- package/dist/esm/common/config/init-types.js +1 -1
- package/dist/esm/common/config/init.js +7 -1
- package/dist/esm/common/config/runtime.js +1 -1
- package/dist/esm/common/constants/agent-constants.js +9 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/drain/drain.js +1 -1
- package/dist/esm/common/harvest/harvester.js +30 -39
- package/dist/esm/common/util/mfe.js +38 -0
- package/dist/esm/features/ajax/aggregate/index.js +5 -1
- package/dist/esm/features/generic_events/aggregate/index.js +9 -8
- package/dist/esm/features/generic_events/constants.js +3 -1
- package/dist/esm/features/generic_events/instrument/index.js +38 -32
- package/dist/esm/features/jserrors/aggregate/index.js +18 -17
- package/dist/esm/features/logging/aggregate/index.js +19 -15
- package/dist/esm/features/logging/shared/utils.js +3 -3
- package/dist/esm/features/page_view_event/aggregate/index.js +3 -33
- package/dist/esm/features/session_replay/aggregate/index.js +13 -13
- package/dist/esm/features/session_replay/shared/recorder.js +3 -2
- package/dist/esm/features/session_trace/aggregate/index.js +0 -2
- package/dist/esm/features/soft_navigations/aggregate/index.js +1 -2
- package/dist/esm/features/utils/aggregate-base.js +46 -48
- package/dist/esm/loaders/api/addPageAction.js +2 -2
- package/dist/esm/loaders/api/log.js +2 -2
- package/dist/esm/loaders/api/noticeError.js +2 -2
- package/dist/esm/loaders/api/register-api-types.js +5 -5
- package/dist/esm/loaders/api/register.js +80 -97
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/config/init-types.d.ts +4 -1
- package/dist/types/common/config/init-types.d.ts.map +1 -1
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/constants/agent-constants.d.ts +7 -4
- package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
- package/dist/types/common/harvest/harvester.d.ts +2 -2
- package/dist/types/common/harvest/harvester.d.ts.map +1 -1
- package/dist/types/common/util/mfe.d.ts +20 -0
- package/dist/types/common/util/mfe.d.ts.map +1 -0
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts +2 -2
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts +1 -0
- package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +3 -3
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts +3 -3
- package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/shared/utils.d.ts +2 -2
- package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -4
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +13 -4
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts +1 -0
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts +13 -5
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/loaders/api/addPageAction.d.ts +1 -1
- package/dist/types/loaders/api/addPageAction.d.ts.map +1 -1
- package/dist/types/loaders/api/log.d.ts +1 -1
- package/dist/types/loaders/api/log.d.ts.map +1 -1
- package/dist/types/loaders/api/noticeError.d.ts +1 -1
- package/dist/types/loaders/api/noticeError.d.ts.map +1 -1
- package/dist/types/loaders/api/register-api-types.d.ts +4 -4
- package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
- package/dist/types/loaders/api/register.d.ts +8 -1
- package/dist/types/loaders/api/register.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/config/init-types.js +1 -1
- package/src/common/config/init.js +3 -1
- package/src/common/config/runtime.js +1 -1
- package/src/common/constants/agent-constants.js +10 -0
- package/src/common/drain/drain.js +1 -1
- package/src/common/harvest/harvester.js +27 -32
- package/src/common/util/mfe.js +35 -0
- package/src/features/ajax/aggregate/index.js +7 -1
- package/src/features/generic_events/aggregate/index.js +9 -8
- package/src/features/generic_events/constants.js +3 -1
- package/src/features/generic_events/instrument/index.js +44 -35
- package/src/features/jserrors/aggregate/index.js +17 -17
- package/src/features/logging/aggregate/index.js +20 -13
- package/src/features/logging/shared/utils.js +3 -3
- package/src/features/page_view_event/aggregate/index.js +3 -28
- package/src/features/session_replay/aggregate/index.js +12 -10
- package/src/features/session_replay/shared/recorder.js +3 -2
- package/src/features/session_trace/aggregate/index.js +0 -2
- package/src/features/soft_navigations/aggregate/index.js +1 -2
- package/src/features/utils/aggregate-base.js +47 -42
- package/src/loaders/api/addPageAction.js +2 -2
- package/src/loaders/api/log.js +2 -2
- package/src/loaders/api/noticeError.js +2 -2
- package/src/loaders/api/register-api-types.js +5 -5
- package/src/loaders/api/register.js +62 -89
- package/dist/cjs/common/util/target.js +0 -34
- package/dist/cjs/features/utils/entity-manager.js +0 -46
- package/dist/cjs/features/utils/event-store-manager.js +0 -174
- package/dist/cjs/loaders/api/register-api.js +0 -165
- package/dist/esm/common/util/target.js +0 -27
- package/dist/esm/features/utils/entity-manager.js +0 -39
- package/dist/esm/features/utils/event-store-manager.js +0 -166
- package/dist/esm/loaders/api/register-api.js +0 -159
- package/dist/types/common/util/target.d.ts +0 -18
- package/dist/types/common/util/target.d.ts.map +0 -1
- package/dist/types/features/utils/entity-manager.d.ts +0 -11
- package/dist/types/features/utils/entity-manager.d.ts.map +0 -1
- package/dist/types/features/utils/event-store-manager.d.ts +0 -85
- package/dist/types/features/utils/event-store-manager.d.ts.map +0 -1
- package/dist/types/loaders/api/register-api.d.ts +0 -14
- package/dist/types/loaders/api/register-api.d.ts.map +0 -1
- package/src/common/util/target.js +0 -27
- package/src/features/utils/entity-manager.js +0 -45
- package/src/features/utils/event-store-manager.js +0 -165
- package/src/loaders/api/register-api.js +0 -152
|
@@ -137,10 +137,16 @@ export class Aggregate extends AggregateBase {
|
|
|
137
137
|
const addString = getAddStringContext(this.agentRef.runtime.obfuscator)
|
|
138
138
|
let payload = 'bel.7;'
|
|
139
139
|
|
|
140
|
+
let firstTimestamp = 0
|
|
141
|
+
|
|
140
142
|
for (let i = 0; i < eventBuffer.length; i++) {
|
|
141
143
|
const event = eventBuffer[i]
|
|
144
|
+
// ajax nodes are relative to the first node (or page origin if no previous node), so we need to calculate the relative start time
|
|
145
|
+
const relativeStartTime = event.startTime - firstTimestamp
|
|
146
|
+
if (i === 0) firstTimestamp = event.startTime
|
|
147
|
+
|
|
142
148
|
const fields = [
|
|
143
|
-
numeric(
|
|
149
|
+
numeric(relativeStartTime),
|
|
144
150
|
numeric(event.endTime - event.startTime),
|
|
145
151
|
numeric(0), // callbackEnd
|
|
146
152
|
numeric(0), // no callbackDuration for non-SPA events
|
|
@@ -14,6 +14,7 @@ import { applyFnToProps } from '../../../common/util/traverse'
|
|
|
14
14
|
import { UserActionsAggregator } from './user-actions/user-actions-aggregator'
|
|
15
15
|
import { isIFrameWindow } from '../../../common/dom/iframe'
|
|
16
16
|
import { isPureObject } from '../../../common/util/type-check'
|
|
17
|
+
import { getVersion2Attributes } from '../../../common/util/mfe'
|
|
17
18
|
|
|
18
19
|
export class Aggregate extends AggregateBase {
|
|
19
20
|
static featureName = FEATURE_NAME
|
|
@@ -42,8 +43,7 @@ export class Aggregate extends AggregateBase {
|
|
|
42
43
|
}, this.featureName, this.ee)
|
|
43
44
|
|
|
44
45
|
if (agentRef.init.page_action.enabled) {
|
|
45
|
-
registerHandler('api-addPageAction', (timestamp, name, attributes,
|
|
46
|
-
if (!this.agentRef.runtime.entityManager.get(targetEntityGuid)) return warn(56, this.featureName)
|
|
46
|
+
registerHandler('api-addPageAction', (timestamp, name, attributes, target) => {
|
|
47
47
|
this.addEvent({
|
|
48
48
|
...attributes,
|
|
49
49
|
eventType: 'PageAction',
|
|
@@ -55,7 +55,7 @@ export class Aggregate extends AggregateBase {
|
|
|
55
55
|
browserWidth: window.document.documentElement?.clientWidth,
|
|
56
56
|
browserHeight: window.document.documentElement?.clientHeight
|
|
57
57
|
})
|
|
58
|
-
},
|
|
58
|
+
}, target)
|
|
59
59
|
}, this.featureName, this.ee)
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -242,7 +242,6 @@ export class Aggregate extends AggregateBase {
|
|
|
242
242
|
this.addEvent(event)
|
|
243
243
|
}, this.featureName, this.ee)
|
|
244
244
|
|
|
245
|
-
agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
246
245
|
this.drain()
|
|
247
246
|
})
|
|
248
247
|
}
|
|
@@ -258,10 +257,10 @@ export class Aggregate extends AggregateBase {
|
|
|
258
257
|
* * sessionTraceId: set by the `ptid=` query param
|
|
259
258
|
* * userAgent*: set by the userAgent header
|
|
260
259
|
* @param {object=} obj the event object for storing in the event buffer
|
|
261
|
-
* @param {string=}
|
|
260
|
+
* @param {string=} target the target metadata for the event to scope buffering and harvesting. Defaults to container agent config if undefined
|
|
262
261
|
* @returns void
|
|
263
262
|
*/
|
|
264
|
-
addEvent (obj = {},
|
|
263
|
+
addEvent (obj = {}, target) {
|
|
265
264
|
if (!obj || !Object.keys(obj).length) return
|
|
266
265
|
if (!obj.eventType) {
|
|
267
266
|
warn(44)
|
|
@@ -278,7 +277,9 @@ export class Aggregate extends AggregateBase {
|
|
|
278
277
|
timestamp: Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(now())),
|
|
279
278
|
/** all generic events require pageUrl(s) */
|
|
280
279
|
pageUrl: cleanURL('' + initialLocation),
|
|
281
|
-
currentUrl: cleanURL('' + location)
|
|
280
|
+
currentUrl: cleanURL('' + location),
|
|
281
|
+
/** Specific attributes only supplied if harvesting to endpoint version 2 */
|
|
282
|
+
...(getVersion2Attributes(target, this))
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
const eventAttributes = {
|
|
@@ -290,7 +291,7 @@ export class Aggregate extends AggregateBase {
|
|
|
290
291
|
...obj
|
|
291
292
|
}
|
|
292
293
|
|
|
293
|
-
this.events.add(eventAttributes
|
|
294
|
+
this.events.add(eventAttributes)
|
|
294
295
|
}
|
|
295
296
|
|
|
296
297
|
serializer (eventBuffer) {
|
|
@@ -17,5 +17,7 @@ export const FRUSTRATION_TIMEOUT_MS = 2000
|
|
|
17
17
|
export const RESERVED_EVENT_TYPES = ['PageAction', 'UserAction', 'BrowserPerformance']
|
|
18
18
|
|
|
19
19
|
export const FEATURE_FLAGS = {
|
|
20
|
-
RESOURCES: 'experimental.resources'
|
|
20
|
+
RESOURCES: 'experimental.resources',
|
|
21
|
+
REGISTER: 'register'
|
|
22
|
+
// register.jserrors and register.generic_events are also used, but not referenced directly so no need to represent here
|
|
21
23
|
}
|
|
@@ -41,6 +41,14 @@ export class Instrument extends InstrumentBase {
|
|
|
41
41
|
setupRegisterAPI(agentRef)
|
|
42
42
|
setupMeasureAPI(agentRef)
|
|
43
43
|
|
|
44
|
+
const ufEnabled = agentRef.init.feature_flags.includes('user_frustrations')
|
|
45
|
+
let historyEE
|
|
46
|
+
if (isBrowserScope && ufEnabled) {
|
|
47
|
+
wrapFetch(this.ee)
|
|
48
|
+
wrapXhr(this.ee)
|
|
49
|
+
historyEE = wrapHistory(this.ee)
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
if (isBrowserScope) {
|
|
45
53
|
if (agentRef.init.user_actions.enabled) {
|
|
46
54
|
OBSERVED_EVENTS.forEach(eventType =>
|
|
@@ -52,7 +60,43 @@ export class Instrument extends InstrumentBase {
|
|
|
52
60
|
}
|
|
53
61
|
// Capture is not used here so that we don't get element focus/blur events, only the window's as they do not bubble. They are also not cancellable, so no worries about being front of line.
|
|
54
62
|
)
|
|
63
|
+
|
|
64
|
+
if (ufEnabled) {
|
|
65
|
+
globalScope.addEventListener('error', () => {
|
|
66
|
+
handle('uaErr', [], undefined, FEATURE_NAMES.genericEvents, this.ee)
|
|
67
|
+
}, eventListenerOpts(false, this.removeOnAbort?.signal))
|
|
68
|
+
|
|
69
|
+
this.ee.on('open-xhr-start', (args, xhr) => {
|
|
70
|
+
if (!isInternalTraffic(args[1])) {
|
|
71
|
+
xhr.addEventListener('readystatechange', () => {
|
|
72
|
+
if (xhr.readyState === 2) { // HEADERS_RECEIVED
|
|
73
|
+
handle('uaXhr', [], undefined, FEATURE_NAMES.genericEvents, this.ee)
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
this.ee.on('fetch-start', (fetchArguments) => {
|
|
79
|
+
if (fetchArguments.length >= 1 && !isInternalTraffic(extractUrl(fetchArguments[0]))) {
|
|
80
|
+
handle('uaXhr', [], undefined, FEATURE_NAMES.genericEvents, this.ee)
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
function isInternalTraffic (url) {
|
|
85
|
+
const parsedUrl = parseUrl(url)
|
|
86
|
+
return agentRef.beacons.includes(parsedUrl.hostname + ':' + parsedUrl.port)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
historyEE.on('pushState-end', navigationChange)
|
|
90
|
+
historyEE.on('replaceState-end', navigationChange)
|
|
91
|
+
window.addEventListener('hashchange', navigationChange, eventListenerOpts(true, this.removeOnAbort?.signal))
|
|
92
|
+
window.addEventListener('popstate', navigationChange, eventListenerOpts(true, this.removeOnAbort?.signal))
|
|
93
|
+
|
|
94
|
+
function navigationChange () {
|
|
95
|
+
historyEE.emit('navChange')
|
|
96
|
+
}
|
|
97
|
+
}
|
|
55
98
|
}
|
|
99
|
+
|
|
56
100
|
if (agentRef.init.performance.resources.enabled && globalScope.PerformanceObserver?.supportedEntryTypes.includes('resource')) {
|
|
57
101
|
const observer = new PerformanceObserver((list) => {
|
|
58
102
|
list.getEntries().forEach(entry => {
|
|
@@ -61,15 +105,6 @@ export class Instrument extends InstrumentBase {
|
|
|
61
105
|
})
|
|
62
106
|
observer.observe({ type: 'resource', buffered: true })
|
|
63
107
|
}
|
|
64
|
-
|
|
65
|
-
const historyEE = wrapHistory(this.ee)
|
|
66
|
-
historyEE.on('pushState-end', navigationChange)
|
|
67
|
-
historyEE.on('replaceState-end', navigationChange)
|
|
68
|
-
window.addEventListener('hashchange', navigationChange, eventListenerOpts(true, this.removeOnAbort?.signal))
|
|
69
|
-
window.addEventListener('popstate', navigationChange, eventListenerOpts(true, this.removeOnAbort?.signal))
|
|
70
|
-
function navigationChange () {
|
|
71
|
-
historyEE.emit('navChange')
|
|
72
|
-
}
|
|
73
108
|
}
|
|
74
109
|
|
|
75
110
|
try {
|
|
@@ -81,32 +116,6 @@ export class Instrument extends InstrumentBase {
|
|
|
81
116
|
this.abortHandler = undefined // weakly allow this abort op to run only once
|
|
82
117
|
}
|
|
83
118
|
|
|
84
|
-
globalScope.addEventListener('error', () => {
|
|
85
|
-
handle('uaErr', [], undefined, FEATURE_NAMES.genericEvents, this.ee)
|
|
86
|
-
}, eventListenerOpts(false, this.removeOnAbort?.signal))
|
|
87
|
-
|
|
88
|
-
wrapFetch(this.ee)
|
|
89
|
-
wrapXhr(this.ee)
|
|
90
|
-
this.ee.on('open-xhr-start', (args, xhr) => {
|
|
91
|
-
if (!isInternalTraffic(args[1])) {
|
|
92
|
-
xhr.addEventListener('readystatechange', () => {
|
|
93
|
-
if (xhr.readyState === 2) { // HEADERS_RECEIVED
|
|
94
|
-
handle('uaXhr', [], undefined, FEATURE_NAMES.genericEvents, this.ee)
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
this.ee.on('fetch-start', (fetchArguments) => {
|
|
100
|
-
if (fetchArguments.length >= 1 && !isInternalTraffic(extractUrl(fetchArguments[0]))) {
|
|
101
|
-
handle('uaXhr', [], undefined, FEATURE_NAMES.genericEvents, this.ee)
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
function isInternalTraffic (url) {
|
|
106
|
-
const parsedUrl = parseUrl(url)
|
|
107
|
-
return agentRef.beacons.includes(parsedUrl.hostname + ':' + parsedUrl.port)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
119
|
/** If any of the sources are active, import the aggregator. otherwise deregister */
|
|
111
120
|
if (genericEventSourceConfigs.some(x => x)) this.importAggregator(agentRef, () => import(/* webpackChunkName: "generic_events-aggregate" */ '../aggregate'))
|
|
112
121
|
else this.deregisterDrain()
|
|
@@ -19,8 +19,7 @@ import { AggregateBase } from '../../utils/aggregate-base'
|
|
|
19
19
|
import { now } from '../../../common/timing/now'
|
|
20
20
|
import { applyFnToProps } from '../../../common/util/traverse'
|
|
21
21
|
import { evaluateInternalError } from './internal-errors'
|
|
22
|
-
import {
|
|
23
|
-
import { warn } from '../../../common/util/console'
|
|
22
|
+
import { getVersion2Attributes } from '../../../common/util/mfe'
|
|
24
23
|
import { buildCauseString } from './cause-string'
|
|
25
24
|
|
|
26
25
|
/**
|
|
@@ -32,6 +31,8 @@ export class Aggregate extends AggregateBase {
|
|
|
32
31
|
constructor (agentRef) {
|
|
33
32
|
super(agentRef, FEATURE_NAME)
|
|
34
33
|
|
|
34
|
+
/** set up agg-level behaviors specific to this feature */
|
|
35
|
+
this.harvestOpts.aggregatorTypes = ['err', 'ierr', 'xhr'] // the types in EventAggregator this feature cares about
|
|
35
36
|
this.stackReported = {}
|
|
36
37
|
this.observedAt = {}
|
|
37
38
|
this.pageviewReported = {}
|
|
@@ -46,8 +47,6 @@ export class Aggregate extends AggregateBase {
|
|
|
46
47
|
register('softNavFlush', (interactionId, wasFinished, softNavAttrs, interactionEndTime) =>
|
|
47
48
|
this.onSoftNavNotification(interactionId, wasFinished, softNavAttrs, interactionEndTime), this.featureName, this.ee) // when an ixn is done or cancelled
|
|
48
49
|
|
|
49
|
-
this.harvestOpts.aggregatorTypes = ['err', 'ierr', 'xhr'] // the types in EventAggregator this feature cares about
|
|
50
|
-
|
|
51
50
|
// 0 == off, 1 == on
|
|
52
51
|
this.waitForFlags(['err']).then(([errFlag]) => {
|
|
53
52
|
if (errFlag) {
|
|
@@ -113,10 +112,8 @@ export class Aggregate extends AggregateBase {
|
|
|
113
112
|
* @param {object=} target the target to buffer and harvest to, if undefined the default configuration target is used
|
|
114
113
|
* @returns
|
|
115
114
|
*/
|
|
116
|
-
storeError (err, time, internal, customAttributes, hasReplay, swallowReason,
|
|
115
|
+
storeError (err, time, internal, customAttributes, hasReplay, swallowReason, target) {
|
|
117
116
|
if (!err) return
|
|
118
|
-
const target = this.agentRef.runtime.entityManager.get(targetEntityGuid)
|
|
119
|
-
if (!target) return warn(56, this.featureName)
|
|
120
117
|
// are we in an interaction
|
|
121
118
|
time = time || now()
|
|
122
119
|
let filterOutput
|
|
@@ -155,7 +152,7 @@ export class Aggregate extends AggregateBase {
|
|
|
155
152
|
if (filterOutput?.group) params.errorGroup = filterOutput.group
|
|
156
153
|
|
|
157
154
|
// Should only decorate "hasReplay" for the container agent, so check if the target matches the config
|
|
158
|
-
if (hasReplay &&
|
|
155
|
+
if (hasReplay && !target) params.hasReplay = hasReplay
|
|
159
156
|
/**
|
|
160
157
|
* The bucketHash is different from the params.stackHash because the params.stackHash is based on the canonicalized
|
|
161
158
|
* stack trace and is used downstream in NR1 to attempt to group the same errors across different browsers. However,
|
|
@@ -191,7 +188,7 @@ export class Aggregate extends AggregateBase {
|
|
|
191
188
|
|
|
192
189
|
// Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
|
|
193
190
|
const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes]
|
|
194
|
-
if (this.shouldAllowMainAgentToCapture(
|
|
191
|
+
if (this.shouldAllowMainAgentToCapture(target)) handle('trace-jserror', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee)
|
|
195
192
|
// still send EE events for other features such as above, but stop this one from aggregating internal data
|
|
196
193
|
if (this.blocked) return
|
|
197
194
|
|
|
@@ -200,7 +197,7 @@ export class Aggregate extends AggregateBase {
|
|
|
200
197
|
params._interactionNodeId = err.__newrelic[this.agentIdentifier].interactionNodeId
|
|
201
198
|
}
|
|
202
199
|
|
|
203
|
-
if (this.shouldAllowMainAgentToCapture(
|
|
200
|
+
if (this.shouldAllowMainAgentToCapture(target)) {
|
|
204
201
|
const softNavInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.softNav])
|
|
205
202
|
// Note: the following are subject to potential race cond wherein if the other feature aren't fully initialized, it'll be treated as there being no associated interaction.
|
|
206
203
|
// They each will also tack on their respective properties to the params object as part of the decision flow.
|
|
@@ -221,12 +218,15 @@ export class Aggregate extends AggregateBase {
|
|
|
221
218
|
}
|
|
222
219
|
|
|
223
220
|
// always add directly if scoped to a sub-entity, the other pathways above will be deterministic if the main agent should procede
|
|
224
|
-
if (
|
|
221
|
+
if (target) this.#storeJserrorForHarvest([...jsErrorEvent, target], false, params._softNavAttributes)
|
|
225
222
|
}
|
|
226
223
|
|
|
227
224
|
#storeJserrorForHarvest (errorInfoArr, softNavOccurredFinished, softNavCustomAttrs = {}) {
|
|
228
|
-
let [type, bucketHash, params, newMetrics, localAttrs,
|
|
229
|
-
const allCustomAttrs = {
|
|
225
|
+
let [type, bucketHash, params, newMetrics, localAttrs, target] = errorInfoArr
|
|
226
|
+
const allCustomAttrs = {
|
|
227
|
+
/** MFE specific attributes if in "multiple" mode (ie consumer version 2) */
|
|
228
|
+
...getVersion2Attributes(target, this)
|
|
229
|
+
}
|
|
230
230
|
|
|
231
231
|
if (softNavOccurredFinished) {
|
|
232
232
|
Object.entries(softNavCustomAttrs).forEach(([k, v]) => setCustom(k, v)) // when an ixn finishes, it'll include stuff in jsAttributes + attrs specific to the ixn
|
|
@@ -243,7 +243,7 @@ export class Aggregate extends AggregateBase {
|
|
|
243
243
|
const jsAttributesHash = stringHashCode(stringify(allCustomAttrs))
|
|
244
244
|
const aggregateHash = bucketHash + ':' + jsAttributesHash
|
|
245
245
|
|
|
246
|
-
this.events.add([type, aggregateHash, params, newMetrics, allCustomAttrs]
|
|
246
|
+
this.events.add([type, aggregateHash, params, newMetrics, allCustomAttrs])
|
|
247
247
|
|
|
248
248
|
function setCustom (key, val) {
|
|
249
249
|
allCustomAttrs[key] = (val && typeof val === 'object' ? stringify(val) : val)
|
|
@@ -253,11 +253,11 @@ export class Aggregate extends AggregateBase {
|
|
|
253
253
|
/**
|
|
254
254
|
* If the event lacks an entityGuid (the default behavior), the main agent should capture the data. If the data is assigned to a sub-entity target
|
|
255
255
|
* the main agent should not capture events unless it is configured to do so.
|
|
256
|
-
* @param {string}
|
|
256
|
+
* @param {string} target - the context object for the event
|
|
257
257
|
* @returns {boolean} - whether the main agent should capture the event to its internal target
|
|
258
258
|
*/
|
|
259
|
-
shouldAllowMainAgentToCapture (
|
|
260
|
-
return (!
|
|
259
|
+
shouldAllowMainAgentToCapture (target) {
|
|
260
|
+
return (!target || this.agentRef.init.api.duplicate_registered_data)
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
// TO-DO: Remove this function when old spa is taken out. #storeJserrorForHarvest handles the work with the softnav feature.
|
|
@@ -10,10 +10,10 @@ import { FEATURE_NAME, LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS, LOGGING_MODE }
|
|
|
10
10
|
import { Log } from '../shared/log'
|
|
11
11
|
import { isValidLogLevel } from '../shared/utils'
|
|
12
12
|
import { applyFnToProps } from '../../../common/util/traverse'
|
|
13
|
-
import { isContainerAgentTarget } from '../../../common/util/target'
|
|
14
13
|
import { SESSION_EVENT_TYPES, SESSION_EVENTS } from '../../../common/session/constants'
|
|
15
14
|
import { ABORT_REASONS } from '../../session_replay/constants'
|
|
16
15
|
import { canEnableSessionTracking } from '../../utils/feature-gates'
|
|
16
|
+
import { getVersion2Attributes } from '../../../common/util/mfe'
|
|
17
17
|
|
|
18
18
|
export class Aggregate extends AggregateBase {
|
|
19
19
|
static featureName = FEATURE_NAME
|
|
@@ -21,6 +21,8 @@ export class Aggregate extends AggregateBase {
|
|
|
21
21
|
super(agentRef, FEATURE_NAME)
|
|
22
22
|
this.isSessionTrackingEnabled = canEnableSessionTracking(agentRef.init) && agentRef.runtime.session
|
|
23
23
|
|
|
24
|
+
/** set up agg-level behaviors specific to this feature */
|
|
25
|
+
this.harvestOpts.raw = true
|
|
24
26
|
super.customAttributesAreSeparate = true
|
|
25
27
|
|
|
26
28
|
// The SessionEntity class can emit a message indicating the session was cleared and reset (expiry, inactivity). This feature must abort and never resume if that occurs.
|
|
@@ -34,7 +36,6 @@ export class Aggregate extends AggregateBase {
|
|
|
34
36
|
else this.loggingMode = data.loggingMode
|
|
35
37
|
})
|
|
36
38
|
|
|
37
|
-
this.harvestOpts.raw = true
|
|
38
39
|
this.waitForFlags(['log']).then(([loggingMode]) => {
|
|
39
40
|
const session = this.agentRef.runtime.session ?? {}
|
|
40
41
|
if (this.loggingMode === LOGGING_MODE.OFF || (session.isNew && loggingMode === LOGGING_MODE.OFF)) {
|
|
@@ -63,11 +64,17 @@ export class Aggregate extends AggregateBase {
|
|
|
63
64
|
})
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO,
|
|
67
|
-
if (!this.agentRef.runtime.entityManager.get(targetEntityGuid)) return warn(56, this.featureName)
|
|
67
|
+
handleLog (timestamp, message, attributes = {}, level = LOG_LEVELS.INFO, target) {
|
|
68
68
|
if (this.blocked || !this.loggingMode) return
|
|
69
69
|
|
|
70
70
|
if (!attributes || typeof attributes !== 'object') attributes = {}
|
|
71
|
+
|
|
72
|
+
attributes = {
|
|
73
|
+
...attributes,
|
|
74
|
+
/** Specific attributes only supplied if harvesting to endpoint version 2 */
|
|
75
|
+
...(getVersion2Attributes(target, this))
|
|
76
|
+
}
|
|
77
|
+
|
|
71
78
|
if (typeof level === 'string') level = level.toUpperCase()
|
|
72
79
|
if (!isValidLogLevel(level)) return warn(30, level)
|
|
73
80
|
if (this.loggingMode < (LOGGING_MODE[level] || Infinity)) {
|
|
@@ -100,25 +107,26 @@ export class Aggregate extends AggregateBase {
|
|
|
100
107
|
level
|
|
101
108
|
)
|
|
102
109
|
|
|
103
|
-
this.events.add(log
|
|
110
|
+
this.events.add(log)
|
|
104
111
|
}
|
|
105
112
|
|
|
106
|
-
serializer (eventBuffer
|
|
107
|
-
const target = this.agentRef.runtime.entityManager.get(targetEntityGuid)
|
|
113
|
+
serializer (eventBuffer) {
|
|
108
114
|
const sessionEntity = this.agentRef.runtime.session
|
|
109
115
|
return [{
|
|
110
116
|
common: {
|
|
111
117
|
/** Attributes in the `common` section are added to `all` logs generated in the payload */
|
|
112
118
|
attributes: {
|
|
113
119
|
...this.agentRef.info.jsAttributes, // user-provided custom attributes
|
|
114
|
-
|
|
120
|
+
...(this.harvestEndpointVersion === 1 && {
|
|
121
|
+
'entity.guid': this.agentRef.runtime.appMetadata.agents[0].entityGuid,
|
|
122
|
+
appId: this.agentRef.info.applicationID
|
|
123
|
+
}),
|
|
115
124
|
...(sessionEntity && {
|
|
116
125
|
session: sessionEntity.state.value || '0', // The session ID that we generate and keep across page loads
|
|
117
|
-
hasReplay: sessionEntity.state.sessionReplayMode === 1
|
|
126
|
+
hasReplay: sessionEntity.state.sessionReplayMode === 1, // True if a session replay recording is running
|
|
118
127
|
hasTrace: sessionEntity.state.sessionTraceMode === 1 // True if a session trace recording is running
|
|
119
128
|
}),
|
|
120
129
|
ptid: this.agentRef.runtime.ptid, // page trace id
|
|
121
|
-
appId: target.applicationID || this.agentRef.info.applicationID, // Application ID from info object,
|
|
122
130
|
standalone: Boolean(this.agentRef.info.sa), // copy paste (true) vs APM (false)
|
|
123
131
|
agentVersion: this.agentRef.runtime.version, // browser agent version
|
|
124
132
|
// The following 3 attributes are evaluated and dropped at ingest processing time and do not get stored on NRDB:
|
|
@@ -135,9 +143,8 @@ export class Aggregate extends AggregateBase {
|
|
|
135
143
|
}]
|
|
136
144
|
}
|
|
137
145
|
|
|
138
|
-
queryStringsBuilder (
|
|
139
|
-
|
|
140
|
-
return { browser_monitoring_key: target.licenseKey }
|
|
146
|
+
queryStringsBuilder () {
|
|
147
|
+
return { browser_monitoring_key: this.agentRef.info.licenseKey }
|
|
141
148
|
}
|
|
142
149
|
|
|
143
150
|
/** Abort the feature, once aborted it will not resume */
|
|
@@ -13,11 +13,11 @@ import { LOGGING_EVENT_EMITTER_CHANNEL, LOG_LEVELS } from '../constants'
|
|
|
13
13
|
* @param {string} message - the log message string
|
|
14
14
|
* @param {{[key: string]: *}} customAttributes - The log's custom attributes if any
|
|
15
15
|
* @param {enum} level - the log level enum
|
|
16
|
-
* @param {object=}
|
|
16
|
+
* @param {object=} target - the optional target provided by an api call
|
|
17
17
|
*/
|
|
18
|
-
export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO,
|
|
18
|
+
export function bufferLog (ee, message, customAttributes = {}, level = LOG_LEVELS.INFO, target, timestamp = now()) {
|
|
19
19
|
handle(SUPPORTABILITY_METRIC_CHANNEL, [`API/logging/${level.toLowerCase()}/called`], undefined, FEATURE_NAMES.metrics, ee)
|
|
20
|
-
handle(LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level,
|
|
20
|
+
handle(LOGGING_EVENT_EMITTER_CHANNEL, [timestamp, message, customAttributes, level, target], undefined, FEATURE_NAMES.logging, ee)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
@@ -17,8 +17,6 @@ import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
|
|
|
17
17
|
import { now } from '../../../common/timing/now'
|
|
18
18
|
import { TimeKeeper } from '../../../common/timing/time-keeper'
|
|
19
19
|
import { applyFnToProps } from '../../../common/util/traverse'
|
|
20
|
-
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
21
|
-
import { isContainerAgentTarget } from '../../../common/util/target'
|
|
22
20
|
|
|
23
21
|
export class Aggregate extends AggregateBase {
|
|
24
22
|
static featureName = CONSTANTS.FEATURE_NAME
|
|
@@ -29,10 +27,6 @@ export class Aggregate extends AggregateBase {
|
|
|
29
27
|
this.firstByteToWindowLoad = 0 // our "frontend" duration
|
|
30
28
|
this.firstByteToDomContent = 0 // our "dom processing" duration
|
|
31
29
|
|
|
32
|
-
registerHandler('send-rum', (customAttibutes, target) => {
|
|
33
|
-
this.sendRum(customAttibutes, target)
|
|
34
|
-
}, this.featureName, this.ee)
|
|
35
|
-
|
|
36
30
|
if (!isValid(agentRef.info)) {
|
|
37
31
|
this.ee.abort()
|
|
38
32
|
return warn(43)
|
|
@@ -58,7 +52,7 @@ export class Aggregate extends AggregateBase {
|
|
|
58
52
|
*
|
|
59
53
|
* @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
|
|
60
54
|
* @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
|
|
61
|
-
* @param {*} target The target to harvest to
|
|
55
|
+
* @param {*} target The target to harvest to
|
|
62
56
|
*/
|
|
63
57
|
sendRum (customAttributes = this.agentRef.info.jsAttributes, target = { licenseKey: this.agentRef.info.licenseKey, applicationID: this.agentRef.info.applicationID }) {
|
|
64
58
|
const info = this.agentRef.info
|
|
@@ -122,7 +116,7 @@ export class Aggregate extends AggregateBase {
|
|
|
122
116
|
|
|
123
117
|
this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
124
118
|
directSend: {
|
|
125
|
-
|
|
119
|
+
target,
|
|
126
120
|
payload: { qs: queryParameters, body }
|
|
127
121
|
},
|
|
128
122
|
needResponse: true,
|
|
@@ -130,20 +124,16 @@ export class Aggregate extends AggregateBase {
|
|
|
130
124
|
})
|
|
131
125
|
}
|
|
132
126
|
|
|
133
|
-
postHarvestCleanup ({ status, responseText, xhr
|
|
127
|
+
postHarvestCleanup ({ status, responseText, xhr }) {
|
|
134
128
|
const rumEndTime = now()
|
|
135
129
|
let app, flags
|
|
136
130
|
try {
|
|
137
131
|
({ app, ...flags } = JSON.parse(responseText))
|
|
138
|
-
this.processEntities(app.agents, targetApp)
|
|
139
132
|
} catch (error) {
|
|
140
133
|
// wont set entity stuff here, if main agent will later abort, if registered agent, nothing will happen
|
|
141
134
|
warn(53, error)
|
|
142
135
|
}
|
|
143
136
|
|
|
144
|
-
/** Only run agent-wide side-effects if the harvest was for the main agent */
|
|
145
|
-
if (!isContainerAgentTarget(targetApp, this.agentRef)) return
|
|
146
|
-
|
|
147
137
|
if (status >= 400 || status === 0) {
|
|
148
138
|
warn(18, status)
|
|
149
139
|
// Adding retry logic for the rum call will be a separate change; this.blocked will need to be changed since that prevents another triggerHarvestFor()
|
|
@@ -176,19 +166,4 @@ export class Aggregate extends AggregateBase {
|
|
|
176
166
|
this.agentRef.runtime.harvester.startTimer()
|
|
177
167
|
activateFeatures(flags, this.agentRef)
|
|
178
168
|
}
|
|
179
|
-
|
|
180
|
-
processEntities (entities, targetApp) {
|
|
181
|
-
if (!entities || !targetApp) return
|
|
182
|
-
entities.forEach(agent => {
|
|
183
|
-
const entityManager = this.agentRef.runtime.entityManager
|
|
184
|
-
const entityGuid = agent.entityGuid
|
|
185
|
-
const entity = entityManager.get(entityGuid)
|
|
186
|
-
if (entity) return // already processed
|
|
187
|
-
|
|
188
|
-
if (isContainerAgentTarget(targetApp, this.agentRef)) {
|
|
189
|
-
entityManager.setDefaultEntity({ ...targetApp, entityGuid })
|
|
190
|
-
}
|
|
191
|
-
entityManager.set(agent.entityGuid, { ...targetApp, entityGuid })
|
|
192
|
-
})
|
|
193
|
-
}
|
|
194
169
|
}
|
|
@@ -37,6 +37,8 @@ export class Aggregate extends AggregateBase {
|
|
|
37
37
|
this.gzipper = undefined
|
|
38
38
|
/** populated with the u8 string lib async */
|
|
39
39
|
this.u8 = undefined
|
|
40
|
+
/** flips to false if the compressor libraries cannot import */
|
|
41
|
+
this.shouldCompress = true
|
|
40
42
|
|
|
41
43
|
/** set by BCS response */
|
|
42
44
|
this.entitled = false
|
|
@@ -200,14 +202,15 @@ export class Aggregate extends AggregateBase {
|
|
|
200
202
|
this.gzipper = gzipSync
|
|
201
203
|
this.u8 = strToU8
|
|
202
204
|
} catch (err) {
|
|
203
|
-
|
|
205
|
+
this.shouldCompress = false
|
|
206
|
+
// compressor failed to load, but we can still try to record without compression as a last ditch effort
|
|
204
207
|
}
|
|
205
208
|
}
|
|
206
209
|
|
|
207
|
-
makeHarvestPayload (
|
|
208
|
-
|
|
209
|
-
if (this.
|
|
210
|
-
if (!this.recorder || !this.timeKeeper?.ready || !this.recorder.hasSeenSnapshot) return
|
|
210
|
+
makeHarvestPayload () {
|
|
211
|
+
if (this.mode !== MODE.FULL || this.blocked) return // harvests should only be made in FULL mode, and not if the feature is blocked
|
|
212
|
+
if (this.shouldCompress && !this.gzipper) return // if compression is enabled, but the libraries have not loaded, wait for them to load
|
|
213
|
+
if (!this.recorder || !this.timeKeeper?.ready || !(this.recorder.hasSeenSnapshot && this.recorder.hasSeenMeta)) return // if the recorder or the timekeeper is not ready, or the recorder has not yet seen a snapshot, do not harvest
|
|
211
214
|
|
|
212
215
|
const recorderEvents = this.recorder.getEvents()
|
|
213
216
|
// get the event type and use that to trigger another harvest if needed
|
|
@@ -216,7 +219,7 @@ export class Aggregate extends AggregateBase {
|
|
|
216
219
|
const payload = this.getHarvestContents(recorderEvents)
|
|
217
220
|
if (!payload.body.length) {
|
|
218
221
|
this.recorder.clearBuffer()
|
|
219
|
-
return
|
|
222
|
+
return
|
|
220
223
|
}
|
|
221
224
|
|
|
222
225
|
this.reportSupportabilityMetric('SessionReplay/Harvest/Attempts')
|
|
@@ -232,19 +235,18 @@ export class Aggregate extends AggregateBase {
|
|
|
232
235
|
|
|
233
236
|
if (len > MAX_PAYLOAD_SIZE) {
|
|
234
237
|
this.abort(ABORT_REASONS.TOO_BIG, len)
|
|
235
|
-
return
|
|
238
|
+
return
|
|
236
239
|
}
|
|
240
|
+
|
|
237
241
|
// TODO -- Gracefully handle the buffer for retries.
|
|
238
242
|
if (!this.agentRef.runtime.session.state.sessionReplaySentFirstChunk) this.syncWithSessionManager({ sessionReplaySentFirstChunk: true })
|
|
239
243
|
this.recorder.clearBuffer()
|
|
240
|
-
if (recorderEvents.type === 'preloaded') this.agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
241
|
-
payloadOutput.payload = payload
|
|
242
244
|
|
|
243
245
|
if (!this.agentRef.runtime.session.state.traceHarvestStarted) {
|
|
244
246
|
warn(59, JSON.stringify(this.agentRef.runtime.session.state))
|
|
245
247
|
}
|
|
246
248
|
|
|
247
|
-
return
|
|
249
|
+
return payload
|
|
248
250
|
}
|
|
249
251
|
|
|
250
252
|
/**
|
|
@@ -47,6 +47,7 @@ export class Recorder {
|
|
|
47
47
|
this.backloggedEvents = new RecorderEvents(this.shouldFix)
|
|
48
48
|
/** Only set to true once a snapshot node has been processed. Used to block harvests from sending before we know we have a snapshot */
|
|
49
49
|
this.hasSeenSnapshot = false
|
|
50
|
+
this.hasSeenMeta = false
|
|
50
51
|
/** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
|
|
51
52
|
this.lastMeta = false
|
|
52
53
|
/** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
|
|
@@ -191,9 +192,9 @@ export class Recorder {
|
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
// meta event
|
|
194
|
-
this.events.hasMeta ||= event.type === RRWEB_EVENT_TYPES.Meta
|
|
195
|
+
this.hasSeenMeta ||= this.events.hasMeta ||= event.type === RRWEB_EVENT_TYPES.Meta
|
|
195
196
|
// snapshot event
|
|
196
|
-
this.
|
|
197
|
+
this.hasSeenSnapshot ||= this.events.hasSnapshot ||= event.type === RRWEB_EVENT_TYPES.FullSnapshot
|
|
197
198
|
|
|
198
199
|
//* dont let the EventBuffer class double evaluate the event data size, it's a performance burden and we have special reasons to do it outside the event buffer */
|
|
199
200
|
this.events.add(event, eventBytes)
|
|
@@ -97,8 +97,6 @@ export class Aggregate extends AggregateBase {
|
|
|
97
97
|
}
|
|
98
98
|
this.agentRef.runtime.session.write({ sessionTraceMode: this.mode })
|
|
99
99
|
this.drain()
|
|
100
|
-
/** try to harvest immediately. This will not send if the trace is not running in FULL mode due to the pre-harvest checks. */
|
|
101
|
-
this.agentRef.runtime.harvester.triggerHarvestFor(this)
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
preHarvestChecks () {
|
|
@@ -46,7 +46,6 @@ export class Aggregate extends AggregateBase {
|
|
|
46
46
|
this.waitForFlags(['spa']).then(([spaOn]) => {
|
|
47
47
|
if (spaOn) {
|
|
48
48
|
this.drain()
|
|
49
|
-
setTimeout(() => agentRef.runtime.harvester.triggerHarvestFor(this), 0) // send the IPL ixn on next tick, giving some time for any ajax to finish; we may want to just remove this?
|
|
50
49
|
} else {
|
|
51
50
|
this.blocked = true // if rum response determines that customer lacks entitlements for spa endpoint, this feature shouldn't harvest
|
|
52
51
|
this.deregisterDrain()
|
|
@@ -148,7 +147,7 @@ export class Aggregate extends AggregateBase {
|
|
|
148
147
|
*/
|
|
149
148
|
if (this.interactionInProgress?.isActiveDuring(timestamp)) return this.interactionInProgress
|
|
150
149
|
let saveIxn
|
|
151
|
-
const interactionsBuffer = this.interactionsToHarvest.get()
|
|
150
|
+
const interactionsBuffer = this.interactionsToHarvest.get()
|
|
152
151
|
if (!interactionsBuffer) return undefined // no interactions have been staged yet, so nothing to search through)
|
|
153
152
|
for (let idx = interactionsBuffer.length - 1; idx >= 0; idx--) { // reverse search for the latest completed interaction for efficiency
|
|
154
153
|
const finishedInteraction = interactionsBuffer[idx]
|