@newrelic/browser-agent 1.302.0-rc.1 → 1.302.0-rc.11
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 +8 -5
- 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 +14 -10
- package/dist/cjs/loaders/api/register.js +25 -10
- 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 +7 -5
- 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 +14 -10
- package/dist/esm/loaders/api/register.js +26 -10
- 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 +3 -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 +43 -12
- package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
- package/dist/types/loaders/api/register.d.ts +0 -16
- 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 +10 -4
- 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 +14 -10
- package/src/loaders/api/register.js +17 -10
- package/src/loaders/api-base.js +15 -8
|
@@ -17,14 +17,19 @@ 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
|
export class Aggregate extends AggregateBase {
|
|
21
24
|
static featureName = CONSTANTS.FEATURE_NAME;
|
|
22
25
|
constructor(agentRef) {
|
|
23
26
|
super(agentRef, CONSTANTS.FEATURE_NAME);
|
|
27
|
+
this.sentRum = false; // flag to facilitate calling sendRum() once externally (by the consent API in agent-session.js)
|
|
28
|
+
|
|
24
29
|
this.timeToFirstByte = 0;
|
|
25
30
|
this.firstByteToWindowLoad = 0; // our "frontend" duration
|
|
26
31
|
this.firstByteToDomContent = 0; // our "dom processing" duration
|
|
27
|
-
|
|
32
|
+
this.retries = 0;
|
|
28
33
|
if (!isValid(agentRef.info)) {
|
|
29
34
|
this.ee.abort();
|
|
30
35
|
return warn(43);
|
|
@@ -52,12 +57,8 @@ export class Aggregate extends AggregateBase {
|
|
|
52
57
|
*
|
|
53
58
|
* @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
|
|
54
59
|
* @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
|
|
55
|
-
* @param {*} target The target to harvest to
|
|
56
60
|
*/
|
|
57
|
-
sendRum(customAttributes = this.agentRef.info.jsAttributes
|
|
58
|
-
licenseKey: this.agentRef.info.licenseKey,
|
|
59
|
-
applicationID: this.agentRef.info.applicationID
|
|
60
|
-
}) {
|
|
61
|
+
sendRum(customAttributes = this.agentRef.info.jsAttributes) {
|
|
61
62
|
const info = this.agentRef.info;
|
|
62
63
|
const measures = {};
|
|
63
64
|
if (info.queueTime) measures.qt = info.queueTime;
|
|
@@ -108,27 +109,30 @@ export class Aggregate extends AggregateBase {
|
|
|
108
109
|
}
|
|
109
110
|
queryParameters.fp = firstPaint.current.value;
|
|
110
111
|
queryParameters.fcp = firstContentfulPaint.current.value;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
needResponse: true,
|
|
112
|
+
this.queryStringsBuilder = () => {
|
|
113
|
+
// this will be called by AggregateBase.makeHarvestPayload every time harvest is triggered to be qs
|
|
114
|
+
this.rumStartTime = now(); // this should be reset at the beginning of each RUM call for proper timeKeeper calculation in coordination with postHarvestCleanup
|
|
115
|
+
const timeKeeper = this.agentRef.runtime.timeKeeper;
|
|
116
|
+
if (timeKeeper?.ready) {
|
|
117
|
+
queryParameters.timestamp = Math.floor(timeKeeper.correctRelativeTimestamp(this.rumStartTime));
|
|
118
|
+
}
|
|
119
|
+
return queryParameters;
|
|
120
|
+
};
|
|
121
|
+
this.events.add(body);
|
|
122
|
+
if (this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
125
123
|
sendEmptyBody: true
|
|
126
|
-
});
|
|
124
|
+
}).ranSend) this.sentRum = true;
|
|
125
|
+
}
|
|
126
|
+
serializer(eventBuffer) {
|
|
127
|
+
// this is necessary because PVE sends a single item rather than an array; in the case of undefined, this prevents sending [null] as body
|
|
128
|
+
return eventBuffer[0];
|
|
127
129
|
}
|
|
128
130
|
postHarvestCleanup({
|
|
131
|
+
sent,
|
|
129
132
|
status,
|
|
130
133
|
responseText,
|
|
131
|
-
xhr
|
|
134
|
+
xhr,
|
|
135
|
+
retry
|
|
132
136
|
}) {
|
|
133
137
|
const rumEndTime = now();
|
|
134
138
|
let app, flags;
|
|
@@ -141,8 +145,65 @@ export class Aggregate extends AggregateBase {
|
|
|
141
145
|
// wont set entity stuff here, if main agent will later abort, if registered agent, nothing will happen
|
|
142
146
|
warn(53, error);
|
|
143
147
|
}
|
|
148
|
+
super.postHarvestCleanup({
|
|
149
|
+
sent,
|
|
150
|
+
retry
|
|
151
|
+
}); // this will set isRetrying & re-buffer the body if request is to be retried
|
|
152
|
+
if (this.isRetrying && this.retries++ < 1) {
|
|
153
|
+
// Only retry once
|
|
154
|
+
setTimeout(() => this.agentRef.runtime.harvester.triggerHarvestFor(this, {
|
|
155
|
+
sendEmptyBody: true
|
|
156
|
+
}), 5000); // Retry sending the RUM event after 5 seconds
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
144
159
|
if (status >= 400 || status === 0) {
|
|
145
160
|
warn(18, status);
|
|
161
|
+
this.blocked = true;
|
|
162
|
+
|
|
163
|
+
// Get estimated payload size of our backlog
|
|
164
|
+
const textEncoder = new TextEncoder();
|
|
165
|
+
const payloadSize = Object.values(newrelic.ee.backlog).reduce((acc, value) => {
|
|
166
|
+
if (!value) return acc;
|
|
167
|
+
const encoded = textEncoder.encode(value);
|
|
168
|
+
return acc + encoded.byteLength;
|
|
169
|
+
}, 0);
|
|
170
|
+
|
|
171
|
+
// Send SMs about failed RUM request
|
|
172
|
+
const body = {
|
|
173
|
+
sm: [{
|
|
174
|
+
params: {
|
|
175
|
+
name: "Browser/Supportability/BCS/Error/".concat(status)
|
|
176
|
+
},
|
|
177
|
+
stats: {
|
|
178
|
+
c: 1
|
|
179
|
+
}
|
|
180
|
+
}, {
|
|
181
|
+
params: {
|
|
182
|
+
name: 'Browser/Supportability/BCS/Error/Dropped/Bytes'
|
|
183
|
+
},
|
|
184
|
+
stats: {
|
|
185
|
+
c: 1,
|
|
186
|
+
t: payloadSize
|
|
187
|
+
}
|
|
188
|
+
}, {
|
|
189
|
+
params: {
|
|
190
|
+
name: 'Browser/Supportability/BCS/Error/Duration/Ms'
|
|
191
|
+
},
|
|
192
|
+
stats: {
|
|
193
|
+
c: 1,
|
|
194
|
+
t: rumEndTime - this.rumStartTime
|
|
195
|
+
}
|
|
196
|
+
}]
|
|
197
|
+
};
|
|
198
|
+
send(this.agentRef, {
|
|
199
|
+
endpoint: FEATURE_TO_ENDPOINT[FEATURE_NAMES.metrics],
|
|
200
|
+
payload: {
|
|
201
|
+
body
|
|
202
|
+
},
|
|
203
|
+
submitMethod: getSubmitMethod(),
|
|
204
|
+
featureName: FEATURE_NAMES.metrics
|
|
205
|
+
});
|
|
206
|
+
|
|
146
207
|
// Adding retry logic for the rum call will be a separate change; this.blocked will need to be changed since that prevents another triggerHarvestFor()
|
|
147
208
|
this.ee.abort();
|
|
148
209
|
return;
|
|
@@ -161,6 +222,7 @@ export class Aggregate extends AggregateBase {
|
|
|
161
222
|
}
|
|
162
223
|
} catch (error) {
|
|
163
224
|
this.ee.abort();
|
|
225
|
+
this.blocked = true;
|
|
164
226
|
warn(17, error);
|
|
165
227
|
return;
|
|
166
228
|
}
|
|
@@ -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';
|
|
@@ -19,9 +18,6 @@ export class Instrument extends InstrumentBase {
|
|
|
19
18
|
|
|
20
19
|
/** feature specific APIs */
|
|
21
20
|
setupSetPageViewNameAPI(agentRef);
|
|
22
|
-
|
|
23
|
-
/** messages from the register API that can trigger a new RUM call */
|
|
24
|
-
this.ee.on('api-send-rum', (attrs, target) => handle('send-rum', [attrs, target], undefined, this.featureName, this.ee));
|
|
25
21
|
this.importAggregator(agentRef, () => import(/* webpackChunkName: "page_view_event-aggregate" */'../aggregate'));
|
|
26
22
|
}
|
|
27
23
|
setupInspectionEvents(agentIdentifier) {
|
|
@@ -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
|
export class Aggregate extends AggregateBase {
|
|
25
26
|
static featureName = FEATURE_NAME;
|
|
26
27
|
mode = MODE.OFF;
|
|
@@ -72,10 +73,10 @@ export class Aggregate extends AggregateBase {
|
|
|
72
73
|
if (this.mode !== MODE.OFF && data.sessionReplayMode === MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);
|
|
73
74
|
this.mode = data.sessionReplayMode;
|
|
74
75
|
});
|
|
75
|
-
registerHandler(
|
|
76
|
+
registerHandler(PAUSE_REPLAY, () => {
|
|
76
77
|
this.forceStop(this.mode === MODE.FULL);
|
|
77
78
|
}, this.featureName, this.ee);
|
|
78
|
-
registerHandler(
|
|
79
|
+
registerHandler(ERROR_DURING_REPLAY, e => {
|
|
79
80
|
this.handleError(e);
|
|
80
81
|
}, this.featureName, this.ee);
|
|
81
82
|
const {
|
|
@@ -5,11 +5,7 @@
|
|
|
5
5
|
import { MODE } from '../../common/session/constants';
|
|
6
6
|
import { FEATURE_NAMES } from '../../loaders/features/features';
|
|
7
7
|
export const FEATURE_NAME = FEATURE_NAMES.sessionReplay;
|
|
8
|
-
export const
|
|
9
|
-
RECORD: 'recordReplay',
|
|
10
|
-
PAUSE: 'pauseReplay',
|
|
11
|
-
ERROR_DURING_REPLAY: 'errorDuringReplay'
|
|
12
|
-
};
|
|
8
|
+
export const ERROR_DURING_REPLAY = 'errorDuringReplay';
|
|
13
9
|
export const AVG_COMPRESSION = 0.12;
|
|
14
10
|
export const RRWEB_EVENT_TYPES = {
|
|
15
11
|
DomContentLoaded: 0,
|
|
@@ -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
|
export class Instrument extends InstrumentBase {
|
|
17
18
|
static featureName = FEATURE_NAME;
|
|
18
19
|
/** @type {Promise|undefined} A promise that resolves when the recorder module is imported and added to the class. Undefined if the recorder has never been staged to import with `importRecorder`. */
|
|
@@ -30,7 +31,7 @@ export class Instrument extends InstrumentBase {
|
|
|
30
31
|
session = JSON.parse(localStorage.getItem("".concat(PREFIX, "_").concat(DEFAULT_KEY)));
|
|
31
32
|
} catch (err) {}
|
|
32
33
|
if (hasReplayPrerequisite(agentRef.init)) {
|
|
33
|
-
this.ee.on(
|
|
34
|
+
this.ee.on(RECORD_REPLAY, () => this.#apiStartOrRestartReplay());
|
|
34
35
|
}
|
|
35
36
|
if (this.#canPreloadRecorder(session)) {
|
|
36
37
|
this.importRecorder().then(recorder => {
|
|
@@ -44,7 +45,7 @@ export class Instrument extends InstrumentBase {
|
|
|
44
45
|
if (this.blocked) return;
|
|
45
46
|
if (this.agentRef.runtime.isRecording) {
|
|
46
47
|
this.errorNoticed = true;
|
|
47
|
-
handle(
|
|
48
|
+
handle(ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee);
|
|
48
49
|
}
|
|
49
50
|
});
|
|
50
51
|
}
|
|
@@ -52,6 +52,19 @@ export function setupAgentSession(agentRef) {
|
|
|
52
52
|
registerHandler('api-setUserId', (time, key, value) => {
|
|
53
53
|
agentRef.runtime.session.syncCustomAttribute(key, value);
|
|
54
54
|
}, 'session', sharedEE);
|
|
55
|
+
registerHandler('api-consent', accept => {
|
|
56
|
+
agentRef.runtime.session.write({
|
|
57
|
+
consent: accept === undefined ? true : accept
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// call sendRum if it wasn't called yet
|
|
61
|
+
agentRef.features.page_view_event.onAggregateImported.then(loaded => {
|
|
62
|
+
const pveAgg = agentRef.features.page_view_event.featAggregate;
|
|
63
|
+
if (loaded && !pveAgg.sentRum) {
|
|
64
|
+
pveAgg.sendRum();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}, 'session', sharedEE);
|
|
55
68
|
drain(agentRef.agentIdentifier, 'session');
|
|
56
69
|
return agentRef.runtime.session;
|
|
57
70
|
}
|
|
@@ -48,7 +48,10 @@ export class InstrumentBase extends FeatureBase {
|
|
|
48
48
|
* @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
|
|
49
49
|
* one another if there are inter-features dependencies.
|
|
50
50
|
*/
|
|
51
|
-
this.
|
|
51
|
+
this.loadedSuccessfully = undefined;
|
|
52
|
+
this.onAggregateImported = new Promise(resolve => {
|
|
53
|
+
this.loadedSuccessfully = resolve;
|
|
54
|
+
});
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* used in conjunction with newrelic.start() to defer harvesting in features
|
|
@@ -80,10 +83,6 @@ export class InstrumentBase extends FeatureBase {
|
|
|
80
83
|
*/
|
|
81
84
|
importAggregator(agentRef, fetchAggregator, argsObjFromInstrument = {}) {
|
|
82
85
|
if (this.featAggregate) return;
|
|
83
|
-
let loadedSuccessfully;
|
|
84
|
-
this.onAggregateImported = new Promise(resolve => {
|
|
85
|
-
loadedSuccessfully = resolve;
|
|
86
|
-
});
|
|
87
86
|
const importLater = async () => {
|
|
88
87
|
// wait for the deferred promise to resolve before proceeding
|
|
89
88
|
// this will resolve immediately if the feature is auto-started,
|
|
@@ -111,7 +110,7 @@ export class InstrumentBase extends FeatureBase {
|
|
|
111
110
|
try {
|
|
112
111
|
if (!this.#shouldImportAgg(this.featureName, session, agentRef.init)) {
|
|
113
112
|
drain(this.agentIdentifier, this.featureName);
|
|
114
|
-
loadedSuccessfully(false); // aggregate module isn't loaded at all
|
|
113
|
+
this.loadedSuccessfully(false); // aggregate module isn't loaded at all
|
|
115
114
|
return;
|
|
116
115
|
}
|
|
117
116
|
const {
|
|
@@ -119,13 +118,13 @@ export class InstrumentBase extends FeatureBase {
|
|
|
119
118
|
} = await fetchAggregator();
|
|
120
119
|
this.featAggregate = new Aggregate(agentRef, argsObjFromInstrument);
|
|
121
120
|
agentRef.runtime.harvester.initializedAggregates.push(this.featAggregate); // "subscribe" the feature to future harvest intervals (PVE will start the timer)
|
|
122
|
-
loadedSuccessfully(true);
|
|
121
|
+
this.loadedSuccessfully(true);
|
|
123
122
|
} catch (e) {
|
|
124
123
|
warn(34, e);
|
|
125
124
|
this.abortHandler?.(); // undo any important alterations made to the page
|
|
126
125
|
// not supported yet but nice to do: "abort" this agent's EE for this feature specifically
|
|
127
126
|
drain(this.agentIdentifier, this.featureName, true);
|
|
128
|
-
loadedSuccessfully(false);
|
|
127
|
+
this.loadedSuccessfully(false);
|
|
129
128
|
if (this.ee) this.ee.abort();
|
|
130
129
|
}
|
|
131
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/}
|
|
@@ -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
|
|
@@ -67,6 +68,7 @@ export class Agent extends AgentBase {
|
|
|
67
68
|
setupSetUserIdAPI(this);
|
|
68
69
|
setupSetApplicationVersionAPI(this);
|
|
69
70
|
setupStartAPI(this);
|
|
71
|
+
setupConsentAPI(this);
|
|
70
72
|
this.run();
|
|
71
73
|
}
|
|
72
74
|
get config() {
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
export function setupConsentAPI(agent) {
|
|
10
|
+
setupAPI(CONSENT, function (accept) {
|
|
11
|
+
if (accept !== undefined && typeof accept !== 'boolean') {
|
|
12
|
+
warn(65, typeof accept);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
handle(prefix + CONSENT, [accept], undefined, 'session', agent.ee);
|
|
16
|
+
}, agent);
|
|
17
|
+
}
|
|
@@ -23,4 +23,5 @@ export const SET_PAGE_VIEW_NAME = 'setPageViewName';
|
|
|
23
23
|
export const SET_USER_ID = 'setUserId';
|
|
24
24
|
export const START = 'start';
|
|
25
25
|
export const WRAP_LOGGER = 'wrapLogger';
|
|
26
|
-
export const MEASURE = 'measure';
|
|
26
|
+
export const MEASURE = 'measure';
|
|
27
|
+
export const CONSENT = 'consent';
|
|
@@ -9,45 +9,45 @@ import { FEATURE_NAMES } from '../features/features';
|
|
|
9
9
|
import { prefix, MEASURE } from './constants';
|
|
10
10
|
import { setupAPI } from './sharedHandlers';
|
|
11
11
|
export function setupMeasureAPI(agent) {
|
|
12
|
-
setupAPI(MEASURE,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
setupAPI(MEASURE, (name, options) => measure(name, options, agent), agent);
|
|
13
|
+
}
|
|
14
|
+
export function measure(name, options, agentRef, target, timestamp = now()) {
|
|
15
|
+
const {
|
|
16
|
+
start,
|
|
17
|
+
end,
|
|
18
|
+
customAttributes
|
|
19
|
+
} = options || {};
|
|
20
|
+
const returnObj = {
|
|
21
|
+
customAttributes: customAttributes || {}
|
|
22
|
+
};
|
|
23
|
+
if (typeof returnObj.customAttributes !== 'object' || typeof name !== 'string' || name.length === 0) {
|
|
24
|
+
warn(57);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
/**
|
|
28
29
|
* getValueFromTiming - Helper function to extract a numeric value from a supplied option.
|
|
29
30
|
* @param {Number|PerformanceMark} [timing] The timing value
|
|
30
31
|
* @param {Number} [d] The default value to return if timing is invalid
|
|
31
32
|
* @returns {Number} The timing value or the default value
|
|
32
33
|
*/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}, agent);
|
|
34
|
+
const getValueFromTiming = (timing, d) => {
|
|
35
|
+
if (timing == null) return d;
|
|
36
|
+
if (typeof timing === 'number') return timing;
|
|
37
|
+
if (timing instanceof PerformanceMark) return timing.startTime;
|
|
38
|
+
return Number.NaN;
|
|
39
|
+
};
|
|
40
|
+
returnObj.start = getValueFromTiming(start, 0);
|
|
41
|
+
returnObj.end = getValueFromTiming(end, timestamp);
|
|
42
|
+
if (Number.isNaN(returnObj.start) || Number.isNaN(returnObj.end)) {
|
|
43
|
+
warn(57);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
returnObj.duration = returnObj.end - returnObj.start;
|
|
47
|
+
if (returnObj.duration < 0) {
|
|
48
|
+
warn(58);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
handle(prefix + MEASURE, [returnObj, name, target], undefined, FEATURE_NAMES.genericEvents, agentRef.ee);
|
|
52
|
+
return returnObj;
|
|
53
53
|
}
|
|
@@ -8,7 +8,8 @@ import { FEATURE_NAMES } from '../features/features';
|
|
|
8
8
|
import { prefix, RECORD_CUSTOM_EVENT } from './constants';
|
|
9
9
|
import { setupAPI } from './sharedHandlers';
|
|
10
10
|
export function setupRecordCustomEventAPI(agent) {
|
|
11
|
-
setupAPI(RECORD_CUSTOM_EVENT,
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
setupAPI(RECORD_CUSTOM_EVENT, (eventType, attributes) => recordCustomEvent(eventType, attributes, agent), agent);
|
|
12
|
+
}
|
|
13
|
+
export function recordCustomEvent(eventType, attributes = {}, agentRef, target, timestamp = now()) {
|
|
14
|
+
handle(prefix + RECORD_CUSTOM_EVENT, [timestamp, eventType, attributes, target], undefined, FEATURE_NAMES.genericEvents, agentRef.ee);
|
|
14
15
|
}
|
|
@@ -5,29 +5,33 @@
|
|
|
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 {(target: RegisterAPIConstructor) => RegisterAPI} register - Record a custom event for the registered entity.
|
|
12
|
+
* @property {(eventType: string, attributes?: Object) => void} recordCustomEvent - Record a custom event for the registered entity.
|
|
13
|
+
* @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.
|
|
14
|
+
* @property {(value: string | null) => void} setApplicationVersion - Add an application.version attribute to all outgoing data for the registered entity.
|
|
15
|
+
* @property {(name: string, value: string | number | boolean | null, persist?: boolean) => void} setCustomAttribute - Add a custom attribute to outgoing data for the registered entity.
|
|
16
|
+
* @property {(value: string | null) => void} setUserId - Add an enduser.id attribute to all outgoing API data for the registered entity.
|
|
14
17
|
* @property {RegisterAPIMetadata} metadata - The metadata object containing the custom attributes and target information for the registered entity.
|
|
15
18
|
*/
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* @typedef {Object} RegisterAPIConstructor
|
|
19
|
-
* @property {
|
|
20
|
-
* @property {string}
|
|
21
|
-
* @property {string}
|
|
22
|
+
* @property {string|number} id - The unique id for the registered entity. This will be assigned to any synthesized entities.
|
|
23
|
+
* @property {string} name - The readable name for the registered entity. This will be assigned to any synthesized entities.
|
|
24
|
+
* @property {string} [parentId] - The parentId for the registered entity. If none was supplied, it will assume the entity guid from the main agent.
|
|
22
25
|
*/
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
28
|
* @typedef {Object} RegisterAPIMetadata
|
|
26
29
|
* @property {Object} customAttributes - The custom attributes for the registered entity.
|
|
27
30
|
* @property {Object} target - The options for the registered entity.
|
|
28
|
-
* @property {string} target.licenseKey - The license key for the registered entity. If none was supplied, it will assume the license key from the main agent.
|
|
31
|
+
* @property {string} [target.licenseKey] - The license key for the registered entity. If none was supplied, it will assume the license key from the main agent.
|
|
29
32
|
* @property {string} target.id - The ID for the registered entity.
|
|
30
33
|
* @property {string} target.name - The name returned for the registered entity.
|
|
34
|
+
* @property {string} [target.parentId] - The parentId for the registered entity. If none was supplied, it will assume the entity guid from the main agent.
|
|
31
35
|
*/
|
|
32
36
|
|
|
33
37
|
export default {};
|
|
@@ -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
|
|
@@ -26,7 +28,7 @@ import { single } from '../../common/util/invoke';
|
|
|
26
28
|
*/
|
|
27
29
|
export function setupRegisterAPI(agent) {
|
|
28
30
|
setupAPI(REGISTER, function (target) {
|
|
29
|
-
return
|
|
31
|
+
return register(agent, target);
|
|
30
32
|
}, agent);
|
|
31
33
|
}
|
|
32
34
|
|
|
@@ -34,19 +36,18 @@ export function setupRegisterAPI(agent) {
|
|
|
34
36
|
* Builds the api object that will be returned from the register api method.
|
|
35
37
|
* Also conducts certain side-effects, such as harvesting a PageView event when triggered and gathering metadata for the registered entity.
|
|
36
38
|
* @param {Object} agentRef the reference to the base agent instance
|
|
37
|
-
* @param {
|
|
38
|
-
* @param {
|
|
39
|
-
* @param {string} [target.licenseKey] the license key of the target to report data to
|
|
40
|
-
* @param {string} target.id the entity ID of the target to report data to
|
|
41
|
-
* @param {string} target.name the entity name of the target to report data to
|
|
39
|
+
* @param {import('./register-api-types').RegisterAPIConstructor} target
|
|
40
|
+
* @param {import('./register-api-types').RegisterAPIConstructor} [parent]
|
|
42
41
|
* @returns {RegisterAPI} the api object to be returned from the register api method
|
|
43
42
|
*/
|
|
44
|
-
|
|
43
|
+
function register(agentRef, target, parent) {
|
|
45
44
|
const attrs = {};
|
|
46
45
|
warn(54, 'newrelic.register');
|
|
47
46
|
target ||= {};
|
|
47
|
+
target.eventSource = 'MicroFrontendBrowserAgent';
|
|
48
48
|
target.licenseKey ||= agentRef.info.licenseKey; // will inherit the license key from the container agent if not provided for brevity. A future state may dictate that we need different license keys to do different things.
|
|
49
49
|
target.blocked = false;
|
|
50
|
+
target.parent = parent || {};
|
|
50
51
|
|
|
51
52
|
/** @type {Function} a function that is set and reports when APIs are triggered -- warns the customer of the invalid state */
|
|
52
53
|
let invalidApiResponse = () => {};
|
|
@@ -79,6 +80,9 @@ export function buildRegisterApi(agentRef, target) {
|
|
|
79
80
|
/** primary cases that can block the register API from working at init time */
|
|
80
81
|
if (!agentRef.init.api.allow_registered_children) block(single(() => warn(55)));
|
|
81
82
|
if (!isValidMFETarget(target)) block(single(() => warn(48, target)));
|
|
83
|
+
if (!hasValidValue(target.id) || !hasValidValue(target.name)) {
|
|
84
|
+
block(single(() => warn(48, target)));
|
|
85
|
+
}
|
|
82
86
|
|
|
83
87
|
/** @type {RegisterAPI} */
|
|
84
88
|
const api = {
|
|
@@ -93,10 +97,22 @@ export function buildRegisterApi(agentRef, target) {
|
|
|
93
97
|
...(options.customAttributes || {})
|
|
94
98
|
}
|
|
95
99
|
}, agentRef], target),
|
|
100
|
+
measure: (name, options = {}) => report(measure, [name, {
|
|
101
|
+
...options,
|
|
102
|
+
customAttributes: {
|
|
103
|
+
...attrs,
|
|
104
|
+
...(options.customAttributes || {})
|
|
105
|
+
}
|
|
106
|
+
}, agentRef], target),
|
|
96
107
|
noticeError: (error, attributes = {}) => report(noticeError, [error, {
|
|
97
108
|
...attrs,
|
|
98
109
|
...attributes
|
|
99
110
|
}, agentRef], target),
|
|
111
|
+
register: (target = {}) => report(register, [agentRef, target], api.metadata.target),
|
|
112
|
+
recordCustomEvent: (eventType, attributes = {}) => report(recordCustomEvent, [eventType, {
|
|
113
|
+
...attrs,
|
|
114
|
+
...attributes
|
|
115
|
+
}, agentRef], target),
|
|
100
116
|
setApplicationVersion: value => setLocalValue('application.version', value),
|
|
101
117
|
setCustomAttribute: (key, value) => setLocalValue(key, value),
|
|
102
118
|
setUserId: value => setLocalValue('enduser.id', value),
|
|
@@ -144,8 +160,8 @@ export function buildRegisterApi(agentRef, target) {
|
|
|
144
160
|
const timestamp = now();
|
|
145
161
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ["API/register/".concat(methodToCall.name, "/called")], undefined, FEATURE_NAMES.metrics, agentRef.ee);
|
|
146
162
|
try {
|
|
147
|
-
const shouldDuplicate = agentRef.init.api.duplicate_registered_data;
|
|
148
|
-
if (shouldDuplicate
|
|
163
|
+
const shouldDuplicate = agentRef.init.api.duplicate_registered_data && methodToCall.name !== 'register';
|
|
164
|
+
if (shouldDuplicate) {
|
|
149
165
|
// also report to container by providing undefined target
|
|
150
166
|
methodToCall(...args, undefined, timestamp);
|
|
151
167
|
}
|