@newrelic/browser-agent 1.302.0-rc.0 → 1.302.0-rc.10
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/dist/cjs/common/config/init-types.js +2 -0
- package/dist/cjs/common/config/init.js +3 -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/harvest/harvester.js +13 -9
- package/dist/cjs/common/harvest/types.js +0 -1
- package/dist/cjs/common/session/session-entity.js +4 -2
- package/dist/cjs/common/util/mfe.js +4 -0
- package/dist/cjs/common/wrap/wrap-promise.js +10 -5
- package/dist/cjs/features/generic_events/aggregate/index.js +4 -4
- package/dist/cjs/features/logging/aggregate/index.js +1 -2
- package/dist/cjs/features/page_view_event/aggregate/index.js +84 -22
- package/dist/cjs/features/page_view_event/instrument/index.js +0 -4
- package/dist/cjs/features/session_replay/aggregate/index.js +3 -2
- package/dist/cjs/features/session_replay/constants.js +2 -6
- package/dist/cjs/features/session_replay/instrument/index.js +3 -2
- package/dist/cjs/features/utils/agent-session.js +13 -0
- package/dist/cjs/features/utils/instrument-base.js +7 -8
- package/dist/cjs/interfaces/registered-entity.js +21 -0
- package/dist/cjs/loaders/agent.js +2 -0
- package/dist/cjs/loaders/api/consent.js +24 -0
- package/dist/cjs/loaders/api/constants.js +3 -2
- package/dist/cjs/loaders/api/measure.js +36 -35
- package/dist/cjs/loaders/api/recordCustomEvent.js +5 -3
- package/dist/cjs/loaders/api/register-api-types.js +10 -9
- package/dist/cjs/loaders/api/register.js +16 -0
- package/dist/cjs/loaders/api-base.js +14 -7
- package/dist/esm/common/config/init-types.js +2 -0
- package/dist/esm/common/config/init.js +3 -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/harvest/harvester.js +13 -9
- package/dist/esm/common/harvest/types.js +0 -1
- package/dist/esm/common/session/session-entity.js +4 -2
- package/dist/esm/common/util/mfe.js +3 -0
- package/dist/esm/common/wrap/wrap-promise.js +10 -5
- package/dist/esm/features/generic_events/aggregate/index.js +4 -4
- package/dist/esm/features/logging/aggregate/index.js +1 -2
- package/dist/esm/features/page_view_event/aggregate/index.js +84 -22
- package/dist/esm/features/page_view_event/instrument/index.js +0 -4
- package/dist/esm/features/session_replay/aggregate/index.js +4 -3
- package/dist/esm/features/session_replay/constants.js +1 -5
- package/dist/esm/features/session_replay/instrument/index.js +4 -3
- package/dist/esm/features/utils/agent-session.js +13 -0
- package/dist/esm/features/utils/instrument-base.js +7 -8
- package/dist/esm/interfaces/registered-entity.js +21 -0
- package/dist/esm/loaders/agent.js +2 -0
- package/dist/esm/loaders/api/consent.js +17 -0
- package/dist/esm/loaders/api/constants.js +2 -1
- package/dist/esm/loaders/api/measure.js +35 -35
- package/dist/esm/loaders/api/recordCustomEvent.js +4 -3
- package/dist/esm/loaders/api/register-api-types.js +10 -9
- package/dist/esm/loaders/api/register.js +17 -1
- package/dist/esm/loaders/api-base.js +15 -8
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/config/init-types.d.ts +6 -0
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/harvest/harvester.d.ts.map +1 -1
- package/dist/types/common/harvest/types.d.ts +0 -2
- package/dist/types/common/harvest/types.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/util/mfe.d.ts +1 -0
- package/dist/types/common/util/mfe.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +22 -3
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts +1 -5
- package/dist/types/features/session_replay/constants.d.ts.map +1 -1
- package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
- package/dist/types/features/utils/agent-session.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts +1 -0
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/interfaces/registered-entity.d.ts +25 -0
- package/dist/types/interfaces/registered-entity.d.ts.map +1 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/consent.d.ts +2 -0
- package/dist/types/loaders/api/consent.d.ts.map +1 -0
- package/dist/types/loaders/api/constants.d.ts +1 -0
- package/dist/types/loaders/api/constants.d.ts.map +1 -1
- package/dist/types/loaders/api/measure.d.ts +3 -0
- package/dist/types/loaders/api/measure.d.ts.map +1 -1
- package/dist/types/loaders/api/recordCustomEvent.d.ts +1 -0
- package/dist/types/loaders/api/recordCustomEvent.d.ts.map +1 -1
- package/dist/types/loaders/api/register-api-types.d.ts +28 -11
- package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
- package/dist/types/loaders/api/register.d.ts.map +1 -1
- package/dist/types/loaders/api-base.d.ts +20 -15
- package/dist/types/loaders/api-base.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/config/init-types.js +2 -0
- package/src/common/config/init.js +1 -0
- package/src/common/harvest/harvester.js +11 -8
- package/src/common/harvest/types.js +0 -1
- package/src/common/session/session-entity.js +6 -2
- package/src/common/util/mfe.js +4 -0
- package/src/common/wrap/wrap-promise.js +16 -6
- package/src/features/generic_events/aggregate/index.js +4 -4
- package/src/features/logging/aggregate/index.js +1 -1
- package/src/features/page_view_event/aggregate/index.js +79 -15
- package/src/features/page_view_event/instrument/index.js +0 -4
- package/src/features/session_replay/aggregate/index.js +4 -3
- package/src/features/session_replay/constants.js +1 -5
- package/src/features/session_replay/instrument/index.js +4 -3
- package/src/features/utils/agent-session.js +12 -0
- package/src/features/utils/instrument-base.js +7 -9
- package/src/interfaces/registered-entity.js +21 -0
- package/src/loaders/agent.js +2 -0
- package/src/loaders/api/consent.js +18 -0
- package/src/loaders/api/constants.js +1 -0
- package/src/loaders/api/measure.js +34 -33
- package/src/loaders/api/recordCustomEvent.js +5 -3
- package/src/loaders/api/register-api-types.js +10 -9
- package/src/loaders/api/register.js +8 -1
- package/src/loaders/api-base.js +15 -8
|
@@ -17,15 +17,22 @@ 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 { send } from '../../../common/harvest/harvester'
|
|
21
|
+
import { FEATURE_NAMES, FEATURE_TO_ENDPOINT } from '../../../loaders/features/features'
|
|
22
|
+
import { getSubmitMethod } from '../../../common/util/submit-data'
|
|
20
23
|
|
|
21
24
|
export class Aggregate extends AggregateBase {
|
|
22
25
|
static featureName = CONSTANTS.FEATURE_NAME
|
|
26
|
+
|
|
23
27
|
constructor (agentRef) {
|
|
24
28
|
super(agentRef, CONSTANTS.FEATURE_NAME)
|
|
25
29
|
|
|
30
|
+
this.sentRum = false // flag to facilitate calling sendRum() once externally (by the consent API in agent-session.js)
|
|
31
|
+
|
|
26
32
|
this.timeToFirstByte = 0
|
|
27
33
|
this.firstByteToWindowLoad = 0 // our "frontend" duration
|
|
28
34
|
this.firstByteToDomContent = 0 // our "dom processing" duration
|
|
35
|
+
this.retries = 0
|
|
29
36
|
|
|
30
37
|
if (!isValid(agentRef.info)) {
|
|
31
38
|
this.ee.abort()
|
|
@@ -52,9 +59,8 @@ export class Aggregate extends AggregateBase {
|
|
|
52
59
|
*
|
|
53
60
|
* @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
|
|
54
61
|
* @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
|
|
55
|
-
* @param {*} target The target to harvest to
|
|
56
62
|
*/
|
|
57
|
-
sendRum (customAttributes = this.agentRef.info.jsAttributes
|
|
63
|
+
sendRum (customAttributes = this.agentRef.info.jsAttributes) {
|
|
58
64
|
const info = this.agentRef.info
|
|
59
65
|
const measures = {}
|
|
60
66
|
|
|
@@ -107,24 +113,26 @@ export class Aggregate extends AggregateBase {
|
|
|
107
113
|
queryParameters.fp = firstPaint.current.value
|
|
108
114
|
queryParameters.fcp = firstContentfulPaint.current.value
|
|
109
115
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
this.queryStringsBuilder = () => { // this will be called by AggregateBase.makeHarvestPayload every time harvest is triggered to be qs
|
|
117
|
+
this.rumStartTime = now() // this should be reset at the beginning of each RUM call for proper timeKeeper calculation in coordination with postHarvestCleanup
|
|
118
|
+
const timeKeeper = this.agentRef.runtime.timeKeeper
|
|
119
|
+
if (timeKeeper?.ready) {
|
|
120
|
+
queryParameters.timestamp = Math.floor(timeKeeper.correctRelativeTimestamp(this.rumStartTime))
|
|
121
|
+
}
|
|
122
|
+
return queryParameters
|
|
113
123
|
}
|
|
124
|
+
this.events.add(body)
|
|
114
125
|
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
118
|
-
directSend: {
|
|
119
|
-
target,
|
|
120
|
-
payload: { qs: queryParameters, body }
|
|
121
|
-
},
|
|
122
|
-
needResponse: true,
|
|
126
|
+
if (this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
123
127
|
sendEmptyBody: true
|
|
124
|
-
})
|
|
128
|
+
}).ranSend) this.sentRum = true
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
serializer (eventBuffer) { // this is necessary because PVE sends a single item rather than an array; in the case of undefined, this prevents sending [null] as body
|
|
132
|
+
return eventBuffer[0]
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
postHarvestCleanup ({ status, responseText, xhr }) {
|
|
135
|
+
postHarvestCleanup ({ sent, status, responseText, xhr, retry }) {
|
|
128
136
|
const rumEndTime = now()
|
|
129
137
|
let app, flags
|
|
130
138
|
try {
|
|
@@ -134,8 +142,63 @@ export class Aggregate extends AggregateBase {
|
|
|
134
142
|
warn(53, error)
|
|
135
143
|
}
|
|
136
144
|
|
|
145
|
+
super.postHarvestCleanup({ sent, retry }) // this will set isRetrying & re-buffer the body if request is to be retried
|
|
146
|
+
if (this.isRetrying && this.retries++ < 1) { // Only retry once
|
|
147
|
+
setTimeout(() => this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
148
|
+
sendEmptyBody: true
|
|
149
|
+
}), 5000) // Retry sending the RUM event after 5 seconds
|
|
150
|
+
return
|
|
151
|
+
}
|
|
137
152
|
if (status >= 400 || status === 0) {
|
|
138
153
|
warn(18, status)
|
|
154
|
+
this.blocked = true
|
|
155
|
+
|
|
156
|
+
// Get estimated payload size of our backlog
|
|
157
|
+
const textEncoder = new TextEncoder()
|
|
158
|
+
const payloadSize = Object.values(newrelic.ee.backlog).reduce((acc, value) => {
|
|
159
|
+
if (!value) return acc
|
|
160
|
+
|
|
161
|
+
const encoded = textEncoder.encode(value)
|
|
162
|
+
return acc + encoded.byteLength
|
|
163
|
+
}, 0)
|
|
164
|
+
|
|
165
|
+
// Send SMs about failed RUM request
|
|
166
|
+
const body = {
|
|
167
|
+
sm: [{
|
|
168
|
+
params: {
|
|
169
|
+
name: `Browser/Supportability/BCS/Error/${status}`
|
|
170
|
+
},
|
|
171
|
+
stats: {
|
|
172
|
+
c: 1
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
params: {
|
|
177
|
+
name: 'Browser/Supportability/BCS/Error/Dropped/Bytes'
|
|
178
|
+
},
|
|
179
|
+
stats: {
|
|
180
|
+
c: 1,
|
|
181
|
+
t: payloadSize
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
params: {
|
|
186
|
+
name: 'Browser/Supportability/BCS/Error/Duration/Ms'
|
|
187
|
+
},
|
|
188
|
+
stats: {
|
|
189
|
+
c: 1,
|
|
190
|
+
t: rumEndTime - this.rumStartTime
|
|
191
|
+
}
|
|
192
|
+
}]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
send(this.agentRef, {
|
|
196
|
+
endpoint: FEATURE_TO_ENDPOINT[FEATURE_NAMES.metrics],
|
|
197
|
+
payload: { body },
|
|
198
|
+
submitMethod: getSubmitMethod(),
|
|
199
|
+
featureName: FEATURE_NAMES.metrics
|
|
200
|
+
})
|
|
201
|
+
|
|
139
202
|
// Adding retry logic for the rum call will be a separate change; this.blocked will need to be changed since that prevents another triggerHarvestFor()
|
|
140
203
|
this.ee.abort()
|
|
141
204
|
return
|
|
@@ -155,6 +218,7 @@ export class Aggregate extends AggregateBase {
|
|
|
155
218
|
}
|
|
156
219
|
} catch (error) {
|
|
157
220
|
this.ee.abort()
|
|
221
|
+
this.blocked = true
|
|
158
222
|
warn(17, error)
|
|
159
223
|
return
|
|
160
224
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
|
-
import { handle } from '../../../common/event-emitter/handle'
|
|
6
5
|
import { setupSetPageViewNameAPI } from '../../../loaders/api/setPageViewName'
|
|
7
6
|
import { InstrumentBase } from '../../utils/instrument-base'
|
|
8
7
|
import * as CONSTANTS from '../constants'
|
|
@@ -21,9 +20,6 @@ export class Instrument extends InstrumentBase {
|
|
|
21
20
|
/** feature specific APIs */
|
|
22
21
|
setupSetPageViewNameAPI(agentRef)
|
|
23
22
|
|
|
24
|
-
/** messages from the register API that can trigger a new RUM call */
|
|
25
|
-
this.ee.on('api-send-rum', (attrs, target) => handle('send-rum', [attrs, target], undefined, this.featureName, this.ee))
|
|
26
|
-
|
|
27
23
|
this.importAggregator(agentRef, () => import(/* webpackChunkName: "page_view_event-aggregate" */ '../aggregate'))
|
|
28
24
|
}
|
|
29
25
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { registerHandler } from '../../../common/event-emitter/register-handler'
|
|
10
|
-
import { ABORT_REASONS, FEATURE_NAME, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES,
|
|
10
|
+
import { ABORT_REASONS, ERROR_DURING_REPLAY, FEATURE_NAME, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES, TRIGGERS } from '../constants'
|
|
11
11
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
12
12
|
import { sharedChannel } from '../../../common/constants/shared-channel'
|
|
13
13
|
import { obj as encodeObj } from '../../../common/url/encode'
|
|
@@ -21,6 +21,7 @@ import { now } from '../../../common/timing/now'
|
|
|
21
21
|
import { MAX_PAYLOAD_SIZE } from '../../../common/constants/agent-constants'
|
|
22
22
|
import { cleanURL } from '../../../common/url/clean-url'
|
|
23
23
|
import { canEnableSessionTracking } from '../../utils/feature-gates'
|
|
24
|
+
import { PAUSE_REPLAY } from '../../../loaders/api/constants'
|
|
24
25
|
|
|
25
26
|
export class Aggregate extends AggregateBase {
|
|
26
27
|
static featureName = FEATURE_NAME
|
|
@@ -77,11 +78,11 @@ export class Aggregate extends AggregateBase {
|
|
|
77
78
|
this.mode = data.sessionReplayMode
|
|
78
79
|
})
|
|
79
80
|
|
|
80
|
-
registerHandler(
|
|
81
|
+
registerHandler(PAUSE_REPLAY, () => {
|
|
81
82
|
this.forceStop(this.mode === MODE.FULL)
|
|
82
83
|
}, this.featureName, this.ee)
|
|
83
84
|
|
|
84
|
-
registerHandler(
|
|
85
|
+
registerHandler(ERROR_DURING_REPLAY, e => {
|
|
85
86
|
this.handleError(e)
|
|
86
87
|
}, this.featureName, this.ee)
|
|
87
88
|
|
|
@@ -7,11 +7,7 @@ import { FEATURE_NAMES } from '../../loaders/features/features'
|
|
|
7
7
|
|
|
8
8
|
export const FEATURE_NAME = FEATURE_NAMES.sessionReplay
|
|
9
9
|
|
|
10
|
-
export const
|
|
11
|
-
RECORD: 'recordReplay',
|
|
12
|
-
PAUSE: 'pauseReplay',
|
|
13
|
-
ERROR_DURING_REPLAY: 'errorDuringReplay'
|
|
14
|
-
}
|
|
10
|
+
export const ERROR_DURING_REPLAY = 'errorDuringReplay'
|
|
15
11
|
|
|
16
12
|
export const AVG_COMPRESSION = 0.12
|
|
17
13
|
export const RRWEB_EVENT_TYPES = {
|
|
@@ -10,9 +10,10 @@ import { handle } from '../../../common/event-emitter/handle'
|
|
|
10
10
|
import { DEFAULT_KEY, MODE, PREFIX } from '../../../common/session/constants'
|
|
11
11
|
import { InstrumentBase } from '../../utils/instrument-base'
|
|
12
12
|
import { hasReplayPrerequisite, isPreloadAllowed } from '../shared/utils'
|
|
13
|
-
import {
|
|
13
|
+
import { ERROR_DURING_REPLAY, FEATURE_NAME, TRIGGERS } from '../constants'
|
|
14
14
|
import { setupRecordReplayAPI } from '../../../loaders/api/recordReplay'
|
|
15
15
|
import { setupPauseReplayAPI } from '../../../loaders/api/pauseReplay'
|
|
16
|
+
import { RECORD_REPLAY } from '../../../loaders/api/constants'
|
|
16
17
|
|
|
17
18
|
export class Instrument extends InstrumentBase {
|
|
18
19
|
static featureName = FEATURE_NAME
|
|
@@ -34,7 +35,7 @@ export class Instrument extends InstrumentBase {
|
|
|
34
35
|
} catch (err) { }
|
|
35
36
|
|
|
36
37
|
if (hasReplayPrerequisite(agentRef.init)) {
|
|
37
|
-
this.ee.on(
|
|
38
|
+
this.ee.on(RECORD_REPLAY, () => this.#apiStartOrRestartReplay())
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
if (this.#canPreloadRecorder(session)) {
|
|
@@ -50,7 +51,7 @@ export class Instrument extends InstrumentBase {
|
|
|
50
51
|
if (this.blocked) return
|
|
51
52
|
if (this.agentRef.runtime.isRecording) {
|
|
52
53
|
this.errorNoticed = true
|
|
53
|
-
handle(
|
|
54
|
+
handle(ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee)
|
|
54
55
|
}
|
|
55
56
|
})
|
|
56
57
|
}
|
|
@@ -56,6 +56,18 @@ export function setupAgentSession (agentRef) {
|
|
|
56
56
|
agentRef.runtime.session.syncCustomAttribute(key, value)
|
|
57
57
|
}, 'session', sharedEE)
|
|
58
58
|
|
|
59
|
+
registerHandler('api-consent', (accept) => {
|
|
60
|
+
agentRef.runtime.session.write({ consent: accept === undefined ? true : accept })
|
|
61
|
+
|
|
62
|
+
// call sendRum if it wasn't called yet
|
|
63
|
+
agentRef.features.page_view_event.onAggregateImported.then((loaded) => {
|
|
64
|
+
const pveAgg = agentRef.features.page_view_event.featAggregate
|
|
65
|
+
if (loaded && !pveAgg.sentRum) {
|
|
66
|
+
pveAgg.sendRum()
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}, 'session', sharedEE)
|
|
70
|
+
|
|
59
71
|
drain(agentRef.agentIdentifier, 'session')
|
|
60
72
|
|
|
61
73
|
return agentRef.runtime.session
|
|
@@ -49,7 +49,10 @@ export class InstrumentBase extends FeatureBase {
|
|
|
49
49
|
* @type {Promise} Assigned immediately after @see importAggregator runs. Serves as a signal for when the inner async fn finishes execution. Useful for features to await
|
|
50
50
|
* one another if there are inter-features dependencies.
|
|
51
51
|
*/
|
|
52
|
-
this.
|
|
52
|
+
this.loadedSuccessfully = undefined
|
|
53
|
+
this.onAggregateImported = new Promise(resolve => {
|
|
54
|
+
this.loadedSuccessfully = resolve
|
|
55
|
+
})
|
|
53
56
|
|
|
54
57
|
/**
|
|
55
58
|
* used in conjunction with newrelic.start() to defer harvesting in features
|
|
@@ -83,11 +86,6 @@ export class InstrumentBase extends FeatureBase {
|
|
|
83
86
|
importAggregator (agentRef, fetchAggregator, argsObjFromInstrument = {}) {
|
|
84
87
|
if (this.featAggregate) return
|
|
85
88
|
|
|
86
|
-
let loadedSuccessfully
|
|
87
|
-
this.onAggregateImported = new Promise(resolve => {
|
|
88
|
-
loadedSuccessfully = resolve
|
|
89
|
-
})
|
|
90
|
-
|
|
91
89
|
const importLater = async () => {
|
|
92
90
|
// wait for the deferred promise to resolve before proceeding
|
|
93
91
|
// this will resolve immediately if the feature is auto-started,
|
|
@@ -113,20 +111,20 @@ export class InstrumentBase extends FeatureBase {
|
|
|
113
111
|
try {
|
|
114
112
|
if (!this.#shouldImportAgg(this.featureName, session, agentRef.init)) {
|
|
115
113
|
drain(this.agentIdentifier, this.featureName)
|
|
116
|
-
loadedSuccessfully(false) // aggregate module isn't loaded at all
|
|
114
|
+
this.loadedSuccessfully(false) // aggregate module isn't loaded at all
|
|
117
115
|
return
|
|
118
116
|
}
|
|
119
117
|
const { Aggregate } = await fetchAggregator()
|
|
120
118
|
this.featAggregate = new Aggregate(agentRef, argsObjFromInstrument)
|
|
121
119
|
|
|
122
120
|
agentRef.runtime.harvester.initializedAggregates.push(this.featAggregate) // "subscribe" the feature to future harvest intervals (PVE will start the timer)
|
|
123
|
-
loadedSuccessfully(true)
|
|
121
|
+
this.loadedSuccessfully(true)
|
|
124
122
|
} catch (e) {
|
|
125
123
|
warn(34, e)
|
|
126
124
|
this.abortHandler?.() // undo any important alterations made to the page
|
|
127
125
|
// not supported yet but nice to do: "abort" this agent's EE for this feature specifically
|
|
128
126
|
drain(this.agentIdentifier, this.featureName, true)
|
|
129
|
-
loadedSuccessfully(false)
|
|
127
|
+
this.loadedSuccessfully(false)
|
|
130
128
|
if (this.ee) this.ee.abort()
|
|
131
129
|
}
|
|
132
130
|
}
|
|
@@ -48,6 +48,27 @@ export class RegisteredEntity {
|
|
|
48
48
|
warn(35, 'addPageAction')
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Records a custom event with a specified eventType and attributes.
|
|
53
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordCustomEvent/}
|
|
54
|
+
* @param {string} eventType The eventType to store the event as.
|
|
55
|
+
* @param {Object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}.
|
|
56
|
+
*/
|
|
57
|
+
recordCustomEvent (eventType, attributes) {
|
|
58
|
+
warn(35, 'recordCustomEvent')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Measures a task that is recorded as a BrowserPerformance event.
|
|
63
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
|
|
64
|
+
* @param {string} name The name of the task
|
|
65
|
+
* @param {{start: number, end: number, duration: number, customAttributes: object}} [options] An object used to control the way the measure API operates
|
|
66
|
+
* @returns {{start: number, end: number, duration: number, customAttributes: object}} Measurement details
|
|
67
|
+
*/
|
|
68
|
+
measure (name, options) {
|
|
69
|
+
warn(35, 'measure')
|
|
70
|
+
}
|
|
71
|
+
|
|
51
72
|
/**
|
|
52
73
|
* Adds a user-defined attribute name and value to subsequent events on the page for the registered target. Note -- the persist flag does not work with the register API.
|
|
53
74
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcustomattribute/}
|
package/src/loaders/agent.js
CHANGED
|
@@ -23,6 +23,7 @@ import { setupSetCustomAttributeAPI } from './api/setCustomAttribute'
|
|
|
23
23
|
import { setupSetUserIdAPI } from './api/setUserId'
|
|
24
24
|
import { setupSetApplicationVersionAPI } from './api/setApplicationVersion'
|
|
25
25
|
import { setupStartAPI } from './api/start'
|
|
26
|
+
import { setupConsentAPI } from './api/consent'
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* @typedef {Object} AgentOptions
|
|
@@ -70,6 +71,7 @@ export class Agent extends AgentBase {
|
|
|
70
71
|
setupSetUserIdAPI(this)
|
|
71
72
|
setupSetApplicationVersionAPI(this)
|
|
72
73
|
setupStartAPI(this)
|
|
74
|
+
setupConsentAPI(this)
|
|
73
75
|
|
|
74
76
|
this.run()
|
|
75
77
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { prefix, CONSENT } from './constants'
|
|
6
|
+
import { setupAPI } from './sharedHandlers'
|
|
7
|
+
import { handle } from '../../common/event-emitter/handle'
|
|
8
|
+
import { warn } from '../../common/util/console'
|
|
9
|
+
|
|
10
|
+
export function setupConsentAPI (agent) {
|
|
11
|
+
setupAPI(CONSENT, function (accept) {
|
|
12
|
+
if (accept !== undefined && typeof accept !== 'boolean') {
|
|
13
|
+
warn(65, typeof accept)
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
handle(prefix + CONSENT, [accept], undefined, 'session', agent.ee)
|
|
17
|
+
}, agent)
|
|
18
|
+
}
|
|
@@ -10,44 +10,45 @@ import { prefix, MEASURE } from './constants'
|
|
|
10
10
|
import { setupAPI } from './sharedHandlers'
|
|
11
11
|
|
|
12
12
|
export function setupMeasureAPI (agent) {
|
|
13
|
-
setupAPI(MEASURE,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
setupAPI(MEASURE, (name, options) => measure(name, options, agent), agent)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function measure (name, options, agentRef, target, timestamp = now()) {
|
|
17
|
+
const { start, end, customAttributes } = options || {}
|
|
18
|
+
const returnObj = { customAttributes: customAttributes || {} }
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if (typeof returnObj.customAttributes !== 'object' || typeof name !== 'string' || name.length === 0) {
|
|
21
|
+
warn(57)
|
|
22
|
+
return
|
|
23
|
+
}
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
/**
|
|
24
26
|
* getValueFromTiming - Helper function to extract a numeric value from a supplied option.
|
|
25
27
|
* @param {Number|PerformanceMark} [timing] The timing value
|
|
26
28
|
* @param {Number} [d] The default value to return if timing is invalid
|
|
27
29
|
* @returns {Number} The timing value or the default value
|
|
28
30
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}, agent)
|
|
31
|
+
const getValueFromTiming = (timing, d) => {
|
|
32
|
+
if (timing == null) return d
|
|
33
|
+
if (typeof timing === 'number') return timing
|
|
34
|
+
if (timing instanceof PerformanceMark) return timing.startTime
|
|
35
|
+
return Number.NaN
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
returnObj.start = getValueFromTiming(start, 0)
|
|
39
|
+
returnObj.end = getValueFromTiming(end, timestamp)
|
|
40
|
+
if (Number.isNaN(returnObj.start) || Number.isNaN(returnObj.end)) {
|
|
41
|
+
warn(57)
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
returnObj.duration = returnObj.end - returnObj.start
|
|
46
|
+
if (returnObj.duration < 0) {
|
|
47
|
+
warn(58)
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
handle(prefix + MEASURE, [returnObj, name, target], undefined, FEATURE_NAMES.genericEvents, agentRef.ee)
|
|
52
|
+
|
|
53
|
+
return returnObj
|
|
53
54
|
}
|
|
@@ -9,7 +9,9 @@ import { prefix, RECORD_CUSTOM_EVENT } from './constants'
|
|
|
9
9
|
import { setupAPI } from './sharedHandlers'
|
|
10
10
|
|
|
11
11
|
export function setupRecordCustomEventAPI (agent) {
|
|
12
|
-
setupAPI(RECORD_CUSTOM_EVENT,
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
setupAPI(RECORD_CUSTOM_EVENT, (eventType, attributes) => recordCustomEvent(eventType, attributes, agent), agent)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function recordCustomEvent (eventType, attributes = {}, agentRef, target, timestamp = now()) {
|
|
16
|
+
handle(prefix + RECORD_CUSTOM_EVENT, [timestamp, eventType, attributes, target], undefined, FEATURE_NAMES.genericEvents, agentRef.ee)
|
|
15
17
|
}
|
|
@@ -5,20 +5,21 @@
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @typedef {Object} RegisterAPI
|
|
8
|
-
* @property {
|
|
9
|
-
* @property {
|
|
10
|
-
* @property {
|
|
11
|
-
* @property {
|
|
12
|
-
* @property {
|
|
13
|
-
* @property {
|
|
8
|
+
* @property {(name: string, attributes?: object) => void} addPageAction - Add a page action for the registered entity.
|
|
9
|
+
* @property {(message: string, options?: { customAttributes?: object, level?: 'ERROR' | 'TRACE' | 'DEBUG' | 'INFO' | 'WARN'}) => void} log - Capture a log for the registered entity.
|
|
10
|
+
* @property {(error: Error | string, customAttributes?: object) => void} noticeError - Notice an error for the registered entity.
|
|
11
|
+
* @property {(eventType: string, attributes?: Object) => void} recordCustomEvent - Record a custom event for the registered entity.
|
|
12
|
+
* @property {(eventType: string, options?: {start: number, end: number, duration: number, customAttributes: object}) => {{start: number, end: number, duration: number, customAttributes: object}}} measure - Measures a task that is recorded as a BrowserPerformance event.
|
|
13
|
+
* @property {(value: string | null) => void} setApplicationVersion - Add an application.version attribute to all outgoing data for the registered entity.
|
|
14
|
+
* @property {(name: string, value: string | number | boolean | null, persist?: boolean) => void} setCustomAttribute - Add a custom attribute to outgoing data for the registered entity.
|
|
15
|
+
* @property {(value: string | null) => void} setUserId - Add an enduser.id attribute to all outgoing API data for the registered entity.
|
|
14
16
|
* @property {RegisterAPIMetadata} metadata - The metadata object containing the custom attributes and target information for the registered entity.
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* @typedef {Object} RegisterAPIConstructor
|
|
19
|
-
* @property {
|
|
20
|
-
* @property {string}
|
|
21
|
-
* @property {string} opts.name - The readable name for the registered entity. This will be assigned to any synthesized entities.
|
|
21
|
+
* @property {string|number} id - The unique id for the registered entity. This will be assigned to any synthesized entities.
|
|
22
|
+
* @property {string} name - The readable name for the registered entity. This will be assigned to any synthesized entities.
|
|
22
23
|
*/
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { handle } from '../../common/event-emitter/handle'
|
|
6
6
|
import { warn } from '../../common/util/console'
|
|
7
|
-
import { isValidMFETarget } from '../../common/util/mfe'
|
|
7
|
+
import { hasValidValue, isValidMFETarget } from '../../common/util/mfe'
|
|
8
8
|
import { FEATURE_NAMES } from '../features/features'
|
|
9
9
|
import { now } from '../../common/timing/now'
|
|
10
10
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants'
|
|
@@ -14,6 +14,8 @@ import { log } from './log'
|
|
|
14
14
|
import { addPageAction } from './addPageAction'
|
|
15
15
|
import { noticeError } from './noticeError'
|
|
16
16
|
import { single } from '../../common/util/invoke'
|
|
17
|
+
import { measure } from './measure'
|
|
18
|
+
import { recordCustomEvent } from './recordCustomEvent'
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* @typedef {import('./register-api-types').RegisterAPI} RegisterAPI
|
|
@@ -73,12 +75,17 @@ export function buildRegisterApi (agentRef, target) {
|
|
|
73
75
|
/** primary cases that can block the register API from working at init time */
|
|
74
76
|
if (!agentRef.init.api.allow_registered_children) block(single(() => warn(55)))
|
|
75
77
|
if (!isValidMFETarget(target)) block(single(() => warn(48, target)))
|
|
78
|
+
if (!hasValidValue(target.id) || !hasValidValue(target.name)) {
|
|
79
|
+
block(single(() => warn(48, target)))
|
|
80
|
+
}
|
|
76
81
|
|
|
77
82
|
/** @type {RegisterAPI} */
|
|
78
83
|
const api = {
|
|
79
84
|
addPageAction: (name, attributes = {}) => report(addPageAction, [name, { ...attrs, ...attributes }, agentRef], target),
|
|
80
85
|
log: (message, options = {}) => report(log, [message, { ...options, customAttributes: { ...attrs, ...(options.customAttributes || {}) } }, agentRef], target),
|
|
86
|
+
measure: (name, options = {}) => report(measure, [name, { ...options, customAttributes: { ...attrs, ...(options.customAttributes || {}) } }, agentRef], target),
|
|
81
87
|
noticeError: (error, attributes = {}) => report(noticeError, [error, { ...attrs, ...attributes }, agentRef], target),
|
|
88
|
+
recordCustomEvent: (eventType, attributes = {}) => report(recordCustomEvent, [eventType, { ...attrs, ...attributes }, agentRef], target),
|
|
82
89
|
setApplicationVersion: (value) => setLocalValue('application.version', value),
|
|
83
90
|
setCustomAttribute: (key, value) => setLocalValue(key, value),
|
|
84
91
|
setUserId: (value) => setLocalValue('enduser.id', value),
|
package/src/loaders/api-base.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { warn } from '../common/util/console'
|
|
6
|
-
import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER, MEASURE } from './api/constants'
|
|
6
|
+
import { ADD_PAGE_ACTION, ADD_RELEASE, ADD_TO_TRACE, CONSENT, FINISHED, INTERACTION, LOG, NOTICE_ERROR, PAUSE_REPLAY, RECORD_CUSTOM_EVENT, RECORD_REPLAY, REGISTER, SET_APPLICATION_VERSION, SET_CURRENT_ROUTE_NAME, SET_CUSTOM_ATTRIBUTE, SET_ERROR_HANDLER, SET_PAGE_VIEW_NAME, SET_USER_ID, START, WRAP_LOGGER, MEASURE } from './api/constants'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance
|
|
@@ -32,11 +32,8 @@ export class ApiBase {
|
|
|
32
32
|
* It is not recommended for use in production environments and will not receive support for issues.
|
|
33
33
|
*
|
|
34
34
|
* Registers an external caller to report through the base agent to a different target than the base agent.
|
|
35
|
-
* @param {
|
|
36
|
-
|
|
37
|
-
* @param {string} target.applicationID The applicationID to report data to
|
|
38
|
-
* @param {string=} target.entityGuid The entityGuid to report data to
|
|
39
|
-
* @returns {object} Returns an object that contains the available API methods and configurations to use with the external caller. See loaders/api/api.js for more information.
|
|
35
|
+
* @param {import('./api/register-api-types').RegisterAPIConstructor} target the target object to report data to
|
|
36
|
+
@returns {import('./api/register-api-types').RegisterAPI} Returns an object that contains the available API methods and configurations to use with the external caller. See loaders/api/api.js for more information.
|
|
40
37
|
*/
|
|
41
38
|
register (target) {
|
|
42
39
|
return this.#callMethod(REGISTER, target)
|
|
@@ -46,7 +43,7 @@ export class ApiBase {
|
|
|
46
43
|
* Records a custom event with a specified eventType and attributes.
|
|
47
44
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordCustomEvent/}
|
|
48
45
|
* @param {string} eventType The eventType to store the event as.
|
|
49
|
-
* @param {
|
|
46
|
+
* @param {Object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}.
|
|
50
47
|
*/
|
|
51
48
|
recordCustomEvent (eventType, attributes) {
|
|
52
49
|
return this.#callMethod(RECORD_CUSTOM_EVENT, eventType, attributes)
|
|
@@ -222,10 +219,20 @@ export class ApiBase {
|
|
|
222
219
|
* Measures a task that is recorded as a BrowserPerformance event.
|
|
223
220
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
|
|
224
221
|
* @param {string} name The name of the task
|
|
225
|
-
* @param {object
|
|
222
|
+
* @param {{start: number, end: number, duration: number, customAttributes: object}} [options] An object used to control the way the measure API operates
|
|
226
223
|
* @returns {{start: number, end: number, duration: number, customAttributes: object}} Measurement details
|
|
227
224
|
*/
|
|
228
225
|
measure (name, options) {
|
|
229
226
|
return this.#callMethod(MEASURE, name, options)
|
|
230
227
|
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Accepts or rejects consent when the agent is configured to require consent before harvesting.
|
|
231
|
+
* The consent state is stored in session storage inside the NRBA_SESSION object.
|
|
232
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/consent/}
|
|
233
|
+
* @param {boolean?} accept Whether to accept or reject consent. Defaults to true (accept) if left undefined.
|
|
234
|
+
*/
|
|
235
|
+
consent (accept) {
|
|
236
|
+
return this.#callMethod(CONSENT, accept)
|
|
237
|
+
}
|
|
231
238
|
}
|