@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
package/CHANGELOG.md CHANGED
@@ -3,6 +3,33 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.276.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.275.0...v1.276.0) (2024-12-16)
7
+
8
+
9
+ ### Features
10
+
11
+ * allow feature flags to control experimental features ([#1282](https://github.com/newrelic/newrelic-browser-agent/issues/1282)) ([537e72d](https://github.com/newrelic/newrelic-browser-agent/commit/537e72da0821792006abd16c41ffa025fd73b474))
12
+ * Capture Page Resource Assets ([#1257](https://github.com/newrelic/newrelic-browser-agent/issues/1257)) ([e4c7deb](https://github.com/newrelic/newrelic-browser-agent/commit/e4c7debe2a1653efdad8940c55a71dd0140ce900))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * Fix syntax error in fallback when tracking xhr readyState ([#1272](https://github.com/newrelic/newrelic-browser-agent/issues/1272)) ([2bb6b5b](https://github.com/newrelic/newrelic-browser-agent/commit/2bb6b5b2e104a6b3ac9ceb70d396f65cfb61c1bd))
18
+ * Ignore reserved attribute names on UserActions tied to the window ([#1261](https://github.com/newrelic/newrelic-browser-agent/issues/1261)) ([8410dbe](https://github.com/newrelic/newrelic-browser-agent/commit/8410dbeef30aeb0e578c63a293ec311c3d42f8da))
19
+
20
+ ## [1.275.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.274.0...v1.275.0) (2024-12-03)
21
+
22
+
23
+ ### Features
24
+
25
+ * Allow logs api wrapper to update custom attributes ([#1265](https://github.com/newrelic/newrelic-browser-agent/issues/1265)) ([8d10e14](https://github.com/newrelic/newrelic-browser-agent/commit/8d10e14953f9a5b9ba97e865ba5476fc527ba384))
26
+ * Enable the browser agent to run in extension background contexts ([#1206](https://github.com/newrelic/newrelic-browser-agent/issues/1206)) ([37e976b](https://github.com/newrelic/newrelic-browser-agent/commit/37e976bf360c209efd163855e7fbe84d665e444b))
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * Harvest generic events when max size is reached ([#1250](https://github.com/newrelic/newrelic-browser-agent/issues/1250)) ([e00a469](https://github.com/newrelic/newrelic-browser-agent/commit/e00a46975bcc93c48798bd9153f3a503998b0915))
32
+
6
33
  ## [1.274.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.273.1...v1.274.0) (2024-11-19)
7
34
 
8
35
 
@@ -6,15 +6,22 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.getConfiguration = getConfiguration;
7
7
  exports.getConfigurationValue = getConfigurationValue;
8
8
  exports.setConfiguration = setConfiguration;
9
- var _constants = require("../../features/logging/constants");
9
+ var _constants = require("../../features/generic_events/constants");
10
+ var _constants2 = require("../../features/logging/constants");
10
11
  var _querySelector = require("../dom/query-selector");
11
- var _constants2 = require("../session/constants");
12
+ var _constants3 = require("../session/constants");
12
13
  var _console = require("../util/console");
13
14
  var _nreum = require("../window/nreum");
14
15
  var _configurable = require("./configurable");
15
16
  const nrMask = '[data-nr-mask]';
16
17
  const model = () => {
17
18
  const hiddenState = {
19
+ feature_flags: [],
20
+ experimental: {
21
+ marks: false,
22
+ measures: false,
23
+ resources: false
24
+ },
18
25
  mask_selector: '*',
19
26
  block_selector: '[data-nr-block]',
20
27
  mask_input_options: {
@@ -52,7 +59,12 @@ const model = () => {
52
59
  cors_use_tracecontext_headers: undefined,
53
60
  allowed_origins: undefined
54
61
  },
55
- feature_flags: [],
62
+ get feature_flags() {
63
+ return hiddenState.feature_flags;
64
+ },
65
+ set feature_flags(val) {
66
+ hiddenState.feature_flags = val;
67
+ },
56
68
  generic_events: {
57
69
  enabled: true,
58
70
  harvestTimeSeconds: 30,
@@ -70,7 +82,7 @@ const model = () => {
70
82
  enabled: true,
71
83
  harvestTimeSeconds: 10,
72
84
  autoStart: true,
73
- level: _constants.LOG_LEVELS.INFO
85
+ level: _constants2.LOG_LEVELS.INFO
74
86
  },
75
87
  metrics: {
76
88
  enabled: true,
@@ -90,8 +102,32 @@ const model = () => {
90
102
  autoStart: true
91
103
  },
92
104
  performance: {
93
- capture_marks: false,
94
- capture_measures: false // false by default through experimental phase, but flipped to true once GA'd
105
+ get capture_marks() {
106
+ return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.MARKS) || hiddenState.experimental.marks;
107
+ },
108
+ set capture_marks(val) {
109
+ hiddenState.experimental.marks = val;
110
+ },
111
+ get capture_measures() {
112
+ return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.MEASURES) || hiddenState.experimental.measures;
113
+ },
114
+ set capture_measures(val) {
115
+ hiddenState.experimental.measures = val;
116
+ },
117
+ resources: {
118
+ // 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
119
+ get enabled() {
120
+ return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.RESOURCES) || hiddenState.experimental.resources;
121
+ },
122
+ set enabled(val) {
123
+ hiddenState.experimental.resources = val;
124
+ },
125
+ asset_types: [],
126
+ // MDN types to collect, empty array will collect all types
127
+ first_party_domains: [],
128
+ // when included, will decorate the resource as first party if matching
129
+ ignore_newrelic: true // ignore capturing internal agent scripts and harvest calls
130
+ }
95
131
  },
96
132
  privacy: {
97
133
  cookies_enabled: true
@@ -103,8 +139,8 @@ const model = () => {
103
139
  beacon: undefined // likewise for the url to which we send analytics
104
140
  },
105
141
  session: {
106
- expiresMs: _constants2.DEFAULT_EXPIRES_MS,
107
- inactiveMs: _constants2.DEFAULT_INACTIVE_MS
142
+ expiresMs: _constants3.DEFAULT_EXPIRES_MS,
143
+ inactiveMs: _constants3.DEFAULT_INACTIVE_MS
108
144
  },
109
145
  session_replay: {
110
146
  // feature settings
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = exports.VERSION = "1.274.0";
15
+ const VERSION = exports.VERSION = "1.276.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = exports.VERSION = "1.274.0";
15
+ const VERSION = exports.VERSION = "1.276.0";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -174,6 +174,27 @@ class Harvest extends _sharedContext.SharedContext {
174
174
  }
175
175
  cbFinished(cbResult);
176
176
  }, (0, _eventListenerOpts.eventListenerOpts)(false));
177
+ } else if (!opts.unload && cbFinished && submitMethod === submitData.xhrFetch) {
178
+ const harvestScope = this;
179
+ result.then(async function (response) {
180
+ const status = response.status;
181
+ const cbResult = {
182
+ sent: true,
183
+ status,
184
+ fullUrl,
185
+ fetchResponse: response
186
+ };
187
+ if (response.status === 429) {
188
+ cbResult.retry = true;
189
+ cbResult.delay = harvestScope.tooManyRequestsDelay;
190
+ } else if (status === 408 || status === 500 || status === 503) {
191
+ cbResult.retry = true;
192
+ }
193
+ if (opts.needResponse) {
194
+ cbResult.responseText = await response.text();
195
+ }
196
+ cbFinished(cbResult);
197
+ });
177
198
  }
178
199
  return result;
179
200
  }
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.beacon = beacon;
7
7
  exports.getSubmitMethod = getSubmitMethod;
8
8
  exports.xhr = xhr;
9
+ exports.xhrFetch = xhrFetch;
9
10
  var _runtime = require("../constants/runtime");
10
11
  /**
11
12
  * @file Contains common methods used to transmit harvested data.
@@ -26,11 +27,47 @@ var _runtime = require("../constants/runtime");
26
27
  function getSubmitMethod({
27
28
  isFinalHarvest = false
28
29
  } = {}) {
29
- return isFinalHarvest && _runtime.isBrowserScope
30
- // Use sendBeacon for final harvest
31
- ? beacon
32
- // If not final harvest, or not browserScope, always use xhr post
33
- : xhr;
30
+ if (isFinalHarvest && _runtime.isBrowserScope) {
31
+ // Use sendBeacon for final harvest
32
+ return beacon;
33
+ }
34
+
35
+ // If not final harvest, or not browserScope, use XHR post if available
36
+ if (typeof XMLHttpRequest !== 'undefined') {
37
+ return xhr;
38
+ }
39
+
40
+ // Fallback for web workers where XMLHttpRequest is not available
41
+ return xhrFetch;
42
+ }
43
+
44
+ /**
45
+ *
46
+ * @param url
47
+ * @param body
48
+ * @param method
49
+ * @param headers
50
+ * @returns {Promise<Response>}
51
+ */
52
+ function xhrFetch({
53
+ url,
54
+ body = null,
55
+ method = 'POST',
56
+ headers = [{
57
+ key: 'content-type',
58
+ value: 'text/plain'
59
+ }]
60
+ }) {
61
+ const objHeaders = {};
62
+ for (const header of headers) {
63
+ objHeaders[header.key] = header.value;
64
+ }
65
+ return fetch(url, {
66
+ headers: objHeaders,
67
+ method,
68
+ body,
69
+ credentials: 'include'
70
+ });
34
71
  }
35
72
 
36
73
  /**
@@ -44,8 +44,8 @@ function wrapEvents(sharedEE) {
44
44
  // Guard against instrumenting environments w/o necessary features
45
45
  if ('getPrototypeOf' in Object) {
46
46
  if (_runtime.isBrowserScope) findEventListenerProtoAndCb(document, wrapNode);
47
+ if (XHR) findEventListenerProtoAndCb(XHR.prototype, wrapNode);
47
48
  findEventListenerProtoAndCb(_runtime.globalScope, wrapNode);
48
- findEventListenerProtoAndCb(XHR.prototype, wrapNode);
49
49
  }
50
50
  ee.on(ADD_EVENT_LISTENER + '-start', function (args, target) {
51
51
  var originalListener = args[1];
@@ -18,6 +18,8 @@ var _wrapFunction = require("./wrap-function");
18
18
  * This module is used by: jserrors, spa.
19
19
  */
20
20
 
21
+ const contextMap = new Map();
22
+
21
23
  /**
22
24
  * Wraps a supplied function and adds emitter events under the `-wrap-logger-` prefix
23
25
  * @param {Object} sharedEE - The shared event emitter on which a new scoped event emitter will be based.
@@ -38,9 +40,11 @@ function wrapLogger(sharedEE, parent, loggerFn, context) {
38
40
  const ctx = new _eventContext.EventContext(_contextualEe.contextId);
39
41
  ctx.level = context.level;
40
42
  ctx.customAttributes = context.customAttributes;
43
+ const contextLookupKey = parent[loggerFn]?.[_wrapFunction.flag] || parent[loggerFn];
44
+ contextMap.set(contextLookupKey, ctx);
41
45
 
42
46
  /** observe calls to <loggerFn> and emit events prefixed with `wrap-logger-` */
43
- wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', ctx);
47
+ wrapFn.inPlace(parent, [loggerFn], 'wrap-logger-', () => contextMap.get(contextLookupKey));
44
48
  return ee;
45
49
  }
46
50
 
@@ -33,6 +33,7 @@ const XHR_PROPS = ['open', 'send']; // these are the specific funcs being wrappe
33
33
  function wrapXhr(sharedEE) {
34
34
  var baseEE = sharedEE || _contextualEe.ee;
35
35
  const ee = scopedEE(baseEE);
36
+ if (typeof _runtime.globalScope.XMLHttpRequest === 'undefined') return ee;
36
37
 
37
38
  // Notice if our wrapping never ran yet, the falsy NaN will not early return; but if it has,
38
39
  // then we increment the count to track # of feats using this at runtime.
@@ -346,7 +346,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
346
346
  if ((0, _denyList.hasUndefinedHostname)(params)) return; // don't bother with XHR of url with no hostname
347
347
 
348
348
  metrics.duration = (0, _now.now)() - this.startTime;
349
- if (!this.loadCazptureCalled && xhr.readyState === 4) {
349
+ if (!this.loadCaptureCalled && xhr.readyState === 4) {
350
350
  captureXhrData(this, xhr);
351
351
  } else if (params.status == null) {
352
352
  params.status = 0;
@@ -15,10 +15,10 @@ var _now = require("../../../common/timing/now");
15
15
  var _registerHandler = require("../../../common/event-emitter/register-handler");
16
16
  var _constants2 = require("../../metrics/constants");
17
17
  var _traverse = require("../../../common/util/traverse");
18
- var _agentConstants = require("../../../common/constants/agent-constants");
19
18
  var _features = require("../../../loaders/features/features");
20
19
  var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
21
20
  var _iframe = require("../../../common/dom/iframe");
21
+ var _handle = require("../../../common/event-emitter/handle");
22
22
  /*
23
23
  * Copyright 2020 New Relic Corporation. All rights reserved.
24
24
  * SPDX-License-Identifier: Apache-2.0
@@ -37,6 +37,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
37
37
  this.deregisterDrain();
38
38
  return;
39
39
  }
40
+ this.trackSupportabilityMetrics();
40
41
  if (agentRef.init.page_action.enabled) {
41
42
  (0, _registerHandler.registerHandler)('api-addPageAction', (timestamp, name, attributes) => {
42
43
  this.addEvent({
@@ -78,19 +79,28 @@ class Aggregate extends _aggregateBase.AggregateBase {
78
79
  ...((0, _iframe.isIFrameWindow)(window) && {
79
80
  iframe: true
80
81
  }),
81
- ...(target?.id && {
82
+ ...(canTrustTargetAttribute('id') && {
82
83
  targetId: target.id
83
84
  }),
84
- ...(target?.tagName && {
85
+ ...(canTrustTargetAttribute('tagName') && {
85
86
  targetTag: target.tagName
86
87
  }),
87
- ...(target?.type && {
88
+ ...(canTrustTargetAttribute('type') && {
88
89
  targetType: target.type
89
90
  }),
90
- ...(target?.className && {
91
+ ...(canTrustTargetAttribute('className') && {
91
92
  targetClass: target.className
92
93
  })
93
94
  });
95
+
96
+ /**
97
+ * Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
98
+ * @param {string} attribute The attribute to check for on the target element
99
+ * @returns {boolean} Whether the target element has the attribute and can be trusted
100
+ */
101
+ function canTrustTargetAttribute(attribute) {
102
+ return !!(aggregatedUserAction.selectorPath !== 'window' && aggregatedUserAction.selectorPath !== 'document' && target instanceof HTMLElement && target?.[attribute]);
103
+ }
94
104
  }
95
105
  } catch (e) {
96
106
  // do nothing for now
@@ -117,10 +127,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
117
127
  const observer = new PerformanceObserver(list => {
118
128
  list.getEntries().forEach(entry => {
119
129
  try {
130
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen']);
120
131
  this.addEvent({
121
132
  eventType: 'BrowserPerformance',
122
133
  timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entry.startTime)),
123
- entryName: entry.name,
134
+ entryName: (0, _cleanUrl.cleanURL)(entry.name),
124
135
  entryDuration: entry.duration,
125
136
  entryType: type,
126
137
  ...(entry.detail && {
@@ -140,6 +151,47 @@ class Aggregate extends _aggregateBase.AggregateBase {
140
151
  // Something failed in our set up, likely the browser does not support PO's... do nothing
141
152
  }
142
153
  }
154
+ if (_runtime.isBrowserScope && agentRef.init.performance.resources.enabled) {
155
+ (0, _registerHandler.registerHandler)('browserPerformance.resource', entry => {
156
+ try {
157
+ // convert the entry to a plain object and separate the name and duration from the object
158
+ // you need to do this to be able to spread it into the addEvent call later, and name and duration
159
+ // would be duplicative of entryName and entryDuration and are protected keys in NR1
160
+ const {
161
+ name,
162
+ duration,
163
+ ...entryObject
164
+ } = entry.toJSON();
165
+ let firstParty = false;
166
+ try {
167
+ const entryDomain = new URL(name).hostname;
168
+ const isNr = entryDomain.includes('newrelic.com') || entryDomain.includes('nr-data.net') || entryDomain.includes('nr-local.net');
169
+ /** decide if we should ignore nr-specific assets */
170
+ if (this.agentRef.init.performance.resources.ignore_newrelic && isNr) return;
171
+ /** decide if we should ignore the asset type (empty means allow everything, which is the default) */
172
+ if (this.agentRef.init.performance.resources.asset_types.length && !this.agentRef.init.performance.resources.asset_types.includes(entryObject.initiatorType)) return;
173
+ /** decide if the entryDomain is a first party domain */
174
+ firstParty = entryDomain === _runtime.globalScope?.location.hostname || agentRef.init.performance.resources.first_party_domains.includes(entryDomain);
175
+ if (firstParty) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/FirstPartyResource/Seen']);
176
+ if (isNr) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/NrResource/Seen']);
177
+ } catch (err) {
178
+ // couldnt parse the URL, so firstParty will just default to false
179
+ }
180
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/Resource/Seen']);
181
+ const event = {
182
+ ...entryObject,
183
+ eventType: 'BrowserPerformance',
184
+ timestamp: Math.floor(agentRef.runtime.timeKeeper.correctRelativeTimestamp(entryObject.startTime)),
185
+ entryName: name,
186
+ entryDuration: duration,
187
+ firstParty
188
+ };
189
+ this.addEvent(event);
190
+ } catch (err) {
191
+ this.ee.emit('internal-error', [err, 'GenericEvents-Resource']);
192
+ }
193
+ }, this.featureName, this.ee);
194
+ }
143
195
  this.harvestScheduler = new _harvestScheduler.HarvestScheduler(_features.FEATURE_TO_ENDPOINT[this.featureName], {
144
196
  onFinished: result => this.postHarvestCleanup(result.sent && result.retry),
145
197
  onUnload: () => addUserAction?.(this.userActionAggregator.aggregationEvent)
@@ -188,8 +240,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
188
240
  /** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */
189
241
  ...obj
190
242
  };
191
- this.events.add(eventAttributes);
192
- this.checkEventLimits();
243
+ const addedEvent = this.events.add(eventAttributes);
244
+ if (!addedEvent && !this.events.isEmpty()) {
245
+ /** could not add the event because it pushed the buffer over the limit
246
+ * so we harvest early, and try to add it again now that the buffer is cleared
247
+ * if it fails again, we do nothing
248
+ */
249
+ this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
250
+ this.harvestScheduler.runHarvest();
251
+ this.events.add(eventAttributes);
252
+ }
193
253
  }
194
254
  serializer(eventBuffer) {
195
255
  return (0, _traverse.applyFnToProps)({
@@ -202,12 +262,15 @@ class Aggregate extends _aggregateBase.AggregateBase {
202
262
  at: this.agentRef.info.atts
203
263
  };
204
264
  }
205
- checkEventLimits() {
206
- // check if we've reached any harvest limits...
207
- if (this.events.byteSize() > _agentConstants.IDEAL_PAYLOAD_SIZE) {
208
- this.ee.emit(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
209
- this.harvestScheduler.runHarvest();
210
- }
265
+ trackSupportabilityMetrics() {
266
+ /** track usage SMs to improve these experimental features */
267
+ const configPerfTag = 'Config/Performance/';
268
+ if (this.agentRef.init.performance.capture_marks) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMarks/Enabled']);
269
+ if (this.agentRef.init.performance.capture_measures) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMeasures/Enabled']);
270
+ if (this.agentRef.init.performance.resources.enabled) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/Enabled']);
271
+ if (this.agentRef.init.performance.resources.asset_types?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/AssetTypes/Changed']);
272
+ if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed']);
273
+ if (this.agentRef.init.performance.resources.ignore_newrelic === false) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed']);
211
274
  }
212
275
  }
213
276
  exports.Aggregate = Aggregate;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.FEATURE_NAME = void 0;
6
+ exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.FEATURE_NAME = exports.FEATURE_FLAGS = void 0;
7
7
  var _features = require("../../loaders/features/features");
8
8
  const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.genericEvents;
9
9
  const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = 64000;
@@ -11,4 +11,9 @@ const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
11
11
  const OBSERVED_EVENTS = exports.OBSERVED_EVENTS = ['auxclick', 'click', 'copy', 'keydown', 'paste', 'scrollend'];
12
12
  const OBSERVED_WINDOW_EVENTS = exports.OBSERVED_WINDOW_EVENTS = ['focus', 'blur'];
13
13
  const RAGE_CLICK_THRESHOLD_EVENTS = exports.RAGE_CLICK_THRESHOLD_EVENTS = 4;
14
- const RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_MS = 1000;
14
+ const RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_MS = 1000;
15
+ const FEATURE_FLAGS = exports.FEATURE_FLAGS = {
16
+ MARKS: 'experimental.marks',
17
+ MEASURES: 'experimental.measures',
18
+ RESOURCES: 'experimental.resources'
19
+ };
@@ -17,14 +17,26 @@ class Instrument extends _instrumentBase.InstrumentBase {
17
17
  static featureName = _constants.FEATURE_NAME;
18
18
  constructor(agentRef, auto = true) {
19
19
  super(agentRef, _constants.FEATURE_NAME, auto);
20
- const genericEventSourceConfigs = [agentRef.init.page_action.enabled, agentRef.init.performance.capture_marks, agentRef.init.performance.capture_measures, agentRef.init.user_actions.enabled
21
- // other future generic event source configs to go here, like M&Ms, PageResouce, etc.
22
- ];
23
- if (_runtime.isBrowserScope && agentRef.init.user_actions.enabled) {
24
- _constants.OBSERVED_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee), true));
25
- _constants.OBSERVED_WINDOW_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee))
26
- // 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.
27
- );
20
+ /** config values that gate whether the generic events aggregator should be imported at all */
21
+ 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];
22
+ if (_runtime.isBrowserScope) {
23
+ if (agentRef.init.user_actions.enabled) {
24
+ _constants.OBSERVED_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee), true));
25
+ _constants.OBSERVED_WINDOW_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee))
26
+ // 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.
27
+ );
28
+ }
29
+ if (agentRef.init.performance.resources.enabled && _runtime.globalScope.PerformanceObserver?.supportedEntryTypes.includes('resource')) {
30
+ const observer = new PerformanceObserver(list => {
31
+ list.getEntries().forEach(entry => {
32
+ (0, _handle.handle)('browserPerformance.resource', [entry], undefined, this.featureName, this.ee);
33
+ });
34
+ });
35
+ observer.observe({
36
+ type: 'resource',
37
+ buffered: true
38
+ });
39
+ }
28
40
  }
29
41
 
30
42
  /** If any of the sources are active, import the aggregator. otherwise deregister */
@@ -112,9 +112,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
112
112
  * @param {boolean=} internal if the error was "caught" and deemed "internal" before reporting to the jserrors feature
113
113
  * @param {object=} customAttributes any custom attributes to be included in the error payload
114
114
  * @param {boolean=} hasReplay a flag indicating if the error occurred during a replay session
115
+ * @param {string=} swallowReason a string indicating pre-defined reason if swallowing the error. Mainly used by the internal error SMs.
115
116
  * @returns
116
117
  */
117
- storeError(err, time, internal, customAttributes, hasReplay) {
118
+ storeError(err, time, internal, customAttributes, hasReplay, swallowReason) {
118
119
  if (!err) return;
119
120
  // are we in an interaction
120
121
  time = time || (0, _now.now)();
@@ -132,7 +133,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
132
133
  const {
133
134
  shouldSwallow,
134
135
  reason
135
- } = (0, _internalErrors.evaluateInternalError)(stackInfo, internal);
136
+ } = (0, _internalErrors.evaluateInternalError)(stackInfo, internal, swallowReason);
136
137
  if (shouldSwallow) {
137
138
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Internal/Error/' + reason], undefined, _features.FEATURE_NAMES.metrics, this.ee);
138
139
  return;
@@ -11,10 +11,10 @@ const REASON_SECURITY_POLICY = 'Security-Policy';
11
11
  * @param {Object} stackInfo - The error stack information.
12
12
  * @returns {boolean} - Whether the error should be swallowed or not.
13
13
  */
14
- function evaluateInternalError(stackInfo, internal) {
14
+ function evaluateInternalError(stackInfo, internal, reason) {
15
15
  const output = {
16
16
  shouldSwallow: internal || false,
17
- reason: 'Other'
17
+ reason: reason || 'Other'
18
18
  };
19
19
  const leadingFrame = stackInfo.frames?.[0];
20
20
  /** If we cant otherwise determine from the frames and message, the default of internal + reason will be the fallback */
@@ -26,9 +26,9 @@ class Instrument extends _instrumentBase.InstrumentBase {
26
26
  // this try-catch can be removed when IE11 is completely unsupported & gone
27
27
  this.removeOnAbort = new AbortController();
28
28
  } catch (e) {}
29
- this.ee.on('internal-error', error => {
29
+ this.ee.on('internal-error', (error, reason) => {
30
30
  if (!this.abortHandler) return;
31
- (0, _handle.handle)('ierr', [(0, _castError.castError)(error), (0, _now.now)(), true, {}, this.#replayRunning], undefined, this.featureName, this.ee);
31
+ (0, _handle.handle)('ierr', [(0, _castError.castError)(error), (0, _now.now)(), true, {}, this.#replayRunning, reason], undefined, this.featureName, this.ee);
32
32
  });
33
33
  this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
34
34
  this.#replayRunning = isRunning;
@@ -154,41 +154,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
154
154
  });
155
155
  }
156
156
  unload() {
157
- try {
158
- if (this.resourcesSent) return;
159
- this.resourcesSent = true; // make sure this only gets sent once
160
-
161
- // Capture SMs around network resources using the performance API to assess
162
- // work to split this out from the ST nodes
163
- // differentiate between internal+external and ajax+non-ajax
164
- const ajaxResources = ['beacon', 'fetch', 'xmlhttprequest'];
165
- const internalUrls = ['nr-data.net', 'newrelic.com', 'nr-local.net', 'localhost'];
166
- function isInternal(x) {
167
- return internalUrls.some(y => x.name.indexOf(y) >= 0);
168
- }
169
- function isAjax(x) {
170
- return ajaxResources.includes(x.initiatorType);
171
- }
172
- const allResources = performance?.getEntriesByType('resource') || [];
173
- allResources.forEach(entry => {
174
- if (isInternal(entry)) {
175
- if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/Internal');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/Internal');
176
- } else {
177
- if (isAjax(entry)) this.storeSupportabilityMetrics('Generic/Resources/Ajax/External');else this.storeSupportabilityMetrics('Generic/Resources/Non-Ajax/External');
178
- }
179
- });
180
-
181
- // Capture SMs for performance markers and measures to assess the usage and possible inclusion of this
182
- // data in the agent for use in NR
183
- if (typeof performance !== 'undefined') {
184
- const markers = performance.getEntriesByType('mark');
185
- const measures = performance.getEntriesByType('measure');
186
- if (markers.length) this.storeSupportabilityMetrics('Generic/Performance/Mark/Seen', markers.length);
187
- if (measures.length) this.storeSupportabilityMetrics('Generic/Performance/Measure/Seen', measures.length);
188
- }
189
- } catch (e) {
190
- // do nothing
191
- }
157
+ // do nothing for now, marks and measures and resources stats are now being captured by the ge feature
192
158
  }
193
159
  }
194
160
  exports.Aggregate = Aggregate;