@newrelic/browser-agent 1.236.0 → 1.237.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.
Files changed (118) hide show
  1. package/dist/cjs/common/config/state/init.js +1 -0
  2. package/dist/cjs/common/config/state/runtime.js +2 -1
  3. package/dist/cjs/common/constants/env.cdn.js +1 -1
  4. package/dist/cjs/common/constants/env.npm.js +1 -1
  5. package/dist/cjs/common/deny-list/deny-list.js +14 -10
  6. package/dist/cjs/common/harvest/harvest.js +8 -22
  7. package/dist/cjs/common/util/submit-data.js +4 -36
  8. package/dist/cjs/common/wrap/wrap-jsonp.js +12 -6
  9. package/dist/cjs/features/ajax/aggregate/index.js +24 -27
  10. package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +1 -1
  11. package/dist/cjs/features/jserrors/constants.js +2 -4
  12. package/dist/cjs/features/jserrors/instrument/index.js +79 -88
  13. package/dist/cjs/features/jserrors/instrument/uncaught-error.js +22 -0
  14. package/dist/cjs/features/metrics/aggregate/index.js +8 -0
  15. package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +23 -19
  16. package/dist/cjs/features/session_replay/aggregate/index.js +65 -34
  17. package/dist/cjs/features/session_trace/aggregate/index.js +3 -4
  18. package/dist/cjs/features/spa/aggregate/index.js +1 -1
  19. package/dist/cjs/features/utils/instrument-base.js +6 -8
  20. package/dist/cjs/loaders/agent-base.js +87 -0
  21. package/dist/cjs/loaders/agent.js +41 -1
  22. package/dist/cjs/loaders/api/api.js +1 -1
  23. package/dist/cjs/loaders/api/interaction-types.js +87 -0
  24. package/dist/cjs/loaders/configure/configure.js +3 -1
  25. package/dist/cjs/loaders/micro-agent.js +3 -1
  26. package/dist/esm/common/config/state/init.js +1 -0
  27. package/dist/esm/common/config/state/runtime.js +2 -1
  28. package/dist/esm/common/constants/env.cdn.js +1 -1
  29. package/dist/esm/common/constants/env.npm.js +1 -1
  30. package/dist/esm/common/deny-list/deny-list.js +14 -10
  31. package/dist/esm/common/harvest/harvest.js +7 -22
  32. package/dist/esm/common/util/submit-data.js +4 -35
  33. package/dist/esm/common/wrap/wrap-jsonp.js +12 -6
  34. package/dist/esm/features/ajax/aggregate/index.js +25 -28
  35. package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +1 -1
  36. package/dist/esm/features/jserrors/constants.js +1 -2
  37. package/dist/esm/features/jserrors/instrument/index.js +78 -87
  38. package/dist/esm/features/jserrors/instrument/uncaught-error.js +15 -0
  39. package/dist/esm/features/metrics/aggregate/index.js +8 -0
  40. package/dist/esm/features/page_view_event/aggregate/initialized-features.js +23 -19
  41. package/dist/esm/features/session_replay/aggregate/index.js +65 -34
  42. package/dist/esm/features/session_trace/aggregate/index.js +3 -4
  43. package/dist/esm/features/spa/aggregate/index.js +1 -1
  44. package/dist/esm/features/utils/instrument-base.js +7 -9
  45. package/dist/esm/loaders/agent-base.js +80 -0
  46. package/dist/esm/loaders/agent.js +41 -1
  47. package/dist/esm/loaders/api/api.js +1 -1
  48. package/dist/esm/loaders/api/interaction-types.js +80 -0
  49. package/dist/esm/loaders/configure/configure.js +5 -3
  50. package/dist/esm/loaders/micro-agent.js +3 -1
  51. package/dist/types/common/config/state/runtime.d.ts.map +1 -1
  52. package/dist/types/common/event-emitter/register-handler.d.ts +1 -1
  53. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  54. package/dist/types/common/session/session-entity.d.ts +6 -6
  55. package/dist/types/common/util/submit-data.d.ts +2 -20
  56. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  57. package/dist/types/common/window/nreum.d.ts +2 -2
  58. package/dist/types/common/wrap/wrap-jsonp.d.ts.map +1 -1
  59. package/dist/types/features/ajax/aggregate/index.d.ts +5 -5
  60. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  61. package/dist/types/features/jserrors/constants.d.ts +0 -1
  62. package/dist/types/features/jserrors/constants.d.ts.map +1 -1
  63. package/dist/types/features/jserrors/instrument/index.d.ts +0 -13
  64. package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
  65. package/dist/types/features/jserrors/instrument/uncaught-error.d.ts +15 -0
  66. package/dist/types/features/jserrors/instrument/uncaught-error.d.ts.map +1 -0
  67. package/dist/types/features/metrics/aggregate/endpoint-map.d.ts +5 -5
  68. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  69. package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
  70. package/dist/types/features/session_replay/aggregate/index.d.ts +16 -30
  71. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  72. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  73. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  74. package/dist/types/loaders/agent-base.d.ts +59 -0
  75. package/dist/types/loaders/agent-base.d.ts.map +1 -0
  76. package/dist/types/loaders/agent.d.ts +35 -1
  77. package/dist/types/loaders/agent.d.ts.map +1 -1
  78. package/dist/types/loaders/api/interaction-types.d.ts +122 -0
  79. package/dist/types/loaders/api/interaction-types.d.ts.map +1 -0
  80. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  81. package/dist/types/loaders/features/features.d.ts +9 -9
  82. package/dist/types/loaders/micro-agent.d.ts +3 -2
  83. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  84. package/package.json +1 -1
  85. package/src/common/config/state/init.js +1 -1
  86. package/src/common/config/state/runtime.js +2 -1
  87. package/src/common/deny-list/deny-list.js +11 -11
  88. package/src/common/deny-list/deny-list.test.js +31 -0
  89. package/src/common/harvest/harvest.js +8 -18
  90. package/src/common/harvest/harvest.test.js +16 -36
  91. package/src/common/util/__mocks__/submit-data.js +0 -1
  92. package/src/common/util/submit-data.js +2 -24
  93. package/src/common/util/submit-data.test.js +0 -56
  94. package/src/common/wrap/wrap-jsonp.js +11 -6
  95. package/src/features/ajax/aggregate/index.js +25 -31
  96. package/src/features/jserrors/aggregate/compute-stack-trace.js +1 -1
  97. package/src/features/jserrors/constants.js +0 -1
  98. package/src/features/jserrors/instrument/index.js +91 -87
  99. package/src/features/jserrors/instrument/uncaught-error.js +15 -0
  100. package/src/features/metrics/aggregate/index.js +8 -0
  101. package/src/features/page_view_event/aggregate/initialized-features.js +18 -14
  102. package/src/features/session_replay/aggregate/index.component-test.js +17 -56
  103. package/src/features/session_replay/aggregate/index.js +47 -28
  104. package/src/features/session_trace/aggregate/index.js +3 -4
  105. package/src/features/spa/aggregate/index.js +1 -1
  106. package/src/features/utils/instrument-base.js +6 -9
  107. package/src/features/utils/instrument-base.test.js +7 -0
  108. package/src/loaders/agent-base.js +81 -0
  109. package/src/loaders/agent.js +42 -1
  110. package/src/loaders/api/api.js +1 -1
  111. package/src/loaders/api/interaction-types.js +80 -0
  112. package/src/loaders/configure/configure.js +14 -4
  113. package/src/loaders/micro-agent.js +4 -1
  114. package/dist/cjs/features/jserrors/instrument/debug.js +0 -40
  115. package/dist/esm/features/jserrors/instrument/debug.js +0 -38
  116. package/dist/types/features/jserrors/instrument/debug.d.ts +0 -2
  117. package/dist/types/features/jserrors/instrument/debug.d.ts.map +0 -1
  118. package/src/features/jserrors/instrument/debug.js +0 -36
@@ -19,6 +19,7 @@ import { getConfigurationValue, getInfo, getRuntime } from '../../../common/conf
19
19
  import { SESSION_EVENTS, MODE } from '../../../common/session/session-entity';
20
20
  import { AggregateBase } from '../../utils/aggregate-base';
21
21
  import { sharedChannel } from '../../../common/constants/shared-channel';
22
+ import { obj as encodeObj } from '../../../common/url/encode';
22
23
 
23
24
  // would be better to get this dynamically in some way
24
25
  export const RRWEB_VERSION = '2.0.0-alpha.8';
@@ -29,8 +30,12 @@ let recorder, gzipper, u8;
29
30
  export const MAX_PAYLOAD_SIZE = 1000000;
30
31
  /** Unloading caps around 64kb */
31
32
  export const IDEAL_PAYLOAD_SIZE = 64000;
32
- /** Interval between forcing new full snapshots in "error" mode */
33
- const CHECKOUT_MS = 30000;
33
+ /** Interval between forcing new full snapshots -- 30 seconds in error mode, 5 minutes in full mode */
34
+ const CHECKOUT_MS = {
35
+ [MODE.ERROR]: 30000,
36
+ [MODE.FULL]: 300000,
37
+ [MODE.OFF]: 0
38
+ };
34
39
  export class Aggregate extends AggregateBase {
35
40
  static featureName = FEATURE_NAME;
36
41
  constructor(agentIdentifier, aggregator) {
@@ -62,6 +67,12 @@ export class Aggregate extends AggregateBase {
62
67
  /** Payload metadata -- Should indicate that the payload being sent contains an error. Used for query/filter purposes in UI */
63
68
  this.hasError = false;
64
69
 
70
+ /** Payload metadata -- Should indicate when a replay blob started recording. Resets each time a harvest occurs. */
71
+ this.timestamp = {
72
+ first: undefined,
73
+ last: undefined
74
+ };
75
+
65
76
  /** A value which increments with every new mutation node reported. Resets after a harvest is sent */
66
77
  this.payloadBytesEstimation = 0;
67
78
  const shouldSetup = getConfigurationValue(agentIdentifier, 'privacy.cookies_enabled') === true && getConfigurationValue(agentIdentifier, 'session_trace.enabled') === true;
@@ -85,7 +96,7 @@ export class Aggregate extends AggregateBase {
85
96
  });
86
97
 
87
98
  // Bespoke logic for new endpoint. This will change as downstream dependencies become solidified.
88
- this.scheduler = new HarvestScheduler('blob', {
99
+ this.scheduler = new HarvestScheduler('browser/blobs', {
89
100
  onFinished: this.onHarvestFinished.bind(this),
90
101
  retryDelay: this.harvestTimeSeconds,
91
102
  getPayload: this.prepareHarvest.bind(this),
@@ -105,10 +116,9 @@ export class Aggregate extends AggregateBase {
105
116
  this.stopRecording();
106
117
  this.startRecording();
107
118
  this.scheduler.startTimer(this.harvestTimeSeconds);
108
- const {
109
- session
110
- } = getRuntime(this.agentIdentifier);
111
- session.state.sessionReplay = this.mode;
119
+ this.syncWithSessionManager({
120
+ sessionReplay: this.mode
121
+ });
112
122
  }
113
123
  }
114
124
  }, this.featureName, this.ee);
@@ -181,17 +191,18 @@ export class Aggregate extends AggregateBase {
181
191
  }
182
192
  this.startRecording();
183
193
  this.isFirstChunk = !!session.isNew;
184
- session.state.sessionReplay = this.mode;
194
+ this.syncWithSessionManager({
195
+ sessionReplay: this.mode
196
+ });
185
197
  }
186
198
  prepareHarvest() {
187
- if (this.events.length === 0) return;
199
+ if (this.events.length === 0 || this.mode !== MODE.FULL && !this.blocked) return;
188
200
  const payload = this.getHarvestContents();
189
201
  if (this.shouldCompress) {
190
202
  payload.body = gzipper(u8(stringify(payload.body)));
191
203
  this.scheduler.opts.gzip = true;
192
204
  } else {
193
205
  this.scheduler.opts.gzip = false;
194
- delete payload.qs.content_encoding;
195
206
  }
196
207
  // TODO -- Gracefully handle the buffer for retries.
197
208
  this.clearBuffer();
@@ -202,25 +213,28 @@ export class Aggregate extends AggregateBase {
202
213
  const info = getInfo(this.agentIdentifier);
203
214
  return {
204
215
  qs: {
205
- protocol_version: '0',
206
- content_encoding: 'gzip',
207
- browser_monitoring_key: info.licenseKey
208
- },
209
- body: {
216
+ browser_monitoring_key: info.licenseKey,
210
217
  type: 'SessionReplay',
211
- appId: Number(info.applicationID),
212
- timestamp: Date.now(),
213
- blob: JSON.stringify(this.events),
214
- // this needs to be a stringified JSON array of rrweb nodes
215
- attributes: {
218
+ app_id: info.applicationID,
219
+ protocol_version: '0',
220
+ attributes: encodeObj({
221
+ ...(this.shouldCompress && {
222
+ content_encoding: 'gzip'
223
+ }),
224
+ 'replay.firstTimestamp': this.timestamp.first,
225
+ 'replay.lastTimestamp': this.timestamp.last,
226
+ 'replay.durationMs': this.timestamp.last - this.timestamp.first,
227
+ agentVersion: agentRuntime.version,
216
228
  session: agentRuntime.session.state.value,
217
229
  hasSnapshot: this.hasSnapshot,
218
230
  hasError: this.hasError,
219
- agentVersion: agentRuntime.version,
220
231
  isFirstChunk: this.isFirstChunk,
232
+ decompressedBytes: this.payloadBytesEstimation,
221
233
  'nr.rrweb.version': RRWEB_VERSION
222
- }
223
- }
234
+ }, MAX_PAYLOAD_SIZE - this.payloadBytesEstimation).substring(1) // remove the leading '&'
235
+ },
236
+
237
+ body: this.events
224
238
  };
225
239
  }
226
240
  onHarvestFinished(result) {
@@ -238,6 +252,7 @@ export class Aggregate extends AggregateBase {
238
252
  this.hasSnapshot = false;
239
253
  this.hasError = false;
240
254
  this.payloadBytesEstimation = 0;
255
+ this.clearTimestamps();
241
256
  }
242
257
 
243
258
  /** Begin recording using configured recording lib */
@@ -246,6 +261,7 @@ export class Aggregate extends AggregateBase {
246
261
  warn('Recording library was never imported');
247
262
  return this.abort();
248
263
  }
264
+ this.recording = true;
249
265
  const {
250
266
  blockClass,
251
267
  ignoreClass,
@@ -255,7 +271,6 @@ export class Aggregate extends AggregateBase {
255
271
  maskTextSelector,
256
272
  maskAllInputs
257
273
  } = getConfigurationValue(this.agentIdentifier, 'session_replay');
258
- this.hasSnapshot = true;
259
274
  // set up rrweb configurations for maximum privacy --
260
275
  // https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
261
276
  const stop = recorder({
@@ -267,11 +282,8 @@ export class Aggregate extends AggregateBase {
267
282
  maskInputOptions,
268
283
  maskTextSelector,
269
284
  maskAllInputs,
270
- ...(this.mode === MODE.ERROR && {
271
- checkoutEveryNms: CHECKOUT_MS
272
- })
285
+ checkoutEveryNms: CHECKOUT_MS[this.mode]
273
286
  });
274
- this.recording = true;
275
287
  this.stopRecording = () => {
276
288
  this.recording = false;
277
289
  stop();
@@ -296,12 +308,14 @@ export class Aggregate extends AggregateBase {
296
308
  // we are still waiting for an error to throw, so keep wiping the buffer over time
297
309
  this.clearBuffer();
298
310
  }
311
+ this.setTimestamps(event);
312
+ if (event.type === 2) this.hasSnapshot = true;
299
313
  this.events.push(event);
300
314
  this.payloadBytesEstimation += eventBytes;
301
315
 
302
316
  // We are making an effort to try to keep payloads manageable for unloading. If they reach the unload limit before their interval,
303
317
  // it will send immediately. This often happens on the first snapshot, which can be significantly larger than the other payloads.
304
- if (payloadSize > IDEAL_PAYLOAD_SIZE) {
318
+ if (payloadSize > IDEAL_PAYLOAD_SIZE && this.mode !== MODE.ERROR) {
305
319
  // if we've made it to the ideal size of ~64kb before the interval timer, we should send early.
306
320
  this.scheduler.runHarvest();
307
321
  }
@@ -311,7 +325,17 @@ export class Aggregate extends AggregateBase {
311
325
  takeFullSnapshot() {
312
326
  if (!recorder) return;
313
327
  recorder.takeFullSnapshot();
314
- this.hasSnapshot = true;
328
+ }
329
+ setTimestamps(rrwebEvent) {
330
+ if (!rrwebEvent) return;
331
+ if (!this.timestamp.first) this.timestamp.first = rrwebEvent.timestamp;
332
+ this.timestamp.last = rrwebEvent.timestamp;
333
+ }
334
+ clearTimestamps() {
335
+ this.timestamp = {
336
+ first: undefined,
337
+ last: undefined
338
+ };
315
339
  }
316
340
 
317
341
  /** Estimate the payload size */
@@ -326,11 +350,11 @@ export class Aggregate extends AggregateBase {
326
350
  this.blocked = true;
327
351
  this.mode = MODE.OFF;
328
352
  this.stopRecording();
353
+ this.syncWithSessionManager({
354
+ sessionReplay: this.mode
355
+ });
356
+ this.clearTimestamps();
329
357
  this.ee.emit('REPLAY_ABORTED');
330
- const {
331
- session
332
- } = getRuntime(this.agentIdentifier);
333
- session.state.sessionReplay = this.mode;
334
358
  }
335
359
 
336
360
  /** Extensive research has yielded about an 88% compression factor on these payloads.
@@ -341,4 +365,11 @@ export class Aggregate extends AggregateBase {
341
365
  if (this.shouldCompress) return data * AVG_COMPRESSION;
342
366
  return data;
343
367
  }
368
+ syncWithSessionManager() {
369
+ let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
370
+ const {
371
+ session
372
+ } = getRuntime(this.agentIdentifier);
373
+ session.write(state);
374
+ }
344
375
  }
@@ -109,8 +109,8 @@ export class Aggregate extends AggregateBase {
109
109
  sessionTraceMode: MODE.OFF
110
110
  });
111
111
  operationalGate.permanentlyDecide(false);
112
- this.#scheduler?.stopTimer(true);
113
112
  if (mostRecentModeKnown === MODE.FULL) this.#scheduler?.runHarvest(); // allow queued nodes (past opGate) to final harvest, unless they were buffered in other modes
113
+ this.#scheduler?.stopTimer(true); // the 'true' arg here will forcibly block any future call to runHarvest, so the last runHarvest above must be prior
114
114
  this.#scheduler = null;
115
115
  };
116
116
 
@@ -136,9 +136,8 @@ export class Aggregate extends AggregateBase {
136
136
  this.ee.on(SESSION_EVENTS.PAUSE, () => mostRecentModeKnown = sessionEntity.state.sessionTraceMode);
137
137
  if (!sessionEntity.isNew) {
138
138
  // inherit the same mode as existing session's Trace
139
- const existingTraceMode = mostRecentModeKnown = sessionEntity.state.sessionTraceMode;
140
- if (existingTraceMode === MODE.OFF) this.isStandalone = true;
141
- controlTraceOp(existingTraceMode);
139
+ if (sessionEntity.state.sessionReplay === MODE.OFF) this.isStandalone = true;
140
+ controlTraceOp(mostRecentModeKnown = sessionEntity.state.sessionTraceMode);
142
141
  } else {
143
142
  // for new sessions, see the truth table associated with NEWRELIC-8662 wrt the new Trace behavior under session management
144
143
  const replayMode = await getSessionReplayMode(agentIdentifier);
@@ -398,7 +398,7 @@ export class Aggregate extends AggregateBase {
398
398
  register(FETCH_DONE, function (err, res) {
399
399
  var node = this[SPA_NODE];
400
400
  if (node) {
401
- if (err) {
401
+ if (err || !shouldCollectEvent(this.params)) {
402
402
  node.cancel();
403
403
  return;
404
404
  }
@@ -10,7 +10,7 @@ import { onWindowLoad } from '../../common/window/load';
10
10
  import { isBrowserScope } from '../../common/constants/runtime';
11
11
  import { warn } from '../../common/util/console';
12
12
  import { FEATURE_NAMES } from '../../loaders/features/features';
13
- import { getConfigurationValue } from '../../common/config/config';
13
+ import { getConfigurationValue, originals } from '../../common/config/config';
14
14
 
15
15
  /**
16
16
  * Base class for instrumenting a feature.
@@ -56,7 +56,7 @@ export class InstrumentBase extends FeatureBase {
56
56
  let argsObjFromInstrument = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
57
57
  if (this.featAggregate || !this.auto) return;
58
58
  const enableSessionTracking = isBrowserScope && getConfigurationValue(this.agentIdentifier, 'privacy.cookies_enabled') === true;
59
- let loadedSuccessfully, loadFailed;
59
+ let loadedSuccessfully;
60
60
  this.onAggregateImported = new Promise(resolve => {
61
61
  loadedSuccessfully = resolve;
62
62
  });
@@ -81,6 +81,7 @@ export class InstrumentBase extends FeatureBase {
81
81
  try {
82
82
  if (!this.shouldImportAgg(this.featureName, session)) {
83
83
  drain(this.agentIdentifier, this.featureName);
84
+ loadedSuccessfully(false); // aggregate module isn't loaded at all
84
85
  return;
85
86
  }
86
87
  const {
@@ -111,15 +112,12 @@ export class InstrumentBase extends FeatureBase {
111
112
  * @returns
112
113
  */
113
114
  shouldImportAgg(featureName, session) {
114
- // if this isnt the FIRST load of a session AND
115
- // we are not actively recording SR... DO NOT run the aggregator
116
- // session replay samples can only be decided on the first load of a session
117
- // session replays can continue if in progress
118
115
  if (featureName === FEATURE_NAMES.sessionReplay) {
119
- if (getConfigurationValue(this.agentIdentifier, 'session_trace.enabled') === false) return false;
120
- return !!session?.isNew || !!session?.state.sessionReplay;
116
+ if (!originals.MO) return false; // Session Replay cannot work without Mutation Observer
117
+ if (getConfigurationValue(this.agentIdentifier, 'session_trace.enabled') === false) return false; // Session Replay as of now is tightly coupled with Session Trace in the UI
118
+ return !!session?.isNew || !!session?.state.sessionReplay; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
121
119
  }
122
- // todo -- add case like above for session trace
120
+
123
121
  return true;
124
122
  }
125
123
  }
@@ -0,0 +1,80 @@
1
+ import { warn } from '../common/util/console';
2
+ export class AgentBase {
3
+ /**
4
+ * Reports a browser PageAction event along with a name and optional attributes.
5
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/addpageaction/}
6
+ * @param {string} name Name or category of the action. Reported as the actionName attribute.
7
+ * @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}. The key is reported as its own PageAction attribute with the specified values.
8
+ */
9
+ addPageAction(name, attributes) {
10
+ warn('Call to agent api addPageAction failed. The session trace feature is not currently initialized.');
11
+ }
12
+
13
+ /**
14
+ * Groups page views to help URL structure or to capture the URL's routing information.
15
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setpageviewname/}
16
+ * @param {string} name The page name you want to use. Use alphanumeric characters.
17
+ * @param {string} [host] Default is http://custom.transaction. Typically set host to your site's domain URI.
18
+ */
19
+ setPageViewName(name, host) {
20
+ warn('Call to agent api setPageViewName failed. The page view feature is not currently initialized.');
21
+ }
22
+
23
+ /**
24
+ * Adds a user-defined attribute name and value to subsequent events on the page.
25
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcustomattribute/}
26
+ * @param {string} name Name of the attribute. Appears as column in the PageView event. It will also appear as a column in the PageAction event if you are using it.
27
+ * @param {string|number|null} value Value of the attribute. Appears as the value in the named attribute column in the PageView event. It will appear as a column in the PageAction event if you are using it. Custom attribute values cannot be complex objects, only simple types such as Strings and Integers.
28
+ * @param {boolean} [persist] Default false. f set to true, the name-value pair will also be set into the browser's storage API. Then on the following instrumented pages that load within the same session, the pair will be re-applied as a custom attribute.
29
+ */
30
+ setCustomAttribute(name, value, persist) {
31
+ warn('Call to agent api setCustomAttribute failed. The js errors feature is not currently initialized.');
32
+ }
33
+
34
+ /**
35
+ * Identifies a browser error without disrupting your app's operations.
36
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/noticeerror/}
37
+ * @param {Error|string} error Provide a meaningful error message that you can use when analyzing data on browser's JavaScript errors page.
38
+ * @param {object} [customAttributes] An object containing name/value pairs representing custom attributes.
39
+ */
40
+ noticeError(error, customAttributes) {
41
+ warn('Call to agent api noticeError failed. The js errors feature is not currently initialized.');
42
+ }
43
+
44
+ /**
45
+ * Adds a user-defined identifier string to subsequent events on the page.
46
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setuserid/}
47
+ * @param {string|null} value A string identifier for the end-user, useful for tying all browser events to specific users. The value parameter does not have to be unique. If IDs should be unique, the caller is responsible for that validation. Passing a null value unsets any existing user ID.
48
+ */
49
+ setUserId(value) {
50
+ warn('Call to agent api setUserId failed. The js errors feature is not currently initialized.');
51
+ }
52
+
53
+ /**
54
+ * Allows selective ignoring and grouping of known errors that the browser agent captures.
55
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/seterrorhandler/}
56
+ * @param {(error: Error|string) => boolean | { group: string }} callback When an error occurs, the callback is called with the error object as a parameter. The callback will be called with each error, so it is not specific to one error.
57
+ */
58
+ setErrorHandler(callback) {
59
+ warn('Call to agent api setErrorHandler failed. The js errors feature is not currently initialized.');
60
+ }
61
+
62
+ /**
63
+ * Records an additional time point as "finished" in a session trace, and sends the event to New Relic.
64
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/finished/}
65
+ * @param {number} [timeStamp] Defaults to the current time of the call. If used, this marks the time that the page is "finished" according to your own criteria.
66
+ */
67
+ finished(timeStamp) {
68
+ warn('Call to agent api finished failed. The page action feature is not currently initialized.');
69
+ }
70
+
71
+ /**
72
+ * Adds a unique name and ID to identify releases with multiple JavaScript bundles on the same page.
73
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/addrelease/}
74
+ * @param {string} name A short description of the component; for example, the name of a project, application, file, or library.
75
+ * @param {string} id The ID or version of this release; for example, a version number, build number from your CI environment, GitHub SHA, GUID, or a hash of the contents.
76
+ */
77
+ addRelease(name, id) {
78
+ warn('Call to agent api addRelease failed. The agent is not currently initialized.');
79
+ }
80
+ }
@@ -1,4 +1,5 @@
1
1
  // loader files
2
+ import { AgentBase } from './agent-base';
2
3
  import { getEnabledFeatures } from './features/enabled-features';
3
4
  import { configure } from './configure/configure';
4
5
  import { getFeatureDependencyNames } from './features/featureDependencies';
@@ -14,13 +15,18 @@ import { warn } from '../common/util/console';
14
15
  import { stringify } from '../common/util/stringify';
15
16
  import { globalScope } from '../common/constants/runtime';
16
17
 
18
+ /**
19
+ * @typedef {import('./api/interaction-types').InteractionInstance} InteractionInstance
20
+ */
21
+
17
22
  /**
18
23
  * A flexible class that may be used to compose an agent from a select subset of feature modules. In applications
19
24
  * sensitive to network load, this may result in smaller builds with slightly lower performance impact.
20
25
  */
21
- export class Agent {
26
+ export class Agent extends AgentBase {
22
27
  constructor(options) {
23
28
  let agentIdentifier = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : generateRandomHexString(16);
29
+ super();
24
30
  if (!globalScope) {
25
31
  // We could not determine the runtime environment. Short-circuite the agent here
26
32
  // to avoid possible exceptions later that may cause issues with customer's application.
@@ -81,4 +87,38 @@ export class Agent {
81
87
  return false;
82
88
  }
83
89
  }
90
+
91
+ /* Below API methods are only available on a standard agent and not the micro agent */
92
+
93
+ /**
94
+ * Adds a JavaScript object with a custom name, start time, etc. to an in-progress session trace.
95
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/addtotrace/}
96
+ * @param {{name: string, start: number, end?: number, origin?: string, type?: string}} customAttributes Supply a JavaScript object with these required and optional name/value pairs:
97
+ *
98
+ * - Required name/value pairs: name, start
99
+ * - Optional name/value pairs: end, origin, type
100
+ *
101
+ * If you are sending the same event object to New Relic as a PageAction, omit the TYPE attribute. (type is a string to describe what type of event you are marking inside of a session trace.) If included, it will override the event type and cause the PageAction event to be sent incorrectly. Instead, use the name attribute for event information.
102
+ */
103
+ addToTrace(customAttributes) {
104
+ warn('Call to agent api addToTrace failed. The page action feature is not currently initialized.');
105
+ }
106
+
107
+ /**
108
+ * Gives SPA routes more accurate names than default names. Monitors specific routes rather than by default grouping.
109
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcurrentroutename/}
110
+ * @param {string} name Current route name for the page.
111
+ */
112
+ setCurrentRouteName(name) {
113
+ warn('Call to agent api setCurrentRouteName failed. The spa feature is not currently initialized.');
114
+ }
115
+
116
+ /**
117
+ * Returns a new API object that is bound to the current SPA interaction.
118
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/interaction/}
119
+ * @returns {InteractionInstance} An API object that is bound to a specific BrowserInteraction event. Each time this method is called for the same BrowserInteraction, a new object is created, but it still references the same interaction.
120
+ */
121
+ interaction() {
122
+ warn('Call to agent api interaction failed. The spa feature is not currently initialized.');
123
+ }
84
124
  }
@@ -122,7 +122,7 @@ export function setAPI(agentIdentifier, forceDrain) {
122
122
  try {
123
123
  return cb.apply(this, arguments);
124
124
  } catch (err) {
125
- tracerEE.emit('fn-err', [arguments, this, typeof err == 'string' ? new Error(err) : err], contextStore);
125
+ tracerEE.emit('fn-err', [arguments, this, err], contextStore);
126
126
  // the error came from outside the agent, so don't swallow
127
127
  throw err;
128
128
  } finally {
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @typedef InteractionInstance
3
+ * @property {actionText} actionText
4
+ * @property {createTracer} createTracer
5
+ * @property {end} end
6
+ * @property {getContext} getContext
7
+ * @property {ignore} ignore
8
+ * @property {onEnd} onEnd
9
+ * @property {onEnd} save
10
+ * @property {setAttribute} setAttribute
11
+ * @property {setName} setName
12
+ */
13
+
14
+ /**
15
+ * Sets the text value of the HTML element that was clicked to start a browser interaction.
16
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/actiontext/}
17
+ * @callback actionText
18
+ * @param {string} value The text value of the HTML element that represents the action that started the interaction.
19
+ * @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
20
+ */
21
+
22
+ /**
23
+ * Times sub-components of a SPA interaction separately, including wait time and JS execution time.
24
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/createtracer/}
25
+ * @callback createTracer
26
+ * @param {string} name This will be used as the name of the tracer.
27
+ * @param {string} [callback] A callback that contains the synchronous work to run at the end of the async work. To execute this callback, call the wrapper function returned using createTracer().
28
+ * @returns {Function} Returns a method that wraps the original callback. When this method is invoked, it calls the original callback and ends the async timing.
29
+ */
30
+
31
+ /**
32
+ * Ends the SPA interaction at the current time.
33
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/end/}
34
+ * @callback end
35
+ * @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
36
+ */
37
+
38
+ /**
39
+ * Stores values for the current SPA interaction asynchronously in browser.
40
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/getcontext/}
41
+ * @callback getContext
42
+ * @param {(ctx: object) => void} callback This function is called when the interaction ends. It is called with one parameter, which is the interaction context.
43
+ * @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
44
+ */
45
+
46
+ /**
47
+ * Change the values associated with a SPA interaction before the interaction is saved.
48
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/onend/}
49
+ * @callback onEnd
50
+ * @param {(ctx: object) => void} callback This function is called when the interaction ends. It is called with one parameter, which is the interaction context.
51
+ * @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
52
+ */
53
+
54
+ /**
55
+ * Ensures a SPA browser interaction will be saved when it ends.
56
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/save/}
57
+ * @callback save
58
+ * @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
59
+ */
60
+
61
+ /**
62
+ * Adds a custom SPA attribute only to the current interaction in browser.
63
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setattribute/}
64
+ * @callback setAttribute
65
+ * @param {string} key Used as the attribute name on the BrowserInteraction event.
66
+ * @param {any} key Used as the attribute value on the BrowserInteraction event. This can be a string, number, boolean, or object. If it is an object, New Relic serializes it to a JSON string.
67
+ * @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
68
+ */
69
+
70
+ /**
71
+ * Sets the name and trigger of a SPA's browser interaction that is not a route change or URL change.
72
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setname/}
73
+ * @callback setName
74
+ * @param {string} name If null, the name will be set using the targetGroupedUrl attribute. If not null, this will set the browserInteractionName attribute in the BrowserInteraction event.
75
+ * @param {string} [trigger] If not null, this will set the TRIGGER attribute on the BrowserInteraction event.
76
+ * @returns {InteractionInstance} Returns the same interaction object allowing method chaining.
77
+ */
78
+
79
+ /* istanbul ignore next */
80
+ export const unused = {};
@@ -1,7 +1,7 @@
1
1
  import { setAPI, setTopLevelCallers } from '../api/api';
2
2
  import { addToNREUM, gosCDN, gosNREUMInitializedAgents } from '../../common/window/nreum';
3
- import { setConfiguration, setInfo, setLoaderConfig, setRuntime } from '../../common/config/config';
4
- import { activateFeatures, activatedFeatures } from '../../common/util/feature-flags';
3
+ import { getConfiguration, setConfiguration, setInfo, setLoaderConfig, setRuntime } from '../../common/config/config';
4
+ import { activatedFeatures } from '../../common/util/feature-flags';
5
5
  import { isWorkerScope } from '../../common/constants/runtime';
6
6
  export function configure(agentIdentifier) {
7
7
  let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
@@ -24,13 +24,15 @@ export function configure(agentIdentifier) {
24
24
  }
25
25
  setConfiguration(agentIdentifier, init || {});
26
26
  setLoaderConfig(agentIdentifier, loader_config || {});
27
- setRuntime(agentIdentifier, runtime);
28
27
  info.jsAttributes ??= {};
29
28
  if (isWorkerScope) {
30
29
  // add a default attr to all worker payloads
31
30
  info.jsAttributes.isWorker = true;
32
31
  }
33
32
  setInfo(agentIdentifier, info);
33
+ const updatedInit = getConfiguration(agentIdentifier);
34
+ runtime.denyList = [...(updatedInit.ajax?.deny_list || []), ...(updatedInit.ajax?.block_internal ? [info.beacon, info.errorBeacon] : [])];
35
+ setRuntime(agentIdentifier, runtime);
34
36
  setTopLevelCallers();
35
37
  const api = setAPI(agentIdentifier, forceDrain);
36
38
  gosNREUMInitializedAgents(agentIdentifier, api, 'api');
@@ -10,6 +10,7 @@ import { getConfiguration, getInfo, getLoaderConfig, getRuntime } from '../commo
10
10
  import { FEATURE_NAMES } from './features/features';
11
11
  import { warn } from '../common/util/console';
12
12
  import { onWindowLoad } from '../common/window/load';
13
+ import { AgentBase } from './agent-base';
13
14
  const nonAutoFeatures = [FEATURE_NAMES.jserrors, FEATURE_NAMES.pageAction, FEATURE_NAMES.metrics];
14
15
 
15
16
  /**
@@ -17,13 +18,14 @@ const nonAutoFeatures = [FEATURE_NAMES.jserrors, FEATURE_NAMES.pageAction, FEATU
17
18
  * automatically instrument. Instead, each MicroAgent instance will lazy load the required features and can support loading multiple instances on one page.
18
19
  * Out of the box, it can manually handle and report Page View, Page Action, and Error events.
19
20
  */
20
- export class MicroAgent {
21
+ export class MicroAgent extends AgentBase {
21
22
  /**
22
23
  * @param {Object} options - Specifies features and runtime configuration,
23
24
  * @param {string=} agentIdentifier - The optional unique ID of the agent.
24
25
  */
25
26
  constructor(options) {
26
27
  let agentIdentifier = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : generateRandomHexString(16);
28
+ super();
27
29
  this.agentIdentifier = agentIdentifier;
28
30
  this.sharedAggregator = new Aggregator({
29
31
  agentIdentifier: this.agentIdentifier
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/runtime.js"],"names":[],"mappings":"AA6BA,yCAIC;AAED,oDAIC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/runtime.js"],"names":[],"mappings":"AA8BA,yCAIC;AAED,oDAIC"}
@@ -1,7 +1,7 @@
1
1
  export function defaultRegister(type: any, handler: any, group: any, ee: any): void;
2
2
  export namespace defaultRegister {
3
3
  export { registerWithSpecificEmitter as on };
4
- export const handlers: {};
4
+ export let handlers: {};
5
5
  }
6
6
  export { defaultRegister as registerHandler };
7
7
  declare function registerWithSpecificEmitter(ee: any, handlers: any, type: any, handler: any, group: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAmBA;;;;;;GAMG;AAEH;IAII,0BAA2H;IAC3H,uBAAoD;IACpD,kCAAsH;IAEtH,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAUzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;OAGG;IACH,wBAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CA8FnB;IAGD,0BAmBC;IAED;;;;;;;OAOG;IACH,wBALW,yBAAyB,WACzB,6BAA6B,GAE3B,cAAc,CA2B1B;IAED;;;;;;;OAOG;IACH,uBAHW,cAAc,GACZ,cAAc,CAwB1B;IAED;;;;;OAKG;IACH,aAHW,yBAAyB,YACzB,sBAAsB,QAQhC;CACF;8BAhQY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BATjC,2BAA2B;2BAF9B,mBAAmB"}
1
+ {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,uBAAoD;IACpD,kCAAsH;IAEtH,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAUzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;OAGG;IACH,wBAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CAmFnB;IAGD,0BAmBC;IAED;;;;;;;OAOG;IACH,wBALW,yBAAyB,WACzB,6BAA6B,GAE3B,cAAc,CA2B1B;IAED;;;;;;;OAOG;IACH,uBAHW,cAAc,GACZ,cAAc,CAuB1B;IAED;;;;;OAKG;IACH,aAHW,yBAAyB,YACzB,sBAAsB,QAQhC;CACF;8BAnPY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAZjC,2BAA2B;2BAF9B,mBAAmB"}
@@ -1,12 +1,12 @@
1
1
  export namespace MODE {
2
- const OFF: number;
3
- const FULL: number;
4
- const ERROR: number;
2
+ let OFF: number;
3
+ let FULL: number;
4
+ let ERROR: number;
5
5
  }
6
6
  export namespace SESSION_EVENTS {
7
- const PAUSE: string;
8
- const RESET: string;
9
- const RESUME: string;
7
+ let PAUSE: string;
8
+ let RESET: string;
9
+ let RESUME: string;
10
10
  }
11
11
  export class SessionEntity {
12
12
  /**