@newrelic/browser-agent 1.245.0 → 1.246.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 +21 -0
- package/README.md +19 -0
- package/dist/cjs/cdn/polyfills.js +2 -1
- package/dist/cjs/common/config/state/configurable.js +1 -1
- package/dist/cjs/common/config/state/init.js +1 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/session/session-entity.js +3 -2
- package/dist/cjs/common/url/parse-url.js +20 -44
- package/dist/cjs/common/vitals/first-input-delay.js +1 -2
- package/dist/cjs/common/vitals/largest-contentful-paint.js +1 -2
- package/dist/cjs/common/vitals/vital-metric.js +2 -12
- package/dist/cjs/features/ajax/aggregate/index.js +1 -0
- package/dist/cjs/features/jserrors/aggregate/index.js +1 -1
- package/dist/cjs/features/page_view_event/aggregate/index.js +2 -0
- package/dist/cjs/features/page_view_timing/aggregate/index.js +10 -1
- package/dist/cjs/features/session_replay/aggregate/index.js +6 -6
- package/dist/cjs/features/session_trace/aggregate/index.js +14 -3
- package/dist/cjs/features/spa/aggregate/index.js +5 -3
- package/dist/cjs/features/utils/feature-base.js +2 -2
- package/dist/cjs/features/utils/instrument-base.js +4 -4
- package/dist/esm/cdn/polyfills.js +2 -1
- package/dist/esm/common/config/state/configurable.js +1 -1
- package/dist/esm/common/config/state/init.js +1 -0
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/session/session-entity.js +3 -2
- package/dist/esm/common/url/parse-url.js +21 -45
- package/dist/esm/common/vitals/first-input-delay.js +1 -2
- package/dist/esm/common/vitals/largest-contentful-paint.js +1 -2
- package/dist/esm/common/vitals/vital-metric.js +1 -11
- package/dist/esm/features/ajax/aggregate/index.js +1 -0
- package/dist/esm/features/jserrors/aggregate/index.js +1 -1
- package/dist/esm/features/page_view_event/aggregate/index.js +2 -0
- package/dist/esm/features/page_view_timing/aggregate/index.js +9 -0
- package/dist/esm/features/session_replay/aggregate/index.js +6 -6
- package/dist/esm/features/session_trace/aggregate/index.js +14 -3
- package/dist/esm/features/spa/aggregate/index.js +5 -3
- package/dist/esm/features/utils/feature-base.js +2 -2
- package/dist/esm/features/utils/instrument-base.js +4 -4
- package/dist/types/common/config/state/configurable.d.ts.map +1 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/event-emitter/contextual-ee.d.ts +2 -2
- package/dist/types/common/event-emitter/register-handler.d.ts +1 -1
- package/dist/types/common/session/session-entity.d.ts +9 -9
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/url/parse-url.d.ts +12 -1
- package/dist/types/common/url/parse-url.d.ts.map +1 -1
- package/dist/types/common/vitals/constants.d.ts +8 -8
- package/dist/types/common/vitals/vital-metric.d.ts +1 -2
- package/dist/types/common/vitals/vital-metric.d.ts.map +1 -1
- package/dist/types/common/window/nreum.d.ts +2 -2
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +2 -2
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +6 -6
- package/dist/types/features/session_trace/aggregate/index.d.ts +1 -0
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts +1 -0
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/feature-base.d.ts +4 -4
- package/dist/types/features/utils/feature-base.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts +6 -6
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/configure/public-path.npm.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts +9 -9
- package/dist/types/loaders/micro-agent.d.ts +2 -2
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cdn/polyfills.js +1 -0
- package/src/common/config/state/configurable.js +2 -1
- package/src/common/config/state/init.js +1 -0
- package/src/common/session/session-entity.js +3 -2
- package/src/common/url/parse-url.js +21 -51
- package/src/common/vitals/first-input-delay.js +1 -2
- package/src/common/vitals/largest-contentful-paint.js +1 -2
- package/src/common/vitals/vital-metric.js +2 -12
- package/src/features/ajax/aggregate/index.js +2 -0
- package/src/features/jserrors/aggregate/index.js +1 -1
- package/src/features/page_view_event/aggregate/index.js +2 -0
- package/src/features/page_view_timing/aggregate/index.js +11 -0
- package/src/features/session_replay/aggregate/index.js +6 -6
- package/src/features/session_trace/aggregate/index.js +10 -2
- package/src/features/spa/aggregate/index.js +5 -3
- package/src/features/utils/feature-base.js +2 -2
- package/src/features/utils/instrument-base.js +4 -4
- package/src/loaders/configure/public-path.npm.js +0 -1
package/src/cdn/polyfills.js
CHANGED
|
@@ -13,7 +13,8 @@ export function getModeledObject (obj, model) {
|
|
|
13
13
|
for (let key in target) {
|
|
14
14
|
if (obj[key] !== undefined) {
|
|
15
15
|
try {
|
|
16
|
-
if (
|
|
16
|
+
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]))
|
|
17
|
+
else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key])
|
|
17
18
|
else output[key] = obj[key]
|
|
18
19
|
} catch (e) {
|
|
19
20
|
warn('An error occurred while setting a property of a Configurable', e)
|
|
@@ -29,6 +29,7 @@ const model = () => {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
return {
|
|
32
|
+
feature_flags: [],
|
|
32
33
|
proxy: {
|
|
33
34
|
assets: undefined, // if this value is set, it will be used to overwrite the webpack asset path used to fetch assets
|
|
34
35
|
beacon: undefined // likewise for the url to which we send analytics
|
|
@@ -25,9 +25,10 @@ const model = {
|
|
|
25
25
|
inactiveAt: 0,
|
|
26
26
|
expiresAt: 0,
|
|
27
27
|
updatedAt: Date.now(),
|
|
28
|
-
|
|
28
|
+
sessionReplayMode: MODE.OFF,
|
|
29
29
|
sessionReplaySentFirstChunk: false,
|
|
30
30
|
sessionTraceMode: MODE.OFF,
|
|
31
|
+
traceHarvestStarted: false,
|
|
31
32
|
custom: {}
|
|
32
33
|
}
|
|
33
34
|
export const SESSION_EVENTS = {
|
|
@@ -140,7 +141,7 @@ export class SessionEntity {
|
|
|
140
141
|
|
|
141
142
|
// The fact that the session is "new" or pre-existing is used in some places in the agent. Session Replay and Trace
|
|
142
143
|
// can use this info to inform whether to trust a new sampling decision vs continue a previous tracking effort.
|
|
143
|
-
|
|
144
|
+
this.isNew = !Object.keys(initialRead).length
|
|
144
145
|
// if its a "new" session, we write to storage API with the default values. These values may change over the lifespan of the agent run.
|
|
145
146
|
// we can use a modeled object here to help us know and manage what values are being used. -- see "model" above
|
|
146
147
|
if (this.isNew) this.write(getModeledObject(this.state, model), true)
|
|
@@ -3,15 +3,9 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { globalScope
|
|
7
|
-
|
|
8
|
-
var stringsToParsedUrls = {}
|
|
6
|
+
import { globalScope } from '../constants/runtime'
|
|
9
7
|
|
|
10
8
|
export function parseUrl (url) {
|
|
11
|
-
if (url in stringsToParsedUrls) {
|
|
12
|
-
return stringsToParsedUrls[url]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
9
|
// Return if URL is a data URL, parseUrl assumes urls are http/https
|
|
16
10
|
if ((url || '').indexOf('data:') === 0) {
|
|
17
11
|
return {
|
|
@@ -19,54 +13,30 @@ export function parseUrl (url) {
|
|
|
19
13
|
}
|
|
20
14
|
}
|
|
21
15
|
|
|
22
|
-
let urlEl
|
|
23
|
-
var location = globalScope?.location
|
|
24
|
-
var ret = {}
|
|
25
|
-
|
|
26
16
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
const parsedUrl = new URL(url, location.href)
|
|
18
|
+
const returnVal = {
|
|
19
|
+
port: parsedUrl.port,
|
|
20
|
+
hostname: parsedUrl.hostname,
|
|
21
|
+
pathname: parsedUrl.pathname,
|
|
22
|
+
search: parsedUrl.search,
|
|
23
|
+
protocol: parsedUrl.protocol.slice(0, parsedUrl.protocol.indexOf(':')),
|
|
24
|
+
sameOrigin: parsedUrl.protocol === globalScope?.location?.protocol && parsedUrl.host === globalScope?.location?.host
|
|
35
25
|
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
ret.port = urlEl.port
|
|
39
|
-
|
|
40
|
-
ret.search = urlEl.search
|
|
41
26
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
if (!ret.port || ret.port === '0') ret.port = (firstSplit[0] === 'https' ? '443' : '80')
|
|
48
|
-
|
|
49
|
-
// Host not provided in IE for relative urls
|
|
50
|
-
ret.hostname = (urlEl.hostname || location.hostname)
|
|
51
|
-
|
|
52
|
-
ret.pathname = urlEl.pathname
|
|
53
|
-
|
|
54
|
-
ret.protocol = firstSplit[0]
|
|
55
|
-
|
|
56
|
-
// Pathname sometimes doesn't have leading slash (IE 8 and 9)
|
|
57
|
-
if (ret.pathname.charAt(0) !== '/') ret.pathname = '/' + ret.pathname
|
|
58
|
-
|
|
59
|
-
// urlEl.protocol is ':' in old ie when protocol is not specified
|
|
60
|
-
var sameProtocol = !urlEl.protocol || urlEl.protocol === ':' || urlEl.protocol === location.protocol
|
|
61
|
-
var sameDomain = urlEl.hostname === location.hostname && urlEl.port === location.port
|
|
27
|
+
if (!returnVal.port || returnVal.port === '') {
|
|
28
|
+
if (parsedUrl.protocol === 'http:') returnVal.port = '80'
|
|
29
|
+
if (parsedUrl.protocol === 'https:') returnVal.port = '443'
|
|
30
|
+
}
|
|
62
31
|
|
|
63
|
-
|
|
64
|
-
|
|
32
|
+
if (!returnVal.pathname || returnVal.pathname === '') {
|
|
33
|
+
returnVal.pathname = '/'
|
|
34
|
+
} else if (!returnVal.pathname.startsWith('/')) {
|
|
35
|
+
returnVal.pathname = `/${returnVal.pathname}`
|
|
36
|
+
}
|
|
65
37
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
38
|
+
return returnVal
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return {}
|
|
69
41
|
}
|
|
70
|
-
|
|
71
|
-
return ret
|
|
72
42
|
}
|
|
@@ -13,8 +13,7 @@ if (isBrowserScope) {
|
|
|
13
13
|
firstInputDelay.update({
|
|
14
14
|
value: entries[0].startTime,
|
|
15
15
|
entries,
|
|
16
|
-
attrs: { type: entries[0].name, fid: Math.round(value) }
|
|
17
|
-
shouldAddConnectionAttributes: true
|
|
16
|
+
attrs: { type: entries[0].name, fid: Math.round(value) }
|
|
18
17
|
})
|
|
19
18
|
})
|
|
20
19
|
}
|
|
@@ -8,7 +8,7 @@ export class VitalMetric {
|
|
|
8
8
|
this.roundingMethod = typeof roundingMethod === 'function' ? roundingMethod : Math.floor
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
update ({ value, entries = [], attrs = {}
|
|
11
|
+
update ({ value, entries = [], attrs = {} }) {
|
|
12
12
|
if (value < 0) return
|
|
13
13
|
const state = {
|
|
14
14
|
value: this.roundingMethod(value),
|
|
@@ -16,7 +16,7 @@ export class VitalMetric {
|
|
|
16
16
|
entries,
|
|
17
17
|
attrs
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
this.history.push(state)
|
|
21
21
|
this.#subscribers.forEach(cb => {
|
|
22
22
|
try {
|
|
@@ -48,13 +48,3 @@ export class VitalMetric {
|
|
|
48
48
|
return () => { this.#subscribers.delete(callback) }
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
function addConnectionAttributes (obj) {
|
|
53
|
-
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection // to date, both window & worker shares the same support for connection
|
|
54
|
-
if (!connection) return
|
|
55
|
-
|
|
56
|
-
if (connection.type) obj['net-type'] = connection.type
|
|
57
|
-
if (connection.effectiveType) obj['net-etype'] = connection.effectiveType
|
|
58
|
-
if (connection.rtt) obj['net-rtt'] = connection.rtt
|
|
59
|
-
if (connection.downlink) obj['net-dlink'] = connection.downlink
|
|
60
|
-
}
|
|
@@ -126,6 +126,8 @@ export class Aggregate extends AggregateBase {
|
|
|
126
126
|
query: this?.parsedOrigin?.search
|
|
127
127
|
})
|
|
128
128
|
|
|
129
|
+
if (event.gql) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length], undefined, FEATURE_NAMES.metrics, ee)
|
|
130
|
+
|
|
129
131
|
// if the ajax happened inside an interaction, hold it until the interaction finishes
|
|
130
132
|
if (this.spaNode) {
|
|
131
133
|
var interactionId = this.spaNode.interaction.id
|
|
@@ -191,7 +191,7 @@ export class Aggregate extends AggregateBase {
|
|
|
191
191
|
this.pageviewReported[bucketHash] = true
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
if (agentRuntime?.session?.state?.
|
|
194
|
+
if (agentRuntime?.session?.state?.sessionReplayMode) params.hasReplay = true
|
|
195
195
|
params.firstOccurrenceTimestamp = this.observedAt[bucketHash]
|
|
196
196
|
|
|
197
197
|
var type = internal ? 'ierr' : 'err'
|
|
@@ -69,6 +69,8 @@ export class Aggregate extends AggregateBase {
|
|
|
69
69
|
at: info.atts
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
if (agentRuntime.session) queryParameters.fsh = Number(agentRuntime.session.isNew) // "first session harvest" aka RUM request or PageView event of a session
|
|
73
|
+
|
|
72
74
|
let body
|
|
73
75
|
if (typeof info.jsAttributes === 'object' && Object.keys(info.jsAttributes).length > 0) {
|
|
74
76
|
body = { ja: info.jsAttributes }
|
|
@@ -94,6 +94,7 @@ export class Aggregate extends AggregateBase {
|
|
|
94
94
|
|
|
95
95
|
addTiming (name, value, attrs) {
|
|
96
96
|
attrs = attrs || {}
|
|
97
|
+
addConnectionAttributes(attrs) // network conditions may differ from the actual for VitalMetrics when they were captured
|
|
97
98
|
|
|
98
99
|
// If cls was set to another value by `onCLS`, then it's supported and is attached onto any timing but is omitted until such time.
|
|
99
100
|
/*
|
|
@@ -175,3 +176,13 @@ export class Aggregate extends AggregateBase {
|
|
|
175
176
|
return payload
|
|
176
177
|
}
|
|
177
178
|
}
|
|
179
|
+
|
|
180
|
+
function addConnectionAttributes (obj) {
|
|
181
|
+
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection // to date, both window & worker shares the same support for connection
|
|
182
|
+
if (!connection) return
|
|
183
|
+
|
|
184
|
+
if (connection.type) obj['net-type'] = connection.type
|
|
185
|
+
if (connection.effectiveType) obj['net-etype'] = connection.effectiveType
|
|
186
|
+
if (connection.rtt) obj['net-rtt'] = connection.rtt
|
|
187
|
+
if (connection.downlink) obj['net-dlink'] = connection.downlink
|
|
188
|
+
}
|
|
@@ -133,14 +133,14 @@ export class Aggregate extends AggregateBase {
|
|
|
133
133
|
this.ee.on(SESSION_EVENTS.RESUME, () => {
|
|
134
134
|
// if the mode changed on a different tab, it needs to update this instance to match
|
|
135
135
|
const { session } = getRuntime(this.agentIdentifier)
|
|
136
|
-
this.mode = session.state.
|
|
136
|
+
this.mode = session.state.sessionReplayMode
|
|
137
137
|
if (!this.initialized || this.mode === MODE.OFF) return
|
|
138
138
|
this.startRecording()
|
|
139
139
|
})
|
|
140
140
|
|
|
141
141
|
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
142
142
|
if (!this.initialized || this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return
|
|
143
|
-
if (this.mode !== MODE.OFF && data.
|
|
143
|
+
if (this.mode !== MODE.OFF && data.sessionReplayMode === MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB)
|
|
144
144
|
this.mode = data.sessionReplay
|
|
145
145
|
})
|
|
146
146
|
|
|
@@ -167,7 +167,7 @@ export class Aggregate extends AggregateBase {
|
|
|
167
167
|
|
|
168
168
|
this.scheduler.startTimer(this.harvestTimeSeconds)
|
|
169
169
|
|
|
170
|
-
this.syncWithSessionManager({
|
|
170
|
+
this.syncWithSessionManager({ sessionReplayMode: this.mode })
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
}, this.featureName, this.ee)
|
|
@@ -201,7 +201,7 @@ export class Aggregate extends AggregateBase {
|
|
|
201
201
|
// session replay samples can only be decided on the first load of a session
|
|
202
202
|
// session replays can continue if already in progress
|
|
203
203
|
if (!session.isNew) { // inherit the mode of the existing session
|
|
204
|
-
this.mode = session.state.
|
|
204
|
+
this.mode = session.state.sessionReplayMode
|
|
205
205
|
} else {
|
|
206
206
|
// The session is new... determine the mode the new session should start in
|
|
207
207
|
if (fullSample) this.mode = MODE.FULL // full mode has precedence over error mode
|
|
@@ -241,7 +241,7 @@ export class Aggregate extends AggregateBase {
|
|
|
241
241
|
}
|
|
242
242
|
this.startRecording()
|
|
243
243
|
|
|
244
|
-
this.syncWithSessionManager({
|
|
244
|
+
this.syncWithSessionManager({ sessionReplayMode: this.mode })
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
prepareHarvest () {
|
|
@@ -439,7 +439,7 @@ export class Aggregate extends AggregateBase {
|
|
|
439
439
|
this.blocked = true
|
|
440
440
|
this.mode = MODE.OFF
|
|
441
441
|
this.stopRecording()
|
|
442
|
-
this.syncWithSessionManager({
|
|
442
|
+
this.syncWithSessionManager({ sessionReplayMode: this.mode })
|
|
443
443
|
this.clearTimestamps()
|
|
444
444
|
this.ee.emit('REPLAY_ABORTED')
|
|
445
445
|
}
|
|
@@ -125,7 +125,7 @@ export class Aggregate extends AggregateBase {
|
|
|
125
125
|
this.ee.on(SESSION_EVENTS.PAUSE, () => { mostRecentModeKnown = sessionEntity.state.sessionTraceMode })
|
|
126
126
|
|
|
127
127
|
if (!sessionEntity.isNew) { // inherit the same mode as existing session's Trace
|
|
128
|
-
if (sessionEntity.state.
|
|
128
|
+
if (sessionEntity.state.sessionReplayMode === MODE.OFF) this.isStandalone = true
|
|
129
129
|
controlTraceOp(mostRecentModeKnown = sessionEntity.state.sessionTraceMode)
|
|
130
130
|
} else { // for new sessions, see the truth table associated with NEWRELIC-8662 wrt the new Trace behavior under session management
|
|
131
131
|
const replayMode = await getSessionReplayMode(agentIdentifier)
|
|
@@ -462,6 +462,13 @@ export class Aggregate extends AggregateBase {
|
|
|
462
462
|
this.trace = {}
|
|
463
463
|
this.nodeCount = 0
|
|
464
464
|
|
|
465
|
+
let firstHarvestOfSession
|
|
466
|
+
if (this.agentRuntime.session) {
|
|
467
|
+
const isFirstPayload = !this.agentRuntime.session.state.traceHarvestStarted
|
|
468
|
+
firstHarvestOfSession = { fsh: Number(isFirstPayload) } // converted to '0' | '1'
|
|
469
|
+
if (isFirstPayload) this.agentRuntime.session.write({ traceHarvestStarted: true })
|
|
470
|
+
}
|
|
471
|
+
|
|
465
472
|
return {
|
|
466
473
|
qs: {
|
|
467
474
|
st: this.agentRuntime.offset,
|
|
@@ -472,7 +479,8 @@ export class Aggregate extends AggregateBase {
|
|
|
472
479
|
* so that blob parsing doesn't need to happen to support UI/API functions */
|
|
473
480
|
fts: this.agentRuntime.offset + earliestTimeStamp,
|
|
474
481
|
/** n === "nodeCount" in NR1, a count of nodes in the ST payload, so that blob parsing doesn't need to happen to support UI/API functions */
|
|
475
|
-
n: stns.length // node count
|
|
482
|
+
n: stns.length, // node count
|
|
483
|
+
...firstHarvestOfSession
|
|
476
484
|
},
|
|
477
485
|
body: { res: stns }
|
|
478
486
|
}
|
|
@@ -46,7 +46,9 @@ export class Aggregate extends AggregateBase {
|
|
|
46
46
|
depth: 0,
|
|
47
47
|
harvestTimeSeconds: getConfigurationValue(agentIdentifier, 'spa.harvestTimeSeconds') || 10,
|
|
48
48
|
interactionsToHarvest: [],
|
|
49
|
-
interactionsSent: []
|
|
49
|
+
interactionsSent: [],
|
|
50
|
+
// The below feature flag is used to disable the SPA ajax fix for specific customers, see https://new-relic.atlassian.net/browse/NR-172169
|
|
51
|
+
disableSpaFix: (getConfigurationValue(agentIdentifier, 'feature_flags') || []).indexOf('disable-spa-fix') > -1
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
this.serializer = new Serializer(this)
|
|
@@ -279,7 +281,7 @@ export class Aggregate extends AggregateBase {
|
|
|
279
281
|
// context is stored on the xhr and is shared with all callbacks associated
|
|
280
282
|
// with the new xhr
|
|
281
283
|
register('new-xhr', function () {
|
|
282
|
-
if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
284
|
+
if (!state.disableSpaFix && !state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
283
285
|
/*
|
|
284
286
|
* The previous interaction was discarded before a route change. Restore the interaction
|
|
285
287
|
* in case this XHR is associated with a route change.
|
|
@@ -382,7 +384,7 @@ export class Aggregate extends AggregateBase {
|
|
|
382
384
|
|
|
383
385
|
register(FETCH_START, function (fetchArguments, dtPayload) {
|
|
384
386
|
if (fetchArguments) {
|
|
385
|
-
if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
387
|
+
if (!state.disableSpaFix && !state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
386
388
|
/*
|
|
387
389
|
* The previous interaction was discarded before a route change. Restore the interaction
|
|
388
390
|
* in case this XHR is associated with a route change.
|
|
@@ -4,9 +4,9 @@ export class FeatureBase {
|
|
|
4
4
|
constructor (agentIdentifier, aggregator, featureName) {
|
|
5
5
|
/** @type {string} */
|
|
6
6
|
this.agentIdentifier = agentIdentifier
|
|
7
|
-
/** @type {Aggregator} */
|
|
7
|
+
/** @type {import('../../common/aggregate/aggregator').Aggregator} */
|
|
8
8
|
this.aggregator = aggregator
|
|
9
|
-
/** @type {
|
|
9
|
+
/** @type {import('../../common/event-emitter/contextual-ee').ee} */
|
|
10
10
|
this.ee = ee.get(agentIdentifier)
|
|
11
11
|
/** @type {string} */
|
|
12
12
|
this.featureName = featureName
|
|
@@ -20,7 +20,7 @@ export class InstrumentBase extends FeatureBase {
|
|
|
20
20
|
/**
|
|
21
21
|
* Instantiate InstrumentBase.
|
|
22
22
|
* @param {string} agentIdentifier - The unique ID of the instantiated agent (relative to global scope).
|
|
23
|
-
* @param {Aggregator} aggregator - The shared Aggregator that will handle batching and reporting of data.
|
|
23
|
+
* @param {import('../../common/aggregate/aggregator').Aggregator} aggregator - The shared Aggregator that will handle batching and reporting of data.
|
|
24
24
|
* @param {string} featureName - The name of the feature module (used to construct file path).
|
|
25
25
|
* @param {boolean} [auto=true] - Determines whether the feature should automatically register to have the draining
|
|
26
26
|
* of its pooled instrumentation data handled by the agent's centralized drain functionality, rather than draining
|
|
@@ -34,7 +34,7 @@ export class InstrumentBase extends FeatureBase {
|
|
|
34
34
|
this.abortHandler = undefined
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* @type {
|
|
37
|
+
* @type {import('./aggregate-base').AggregateBase} Holds the reference to the feature's aggregate module counterpart, if and after it has been initialized. This may not be assigned until after page loads!
|
|
38
38
|
* The only purpose of this for now is to expose it to the NREUM interface, as the feature's instrument instance is already exposed.
|
|
39
39
|
*/
|
|
40
40
|
this.featAggregate = undefined
|
|
@@ -122,14 +122,14 @@ export class InstrumentBase extends FeatureBase {
|
|
|
122
122
|
/**
|
|
123
123
|
* Make a determination if an aggregate class should even be imported
|
|
124
124
|
* @param {string} featureName
|
|
125
|
-
* @param {SessionEntity} session
|
|
125
|
+
* @param {import('../../common/session/session-entity').SessionEntity} session
|
|
126
126
|
* @returns
|
|
127
127
|
*/
|
|
128
128
|
shouldImportAgg (featureName, session) {
|
|
129
129
|
if (featureName === FEATURE_NAMES.sessionReplay) {
|
|
130
130
|
if (!originals.MO) return false // Session Replay cannot work without Mutation Observer
|
|
131
131
|
if (getConfigurationValue(this.agentIdentifier, 'session_trace.enabled') === false) return false // Session Replay as of now is tightly coupled with Session Trace in the UI
|
|
132
|
-
return !!session?.isNew || !!session?.state.
|
|
132
|
+
return !!session?.isNew || !!session?.state.sessionReplayMode // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
|
|
133
133
|
}
|
|
134
134
|
return true
|
|
135
135
|
}
|