@newrelic/browser-agent 1.302.0 → 1.303.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 (122) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cjs/common/config/init-types.js +2 -0
  3. package/dist/cjs/common/config/init.js +3 -0
  4. package/dist/cjs/common/config/runtime.js +9 -0
  5. package/dist/cjs/common/constants/env.cdn.js +1 -1
  6. package/dist/cjs/common/constants/env.npm.js +1 -1
  7. package/dist/cjs/common/harvest/harvester.js +13 -9
  8. package/dist/cjs/common/harvest/types.js +0 -1
  9. package/dist/cjs/common/session/session-entity.js +4 -2
  10. package/dist/cjs/common/util/mfe.js +8 -5
  11. package/dist/cjs/common/wrap/wrap-promise.js +10 -5
  12. package/dist/cjs/features/generic_events/aggregate/index.js +4 -4
  13. package/dist/cjs/features/logging/aggregate/index.js +1 -2
  14. package/dist/cjs/features/page_view_event/aggregate/index.js +84 -22
  15. package/dist/cjs/features/page_view_event/instrument/index.js +0 -4
  16. package/dist/cjs/features/session_replay/aggregate/index.js +3 -2
  17. package/dist/cjs/features/session_replay/constants.js +2 -6
  18. package/dist/cjs/features/session_replay/instrument/index.js +3 -2
  19. package/dist/cjs/features/utils/agent-session.js +5 -0
  20. package/dist/cjs/features/utils/instrument-base.js +7 -8
  21. package/dist/cjs/interfaces/registered-entity.js +21 -0
  22. package/dist/cjs/loaders/agent.js +2 -0
  23. package/dist/cjs/loaders/api/consent.js +37 -0
  24. package/dist/cjs/loaders/api/constants.js +3 -2
  25. package/dist/cjs/loaders/api/measure.js +36 -35
  26. package/dist/cjs/loaders/api/recordCustomEvent.js +5 -3
  27. package/dist/cjs/loaders/api/register-api-types.js +14 -10
  28. package/dist/cjs/loaders/api/register.js +25 -10
  29. package/dist/cjs/loaders/api-base.js +14 -7
  30. package/dist/esm/common/config/init-types.js +2 -0
  31. package/dist/esm/common/config/init.js +3 -0
  32. package/dist/esm/common/config/runtime.js +9 -0
  33. package/dist/esm/common/constants/env.cdn.js +1 -1
  34. package/dist/esm/common/constants/env.npm.js +1 -1
  35. package/dist/esm/common/harvest/harvester.js +13 -9
  36. package/dist/esm/common/harvest/types.js +0 -1
  37. package/dist/esm/common/session/session-entity.js +4 -2
  38. package/dist/esm/common/util/mfe.js +7 -5
  39. package/dist/esm/common/wrap/wrap-promise.js +10 -5
  40. package/dist/esm/features/generic_events/aggregate/index.js +4 -4
  41. package/dist/esm/features/logging/aggregate/index.js +1 -2
  42. package/dist/esm/features/page_view_event/aggregate/index.js +84 -22
  43. package/dist/esm/features/page_view_event/instrument/index.js +0 -4
  44. package/dist/esm/features/session_replay/aggregate/index.js +4 -3
  45. package/dist/esm/features/session_replay/constants.js +1 -5
  46. package/dist/esm/features/session_replay/instrument/index.js +4 -3
  47. package/dist/esm/features/utils/agent-session.js +5 -0
  48. package/dist/esm/features/utils/instrument-base.js +7 -8
  49. package/dist/esm/interfaces/registered-entity.js +21 -0
  50. package/dist/esm/loaders/agent.js +2 -0
  51. package/dist/esm/loaders/api/consent.js +30 -0
  52. package/dist/esm/loaders/api/constants.js +2 -1
  53. package/dist/esm/loaders/api/measure.js +35 -35
  54. package/dist/esm/loaders/api/recordCustomEvent.js +4 -3
  55. package/dist/esm/loaders/api/register-api-types.js +14 -10
  56. package/dist/esm/loaders/api/register.js +26 -10
  57. package/dist/esm/loaders/api-base.js +15 -8
  58. package/dist/tsconfig.tsbuildinfo +1 -1
  59. package/dist/types/common/config/init-types.d.ts +6 -0
  60. package/dist/types/common/config/init.d.ts.map +1 -1
  61. package/dist/types/common/config/runtime.d.ts.map +1 -1
  62. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  63. package/dist/types/common/harvest/types.d.ts +0 -2
  64. package/dist/types/common/harvest/types.d.ts.map +1 -1
  65. package/dist/types/common/session/session-entity.d.ts.map +1 -1
  66. package/dist/types/common/util/mfe.d.ts +3 -0
  67. package/dist/types/common/util/mfe.d.ts.map +1 -1
  68. package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
  69. package/dist/types/features/page_view_event/aggregate/index.d.ts +22 -3
  70. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  71. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  72. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  73. package/dist/types/features/session_replay/constants.d.ts +1 -5
  74. package/dist/types/features/session_replay/constants.d.ts.map +1 -1
  75. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  76. package/dist/types/features/utils/agent-session.d.ts.map +1 -1
  77. package/dist/types/features/utils/instrument-base.d.ts +1 -0
  78. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  79. package/dist/types/interfaces/registered-entity.d.ts +25 -0
  80. package/dist/types/interfaces/registered-entity.d.ts.map +1 -1
  81. package/dist/types/loaders/agent.d.ts.map +1 -1
  82. package/dist/types/loaders/api/consent.d.ts +2 -0
  83. package/dist/types/loaders/api/consent.d.ts.map +1 -0
  84. package/dist/types/loaders/api/constants.d.ts +1 -0
  85. package/dist/types/loaders/api/constants.d.ts.map +1 -1
  86. package/dist/types/loaders/api/measure.d.ts +3 -0
  87. package/dist/types/loaders/api/measure.d.ts.map +1 -1
  88. package/dist/types/loaders/api/recordCustomEvent.d.ts +1 -0
  89. package/dist/types/loaders/api/recordCustomEvent.d.ts.map +1 -1
  90. package/dist/types/loaders/api/register-api-types.d.ts +43 -12
  91. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
  92. package/dist/types/loaders/api/register.d.ts +0 -16
  93. package/dist/types/loaders/api/register.d.ts.map +1 -1
  94. package/dist/types/loaders/api-base.d.ts +20 -15
  95. package/dist/types/loaders/api-base.d.ts.map +1 -1
  96. package/package.json +3 -3
  97. package/src/common/config/init-types.js +2 -0
  98. package/src/common/config/init.js +1 -0
  99. package/src/common/config/runtime.js +10 -0
  100. package/src/common/harvest/harvester.js +11 -8
  101. package/src/common/harvest/types.js +0 -1
  102. package/src/common/session/session-entity.js +6 -2
  103. package/src/common/util/mfe.js +10 -4
  104. package/src/common/wrap/wrap-promise.js +16 -6
  105. package/src/features/generic_events/aggregate/index.js +4 -4
  106. package/src/features/logging/aggregate/index.js +1 -1
  107. package/src/features/page_view_event/aggregate/index.js +79 -15
  108. package/src/features/page_view_event/instrument/index.js +0 -4
  109. package/src/features/session_replay/aggregate/index.js +4 -3
  110. package/src/features/session_replay/constants.js +1 -5
  111. package/src/features/session_replay/instrument/index.js +4 -3
  112. package/src/features/utils/agent-session.js +4 -0
  113. package/src/features/utils/instrument-base.js +7 -9
  114. package/src/interfaces/registered-entity.js +21 -0
  115. package/src/loaders/agent.js +2 -0
  116. package/src/loaders/api/consent.js +31 -0
  117. package/src/loaders/api/constants.js +1 -0
  118. package/src/loaders/api/measure.js +34 -33
  119. package/src/loaders/api/recordCustomEvent.js +5 -3
  120. package/src/loaders/api/register-api-types.js +14 -10
  121. package/src/loaders/api/register.js +17 -10
  122. package/src/loaders/api-base.js +15 -8
package/CHANGELOG.md CHANGED
@@ -3,6 +3,26 @@
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.303.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.302.0...v1.303.0) (2025-11-13)
7
+
8
+
9
+ ### Features
10
+
11
+ * Add custom event support to register API ([#1606](https://github.com/newrelic/newrelic-browser-agent/issues/1606)) ([4137dcb](https://github.com/newrelic/newrelic-browser-agent/commit/4137dcbb339642219164d68695b84a8e48fa9b6f))
12
+ * Add measure support to register API ([#1623](https://github.com/newrelic/newrelic-browser-agent/issues/1623)) ([c2785f3](https://github.com/newrelic/newrelic-browser-agent/commit/c2785f39ca9ea1f5b87f3e2cf4fc7e462d6084e7))
13
+ * Add consent API ([#1533](https://github.com/newrelic/newrelic-browser-agent/issues/1533)) ([4e8ad2a](https://github.com/newrelic/newrelic-browser-agent/commit/4e8ad2a6aea34bbaa39b6beb67c3947feee3c01d))
14
+ * Additional validation to prepare agent for MFE registrations ([#1625](https://github.com/newrelic/newrelic-browser-agent/issues/1625)) ([aa8c02f](https://github.com/newrelic/newrelic-browser-agent/commit/aa8c02f966e9c7df5c4e83c911f9342b2183447f))
15
+ * Allow consent API to be invoked without localStorage access ([#1627](https://github.com/newrelic/newrelic-browser-agent/issues/1627)) ([0690a65](https://github.com/newrelic/newrelic-browser-agent/commit/0690a65bcfa35eabe0e83b13c058398746e924b8))
16
+ * Allow nested registrations ([#1616](https://github.com/newrelic/newrelic-browser-agent/issues/1616)) ([74a8d4a](https://github.com/newrelic/newrelic-browser-agent/commit/74a8d4ac745cff33eb15f9469775f85fd2641097))
17
+ * Retry initial connect call ([#1605](https://github.com/newrelic/newrelic-browser-agent/issues/1605)) ([9770132](https://github.com/newrelic/newrelic-browser-agent/commit/9770132209b9fb1320b3f60eedfa932b0fcfa3a4))
18
+ * SMs for browser connect response ([#1611](https://github.com/newrelic/newrelic-browser-agent/issues/1611)) ([29f5bdf](https://github.com/newrelic/newrelic-browser-agent/commit/29f5bdf6fa36020a87e5c0aa443d64ce55722c85))
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * memoize promise context propagation to avoid safari hangs ([#1597](https://github.com/newrelic/newrelic-browser-agent/issues/1597)) ([23cb559](https://github.com/newrelic/newrelic-browser-agent/commit/23cb55993add2eb7e7b14b0d77c6fa3bbb9abcf1))
24
+ * Obfuscate custom attributes for logs added after PVE ([#1622](https://github.com/newrelic/newrelic-browser-agent/issues/1622)) ([f648e3f](https://github.com/newrelic/newrelic-browser-agent/commit/f648e3f1f843913d4c9be6534b0d1e3ba47eb260))
25
+
6
26
  ## [1.302.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.301.0...v1.302.0) (2025-10-24)
7
27
 
8
28
 
@@ -89,6 +89,8 @@ exports.default = void 0;
89
89
  * @property {boolean} [spa.enabled] - Turn on/off the single page application feature (on by default). NOTE: the SPA feature is deprecated and under removal procedure.
90
90
  * @property {boolean} [spa.autoStart] - If true, the agent will automatically start the single page application feature. Otherwise, it will be in a deferred state until the `start` API method is called.
91
91
  * @property {boolean} [ssl] - If explicitly false, the agent will use HTTP instead of HTTPS. This setting should NOT be used.
92
+ * @property {Object} [browser_consent_mode]
93
+ * @property {boolean} [browser_consent_mode.enabled] - If true, the agent will use consent mode for whether to allow or disallow data harvest.
92
94
  * @property {Object} [user_actions]
93
95
  * @property {boolean} [user_actions.enabled] - Must be true to allow UserAction events to be captured.
94
96
  * @property {Array<string>} [user_actions.elementAttributes] - List of HTML Element properties to be captured with UserAction events' target elements. This may help to identify the source element being interacted with in the UI.
@@ -68,6 +68,9 @@ const InitModelFn = () => {
68
68
  },
69
69
  duplicate_registered_data: false
70
70
  },
71
+ browser_consent_mode: {
72
+ enabled: false
73
+ },
71
74
  distributed_tracing: {
72
75
  enabled: undefined,
73
76
  exclude_newrelic_header: undefined,
@@ -23,9 +23,18 @@ const ReadOnly = {
23
23
  version: _env.VERSION,
24
24
  originTime: _runtime.originTime
25
25
  };
26
+ const hiddenState = {
27
+ consented: false
28
+ };
26
29
  const RuntimeModel = {
27
30
  /** Agent-specific metadata found in the RUM call response. ex. entityGuid */
28
31
  appMetadata: {},
32
+ get consented() {
33
+ return this.session?.state?.consent || hiddenState.consented;
34
+ },
35
+ set consented(value) {
36
+ hiddenState.consented = value;
37
+ },
29
38
  customTransaction: undefined,
30
39
  denyList: undefined,
31
40
  disabled: false,
@@ -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.302.0";
20
+ const VERSION = exports.VERSION = "1.303.0-rc.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -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.302.0";
20
+ const VERSION = exports.VERSION = "1.303.0-rc.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -25,8 +25,10 @@ var _globalEvent = require("../dispatch/global-event");
25
25
  * SPDX-License-Identifier: Apache-2.0
26
26
  */
27
27
 
28
- const RETRY_FAILED = 'Harvester/Retry/Failed/';
29
- const RETRY_SUCCEEDED = 'Harvester/Retry/Succeeded/';
28
+ const RETRY = 'Harvester/Retry/';
29
+ const RETRY_ATTEMPTED = RETRY + 'Attempted/';
30
+ const RETRY_FAILED = RETRY + 'Failed/';
31
+ const RETRY_SUCCEEDED = RETRY + 'Succeeded/';
30
32
  class Harvester {
31
33
  #started = false;
32
34
  initializedAggregates = [];
@@ -67,11 +69,11 @@ class Harvester {
67
69
  endpointVersion: aggregateInst.harvestEndpointVersion || 1
68
70
  };
69
71
  if (aggregateInst.blocked) return output;
72
+ if (this.agentRef.init?.browser_consent_mode?.enabled && !this.agentRef.runtime.consented) return output;
70
73
  const submitMethod = (0, _submitData.getSubmitMethod)(localOpts);
71
74
  if (!submitMethod) return output;
72
75
  const shouldRetryOnFail = !localOpts.isFinalHarvest && submitMethod === _submitData.xhr; // always retry all features harvests except for final
73
- output.payload = !localOpts.directSend ? aggregateInst.makeHarvestPayload(shouldRetryOnFail, localOpts) : localOpts.directSend?.payload; // features like PVE can define the payload directly, bypassing the makeHarvestPayload logic
74
-
76
+ output.payload = aggregateInst.makeHarvestPayload(shouldRetryOnFail, localOpts);
75
77
  if (!output.payload) return output;
76
78
  send(this.agentRef, {
77
79
  endpoint: _features.FEATURE_TO_ENDPOINT[aggregateInst.featureName],
@@ -93,7 +95,9 @@ class Harvester {
93
95
  function cbFinished(result) {
94
96
  if (aggregateInst.harvestOpts.prevAttemptCode) {
95
97
  // this means we just retried a harvest that last failed
96
- (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [(result.retry ? RETRY_FAILED : RETRY_SUCCEEDED) + aggregateInst.harvestOpts.prevAttemptCode], undefined, _features.FEATURE_NAMES.metrics, aggregateInst.ee);
98
+ const reportSM = message => (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [message], undefined, _features.FEATURE_NAMES.metrics, aggregateInst.ee);
99
+ reportSM(RETRY_ATTEMPTED + aggregateInst.featureName);
100
+ reportSM((result.retry ? RETRY_FAILED : RETRY_SUCCEEDED) + aggregateInst.harvestOpts.prevAttemptCode);
97
101
  delete aggregateInst.harvestOpts.prevAttemptCode; // always reset last observation so we don't falsely report again next harvest
98
102
  // In case this re-attempt failed again, that'll be handled (re-marked again) next.
99
103
  }
@@ -183,9 +187,9 @@ function send(agentRef, {
183
187
  status: this.status,
184
188
  retry: shouldRetry(this.status),
185
189
  fullUrl,
186
- xhr: this
190
+ xhr: this,
191
+ responseText: this.responseText
187
192
  };
188
- if (localOpts.needResponse) cbResult.responseText = this.responseText;
189
193
  cbFinished(cbResult);
190
194
 
191
195
  /** temporary audit of consistency of harvest metadata flags */
@@ -199,9 +203,9 @@ function send(agentRef, {
199
203
  status,
200
204
  retry: shouldRetry(status),
201
205
  fullUrl,
202
- fetchResponse: response
206
+ fetchResponse: response,
207
+ responseText: await response.text()
203
208
  };
204
- if (localOpts.needResponse) cbResult.responseText = await response.text();
205
209
  cbFinished(cbResult);
206
210
  /** temporary audit of consistency of harvest metadata flags */
207
211
  if (!shouldRetry(status)) trackHarvestMetadata();
@@ -30,7 +30,6 @@ exports.unused = void 0;
30
30
  * @property {HarvestEndpointIdentifier} endpoint The endpoint to use (jserrors, events, resources etc.)
31
31
  * @property {HarvestPayload} payload Object representing payload.
32
32
  * @property {object} localOpts Additional options for sending data
33
- * @property {boolean} localOpts.needResponse Specify whether the caller expects a response data.
34
33
  * @property {boolean} localOpts.isFinalHarvest Specify whether the call is a final harvest during page unload.
35
34
  * @property {boolean} localOpts.sendEmptyBody Specify whether the call should be made even if the body is empty. Useful for rum calls.
36
35
  * @property {boolean} localOpts.forceNoRetry Don't save the buffered data in the case of a need to retry the transmission.
@@ -39,7 +39,8 @@ const model = {
39
39
  serverTimeDiff: null,
40
40
  // set by TimeKeeper; "undefined" value will not be stringified and stored but "null" will
41
41
  custom: {},
42
- numOfResets: 0
42
+ numOfResets: 0,
43
+ consent: false // set by consent() API call
43
44
  };
44
45
  class SessionEntity {
45
46
  /**
@@ -90,7 +91,8 @@ class SessionEntity {
90
91
  }) {
91
92
  /** Ensure that certain properties are preserved across a reset if already set */
92
93
  const persistentAttributes = {
93
- serverTimeDiff: this.state.serverTimeDiff || model.serverTimeDiff
94
+ serverTimeDiff: this.state.serverTimeDiff || model.serverTimeDiff,
95
+ consent: this.state.consent || model.consent
94
96
  };
95
97
  this.state = {};
96
98
  this.sync({
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.getVersion2Attributes = getVersion2Attributes;
7
+ exports.hasValidValue = hasValidValue;
7
8
  exports.isValidMFETarget = isValidMFETarget;
8
9
  /**
9
10
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
@@ -17,9 +18,14 @@ exports.isValidMFETarget = isValidMFETarget;
17
18
  function isValidMFETarget(target = {}) {
18
19
  return !!(target.id && target.name);
19
20
  }
21
+ function hasValidValue(val) {
22
+ return typeof val === 'string' && val.trim().length < 501 || typeof val === 'number';
23
+ }
20
24
 
21
25
  /**
22
26
  * When given a valid target, returns an object with the MFE payload attributes. Returns an empty object otherwise.
27
+ * @note Field names may change as the schema is finalized
28
+ *
23
29
  * @param {Object} [target] the registered target
24
30
  * @param {AggregateInstance} [aggregateInstance] the aggregate instance calling the method
25
31
  * @returns {{'mfe.id': *, 'mfe.name': String}|{}} returns an empty object if args are not supplied or the aggregate instance is not supporting version 2
@@ -35,11 +41,8 @@ function getVersion2Attributes(target, aggregateInstance) {
35
41
  }
36
42
  return {
37
43
  'mfe.id': target.id,
38
- // these field names may change as the schema is finalized
39
44
  'mfe.name': target.name,
40
- // these field names may change as the schema is finalized
41
- eventSource: 'MicroFrontendBrowserAgent',
42
- // these field names may change as the schema is finalized
43
- 'parent.id': containerAgentEntityGuid
45
+ eventSource: target.eventSource,
46
+ 'parent.id': target.parent?.id || containerAgentEntityGuid
44
47
  };
45
48
  }
@@ -136,12 +136,17 @@ function wrapPromise(sharedEE) {
136
136
  });
137
137
  promiseEE.on('propagate', function (val, overwrite, trigger) {
138
138
  if (!this.getCtx || overwrite) {
139
- this.getCtx = function () {
140
- // eslint-disable-next-line
141
- if (val instanceof Promise) {
142
- var store = promiseEE.context(val);
139
+ const selfStore = this;
140
+ const parentStore = val instanceof Promise ? promiseEE.context(val) : null;
141
+ let cachedCtx;
142
+ this.getCtx = function getCtx() {
143
+ if (cachedCtx) return cachedCtx;
144
+ if (parentStore && parentStore !== selfStore) {
145
+ cachedCtx = typeof parentStore.getCtx === 'function' ? parentStore.getCtx() : parentStore;
146
+ } else {
147
+ cachedCtx = selfStore;
143
148
  }
144
- return store && store.getCtx ? store.getCtx() : this;
149
+ return cachedCtx;
145
150
  };
146
151
  }
147
152
  });
@@ -35,13 +35,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
35
35
  return;
36
36
  }
37
37
  this.#trackSupportabilityMetrics();
38
- (0, _registerHandler.registerHandler)('api-recordCustomEvent', (timestamp, eventType, attributes) => {
38
+ (0, _registerHandler.registerHandler)('api-recordCustomEvent', (timestamp, eventType, attributes, target) => {
39
39
  if (_constants.RESERVED_EVENT_TYPES.includes(eventType)) return (0, _console.warn)(46);
40
40
  this.addEvent({
41
41
  eventType,
42
42
  timestamp: this.toEpoch(timestamp),
43
43
  ...attributes
44
- });
44
+ }, target);
45
45
  }, this.featureName, this.ee);
46
46
  if (agentRef.init.page_action.enabled) {
47
47
  (0, _registerHandler.registerHandler)('api-addPageAction', (timestamp, name, attributes, target) => {
@@ -238,7 +238,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
238
238
  }
239
239
  }, this.featureName, this.ee);
240
240
  }
241
- (0, _registerHandler.registerHandler)('api-measure', (args, n) => {
241
+ (0, _registerHandler.registerHandler)('api-measure', (args, n, target) => {
242
242
  const {
243
243
  start,
244
244
  duration,
@@ -252,7 +252,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
252
252
  entryDuration: duration,
253
253
  entryType: 'measure'
254
254
  };
255
- this.addEvent(event);
255
+ this.addEvent(event, target);
256
256
  }, this.featureName, this.ee);
257
257
  this.drain();
258
258
  });
@@ -104,8 +104,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
104
104
  common: {
105
105
  /** Attributes in the `common` section are added to `all` logs generated in the payload */
106
106
  attributes: {
107
- ...this.agentRef.info.jsAttributes,
108
- // user-provided custom attributes
107
+ ...(0, _traverse.applyFnToProps)(this.agentRef.info.jsAttributes, this.obfuscator.obfuscateString.bind(this.obfuscator), 'string'),
109
108
  ...(this.harvestEndpointVersion === 1 && {
110
109
  'entity.guid': this.agentRef.runtime.appMetadata.agents[0].entityGuid,
111
110
  appId: this.agentRef.info.applicationID
@@ -19,6 +19,9 @@ var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
19
19
  var _now = require("../../../common/timing/now");
20
20
  var _timeKeeper = require("../../../common/timing/time-keeper");
21
21
  var _traverse = require("../../../common/util/traverse");
22
+ var _harvester = require("../../../common/harvest/harvester");
23
+ var _features = require("../../../loaders/features/features");
24
+ var _submitData = require("../../../common/util/submit-data");
22
25
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
23
26
  /**
24
27
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
@@ -29,10 +32,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
29
32
  static featureName = CONSTANTS.FEATURE_NAME;
30
33
  constructor(agentRef) {
31
34
  super(agentRef, CONSTANTS.FEATURE_NAME);
35
+ this.sentRum = false; // flag to facilitate calling sendRum() once externally (by the consent API in agent-session.js)
36
+
32
37
  this.timeToFirstByte = 0;
33
38
  this.firstByteToWindowLoad = 0; // our "frontend" duration
34
39
  this.firstByteToDomContent = 0; // our "dom processing" duration
35
-
40
+ this.retries = 0;
36
41
  if (!(0, _info.isValid)(agentRef.info)) {
37
42
  this.ee.abort();
38
43
  return (0, _console.warn)(43);
@@ -60,12 +65,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
60
65
  *
61
66
  * @param {Function} cb A function to run once the RUM call has finished - Defaults to activateFeatures
62
67
  * @param {*} customAttributes custom attributes to attach to the RUM call - Defaults to info.js
63
- * @param {*} target The target to harvest to
64
68
  */
65
- sendRum(customAttributes = this.agentRef.info.jsAttributes, target = {
66
- licenseKey: this.agentRef.info.licenseKey,
67
- applicationID: this.agentRef.info.applicationID
68
- }) {
69
+ sendRum(customAttributes = this.agentRef.info.jsAttributes) {
69
70
  const info = this.agentRef.info;
70
71
  const measures = {};
71
72
  if (info.queueTime) measures.qt = info.queueTime;
@@ -116,27 +117,30 @@ class Aggregate extends _aggregateBase.AggregateBase {
116
117
  }
117
118
  queryParameters.fp = _firstPaint.firstPaint.current.value;
118
119
  queryParameters.fcp = _firstContentfulPaint.firstContentfulPaint.current.value;
119
- const timeKeeper = this.agentRef.runtime.timeKeeper;
120
- if (timeKeeper?.ready) {
121
- queryParameters.timestamp = Math.floor(timeKeeper.correctRelativeTimestamp((0, _now.now)()));
122
- }
123
- this.rumStartTime = (0, _now.now)();
124
- this.agentRef.runtime.harvester.triggerHarvestFor(this, {
125
- directSend: {
126
- target,
127
- payload: {
128
- qs: queryParameters,
129
- body
130
- }
131
- },
132
- needResponse: true,
120
+ this.queryStringsBuilder = () => {
121
+ // this will be called by AggregateBase.makeHarvestPayload every time harvest is triggered to be qs
122
+ this.rumStartTime = (0, _now.now)(); // this should be reset at the beginning of each RUM call for proper timeKeeper calculation in coordination with postHarvestCleanup
123
+ const timeKeeper = this.agentRef.runtime.timeKeeper;
124
+ if (timeKeeper?.ready) {
125
+ queryParameters.timestamp = Math.floor(timeKeeper.correctRelativeTimestamp(this.rumStartTime));
126
+ }
127
+ return queryParameters;
128
+ };
129
+ this.events.add(body);
130
+ if (this.agentRef.runtime.harvester.triggerHarvestFor(this, {
133
131
  sendEmptyBody: true
134
- });
132
+ }).ranSend) this.sentRum = true;
133
+ }
134
+ serializer(eventBuffer) {
135
+ // this is necessary because PVE sends a single item rather than an array; in the case of undefined, this prevents sending [null] as body
136
+ return eventBuffer[0];
135
137
  }
136
138
  postHarvestCleanup({
139
+ sent,
137
140
  status,
138
141
  responseText,
139
- xhr
142
+ xhr,
143
+ retry
140
144
  }) {
141
145
  const rumEndTime = (0, _now.now)();
142
146
  let app, flags;
@@ -149,8 +153,65 @@ class Aggregate extends _aggregateBase.AggregateBase {
149
153
  // wont set entity stuff here, if main agent will later abort, if registered agent, nothing will happen
150
154
  (0, _console.warn)(53, error);
151
155
  }
156
+ super.postHarvestCleanup({
157
+ sent,
158
+ retry
159
+ }); // this will set isRetrying & re-buffer the body if request is to be retried
160
+ if (this.isRetrying && this.retries++ < 1) {
161
+ // Only retry once
162
+ setTimeout(() => this.agentRef.runtime.harvester.triggerHarvestFor(this, {
163
+ sendEmptyBody: true
164
+ }), 5000); // Retry sending the RUM event after 5 seconds
165
+ return;
166
+ }
152
167
  if (status >= 400 || status === 0) {
153
168
  (0, _console.warn)(18, status);
169
+ this.blocked = true;
170
+
171
+ // Get estimated payload size of our backlog
172
+ const textEncoder = new TextEncoder();
173
+ const payloadSize = Object.values(newrelic.ee.backlog).reduce((acc, value) => {
174
+ if (!value) return acc;
175
+ const encoded = textEncoder.encode(value);
176
+ return acc + encoded.byteLength;
177
+ }, 0);
178
+
179
+ // Send SMs about failed RUM request
180
+ const body = {
181
+ sm: [{
182
+ params: {
183
+ name: "Browser/Supportability/BCS/Error/".concat(status)
184
+ },
185
+ stats: {
186
+ c: 1
187
+ }
188
+ }, {
189
+ params: {
190
+ name: 'Browser/Supportability/BCS/Error/Dropped/Bytes'
191
+ },
192
+ stats: {
193
+ c: 1,
194
+ t: payloadSize
195
+ }
196
+ }, {
197
+ params: {
198
+ name: 'Browser/Supportability/BCS/Error/Duration/Ms'
199
+ },
200
+ stats: {
201
+ c: 1,
202
+ t: rumEndTime - this.rumStartTime
203
+ }
204
+ }]
205
+ };
206
+ (0, _harvester.send)(this.agentRef, {
207
+ endpoint: _features.FEATURE_TO_ENDPOINT[_features.FEATURE_NAMES.metrics],
208
+ payload: {
209
+ body
210
+ },
211
+ submitMethod: (0, _submitData.getSubmitMethod)(),
212
+ featureName: _features.FEATURE_NAMES.metrics
213
+ });
214
+
154
215
  // Adding retry logic for the rum call will be a separate change; this.blocked will need to be changed since that prevents another triggerHarvestFor()
155
216
  this.ee.abort();
156
217
  return;
@@ -169,6 +230,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
169
230
  }
170
231
  } catch (error) {
171
232
  this.ee.abort();
233
+ this.blocked = true;
172
234
  (0, _console.warn)(17, error);
173
235
  return;
174
236
  }
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.PageViewEvent = exports.Instrument = void 0;
7
- var _handle = require("../../../common/event-emitter/handle");
8
7
  var _setPageViewName = require("../../../loaders/api/setPageViewName");
9
8
  var _instrumentBase = require("../../utils/instrument-base");
10
9
  var CONSTANTS = _interopRequireWildcard(require("../constants"));
@@ -25,9 +24,6 @@ class Instrument extends _instrumentBase.InstrumentBase {
25
24
 
26
25
  /** feature specific APIs */
27
26
  (0, _setPageViewName.setupSetPageViewNameAPI)(agentRef);
28
-
29
- /** messages from the register API that can trigger a new RUM call */
30
- this.ee.on('api-send-rum', (attrs, target) => (0, _handle.handle)('send-rum', [attrs, target], undefined, this.featureName, this.ee));
31
27
  this.importAggregator(agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "page_view_event-aggregate" */'../aggregate'))));
32
28
  }
33
29
  setupInspectionEvents(agentIdentifier) {
@@ -19,6 +19,7 @@ var _now = require("../../../common/timing/now");
19
19
  var _agentConstants = require("../../../common/constants/agent-constants");
20
20
  var _cleanUrl = require("../../../common/url/clean-url");
21
21
  var _featureGates = require("../../utils/feature-gates");
22
+ var _constants3 = require("../../../loaders/api/constants");
22
23
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /**
23
24
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
24
25
  * SPDX-License-Identifier: Apache-2.0
@@ -76,10 +77,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
76
77
  if (this.mode !== _constants2.MODE.OFF && data.sessionReplayMode === _constants2.MODE.OFF) this.abort(_constants.ABORT_REASONS.CROSS_TAB);
77
78
  this.mode = data.sessionReplayMode;
78
79
  });
79
- (0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.PAUSE, () => {
80
+ (0, _registerHandler.registerHandler)(_constants3.PAUSE_REPLAY, () => {
80
81
  this.forceStop(this.mode === _constants2.MODE.FULL);
81
82
  }, this.featureName, this.ee);
82
- (0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, e => {
83
+ (0, _registerHandler.registerHandler)(_constants.ERROR_DURING_REPLAY, e => {
83
84
  this.handleError(e);
84
85
  }, this.featureName, this.ee);
85
86
  const {
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.TRIGGERS = exports.SR_EVENT_EMITTER_TYPES = exports.RRWEB_EVENT_TYPES = exports.QUERY_PARAM_PADDING = exports.FEATURE_NAME = exports.CHECKOUT_MS = exports.AVG_COMPRESSION = exports.ABORT_REASONS = void 0;
6
+ exports.TRIGGERS = exports.RRWEB_EVENT_TYPES = exports.QUERY_PARAM_PADDING = exports.FEATURE_NAME = exports.ERROR_DURING_REPLAY = exports.CHECKOUT_MS = exports.AVG_COMPRESSION = exports.ABORT_REASONS = void 0;
7
7
  var _constants = require("../../common/session/constants");
8
8
  var _features = require("../../loaders/features/features");
9
9
  /**
@@ -12,11 +12,7 @@ var _features = require("../../loaders/features/features");
12
12
  */
13
13
 
14
14
  const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionReplay;
15
- const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
16
- RECORD: 'recordReplay',
17
- PAUSE: 'pauseReplay',
18
- ERROR_DURING_REPLAY: 'errorDuringReplay'
19
- };
15
+ const ERROR_DURING_REPLAY = exports.ERROR_DURING_REPLAY = 'errorDuringReplay';
20
16
  const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
21
17
  const RRWEB_EVENT_TYPES = exports.RRWEB_EVENT_TYPES = {
22
18
  DomContentLoaded: 0,
@@ -11,6 +11,7 @@ var _utils = require("../shared/utils");
11
11
  var _constants2 = require("../constants");
12
12
  var _recordReplay = require("../../../loaders/api/recordReplay");
13
13
  var _pauseReplay = require("../../../loaders/api/pauseReplay");
14
+ var _constants3 = require("../../../loaders/api/constants");
14
15
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /**
15
16
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
16
17
  * SPDX-License-Identifier: Apache-2.0
@@ -34,7 +35,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
34
35
  session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
35
36
  } catch (err) {}
36
37
  if ((0, _utils.hasReplayPrerequisite)(agentRef.init)) {
37
- this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.RECORD, () => this.#apiStartOrRestartReplay());
38
+ this.ee.on(_constants3.RECORD_REPLAY, () => this.#apiStartOrRestartReplay());
38
39
  }
39
40
  if (this.#canPreloadRecorder(session)) {
40
41
  this.importRecorder().then(recorder => {
@@ -48,7 +49,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
48
49
  if (this.blocked) return;
49
50
  if (this.agentRef.runtime.isRecording) {
50
51
  this.errorNoticed = true;
51
- (0, _handle.handle)(_constants2.SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee);
52
+ (0, _handle.handle)(_constants2.ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee);
52
53
  }
53
54
  });
54
55
  }
@@ -59,6 +59,11 @@ function setupAgentSession(agentRef) {
59
59
  (0, _registerHandler.registerHandler)('api-setUserId', (time, key, value) => {
60
60
  agentRef.runtime.session.syncCustomAttribute(key, value);
61
61
  }, 'session', sharedEE);
62
+ (0, _registerHandler.registerHandler)('api-consent', accept => {
63
+ agentRef.runtime.session.write({
64
+ consent: accept === undefined ? true : accept
65
+ });
66
+ }, 'session', sharedEE);
62
67
  (0, _drain.drain)(agentRef.agentIdentifier, 'session');
63
68
  return agentRef.runtime.session;
64
69
  }
@@ -50,7 +50,10 @@ class InstrumentBase extends _featureBase.FeatureBase {
50
50
  * @type {Promise} Assigned immediately after @see importAggregator runs. Serves as a signal for when the inner async fn finishes execution. Useful for features to await
51
51
  * one another if there are inter-features dependencies.
52
52
  */
53
- this.onAggregateImported = undefined;
53
+ this.loadedSuccessfully = undefined;
54
+ this.onAggregateImported = new Promise(resolve => {
55
+ this.loadedSuccessfully = resolve;
56
+ });
54
57
 
55
58
  /**
56
59
  * used in conjunction with newrelic.start() to defer harvesting in features
@@ -82,10 +85,6 @@ class InstrumentBase extends _featureBase.FeatureBase {
82
85
  */
83
86
  importAggregator(agentRef, fetchAggregator, argsObjFromInstrument = {}) {
84
87
  if (this.featAggregate) return;
85
- let loadedSuccessfully;
86
- this.onAggregateImported = new Promise(resolve => {
87
- loadedSuccessfully = resolve;
88
- });
89
88
  const importLater = async () => {
90
89
  // wait for the deferred promise to resolve before proceeding
91
90
  // this will resolve immediately if the feature is auto-started,
@@ -113,7 +112,7 @@ class InstrumentBase extends _featureBase.FeatureBase {
113
112
  try {
114
113
  if (!this.#shouldImportAgg(this.featureName, session, agentRef.init)) {
115
114
  (0, _drain.drain)(this.agentIdentifier, this.featureName);
116
- loadedSuccessfully(false); // aggregate module isn't loaded at all
115
+ this.loadedSuccessfully(false); // aggregate module isn't loaded at all
117
116
  return;
118
117
  }
119
118
  const {
@@ -121,13 +120,13 @@ class InstrumentBase extends _featureBase.FeatureBase {
121
120
  } = await fetchAggregator();
122
121
  this.featAggregate = new Aggregate(agentRef, argsObjFromInstrument);
123
122
  agentRef.runtime.harvester.initializedAggregates.push(this.featAggregate); // "subscribe" the feature to future harvest intervals (PVE will start the timer)
124
- loadedSuccessfully(true);
123
+ this.loadedSuccessfully(true);
125
124
  } catch (e) {
126
125
  (0, _console.warn)(34, e);
127
126
  this.abortHandler?.(); // undo any important alterations made to the page
128
127
  // not supported yet but nice to do: "abort" this agent's EE for this feature specifically
129
128
  (0, _drain.drain)(this.agentIdentifier, this.featureName, true);
130
- loadedSuccessfully(false);
129
+ this.loadedSuccessfully(false);
131
130
  if (this.ee) this.ee.abort();
132
131
  }
133
132
  };
@@ -54,6 +54,27 @@ class RegisteredEntity {
54
54
  (0, _console.warn)(35, 'addPageAction');
55
55
  }
56
56
 
57
+ /**
58
+ * Records a custom event with a specified eventType and attributes.
59
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordCustomEvent/}
60
+ * @param {string} eventType The eventType to store the event as.
61
+ * @param {Object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}.
62
+ */
63
+ recordCustomEvent(eventType, attributes) {
64
+ (0, _console.warn)(35, 'recordCustomEvent');
65
+ }
66
+
67
+ /**
68
+ * Measures a task that is recorded as a BrowserPerformance event.
69
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
70
+ * @param {string} name The name of the task
71
+ * @param {{start: number, end: number, duration: number, customAttributes: object}} [options] An object used to control the way the measure API operates
72
+ * @returns {{start: number, end: number, duration: number, customAttributes: object}} Measurement details
73
+ */
74
+ measure(name, options) {
75
+ (0, _console.warn)(35, 'measure');
76
+ }
77
+
57
78
  /**
58
79
  * Adds a user-defined attribute name and value to subsequent events on the page for the registered target. Note -- the persist flag does not work with the register API.
59
80
  * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcustomattribute/}
@@ -19,6 +19,7 @@ var _setCustomAttribute = require("./api/setCustomAttribute");
19
19
  var _setUserId = require("./api/setUserId");
20
20
  var _setApplicationVersion = require("./api/setApplicationVersion");
21
21
  var _start = require("./api/start");
22
+ var _consent = require("./api/consent");
22
23
  /**
23
24
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
24
25
  * SPDX-License-Identifier: Apache-2.0
@@ -77,6 +78,7 @@ class Agent extends _agentBase.AgentBase {
77
78
  (0, _setUserId.setupSetUserIdAPI)(this);
78
79
  (0, _setApplicationVersion.setupSetApplicationVersionAPI)(this);
79
80
  (0, _start.setupStartAPI)(this);
81
+ (0, _consent.setupConsentAPI)(this);
80
82
  this.run();
81
83
  }
82
84
  get config() {