@newrelic/browser-agent 1.316.0 → 1.317.0-rc.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 (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
package/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
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.317.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.316.0...v1.317.0) (2026-06-18)
7
+
8
+
9
+ ### Features
10
+
11
+ * Detect and report AJAX payloads ([#1651](https://github.com/newrelic/newrelic-browser-agent/issues/1651)) ([08394db](https://github.com/newrelic/newrelic-browser-agent/commit/08394db8a5a9f2b6fd627e9eee76022977cb433c))
12
+ * Enhance obfuscation to support event filter ([#1782](https://github.com/newrelic/newrelic-browser-agent/issues/1782)) ([c97aaf9](https://github.com/newrelic/newrelic-browser-agent/commit/c97aaf9773cfb0b62a810eb07b37e6f5b96d5ab5))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * Calculate browser_stack_hash from truncated stack trace ([#1789](https://github.com/newrelic/newrelic-browser-agent/issues/1789)) ([f7df627](https://github.com/newrelic/newrelic-browser-agent/commit/f7df627b14e3e5e8fa4a8d94433ea76e9a9902bb))
18
+ * Get responseBodySize from body if content-length is missing ([#1793](https://github.com/newrelic/newrelic-browser-agent/issues/1793)) ([58663ec](https://github.com/newrelic/newrelic-browser-agent/commit/58663ec19dc99ec94e34c17a5dfaa20a07038dba))
19
+ * Patch handling of inlining images for Session Replay ([#1765](https://github.com/newrelic/newrelic-browser-agent/issues/1765)) ([b330f8e](https://github.com/newrelic/newrelic-browser-agent/commit/b330f8e3e35f2fda076967092bcb70e792de6eeb))
20
+
6
21
  ## [1.316.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.315.0...v1.316.0) (2026-06-01)
7
22
 
8
23
 
@@ -24,7 +24,7 @@ function getModeledObject(obj, model) {
24
24
  output[key] = null;
25
25
  continue;
26
26
  }
27
- if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (obj[key] instanceof Map || obj[key] instanceof Set || obj[key] instanceof Date || obj[key] instanceof RegExp) output[key] = obj[key];else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
27
+ if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (obj[key] instanceof Map || obj[key] instanceof Set || obj[key] instanceof Date || obj[key] instanceof RegExp) output[key] = obj[key];else if (typeof obj[key] === 'object' && model[key] !== null && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
28
28
  } catch (e) {
29
29
  if (!output[key]) (0, _console.warn)(1, e);
30
30
  }
@@ -16,6 +16,7 @@ exports.default = void 0;
16
16
  * @property {boolean} [ajax.block_internal] - If true, agent requests going to harvest endpoint are treated as on deny list. In other words, agent will not self-report AJAX.
17
17
  * @property {boolean} [ajax.enabled] - Turn on/off the ajax feature (on by default).
18
18
  * @property {boolean} [ajax.autoStart] - If true, the agent will automatically start the ajax feature. Otherwise, it will be in a deferred state until the `start` API method is called.
19
+ * @property {('none'|'failures'|'all')} [ajax.capture_payloads] - Controls when AJAX request/response payloads are captured. 'none' = never capture, 'failures' = capture only on errors (4xx, 5xx, network errors, GraphQL errors), 'all' = always capture.
19
20
  * @property {Object} [api]
20
21
  * @property {Object} [api.register]
21
22
  * @property {boolean} [api.register.enabled] - If true, the agent will allow registered children to be sent to the server.
@@ -5,8 +5,9 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.mergeInit = void 0;
7
7
  var _constants = require("../../features/generic_events/constants");
8
+ var _constants2 = require("../../features/ajax/constants");
8
9
  var _querySelector = require("../dom/query-selector");
9
- var _constants2 = require("../session/constants");
10
+ var _constants3 = require("../session/constants");
10
11
  var _console = require("../util/console");
11
12
  var _configurable = require("./configurable");
12
13
  /**
@@ -57,7 +58,8 @@ const InitModelFn = () => {
57
58
  deny_list: undefined,
58
59
  block_internal: true,
59
60
  enabled: true,
60
- autoStart: true
61
+ autoStart: true,
62
+ capture_payloads: _constants2.CAPTURE_PAYLOAD_SETTINGS.NONE
61
63
  },
62
64
  api: {
63
65
  register: {
@@ -141,8 +143,8 @@ const InitModelFn = () => {
141
143
  beacon: undefined
142
144
  },
143
145
  session: {
144
- expiresMs: _constants2.DEFAULT_EXPIRES_MS,
145
- inactiveMs: _constants2.DEFAULT_INACTIVE_MS
146
+ expiresMs: _constants3.DEFAULT_EXPIRES_MS,
147
+ inactiveMs: _constants3.DEFAULT_INACTIVE_MS
146
148
  },
147
149
  session_replay: {
148
150
  autoStart: true,
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.316.0";
20
+ const VERSION = exports.VERSION = "1.317.0-rc.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -31,4 +31,4 @@ const BUILD_ENV = exports.BUILD_ENV = "CDN";
31
31
  const DIST_METHOD = exports.DIST_METHOD = 'CDN';
32
32
  const RRWEB_PACKAGE_NAME = exports.RRWEB_PACKAGE_NAME = '@newrelic/rrweb';
33
33
  // Babel will inline this with the rrweb (fork) version on CDN builds.
34
- const RRWEB_VERSION = exports.RRWEB_VERSION = "1.1.0";
34
+ const RRWEB_VERSION = exports.RRWEB_VERSION = "^1.1.2";
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.316.0";
20
+ const VERSION = exports.VERSION = "1.317.0-rc.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -32,4 +32,4 @@ const BUILD_ENV = exports.BUILD_ENV = 'NPM';
32
32
  const DIST_METHOD = exports.DIST_METHOD = 'NPM';
33
33
  const RRWEB_PACKAGE_NAME = exports.RRWEB_PACKAGE_NAME = '@newrelic/rrweb';
34
34
  // Babel will inline this with the rrweb (fork) version on NPM dist esm/cjs builds.
35
- const RRWEB_VERSION = exports.RRWEB_VERSION = "1.1.0";
35
+ const RRWEB_VERSION = exports.RRWEB_VERSION = "^1.1.2";
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.EVENT_TYPES = void 0;
7
+ /**
8
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
9
+ * SPDX-License-Identifier: Apache-2.0
10
+ */
11
+
12
+ const EVENT_TYPES = exports.EVENT_TYPES = {
13
+ AJAX: 'AjaxRequest',
14
+ PA: 'PageAction',
15
+ UA: 'UserAction',
16
+ BP: 'BrowserPerformance',
17
+ WS: 'WebSocket',
18
+ SPV: 'SecurityPolicyViolation',
19
+ JSE: 'JavaScriptError',
20
+ LOG: 'Log',
21
+ PVE: 'PageView',
22
+ PVT: 'PageViewTiming',
23
+ SR: 'SessionReplay',
24
+ ST: 'SessionTrace',
25
+ BI: 'BrowserInteraction'
26
+ };
@@ -4,21 +4,13 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Harvester = void 0;
7
- exports.send = send;
8
7
  var _constants = require("../../features/metrics/constants");
9
8
  var _features = require("../../loaders/features/features");
10
- var _env = require("../constants/env.npm");
11
- var _runtime = require("../constants/runtime");
12
9
  var _handle = require("../event-emitter/handle");
13
- var _eventListenerOpts = require("../event-listener/event-listener-opts");
14
- var _now = require("../timing/now");
15
10
  var _eol = require("../unload/eol");
16
- var _cleanUrl = require("../url/clean-url");
17
- var _encode = require("../url/encode");
18
- var _console = require("../util/console");
19
- var _stringify = require("../util/stringify");
20
11
  var _submitData = require("../util/submit-data");
21
- var _globalEvent = require("../dispatch/global-event");
12
+ var _send = require("./send");
13
+ var _obfuscate = require("../util/obfuscate");
22
14
  /**
23
15
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
24
16
  * SPDX-License-Identifier: Apache-2.0
@@ -33,6 +25,10 @@ class Harvester {
33
25
  initializedAggregates = [];
34
26
  constructor(agentRef) {
35
27
  this.agentRef = agentRef;
28
+
29
+ // Create obfuscator for harvest metadata (referrer URL, etc.)
30
+ // No event type specified - applies to all harvests regardless of feature
31
+ this.obfuscator = new _obfuscate.Obfuscator(agentRef);
36
32
  (0, _eol.subscribeToEOL)(() => {
37
33
  // do one last harvest round or check
38
34
  this.initializedAggregates.forEach(aggregateInst => {
@@ -74,11 +70,12 @@ class Harvester {
74
70
  const shouldRetryOnFail = !localOpts.isFinalHarvest && submitMethod === _submitData.xhr; // always retry all features harvests except for final
75
71
  output.payload = aggregateInst.makeHarvestPayload(shouldRetryOnFail, localOpts);
76
72
  if (!output.payload) return output;
77
- send(this.agentRef, {
73
+ (0, _send.send)(this.agentRef, {
78
74
  endpoint: _features.FEATURE_TO_ENDPOINT[aggregateInst.featureName],
79
75
  payload: output.payload,
80
76
  localOpts,
81
77
  submitMethod,
78
+ harvesterObfuscator: this.obfuscator,
82
79
  cbFinished,
83
80
  raw: aggregateInst.harvestOpts.raw,
84
81
  featureName: aggregateInst.featureName,
@@ -108,209 +105,4 @@ class Harvester {
108
105
  }
109
106
  }
110
107
  }
111
-
112
- /**
113
- * @typedef {import('./types.js').NetworkSendSpec} NetworkSendSpec
114
- */
115
- exports.Harvester = Harvester;
116
- const warnings = {};
117
- /**
118
- * Initiate a harvest call.
119
- * @param {NetworkSendSpec} param0 Specification for sending data
120
- * @returns {boolean} True if a network call was made. Note that this does not mean or guarantee that it was successful.
121
- */
122
- function send(agentRef, {
123
- endpoint,
124
- payload,
125
- localOpts = {},
126
- submitMethod,
127
- cbFinished,
128
- raw,
129
- featureName,
130
- endpointVersion = 1
131
- }) {
132
- if (!agentRef.info.errorBeacon) return false;
133
- let {
134
- body,
135
- qs
136
- } = cleanPayload(payload);
137
- if (Object.keys(body).length === 0 && !localOpts.sendEmptyBody) {
138
- // if there's no body to send, just run onfinish stuff and return
139
- if (cbFinished) cbFinished({
140
- sent: false
141
- });
142
- return false;
143
- }
144
- const protocol = agentRef.init.ssl === false ? 'http' : 'https';
145
- const perceivedBeacon = agentRef.init.proxy.beacon || agentRef.info.errorBeacon;
146
- const url = raw ? "".concat(protocol, "://").concat(perceivedBeacon, "/").concat(endpoint) : "".concat(protocol, "://").concat(perceivedBeacon).concat(endpoint !== _features.RUM ? '/' + endpoint : '', "/").concat(endpointVersion, "/").concat(agentRef.info.licenseKey);
147
- const baseParams = !raw ? baseQueryString(agentRef, qs, endpoint) : '';
148
- let payloadParams = (0, _encode.obj)(qs, agentRef.runtime.maxBytes);
149
- if (baseParams === '' && payloadParams.startsWith('&')) {
150
- payloadParams = payloadParams.substring(1);
151
- }
152
- const fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
153
- const gzip = !!qs?.attributes?.includes('gzip');
154
-
155
- // all gzipped data is already in the correct format and needs no transformation
156
- // all features going to 'events' endpoint should already be serialized & stringified
157
- let stringBody = gzip || endpoint === _features.EVENTS ? body : (0, _stringify.stringify)(body);
158
-
159
- // If body is null, undefined, or an empty object or array after stringifying, send an empty string instead.
160
- if (!stringBody || stringBody.length === 0 || stringBody === '{}' || stringBody === '[]') stringBody = '';
161
-
162
- // Warn--once per endpoint--if the agent tries to send large payloads
163
- if (endpoint !== _features.BLOBS && stringBody.length > 750000 && (warnings[endpoint] = (warnings[endpoint] || 0) + 1) === 1) (0, _console.warn)(28, endpoint);
164
- const headers = [{
165
- key: 'content-type',
166
- value: 'text/plain'
167
- }];
168
-
169
- /* Since workers don't support sendBeacon right now, they can only use XHR method.
170
- Because they still do permit synch XHR, the idea is that at final harvest time (worker is closing),
171
- we just make a BLOCKING request--trivial impact--with the remaining data as a temp fill-in for sendBeacon.
172
- Following the removal of img-element method. */
173
- let result = submitMethod({
174
- url: fullUrl,
175
- body: stringBody,
176
- sync: localOpts.isFinalHarvest && _runtime.isWorkerScope,
177
- headers
178
- });
179
- if (!localOpts.isFinalHarvest && cbFinished) {
180
- // final harvests don't hold onto buffer data (shouldRetryOnFail is false), so cleanup isn't needed
181
- if (submitMethod === _submitData.xhr) {
182
- result.addEventListener('loadend', function () {
183
- // `this` here in block refers to the XHR object in this scope, do not change the anon function to an arrow function
184
- // status 0 refers to a local error, such as CORS or network failure, or a blocked request by the browser (e.g. adblocker)
185
- const cbResult = {
186
- sent: this.status !== 0,
187
- status: this.status,
188
- retry: shouldRetry(this.status),
189
- fullUrl,
190
- xhr: this,
191
- responseText: this.responseText
192
- };
193
- cbFinished(cbResult);
194
-
195
- /** temporary audit of consistency of harvest metadata flags */
196
- if (!shouldRetry(this.status)) trackHarvestMetadata();
197
- }, (0, _eventListenerOpts.eventListenerOpts)(false));
198
- } else if (submitMethod === _submitData.xhrFetch) {
199
- result.then(async function (response) {
200
- const status = response.status;
201
- const cbResult = {
202
- sent: true,
203
- status,
204
- retry: shouldRetry(status),
205
- fullUrl,
206
- fetchResponse: response,
207
- responseText: await response.text()
208
- };
209
- cbFinished(cbResult);
210
- /** temporary audit of consistency of harvest metadata flags */
211
- if (!shouldRetry(status)) trackHarvestMetadata();
212
- });
213
- }
214
- function trackHarvestMetadata() {
215
- try {
216
- if (featureName === _features.FEATURE_NAMES.jserrors && !body?.err) return;
217
- const hasReplay = baseParams.includes('hr=1');
218
- const hasTrace = baseParams.includes('ht=1');
219
- const hasError = qs?.attributes?.includes('hasError=true');
220
- (0, _handle.handle)('harvest-metadata', [{
221
- [featureName]: {
222
- ...(hasReplay && {
223
- hasReplay
224
- }),
225
- ...(hasTrace && {
226
- hasTrace
227
- }),
228
- ...(hasError && {
229
- hasError
230
- })
231
- }
232
- }], undefined, _features.FEATURE_NAMES.metrics, agentRef.ee);
233
- } catch (err) {
234
- // do nothing
235
- }
236
- }
237
- }
238
- (0, _globalEvent.dispatchGlobalEvent)({
239
- drained: !!agentRef.runtime?.activatedFeatures,
240
- type: 'data',
241
- name: 'harvest',
242
- feature: featureName,
243
- data: {
244
- endpoint,
245
- headers,
246
- payload,
247
- submitMethod: getSubmitMethodName(),
248
- raw,
249
- synchronousXhr: !!(localOpts.isFinalHarvest && _runtime.isWorkerScope)
250
- }
251
- });
252
- return true;
253
- function shouldRetry(status) {
254
- switch (status) {
255
- case 408:
256
- case 429:
257
- case 500:
258
- return true;
259
- }
260
- return status >= 502 && status <= 504 || status >= 512 && status <= 530;
261
- }
262
- function getSubmitMethodName() {
263
- if (submitMethod === _submitData.xhr) return 'xhr';
264
- if (submitMethod === _submitData.xhrFetch) return 'fetch';
265
- return 'beacon';
266
- }
267
- }
268
-
269
- /**
270
- * Cleans and returns a payload object containing a body and qs
271
- * object with key/value pairs. KV pairs where the value is null,
272
- * undefined, or an empty string are removed to save on transmission
273
- * size.
274
- * @param {HarvestPayload} payload Payload to be sent to the endpoint.
275
- * @returns {HarvestPayload} Cleaned payload payload to be sent to the endpoint.
276
- */
277
- function cleanPayload(payload = {}) {
278
- const clean = input => {
279
- if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array || Array.isArray(input)) return input;
280
- if (typeof input === 'string') return input;
281
- return Object.entries(input || {}).reduce((accumulator, [key, value]) => {
282
- if (typeof value === 'number' || typeof value === 'string' && value.length > 0 || typeof value === 'object' && Object.keys(value || {}).length > 0) {
283
- accumulator[key] = value;
284
- }
285
- return accumulator;
286
- }, {});
287
- };
288
- return {
289
- body: clean(payload.body),
290
- qs: clean(payload.qs)
291
- };
292
- }
293
-
294
- // The stuff that gets sent every time.
295
- function baseQueryString(agentRef, qs, endpoint) {
296
- const ref = agentRef.runtime.obfuscator.obfuscateString((0, _cleanUrl.cleanURL)('' + _runtime.globalScope.location));
297
- const session = agentRef.runtime.session;
298
- const hr = !!session?.state.sessionReplaySentFirstChunk && session?.state.sessionReplayMode === 1 && endpoint !== _features.JSERRORS;
299
- const ht = !!session?.state.traceHarvestStarted && session?.state.sessionTraceMode === 1 && ![_features.LOGS, _features.BLOBS].includes(endpoint);
300
- const qps = ['a=' + agentRef.info.applicationID, (0, _encode.param)('sa', agentRef.info.sa ? '' + agentRef.info.sa : ''), (0, _encode.param)('v', _env.VERSION), transactionNameParam(), (0, _encode.param)('ct', agentRef.runtime.customTransaction), '&rst=' + (0, _now.now)(), '&ck=0',
301
- // ck param DEPRECATED - still expected by backend
302
- '&s=' + (session?.state.value || '0'),
303
- // the 0 id encaps all untrackable and default traffic
304
- (0, _encode.param)('ref', ref), (0, _encode.param)('ptid', agentRef.runtime.ptid ? '' + agentRef.runtime.ptid : '')];
305
- if (hr) qps.push((0, _encode.param)('hr', '1', qs));
306
- if (ht) qps.push((0, _encode.param)('ht', '1', qs));
307
- return qps.join('');
308
-
309
- // Constructs the transaction name param for the beacon URL.
310
- // Prefers the obfuscated transaction name over the plain text.
311
- // Falls back to making up a name.
312
- function transactionNameParam() {
313
- if (agentRef.info.transactionName) return (0, _encode.param)('to', agentRef.info.transactionName);
314
- return (0, _encode.param)('t', agentRef.info.tNamePlain || 'Unnamed Transaction');
315
- }
316
- }
108
+ exports.Harvester = Harvester;
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.send = send;
7
+ var _features = require("../../loaders/features/features");
8
+ var _env = require("../constants/env.npm");
9
+ var _runtime = require("../constants/runtime");
10
+ var _handle = require("../event-emitter/handle");
11
+ var _eventListenerOpts = require("../event-listener/event-listener-opts");
12
+ var _now = require("../timing/now");
13
+ var _cleanUrl = require("../url/clean-url");
14
+ var _encode = require("../url/encode");
15
+ var _console = require("../util/console");
16
+ var _stringify = require("../util/stringify");
17
+ var _submitData = require("../util/submit-data");
18
+ var _globalEvent = require("../dispatch/global-event");
19
+ /**
20
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
21
+ * SPDX-License-Identifier: Apache-2.0
22
+ */
23
+
24
+ /**
25
+ * @typedef {import('./types.js').NetworkSendSpec} NetworkSendSpec
26
+ */
27
+
28
+ const warnings = {};
29
+
30
+ /**
31
+ * Initiate a harvest call.
32
+ * @param {object} agentRef The initialized agent reference
33
+ * @param {NetworkSendSpec} spec Specification for sending data
34
+ * @returns {boolean} True if a network call was made. Note that this does not mean or guarantee that it was successful.
35
+ */
36
+ function send(agentRef, {
37
+ endpoint,
38
+ payload,
39
+ localOpts = {},
40
+ submitMethod,
41
+ cbFinished,
42
+ raw,
43
+ featureName,
44
+ endpointVersion = 1,
45
+ harvesterObfuscator
46
+ }) {
47
+ if (!agentRef.info.errorBeacon) return false;
48
+ let {
49
+ body,
50
+ qs
51
+ } = cleanPayload(payload);
52
+ if (Object.keys(body).length === 0 && !localOpts.sendEmptyBody) {
53
+ // if there's no body to send, just run onfinish stuff and return
54
+ if (cbFinished) cbFinished({
55
+ sent: false
56
+ });
57
+ return false;
58
+ }
59
+ const protocol = agentRef.init.ssl === false ? 'http' : 'https';
60
+ const perceivedBeacon = agentRef.init.proxy.beacon || agentRef.info.errorBeacon;
61
+ const url = raw ? "".concat(protocol, "://").concat(perceivedBeacon, "/").concat(endpoint) : "".concat(protocol, "://").concat(perceivedBeacon).concat(endpoint !== _features.RUM ? '/' + endpoint : '', "/").concat(endpointVersion, "/").concat(agentRef.info.licenseKey);
62
+ const baseParams = !raw ? baseQueryString(agentRef, qs, endpoint, harvesterObfuscator) : '';
63
+ let payloadParams = (0, _encode.obj)(qs, agentRef.runtime.maxBytes);
64
+ if (baseParams === '' && payloadParams.startsWith('&')) {
65
+ payloadParams = payloadParams.substring(1);
66
+ }
67
+ const fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
68
+ const gzip = !!qs?.attributes?.includes('gzip');
69
+
70
+ // all gzipped data is already in the correct format and needs no transformation
71
+ // all features going to 'events' endpoint should already be serialized & stringified
72
+ let stringBody = gzip || endpoint === _features.EVENTS ? body : (0, _stringify.stringify)(body);
73
+
74
+ // If body is null, undefined, or an empty object or array after stringifying, send an empty string instead.
75
+ if (!stringBody || stringBody.length === 0 || stringBody === '{}' || stringBody === '[]') stringBody = '';
76
+
77
+ // Warn--once per endpoint--if the agent tries to send large payloads
78
+ if (endpoint !== _features.BLOBS && stringBody.length > 750000 && (warnings[endpoint] = (warnings[endpoint] || 0) + 1) === 1) (0, _console.warn)(28, endpoint);
79
+ const headers = [{
80
+ key: 'content-type',
81
+ value: 'text/plain'
82
+ }];
83
+
84
+ /* Since workers don't support sendBeacon right now, they can only use XHR method.
85
+ Because they still do permit synch XHR, the idea is that at final harvest time (worker is closing),
86
+ we just make a BLOCKING request--trivial impact--with the remaining data as a temp fill-in for sendBeacon.
87
+ Following the removal of img-element method. */
88
+ let result = submitMethod({
89
+ url: fullUrl,
90
+ body: stringBody,
91
+ sync: localOpts.isFinalHarvest && _runtime.isWorkerScope,
92
+ headers
93
+ });
94
+ if (!localOpts.isFinalHarvest && cbFinished) {
95
+ // final harvests don't hold onto buffer data (shouldRetryOnFail is false), so cleanup isn't needed
96
+ if (submitMethod === _submitData.xhr) {
97
+ result.addEventListener('loadend', function () {
98
+ // `this` here in block refers to the XHR object in this scope, do not change the anon function to an arrow function
99
+ // status 0 refers to a local error, such as CORS or network failure, or a blocked request by the browser (e.g. adblocker)
100
+ const cbResult = {
101
+ sent: this.status !== 0,
102
+ status: this.status,
103
+ retry: shouldRetry(this.status),
104
+ fullUrl,
105
+ xhr: this,
106
+ responseText: this.responseText
107
+ };
108
+ cbFinished(cbResult);
109
+
110
+ /** temporary audit of consistency of harvest metadata flags */
111
+ if (!shouldRetry(this.status)) trackHarvestMetadata();
112
+ }, (0, _eventListenerOpts.eventListenerOpts)(false));
113
+ } else if (submitMethod === _submitData.xhrFetch) {
114
+ result.then(async function (response) {
115
+ const status = response.status;
116
+ const cbResult = {
117
+ sent: true,
118
+ status,
119
+ retry: shouldRetry(status),
120
+ fullUrl,
121
+ fetchResponse: response,
122
+ responseText: await response.text()
123
+ };
124
+ cbFinished(cbResult);
125
+ /** temporary audit of consistency of harvest metadata flags */
126
+ if (!shouldRetry(status)) trackHarvestMetadata();
127
+ });
128
+ }
129
+ function trackHarvestMetadata() {
130
+ try {
131
+ if (featureName === _features.FEATURE_NAMES.jserrors && !body?.err) return;
132
+ const hasReplay = baseParams.includes('hr=1');
133
+ const hasTrace = baseParams.includes('ht=1');
134
+ const hasError = qs?.attributes?.includes('hasError=true');
135
+ (0, _handle.handle)('harvest-metadata', [{
136
+ [featureName]: {
137
+ ...(hasReplay && {
138
+ hasReplay
139
+ }),
140
+ ...(hasTrace && {
141
+ hasTrace
142
+ }),
143
+ ...(hasError && {
144
+ hasError
145
+ })
146
+ }
147
+ }], undefined, _features.FEATURE_NAMES.metrics, agentRef.ee);
148
+ } catch (err) {
149
+ // do nothing
150
+ }
151
+ }
152
+ }
153
+ (0, _globalEvent.dispatchGlobalEvent)({
154
+ drained: !!agentRef.runtime?.activatedFeatures,
155
+ type: 'data',
156
+ name: 'harvest',
157
+ feature: featureName,
158
+ data: {
159
+ endpoint,
160
+ headers,
161
+ payload,
162
+ submitMethod: getSubmitMethodName(),
163
+ raw,
164
+ synchronousXhr: !!(localOpts.isFinalHarvest && _runtime.isWorkerScope)
165
+ }
166
+ });
167
+ return true;
168
+ function shouldRetry(status) {
169
+ switch (status) {
170
+ case 408:
171
+ case 429:
172
+ case 500:
173
+ return true;
174
+ }
175
+ return status >= 502 && status <= 504 || status >= 512 && status <= 530;
176
+ }
177
+ function getSubmitMethodName() {
178
+ if (submitMethod === _submitData.xhr) return 'xhr';
179
+ if (submitMethod === _submitData.xhrFetch) return 'fetch';
180
+ return 'beacon';
181
+ }
182
+ }
183
+
184
+ /**
185
+ * Cleans and returns a payload object containing a body and qs
186
+ * object with key/value pairs. KV pairs where the value is null,
187
+ * undefined, or an empty string are removed to save on transmission
188
+ * size.
189
+ * @param {HarvestPayload} payload Payload to be sent to the endpoint.
190
+ * @returns {HarvestPayload} Cleaned payload payload to be sent to the endpoint.
191
+ */
192
+ function cleanPayload(payload = {}) {
193
+ const clean = input => {
194
+ if (typeof Uint8Array !== 'undefined' && input instanceof Uint8Array || Array.isArray(input)) return input;
195
+ if (typeof input === 'string') return input;
196
+ return Object.entries(input || {}).reduce((accumulator, [key, value]) => {
197
+ if (typeof value === 'number' || typeof value === 'string' && value.length > 0 || typeof value === 'object' && Object.keys(value || {}).length > 0) {
198
+ accumulator[key] = value;
199
+ }
200
+ return accumulator;
201
+ }, {});
202
+ };
203
+ return {
204
+ body: clean(payload.body),
205
+ qs: clean(payload.qs)
206
+ };
207
+ }
208
+
209
+ // The stuff that gets sent every time.
210
+ function baseQueryString(agentRef, qs, endpoint, harvesterObfuscator) {
211
+ const cleanedURL = (0, _cleanUrl.cleanURL)('' + _runtime.globalScope.location);
212
+ const ref = harvesterObfuscator?.obfuscateString(cleanedURL) ?? cleanedURL;
213
+ const session = agentRef.runtime.session;
214
+ const hr = !!session?.state.sessionReplaySentFirstChunk && session?.state.sessionReplayMode === 1 && endpoint !== _features.JSERRORS;
215
+ const ht = !!session?.state.traceHarvestStarted && session?.state.sessionTraceMode === 1 && ![_features.LOGS, _features.BLOBS].includes(endpoint);
216
+ const qps = ['a=' + agentRef.info.applicationID, (0, _encode.param)('sa', agentRef.info.sa ? '' + agentRef.info.sa : ''), (0, _encode.param)('v', _env.VERSION), transactionNameParam(), (0, _encode.param)('ct', agentRef.runtime.customTransaction), '&rst=' + (0, _now.now)(), '&ck=0',
217
+ // ck param DEPRECATED - still expected by backend
218
+ '&s=' + (session?.state.value || '0'),
219
+ // the 0 id encaps all untrackable and default traffic
220
+ (0, _encode.param)('ref', ref), (0, _encode.param)('ptid', agentRef.runtime.ptid ? '' + agentRef.runtime.ptid : '')];
221
+ if (hr) qps.push((0, _encode.param)('hr', '1', qs));
222
+ if (ht) qps.push((0, _encode.param)('ht', '1', qs));
223
+ return qps.join('');
224
+
225
+ // Constructs the transaction name param for the beacon URL.
226
+ // Prefers the obfuscated transaction name over the plain text.
227
+ // Falls back to making up a name.
228
+ function transactionNameParam() {
229
+ if (agentRef.info.transactionName) return (0, _encode.param)('to', agentRef.info.transactionName);
230
+ return (0, _encode.param)('t', agentRef.info.tNamePlain || 'Unnamed Transaction');
231
+ }
232
+ }
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.unused = void 0;
7
7
  /**
8
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
8
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
9
9
  * SPDX-License-Identifier: Apache-2.0
10
10
  */
11
11
 
@@ -29,11 +29,15 @@ exports.unused = void 0;
29
29
  * @typedef {object} NetworkSendSpec
30
30
  * @property {HarvestEndpointIdentifier} endpoint The endpoint to use (jserrors, events, resources etc.)
31
31
  * @property {HarvestPayload} payload Object representing payload.
32
- * @property {object} localOpts Additional options for sending data
33
- * @property {boolean} localOpts.isFinalHarvest Specify whether the call is a final harvest during page unload.
34
- * @property {boolean} localOpts.sendEmptyBody Specify whether the call should be made even if the body is empty. Useful for rum calls.
35
- * @property {boolean} localOpts.forceNoRetry Don't save the buffered data in the case of a need to retry the transmission.
32
+ * @property {object} [localOpts] Additional options for sending data
33
+ * @property {boolean} [localOpts.isFinalHarvest] Specify whether the call is a final harvest during page unload.
34
+ * @property {boolean} [localOpts.sendEmptyBody] Specify whether the call should be made even if the body is empty. Useful for rum calls.
35
+ * @property {boolean} [localOpts.forceNoRetry] Don't save the buffered data in the case of a need to retry the transmission.
36
36
  * @property {import('../util/submit-data.js').NetworkMethods} submitMethod The network method to use {@link ../util/submit-data.js}
37
+ * @property {function(object): void} [cbFinished] Callback invoked with the result once the network request completes. Receives an object with sent, status, retry, fullUrl, responseText, and either xhr or fetchResponse.
38
+ * @property {boolean} [raw] If true, skips standard versioned URL path construction and uses a bare endpoint URL.
39
+ * @property {string} [featureName] The feature name associated with this harvest, used for metadata tracking and the global harvest event.
40
+ * @property {number} [endpointVersion] The endpoint version included in the URL path. Defaults to 1.
37
41
  */
38
42
 
39
43
  const unused = exports.unused = {};