@newrelic/browser-agent 1.279.1 → 1.281.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 (68) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/cjs/common/config/init.js +2 -1
  3. package/dist/cjs/common/constants/env.cdn.js +1 -1
  4. package/dist/cjs/common/constants/env.npm.js +1 -1
  5. package/dist/cjs/common/dom/selector-path.js +20 -3
  6. package/dist/cjs/common/vitals/constants.js +2 -5
  7. package/dist/cjs/common/vitals/first-input-delay.js +36 -0
  8. package/dist/cjs/features/generic_events/aggregate/index.js +32 -24
  9. package/dist/cjs/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
  10. package/dist/cjs/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +20 -6
  11. package/dist/cjs/features/logging/aggregate/index.js +4 -3
  12. package/dist/cjs/features/page_view_timing/aggregate/index.js +2 -2
  13. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +7 -0
  14. package/dist/cjs/loaders/agent.js +2 -3
  15. package/dist/cjs/loaders/micro-agent-base.js +2 -2
  16. package/dist/cjs/loaders/micro-agent.js +2 -3
  17. package/dist/esm/common/config/init.js +2 -1
  18. package/dist/esm/common/constants/env.cdn.js +1 -1
  19. package/dist/esm/common/constants/env.npm.js +1 -1
  20. package/dist/esm/common/dom/selector-path.js +20 -3
  21. package/dist/esm/common/vitals/constants.js +1 -4
  22. package/dist/esm/common/vitals/first-input-delay.js +29 -0
  23. package/dist/esm/features/generic_events/aggregate/index.js +32 -24
  24. package/dist/esm/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
  25. package/dist/esm/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +20 -6
  26. package/dist/esm/features/logging/aggregate/index.js +4 -3
  27. package/dist/esm/features/page_view_timing/aggregate/index.js +2 -2
  28. package/dist/esm/features/session_trace/aggregate/trace/storage.js +7 -0
  29. package/dist/esm/loaders/agent.js +2 -3
  30. package/dist/esm/loaders/micro-agent-base.js +2 -2
  31. package/dist/esm/loaders/micro-agent.js +2 -3
  32. package/dist/types/common/dom/selector-path.d.ts +1 -1
  33. package/dist/types/common/dom/selector-path.d.ts.map +1 -1
  34. package/dist/types/common/vitals/constants.d.ts +1 -4
  35. package/dist/types/common/vitals/first-input-delay.d.ts +3 -0
  36. package/dist/types/common/vitals/first-input-delay.d.ts.map +1 -0
  37. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  38. package/dist/types/features/generic_events/aggregate/user-actions/aggregated-user-action.d.ts +2 -1
  39. package/dist/types/features/generic_events/aggregate/user-actions/aggregated-user-action.d.ts.map +1 -1
  40. package/dist/types/features/generic_events/aggregate/user-actions/user-actions-aggregator.d.ts +1 -1
  41. package/dist/types/features/generic_events/aggregate/user-actions/user-actions-aggregator.d.ts.map +1 -1
  42. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  43. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  44. package/dist/types/loaders/agent.d.ts +1 -2
  45. package/dist/types/loaders/agent.d.ts.map +1 -1
  46. package/dist/types/loaders/micro-agent-base.d.ts +0 -1
  47. package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
  48. package/dist/types/loaders/micro-agent.d.ts +1 -2
  49. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  50. package/package.json +2 -2
  51. package/src/common/config/init.js +1 -1
  52. package/src/common/dom/selector-path.js +15 -3
  53. package/src/common/vitals/constants.js +1 -5
  54. package/src/common/vitals/first-input-delay.js +28 -0
  55. package/src/features/generic_events/aggregate/index.js +32 -16
  56. package/src/features/generic_events/aggregate/user-actions/aggregated-user-action.js +2 -1
  57. package/src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js +11 -7
  58. package/src/features/logging/aggregate/index.js +4 -3
  59. package/src/features/page_view_timing/aggregate/index.js +2 -2
  60. package/src/features/session_trace/aggregate/trace/storage.js +5 -0
  61. package/src/loaders/agent.js +2 -3
  62. package/src/loaders/micro-agent-base.js +2 -2
  63. package/src/loaders/micro-agent.js +2 -3
  64. package/dist/cjs/common/vitals/first-interaction.js +0 -44
  65. package/dist/esm/common/vitals/first-interaction.js +0 -38
  66. package/dist/types/common/vitals/first-interaction.d.ts +0 -3
  67. package/dist/types/common/vitals/first-interaction.d.ts.map +0 -1
  68. package/src/common/vitals/first-interaction.js +0 -38
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
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.281.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.280.0...v1.281.0) (2025-02-04)
7
+
8
+
9
+ ### Features
10
+
11
+ * Capture Nearest UserAction Fields ([#1267](https://github.com/newrelic/newrelic-browser-agent/issues/1267)) ([d410937](https://github.com/newrelic/newrelic-browser-agent/commit/d410937983545a6a6aa39c52c3762f621acf1110))
12
+
13
+ ## [1.280.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.279.1...v1.280.0) (2025-01-31)
14
+
15
+
16
+ ### Features
17
+
18
+ * Remove agentIdentifier argument from agent constructors ([#1353](https://github.com/newrelic/newrelic-browser-agent/issues/1353)) ([cb866e5](https://github.com/newrelic/newrelic-browser-agent/commit/cb866e5678bf6aa898c082f2be83145a5014fd0e))
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * Roll back to previous FirstInteraction implementation ([#1359](https://github.com/newrelic/newrelic-browser-agent/issues/1359)) ([c2e22ab](https://github.com/newrelic/newrelic-browser-agent/commit/c2e22ab49b00e9cfbeb54664a820e1aa4ed28a53))
24
+
6
25
  ## [1.279.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.279.0...v1.279.1) (2025-01-28)
7
26
 
8
27
 
@@ -211,7 +211,8 @@ const model = () => {
211
211
  },
212
212
  ssl: undefined,
213
213
  user_actions: {
214
- enabled: true
214
+ enabled: true,
215
+ elementAttributes: ['id', 'className', 'tagName', 'type']
215
216
  }
216
217
  };
217
218
  };
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.279.1";
20
+ const VERSION = exports.VERSION = "1.281.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.279.1";
20
+ const VERSION = exports.VERSION = "1.281.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -16,8 +16,11 @@ exports.generateSelectorPath = void 0;
16
16
  * @param {boolean} includeClass
17
17
  * @returns {string|undefined}
18
18
  */
19
- const generateSelectorPath = elem => {
20
- if (!elem) return;
19
+ const generateSelectorPath = (elem, targetFields = []) => {
20
+ if (!elem) return {
21
+ path: undefined,
22
+ nearestFields: {}
23
+ };
21
24
  const getNthOfTypeIndex = node => {
22
25
  try {
23
26
  let i = 1;
@@ -35,12 +38,16 @@ const generateSelectorPath = elem => {
35
38
  };
36
39
  let pathSelector = '';
37
40
  let index = getNthOfTypeIndex(elem);
41
+ const nearestFields = {};
38
42
  try {
39
43
  while (elem?.tagName) {
40
44
  const {
41
45
  id,
42
46
  localName
43
47
  } = elem;
48
+ targetFields.forEach(field => {
49
+ nearestFields[nearestAttrName(field)] ||= elem[field];
50
+ });
44
51
  const selector = [localName, id ? "#".concat(id) : '', pathSelector ? ">".concat(pathSelector) : ''].join('');
45
52
  pathSelector = selector;
46
53
  elem = elem.parentNode;
@@ -48,6 +55,16 @@ const generateSelectorPath = elem => {
48
55
  } catch (err) {
49
56
  // do nothing for now
50
57
  }
51
- return pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
58
+ const path = pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
59
+ return {
60
+ path,
61
+ nearestFields
62
+ };
63
+ function nearestAttrName(originalFieldName) {
64
+ /** preserve original renaming structure for pre-existing field maps */
65
+ if (originalFieldName === 'tagName') originalFieldName = 'tag';
66
+ if (originalFieldName === 'className') originalFieldName = 'class';
67
+ return "nearest".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
68
+ }
52
69
  };
53
70
  exports.generateSelectorPath = generateSelectorPath;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.VITAL_NAMES = exports.PERFORMANCE_ENTRY_TYPE = void 0;
6
+ exports.VITAL_NAMES = void 0;
7
7
  /**
8
8
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
9
  * SPDX-License-Identifier: Apache-2.0
@@ -11,12 +11,9 @@ exports.VITAL_NAMES = exports.PERFORMANCE_ENTRY_TYPE = void 0;
11
11
  const VITAL_NAMES = exports.VITAL_NAMES = {
12
12
  FIRST_PAINT: 'fp',
13
13
  FIRST_CONTENTFUL_PAINT: 'fcp',
14
- FIRST_INTERACTION: 'fi',
14
+ FIRST_INPUT_DELAY: 'fi',
15
15
  LARGEST_CONTENTFUL_PAINT: 'lcp',
16
16
  CUMULATIVE_LAYOUT_SHIFT: 'cls',
17
17
  INTERACTION_TO_NEXT_PAINT: 'inp',
18
18
  TIME_TO_FIRST_BYTE: 'ttfb'
19
- };
20
- const PERFORMANCE_ENTRY_TYPE = exports.PERFORMANCE_ENTRY_TYPE = {
21
- FIRST_INPUT: 'first-input'
22
19
  };
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.firstInputDelay = void 0;
7
+ var _attribution = require("web-vitals/attribution");
8
+ var _vitalMetric = require("./vital-metric");
9
+ var _constants = require("./constants");
10
+ var _runtime = require("../constants/runtime");
11
+ /**
12
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
13
+ * SPDX-License-Identifier: Apache-2.0
14
+ */
15
+
16
+ const firstInputDelay = exports.firstInputDelay = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.FIRST_INPUT_DELAY);
17
+ if (_runtime.isBrowserScope) {
18
+ (0, _attribution.onFID)(({
19
+ value,
20
+ attribution
21
+ }) => {
22
+ if (_runtime.initiallyHidden || firstInputDelay.isValid) return;
23
+ const attrs = {
24
+ type: attribution.eventType,
25
+ fid: Math.round(value),
26
+ eventTarget: attribution.eventTarget,
27
+ loadState: attribution.loadState
28
+ };
29
+
30
+ // CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
31
+ firstInputDelay.update({
32
+ value: attribution.eventTime,
33
+ attrs
34
+ });
35
+ });
36
+ }
@@ -18,6 +18,7 @@ var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
18
18
  var _iframe = require("../../../common/dom/iframe");
19
19
  var _handle = require("../../../common/event-emitter/handle");
20
20
  var _typeCheck = require("../../../common/util/type-check");
21
+ var _features = require("../../../loaders/features/features");
21
22
  /**
22
23
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
23
24
  * SPDX-License-Identifier: Apache-2.0
@@ -60,7 +61,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
60
61
  });
61
62
  }, this.featureName, this.ee);
62
63
  }
63
- let addUserAction;
64
+ let addUserAction = () => {/** no-op */};
64
65
  if (_runtime.isBrowserScope && agentRef.init.user_actions.enabled) {
65
66
  this.userActionAggregator = new _userActionsAggregator.UserActionsAggregator();
66
67
  this.harvestOpts.beforeUnload = () => addUserAction?.(this.userActionAggregator.aggregationEvent);
@@ -86,20 +87,27 @@ class Aggregate extends _aggregateBase.AggregateBase {
86
87
  ...((0, _iframe.isIFrameWindow)(window) && {
87
88
  iframe: true
88
89
  }),
89
- ...(canTrustTargetAttribute('id') && {
90
- targetId: target.id
91
- }),
92
- ...(canTrustTargetAttribute('tagName') && {
93
- targetTag: target.tagName
94
- }),
95
- ...(canTrustTargetAttribute('type') && {
96
- targetType: target.type
97
- }),
98
- ...(canTrustTargetAttribute('className') && {
99
- targetClass: target.className
100
- })
90
+ ...this.agentRef.init.user_actions.elementAttributes.reduce((acc, field) => {
91
+ /** prevent us from capturing an obscenely long value */
92
+ if (canTrustTargetAttribute(field)) acc[targetAttrName(field)] = String(target[field]).trim().slice(0, 128);
93
+ return acc;
94
+ }, {}),
95
+ ...aggregatedUserAction.nearestTargetFields
101
96
  });
102
97
 
98
+ /**
99
+ * Returns the original target field name with `target` prepended and camelCased
100
+ * @param {string} originalFieldName
101
+ * @returns {string} the target field name
102
+ */
103
+ function targetAttrName(originalFieldName) {
104
+ /** preserve original renaming structure for pre-existing field maps */
105
+ if (originalFieldName === 'tagName') originalFieldName = 'tag';
106
+ if (originalFieldName === 'className') originalFieldName = 'class';
107
+ /** return the original field name, cap'd and prepended with target to match formatting */
108
+ return "target".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
109
+ }
110
+
103
111
  /**
104
112
  * Only trust attributes that exist on HTML element targets, which excludes the window and the document targets
105
113
  * @param {string} attribute The attribute to check for on the target element
@@ -115,7 +123,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
115
123
  };
116
124
  (0, _registerHandler.registerHandler)('ua', evt => {
117
125
  /** the processor will return the previously aggregated event if it has been completed by processing the current event */
118
- addUserAction(this.userActionAggregator.process(evt));
126
+ addUserAction(this.userActionAggregator.process(evt, this.agentRef.init.user_actions.elementAttributes));
119
127
  }, this.featureName, this.ee);
120
128
  }
121
129
 
@@ -134,7 +142,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
134
142
  const observer = new PerformanceObserver(list => {
135
143
  list.getEntries().forEach(entry => {
136
144
  try {
137
- (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen']);
145
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
138
146
  const detailObj = agentRef.init.performance.capture_detail ? createDetailAttrs(entry.detail) : {};
139
147
  this.addEvent({
140
148
  ...detailObj,
@@ -196,12 +204,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
196
204
  if (this.agentRef.init.performance.resources.asset_types.length && !this.agentRef.init.performance.resources.asset_types.includes(entryObject.initiatorType)) return;
197
205
  /** decide if the entryDomain is a first party domain */
198
206
  firstParty = entryDomain === _runtime.globalScope?.location.hostname || agentRef.init.performance.resources.first_party_domains.includes(entryDomain);
199
- if (firstParty) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/FirstPartyResource/Seen']);
200
- if (isNr) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/NrResource/Seen']);
207
+ if (firstParty) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/FirstPartyResource/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
208
+ if (isNr) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/NrResource/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
201
209
  } catch (err) {
202
210
  // couldnt parse the URL, so firstParty will just default to false
203
211
  }
204
- (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/Resource/Seen']);
212
+ (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/Resource/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
205
213
  const event = {
206
214
  ...entryObject,
207
215
  eventType: 'BrowserPerformance',
@@ -287,12 +295,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
287
295
  trackSupportabilityMetrics() {
288
296
  /** track usage SMs to improve these experimental features */
289
297
  const configPerfTag = 'Config/Performance/';
290
- if (this.agentRef.init.performance.capture_marks) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMarks/Enabled']);
291
- if (this.agentRef.init.performance.capture_measures) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMeasures/Enabled']);
292
- if (this.agentRef.init.performance.resources.enabled) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/Enabled']);
293
- if (this.agentRef.init.performance.resources.asset_types?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/AssetTypes/Changed']);
294
- if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed']);
295
- if (this.agentRef.init.performance.resources.ignore_newrelic === false) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed']);
298
+ if (this.agentRef.init.performance.capture_marks) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMarks/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
299
+ if (this.agentRef.init.performance.capture_measures) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'CaptureMeasures/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
300
+ if (this.agentRef.init.performance.resources.enabled) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
301
+ if (this.agentRef.init.performance.resources.asset_types?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/AssetTypes/Changed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
302
+ if (this.agentRef.init.performance.resources.first_party_domains?.length !== 0) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/FirstPartyDomains/Changed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
303
+ if (this.agentRef.init.performance.resources.ignore_newrelic === false) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [configPerfTag + 'Resources/IgnoreNewrelic/Changed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
296
304
  }
297
305
  }
298
306
  exports.Aggregate = Aggregate;
@@ -11,13 +11,14 @@ var _constants = require("../../constants");
11
11
  */
12
12
 
13
13
  class AggregatedUserAction {
14
- constructor(evt, selectorPath) {
14
+ constructor(evt, selectorPath, nearestTargetFields) {
15
15
  this.event = evt;
16
16
  this.count = 1;
17
17
  this.originMs = Math.floor(evt.timeStamp);
18
18
  this.relativeMs = [0];
19
19
  this.selectorPath = selectorPath;
20
20
  this.rageClick = undefined;
21
+ this.nearestTargetFields = nearestTargetFields;
21
22
  }
22
23
 
23
24
  /**
@@ -31,9 +31,12 @@ class UserActionsAggregator {
31
31
  * @param {Event} evt The event supplied by the addEventListener callback
32
32
  * @returns {AggregatedUserAction|undefined} The previous aggregation set if it has been completed by processing the current event
33
33
  */
34
- process(evt) {
34
+ process(evt, targetFields) {
35
35
  if (!evt) return;
36
- const selectorPath = getSelectorPath(evt);
36
+ const {
37
+ selectorPath,
38
+ nearestTargetFields
39
+ } = getSelectorPath(evt, targetFields);
37
40
  const aggregationKey = getAggregationKey(evt, selectorPath);
38
41
  if (!!aggregationKey && aggregationKey === this.#aggregationKey) {
39
42
  // an aggregation exists already, so lets just continue to increment
@@ -43,7 +46,7 @@ class UserActionsAggregator {
43
46
  const finishedEvent = this.#aggregationEvent;
44
47
  // then set as this new event aggregation
45
48
  this.#aggregationKey = aggregationKey;
46
- this.#aggregationEvent = new _aggregatedUserAction.AggregatedUserAction(evt, selectorPath);
49
+ this.#aggregationEvent = new _aggregatedUserAction.AggregatedUserAction(evt, selectorPath, nearestTargetFields);
47
50
  return finishedEvent;
48
51
  }
49
52
  }
@@ -56,13 +59,24 @@ class UserActionsAggregator {
56
59
  * @returns {string}
57
60
  */
58
61
  exports.UserActionsAggregator = UserActionsAggregator;
59
- function getSelectorPath(evt) {
62
+ function getSelectorPath(evt, targetFields) {
60
63
  let selectorPath;
64
+ let nearestTargetFields = {};
61
65
  if (_constants.OBSERVED_WINDOW_EVENTS.includes(evt.type) || evt.target === window) selectorPath = 'window';else if (evt.target === document) selectorPath = 'document';
62
66
  // if still no selectorPath, generate one from target tree that includes elem ids
63
- else selectorPath = (0, _selectorPath.generateSelectorPath)(evt.target);
67
+ else {
68
+ const {
69
+ path,
70
+ nearestFields
71
+ } = (0, _selectorPath.generateSelectorPath)(evt.target, targetFields);
72
+ selectorPath = path;
73
+ nearestTargetFields = nearestFields;
74
+ }
64
75
  // if STILL no selectorPath, it will return undefined which will skip aggregation for this event
65
- return selectorPath;
76
+ return {
77
+ selectorPath,
78
+ nearestTargetFields
79
+ };
66
80
  }
67
81
 
68
82
  /**
@@ -15,6 +15,7 @@ var _log = require("../shared/log");
15
15
  var _utils = require("../shared/utils");
16
16
  var _traverse = require("../../../common/util/traverse");
17
17
  var _agentConstants = require("../../../common/constants/agent-constants");
18
+ var _features = require("../../../loaders/features/features");
18
19
  /**
19
20
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
20
21
  * SPDX-License-Identifier: Apache-2.0
@@ -59,17 +60,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
59
60
  const failToHarvestMessage = 'Logging/Harvest/Failed/Seen';
60
61
  if (logBytes > _agentConstants.MAX_PAYLOAD_SIZE) {
61
62
  // cannot possibly send this, even with an empty buffer
62
- (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [failToHarvestMessage, logBytes]);
63
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [failToHarvestMessage, logBytes], undefined, _features.FEATURE_NAMES.metrics, this.ee);
63
64
  (0, _console.warn)(31, log.message.slice(0, 25) + '...');
64
65
  return;
65
66
  }
66
67
  if (this.events.wouldExceedMaxSize(logBytes)) {
67
- (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Early/Seen', this.events.byteSize() + logBytes]);
68
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Logging/Harvest/Early/Seen', this.events.byteSize() + logBytes], undefined, _features.FEATURE_NAMES.metrics, this.ee);
68
69
  this.agentRef.runtime.harvester.triggerHarvestFor(this); // force a harvest synchronously to try adding again
69
70
  }
70
71
  if (!this.events.add(log)) {
71
72
  // still failed after a harvest attempt despite not being too large would mean harvest failed with options.retry
72
- (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [failToHarvestMessage, logBytes]);
73
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, [failToHarvestMessage, logBytes], undefined, _features.FEATURE_NAMES.metrics, this.ee);
73
74
  (0, _console.warn)(31, log.message.slice(0, 25) + '...');
74
75
  }
75
76
  }
@@ -12,8 +12,8 @@ var _features = require("../../../loaders/features/features");
12
12
  var _aggregateBase = require("../../utils/aggregate-base");
13
13
  var _cumulativeLayoutShift = require("../../../common/vitals/cumulative-layout-shift");
14
14
  var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
15
+ var _firstInputDelay = require("../../../common/vitals/first-input-delay");
15
16
  var _firstPaint = require("../../../common/vitals/first-paint");
16
- var _firstInteraction = require("../../../common/vitals/first-interaction");
17
17
  var _interactionToNextPaint = require("../../../common/vitals/interaction-to-next-paint");
18
18
  var _largestContentfulPaint = require("../../../common/vitals/largest-contentful-paint");
19
19
  var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
@@ -42,8 +42,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
42
42
  this.waitForFlags([]).then(() => {
43
43
  _firstPaint.firstPaint.subscribe(this.#handleVitalMetric);
44
44
  _firstContentfulPaint.firstContentfulPaint.subscribe(this.#handleVitalMetric);
45
+ _firstInputDelay.firstInputDelay.subscribe(this.#handleVitalMetric);
45
46
  _largestContentfulPaint.largestContentfulPaint.subscribe(this.#handleVitalMetric);
46
- _firstInteraction.firstInteraction.subscribe(this.#handleVitalMetric);
47
47
  _interactionToNextPaint.interactionToNextPaint.subscribe(this.#handleVitalMetric);
48
48
  _timeToFirstByte.timeToFirstByte.subscribe(({
49
49
  attrs
@@ -144,6 +144,13 @@ class TraceStorage {
144
144
  this.storeTiming({
145
145
  [name]: value
146
146
  });
147
+ if (hasFID(name, attrs)) this.storeEvent({
148
+ type: 'fid',
149
+ target: 'document'
150
+ }, 'document', value, value + attrs.fid);
151
+ function hasFID(name, attrs) {
152
+ return name === 'fi' && !!attrs && typeof attrs.fid === 'number';
153
+ }
147
154
  }
148
155
  storeTiming(timingEntry, isAbsoluteTimestamp = false) {
149
156
  if (!timingEntry) return;
@@ -35,10 +35,9 @@ var _runtime = require("../common/constants/runtime");
35
35
  class Agent extends _agentBase.AgentBase {
36
36
  /**
37
37
  * @param {Object} options Options to initialize agent with
38
- * @param {string} [agentIdentifier] Optional identifier of agent
39
38
  */
40
- constructor(options, agentIdentifier) {
41
- super(agentIdentifier);
39
+ constructor(options) {
40
+ super();
42
41
  if (!_runtime.globalScope) {
43
42
  // We could not determine the runtime environment. Short-circuite the agent here
44
43
  // to avoid possible exceptions later that may cause issues with customer's application.
@@ -13,8 +13,8 @@ var _uniqueId = require("../common/ids/unique-id");
13
13
 
14
14
  class MicroAgentBase {
15
15
  agentIdentifier;
16
- constructor(agentIdentifier = (0, _uniqueId.generateRandomHexString)(16)) {
17
- this.agentIdentifier = agentIdentifier;
16
+ constructor() {
17
+ this.agentIdentifier = (0, _uniqueId.generateRandomHexString)(16);
18
18
  }
19
19
 
20
20
  /**
@@ -27,10 +27,9 @@ const nonAutoFeatures = [_features.FEATURE_NAMES.jserrors, _features.FEATURE_NAM
27
27
  class MicroAgent extends _microAgentBase.MicroAgentBase {
28
28
  /**
29
29
  * @param {Object} options - Specifies features and runtime configuration,
30
- * @param {string=} agentIdentifier - The optional unique ID of the agent.
31
30
  */
32
- constructor(options, agentIdentifier) {
33
- super(agentIdentifier);
31
+ constructor(options) {
32
+ super();
34
33
  this.features = {};
35
34
  (0, _nreum.setNREUMInitializedAgent)(this.agentIdentifier, this);
36
35
  (0, _configure.configure)(this, {
@@ -202,7 +202,8 @@ const model = () => {
202
202
  },
203
203
  ssl: undefined,
204
204
  user_actions: {
205
- enabled: true
205
+ enabled: true,
206
+ elementAttributes: ['id', 'className', 'tagName', 'type']
206
207
  }
207
208
  };
208
209
  };
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.279.1";
14
+ export const VERSION = "1.281.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.279.1";
14
+ export const VERSION = "1.281.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -10,8 +10,11 @@
10
10
  * @param {boolean} includeClass
11
11
  * @returns {string|undefined}
12
12
  */
13
- export const generateSelectorPath = elem => {
14
- if (!elem) return;
13
+ export const generateSelectorPath = (elem, targetFields = []) => {
14
+ if (!elem) return {
15
+ path: undefined,
16
+ nearestFields: {}
17
+ };
15
18
  const getNthOfTypeIndex = node => {
16
19
  try {
17
20
  let i = 1;
@@ -29,12 +32,16 @@ export const generateSelectorPath = elem => {
29
32
  };
30
33
  let pathSelector = '';
31
34
  let index = getNthOfTypeIndex(elem);
35
+ const nearestFields = {};
32
36
  try {
33
37
  while (elem?.tagName) {
34
38
  const {
35
39
  id,
36
40
  localName
37
41
  } = elem;
42
+ targetFields.forEach(field => {
43
+ nearestFields[nearestAttrName(field)] ||= elem[field];
44
+ });
38
45
  const selector = [localName, id ? "#".concat(id) : '', pathSelector ? ">".concat(pathSelector) : ''].join('');
39
46
  pathSelector = selector;
40
47
  elem = elem.parentNode;
@@ -42,5 +49,15 @@ export const generateSelectorPath = elem => {
42
49
  } catch (err) {
43
50
  // do nothing for now
44
51
  }
45
- return pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
52
+ const path = pathSelector ? index ? "".concat(pathSelector, ":nth-of-type(").concat(index, ")") : pathSelector : undefined;
53
+ return {
54
+ path,
55
+ nearestFields
56
+ };
57
+ function nearestAttrName(originalFieldName) {
58
+ /** preserve original renaming structure for pre-existing field maps */
59
+ if (originalFieldName === 'tagName') originalFieldName = 'tag';
60
+ if (originalFieldName === 'className') originalFieldName = 'class';
61
+ return "nearest".concat(originalFieldName.charAt(0).toUpperCase() + originalFieldName.slice(1));
62
+ }
46
63
  };
@@ -5,12 +5,9 @@
5
5
  export const VITAL_NAMES = {
6
6
  FIRST_PAINT: 'fp',
7
7
  FIRST_CONTENTFUL_PAINT: 'fcp',
8
- FIRST_INTERACTION: 'fi',
8
+ FIRST_INPUT_DELAY: 'fi',
9
9
  LARGEST_CONTENTFUL_PAINT: 'lcp',
10
10
  CUMULATIVE_LAYOUT_SHIFT: 'cls',
11
11
  INTERACTION_TO_NEXT_PAINT: 'inp',
12
12
  TIME_TO_FIRST_BYTE: 'ttfb'
13
- };
14
- export const PERFORMANCE_ENTRY_TYPE = {
15
- FIRST_INPUT: 'first-input'
16
13
  };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { onFID } from 'web-vitals/attribution';
6
+ import { VitalMetric } from './vital-metric';
7
+ import { VITAL_NAMES } from './constants';
8
+ import { initiallyHidden, isBrowserScope } from '../constants/runtime';
9
+ export const firstInputDelay = new VitalMetric(VITAL_NAMES.FIRST_INPUT_DELAY);
10
+ if (isBrowserScope) {
11
+ onFID(({
12
+ value,
13
+ attribution
14
+ }) => {
15
+ if (initiallyHidden || firstInputDelay.isValid) return;
16
+ const attrs = {
17
+ type: attribution.eventType,
18
+ fid: Math.round(value),
19
+ eventTarget: attribution.eventTarget,
20
+ loadState: attribution.loadState
21
+ };
22
+
23
+ // CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
24
+ firstInputDelay.update({
25
+ value: attribution.eventTime,
26
+ attrs
27
+ });
28
+ });
29
+ }