@newrelic/browser-agent 1.314.0-rc.0 → 1.314.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/dist/cjs/common/constants/agent-constants.js +2 -1
  2. package/dist/cjs/common/constants/env.cdn.js +1 -1
  3. package/dist/cjs/common/constants/env.npm.js +1 -1
  4. package/dist/cjs/common/vitals/cumulative-layout-shift.js +3 -2
  5. package/dist/cjs/common/vitals/interaction-to-next-paint.js +3 -2
  6. package/dist/cjs/common/vitals/largest-contentful-paint.js +2 -1
  7. package/dist/cjs/common/vitals/vital-metric.js +7 -4
  8. package/dist/cjs/features/page_view_timing/aggregate/index.js +27 -6
  9. package/dist/esm/common/constants/agent-constants.js +2 -1
  10. package/dist/esm/common/constants/env.cdn.js +1 -1
  11. package/dist/esm/common/constants/env.npm.js +1 -1
  12. package/dist/esm/common/vitals/cumulative-layout-shift.js +3 -2
  13. package/dist/esm/common/vitals/interaction-to-next-paint.js +3 -2
  14. package/dist/esm/common/vitals/largest-contentful-paint.js +2 -1
  15. package/dist/esm/common/vitals/vital-metric.js +7 -4
  16. package/dist/esm/features/page_view_timing/aggregate/index.js +27 -6
  17. package/dist/types/common/constants/agent-constants.d.ts +1 -0
  18. package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
  19. package/dist/types/common/vitals/vital-metric.d.ts +3 -2
  20. package/dist/types/common/vitals/vital-metric.d.ts.map +1 -1
  21. package/dist/types/features/page_view_timing/aggregate/index.d.ts +1 -1
  22. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  23. package/package.json +1 -1
  24. package/src/common/constants/agent-constants.js +2 -1
  25. package/src/common/vitals/cumulative-layout-shift.js +2 -2
  26. package/src/common/vitals/interaction-to-next-paint.js +2 -2
  27. package/src/common/vitals/largest-contentful-paint.js +1 -1
  28. package/src/common/vitals/vital-metric.js +6 -4
  29. package/src/features/page_view_timing/aggregate/index.js +14 -6
@@ -18,5 +18,6 @@ const SUPPORTS_REGISTERED_ENTITIES = exports.SUPPORTS_REGISTERED_ENTITIES = {
18
18
  [_features.FEATURE_NAMES.logging]: true,
19
19
  [_features.FEATURE_NAMES.genericEvents]: true,
20
20
  [_features.FEATURE_NAMES.jserrors]: true,
21
- [_features.FEATURE_NAMES.ajax]: true
21
+ [_features.FEATURE_NAMES.ajax]: true,
22
+ [_features.FEATURE_NAMES.pageViewTiming]: true
22
23
  };
@@ -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.314.0-rc.0";
20
+ const VERSION = exports.VERSION = "1.314.0-rc.1";
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.314.0-rc.0";
20
+ const VERSION = exports.VERSION = "1.314.0-rc.1";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -9,7 +9,7 @@ var _constants = require("./constants");
9
9
  var _vitalMetric = require("./vital-metric");
10
10
  var _runtime = require("../constants/runtime");
11
11
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
12
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
13
  * SPDX-License-Identifier: Apache-2.0
14
14
  */
15
15
 
@@ -29,7 +29,8 @@ if (_runtime.isBrowserScope) {
29
29
  };
30
30
  cumulativeLayoutShift.update({
31
31
  value,
32
- attrs
32
+ attrs,
33
+ element: attribution.largestShiftSource?.node
33
34
  });
34
35
  }, {
35
36
  reportAllChanges: true
@@ -9,7 +9,7 @@ var _vitalMetric = require("./vital-metric");
9
9
  var _constants = require("./constants");
10
10
  var _runtime = require("../constants/runtime");
11
11
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
12
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
13
  * SPDX-License-Identifier: Apache-2.0
14
14
  */
15
15
 
@@ -38,7 +38,8 @@ if (_runtime.isBrowserScope) {
38
38
  };
39
39
  interactionToNextPaint.update({
40
40
  value,
41
- attrs
41
+ attrs,
42
+ element: attribution.interactionTargetElement
42
43
  });
43
44
  });
44
45
  }
@@ -40,7 +40,8 @@ if (_runtime.isBrowserScope) {
40
40
  if (attribution.url) attrs.elUrl = (0, _cleanUrl.cleanURL)(attribution.url);
41
41
  largestContentfulPaint.update({
42
42
  value,
43
- attrs
43
+ attrs,
44
+ element: lcpEntry?.element
44
45
  });
45
46
  });
46
47
  }
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.VitalMetric = 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
  class VitalMetric {
@@ -18,13 +18,15 @@ class VitalMetric {
18
18
  }
19
19
  update({
20
20
  value,
21
- attrs = {}
21
+ attrs = {},
22
+ element
22
23
  }) {
23
24
  if (value === undefined || value === null || value < 0) return;
24
25
  const state = {
25
26
  value: this.roundingMethod(value),
26
27
  name: this.name,
27
- attrs
28
+ attrs,
29
+ element
28
30
  };
29
31
  this.history.push(state);
30
32
  this.#subscribers.forEach(cb => {
@@ -39,7 +41,8 @@ class VitalMetric {
39
41
  return this.history[this.history.length - 1] || {
40
42
  value: undefined,
41
43
  name: this.name,
42
- attrs: {}
44
+ attrs: {},
45
+ element: undefined
43
46
  };
44
47
  }
45
48
  get isValid() {
@@ -21,6 +21,8 @@ var _runtime = require("../../../common/constants/runtime");
21
21
  var _eventOrigin = require("../../../common/util/event-origin");
22
22
  var _loadTime = require("../../../common/vitals/load-time");
23
23
  var _webdriverDetection = require("../../../common/util/webdriver-detection");
24
+ var _selectorPath = require("../../../common/dom/selector-path");
25
+ var _utils = require("../../../common/v2/utils");
24
26
  var _cleanUrl = require("../../../common/url/clean-url");
25
27
  /**
26
28
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
@@ -32,9 +34,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
32
34
  #handleVitalMetric = ({
33
35
  name,
34
36
  value,
35
- attrs
37
+ attrs,
38
+ element
36
39
  }) => {
37
- this.addTiming(name, value, attrs);
40
+ this.addTiming(name, value, attrs, element);
38
41
  };
39
42
  constructor(agentRef) {
40
43
  super(agentRef, _constants.FEATURE_NAME);
@@ -62,10 +65,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
62
65
  const {
63
66
  name,
64
67
  value,
65
- attrs
68
+ attrs,
69
+ element
66
70
  } = _cumulativeLayoutShift.cumulativeLayoutShift.current;
67
71
  if (value === undefined) return;
68
- this.addTiming(name, value * 1000, attrs);
72
+ this.addTiming(name, value * 1000, attrs, element);
69
73
  }, true, true); // CLS node should only reports on vis change rather than on every change
70
74
 
71
75
  this.drain();
@@ -83,7 +87,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
83
87
  this.curSessEndRecorded = true;
84
88
  }
85
89
  }
86
- addTiming(name, value, attrs) {
90
+ addTiming(name, value, attrs, element) {
87
91
  attrs = attrs || {};
88
92
  attrs.pageUrl = (0, _cleanUrl.cleanURL)((0, _runtime.getNavigationEntry)()?.name || _runtime.initialLocation);
89
93
  addConnectionAttributes(attrs); // network conditions may differ from the actual for VitalMetrics when they were captured
@@ -104,7 +108,24 @@ class Aggregate extends _aggregateBase.AggregateBase {
104
108
  value,
105
109
  attrs
106
110
  };
107
- this.events.add(timing);
111
+ const targets = (0, _selectorPath.analyzeElemPath)(element, [], this.agentRef).targets;
112
+ if (!targets.length) targets.push(undefined);
113
+ targets.forEach(target => {
114
+ this.events.add({
115
+ ...timing,
116
+ attrs: {
117
+ ...attrs,
118
+ ...(0, _utils.getVersion2Attributes)(target, this)
119
+ }
120
+ });
121
+ if ((0, _utils.shouldDuplicate)(target, this.agentRef)) this.events.add({
122
+ ...timing,
123
+ attrs: {
124
+ ...attrs,
125
+ ...(0, _utils.getVersion2DuplicationAttributes)(target, this)
126
+ }
127
+ });
128
+ });
108
129
  (0, _handle.handle)('pvtAdded', [name, value, attrs], undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
109
130
  this.checkForFirstInteraction();
110
131
 
@@ -11,5 +11,6 @@ export const SUPPORTS_REGISTERED_ENTITIES = {
11
11
  [FEATURE_NAMES.logging]: true,
12
12
  [FEATURE_NAMES.genericEvents]: true,
13
13
  [FEATURE_NAMES.jserrors]: true,
14
- [FEATURE_NAMES.ajax]: true
14
+ [FEATURE_NAMES.ajax]: true,
15
+ [FEATURE_NAMES.pageViewTiming]: true
15
16
  };
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.314.0-rc.0";
14
+ export const VERSION = "1.314.0-rc.1";
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.314.0-rc.0";
14
+ export const VERSION = "1.314.0-rc.1";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onCLS } from 'web-vitals/attribution';
@@ -22,7 +22,8 @@ if (isBrowserScope) {
22
22
  };
23
23
  cumulativeLayoutShift.update({
24
24
  value,
25
- attrs
25
+ attrs,
26
+ element: attribution.largestShiftSource?.node
26
27
  });
27
28
  }, {
28
29
  reportAllChanges: true
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onINP } from 'web-vitals/attribution';
@@ -31,7 +31,8 @@ if (isBrowserScope) {
31
31
  };
32
32
  interactionToNextPaint.update({
33
33
  value,
34
- attrs
34
+ attrs,
35
+ element: attribution.interactionTargetElement
35
36
  });
36
37
  });
37
38
  }
@@ -33,7 +33,8 @@ if (isBrowserScope) {
33
33
  if (attribution.url) attrs.elUrl = cleanURL(attribution.url);
34
34
  largestContentfulPaint.update({
35
35
  value,
36
- attrs
36
+ attrs,
37
+ element: lcpEntry?.element
37
38
  });
38
39
  });
39
40
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  export class VitalMetric {
@@ -12,13 +12,15 @@ export class VitalMetric {
12
12
  }
13
13
  update({
14
14
  value,
15
- attrs = {}
15
+ attrs = {},
16
+ element
16
17
  }) {
17
18
  if (value === undefined || value === null || value < 0) return;
18
19
  const state = {
19
20
  value: this.roundingMethod(value),
20
21
  name: this.name,
21
- attrs
22
+ attrs,
23
+ element
22
24
  };
23
25
  this.history.push(state);
24
26
  this.#subscribers.forEach(cb => {
@@ -33,7 +35,8 @@ export class VitalMetric {
33
35
  return this.history[this.history.length - 1] || {
34
36
  value: undefined,
35
37
  name: this.name,
36
- attrs: {}
38
+ attrs: {},
39
+ element: undefined
37
40
  };
38
41
  }
39
42
  get isValid() {
@@ -20,15 +20,18 @@ import { initiallyHidden, getNavigationEntry, initialLocation } from '../../../c
20
20
  import { eventOrigin } from '../../../common/util/event-origin';
21
21
  import { loadTime } from '../../../common/vitals/load-time';
22
22
  import { webdriverDetected } from '../../../common/util/webdriver-detection';
23
+ import { analyzeElemPath } from '../../../common/dom/selector-path';
24
+ import { getVersion2Attributes, getVersion2DuplicationAttributes, shouldDuplicate } from '../../../common/v2/utils';
23
25
  import { cleanURL } from '../../../common/url/clean-url';
24
26
  export class Aggregate extends AggregateBase {
25
27
  static featureName = FEATURE_NAME;
26
28
  #handleVitalMetric = ({
27
29
  name,
28
30
  value,
29
- attrs
31
+ attrs,
32
+ element
30
33
  }) => {
31
- this.addTiming(name, value, attrs);
34
+ this.addTiming(name, value, attrs, element);
32
35
  };
33
36
  constructor(agentRef) {
34
37
  super(agentRef, FEATURE_NAME);
@@ -56,10 +59,11 @@ export class Aggregate extends AggregateBase {
56
59
  const {
57
60
  name,
58
61
  value,
59
- attrs
62
+ attrs,
63
+ element
60
64
  } = cumulativeLayoutShift.current;
61
65
  if (value === undefined) return;
62
- this.addTiming(name, value * 1000, attrs);
66
+ this.addTiming(name, value * 1000, attrs, element);
63
67
  }, true, true); // CLS node should only reports on vis change rather than on every change
64
68
 
65
69
  this.drain();
@@ -77,7 +81,7 @@ export class Aggregate extends AggregateBase {
77
81
  this.curSessEndRecorded = true;
78
82
  }
79
83
  }
80
- addTiming(name, value, attrs) {
84
+ addTiming(name, value, attrs, element) {
81
85
  attrs = attrs || {};
82
86
  attrs.pageUrl = cleanURL(getNavigationEntry()?.name || initialLocation);
83
87
  addConnectionAttributes(attrs); // network conditions may differ from the actual for VitalMetrics when they were captured
@@ -98,7 +102,24 @@ export class Aggregate extends AggregateBase {
98
102
  value,
99
103
  attrs
100
104
  };
101
- this.events.add(timing);
105
+ const targets = analyzeElemPath(element, [], this.agentRef).targets;
106
+ if (!targets.length) targets.push(undefined);
107
+ targets.forEach(target => {
108
+ this.events.add({
109
+ ...timing,
110
+ attrs: {
111
+ ...attrs,
112
+ ...getVersion2Attributes(target, this)
113
+ }
114
+ });
115
+ if (shouldDuplicate(target, this.agentRef)) this.events.add({
116
+ ...timing,
117
+ attrs: {
118
+ ...attrs,
119
+ ...getVersion2DuplicationAttributes(target, this)
120
+ }
121
+ });
122
+ });
102
123
  handle('pvtAdded', [name, value, attrs], undefined, FEATURE_NAMES.sessionTrace, this.ee);
103
124
  this.checkForFirstInteraction();
104
125
 
@@ -7,6 +7,7 @@ export const SUPPORTS_REGISTERED_ENTITIES: {
7
7
  [FEATURE_NAMES.genericEvents]: boolean;
8
8
  [FEATURE_NAMES.jserrors]: boolean;
9
9
  [FEATURE_NAMES.ajax]: boolean;
10
+ [FEATURE_NAMES.pageViewTiming]: boolean;
10
11
  };
11
12
  import { FEATURE_NAMES } from '../../loaders/features/features';
12
13
  //# sourceMappingURL=agent-constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-constants.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/agent-constants.js"],"names":[],"mappings":"AAMA,iCAAkC,KAAK,CAAA;AACvC,+BAAgC,OAAO,CAAA;AACvC,0BAA2B,oBAAoB,CAAA;AAC/C,4BAA6B,eAAe,CAAA;AAE5C;IACE,CAAC,aAAa,CAAC,OAAO,CAAC,UAAM;IAC7B,CAAC,aAAa,CAAC,aAAa,CAAC,UAAM;IACnC,CAAC,aAAa,CAAC,QAAQ,CAAC,UAAM;IAC9B,CAAC,aAAa,CAAC,IAAI,CAAC,UAAM;EAC3B;8BAZ6B,iCAAiC"}
1
+ {"version":3,"file":"agent-constants.d.ts","sourceRoot":"","sources":["../../../../src/common/constants/agent-constants.js"],"names":[],"mappings":"AAMA,iCAAkC,KAAK,CAAA;AACvC,+BAAgC,OAAO,CAAA;AACvC,0BAA2B,oBAAoB,CAAA;AAC/C,4BAA6B,eAAe,CAAA;AAE5C;IACE,CAAC,aAAa,CAAC,OAAO,CAAC,UAAM;IAC7B,CAAC,aAAa,CAAC,aAAa,CAAC,UAAM;IACnC,CAAC,aAAa,CAAC,QAAQ,CAAC,UAAM;IAC9B,CAAC,aAAa,CAAC,IAAI,CAAC,UAAM;IAC1B,CAAC,aAAa,CAAC,cAAc,CAAC,UAAM;EACrC;8BAb6B,iCAAiC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  export class VitalMetric {
@@ -8,9 +8,10 @@ export class VitalMetric {
8
8
  name: any;
9
9
  attrs: {};
10
10
  roundingMethod: any;
11
- update({ value, attrs }: {
11
+ update({ value, attrs, element }: {
12
12
  value: any;
13
13
  attrs?: {} | undefined;
14
+ element: any;
14
15
  }): void;
15
16
  get current(): any;
16
17
  get isValid(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"vital-metric.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/vital-metric.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;IAIE,4CAIC;IAND,eAAY;IAGV,UAAgB;IAChB,UAAe;IACf,oBAAwF;IAG1F;;;aAgBC;IAED,mBAMC;IAED,uBAEC;IAED,uEAMC;;CACF"}
1
+ {"version":3,"file":"vital-metric.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/vital-metric.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;IAIE,4CAIC;IAND,eAAY;IAGV,UAAgB;IAChB,UAAe;IACf,oBAAwF;IAG1F;;;;aAiBC;IAED,mBAOC;IAED,uBAEC;IAED,uEAMC;;CACF"}
@@ -8,7 +8,7 @@ export class Aggregate extends AggregateBase {
8
8
  * @param {number} timestamp
9
9
  */
10
10
  endCurrentSession(timestamp: number): void;
11
- addTiming(name: any, value: any, attrs: any): {
11
+ addTiming(name: any, value: any, attrs: any, element: any): {
12
12
  name: any;
13
13
  value: any;
14
14
  attrs: any;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"AAwBA;IACE,2BAAiC;IAMjC,2BA4BC;IA1BC,4BAA+B;IAC/B,0BAA6B;IA2B/B;;;OAGG;IACH,6BAFW,MAAM,QAOhB;IAED;;;;MA+BC;IAED;;;OAGG;IACH,4BAFa,IAAI,CAahB;IAED,gDAWC;IAED,4BAGC;IAGD,qCAwBC;;CACF;8BA5J6B,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"AA0BA;IACE,2BAAiC;IAMjC,2BA4BC;IA1BC,4BAA+B;IAC/B,0BAA6B;IA2B/B;;;OAGG;IACH,6BAFW,MAAM,QAOhB;IAED;;;;MAqCC;IAED;;;OAGG;IACH,4BAFa,IAAI,CAahB;IAED,gDAWC;IAED,4BAGC;IAGD,qCAwBC;;CACF;8BApK6B,4BAA4B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.314.0-rc.0",
3
+ "version": "1.314.0-rc.1",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -13,5 +13,6 @@ export const SUPPORTS_REGISTERED_ENTITIES = {
13
13
  [FEATURE_NAMES.logging]: true,
14
14
  [FEATURE_NAMES.genericEvents]: true,
15
15
  [FEATURE_NAMES.jserrors]: true,
16
- [FEATURE_NAMES.ajax]: true
16
+ [FEATURE_NAMES.ajax]: true,
17
+ [FEATURE_NAMES.pageViewTiming]: true
17
18
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onCLS } from 'web-vitals/attribution'
@@ -18,6 +18,6 @@ if (isBrowserScope) {
18
18
  largestShiftValue: attribution.largestShiftValue,
19
19
  loadState: attribution.loadState
20
20
  }
21
- cumulativeLayoutShift.update({ value, attrs })
21
+ cumulativeLayoutShift.update({ value, attrs, element: attribution.largestShiftSource?.node })
22
22
  }, { reportAllChanges: true })
23
23
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onINP } from 'web-vitals/attribution'
@@ -25,6 +25,6 @@ if (isBrowserScope) {
25
25
  presentationDelay: attribution.presentationDelay,
26
26
  loadState: attribution.loadState
27
27
  }
28
- interactionToNextPaint.update({ value, attrs })
28
+ interactionToNextPaint.update({ value, attrs, element: attribution.interactionTargetElement })
29
29
  })
30
30
  }
@@ -31,6 +31,6 @@ if (isBrowserScope) {
31
31
  if (attribution.element) attrs.element = attribution.element
32
32
  if (attribution.url) attrs.elUrl = cleanURL(attribution.url)
33
33
 
34
- largestContentfulPaint.update({ value, attrs })
34
+ largestContentfulPaint.update({ value, attrs, element: lcpEntry?.element })
35
35
  })
36
36
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  export class VitalMetric {
@@ -12,12 +12,13 @@ export class VitalMetric {
12
12
  this.roundingMethod = typeof roundingMethod === 'function' ? roundingMethod : Math.floor
13
13
  }
14
14
 
15
- update ({ value, attrs = {} }) {
15
+ update ({ value, attrs = {}, element }) {
16
16
  if (value === undefined || value === null || value < 0) return
17
17
  const state = {
18
18
  value: this.roundingMethod(value),
19
19
  name: this.name,
20
- attrs
20
+ attrs,
21
+ element
21
22
  }
22
23
 
23
24
  this.history.push(state)
@@ -34,7 +35,8 @@ export class VitalMetric {
34
35
  return this.history[this.history.length - 1] || {
35
36
  value: undefined,
36
37
  name: this.name,
37
- attrs: {}
38
+ attrs: {},
39
+ element: undefined
38
40
  }
39
41
  }
40
42
 
@@ -20,13 +20,15 @@ import { initiallyHidden, getNavigationEntry, initialLocation } from '../../../c
20
20
  import { eventOrigin } from '../../../common/util/event-origin'
21
21
  import { loadTime } from '../../../common/vitals/load-time'
22
22
  import { webdriverDetected } from '../../../common/util/webdriver-detection'
23
+ import { analyzeElemPath } from '../../../common/dom/selector-path'
24
+ import { getVersion2Attributes, getVersion2DuplicationAttributes, shouldDuplicate } from '../../../common/v2/utils'
23
25
  import { cleanURL } from '../../../common/url/clean-url'
24
26
 
25
27
  export class Aggregate extends AggregateBase {
26
28
  static featureName = FEATURE_NAME
27
29
 
28
- #handleVitalMetric = ({ name, value, attrs }) => {
29
- this.addTiming(name, value, attrs)
30
+ #handleVitalMetric = ({ name, value, attrs, element }) => {
31
+ this.addTiming(name, value, attrs, element)
30
32
  }
31
33
 
32
34
  constructor (agentRef) {
@@ -50,9 +52,9 @@ export class Aggregate extends AggregateBase {
50
52
  /* Downstream, the event consumer interprets all timing node value as ms-unit and converts it to seconds via division by 1000. CLS is unitless so this normally is a problem.
51
53
  bel.6 schema also doesn't support decimal values, of which cls within [0,1). However, the two nicely cancels out, and we can multiply cls by 1000 to both negate the division
52
54
  and send an integer > 1. We effectively lose some precision down to 3 decimal places for this workaround. E.g. (real) 0.749132... -> 749.132...-> 749 -> 0.749 (final) */
53
- const { name, value, attrs } = cumulativeLayoutShift.current
55
+ const { name, value, attrs, element } = cumulativeLayoutShift.current
54
56
  if (value === undefined) return
55
- this.addTiming(name, value * 1000, attrs)
57
+ this.addTiming(name, value * 1000, attrs, element)
56
58
  }, true, true) // CLS node should only reports on vis change rather than on every change
57
59
 
58
60
  this.drain()
@@ -70,7 +72,7 @@ export class Aggregate extends AggregateBase {
70
72
  }
71
73
  }
72
74
 
73
- addTiming (name, value, attrs) {
75
+ addTiming (name, value, attrs, element) {
74
76
  attrs = attrs || {}
75
77
  attrs.pageUrl = cleanURL(getNavigationEntry()?.name || initialLocation)
76
78
 
@@ -93,7 +95,13 @@ export class Aggregate extends AggregateBase {
93
95
  value,
94
96
  attrs
95
97
  }
96
- this.events.add(timing)
98
+
99
+ const targets = analyzeElemPath(element, [], this.agentRef).targets
100
+ if (!targets.length) targets.push(undefined)
101
+ targets.forEach(target => {
102
+ this.events.add({ ...timing, attrs: { ...attrs, ...getVersion2Attributes(target, this) } })
103
+ if (shouldDuplicate(target, this.agentRef)) this.events.add({ ...timing, attrs: { ...attrs, ...getVersion2DuplicationAttributes(target, this) } })
104
+ })
97
105
 
98
106
  handle('pvtAdded', [name, value, attrs], undefined, FEATURE_NAMES.sessionTrace, this.ee)
99
107