@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
@@ -25,6 +25,7 @@ const model = () => {
25
25
  // *cli - per discussion, default should be true
26
26
  ajax: {
27
27
  deny_list: undefined,
28
+ block_internal: true,
28
29
  enabled: true,
29
30
  harvestTimeSeconds: 10
30
31
  },
@@ -30,7 +30,8 @@ const model = {
30
30
  releaseIds: {},
31
31
  session: undefined,
32
32
  xhrWrappable: typeof _runtime.globalScope.XMLHttpRequest?.prototype?.addEventListener === 'function',
33
- version: _env.VERSION
33
+ version: _env.VERSION,
34
+ denyList: undefined
34
35
  };
35
36
  const _cache = {};
36
37
  function getRuntime(id) {
@@ -12,7 +12,7 @@ exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = "1.236.0";
15
+ const VERSION = "1.237.1";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -12,7 +12,7 @@ exports.VERSION = exports.DIST_METHOD = exports.BUILD_ENV = void 0;
12
12
  /**
13
13
  * Exposes the version of the agent
14
14
  */
15
- const VERSION = "1.236.0";
15
+ const VERSION = "1.237.1";
16
16
 
17
17
  /**
18
18
  * Exposes the build type of the agent
@@ -47,24 +47,28 @@ function setDenyList(denyListConfig) {
47
47
  return;
48
48
  }
49
49
  for (var i = 0; i < denyListConfig.length; i++) {
50
- var url = denyListConfig[i];
50
+ let url = denyListConfig[i];
51
+ if (!url) continue; // ignore bad values like undefined or empty strings
52
+
51
53
  if (url.indexOf('http://') === 0) {
52
54
  url = url.substring(7);
53
55
  } else if (url.indexOf('https://') === 0) {
54
56
  url = url.substring(8);
55
57
  }
56
- var firstSlash = url.indexOf('/');
58
+ const firstSlash = url.indexOf('/');
59
+ let host, pathname;
57
60
  if (firstSlash > 0) {
58
- denyList.push({
59
- hostname: url.substring(0, firstSlash),
60
- pathname: url.substring(firstSlash)
61
- });
61
+ host = url.substring(0, firstSlash);
62
+ pathname = url.substring(firstSlash);
62
63
  } else {
63
- denyList.push({
64
- hostname: url,
65
- pathname: ''
66
- });
64
+ host = url;
65
+ pathname = '';
67
66
  }
67
+ let [hostname, port] = host.split(':');
68
+ denyList.push({
69
+ hostname,
70
+ pathname
71
+ });
68
72
  }
69
73
  }
70
74
  /**
@@ -17,6 +17,7 @@ var _traverse = require("../util/traverse");
17
17
  var _sharedContext = require("../context/shared-context");
18
18
  var _env = require("../constants/env.npm");
19
19
  var _runtime = require("../constants/runtime");
20
+ var _console = require("../util/console");
20
21
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
21
22
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
22
23
  /*
@@ -24,6 +25,8 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
24
25
  * SPDX-License-Identifier: Apache-2.0
25
26
  */
26
27
 
28
+ const warnings = {};
29
+
27
30
  /**
28
31
  * @typedef {import('./types.js').NetworkSendSpec} NetworkSendSpec
29
32
  * @typedef {import('./types.js').HarvestEndpointIdentifier} HarvestEndpointIdentifier
@@ -31,7 +34,6 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
31
34
  * @typedef {import('./types.js').FeatureHarvestCallback} FeatureHarvestCallback
32
35
  * @typedef {import('./types.js').FeatureHarvestCallbackOptions} FeatureHarvestCallbackOptions
33
36
  */
34
-
35
37
  class Harvest extends _sharedContext.SharedContext {
36
38
  constructor(parent) {
37
39
  super(parent); // gets any allowed properties from the parent and stores them in `sharedContext`
@@ -142,13 +144,15 @@ class Harvest extends _sharedContext.SharedContext {
142
144
  payloadParams = payloadParams.substring(1);
143
145
  }
144
146
  const fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
145
- const gzip = qs.content_encoding === 'gzip';
147
+ const gzip = !!qs?.attributes?.includes('gzip');
146
148
  if (!gzip) {
147
149
  if (endpoint === 'events') {
148
150
  body = body.e;
149
151
  } else {
150
152
  body = (0, _stringify.stringify)(body);
151
153
  }
154
+ /** Warn --once per endpoint-- if the agent tries to send large payloads */
155
+ if (body.length > 750000 && (warnings[endpoint] = (warnings?.[endpoint] || 0) + 1) === 1) (0, _console.warn)("The Browser Agent is attempting to send a very large payload to /".concat(endpoint, ". This is usually tied to large amounts of custom attributes. Please check your configurations."));
152
156
  }
153
157
  if (!body || body.length === 0 || body === '{}' || body === '[]') {
154
158
  // If body is null, undefined, or an empty object or array, send an empty string instead
@@ -195,23 +199,6 @@ class Harvest extends _sharedContext.SharedContext {
195
199
  cbFinished(cbResult);
196
200
  }, (0, _eventListenerOpts.eventListenerOpts)(false));
197
201
  }
198
-
199
- // if beacon request failed, retry with an alternative method -- will not happen for workers
200
- if (!result && submitMethod === submitData.beacon) {
201
- // browsers that support sendBeacon also support fetch with keepalive - IE will not retry unload calls
202
- submitMethod = submitData.fetchKeepAlive;
203
- try {
204
- submitMethod({
205
- url: fullUrl,
206
- body,
207
- headers
208
- });
209
- } catch (e) {
210
- // Ignore error in final harvest
211
- } finally {
212
- result = true;
213
- }
214
- }
215
202
  return result;
216
203
  }
217
204
 
@@ -271,9 +258,8 @@ class Harvest extends _sharedContext.SharedContext {
271
258
  cleanPayload() {
272
259
  let payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
273
260
  const clean = input => {
274
- if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array) {
275
- return input.length > 0 ? input : null;
276
- }
261
+ if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array || Array.isArray(input)) return input;
262
+ if (typeof input === 'string') return input.length > 0 ? input : null;
277
263
  return Object.entries(input || {}).reduce((accumulator, _ref2) => {
278
264
  let [key, value] = _ref2;
279
265
  if (typeof value === 'number' || typeof value === 'string' && value.length > 0 || typeof value === 'object' && Object.keys(value || {}).length > 0) {
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.beacon = beacon;
7
- exports.fetchKeepAlive = fetchKeepAlive;
8
7
  exports.getSubmitMethod = getSubmitMethod;
9
8
  exports.xhr = xhr;
10
9
  var _runtime = require("../constants/runtime");
@@ -15,7 +14,7 @@ var _runtime = require("../constants/runtime");
15
14
  */
16
15
 
17
16
  /**
18
- * @typedef {xhr|fetchKeepAlive|beacon} NetworkMethods
17
+ * @typedef {xhr|beacon} NetworkMethods
19
18
  */
20
19
 
21
20
  /**
@@ -72,36 +71,6 @@ function xhr(_ref) {
72
71
  return request;
73
72
  }
74
73
 
75
- /**
76
- * Send via fetch with keepalive true
77
- * @param {Object} args - The args.
78
- * @param {string} args.url - The URL to send to.
79
- * @param {string=} args.body - The Stringified body.
80
- * @param {string=} [args.method=POST] - The XHR method to use.
81
- * @param {{key: string, value: string}[]} [args.headers] - The headers to attach.
82
- * @returns {XMLHttpRequest}
83
- */
84
- function fetchKeepAlive(_ref2) {
85
- let {
86
- url,
87
- body = null,
88
- method = 'POST',
89
- headers = [{
90
- key: 'content-type',
91
- value: 'text/plain'
92
- }]
93
- } = _ref2;
94
- return fetch(url, {
95
- method,
96
- headers: headers.reduce((aggregator, header) => {
97
- aggregator.push([header.key, header.value]);
98
- return aggregator;
99
- }, []),
100
- body,
101
- keepalive: true
102
- });
103
- }
104
-
105
74
  /**
106
75
  * Send via sendBeacon. Do NOT call this function outside of a guaranteed web window environment.
107
76
  * @param {Object} args - The args
@@ -109,11 +78,11 @@ function fetchKeepAlive(_ref2) {
109
78
  * @param {string=} args.body - The Stringified body
110
79
  * @returns {boolean}
111
80
  */
112
- function beacon(_ref3) {
81
+ function beacon(_ref2) {
113
82
  let {
114
83
  url,
115
84
  body
116
- } = _ref3;
85
+ } = _ref2;
117
86
  try {
118
87
  // Navigator has to be bound to ensure it does not error in some browsers
119
88
  // https://xgwang.me/posts/you-may-not-know-beacon/#it-may-throw-error%2C-be-sure-to-catch
@@ -121,8 +90,7 @@ function beacon(_ref3) {
121
90
  return send(url, body);
122
91
  } catch (err) {
123
92
  // if sendBeacon still trys to throw an illegal invocation error,
124
- // we can catch here and return. The harvest module will fallback to use
125
- // fetchKeepAlive to try to send
93
+ // we can catch here and return
126
94
  return false;
127
95
  }
128
96
  }
@@ -93,13 +93,19 @@ function wrapJsonP(sharedEE) {
93
93
  var matches = src.match(CALLBACK_REGEX);
94
94
  return matches ? matches[1] : null;
95
95
  }
96
+
97
+ /**
98
+ * Traverse a nested object using a '.'-delimited string wherein each substring piece maps to each subsequent object property layer.
99
+ * @param {string} longKey
100
+ * @param {object} obj
101
+ * @returns The final nested object referred to by initial longKey.
102
+ */
96
103
  function discoverValue(longKey, obj) {
97
- var matches = longKey.match(VALUE_REGEX);
98
- var key = matches[1];
99
- var remaining = matches[3];
100
- if (!remaining) {
101
- return obj[key];
102
- }
104
+ if (!longKey) return obj; // end of object recursion depth when no more key levels
105
+ const matches = longKey.match(VALUE_REGEX);
106
+ // if 'longKey' was not undefined, that is it at least had 1 level left, then the regexp would've at least matched 1st group
107
+ const key = matches[1];
108
+ const remaining = matches[3];
103
109
  return discoverValue(remaining, obj[key]);
104
110
  }
105
111
  function discoverParent(key) {
@@ -25,13 +25,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
25
25
  static featureName = _constants.FEATURE_NAME;
26
26
  constructor(agentIdentifier, aggregator) {
27
27
  super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
28
+ const agentInit = (0, _config.getConfiguration)(agentIdentifier);
29
+ const allAjaxIsEnabled = agentInit.ajax.enabled !== false;
30
+ (0, _registerHandler.registerHandler)('xhr', storeXhr, this.featureName, this.ee);
31
+ if (!allAjaxIsEnabled) {
32
+ (0, _drain.drain)(this.agentIdentifier, this.featureName);
33
+ return; // feature will only collect timeslice metrics & ajax trace nodes if it's not fully enabled
34
+ }
35
+
36
+ const denyList = (0, _config.getRuntime)(agentIdentifier).denyList;
37
+ (0, _denyList.setDenyList)(denyList);
28
38
  let ajaxEvents = [];
29
39
  let spaAjaxEvents = {};
30
40
  let sentAjaxEvents = [];
31
- let scheduler;
32
41
  const ee = this.ee;
33
- const harvestTimeSeconds = (0, _config.getConfigurationValue)(agentIdentifier, 'ajax.harvestTimeSeconds') || 10;
34
- const MAX_PAYLOAD_SIZE = (0, _config.getConfigurationValue)(agentIdentifier, 'ajax.maxPayloadSize') || 1000000;
42
+ const harvestTimeSeconds = agentInit.ajax.harvestTimeSeconds || 10;
43
+ const MAX_PAYLOAD_SIZE = agentInit.ajax.maxPayloadSize || 1000000;
35
44
 
36
45
  // Exposes these methods to browser test files -- future TO DO: can be removed once these fns are extracted from the constructor into class func
37
46
  this.storeXhr = storeXhr;
@@ -48,24 +57,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
48
57
  delete spaAjaxEvents[interaction.id];
49
58
  });
50
59
  ee.on('interactionDiscarded', interaction => {
51
- if (!spaAjaxEvents[interaction.id] || !allAjaxIsEnabled()) return;
60
+ if (!spaAjaxEvents[interaction.id]) return;
52
61
  spaAjaxEvents[interaction.id].forEach(function (item) {
53
62
  // move it from the spaAjaxEvents buffer to the ajaxEvents buffer for harvesting here
54
63
  ajaxEvents.push(item);
55
64
  });
56
65
  delete spaAjaxEvents[interaction.id];
57
66
  });
58
- if (allAjaxIsEnabled()) (0, _denyList.setDenyList)((0, _config.getConfigurationValue)(agentIdentifier, 'ajax.deny_list'));
59
- (0, _registerHandler.registerHandler)('xhr', storeXhr, this.featureName, this.ee);
60
- if (allAjaxIsEnabled()) {
61
- scheduler = new _harvestScheduler.HarvestScheduler('events', {
62
- onFinished: onEventsHarvestFinished,
63
- getPayload: prepareHarvest
64
- }, this);
65
- ee.on("drain-".concat(this.featureName), () => {
66
- scheduler.startTimer(harvestTimeSeconds);
67
- });
68
- }
67
+ const scheduler = new _harvestScheduler.HarvestScheduler('events', {
68
+ onFinished: onEventsHarvestFinished,
69
+ getPayload: prepareHarvest
70
+ }, this);
71
+ ee.on("drain-".concat(this.featureName), () => {
72
+ scheduler.startTimer(harvestTimeSeconds);
73
+ });
74
+ (0, _drain.drain)(this.agentIdentifier, this.featureName);
75
+ return;
69
76
  function storeXhr(params, metrics, startTime, endTime, type) {
70
77
  metrics.time = startTime;
71
78
 
@@ -80,9 +87,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
80
87
 
81
88
  // store as metric
82
89
  aggregator.store('xhr', hash, params, metrics);
83
- if (!allAjaxIsEnabled()) {
84
- return;
85
- }
90
+ if (!allAjaxIsEnabled) return;
86
91
  if (!(0, _denyList.shouldCollectEvent)(params)) {
87
92
  if (params.hostname === (0, _config.getInfo)(agentIdentifier).errorBeacon) {
88
93
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/Agent'], undefined, _features.FEATURE_NAMES.metrics, ee);
@@ -164,7 +169,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
164
169
  return tooBig ? getPayload(events, maxPayloadSize, ++chunks) : payload;
165
170
  }
166
171
  function onEventsHarvestFinished(result) {
167
- if (result.retry && sentAjaxEvents.length > 0 && allAjaxIsEnabled()) {
172
+ if (result.retry && sentAjaxEvents.length > 0) {
168
173
  ajaxEvents.unshift(...sentAjaxEvents);
169
174
  sentAjaxEvents = [];
170
175
  }
@@ -213,14 +218,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
213
218
  return this.payload.length * 2 > maxPayloadSize;
214
219
  };
215
220
  }
216
- function allAjaxIsEnabled() {
217
- var enabled = (0, _config.getConfigurationValue)(agentIdentifier, 'ajax.enabled');
218
- if (enabled === false) {
219
- return false;
220
- }
221
- return true;
222
- }
223
- (0, _drain.drain)(this.agentIdentifier, this.featureName);
224
221
  }
225
222
  }
226
223
  exports.Aggregate = Aggregate;
@@ -229,7 +229,7 @@ function computeStackTraceBySourceAndLine(ex) {
229
229
  mode: 'sourceline',
230
230
  name: className,
231
231
  message: ex.message,
232
- stackString: getClassName(ex) + ': ' + ex.message + '\n in evaluated code',
232
+ stackString: className + ': ' + ex.message + '\n in evaluated code',
233
233
  frames: [{
234
234
  func: 'evaluated code'
235
235
  }]
@@ -3,9 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.NR_ERR_PROP = exports.FEATURE_NAME = void 0;
6
+ exports.FEATURE_NAME = void 0;
7
7
  var _features = require("../../loaders/features/features");
8
8
  const FEATURE_NAME = _features.FEATURE_NAMES.jserrors;
9
- exports.FEATURE_NAME = FEATURE_NAME;
10
- const NR_ERR_PROP = 'nr@seenError';
11
- exports.NR_ERR_PROP = NR_ERR_PROP;
9
+ exports.FEATURE_NAME = FEATURE_NAME;
@@ -6,16 +6,13 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.Instrument = void 0;
7
7
  var _handle = require("../../../common/event-emitter/handle");
8
8
  var _now = require("../../../common/timing/now");
9
- var _getOrSet = require("../../../common/util/get-or-set");
10
- var _wrap = require("../../../common/wrap");
11
- require("./debug");
12
9
  var _instrumentBase = require("../../utils/instrument-base");
13
10
  var _constants = require("../constants");
14
11
  var _features = require("../../../loaders/features/features");
15
12
  var _runtime = require("../../../common/constants/runtime");
16
13
  var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
17
- var _config = require("../../../common/config/config");
18
14
  var _stringify = require("../../../common/util/stringify");
15
+ var _uncaughtError = require("./uncaught-error");
19
16
  /*
20
17
  * Copyright 2020 New Relic Corporation. All rights reserved.
21
18
  * SPDX-License-Identifier: Apache-2.0
@@ -23,51 +20,44 @@ var _stringify = require("../../../common/util/stringify");
23
20
 
24
21
  class Instrument extends _instrumentBase.InstrumentBase {
25
22
  static featureName = _constants.FEATURE_NAME;
23
+ #seenErrors = new Set();
26
24
  constructor(agentIdentifier, aggregator) {
27
25
  let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
28
26
  super(agentIdentifier, aggregator, _constants.FEATURE_NAME, auto);
29
- // skipNext counter to keep track of uncaught
30
- // errors that will be the same as caught errors.
31
- this.skipNext = 0;
32
27
  try {
33
28
  // this try-catch can be removed when IE11 is completely unsupported & gone
34
29
  this.removeOnAbort = new AbortController();
35
30
  } catch (e) {}
36
- const thisInstrument = this;
37
- thisInstrument.ee.on('fn-start', function (args, obj, methodName) {
38
- if (thisInstrument.abortHandler) thisInstrument.skipNext += 1;
39
- });
40
- thisInstrument.ee.on('fn-err', function (args, obj, err) {
41
- if (thisInstrument.abortHandler && !err[_constants.NR_ERR_PROP]) {
42
- (0, _getOrSet.getOrSet)(err, _constants.NR_ERR_PROP, function getVal() {
43
- return true;
44
- });
45
- this.thrown = true;
46
- (0, _handle.handle)('err', [err, (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, thisInstrument.ee);
47
- }
48
- });
49
- thisInstrument.ee.on('fn-end', function () {
50
- if (!thisInstrument.abortHandler) return;
51
- if (!this.thrown && thisInstrument.skipNext > 0) thisInstrument.skipNext -= 1;
31
+
32
+ // Capture function errors early in case the spa feature is loaded
33
+ this.ee.on('fn-err', (args, obj, error) => {
34
+ if (!this.abortHandler || this.#seenErrors.has(error)) return;
35
+ this.#seenErrors.add(error);
36
+ (0, _handle.handle)('err', [this.#castError(error), (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
52
37
  });
53
- thisInstrument.ee.on('internal-error', function (e) {
54
- (0, _handle.handle)('ierr', [e, (0, _now.now)(), true], undefined, _features.FEATURE_NAMES.jserrors, thisInstrument.ee);
38
+ this.ee.on('internal-error', error => {
39
+ if (!this.abortHandler) return;
40
+ (0, _handle.handle)('ierr', [this.#castError(error), (0, _now.now)(), true], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
55
41
  });
56
-
57
- // Replace global error handler with our own.
58
- this.origOnerror = _runtime.globalScope.onerror;
59
- _runtime.globalScope.onerror = this.onerrorHandler.bind(this);
60
- _runtime.globalScope.addEventListener('unhandledrejection', e => {
61
- /** rejections can contain data of any type -- this is an effort to keep the message human readable */
62
- const err = castReasonToError(e.reason);
63
- (0, _handle.handle)('err', [err, (0, _now.now)(), false, {
42
+ _runtime.globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
43
+ if (!this.abortHandler) return;
44
+ (0, _handle.handle)('err', [this.#castPromiseRejectionEvent(promiseRejectionEvent), (0, _now.now)(), false, {
64
45
  unhandledPromiseRejection: 1
65
46
  }], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
66
47
  }, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
67
- (0, _wrap.wrapRaf)(this.ee);
68
- (0, _wrap.wrapTimer)(this.ee);
69
- (0, _wrap.wrapEvents)(this.ee);
70
- if ((0, _config.getRuntime)(agentIdentifier).xhrWrappable) (0, _wrap.wrapXhr)(this.ee);
48
+ _runtime.globalScope.addEventListener('error', errorEvent => {
49
+ if (!this.abortHandler) return;
50
+
51
+ /**
52
+ * If the spa feature is loaded, errors may already have been captured in the `fn-err` listener above.
53
+ * This ensures those errors are not captured twice.
54
+ */
55
+ if (this.#seenErrors.has(errorEvent.error)) {
56
+ this.#seenErrors.delete(errorEvent.error);
57
+ return;
58
+ }
59
+ (0, _handle.handle)('err', [this.#castErrorEvent(errorEvent), (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
60
+ }, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
71
61
  this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
72
62
  this.importAggregator();
73
63
  }
@@ -75,66 +65,67 @@ class Instrument extends _instrumentBase.InstrumentBase {
75
65
  /** Restoration and resource release tasks to be done if JS error loader is being aborted. Unwind changes to globals. */
76
66
  #abort() {
77
67
  this.removeOnAbort?.abort();
68
+ this.#seenErrors.clear();
78
69
  this.abortHandler = undefined; // weakly allow this abort op to run only once
79
70
  }
80
71
 
81
72
  /**
82
- * FF and Android browsers do not provide error info to the 'error' event callback,
83
- * so we must use window.onerror
84
- * @param {string} message
85
- * @param {string} filename
86
- * @param {number} lineno
87
- * @param {number} column
88
- * @param {Error | *} errorObj
89
- * @returns
73
+ * Any value can be used with the `throw` keyword. This function ensures that the value is
74
+ * either a proper Error instance or attempts to convert it to an UncaughtError instance.
75
+ * @param {any} error The value thrown
76
+ * @returns {Error|UncaughtError} The converted error instance
90
77
  */
91
- onerrorHandler(message, filename, lineno, column, errorObj) {
92
- if (typeof this.origOnerror === 'function') this.origOnerror(...arguments);
93
- try {
94
- if (this.skipNext) this.skipNext -= 1;else (0, _handle.handle)('err', [errorObj || new UncaughtException(message, filename, lineno), (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
95
- } catch (e) {
78
+ #castError(error) {
79
+ if (error instanceof Error) {
80
+ return error;
81
+ }
82
+
83
+ /**
84
+ * The thrown value may contain a message property. If it does, try to treat the thrown
85
+ * value as an Error-like object.
86
+ */
87
+ if (typeof error?.message !== 'undefined') {
88
+ return new _uncaughtError.UncaughtError(error.message, error.filename || error.sourceURL, error.lineno || error.line, error.colno || error.col);
89
+ }
90
+ return new _uncaughtError.UncaughtError(typeof error === 'string' ? error : (0, _stringify.stringify)(error));
91
+ }
92
+
93
+ /**
94
+ * Attempts to convert a PromiseRejectionEvent object to an Error object
95
+ * @param {PromiseRejectionEvent} unhandledRejectionEvent The unhandled promise rejection event
96
+ * @returns {Error} An Error object with the message as the casted reason
97
+ */
98
+ #castPromiseRejectionEvent(promiseRejectionEvent) {
99
+ let prefix = 'Unhandled Promise Rejection: ';
100
+ if (promiseRejectionEvent?.reason instanceof Error) {
96
101
  try {
97
- (0, _handle.handle)('ierr', [e, (0, _now.now)(), true], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
98
- } catch (err) {
99
- // do nothing
102
+ promiseRejectionEvent.reason.message = prefix + promiseRejectionEvent.reason.message;
103
+ return promiseRejectionEvent.reason;
104
+ } catch (e) {
105
+ return promiseRejectionEvent.reason;
100
106
  }
101
107
  }
102
- return false; // maintain default behavior of the error event of Window
108
+ if (typeof promiseRejectionEvent.reason === 'undefined') return new _uncaughtError.UncaughtError(prefix);
109
+ const error = this.#castError(promiseRejectionEvent.reason);
110
+ error.message = prefix + error.message;
111
+ return error;
103
112
  }
104
- }
105
113
 
106
- /**
107
- *
108
- * @param {string} message
109
- * @param {string} filename
110
- * @param {number} lineno
111
- */
112
- exports.Instrument = Instrument;
113
- function UncaughtException(message, filename, lineno) {
114
- this.message = message || 'Uncaught error with no additional information';
115
- this.sourceURL = filename;
116
- this.line = lineno;
117
- }
118
-
119
- /**
120
- * Attempts to cast an unhandledPromiseRejection reason (reject(...)) to an Error object
121
- * @param {*} reason - The reason property from an unhandled promise rejection
122
- * @returns {Error} - An Error object with the message as the casted reason
123
- */
124
- function castReasonToError(reason) {
125
- let prefix = 'Unhandled Promise Rejection: ';
126
- if (reason instanceof Error) {
127
- try {
128
- reason.message = prefix + reason.message;
129
- return reason;
130
- } catch (e) {
131
- return reason;
114
+ /**
115
+ * Attempts to convert an ErrorEvent object to an Error object
116
+ * @param {ErrorEvent} errorEvent The error event
117
+ * @returns {Error|UncaughtError} The error event converted to an Error object
118
+ */
119
+ #castErrorEvent(errorEvent) {
120
+ if (errorEvent.error instanceof Error) {
121
+ return errorEvent.error;
132
122
  }
123
+
124
+ /**
125
+ * Older browsers do not contain the `error` property on the ErrorEvent instance.
126
+ * https://caniuse.com/mdn-api_errorevent_error
127
+ */
128
+ return new _uncaughtError.UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno);
133
129
  }
134
- if (typeof reason === 'undefined') return new Error(prefix);
135
- try {
136
- return new Error(prefix + (0, _stringify.stringify)(reason));
137
- } catch (err) {
138
- return new Error(prefix);
139
- }
140
- }
130
+ }
131
+ exports.Instrument = Instrument;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.UncaughtError = void 0;
7
+ /**
8
+ * Represents an uncaught non Error type error. This class does
9
+ * not extend the Error class to prevent an invalid stack trace
10
+ * from being created. Use this class to cast thrown errors that
11
+ * do not use the Error class (strings, etc) to an object.
12
+ */
13
+ class UncaughtError {
14
+ constructor(message, filename, lineno, colno) {
15
+ this.name = 'UncaughtError';
16
+ this.message = message;
17
+ this.sourceURL = filename;
18
+ this.line = lineno;
19
+ this.column = colno;
20
+ }
21
+ }
22
+ exports.UncaughtError = UncaughtError;
@@ -154,6 +154,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
154
154
  // Capture metrics for size of custom attributes
155
155
  const jsAttributes = (0, _stringify.stringify)(info.jsAttributes);
156
156
  this.storeSupportabilityMetrics('PageSession/Feature/CustomData/Bytes', jsAttributes === '{}' ? 0 : jsAttributes.length);
157
+
158
+ // Capture metrics for performance markers and measures
159
+ if (typeof performance !== 'undefined') {
160
+ const markers = performance.getEntriesByType('mark');
161
+ const measures = performance.getEntriesByType('measure');
162
+ this.storeSupportabilityMetrics('Generic/Performance/Mark/Seen', markers.length);
163
+ this.storeSupportabilityMetrics('Generic/Performance/Measure/Seen', measures.length);
164
+ }
157
165
  } catch (e) {
158
166
  // do nothing
159
167
  }