@newrelic/browser-agent 1.321.0-rc.6 → 1.321.0-rc.8
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.
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/vitals/cumulative-layout-shift.js +6 -4
- package/dist/cjs/common/vitals/first-contentful-paint.js +5 -3
- package/dist/cjs/common/vitals/interaction-to-next-paint.js +10 -4
- package/dist/cjs/common/vitals/largest-contentful-paint.js +5 -3
- package/dist/cjs/common/vitals/register-vital.js +25 -0
- package/dist/cjs/common/vitals/time-to-first-byte.js +4 -2
- package/dist/cjs/common/vitals/vital-metric.js +3 -2
- package/dist/cjs/interfaces/registered-iframe-entity.js +9 -3
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/vitals/cumulative-layout-shift.js +6 -4
- package/dist/esm/common/vitals/first-contentful-paint.js +5 -3
- package/dist/esm/common/vitals/interaction-to-next-paint.js +10 -4
- package/dist/esm/common/vitals/largest-contentful-paint.js +5 -3
- package/dist/esm/common/vitals/register-vital.js +19 -0
- package/dist/esm/common/vitals/time-to-first-byte.js +4 -2
- package/dist/esm/common/vitals/vital-metric.js +3 -2
- package/dist/esm/interfaces/registered-iframe-entity.js +9 -3
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/vitals/cumulative-layout-shift.d.ts.map +1 -1
- package/dist/types/common/vitals/first-contentful-paint.d.ts.map +1 -1
- package/dist/types/common/vitals/interaction-to-next-paint.d.ts.map +1 -1
- package/dist/types/common/vitals/largest-contentful-paint.d.ts.map +1 -1
- package/dist/types/common/vitals/register-vital.d.ts +13 -0
- package/dist/types/common/vitals/register-vital.d.ts.map +1 -0
- package/dist/types/common/vitals/time-to-first-byte.d.ts.map +1 -1
- package/dist/types/common/vitals/vital-metric.d.ts +1 -1
- package/dist/types/common/vitals/vital-metric.d.ts.map +1 -1
- package/dist/types/interfaces/registered-iframe-entity.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/vitals/cumulative-layout-shift.js +5 -3
- package/src/common/vitals/first-contentful-paint.js +5 -3
- package/src/common/vitals/interaction-to-next-paint.js +8 -3
- package/src/common/vitals/largest-contentful-paint.js +5 -3
- package/src/common/vitals/register-vital.js +19 -0
- package/src/common/vitals/time-to-first-byte.js +4 -2
- package/src/common/vitals/vital-metric.js +3 -2
- package/src/interfaces/registered-iframe-entity.js +7 -2
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.321.0-rc.
|
|
20
|
+
const VERSION = exports.VERSION = "1.321.0-rc.8";
|
|
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.321.0-rc.
|
|
20
|
+
const VERSION = exports.VERSION = "1.321.0-rc.8";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -7,15 +7,16 @@ exports.cumulativeLayoutShift = void 0;
|
|
|
7
7
|
var _attribution = require("web-vitals/attribution");
|
|
8
8
|
var _constants = require("./constants");
|
|
9
9
|
var _vitalMetric = require("./vital-metric");
|
|
10
|
+
var _registerVital = require("./register-vital");
|
|
10
11
|
var _runtime = require("../constants/runtime");
|
|
11
12
|
/**
|
|
12
|
-
* Copyright 2020-
|
|
13
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
13
14
|
* SPDX-License-Identifier: Apache-2.0
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
const cumulativeLayoutShift = exports.cumulativeLayoutShift = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT, x => x);
|
|
17
18
|
if (_runtime.isBrowserScope) {
|
|
18
|
-
|
|
19
|
+
const handleCLS = ({
|
|
19
20
|
value,
|
|
20
21
|
attribution,
|
|
21
22
|
id
|
|
@@ -31,7 +32,8 @@ if (_runtime.isBrowserScope) {
|
|
|
31
32
|
value,
|
|
32
33
|
attrs
|
|
33
34
|
});
|
|
34
|
-
}
|
|
35
|
+
};
|
|
36
|
+
(0, _registerVital.registerVital)(() => (0, _attribution.onCLS)(handleCLS, {
|
|
35
37
|
reportAllChanges: true
|
|
36
|
-
});
|
|
38
|
+
}));
|
|
37
39
|
}
|
|
@@ -8,8 +8,9 @@ var _attribution = require("web-vitals/attribution");
|
|
|
8
8
|
var _runtime = require("../constants/runtime");
|
|
9
9
|
var _constants = require("./constants");
|
|
10
10
|
var _vitalMetric = require("./vital-metric");
|
|
11
|
+
var _registerVital = require("./register-vital");
|
|
11
12
|
/**
|
|
12
|
-
* Copyright 2020-
|
|
13
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
13
14
|
* SPDX-License-Identifier: Apache-2.0
|
|
14
15
|
*/
|
|
15
16
|
|
|
@@ -37,7 +38,7 @@ if (_runtime.isBrowserScope) {
|
|
|
37
38
|
// ignore
|
|
38
39
|
}
|
|
39
40
|
} else {
|
|
40
|
-
|
|
41
|
+
const handleFCP = ({
|
|
41
42
|
value,
|
|
42
43
|
attribution
|
|
43
44
|
}) => {
|
|
@@ -51,6 +52,7 @@ if (_runtime.isBrowserScope) {
|
|
|
51
52
|
value,
|
|
52
53
|
attrs
|
|
53
54
|
});
|
|
54
|
-
}
|
|
55
|
+
};
|
|
56
|
+
(0, _registerVital.registerVital)(() => (0, _attribution.onFCP)(handleFCP));
|
|
55
57
|
}
|
|
56
58
|
}
|
|
@@ -7,20 +7,25 @@ exports.interactionToNextPaint = void 0;
|
|
|
7
7
|
var _attribution = require("web-vitals/attribution");
|
|
8
8
|
var _vitalMetric = require("./vital-metric");
|
|
9
9
|
var _constants = require("./constants");
|
|
10
|
+
var _registerVital = require("./register-vital");
|
|
10
11
|
var _runtime = require("../constants/runtime");
|
|
11
12
|
/**
|
|
12
|
-
* Copyright 2020-
|
|
13
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
13
14
|
* SPDX-License-Identifier: Apache-2.0
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
const interactionToNextPaint = exports.interactionToNextPaint = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.INTERACTION_TO_NEXT_PAINT);
|
|
17
18
|
if (_runtime.isBrowserScope) {
|
|
18
19
|
/* Interaction-to-Next-Paint */
|
|
19
|
-
|
|
20
|
+
const handleINP = ({
|
|
20
21
|
value,
|
|
21
22
|
attribution,
|
|
22
|
-
id
|
|
23
|
+
id,
|
|
24
|
+
entries
|
|
23
25
|
}) => {
|
|
26
|
+
/* web-vitals v6 reports a synthetic INP (value 8, no entries, no interaction attribution) after bfcache restores when
|
|
27
|
+
every interaction stayed below the duration threshold; skip those so only measured interactions are reported, as in v4 */
|
|
28
|
+
if (!entries?.length) return;
|
|
24
29
|
const attrs = {
|
|
25
30
|
metricId: id,
|
|
26
31
|
eventTarget: attribution.interactionTarget,
|
|
@@ -40,5 +45,6 @@ if (_runtime.isBrowserScope) {
|
|
|
40
45
|
value,
|
|
41
46
|
attrs
|
|
42
47
|
});
|
|
43
|
-
}
|
|
48
|
+
};
|
|
49
|
+
(0, _registerVital.registerVital)(() => (0, _attribution.onINP)(handleINP));
|
|
44
50
|
}
|
|
@@ -7,6 +7,7 @@ exports.largestContentfulPaint = void 0;
|
|
|
7
7
|
var _attribution = require("web-vitals/attribution");
|
|
8
8
|
var _vitalMetric = require("./vital-metric");
|
|
9
9
|
var _constants = require("./constants");
|
|
10
|
+
var _registerVital = require("./register-vital");
|
|
10
11
|
var _runtime = require("../constants/runtime");
|
|
11
12
|
var _cleanUrl = require("../url/clean-url");
|
|
12
13
|
/**
|
|
@@ -16,7 +17,7 @@ var _cleanUrl = require("../url/clean-url");
|
|
|
16
17
|
|
|
17
18
|
const largestContentfulPaint = exports.largestContentfulPaint = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.LARGEST_CONTENTFUL_PAINT);
|
|
18
19
|
if (_runtime.isBrowserScope) {
|
|
19
|
-
|
|
20
|
+
const handleLCP = ({
|
|
20
21
|
value,
|
|
21
22
|
attribution
|
|
22
23
|
}) => {
|
|
@@ -36,11 +37,12 @@ if (_runtime.isBrowserScope) {
|
|
|
36
37
|
attrs.eid = lcpEntry.id;
|
|
37
38
|
if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName;
|
|
38
39
|
}
|
|
39
|
-
if (attribution.
|
|
40
|
+
if (attribution.target) attrs.element = attribution.target; // renamed from `element` in web-vitals v4->v5; NR attr name kept for backwards compatibility
|
|
40
41
|
if (attribution.url) attrs.elUrl = (0, _cleanUrl.cleanURL)(attribution.url);
|
|
41
42
|
largestContentfulPaint.update({
|
|
42
43
|
value,
|
|
43
44
|
attrs
|
|
44
45
|
});
|
|
45
|
-
}
|
|
46
|
+
};
|
|
47
|
+
(0, _registerVital.registerVital)(() => (0, _attribution.onLCP)(handleLCP));
|
|
46
48
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.registerVital = registerVital;
|
|
7
|
+
/**
|
|
8
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
9
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Invokes a web-vitals registration (e.g. `onLCP(callback)`), tolerating environments where the registration itself
|
|
14
|
+
* throws. web-vitals >= 5 reads Performance Timeline APIs such as `performance.getEntriesByType` unguarded when a
|
|
15
|
+
* metric is registered; where those are missing the metric is simply never reported, instead of the throw failing
|
|
16
|
+
* the feature that imported it.
|
|
17
|
+
* @param {() => void} register
|
|
18
|
+
*/
|
|
19
|
+
function registerVital(register) {
|
|
20
|
+
try {
|
|
21
|
+
register();
|
|
22
|
+
} catch (e) {
|
|
23
|
+
// metric unavailable in this environment
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -7,6 +7,7 @@ exports.timeToFirstByte = void 0;
|
|
|
7
7
|
var _runtime = require("../constants/runtime");
|
|
8
8
|
var _constants = require("./constants");
|
|
9
9
|
var _vitalMetric = require("./vital-metric");
|
|
10
|
+
var _registerVital = require("./register-vital");
|
|
10
11
|
var _attribution = require("web-vitals/attribution");
|
|
11
12
|
/**
|
|
12
13
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
@@ -24,7 +25,7 @@ const timeToFirstByte = exports.timeToFirstByte = new _vitalMetric.VitalMetric(_
|
|
|
24
25
|
* - onTTFB relies on a truthy `responseStart` value, should ensure that exists before relying on it (seen to be falsy in certain Electron.js cases for instance)
|
|
25
26
|
*/
|
|
26
27
|
if (_runtime.isBrowserScope && (0, _runtime.getNavigationEntry)() && !_runtime.isiOS && window === window.parent) {
|
|
27
|
-
|
|
28
|
+
const handleTTFB = ({
|
|
28
29
|
value,
|
|
29
30
|
attribution
|
|
30
31
|
}) => {
|
|
@@ -35,7 +36,8 @@ if (_runtime.isBrowserScope && (0, _runtime.getNavigationEntry)() && !_runtime.i
|
|
|
35
36
|
navigationEntry: attribution.navigationEntry
|
|
36
37
|
}
|
|
37
38
|
});
|
|
38
|
-
}
|
|
39
|
+
};
|
|
40
|
+
(0, _registerVital.registerVital)(() => (0, _attribution.onTTFB)(handleTTFB));
|
|
39
41
|
} else {
|
|
40
42
|
if (!timeToFirstByte.isValid) {
|
|
41
43
|
const entry = {};
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.VitalMetric = void 0;
|
|
7
7
|
/**
|
|
8
|
-
* Copyright 2020-
|
|
8
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
9
9
|
* SPDX-License-Identifier: Apache-2.0
|
|
10
10
|
*/
|
|
11
11
|
class VitalMetric {
|
|
@@ -24,7 +24,8 @@ class VitalMetric {
|
|
|
24
24
|
const state = {
|
|
25
25
|
value: this.roundingMethod(value),
|
|
26
26
|
name: this.name,
|
|
27
|
-
|
|
27
|
+
// optional web-vitals attribution fields are omitted rather than being serialized as null attributes
|
|
28
|
+
attrs: Object.fromEntries(Object.entries(attrs).filter(([, attrValue]) => attrValue !== undefined))
|
|
28
29
|
};
|
|
29
30
|
this.history.push(state);
|
|
30
31
|
this.#subscribers.forEach(cb => {
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.RegisteredIframeEntity = void 0;
|
|
7
7
|
var _webVitals = require("web-vitals");
|
|
8
8
|
var _runtime = require("../common/constants/runtime");
|
|
9
|
+
var _registerVital = require("../common/vitals/register-vital");
|
|
9
10
|
var _iframe = require("../common/dom/iframe");
|
|
10
11
|
var _now = require("../common/timing/now");
|
|
11
12
|
var _console = require("../common/util/console");
|
|
@@ -191,9 +192,14 @@ class RegisteredIframeEntity {
|
|
|
191
192
|
*/
|
|
192
193
|
#setupVitalsListeners() {
|
|
193
194
|
VITALS.forEach(([vitalFn, property]) => {
|
|
194
|
-
vitalFn(({
|
|
195
|
-
value
|
|
195
|
+
(0, _registerVital.registerVital)(() => vitalFn(({
|
|
196
|
+
value,
|
|
197
|
+
entries: vitalEntries
|
|
196
198
|
}) => {
|
|
199
|
+
/* web-vitals v6 reports a synthetic INP (value 8, no entries) after bfcache restores when
|
|
200
|
+
every interaction stayed below the duration threshold; skip those so only measured
|
|
201
|
+
interactions are reported, matching the guard in src/common/vitals/interaction-to-next-paint.js */
|
|
202
|
+
if (property === 'inp' && !vitalEntries?.length) return;
|
|
197
203
|
this.metadata.vitals[property].value = value;
|
|
198
204
|
this.#postMessageToParent(_iframeConstants.IFRAME_VITALS_UPDATE, {
|
|
199
205
|
entries: [{
|
|
@@ -203,7 +209,7 @@ class RegisteredIframeEntity {
|
|
|
203
209
|
});
|
|
204
210
|
}, {
|
|
205
211
|
reportAllChanges: property === 'cls' || property === 'inp'
|
|
206
|
-
});
|
|
212
|
+
}));
|
|
207
213
|
});
|
|
208
214
|
}
|
|
209
215
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
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';
|
|
6
6
|
import { VITAL_NAMES } from './constants';
|
|
7
7
|
import { VitalMetric } from './vital-metric';
|
|
8
|
+
import { registerVital } from './register-vital';
|
|
8
9
|
import { isBrowserScope } from '../constants/runtime';
|
|
9
10
|
export const cumulativeLayoutShift = new VitalMetric(VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT, x => x);
|
|
10
11
|
if (isBrowserScope) {
|
|
11
|
-
|
|
12
|
+
const handleCLS = ({
|
|
12
13
|
value,
|
|
13
14
|
attribution,
|
|
14
15
|
id
|
|
@@ -24,7 +25,8 @@ if (isBrowserScope) {
|
|
|
24
25
|
value,
|
|
25
26
|
attrs
|
|
26
27
|
});
|
|
27
|
-
}
|
|
28
|
+
};
|
|
29
|
+
registerVital(() => onCLS(handleCLS, {
|
|
28
30
|
reportAllChanges: true
|
|
29
|
-
});
|
|
31
|
+
}));
|
|
30
32
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { onFCP } from 'web-vitals/attribution';
|
|
@@ -7,6 +7,7 @@ import { onFCP } from 'web-vitals/attribution';
|
|
|
7
7
|
import { iOSBelow16, initiallyHidden, isBrowserScope } from '../constants/runtime';
|
|
8
8
|
import { VITAL_NAMES } from './constants';
|
|
9
9
|
import { VitalMetric } from './vital-metric';
|
|
10
|
+
import { registerVital } from './register-vital';
|
|
10
11
|
export const firstContentfulPaint = new VitalMetric(VITAL_NAMES.FIRST_CONTENTFUL_PAINT);
|
|
11
12
|
|
|
12
13
|
/* First Contentful Paint - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
|
|
@@ -29,7 +30,7 @@ if (isBrowserScope) {
|
|
|
29
30
|
// ignore
|
|
30
31
|
}
|
|
31
32
|
} else {
|
|
32
|
-
|
|
33
|
+
const handleFCP = ({
|
|
33
34
|
value,
|
|
34
35
|
attribution
|
|
35
36
|
}) => {
|
|
@@ -43,6 +44,7 @@ if (isBrowserScope) {
|
|
|
43
44
|
value,
|
|
44
45
|
attrs
|
|
45
46
|
});
|
|
46
|
-
}
|
|
47
|
+
};
|
|
48
|
+
registerVital(() => onFCP(handleFCP));
|
|
47
49
|
}
|
|
48
50
|
}
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
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';
|
|
6
6
|
import { VitalMetric } from './vital-metric';
|
|
7
7
|
import { VITAL_NAMES } from './constants';
|
|
8
|
+
import { registerVital } from './register-vital';
|
|
8
9
|
import { isBrowserScope } from '../constants/runtime';
|
|
9
10
|
export const interactionToNextPaint = new VitalMetric(VITAL_NAMES.INTERACTION_TO_NEXT_PAINT);
|
|
10
11
|
if (isBrowserScope) {
|
|
11
12
|
/* Interaction-to-Next-Paint */
|
|
12
|
-
|
|
13
|
+
const handleINP = ({
|
|
13
14
|
value,
|
|
14
15
|
attribution,
|
|
15
|
-
id
|
|
16
|
+
id,
|
|
17
|
+
entries
|
|
16
18
|
}) => {
|
|
19
|
+
/* web-vitals v6 reports a synthetic INP (value 8, no entries, no interaction attribution) after bfcache restores when
|
|
20
|
+
every interaction stayed below the duration threshold; skip those so only measured interactions are reported, as in v4 */
|
|
21
|
+
if (!entries?.length) return;
|
|
17
22
|
const attrs = {
|
|
18
23
|
metricId: id,
|
|
19
24
|
eventTarget: attribution.interactionTarget,
|
|
@@ -33,5 +38,6 @@ if (isBrowserScope) {
|
|
|
33
38
|
value,
|
|
34
39
|
attrs
|
|
35
40
|
});
|
|
36
|
-
}
|
|
41
|
+
};
|
|
42
|
+
registerVital(() => onINP(handleINP));
|
|
37
43
|
}
|
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
import { onLCP } from 'web-vitals/attribution';
|
|
6
6
|
import { VitalMetric } from './vital-metric';
|
|
7
7
|
import { VITAL_NAMES } from './constants';
|
|
8
|
+
import { registerVital } from './register-vital';
|
|
8
9
|
import { initiallyHidden, isBrowserScope } from '../constants/runtime';
|
|
9
10
|
import { cleanURL } from '../url/clean-url';
|
|
10
11
|
export const largestContentfulPaint = new VitalMetric(VITAL_NAMES.LARGEST_CONTENTFUL_PAINT);
|
|
11
12
|
if (isBrowserScope) {
|
|
12
|
-
|
|
13
|
+
const handleLCP = ({
|
|
13
14
|
value,
|
|
14
15
|
attribution
|
|
15
16
|
}) => {
|
|
@@ -29,11 +30,12 @@ if (isBrowserScope) {
|
|
|
29
30
|
attrs.eid = lcpEntry.id;
|
|
30
31
|
if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName;
|
|
31
32
|
}
|
|
32
|
-
if (attribution.
|
|
33
|
+
if (attribution.target) attrs.element = attribution.target; // renamed from `element` in web-vitals v4->v5; NR attr name kept for backwards compatibility
|
|
33
34
|
if (attribution.url) attrs.elUrl = cleanURL(attribution.url);
|
|
34
35
|
largestContentfulPaint.update({
|
|
35
36
|
value,
|
|
36
37
|
attrs
|
|
37
38
|
});
|
|
38
|
-
}
|
|
39
|
+
};
|
|
40
|
+
registerVital(() => onLCP(handleLCP));
|
|
39
41
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Invokes a web-vitals registration (e.g. `onLCP(callback)`), tolerating environments where the registration itself
|
|
8
|
+
* throws. web-vitals >= 5 reads Performance Timeline APIs such as `performance.getEntriesByType` unguarded when a
|
|
9
|
+
* metric is registered; where those are missing the metric is simply never reported, instead of the throw failing
|
|
10
|
+
* the feature that imported it.
|
|
11
|
+
* @param {() => void} register
|
|
12
|
+
*/
|
|
13
|
+
export function registerVital(register) {
|
|
14
|
+
try {
|
|
15
|
+
register();
|
|
16
|
+
} catch (e) {
|
|
17
|
+
// metric unavailable in this environment
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { globalScope, isBrowserScope, isiOS, originTime, getNavigationEntry } from '../constants/runtime';
|
|
6
6
|
import { VITAL_NAMES } from './constants';
|
|
7
7
|
import { VitalMetric } from './vital-metric';
|
|
8
|
+
import { registerVital } from './register-vital';
|
|
8
9
|
import { onTTFB } from 'web-vitals/attribution';
|
|
9
10
|
export const timeToFirstByte = new VitalMetric(VITAL_NAMES.TIME_TO_FIRST_BYTE);
|
|
10
11
|
|
|
@@ -17,7 +18,7 @@ export const timeToFirstByte = new VitalMetric(VITAL_NAMES.TIME_TO_FIRST_BYTE);
|
|
|
17
18
|
* - onTTFB relies on a truthy `responseStart` value, should ensure that exists before relying on it (seen to be falsy in certain Electron.js cases for instance)
|
|
18
19
|
*/
|
|
19
20
|
if (isBrowserScope && getNavigationEntry() && !isiOS && window === window.parent) {
|
|
20
|
-
|
|
21
|
+
const handleTTFB = ({
|
|
21
22
|
value,
|
|
22
23
|
attribution
|
|
23
24
|
}) => {
|
|
@@ -28,7 +29,8 @@ if (isBrowserScope && getNavigationEntry() && !isiOS && window === window.parent
|
|
|
28
29
|
navigationEntry: attribution.navigationEntry
|
|
29
30
|
}
|
|
30
31
|
});
|
|
31
|
-
}
|
|
32
|
+
};
|
|
33
|
+
registerVital(() => onTTFB(handleTTFB));
|
|
32
34
|
} else {
|
|
33
35
|
if (!timeToFirstByte.isValid) {
|
|
34
36
|
const entry = {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
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 {
|
|
@@ -18,7 +18,8 @@ export class VitalMetric {
|
|
|
18
18
|
const state = {
|
|
19
19
|
value: this.roundingMethod(value),
|
|
20
20
|
name: this.name,
|
|
21
|
-
|
|
21
|
+
// optional web-vitals attribution fields are omitted rather than being serialized as null attributes
|
|
22
|
+
attrs: Object.fromEntries(Object.entries(attrs).filter(([, attrValue]) => attrValue !== undefined))
|
|
22
23
|
};
|
|
23
24
|
this.history.push(state);
|
|
24
25
|
this.#subscribers.forEach(cb => {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { onCLS, onFCP, onINP, onLCP } from 'web-vitals';
|
|
8
8
|
// internal
|
|
9
9
|
import { globalScope, isBrowserScope } from '../common/constants/runtime';
|
|
10
|
+
import { registerVital } from '../common/vitals/register-vital';
|
|
10
11
|
import { isIFrameWindow } from '../common/dom/iframe';
|
|
11
12
|
import { now } from '../common/timing/now';
|
|
12
13
|
import { warn } from '../common/util/console';
|
|
@@ -183,9 +184,14 @@ export class RegisteredIframeEntity {
|
|
|
183
184
|
*/
|
|
184
185
|
#setupVitalsListeners() {
|
|
185
186
|
VITALS.forEach(([vitalFn, property]) => {
|
|
186
|
-
vitalFn(({
|
|
187
|
-
value
|
|
187
|
+
registerVital(() => vitalFn(({
|
|
188
|
+
value,
|
|
189
|
+
entries: vitalEntries
|
|
188
190
|
}) => {
|
|
191
|
+
/* web-vitals v6 reports a synthetic INP (value 8, no entries) after bfcache restores when
|
|
192
|
+
every interaction stayed below the duration threshold; skip those so only measured
|
|
193
|
+
interactions are reported, matching the guard in src/common/vitals/interaction-to-next-paint.js */
|
|
194
|
+
if (property === 'inp' && !vitalEntries?.length) return;
|
|
189
195
|
this.metadata.vitals[property].value = value;
|
|
190
196
|
this.#postMessageToParent(IFRAME_VITALS_UPDATE, {
|
|
191
197
|
entries: [{
|
|
@@ -195,7 +201,7 @@ export class RegisteredIframeEntity {
|
|
|
195
201
|
});
|
|
196
202
|
}, {
|
|
197
203
|
reportAllChanges: property === 'cls' || property === 'inp'
|
|
198
|
-
});
|
|
204
|
+
}));
|
|
199
205
|
});
|
|
200
206
|
}
|
|
201
207
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/events.js","../src/common/constants/iframe-constants.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/connector.js","../src/common/harvest/harvester.js","../src/common/harvest/send.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/payloads/payloads.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/session/session-key.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/add-url.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/extract-url.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/attribute-size.js","../src/common/util/browser-stack-matchers.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/monkey-patched.js","../src/common/util/obfuscate.js","../src/common/util/short-circuit.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/util/webdriver-detection.js","../src/common/v2/constants.js","../src/common/v2/mfe-vitals.js","../src/common/v2/script-correlation.js","../src/common/v2/script-tracker-constants.js","../src/common/v2/script-tracker.js","../src/common/v2/timing-factory.js","../src/common/v2/utils.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/load-time.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/cause-string.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/harvest-metadata.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/aggregate_v2/index.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/aggregate/trace/utils.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/interfaces/registered-entity.js","../src/interfaces/registered-iframe-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/consent.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/iframe-message-handler.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/events.js","../src/common/constants/iframe-constants.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/connector.js","../src/common/harvest/harvester.js","../src/common/harvest/send.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/payloads/payloads.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/session/session-key.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/add-url.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/extract-url.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/attribute-size.js","../src/common/util/browser-stack-matchers.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/monkey-patched.js","../src/common/util/obfuscate.js","../src/common/util/short-circuit.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/util/webdriver-detection.js","../src/common/v2/constants.js","../src/common/v2/mfe-vitals.js","../src/common/v2/script-correlation.js","../src/common/v2/script-tracker-constants.js","../src/common/v2/script-tracker.js","../src/common/v2/timing-factory.js","../src/common/v2/utils.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/load-time.js","../src/common/vitals/register-vital.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/cause-string.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/harvest-metadata.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/aggregate_v2/index.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/aggregate/trace/utils.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/interfaces/registered-entity.js","../src/interfaces/registered-iframe-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/consent.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/iframe-message-handler.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.9.3"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cumulative-layout-shift.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/cumulative-layout-shift.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cumulative-layout-shift.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/cumulative-layout-shift.js"],"names":[],"mappings":"AAUA,gDAAmG;4BAJvE,gBAAgB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"first-contentful-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/first-contentful-paint.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"first-contentful-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/first-contentful-paint.js"],"names":[],"mappings":"AAWA,+CAAuF;4BAH3D,gBAAgB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interaction-to-next-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/interaction-to-next-paint.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interaction-to-next-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/interaction-to-next-paint.js"],"names":[],"mappings":"AAUA,iDAA4F;4BALhE,gBAAgB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"largest-contentful-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/largest-contentful-paint.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"largest-contentful-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/largest-contentful-paint.js"],"names":[],"mappings":"AAWA,iDAA2F;4BAN/D,gBAAgB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Invokes a web-vitals registration (e.g. `onLCP(callback)`), tolerating environments where the registration itself
|
|
7
|
+
* throws. web-vitals >= 5 reads Performance Timeline APIs such as `performance.getEntriesByType` unguarded when a
|
|
8
|
+
* metric is registered; where those are missing the metric is simply never reported, instead of the throw failing
|
|
9
|
+
* the feature that imported it.
|
|
10
|
+
* @param {() => void} register
|
|
11
|
+
*/
|
|
12
|
+
export function registerVital(register: () => void): void;
|
|
13
|
+
//# sourceMappingURL=register-vital.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-vital.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/register-vital.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,wCAFW,MAAM,IAAI,QAQpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-to-first-byte.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/time-to-first-byte.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"time-to-first-byte.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/time-to-first-byte.js"],"names":[],"mappings":"AAUA,0CAA8E;4BAJlD,gBAAgB"}
|
|
@@ -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;;;
|
|
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,mBAMC;IAED,uBAEC;IAED,uEAMC;;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registered-iframe-entity.d.ts","sourceRoot":"","sources":["../../../src/interfaces/registered-iframe-entity.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registered-iframe-entity.d.ts","sourceRoot":"","sources":["../../../src/interfaces/registered-iframe-entity.js"],"names":[],"mappings":"AAuBA;;;;GAIG;AAEH;;;;;;GAMG;AACH;IAkDE;;;OAGG;IACH,kBAFW,sBAAsB,EAsDhC;IAzGD,kCAAkC;IAClC,UADW,mBAAmB,CAW7B;IAyBD,mBANa,OAAO,EAQnB;IAbD;;;;;;OAMG;IACH,eAFa,OAAO,CAInB;IAoTD;;;;;OAKG;IACH,oBAHW,MAAM,eACN,MAAM,QAIhB;IAED;;;;;;;OAOG;IACH,cAFa,OAAO,CAAC,IAAI,CAAC,CAgBzB;IAED;;;;;SAKK;IACL,6BAHa,MAAM,eACN,MAAM,QAIlB;IAED;;;;;;OAMG;IACH,cAJW,MAAM,YACN;QAAC,KAAK,CAAC,EAAE,MAAM,GAAC,eAAe,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,GAAC,eAAe,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAC,GACvF,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,CAAC,CAI7F;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,OAAO,GAAC,IAAI,YAC1B,OAAO,QAIjB;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,qBACZ,MAAM,QAIhB;IAED;;;;;OAKG;IACH,iBAHW,MAAM,GAAC,IAAI,iBACX,OAAO,QAIjB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,QAMrB;IAED;;;;;MAKE;IACF,aAHW,MAAM,YACN;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,MAAM,GAAC,MAAM,CAAA;KAAC,QAIpF;;CACF;0BA9cY,OAAO,mCAAmC,EAAE,WAAW;kCACvD,OAAO,mCAAmC,EAAE,mBAAmB;qCAC/D,OAAO,mCAAmC,EAAE,sBAAsB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/browser-agent",
|
|
3
|
-
"version": "1.321.0-rc.
|
|
3
|
+
"version": "1.321.0-rc.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
|
|
6
6
|
"description": "New Relic Browser Agent",
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
"dependencies": {
|
|
226
226
|
"@newrelic/rrweb": "^1.1.2",
|
|
227
227
|
"fflate": "0.8.2",
|
|
228
|
-
"web-vitals": "
|
|
228
|
+
"web-vitals": "6.2.1"
|
|
229
229
|
},
|
|
230
230
|
"devDependencies": {
|
|
231
231
|
"@babel/cli": "^7.23.4",
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
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'
|
|
6
6
|
import { VITAL_NAMES } from './constants'
|
|
7
7
|
import { VitalMetric } from './vital-metric'
|
|
8
|
+
import { registerVital } from './register-vital'
|
|
8
9
|
import { isBrowserScope } from '../constants/runtime'
|
|
9
10
|
|
|
10
11
|
export const cumulativeLayoutShift = new VitalMetric(VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT, (x) => x)
|
|
11
12
|
|
|
12
13
|
if (isBrowserScope) {
|
|
13
|
-
|
|
14
|
+
const handleCLS = ({ value, attribution, id }) => {
|
|
14
15
|
const attrs = {
|
|
15
16
|
metricId: id,
|
|
16
17
|
largestShiftTarget: attribution.largestShiftTarget,
|
|
@@ -19,5 +20,6 @@ if (isBrowserScope) {
|
|
|
19
20
|
loadState: attribution.loadState
|
|
20
21
|
}
|
|
21
22
|
cumulativeLayoutShift.update({ value, attrs })
|
|
22
|
-
}
|
|
23
|
+
}
|
|
24
|
+
registerVital(() => onCLS(handleCLS, { reportAllChanges: true }))
|
|
23
25
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
import { onFCP } from 'web-vitals/attribution'
|
|
@@ -7,6 +7,7 @@ import { onFCP } from 'web-vitals/attribution'
|
|
|
7
7
|
import { iOSBelow16, initiallyHidden, isBrowserScope } from '../constants/runtime'
|
|
8
8
|
import { VITAL_NAMES } from './constants'
|
|
9
9
|
import { VitalMetric } from './vital-metric'
|
|
10
|
+
import { registerVital } from './register-vital'
|
|
10
11
|
|
|
11
12
|
export const firstContentfulPaint = new VitalMetric(VITAL_NAMES.FIRST_CONTENTFUL_PAINT)
|
|
12
13
|
|
|
@@ -27,7 +28,7 @@ if (isBrowserScope) {
|
|
|
27
28
|
// ignore
|
|
28
29
|
}
|
|
29
30
|
} else {
|
|
30
|
-
|
|
31
|
+
const handleFCP = ({ value, attribution }) => {
|
|
31
32
|
if (initiallyHidden || firstContentfulPaint.isValid) return
|
|
32
33
|
const attrs = {
|
|
33
34
|
timeToFirstByte: attribution.timeToFirstByte,
|
|
@@ -35,6 +36,7 @@ if (isBrowserScope) {
|
|
|
35
36
|
loadState: attribution.loadState
|
|
36
37
|
}
|
|
37
38
|
firstContentfulPaint.update({ value, attrs })
|
|
38
|
-
}
|
|
39
|
+
}
|
|
40
|
+
registerVital(() => onFCP(handleFCP))
|
|
39
41
|
}
|
|
40
42
|
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
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'
|
|
6
6
|
import { VitalMetric } from './vital-metric'
|
|
7
7
|
import { VITAL_NAMES } from './constants'
|
|
8
|
+
import { registerVital } from './register-vital'
|
|
8
9
|
import { isBrowserScope } from '../constants/runtime'
|
|
9
10
|
|
|
10
11
|
export const interactionToNextPaint = new VitalMetric(VITAL_NAMES.INTERACTION_TO_NEXT_PAINT)
|
|
11
12
|
|
|
12
13
|
if (isBrowserScope) {
|
|
13
14
|
/* Interaction-to-Next-Paint */
|
|
14
|
-
|
|
15
|
+
const handleINP = ({ value, attribution, id, entries }) => {
|
|
16
|
+
/* web-vitals v6 reports a synthetic INP (value 8, no entries, no interaction attribution) after bfcache restores when
|
|
17
|
+
every interaction stayed below the duration threshold; skip those so only measured interactions are reported, as in v4 */
|
|
18
|
+
if (!entries?.length) return
|
|
15
19
|
const attrs = {
|
|
16
20
|
metricId: id,
|
|
17
21
|
eventTarget: attribution.interactionTarget, // event* attrs deprecated in v4, kept for NR backwards compatibility
|
|
@@ -26,5 +30,6 @@ if (isBrowserScope) {
|
|
|
26
30
|
loadState: attribution.loadState
|
|
27
31
|
}
|
|
28
32
|
interactionToNextPaint.update({ value, attrs })
|
|
29
|
-
}
|
|
33
|
+
}
|
|
34
|
+
registerVital(() => onINP(handleINP))
|
|
30
35
|
}
|
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
import { onLCP } from 'web-vitals/attribution'
|
|
6
6
|
import { VitalMetric } from './vital-metric'
|
|
7
7
|
import { VITAL_NAMES } from './constants'
|
|
8
|
+
import { registerVital } from './register-vital'
|
|
8
9
|
import { initiallyHidden, isBrowserScope } from '../constants/runtime'
|
|
9
10
|
import { cleanURL } from '../url/clean-url'
|
|
10
11
|
|
|
11
12
|
export const largestContentfulPaint = new VitalMetric(VITAL_NAMES.LARGEST_CONTENTFUL_PAINT)
|
|
12
13
|
|
|
13
14
|
if (isBrowserScope) {
|
|
14
|
-
|
|
15
|
+
const handleLCP = ({ value, attribution }) => {
|
|
15
16
|
/* Largest Contentful Paint - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
|
|
16
17
|
if (initiallyHidden || largestContentfulPaint.isValid) return
|
|
17
18
|
|
|
@@ -28,9 +29,10 @@ if (isBrowserScope) {
|
|
|
28
29
|
attrs.eid = lcpEntry.id
|
|
29
30
|
if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName
|
|
30
31
|
}
|
|
31
|
-
if (attribution.
|
|
32
|
+
if (attribution.target) attrs.element = attribution.target // renamed from `element` in web-vitals v4->v5; NR attr name kept for backwards compatibility
|
|
32
33
|
if (attribution.url) attrs.elUrl = cleanURL(attribution.url)
|
|
33
34
|
|
|
34
35
|
largestContentfulPaint.update({ value, attrs })
|
|
35
|
-
}
|
|
36
|
+
}
|
|
37
|
+
registerVital(() => onLCP(handleLCP))
|
|
36
38
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Invokes a web-vitals registration (e.g. `onLCP(callback)`), tolerating environments where the registration itself
|
|
8
|
+
* throws. web-vitals >= 5 reads Performance Timeline APIs such as `performance.getEntriesByType` unguarded when a
|
|
9
|
+
* metric is registered; where those are missing the metric is simply never reported, instead of the throw failing
|
|
10
|
+
* the feature that imported it.
|
|
11
|
+
* @param {() => void} register
|
|
12
|
+
*/
|
|
13
|
+
export function registerVital (register) {
|
|
14
|
+
try {
|
|
15
|
+
register()
|
|
16
|
+
} catch (e) {
|
|
17
|
+
// metric unavailable in this environment
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { globalScope, isBrowserScope, isiOS, originTime, getNavigationEntry } from '../constants/runtime'
|
|
6
6
|
import { VITAL_NAMES } from './constants'
|
|
7
7
|
import { VitalMetric } from './vital-metric'
|
|
8
|
+
import { registerVital } from './register-vital'
|
|
8
9
|
import { onTTFB } from 'web-vitals/attribution'
|
|
9
10
|
|
|
10
11
|
export const timeToFirstByte = new VitalMetric(VITAL_NAMES.TIME_TO_FIRST_BYTE)
|
|
@@ -18,10 +19,11 @@ export const timeToFirstByte = new VitalMetric(VITAL_NAMES.TIME_TO_FIRST_BYTE)
|
|
|
18
19
|
* - onTTFB relies on a truthy `responseStart` value, should ensure that exists before relying on it (seen to be falsy in certain Electron.js cases for instance)
|
|
19
20
|
*/
|
|
20
21
|
if (isBrowserScope && getNavigationEntry() && !isiOS && window === window.parent) {
|
|
21
|
-
|
|
22
|
+
const handleTTFB = ({ value, attribution }) => {
|
|
22
23
|
if (timeToFirstByte.isValid) return
|
|
23
24
|
timeToFirstByte.update({ value, attrs: { navigationEntry: attribution.navigationEntry } })
|
|
24
|
-
}
|
|
25
|
+
}
|
|
26
|
+
registerVital(() => onTTFB(handleTTFB))
|
|
25
27
|
} else {
|
|
26
28
|
if (!timeToFirstByte.isValid) {
|
|
27
29
|
const entry = {}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Copyright 2020-
|
|
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 {
|
|
@@ -17,7 +17,8 @@ export class VitalMetric {
|
|
|
17
17
|
const state = {
|
|
18
18
|
value: this.roundingMethod(value),
|
|
19
19
|
name: this.name,
|
|
20
|
-
|
|
20
|
+
// optional web-vitals attribution fields are omitted rather than being serialized as null attributes
|
|
21
|
+
attrs: Object.fromEntries(Object.entries(attrs).filter(([, attrValue]) => attrValue !== undefined))
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
this.history.push(state)
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { onCLS, onFCP, onINP, onLCP } from 'web-vitals'
|
|
8
8
|
// internal
|
|
9
9
|
import { globalScope, isBrowserScope } from '../common/constants/runtime'
|
|
10
|
+
import { registerVital } from '../common/vitals/register-vital'
|
|
10
11
|
import { isIFrameWindow } from '../common/dom/iframe'
|
|
11
12
|
import { now } from '../common/timing/now'
|
|
12
13
|
import { warn } from '../common/util/console'
|
|
@@ -179,12 +180,16 @@ export class RegisteredIframeEntity {
|
|
|
179
180
|
*/
|
|
180
181
|
#setupVitalsListeners () {
|
|
181
182
|
VITALS.forEach(([vitalFn, property]) => {
|
|
182
|
-
vitalFn(({ value }) => {
|
|
183
|
+
registerVital(() => vitalFn(({ value, entries: vitalEntries }) => {
|
|
184
|
+
/* web-vitals v6 reports a synthetic INP (value 8, no entries) after bfcache restores when
|
|
185
|
+
every interaction stayed below the duration threshold; skip those so only measured
|
|
186
|
+
interactions are reported, matching the guard in src/common/vitals/interaction-to-next-paint.js */
|
|
187
|
+
if (property === 'inp' && !vitalEntries?.length) return
|
|
183
188
|
this.metadata.vitals[property].value = value
|
|
184
189
|
this.#postMessageToParent(IFRAME_VITALS_UPDATE, {
|
|
185
190
|
entries: [{ property, value }]
|
|
186
191
|
})
|
|
187
|
-
}, { reportAllChanges: property === 'cls' || property === 'inp' })
|
|
192
|
+
}, { reportAllChanges: property === 'cls' || property === 'inp' }))
|
|
188
193
|
})
|
|
189
194
|
}
|
|
190
195
|
|