@newrelic/browser-agent 1.316.0 → 1.317.0-rc.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 (170) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/common/config/configurable.js +1 -1
  3. package/dist/cjs/common/config/init-types.js +1 -0
  4. package/dist/cjs/common/config/init.js +6 -4
  5. package/dist/cjs/common/constants/env.cdn.js +2 -2
  6. package/dist/cjs/common/constants/env.npm.js +2 -2
  7. package/dist/cjs/common/constants/events.js +26 -0
  8. package/dist/cjs/common/harvest/harvester.js +9 -217
  9. package/dist/cjs/common/harvest/send.js +232 -0
  10. package/dist/cjs/common/harvest/types.js +9 -5
  11. package/dist/cjs/common/payloads/payloads.js +127 -0
  12. package/dist/cjs/common/serialize/bel-serializer.js +4 -3
  13. package/dist/cjs/common/session/session-entity.js +8 -8
  14. package/dist/cjs/common/util/data-size.js +2 -2
  15. package/dist/cjs/common/util/obfuscate.js +147 -3
  16. package/dist/cjs/common/v2/mfe-vitals.js +286 -0
  17. package/dist/cjs/common/wrap/wrap-websocket.js +2 -1
  18. package/dist/cjs/common/wrap/wrap-xhr.js +1 -1
  19. package/dist/cjs/features/ajax/aggregate/gql.js +40 -1
  20. package/dist/cjs/features/ajax/aggregate/index.js +51 -13
  21. package/dist/cjs/features/ajax/constants.js +6 -1
  22. package/dist/cjs/features/ajax/instrument/index.js +105 -17
  23. package/dist/cjs/features/generic_events/aggregate/index.js +15 -10
  24. package/dist/cjs/features/generic_events/constants.js +3 -2
  25. package/dist/cjs/features/jserrors/aggregate/format-stack-trace.js +2 -2
  26. package/dist/cjs/features/jserrors/aggregate/index.js +12 -3
  27. package/dist/cjs/features/logging/aggregate/index.js +12 -8
  28. package/dist/cjs/features/metrics/aggregate/index.js +5 -1
  29. package/dist/cjs/features/page_view_event/aggregate/index.js +72 -33
  30. package/dist/cjs/features/page_view_timing/aggregate/index.js +6 -1
  31. package/dist/cjs/features/session_replay/aggregate/index.js +39 -29
  32. package/dist/cjs/features/session_trace/aggregate/index.js +35 -29
  33. package/dist/cjs/features/session_trace/constants.js +17 -3
  34. package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +36 -5
  35. package/dist/cjs/features/soft_navigations/aggregate/index.js +7 -1
  36. package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
  37. package/dist/cjs/features/soft_navigations/aggregate/interaction.js +6 -4
  38. package/dist/cjs/features/utils/aggregate-base.js +4 -3
  39. package/dist/cjs/loaders/api/register.js +26 -4
  40. package/dist/esm/common/config/configurable.js +1 -1
  41. package/dist/esm/common/config/init-types.js +1 -0
  42. package/dist/esm/common/config/init.js +3 -1
  43. package/dist/esm/common/constants/env.cdn.js +2 -2
  44. package/dist/esm/common/constants/env.npm.js +2 -2
  45. package/dist/esm/common/constants/events.js +20 -0
  46. package/dist/esm/common/harvest/harvester.js +9 -217
  47. package/dist/esm/common/harvest/send.js +226 -0
  48. package/dist/esm/common/harvest/types.js +9 -5
  49. package/dist/esm/common/payloads/payloads.js +118 -0
  50. package/dist/esm/common/serialize/bel-serializer.js +4 -3
  51. package/dist/esm/common/session/session-entity.js +9 -9
  52. package/dist/esm/common/util/data-size.js +2 -2
  53. package/dist/esm/common/util/obfuscate.js +147 -3
  54. package/dist/esm/common/v2/mfe-vitals.js +281 -0
  55. package/dist/esm/common/wrap/wrap-websocket.js +2 -1
  56. package/dist/esm/common/wrap/wrap-xhr.js +1 -1
  57. package/dist/esm/features/ajax/aggregate/gql.js +39 -1
  58. package/dist/esm/features/ajax/aggregate/index.js +52 -14
  59. package/dist/esm/features/ajax/constants.js +5 -0
  60. package/dist/esm/features/ajax/instrument/index.js +106 -18
  61. package/dist/esm/features/generic_events/aggregate/index.js +15 -10
  62. package/dist/esm/features/generic_events/constants.js +3 -2
  63. package/dist/esm/features/jserrors/aggregate/format-stack-trace.js +2 -2
  64. package/dist/esm/features/jserrors/aggregate/index.js +12 -3
  65. package/dist/esm/features/logging/aggregate/index.js +12 -8
  66. package/dist/esm/features/metrics/aggregate/index.js +5 -1
  67. package/dist/esm/features/page_view_event/aggregate/index.js +71 -32
  68. package/dist/esm/features/page_view_timing/aggregate/index.js +6 -1
  69. package/dist/esm/features/session_replay/aggregate/index.js +39 -29
  70. package/dist/esm/features/session_trace/aggregate/index.js +36 -30
  71. package/dist/esm/features/session_trace/constants.js +16 -2
  72. package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +36 -5
  73. package/dist/esm/features/soft_navigations/aggregate/index.js +7 -1
  74. package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
  75. package/dist/esm/features/soft_navigations/aggregate/interaction.js +6 -4
  76. package/dist/esm/features/utils/aggregate-base.js +4 -3
  77. package/dist/esm/loaders/api/register.js +26 -4
  78. package/dist/tsconfig.tsbuildinfo +1 -1
  79. package/dist/types/common/config/init-types.d.ts +4 -0
  80. package/dist/types/common/config/init-types.d.ts.map +1 -1
  81. package/dist/types/common/config/init.d.ts.map +1 -1
  82. package/dist/types/common/constants/events.d.ts +16 -0
  83. package/dist/types/common/constants/events.d.ts.map +1 -0
  84. package/dist/types/common/harvest/harvester.d.ts +2 -16
  85. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  86. package/dist/types/common/harvest/send.d.ts +9 -0
  87. package/dist/types/common/harvest/send.d.ts.map +1 -0
  88. package/dist/types/common/harvest/types.d.ts +39 -10
  89. package/dist/types/common/harvest/types.d.ts.map +1 -1
  90. package/dist/types/common/payloads/payloads.d.ts +42 -0
  91. package/dist/types/common/payloads/payloads.d.ts.map +1 -0
  92. package/dist/types/common/serialize/bel-serializer.d.ts +1 -1
  93. package/dist/types/common/serialize/bel-serializer.d.ts.map +1 -1
  94. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  95. package/dist/types/common/util/obfuscate.d.ts +23 -2
  96. package/dist/types/common/util/obfuscate.d.ts.map +1 -1
  97. package/dist/types/common/v2/mfe-vitals.d.ts +14 -0
  98. package/dist/types/common/v2/mfe-vitals.d.ts.map +1 -0
  99. package/dist/types/common/wrap/wrap-websocket.d.ts.map +1 -1
  100. package/dist/types/features/ajax/aggregate/gql.d.ts +8 -0
  101. package/dist/types/features/ajax/aggregate/gql.d.ts.map +1 -1
  102. package/dist/types/features/ajax/aggregate/index.d.ts +2 -0
  103. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  104. package/dist/types/features/ajax/constants.d.ts +5 -0
  105. package/dist/types/features/ajax/constants.d.ts.map +1 -1
  106. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  107. package/dist/types/features/generic_events/aggregate/index.d.ts +3 -1
  108. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  109. package/dist/types/features/generic_events/constants.d.ts.map +1 -1
  110. package/dist/types/features/jserrors/aggregate/index.d.ts +8 -1
  111. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  112. package/dist/types/features/logging/aggregate/index.d.ts +3 -1
  113. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  114. package/dist/types/features/metrics/aggregate/index.d.ts +2 -0
  115. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  116. package/dist/types/features/page_view_event/aggregate/index.d.ts +3 -0
  117. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  118. package/dist/types/features/page_view_timing/aggregate/index.d.ts +2 -0
  119. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  120. package/dist/types/features/session_replay/aggregate/index.d.ts +4 -2
  121. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  122. package/dist/types/features/session_trace/aggregate/index.d.ts +3 -1
  123. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  124. package/dist/types/features/session_trace/constants.d.ts +17 -0
  125. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +6 -1
  126. package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -1
  127. package/dist/types/features/soft_navigations/aggregate/index.d.ts +3 -0
  128. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  129. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +3 -1
  130. package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -1
  131. package/dist/types/features/utils/aggregate-base.d.ts +0 -1
  132. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  133. package/dist/types/loaders/api/register.d.ts.map +1 -1
  134. package/package.json +3 -3
  135. package/src/common/config/configurable.js +1 -1
  136. package/src/common/config/init-types.js +1 -0
  137. package/src/common/config/init.js +2 -1
  138. package/src/common/constants/events.js +20 -0
  139. package/src/common/harvest/harvester.js +9 -201
  140. package/src/common/harvest/send.js +209 -0
  141. package/src/common/harvest/types.js +9 -5
  142. package/src/common/payloads/payloads.js +135 -0
  143. package/src/common/serialize/bel-serializer.js +4 -3
  144. package/src/common/session/session-entity.js +8 -9
  145. package/src/common/util/data-size.js +2 -2
  146. package/src/common/util/obfuscate.js +154 -3
  147. package/src/common/v2/mfe-vitals.js +260 -0
  148. package/src/common/wrap/wrap-websocket.js +2 -1
  149. package/src/common/wrap/wrap-xhr.js +1 -1
  150. package/src/features/ajax/aggregate/gql.js +42 -1
  151. package/src/features/ajax/aggregate/index.js +43 -13
  152. package/src/features/ajax/constants.js +5 -1
  153. package/src/features/ajax/instrument/index.js +114 -18
  154. package/src/features/generic_events/aggregate/index.js +14 -9
  155. package/src/features/generic_events/constants.js +3 -2
  156. package/src/features/jserrors/aggregate/format-stack-trace.js +3 -3
  157. package/src/features/jserrors/aggregate/index.js +12 -3
  158. package/src/features/logging/aggregate/index.js +12 -11
  159. package/src/features/metrics/aggregate/index.js +5 -1
  160. package/src/features/page_view_event/aggregate/index.js +58 -29
  161. package/src/features/page_view_timing/aggregate/index.js +6 -1
  162. package/src/features/session_replay/aggregate/index.js +37 -28
  163. package/src/features/session_trace/aggregate/index.js +37 -30
  164. package/src/features/session_trace/constants.js +7 -1
  165. package/src/features/soft_navigations/aggregate/ajax-node.js +26 -5
  166. package/src/features/soft_navigations/aggregate/index.js +7 -1
  167. package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +1 -1
  168. package/src/features/soft_navigations/aggregate/interaction.js +6 -4
  169. package/src/features/utils/aggregate-base.js +3 -3
  170. package/src/loaders/api/register.js +24 -4
@@ -10,6 +10,7 @@ var _now = require("../timing/now");
10
10
  var _cleanUrl = require("../url/clean-url");
11
11
  var _nreum = require("../window/nreum");
12
12
  var _pageVisibility = require("../window/page-visibility");
13
+ var _events = require("../constants/events");
13
14
  /**
14
15
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
15
16
  * SPDX-License-Identifier: Apache-2.0
@@ -40,7 +41,7 @@ function wrapWebSocket(sharedEE) {
40
41
  });
41
42
  });
42
43
  class WrappedWebSocket extends WebSocket {
43
- static name = 'WebSocket';
44
+ static name = _events.EVENT_TYPES.WS;
44
45
  static toString() {
45
46
  // fake native WebSocket when static class is stringified
46
47
  return 'function WebSocket() { [native code] }';
@@ -23,7 +23,7 @@ var _utils = require("../v2/utils");
23
23
  */
24
24
 
25
25
  const wrapped = {};
26
- const XHR_PROPS = ['open', 'send']; // these are the specific funcs being wrapped on all XMLHttpRequests(.prototype)
26
+ const XHR_PROPS = ['open', 'send', 'setRequestHeader']; // these are the specific funcs being wrapped on all XMLHttpRequests(.prototype)
27
27
 
28
28
  /**
29
29
  * Wraps the native XMLHttpRequest (XHR) object to emit custom events to its readystatechange event and an assortment
@@ -3,10 +3,11 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.hasGQLErrors = hasGQLErrors;
6
7
  exports.parseGQL = parseGQL;
7
8
  var _typeCheck = require("../../../common/util/type-check");
8
9
  /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
11
  * SPDX-License-Identifier: Apache-2.0
11
12
  */
12
13
 
@@ -95,4 +96,42 @@ function parseGQLQueryString(gqlQueryString) {
95
96
  }
96
97
  function validateGQLObject(obj) {
97
98
  return !(typeof obj !== 'object' || !obj.query || typeof obj.query !== 'string');
99
+ }
100
+
101
+ /**
102
+ * Checks if a response object has valid GraphQL errors.
103
+ * @param {object} response - A single GraphQL response object
104
+ * @returns {boolean} True if the response has valid errors
105
+ */
106
+ function hasValidGQLErrors(response) {
107
+ return Array.isArray(response?.errors) && response.errors.some(err => err && typeof err === 'object' && typeof err.message === 'string');
108
+ }
109
+
110
+ /**
111
+ * Checks if a response body contains GraphQL errors according to the GraphQL spec.
112
+ * A valid GraphQL error response contains an "errors" array with at least one error object.
113
+ * Supports both single and batched GraphQL responses.
114
+ * @param {string|object|array} [responseBody] The response body to check
115
+ * @returns {boolean} True if the response contains GraphQL errors
116
+ */
117
+ function hasGQLErrors(responseBody) {
118
+ if (!responseBody) return false;
119
+ try {
120
+ let parsed = responseBody;
121
+
122
+ // Parse string to object if needed
123
+ if (typeof responseBody === 'string') {
124
+ parsed = JSON.parse(responseBody);
125
+ }
126
+
127
+ // Handle batched GraphQL responses (array of response objects)
128
+ if (Array.isArray(parsed)) {
129
+ return parsed.some(hasValidGQLErrors);
130
+ }
131
+
132
+ // Handle single GraphQL response
133
+ return hasValidGQLErrors(parsed);
134
+ } catch (err) {
135
+ return false;
136
+ }
98
137
  }
@@ -11,10 +11,13 @@ var _denyList = require("../../../common/deny-list/deny-list");
11
11
  var _constants = require("../constants");
12
12
  var _features = require("../../../loaders/features/features");
13
13
  var _aggregateBase = require("../../utils/aggregate-base");
14
- var _gql = require("./gql");
15
14
  var _belSerializer = require("../../../common/serialize/bel-serializer");
16
15
  var _nreum = require("../../../common/window/nreum");
16
+ var _gql = require("./gql");
17
+ var _payloads = require("../../../common/payloads/payloads");
18
+ var _obfuscate = require("../../../common/util/obfuscate");
17
19
  var _utils = require("../../../common/v2/utils");
20
+ var _events = require("../../../common/constants/events");
18
21
  var _uniqueId = require("../../../common/ids/unique-id");
19
22
  /**
20
23
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
@@ -27,6 +30,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
27
30
  super(agentRef, _constants.FEATURE_NAME);
28
31
  (0, _denyList.setDenyList)(agentRef.runtime.denyList);
29
32
  const classThis = this;
33
+
34
+ // Create obfuscator for AJAX requests
35
+ this.obfuscator = new _obfuscate.Obfuscator(agentRef, _events.EVENT_TYPES.AJAX);
30
36
  if (!agentRef.init.ajax.block_internal) {
31
37
  // if the agent is tracking ITSELF, it can spawn endless ajax requests early if they are large from custom attributes, so we just disable early harvest for ajax in this case.
32
38
  super.canHarvestEarly = false;
@@ -91,17 +97,26 @@ class Aggregate extends _aggregateBase.AggregateBase {
91
97
  callbackDuration: metrics.cbTime,
92
98
  [_constants.AJAX_ID]: (0, _uniqueId.generateUuid)() // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
93
99
  };
100
+ event.gql = params.gql = (0, _gql.parseGQL)({
101
+ body: ctx.requestBody,
102
+ query: ctx.parsedOrigin?.search
103
+ });
104
+ if (event.gql) event.gql.operationHasErrors = params.gql.operationHasErrors = (0, _gql.hasGQLErrors)(ctx.responseBody);
105
+ const capturePayloadSetting = this.agentRef.init.ajax.capture_payloads;
106
+ const shouldCapturePayload = (0, _payloads.canCapturePayload)(capturePayloadSetting, params.status, event.gql?.operationHasErrors);
107
+ if (shouldCapturePayload) {
108
+ // Store raw data; obfuscation and truncation will happen in the serializer
109
+ params.requestQuery = event.requestQuery = (0, _payloads.parseQueryString)(ctx.parsedOrigin?.search);
110
+ params.requestHeaders = event.requestHeaders = ctx.requestHeaders;
111
+ params.responseHeaders = event.responseHeaders = ctx.responseHeaders;
112
+ if ((0, _payloads.isLikelyHumanReadable)(ctx.requestHeaders, ctx.requestBody)) params.requestBody = event.requestBody = ctx.requestBody;
113
+ if ((0, _payloads.isLikelyHumanReadable)(ctx.responseHeaders, ctx.responseBody)) params.responseBody = event.responseBody = ctx.responseBody;
114
+ }
94
115
  if (ctx.dt) {
95
116
  event.spanId = ctx.dt.spanId;
96
117
  event.traceId = ctx.dt.traceId;
97
118
  event.spanTimestamp = Math.floor(this.agentRef.runtime.timeKeeper.correctAbsoluteTimestamp(ctx.dt.timestamp));
98
119
  }
99
-
100
- // parsed from the AJAX body, looking for operationName param & parsing query for operationType
101
- event.gql = params.gql = (0, _gql.parseGQL)({
102
- body: ctx.body,
103
- query: ctx.parsedOrigin?.search
104
- });
105
120
  if (event.gql) this.reportSupportabilityMetric('Ajax/Events/GraphQL/Bytes-Added', (0, _stringify.stringify)(event.gql).length);
106
121
 
107
122
  /** make a copy of the event for the MFE target if it exists */
@@ -129,7 +144,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
129
144
  }
130
145
  serializer(eventBuffer) {
131
146
  if (!eventBuffer.length) return;
132
- const addString = (0, _belSerializer.getAddStringContext)(this.agentRef.runtime.obfuscator);
147
+ const {
148
+ addString,
149
+ addStringWithTruncation
150
+ } = (0, _payloads.createStringAdders)(_belSerializer.getAddStringContext, this.obfuscator);
133
151
  let payload = 'bel.7;';
134
152
  let firstTimestamp = 0;
135
153
  for (let i = 0; i < eventBuffer.length; i++) {
@@ -154,15 +172,35 @@ class Aggregate extends _aggregateBase.AggregateBase {
154
172
  // Since configuration objects (like info) are created new each time they are set, we have to grab the current pointer to the attr object here.
155
173
  const jsAttributes = this.agentRef.info.jsAttributes;
156
174
 
157
- // add custom attributes
158
- // gql decorators are added as custom attributes to alleviate need for new BEL schema
159
- const attrParts = (0, _belSerializer.addCustomAttributes)({
175
+ // Regular attributes: obfuscate only
176
+ const regularAttrs = (0, _belSerializer.addCustomAttributes)({
160
177
  ...(jsAttributes || {}),
161
- ...(event.gql || {}),
178
+ [_constants.AJAX_ID]: event[_constants.AJAX_ID],
179
+ // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
162
180
  ...(event.targetAttributes || {}),
163
181
  // used to supply the version 2 attributes, either MFE target or duplication attributes for the main agent app
164
- [_constants.AJAX_ID]: event[_constants.AJAX_ID] // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
182
+ ...(event.gql || {})
165
183
  }, addString);
184
+
185
+ // Payload attributes: obfuscate then truncate
186
+ const payloadAttrs = (0, _belSerializer.addCustomAttributes)({
187
+ ...(event.requestBody ? {
188
+ requestBody: event.requestBody
189
+ } : {}),
190
+ ...(event.requestHeaders ? {
191
+ requestHeaders: event.requestHeaders
192
+ } : {}),
193
+ ...(event.requestQuery ? {
194
+ requestQuery: event.requestQuery
195
+ } : {}),
196
+ ...(event.responseBody ? {
197
+ responseBody: event.responseBody
198
+ } : {}),
199
+ ...(event.responseHeaders ? {
200
+ responseHeaders: event.responseHeaders
201
+ } : {})
202
+ }, addStringWithTruncation);
203
+ const attrParts = [...regularAttrs, ...payloadAttrs];
166
204
  fields.unshift((0, _belSerializer.numeric)(attrParts.length));
167
205
  insert += fields.join(',');
168
206
  if (attrParts && attrParts.length > 0) {
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.FEATURE_NAME = exports.AJAX_ID = void 0;
6
+ exports.FEATURE_NAME = exports.CAPTURE_PAYLOAD_SETTINGS = exports.AJAX_ID = void 0;
7
7
  var _features = require("../../loaders/features/features");
8
8
  /**
9
9
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
@@ -11,4 +11,9 @@ var _features = require("../../loaders/features/features");
11
11
  */
12
12
 
13
13
  const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.ajax;
14
+ const CAPTURE_PAYLOAD_SETTINGS = exports.CAPTURE_PAYLOAD_SETTINGS = {
15
+ NONE: 'none',
16
+ FAILURES: 'failures',
17
+ ALL: 'all'
18
+ };
14
19
  const AJAX_ID = exports.AJAX_ID = 'ajaxRequest.id';
@@ -31,6 +31,7 @@ var handlersLen = handlers.length;
31
31
  var origRequest = (0, _nreum.gosNREUMOriginals)().o.REQ;
32
32
  var origXHR = (0, _nreum.gosNREUMOriginals)().o.XHR;
33
33
  const NR_CAT_HEADER = 'X-NewRelic-App-Data';
34
+ const INTERNAL_ERROR = 'internal-error';
34
35
  class Instrument extends _instrumentBase.InstrumentBase {
35
36
  static featureName = _constants.FEATURE_NAME;
36
37
  constructor(agentRef) {
@@ -77,10 +78,12 @@ class Instrument extends _instrumentBase.InstrumentBase {
77
78
  }
78
79
  exports.Instrument = Instrument;
79
80
  function subscribeToEvents(agentRef, ee, handler, dt) {
81
+ const shouldInterceptPayloads = [_constants.CAPTURE_PAYLOAD_SETTINGS.ALL, _constants.CAPTURE_PAYLOAD_SETTINGS.FAILURES].includes(agentRef.init.ajax?.capture_payloads);
80
82
  ee.on('new-xhr', onNewXhr);
81
83
  ee.on('open-xhr-start', onOpenXhrStart);
82
84
  ee.on('open-xhr-end', onOpenXhrEnd);
83
85
  ee.on('send-xhr-start', onSendXhrStart);
86
+ ee.on('setRequestHeader-xhr-start', onSetRequestHeader);
84
87
  ee.on('xhr-cb-time', onXhrCbTime);
85
88
  ee.on('xhr-load-added', onXhrLoadAdded);
86
89
  ee.on('xhr-load-removed', onXhrLoadRemoved);
@@ -152,6 +155,13 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
152
155
  }
153
156
  }
154
157
  }
158
+ function onSetRequestHeader(args, xhr) {
159
+ // args[0] = header name, args[1] = header value
160
+ if (shouldInterceptPayloads && args.length >= 2) {
161
+ this.requestHeaders ??= {};
162
+ this.requestHeaders[args[0].toLowerCase()] = args[1];
163
+ }
164
+ }
155
165
  function onSendXhrStart(args, xhr) {
156
166
  var metrics = this.metrics;
157
167
  var data = args[0];
@@ -161,7 +171,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
161
171
  if (size) metrics.txSize = size;
162
172
  }
163
173
  this.startTime = (0, _now.now)();
164
- this.body = data;
174
+ this.requestBody = data;
165
175
  this.listener = function (evt) {
166
176
  try {
167
177
  if (evt.type === 'abort' && !context.loadCaptureCalled) {
@@ -170,7 +180,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
170
180
  if (evt.type !== 'load' || context.called === context.totalCbs && (context.onloadCalled || typeof xhr.onload !== 'function') && typeof context.end === 'function') context.end(xhr);
171
181
  } catch (e) {
172
182
  try {
173
- ee.emit('internal-error', [e]);
183
+ ee.emit(INTERNAL_ERROR, [e]);
174
184
  } catch (err) {
175
185
  // do nothing
176
186
  }
@@ -298,8 +308,27 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
298
308
  addUrl(this, (0, _extractUrl.extractUrl)(target));
299
309
  const method = ('' + (target && target instanceof origRequest && target.method || opts.method || 'GET')).toUpperCase();
300
310
  this.params.method = method;
301
- this.body = opts.body;
302
- this.txSize = (0, _dataSize.dataSize)(opts.body) || 0;
311
+ this.txSize = (0, _dataSize.dataSize)(opts.body || target?.body) || 0;
312
+
313
+ // Capture request headers
314
+ try {
315
+ var headers = opts.headers || target?.headers;
316
+ if (shouldInterceptPayloads && headers) {
317
+ this.requestHeaders ??= {};
318
+ if (headers instanceof Headers) {
319
+ headers.forEach(function (value, key) {
320
+ this.requestHeaders[key.toLowerCase()] = value;
321
+ }.bind(this));
322
+ } else if (typeof headers === 'object') {
323
+ for (var key in headers) {
324
+ this.requestHeaders[key.toLowerCase()] = headers[key];
325
+ }
326
+ }
327
+ }
328
+ } catch (e) {
329
+ // Silently fail if we can't access headers
330
+ }
331
+ this.requestBody = opts.body || target?.body;
303
332
  }
304
333
 
305
334
  // we capture failed call as status 0, the actual error is ignored
@@ -310,19 +339,45 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
310
339
  if ((0, _denyList.hasUndefinedHostname)(this.params)) return; // don't bother with fetch to url with no hostname
311
340
 
312
341
  this.params.status = res ? res.status : 0;
342
+ const finishAndReport = () => {
343
+ // convert rxSize to a number - handle both string (from content-length header) and number (from fallback)
344
+ const num = +this.rxSize;
345
+ const responseSize = this.rxSize != null && !isNaN(num) ? num : undefined;
346
+ const metrics = {
347
+ txSize: this.txSize,
348
+ rxSize: responseSize,
349
+ duration: this.endTime - this.startTime
350
+ };
351
+ const payload = [this.params, metrics, this.startTime, this.endTime, 'fetch'];
352
+ this.targets.forEach(target => reportToAgg(payload, this, target));
353
+ };
313
354
 
314
- // convert rxSize to a number
315
- let responseSize;
316
- if (typeof this.rxSize === 'string' && this.rxSize.length > 0) {
317
- responseSize = +this.rxSize;
355
+ /** Since accessing fetch bodies is an async process, these are
356
+ * reasonable conditions to check to not do needless extra work */
357
+ if (!res || !shouldInterceptPayloads) {
358
+ finishAndReport();
359
+ return;
318
360
  }
319
- const metrics = {
320
- txSize: this.txSize,
321
- rxSize: responseSize,
322
- duration: (0, _now.now)() - this.startTime
323
- };
324
- const payload = [this.params, metrics, this.startTime, this.endTime, 'fetch'];
325
- this.targets.forEach(target => reportToAgg(payload, this, target));
361
+
362
+ // Clone the response to read the body without consuming the original
363
+ res.clone().text().then(text => {
364
+ this.responseBody = text;
365
+ // Use captured payload size as fallback if content-length header was missing or is 0 with a body
366
+ // Only apply fallback for non-network-error responses (status !== 0)
367
+ if ((!this.rxSize || this.rxSize === '0' || this.rxSize === 0) && text !== undefined && this.params.status !== 0) {
368
+ this.rxSize = (0, _dataSize.dataSize)(text);
369
+ }
370
+ if (res?.headers) {
371
+ this.responseHeaders = {};
372
+ res.headers.forEach(function (value, key) {
373
+ this.responseHeaders[key.toLowerCase()] = value;
374
+ }.bind(this));
375
+ }
376
+ }).catch(err => {
377
+ ee.emit(INTERNAL_ERROR, [err]);
378
+ }).finally(() => {
379
+ finishAndReport();
380
+ });
326
381
  }
327
382
 
328
383
  // Create report for XHR request that has finished
@@ -337,7 +392,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
337
392
  if (params.aborted) return;
338
393
  if ((0, _denyList.hasUndefinedHostname)(params)) return; // don't bother with XHR of url with no hostname
339
394
 
340
- metrics.duration = (0, _now.now)() - this.startTime;
395
+ metrics.duration = this.endTime - this.startTime;
341
396
  if (!this.loadCaptureCalled && xhr.readyState === 4) {
342
397
  captureXhrData(this, xhr);
343
398
  } else if (params.status == null) {
@@ -346,6 +401,25 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
346
401
 
347
402
  // Always send cbTime, even if no noticeable time was taken.
348
403
  metrics.cbTime = this.cbTime;
404
+ if (shouldInterceptPayloads) {
405
+ try {
406
+ this.responseBody = xhr.responseText;
407
+ } catch (e) {
408
+ this.responseBody = xhr.response;
409
+ }
410
+
411
+ // Use captured payload size as fallback if not already determined or is 0 with a body
412
+ // Only apply fallback for non-network-error responses (status !== 0)
413
+ if ((!metrics.rxSize || metrics.rxSize === 0) && this.responseBody !== undefined && params.status !== 0) {
414
+ const size = (0, _dataSize.dataSize)(this.responseBody);
415
+ if (size !== undefined) metrics.rxSize = size;
416
+ }
417
+ try {
418
+ this.responseHeaders = parseResponseHeaders(xhr.getAllResponseHeaders());
419
+ } catch (err) {
420
+ ee.emit(INTERNAL_ERROR, [err]);
421
+ }
422
+ }
349
423
  const payload = [params, metrics, this.startTime, this.endTime, 'xhr'];
350
424
  this.targets.forEach(target => reportToAgg(payload, this, target));
351
425
  }
@@ -355,7 +429,8 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
355
429
  function captureXhrData(ctx, xhr) {
356
430
  ctx.params.status = xhr.status;
357
431
  var size = (0, _responseSize.responseSizeFromXhr)(xhr, ctx.lastSize);
358
- if (size) ctx.metrics.rxSize = size;
432
+ // Don't set rxSize for network errors (status 0)
433
+ if (size !== undefined && xhr.status !== 0) ctx.metrics.rxSize = size;
359
434
  if (ctx.sameOrigin && xhr.getAllResponseHeaders().indexOf(NR_CAT_HEADER) >= 0) {
360
435
  var header = xhr.getResponseHeader(NR_CAT_HEADER);
361
436
  if (header) {
@@ -377,4 +452,17 @@ function addUrl(ctx, url) {
377
452
  ctx.parsedOrigin = parsed;
378
453
  ctx.sameOrigin = parsed.sameOrigin;
379
454
  }
455
+ function parseResponseHeaders(headerStr) {
456
+ const headers = {};
457
+ if (!headerStr) return headers;
458
+ headerStr.split('\r\n').forEach(function (line) {
459
+ const separatorIndex = line.indexOf(': ');
460
+ if (separatorIndex > 0) {
461
+ const name = line.substring(0, separatorIndex);
462
+ const value = line.substring(separatorIndex + 2);
463
+ headers[name.toLowerCase()] = value;
464
+ }
465
+ });
466
+ return headers;
467
+ }
380
468
  const Ajax = exports.Ajax = Instrument;
@@ -12,10 +12,11 @@ var _aggregateBase = require("../../utils/aggregate-base");
12
12
  var _console = require("../../../common/util/console");
13
13
  var _now = require("../../../common/timing/now");
14
14
  var _registerHandler = require("../../../common/event-emitter/register-handler");
15
- var _traverse = require("../../../common/util/traverse");
15
+ var _obfuscate = require("../../../common/util/obfuscate");
16
16
  var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
17
17
  var _iframe = require("../../../common/dom/iframe");
18
18
  var _typeCheck = require("../../../common/util/type-check");
19
+ var _events = require("../../../common/constants/events");
19
20
  var _utils = require("../../../common/v2/utils");
20
21
  /**
21
22
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
@@ -28,6 +29,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
28
29
  constructor(agentRef) {
29
30
  super(agentRef, _constants.FEATURE_NAME);
30
31
  this.referrerUrl = _runtime.isBrowserScope && document.referrer ? (0, _cleanUrl.cleanURL)(document.referrer) : undefined;
32
+
33
+ // Create generic obfuscator (no specific event types) since this feature handles multiple event types
34
+ // Will check each event's eventType property at runtime against obfuscation rules
35
+ this.obfuscator = new _obfuscate.Obfuscator(agentRef);
31
36
  this.waitForFlags(['ins']).then(([ins]) => {
32
37
  if (!ins) {
33
38
  this.blocked = true;
@@ -47,7 +52,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
47
52
  (0, _registerHandler.registerHandler)('api-addPageAction', (timestamp, name, attributes, target) => {
48
53
  this.addEvent({
49
54
  ...attributes,
50
- eventType: 'PageAction',
55
+ eventType: _events.EVENT_TYPES.PA,
51
56
  timestamp: this.#toEpoch(timestamp),
52
57
  timeSinceLoad: timestamp / 1000,
53
58
  actionName: name,
@@ -75,7 +80,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
75
80
  } = aggregatedUserAction.event;
76
81
  aggregatedUserAction.targets.forEach(mfeTarget => {
77
82
  const userActionEvent = {
78
- eventType: 'UserAction',
83
+ eventType: _events.EVENT_TYPES.UA,
79
84
  timestamp: this.#toEpoch(timeStamp),
80
85
  action: type,
81
86
  actionCount: aggregatedUserAction.count,
@@ -162,7 +167,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
162
167
  const detailObj = agentRef.init.performance.capture_detail ? createDetailAttrs(entry.detail) : {};
163
168
  this.addEvent({
164
169
  ...detailObj,
165
- eventType: 'BrowserPerformance',
170
+ eventType: _events.EVENT_TYPES.BP,
166
171
  timestamp: this.#toEpoch(entry.startTime),
167
172
  entryName: entry.name,
168
173
  entryDuration: entry.duration,
@@ -228,7 +233,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
228
233
  this.reportSupportabilityMetric('Generic/Performance/Resource/Seen');
229
234
  const event = {
230
235
  ...entryObject,
231
- eventType: 'BrowserPerformance',
236
+ eventType: _events.EVENT_TYPES.BP,
232
237
  timestamp: this.#toEpoch(entryObject.startTime),
233
238
  entryName: (0, _cleanUrl.cleanURL)(name),
234
239
  entryDuration: duration,
@@ -248,7 +253,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
248
253
  } = args;
249
254
  const event = {
250
255
  ...customAttributes,
251
- eventType: 'BrowserPerformance',
256
+ eventType: _events.EVENT_TYPES.BP,
252
257
  timestamp: this.#toEpoch(start),
253
258
  entryName: n,
254
259
  entryDuration: duration,
@@ -260,7 +265,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
260
265
  (0, _registerHandler.registerHandler)('ws-complete', nrData => {
261
266
  const event = {
262
267
  ...nrData,
263
- eventType: 'WebSocket',
268
+ eventType: _events.EVENT_TYPES.WS,
264
269
  timestamp: this.#toEpoch(nrData.timestamp),
265
270
  openedAt: this.#toEpoch(nrData.openedAt),
266
271
  closedAt: this.#toEpoch(nrData.closedAt)
@@ -275,7 +280,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
275
280
  if (!agentRef.init.feature_flags.includes('no_spv')) {
276
281
  (0, _registerHandler.registerHandler)('spv', evt => {
277
282
  this.addEvent({
278
- eventType: 'SecurityPolicyViolation',
283
+ eventType: _events.EVENT_TYPES.SPV,
279
284
  timestamp: this.#toEpoch(evt.timeStamp),
280
285
  blockedUri: evt.blockedURI,
281
286
  documentUri: evt.documentURI,
@@ -344,9 +349,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
344
349
  });
345
350
  }
346
351
  serializer(eventBuffer) {
347
- return (0, _traverse.applyFnToProps)({
352
+ return this.obfuscator.traverseAndObfuscateEvents({
348
353
  ins: eventBuffer
349
- }, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
354
+ });
350
355
  }
351
356
  queryStringsBuilder() {
352
357
  return {
@@ -4,9 +4,10 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.RESERVED_EVENT_TYPES = exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.FRUSTRATION_TIMEOUT_MS = exports.FEATURE_NAME = exports.FEATURE_FLAGS = void 0;
7
+ var _events = require("../../common/constants/events");
7
8
  var _features = require("../../loaders/features/features");
8
9
  /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
11
  * SPDX-License-Identifier: Apache-2.0
11
12
  */
12
13
 
@@ -16,7 +17,7 @@ const OBSERVED_WINDOW_EVENTS = exports.OBSERVED_WINDOW_EVENTS = ['focus', 'blur'
16
17
  const RAGE_CLICK_THRESHOLD_EVENTS = exports.RAGE_CLICK_THRESHOLD_EVENTS = 4;
17
18
  const RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_MS = 1000;
18
19
  const FRUSTRATION_TIMEOUT_MS = exports.FRUSTRATION_TIMEOUT_MS = 2000;
19
- const RESERVED_EVENT_TYPES = exports.RESERVED_EVENT_TYPES = ['PageAction', 'UserAction', 'BrowserPerformance'];
20
+ const RESERVED_EVENT_TYPES = exports.RESERVED_EVENT_TYPES = [_events.EVENT_TYPES.PA, _events.EVENT_TYPES.UA, _events.EVENT_TYPES.BP];
20
21
  const FEATURE_FLAGS = exports.FEATURE_FLAGS = {
21
22
  RESOURCES: 'experimental.resources',
22
23
  REGISTER: 'register'
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.formatStackTrace = formatStackTrace;
7
7
  exports.truncateSize = truncateSize;
8
8
  /**
9
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
10
  * SPDX-License-Identifier: Apache-2.0
11
11
  */
12
12
 
@@ -16,7 +16,7 @@ function formatStackTrace(stackLines) {
16
16
  return truncateStackLines(stackLines).replace(stripNewlinesRegex, '');
17
17
  }
18
18
 
19
- // takes array of stack lines and returns string with top 50 and buttom 50 lines
19
+ // takes array of stack lines and returns string with top 50 and bottom 50 lines
20
20
  function truncateStackLines(stackLines) {
21
21
  var stackString;
22
22
  if (stackLines.length > 100) {
@@ -16,11 +16,12 @@ var _constants = require("../constants");
16
16
  var _features = require("../../../loaders/features/features");
17
17
  var _aggregateBase = require("../../utils/aggregate-base");
18
18
  var _now = require("../../../common/timing/now");
19
- var _traverse = require("../../../common/util/traverse");
19
+ var _obfuscate = require("../../../common/util/obfuscate");
20
20
  var _internalErrors = require("./internal-errors");
21
21
  var _utils = require("../../../common/v2/utils");
22
22
  var _causeString = require("./cause-string");
23
23
  var _shortCircuit = require("../../../common/util/short-circuit");
24
+ var _events = require("../../../common/constants/events");
24
25
  /**
25
26
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
26
27
  * SPDX-License-Identifier: Apache-2.0
@@ -41,6 +42,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
41
42
  this.observedAt = {};
42
43
  this.pageviewReported = {};
43
44
  this.errorOnPage = false;
45
+
46
+ // Create obfuscators for each event type this feature handles
47
+ this.errorObfuscator = new _obfuscate.Obfuscator(agentRef, _events.EVENT_TYPES.JSE);
48
+ this.xhrObfuscator = new _obfuscate.Obfuscator(agentRef, _events.EVENT_TYPES.AJAX);
44
49
  (0, _registerHandler.registerHandler)('err', this.processError.bind(this), this.featureName, this.ee);
45
50
  (0, _registerHandler.registerHandler)('ierr', this.processError.bind(this), this.featureName, this.ee);
46
51
  (0, _registerHandler.registerHandler)('returnJserror', (jsErrorEvent, softNavAttrs) => this.#storeJserrorForHarvest(jsErrorEvent, softNavAttrs), this.featureName, this.ee);
@@ -56,7 +61,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
56
61
  });
57
62
  }
58
63
  serializer(aggregatorTypeToBucketsMap) {
59
- return (0, _traverse.applyFnToProps)(aggregatorTypeToBucketsMap, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string');
64
+ return {
65
+ err: this.errorObfuscator.traverseAndObfuscateEvents(aggregatorTypeToBucketsMap.err),
66
+ ierr: this.errorObfuscator.traverseAndObfuscateEvents(aggregatorTypeToBucketsMap.ierr),
67
+ xhr: this.xhrObfuscator.traverseAndObfuscateEvents(aggregatorTypeToBucketsMap.xhr)
68
+ };
60
69
  }
61
70
  queryStringsBuilder(aggregatorTakeReturnedData) {
62
71
  const qs = {};
@@ -258,7 +267,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
258
267
  params.stack_trace = (0, _formatStackTrace.truncateSize)(stackInfo.stackString);
259
268
  this.observedAt[bucketHash] = Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(time));
260
269
  } else {
261
- params.browser_stack_hash = (0, _stringHashCode.stringHashCode)(stackInfo.stackString);
270
+ params.browser_stack_hash = (0, _stringHashCode.stringHashCode)((0, _formatStackTrace.truncateSize)(stackInfo.stackString));
262
271
  }
263
272
  params.releaseIds = (0, _stringify.stringify)(this.agentRef.runtime.releaseIds);
264
273