@newrelic/browser-agent 1.274.0 → 1.276.0

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 (64) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/cjs/common/config/init.js +44 -8
  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/harvest/harvest.js +21 -0
  6. package/dist/cjs/common/util/submit-data.js +42 -5
  7. package/dist/cjs/common/wrap/wrap-events.js +1 -1
  8. package/dist/cjs/common/wrap/wrap-logger.js +5 -1
  9. package/dist/cjs/common/wrap/wrap-xhr.js +1 -0
  10. package/dist/cjs/features/ajax/instrument/index.js +1 -1
  11. package/dist/cjs/features/generic_events/aggregate/index.js +77 -14
  12. package/dist/cjs/features/generic_events/constants.js +7 -2
  13. package/dist/cjs/features/generic_events/instrument/index.js +20 -8
  14. package/dist/cjs/features/jserrors/aggregate/index.js +3 -2
  15. package/dist/cjs/features/jserrors/aggregate/internal-errors.js +2 -2
  16. package/dist/cjs/features/jserrors/instrument/index.js +2 -2
  17. package/dist/cjs/features/metrics/aggregate/index.js +1 -35
  18. package/dist/esm/common/config/init.js +39 -3
  19. package/dist/esm/common/constants/env.cdn.js +1 -1
  20. package/dist/esm/common/constants/env.npm.js +1 -1
  21. package/dist/esm/common/harvest/harvest.js +21 -0
  22. package/dist/esm/common/util/submit-data.js +41 -5
  23. package/dist/esm/common/wrap/wrap-events.js +1 -1
  24. package/dist/esm/common/wrap/wrap-logger.js +5 -2
  25. package/dist/esm/common/wrap/wrap-xhr.js +1 -0
  26. package/dist/esm/features/ajax/instrument/index.js +1 -1
  27. package/dist/esm/features/generic_events/aggregate/index.js +78 -15
  28. package/dist/esm/features/generic_events/constants.js +6 -1
  29. package/dist/esm/features/generic_events/instrument/index.js +21 -9
  30. package/dist/esm/features/jserrors/aggregate/index.js +3 -2
  31. package/dist/esm/features/jserrors/aggregate/internal-errors.js +2 -2
  32. package/dist/esm/features/jserrors/instrument/index.js +2 -2
  33. package/dist/esm/features/metrics/aggregate/index.js +1 -35
  34. package/dist/types/common/config/init.d.ts.map +1 -1
  35. package/dist/types/common/harvest/harvest.d.ts.map +1 -1
  36. package/dist/types/common/util/submit-data.d.ts +18 -1
  37. package/dist/types/common/util/submit-data.d.ts.map +1 -1
  38. package/dist/types/common/wrap/wrap-logger.d.ts.map +1 -1
  39. package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
  40. package/dist/types/features/generic_events/aggregate/index.d.ts +1 -1
  41. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  42. package/dist/types/features/generic_events/constants.d.ts +5 -0
  43. package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
  44. package/dist/types/features/jserrors/aggregate/index.d.ts +2 -1
  45. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  46. package/dist/types/features/jserrors/aggregate/internal-errors.d.ts +1 -1
  47. package/dist/types/features/jserrors/aggregate/internal-errors.d.ts.map +1 -1
  48. package/dist/types/features/metrics/aggregate/index.d.ts +0 -1
  49. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  50. package/package.json +13 -1
  51. package/src/common/config/init.js +21 -3
  52. package/src/common/harvest/harvest.js +19 -0
  53. package/src/common/util/submit-data.js +37 -4
  54. package/src/common/wrap/wrap-events.js +1 -1
  55. package/src/common/wrap/wrap-logger.js +7 -2
  56. package/src/common/wrap/wrap-xhr.js +2 -0
  57. package/src/features/ajax/instrument/index.js +1 -1
  58. package/src/features/generic_events/aggregate/index.js +79 -16
  59. package/src/features/generic_events/constants.js +6 -0
  60. package/src/features/generic_events/instrument/index.js +21 -10
  61. package/src/features/jserrors/aggregate/index.js +3 -2
  62. package/src/features/jserrors/aggregate/internal-errors.js +2 -2
  63. package/src/features/jserrors/instrument/index.js +2 -2
  64. package/src/features/metrics/aggregate/index.js +1 -33
@@ -1,3 +1,4 @@
1
+ import { FEATURE_FLAGS } from '../../features/generic_events/constants';
1
2
  import { LOG_LEVELS } from '../../features/logging/constants';
2
3
  import { isValidSelector } from '../dom/query-selector';
3
4
  import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS } from '../session/constants';
@@ -7,6 +8,12 @@ import { getModeledObject } from './configurable';
7
8
  const nrMask = '[data-nr-mask]';
8
9
  const model = () => {
9
10
  const hiddenState = {
11
+ feature_flags: [],
12
+ experimental: {
13
+ marks: false,
14
+ measures: false,
15
+ resources: false
16
+ },
10
17
  mask_selector: '*',
11
18
  block_selector: '[data-nr-block]',
12
19
  mask_input_options: {
@@ -44,7 +51,12 @@ const model = () => {
44
51
  cors_use_tracecontext_headers: undefined,
45
52
  allowed_origins: undefined
46
53
  },
47
- feature_flags: [],
54
+ get feature_flags() {
55
+ return hiddenState.feature_flags;
56
+ },
57
+ set feature_flags(val) {
58
+ hiddenState.feature_flags = val;
59
+ },
48
60
  generic_events: {
49
61
  enabled: true,
50
62
  harvestTimeSeconds: 30,
@@ -82,8 +94,32 @@ const model = () => {
82
94
  autoStart: true
83
95
  },
84
96
  performance: {
85
- capture_marks: false,
86
- capture_measures: false // false by default through experimental phase, but flipped to true once GA'd
97
+ get capture_marks() {
98
+ return hiddenState.feature_flags.includes(FEATURE_FLAGS.MARKS) || hiddenState.experimental.marks;
99
+ },
100
+ set capture_marks(val) {
101
+ hiddenState.experimental.marks = val;
102
+ },
103
+ get capture_measures() {
104
+ return hiddenState.feature_flags.includes(FEATURE_FLAGS.MEASURES) || hiddenState.experimental.measures;
105
+ },
106
+ set capture_measures(val) {
107
+ hiddenState.experimental.measures = val;
108
+ },
109
+ resources: {
110
+ // whether to run this subfeature or not in the generic_events feature. false by default through experimental phase, but flipped to true once GA'd
111
+ get enabled() {
112
+ return hiddenState.feature_flags.includes(FEATURE_FLAGS.RESOURCES) || hiddenState.experimental.resources;
113
+ },
114
+ set enabled(val) {
115
+ hiddenState.experimental.resources = val;
116
+ },
117
+ asset_types: [],
118
+ // MDN types to collect, empty array will collect all types
119
+ first_party_domains: [],
120
+ // when included, will decorate the resource as first party if matching
121
+ ignore_newrelic: true // ignore capturing internal agent scripts and harvest calls
122
+ }
87
123
  },
88
124
  privacy: {
89
125
  cookies_enabled: true
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.274.0";
9
+ export const VERSION = "1.276.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Exposes the version of the agent
8
8
  */
9
- export const VERSION = "1.274.0";
9
+ export const VERSION = "1.276.0";
10
10
 
11
11
  /**
12
12
  * Exposes the build type of the agent
@@ -166,6 +166,27 @@ export class Harvest extends SharedContext {
166
166
  }
167
167
  cbFinished(cbResult);
168
168
  }, eventListenerOpts(false));
169
+ } else if (!opts.unload && cbFinished && submitMethod === submitData.xhrFetch) {
170
+ const harvestScope = this;
171
+ result.then(async function (response) {
172
+ const status = response.status;
173
+ const cbResult = {
174
+ sent: true,
175
+ status,
176
+ fullUrl,
177
+ fetchResponse: response
178
+ };
179
+ if (response.status === 429) {
180
+ cbResult.retry = true;
181
+ cbResult.delay = harvestScope.tooManyRequestsDelay;
182
+ } else if (status === 408 || status === 500 || status === 503) {
183
+ cbResult.retry = true;
184
+ }
185
+ if (opts.needResponse) {
186
+ cbResult.responseText = await response.text();
187
+ }
188
+ cbFinished(cbResult);
189
+ });
169
190
  }
170
191
  return result;
171
192
  }
@@ -19,11 +19,47 @@ import { isBrowserScope } from '../constants/runtime';
19
19
  export function getSubmitMethod({
20
20
  isFinalHarvest = false
21
21
  } = {}) {
22
- return isFinalHarvest && isBrowserScope
23
- // Use sendBeacon for final harvest
24
- ? beacon
25
- // If not final harvest, or not browserScope, always use xhr post
26
- : xhr;
22
+ if (isFinalHarvest && isBrowserScope) {
23
+ // Use sendBeacon for final harvest
24
+ return beacon;
25
+ }
26
+
27
+ // If not final harvest, or not browserScope, use XHR post if available
28
+ if (typeof XMLHttpRequest !== 'undefined') {
29
+ return xhr;
30
+ }
31
+
32
+ // Fallback for web workers where XMLHttpRequest is not available
33
+ return xhrFetch;
34
+ }
35
+
36
+ /**
37
+ *
38
+ * @param url
39
+ * @param body
40
+ * @param method
41
+ * @param headers
42
+ * @returns {Promise<Response>}
43
+ */
44
+ export function xhrFetch({
45
+ url,
46
+ body = null,
47
+ method = 'POST',
48
+ headers = [{
49
+ key: 'content-type',
50
+ value: 'text/plain'
51
+ }]
52
+ }) {
53
+ const objHeaders = {};
54
+ for (const header of headers) {
55
+ objHeaders[header.key] = header.value;
56
+ }
57
+ return fetch(url, {
58
+ headers: objHeaders,
59
+ method,
60
+ body,
61
+ credentials: 'include'
62
+ });
27
63
  }
28
64
 
29
65
  /**
@@ -36,8 +36,8 @@ export function wrapEvents(sharedEE) {
36
36
  // Guard against instrumenting environments w/o necessary features
37
37
  if ('getPrototypeOf' in Object) {
38
38
  if (isBrowserScope) findEventListenerProtoAndCb(document, wrapNode);
39
+ if (XHR) findEventListenerProtoAndCb(XHR.prototype, wrapNode);
39
40
  findEventListenerProtoAndCb(globalScope, wrapNode);
40
- findEventListenerProtoAndCb(XHR.prototype, wrapNode);
41
41
  }
42
42
  ee.on(ADD_EVENT_LISTENER + '-start', function (args, target) {
43
43
  var originalListener = args[1];
@@ -10,7 +10,8 @@
10
10
  import { ee as baseEE, contextId } from '../event-emitter/contextual-ee';
11
11
  import { EventContext } from '../event-emitter/event-context';
12
12
  import { warn } from '../util/console';
13
- import { createWrapperWithEmitter as wfn } from './wrap-function';
13
+ import { flag, createWrapperWithEmitter as wfn } from './wrap-function';
14
+ const contextMap = new Map();
14
15
 
15
16
  /**
16
17
  * Wraps a supplied function and adds emitter events under the `-wrap-logger-` prefix
@@ -32,9 +33,11 @@ export function wrapLogger(sharedEE, parent, loggerFn, context) {
32
33
  const ctx = new EventContext(contextId);
33
34
  ctx.level = context.level;
34
35
  ctx.customAttributes = context.customAttributes;
36
+ const contextLookupKey = parent[loggerFn]?.[flag] || parent[loggerFn];
37
+ contextMap.set(contextLookupKey, ctx);
35
38
 
36
39
  /** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-` */
37
- wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', ctx);
40
+ wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey));
38
41
  return ee;
39
42
  }
40
43
 
@@ -26,6 +26,7 @@ const XHR_PROPS = ['open', 'send']; // these are the specific funcs being wrappe
26
26
  export function wrapXhr(sharedEE) {
27
27
  var baseEE = sharedEE || contextualEE;
28
28
  const ee = scopedEE(baseEE);
29
+ if (typeof globalScope.XMLHttpRequest === 'undefined') return ee;
29
30
 
30
31
  // Notice if our wrapping never ran yet, the falsy NaN will not early return; but if it has,
31
32
  // then we increment the count to track # of feats using this at runtime.
@@ -338,7 +338,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
338
338
  if (hasUndefinedHostname(params)) return; // don't bother with XHR of url with no hostname
339
339
 
340
340
  metrics.duration = now() - this.startTime;
341
- if (!this.loadCazptureCalled && xhr.readyState === 4) {
341
+ if (!this.loadCaptureCalled && xhr.readyState === 4) {
342
342
  captureXhrData(this, xhr);
343
343
  } else if (params.status == null) {
344
344
  params.status = 0;
@@ -6,17 +6,17 @@ import { stringify } from '../../../common/util/stringify';
6
6
  import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
7
7
  import { cleanURL } from '../../../common/url/clean-url';
8
8
  import { FEATURE_NAME } from '../constants';
9
- import { initialLocation, isBrowserScope } from '../../../common/constants/runtime';
9
+ import { globalScope, initialLocation, isBrowserScope } from '../../../common/constants/runtime';
10
10
  import { AggregateBase } from '../../utils/aggregate-base';
11
11
  import { warn } from '../../../common/util/console';
12
12
  import { now } from '../../../common/timing/now';
13
13
  import { registerHandler } from '../../../common/event-emitter/register-handler';
14
14
  import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
15
15
  import { applyFnToProps } from '../../../common/util/traverse';
16
- import { IDEAL_PAYLOAD_SIZE } from '../../../common/constants/agent-constants';
17
16
  import { FEATURE_TO_ENDPOINT } from '../../../loaders/features/features';
18
17
  import { UserActionsAggregator } from './user-actions/user-actions-aggregator';
19
18
  import { isIFrameWindow } from '../../../common/dom/iframe';
19
+ import { handle } from '../../../common/event-emitter/handle';
20
20
  export class Aggregate extends AggregateBase {
21
21
  static featureName = FEATURE_NAME;
22
22
  constructor(agentRef) {
@@ -30,6 +30,7 @@ export class Aggregate extends AggregateBase {
30
30
  this.deregisterDrain();
31
31
  return;
32
32
  }
33
+ this.trackSupportabilityMetrics();
33
34
  if (agentRef.init.page_action.enabled) {
34
35
  registerHandler('api-addPageAction', (timestamp, name, attributes) => {
35
36
  this.addEvent({
@@ -71,19 +72,28 @@ export class Aggregate extends AggregateBase {
71
72
  ...(isIFrameWindow(window) && {
72
73
  iframe: true
73
74
  }),
74
- ...(target?.id && {
75
+ ...(canTrustTargetAttribute('id') && {
75
76
  targetId: target.id
76
77
  }),
77
- ...(target?.tagName && {
78
+ ...(canTrustTargetAttribute('tagName') && {
78
79
  targetTag: target.tagName
79
80
  }),
80
- ...(target?.type && {
81
+ ...(canTrustTargetAttribute('type') && {
81
82
  targetType: target.type
82
83
  }),
83
- ...(target?.className && {
84
+ ...(canTrustTargetAttribute('className') && {
84
85
  targetClass: target.className
85
86
  })
86
87
  });
88
+
89
+ /**
90
+ * Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
91
+ * @param {string} attribute The attribute to check for on the target element
92
+ * @returns {boolean} Whether the target element has the attribute and can be trusted
93
+ */
94
+ function canTrustTargetAttribute(attribute) {
95
+ return !!(aggregatedUserAction.selectorPath !== 'window' && aggregatedUserAction.selectorPath !== 'document' && target instanceof HTMLElement && target?.[attribute]);
96
+ }
87
97
  }
88
98
  } catch (e) {
89
99
  // do nothing for now
@@ -110,10 +120,11 @@ export class Aggregate extends AggregateBase {
110
120
  const observer = new PerformanceObserver(list => {
111
121
  list.getEntries().forEach(entry => {
112
122
  try {
123
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen']);
113
124
  this.addEvent({
114
125
  eventType: 'BrowserPerformance',
115
126
  timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entry.startTime)),
116
- entryName: entry.name,
127
+ entryName: cleanURL(entry.name),
117
128
  entryDuration: entry.duration,
118
129
  entryType: type,
119
130
  ...(entry.detail && {
@@ -133,6 +144,47 @@ export class Aggregate extends AggregateBase {
133
144
  // Something failed in our set up, likely the browser does not support PO's... do nothing
134
145
  }
135
146
  }
147
+ if (isBrowserScope && agentRef.init.performance.resources.enabled) {
148
+ registerHandler('browserPerformance.resource', entry => {
149
+ try {
150
+ // convert the entry to a plain object and separate the name and duration from the object
151
+ // you need to do this to be able to spread it into the addEvent call later, and name and duration
152
+ // would be duplicative of entryName and entryDuration and are protected keys in NR1
153
+ const {
154
+ name,
155
+ duration,
156
+ ...entryObject
157
+ } = entry.toJSON();
158
+ let firstParty = false;
159
+ try {
160
+ const entryDomain = new URL(name).hostname;
161
+ const isNr = entryDomain.includes('newrelic.com') || entryDomain.includes('nr-data.net') || entryDomain.includes('nr-local.net');
162
+ /** decide if we should ignore nr-specific assets */
163
+ if (this.agentRef.init.performance.resources.ignore_newrelic && isNr) return;
164
+ /** decide if we should ignore the asset type (empty means allow everything, which is the default) */
165
+ if (this.agentRef.init.performance.resources.asset_types.length && !this.agentRef.init.performance.resources.asset_types.includes(entryObject.initiatorType)) return;
166
+ /** decide if the entryDomain is a first party domain */
167
+ firstParty = entryDomain === globalScope?.location.hostname || agentRef.init.performance.resources.first_party_domains.includes(entryDomain);
168
+ if (firstParty) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/FirstPartyResource/Seen']);
169
+ if (isNr) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/NrResource/Seen']);
170
+ } catch (err) {
171
+ // couldnt parse the URL, so firstParty will just default to false
172
+ }
173
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/Resource/Seen']);
174
+ const event = {
175
+ ...entryObject,
176
+ eventType: 'BrowserPerformance',
177
+ timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entryObject.startTime)),
178
+ entryName: name,
179
+ entryDuration: duration,
180
+ firstParty
181
+ };
182
+ this.addEvent(event);
183
+ } catch (err) {
184
+ this.ee.emit('internal-error', [err, 'GenericEvents-Resource']);
185
+ }
186
+ }, this.featureName, this.ee);
187
+ }
136
188
  this.harvestScheduler = new HarvestScheduler(FEATURE_TO_ENDPOINT[this.featureName], {
137
189
  onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
138
190
  onUnload: () => addUserAction?.(this.userActionAggregator.aggregationEvent)
@@ -181,8 +233,16 @@ export class Aggregate extends AggregateBase {
181
233
  /** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */
182
234
  ...obj
183
235
  };
184
- this.events.add(eventAttributes);
185
- this.checkEventLimits();
236
+ const addedEvent = this.events.add(eventAttributes);
237
+ if (!addedEvent && !this.events.isEmpty()) {
238
+ /** could not add the event because it pushed the buffer over the limit
239
+ * so we harvest early, and try to add it again now that the buffer is cleared
240
+ * if it fails again, we do nothing
241
+ */
242
+ this.ee.emit(SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
243
+ this.harvestScheduler.runHarvest();
244
+ this.events.add(eventAttributes);
245
+ }
186
246
  }
187
247
  serializer(eventBuffer) {
188
248
  return applyFnToProps({
@@ -195,11 +255,14 @@ export class Aggregate extends AggregateBase {
195
255
  at: this.agentRef.info.atts
196
256
  };
197
257
  }
198
- checkEventLimits() {
199
- // check if we've reached any harvest limits...
200
- if (this.events.byteSize() > IDEAL_PAYLOAD_SIZE) {
201
- this.ee.emit(SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
202
- this.harvestScheduler.runHarvest();
203
- }
258
+ trackSupportabilityMetrics() {
259
+ /** track usage SMs to improve these experimental features */
260
+ const configPerfTag = 'Config/Performance/';
261
+ if (this.agentRef.init.performance.capture_marks) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMarks/Enabled']);
262
+ if (this.agentRef.init.performance.capture_measures) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMeasures/Enabled']);
263
+ if (this.agentRef.init.performance.resources.enabled) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/Enabled']);
264
+ if (this.agentRef.init.performance.resources.asset_types?.length !== 0) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/AssetTypes/Changed']);
265
+ if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed']);
266
+ if (this.agentRef.init.performance.resources.ignore_newrelic === false) handle(SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed']);
204
267
  }
205
268
  }
@@ -5,4 +5,9 @@ export const MAX_PAYLOAD_SIZE = 1000000;
5
5
  export const OBSERVED_EVENTS = ['auxclick', 'click', 'copy', 'keydown', 'paste', 'scrollend'];
6
6
  export const OBSERVED_WINDOW_EVENTS = ['focus', 'blur'];
7
7
  export const RAGE_CLICK_THRESHOLD_EVENTS = 4;
8
- export const RAGE_CLICK_THRESHOLD_MS = 1000;
8
+ export const RAGE_CLICK_THRESHOLD_MS = 1000;
9
+ export const FEATURE_FLAGS = {
10
+ MARKS: 'experimental.marks',
11
+ MEASURES: 'experimental.measures',
12
+ RESOURCES: 'experimental.resources'
13
+ };
@@ -2,7 +2,7 @@
2
2
  * SPDX-License-Identifier: Apache-2.0
3
3
  */
4
4
 
5
- import { isBrowserScope } from '../../../common/constants/runtime';
5
+ import { globalScope, isBrowserScope } from '../../../common/constants/runtime';
6
6
  import { handle } from '../../../common/event-emitter/handle';
7
7
  import { windowAddEventListener } from '../../../common/event-listener/event-listener-opts';
8
8
  import { InstrumentBase } from '../../utils/instrument-base';
@@ -11,14 +11,26 @@ export class Instrument extends InstrumentBase {
11
11
  static featureName = FEATURE_NAME;
12
12
  constructor(agentRef, auto = true) {
13
13
  super(agentRef, FEATURE_NAME, auto);
14
- const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.performance.capture_marks, agentRef.init.performance.capture_measures, agentRef.init.user_actions.enabled
15
- // other future generic event source configs to go here, like M&Ms, PageResouce, etc.
16
- ];
17
- if (isBrowserScope && agentRef.init.user_actions.enabled) {
18
- OBSERVED_EVENTS.forEach(eventType => windowAddEventListener(eventType, evt => handle('ua', [evt], undefined, this.featureName, this.ee), true));
19
- OBSERVED_WINDOW_EVENTS.forEach(eventType => windowAddEventListener(eventType, evt => handle('ua', [evt], undefined, this.featureName, this.ee))
20
- // Capture is not used here so that we don't get element focus/blur events, only the window's as they do not bubble. They are also not cancellable, so no worries about being front of line.
21
- );
14
+ /** config values that gate whether the generic events aggregator should be imported at all */
15
+ const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.performance.capture_marks, agentRef.init.performance.capture_measures, agentRef.init.user_actions.enabled, agentRef.init.performance.resources.enabled];
16
+ if (isBrowserScope) {
17
+ if (agentRef.init.user_actions.enabled) {
18
+ OBSERVED_EVENTS.forEach(eventType => windowAddEventListener(eventType, evt => handle('ua', [evt], undefined, this.featureName, this.ee), true));
19
+ OBSERVED_WINDOW_EVENTS.forEach(eventType => windowAddEventListener(eventType, evt => handle('ua', [evt], undefined, this.featureName, this.ee))
20
+ // Capture is not used here so that we don't get element focus/blur events, only the window's as they do not bubble. They are also not cancellable, so no worries about being front of line.
21
+ );
22
+ }
23
+ if (agentRef.init.performance.resources.enabled && globalScope.PerformanceObserver?.supportedEntryTypes.includes('resource')) {
24
+ const observer = new PerformanceObserver(list => {
25
+ list.getEntries().forEach(entry => {
26
+ handle('browserPerformance.resource', [entry], undefined, this.featureName, this.ee);
27
+ });
28
+ });
29
+ observer.observe({
30
+ type: 'resource',
31
+ buffered: true
32
+ });
33
+ }
22
34
  }
23
35
 
24
36
  /** If any of the sources are active, import the aggregator. otherwise deregister */
@@ -107,9 +107,10 @@ export class Aggregate extends AggregateBase {
107
107
  * @param {boolean=} internal if the error was "caught" and deemed "internal" before reporting to the jserrors feature
108
108
  * @param {object=} customAttributes any custom attributes to be included in the error payload
109
109
  * @param {boolean=} hasReplay a flag indicating if the error occurred during a replay session
110
+ * @param {string=} swallowReason a string indicating pre-defined reason if swallowing the error. Mainly used by the internal error SMs.
110
111
  * @returns
111
112
  */
112
- storeError(err, time, internal, customAttributes, hasReplay) {
113
+ storeError(err, time, internal, customAttributes, hasReplay, swallowReason) {
113
114
  if (!err) return;
114
115
  // are we in an interaction
115
116
  time = time || now();
@@ -127,7 +128,7 @@ export class Aggregate extends AggregateBase {
127
128
  const {
128
129
  shouldSwallow,
129
130
  reason
130
- } = evaluateInternalError(stackInfo, internal);
131
+ } = evaluateInternalError(stackInfo, internal, swallowReason);
131
132
  if (shouldSwallow) {
132
133
  handle(SUPPORTABILITY_METRIC_CHANNEL, ['Internal/Error/' + reason], undefined, FEATURE_NAMES.metrics, this.ee);
133
134
  return;
@@ -5,10 +5,10 @@ const REASON_SECURITY_POLICY = 'Security-Policy';
5
5
  * @param {Object} stackInfo - The error stack information.
6
6
  * @returns {boolean} - Whether the error should be swallowed or not.
7
7
  */
8
- export function evaluateInternalError(stackInfo, internal) {
8
+ export function evaluateInternalError(stackInfo, internal, reason) {
9
9
  const output = {
10
10
  shouldSwallow: internal || false,
11
- reason: 'Other'
11
+ reason: reason || 'Other'
12
12
  };
13
13
  const leadingFrame = stackInfo.frames?.[0];
14
14
  /** If we cant otherwise determine from the frames and message, the default of internal + reason will be the fallback */
@@ -20,9 +20,9 @@ export class Instrument extends InstrumentBase {
20
20
  // this try-catch can be removed when IE11 is completely unsupported & gone
21
21
  this.removeOnAbort = new AbortController();
22
22
  } catch (e) {}
23
- this.ee.on('internal-error', error => {
23
+ this.ee.on('internal-error', (error, reason) => {
24
24
  if (!this.abortHandler) return;
25
- handle('ierr', [castError(error), now(), true, {}, this.#replayRunning], undefined, this.featureName, this.ee);
25
+ handle('ierr', [castError(error), now(), true, {}, this.#replayRunning, reason], undefined, this.featureName, this.ee);
26
26
  });
27
27
  this.ee.on(SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
28
28
  this.#replayRunning = isRunning;
@@ -148,40 +148,6 @@ export class Aggregate extends AggregateBase {
148
148
  });
149
149
  }
150
150
  unload() {
151
- try {
152
- if (this.resourcesSent) return;
153
- this.resourcesSent = true; // make sure this only gets sent once
154
-
155
- // Capture SMs around network resources using the performance API to assess
156
- // work to split this out from the ST nodes
157
- // differentiate between internal+external and ajax+non-ajax
158
- const ajaxResources = ['beacon', 'fetch', 'xmlhttprequest'];
159
- const internalUrls = ['nr-data.net', 'newrelic.com', 'nr-local.net', 'localhost'];
160
- function isInternal(x) {
161
- return internalUrls.some(y => x.name.indexOf(y) >= 0);
162
- }
163
- function isAjax(x) {
164
- return ajaxResources.includes(x.initiatorType);
165
- }
166
- const allResources = performance?.getEntriesByType('resource') || [];
167
- allResources.forEach(entry => {
168
- if (isInternal(entry)) {
169
- if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/Internal');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/Internal');
170
- } else {
171
- if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/External');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/External');
172
- }
173
- });
174
-
175
- // Capture SMs for performance markers and measures to assess the usage and possible inclusion of this
176
- // data in the agent for use in NR
177
- if (typeof performance !== 'undefined') {
178
- const markers = performance.getEntriesByType('mark');
179
- const measures = performance.getEntriesByType('measure');
180
- if (markers.length) this.storeSupportabilityMetrics('Generic/Performance/Mark/Seen', markers.length);
181
- if (measures.length) this.storeSupportabilityMetrics('Generic/Performance/Measure/Seen', measures.length);
182
- }
183
- } catch (e) {
184
- // do nothing
185
- }
151
+ // do nothing for now, marks and measures and resources stats are now being captured by the ge feature
186
152
  }
187
153
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/common/config/init.js"],"names":[],"mappings":"AAsHA,+CAIC;AAED,0DAKC;AAED,+DAYC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../src/common/config/init.js"],"names":[],"mappings":"AAwIA,+CAIC;AAED,0DAKC;AAED,+DAYC"}
@@ -1 +1 @@
1
- {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,gBAA2E;IAE3E,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CA+EnB;IAGD,gDAqBC;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;8BAvOY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAbjC,2BAA2B"}
1
+ {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../../../src/common/harvest/harvest.js"],"names":[],"mappings":"AAsBA;;;;;;GAMG;AACH;IAII,0BAA2H;IAC3H,gBAA2E;IAE3E,YAAiB;IAGnB;;;;;OAKG;IACH,aAFW,eAAe,WAWzB;IAED;;;OAGG;IACH,YAFW,eAAe,WAMzB;IAED;;;;;;OAMG;IACH,gGAJW,eAAe,GACb,OAAO,CAkGnB;IAGD,gDAqBC;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;8BA1PY,OAAO,YAAY,EAAE,eAAe;wCACpC,OAAO,YAAY,EAAE,yBAAyB;6BAC9C,OAAO,YAAY,EAAE,cAAc;qCACnC,OAAO,YAAY,EAAE,sBAAsB;4CAC3C,OAAO,YAAY,EAAE,6BAA6B;8BAbjC,2BAA2B"}
@@ -9,7 +9,24 @@
9
9
  */
10
10
  export function getSubmitMethod({ isFinalHarvest }?: {
11
11
  isFinalHarvest: boolean;
12
- }): typeof xhr | typeof beacon;
12
+ }): typeof xhr | typeof beacon | typeof xhrFetch;
13
+ /**
14
+ *
15
+ * @param url
16
+ * @param body
17
+ * @param method
18
+ * @param headers
19
+ * @returns {Promise<Response>}
20
+ */
21
+ export function xhrFetch({ url, body, method, headers }: {
22
+ url: any;
23
+ body?: null | undefined;
24
+ method?: string | undefined;
25
+ headers?: {
26
+ key: string;
27
+ value: string;
28
+ }[] | undefined;
29
+ }): Promise<Response>;
13
30
  /**
14
31
  * Send via XHR
15
32
  * @param {Object} args - The args.
@@ -1 +1 @@
1
- {"version":3,"file":"submit-data.d.ts","sourceRoot":"","sources":["../../../../src/common/util/submit-data.js"],"names":[],"mappings":"AAQA;;GAEG;AAEH;;;;;GAKG;AACH,qDAHG;IAAsB,cAAc,EAA5B,OAAO;CAEjB,8BAOA;AAED;;;;;;;;;GASG;AACH,0DAPG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;IACQ,IAAI,GAAnB,OAAO,YAAC;IACO,MAAM,GAArB,MAAM,YAAC;IAC+B,OAAO;aAAvC,MAAM;eAAS,MAAM;;CACnC,GAAU,cAAc,CAmB1B;AAED;;;;;;GAMG;AACH,sCAJG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;CACf,GAAU,OAAO,CAanB;6BAhEY,0BAAU"}
1
+ {"version":3,"file":"submit-data.d.ts","sourceRoot":"","sources":["../../../../src/common/util/submit-data.js"],"names":[],"mappings":"AAQA;;GAEG;AAEH;;;;;GAKG;AACH,qDAHG;IAAsB,cAAc,EAA5B,OAAO;CAEjB,gDAcA;AAED;;;;;;;GAOG;AACH;;;;;;;;IAFa,OAAO,CAAC,QAAQ,CAAC,CAkB7B;AAED;;;;;;;;;GASG;AACH,0DAPG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;IACQ,IAAI,GAAnB,OAAO,YAAC;IACO,MAAM,GAArB,MAAM,YAAC;IAC+B,OAAO;aAAvC,MAAM;eAAS,MAAM;;CACnC,GAAU,cAAc,CAmB1B;AAED;;;;;;GAMG;AACH,sCAJG;IAAqB,GAAG,EAAhB,MAAM;IACQ,IAAI,GAAlB,MAAM,YAAC;CACf,GAAU,OAAO,CAanB;6BAjGY,0BAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"AAcA;;;;;;GAMG;AAEH,qCANW,MAAM,UACN,MAAM,YACN,MAAM,iBACJ,MAAM,CAoBlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
1
+ {"version":3,"file":"wrap-logger.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-logger.js"],"names":[],"mappings":"AAgBA;;;;;;GAMG;AAEH,qCANW,MAAM,UACN,MAAM,YACN,MAAM,iBACJ,MAAM,CAuBlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrap-xhr.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-xhr.js"],"names":[],"mappings":"AAmBA;;;;;GAKG;AAEH,kCAJW,MAAM,GACJ,MAAM,CA+KlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
1
+ {"version":3,"file":"wrap-xhr.d.ts","sourceRoot":"","sources":["../../../../src/common/wrap/wrap-xhr.js"],"names":[],"mappings":"AAmBA;;;;;GAKG;AAEH,kCAJW,MAAM,GACJ,MAAM,CAiLlB;AAED;;;;;;GAMG;AACH,mCAJW,MAAM,GAEJ,MAAM,CAIlB"}
@@ -24,7 +24,7 @@ export class Aggregate extends AggregateBase {
24
24
  ua: any;
25
25
  at: any;
26
26
  };
27
- checkEventLimits(): void;
27
+ trackSupportabilityMetrics(): void;
28
28
  }
29
29
  import { AggregateBase } from '../../utils/aggregate-base';
30
30
  import { UserActionsAggregator } from './user-actions/user-actions-aggregator';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IACjC,2BAiHC;IA9GC,yBAA4B;IAC5B,wBAAyE;IAEzE,gCAAkG;IA4B9F,4CAAuD;IAsEzD,mCAGQ;IASZ;;;;;;;;;;;OAWG;IACH,eAHW,MAAM,YAAC,QAmCjB;IAED,qCAEC;IAED;;;MAEC;IAED,yBAMC;CACF;8BA9L6B,4BAA4B;sCAQpB,wCAAwC;iCAZ7C,2CAA2C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IACjC,2BAsKC;IAnKC,yBAA4B;IAC5B,wBAAyE;IAEzE,gCAAkG;IA8B9F,4CAAuD;IAyHzD,mCAGQ;IASZ;;;;;;;;;;;OAWG;IACH,eAHW,MAAM,YAAC,QA0CjB;IAED,qCAEC;IAED;;;MAEC;IAED,mCASC;CACF;8BA7P6B,4BAA4B;sCAOpB,wCAAwC;iCAX7C,2CAA2C"}
@@ -5,4 +5,9 @@ export const OBSERVED_EVENTS: string[];
5
5
  export const OBSERVED_WINDOW_EVENTS: string[];
6
6
  export const RAGE_CLICK_THRESHOLD_EVENTS: 4;
7
7
  export const RAGE_CLICK_THRESHOLD_MS: 1000;
8
+ export namespace FEATURE_FLAGS {
9
+ let MARKS: string;
10
+ let MEASURES: string;
11
+ let RESOURCES: string;
12
+ }
8
13
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/instrument/index.js"],"names":[],"mappings":"AAUA;IACE,2BAAiC;IACjC,2CAuBC;CACF;AAED,8CAAuC;+BA/BR,6BAA6B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/generic_events/instrument/index.js"],"names":[],"mappings":"AAUA;IACE,2BAAiC;IACjC,2CAkCC;CACF;AAED,8CAAuC;+BA1CR,6BAA6B"}
@@ -29,9 +29,10 @@ export class Aggregate extends AggregateBase {
29
29
  * @param {boolean=} internal if the error was "caught" and deemed "internal" before reporting to the jserrors feature
30
30
  * @param {object=} customAttributes any custom attributes to be included in the error payload
31
31
  * @param {boolean=} hasReplay a flag indicating if the error occurred during a replay session
32
+ * @param {string=} swallowReason a string indicating pre-defined reason if swallowing the error. Mainly used by the internal error SMs.
32
33
  * @returns
33
34
  */
34
- storeError(err: Error | UncaughtError, time: number, internal?: boolean | undefined, customAttributes?: object | undefined, hasReplay?: boolean | undefined): void;
35
+ storeError(err: Error | UncaughtError, time: number, internal?: boolean | undefined, customAttributes?: object | undefined, hasReplay?: boolean | undefined, swallowReason?: string | undefined): void;
35
36
  onInteractionDone(interaction: any, wasSaved: any): void;
36
37
  onSoftNavNotification(interactionId: any, wasFinished: any, softNavAttrs: any): void;
37
38
  #private;