Skip to content

Commit fe05b4d

Browse files
committed
feat(browser): Bump web-vitals to 3.5.2
1 parent 6a09675 commit fe05b4d

27 files changed

+671
-364
lines changed

packages/tracing-internal/.eslintrc.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,11 @@ module.exports = {
77
'@sentry-internal/sdk/no-optional-chaining': 'off',
88
},
99
},
10+
{
11+
files: ['src/browser/web-vitals/**'],
12+
rules: {
13+
'@typescript-eslint/explicit-function-return-type': 'off',
14+
},
15+
},
1016
],
1117
};

packages/tracing-internal/src/browser/instrument.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ interface Metric {
6969
* support that API). For pages that are restored from the bfcache, this
7070
* value will be 'back-forward-cache'.
7171
*/
72-
navigationType: 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender';
72+
navigationType: 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender' | 'restore';
7373
}
7474

7575
type InstrumentHandlerType = InstrumentHandlerTypeMetric | InstrumentHandlerTypePerformanceObserver;

packages/tracing-internal/src/browser/metrics/index.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,46 @@ import {
1616
import { WINDOW } from '../types';
1717
import { getNavigationEntry } from '../web-vitals/lib/getNavigationEntry';
1818
import { getVisibilityWatcher } from '../web-vitals/lib/getVisibilityWatcher';
19-
import type { NavigatorDeviceMemory, NavigatorNetworkInformation } from '../web-vitals/types';
2019
import { isMeasurementValue, startAndEndSpan } from './utils';
2120

21+
interface NavigatorNetworkInformation {
22+
readonly connection?: NetworkInformation;
23+
}
24+
25+
// http://wicg.github.io/netinfo/#connection-types
26+
type ConnectionType = 'bluetooth' | 'cellular' | 'ethernet' | 'mixed' | 'none' | 'other' | 'unknown' | 'wifi' | 'wimax';
27+
28+
// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum
29+
type EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';
30+
31+
// http://wicg.github.io/netinfo/#dom-megabit
32+
type Megabit = number;
33+
// http://wicg.github.io/netinfo/#dom-millisecond
34+
type Millisecond = number;
35+
36+
// http://wicg.github.io/netinfo/#networkinformation-interface
37+
interface NetworkInformation extends EventTarget {
38+
// http://wicg.github.io/netinfo/#type-attribute
39+
readonly type?: ConnectionType;
40+
// http://wicg.github.io/netinfo/#effectivetype-attribute
41+
readonly effectiveType?: EffectiveConnectionType;
42+
// http://wicg.github.io/netinfo/#downlinkmax-attribute
43+
readonly downlinkMax?: Megabit;
44+
// http://wicg.github.io/netinfo/#downlink-attribute
45+
readonly downlink?: Megabit;
46+
// http://wicg.github.io/netinfo/#rtt-attribute
47+
readonly rtt?: Millisecond;
48+
// http://wicg.github.io/netinfo/#savedata-attribute
49+
readonly saveData?: boolean;
50+
// http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection
51+
onchange?: EventListener;
52+
}
53+
54+
// https://w3c.github.io/device-memory/#sec-device-memory-js-api
55+
interface NavigatorDeviceMemory {
56+
readonly deviceMemory?: number;
57+
}
58+
2259
const MAX_INT_AS_BYTES = 2147483647;
2360

2461
/**

packages/tracing-internal/src/browser/web-vitals/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
> A modular library for measuring the [Web Vitals](https://web.dev/vitals/) metrics on real users.
44
5-
This was vendored from: https://github.com/GoogleChrome/web-vitals: v3.0.4
5+
This was vendored from: https://github.com/GoogleChrome/web-vitals: v3.5.2
66

77
The commit SHA used is:
8-
[7f0ed0bfb03c356e348a558a3eda111b498a2a11](https://github.com/GoogleChrome/web-vitals/tree/7f0ed0bfb03c356e348a558a3eda111b498a2a11)
8+
[7b44bea0d5ba6629c5fd34c3a09cc683077871d0](https://github.com/GoogleChrome/web-vitals/tree/7b44bea0d5ba6629c5fd34c3a09cc683077871d0)
99

1010
Current vendored web vitals are:
1111

packages/tracing-internal/src/browser/web-vitals/getCLS.ts

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ import { bindReporter } from './lib/bindReporter';
1818
import { initMetric } from './lib/initMetric';
1919
import { observe } from './lib/observe';
2020
import { onHidden } from './lib/onHidden';
21-
import type { CLSMetric, ReportCallback, StopListening } from './types';
21+
import { runOnce } from './lib/runOnce';
22+
import { onFCP } from './onFCP';
23+
import type { CLSMetric, CLSReportCallback, MetricRatingThresholds, ReportOpts } from './types';
24+
25+
/** Thresholds for CLS. See https://web.dev/articles/cls#what_is_a_good_cls_score */
26+
export const CLSThresholds: MetricRatingThresholds = [0.1, 0.25];
2227

2328
/**
24-
* Calculates the [CLS](https://web.dev/cls/) value for the current page and
29+
* Calculates the [CLS](https://web.dev/articles/cls) value for the current page and
2530
* calls the `callback` function once the value is ready to be reported, along
2631
* with all `layout-shift` performance entries that were used in the metric
2732
* value calculation. The reported value is a `double` (corresponding to a
28-
* [layout shift score](https://web.dev/cls/#layout-shift-score)).
33+
* [layout shift score](https://web.dev/articles/cls#layout_shift_score)).
2934
*
3035
* If the `reportAllChanges` configuration option is set to `true`, the
3136
* `callback` function will be called as soon as the value is initially
@@ -41,63 +46,65 @@ import type { CLSMetric, ReportCallback, StopListening } from './types';
4146
* hidden. As a result, the `callback` function might be called multiple times
4247
* during the same page load._
4348
*/
44-
export const onCLS = (onReport: ReportCallback): StopListening | undefined => {
45-
const metric = initMetric('CLS', 0);
46-
let report: ReturnType<typeof bindReporter>;
49+
export const onCLS = (onReport: CLSReportCallback, opts: ReportOpts = {}): void => {
50+
// Start monitoring FCP so we can only report CLS if FCP is also reported.
51+
// Note: this is done to match the current behavior of CrUX.
52+
onFCP(
53+
runOnce(() => {
54+
const metric = initMetric('CLS', 0);
55+
let report: ReturnType<typeof bindReporter>;
4756

48-
let sessionValue = 0;
49-
let sessionEntries: PerformanceEntry[] = [];
57+
let sessionValue = 0;
58+
let sessionEntries: LayoutShift[] = [];
5059

51-
// const handleEntries = (entries: Metric['entries']) => {
52-
const handleEntries = (entries: LayoutShift[]): void => {
53-
entries.forEach(entry => {
54-
// Only count layout shifts without recent user input.
55-
if (!entry.hadRecentInput) {
56-
const firstSessionEntry = sessionEntries[0];
57-
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
60+
const handleEntries = (entries: LayoutShift[]): void => {
61+
entries.forEach(entry => {
62+
// Only count layout shifts without recent user input.
63+
if (!entry.hadRecentInput) {
64+
const firstSessionEntry = sessionEntries[0];
65+
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
5866

59-
// If the entry occurred less than 1 second after the previous entry and
60-
// less than 5 seconds after the first entry in the session, include the
61-
// entry in the current session. Otherwise, start a new session.
62-
if (
63-
sessionValue &&
64-
sessionEntries.length !== 0 &&
65-
entry.startTime - lastSessionEntry.startTime < 1000 &&
66-
entry.startTime - firstSessionEntry.startTime < 5000
67-
) {
68-
sessionValue += entry.value;
69-
sessionEntries.push(entry);
70-
} else {
71-
sessionValue = entry.value;
72-
sessionEntries = [entry];
73-
}
67+
// If the entry occurred less than 1 second after the previous entry
68+
// and less than 5 seconds after the first entry in the session,
69+
// include the entry in the current session. Otherwise, start a new
70+
// session.
71+
if (
72+
sessionValue &&
73+
entry.startTime - lastSessionEntry.startTime < 1000 &&
74+
entry.startTime - firstSessionEntry.startTime < 5000
75+
) {
76+
sessionValue += entry.value;
77+
sessionEntries.push(entry);
78+
} else {
79+
sessionValue = entry.value;
80+
sessionEntries = [entry];
81+
}
82+
}
83+
});
7484

7585
// If the current session value is larger than the current CLS value,
7686
// update CLS and the entries contributing to it.
7787
if (sessionValue > metric.value) {
7888
metric.value = sessionValue;
7989
metric.entries = sessionEntries;
80-
if (report) {
81-
report();
82-
}
90+
report();
8391
}
84-
}
85-
});
86-
};
87-
88-
const po = observe('layout-shift', handleEntries);
89-
if (po) {
90-
report = bindReporter(onReport, metric);
92+
};
9193

92-
const stopListening = (): void => {
93-
handleEntries(po.takeRecords() as CLSMetric['entries']);
94-
report(true);
95-
};
94+
const po = observe('layout-shift', handleEntries);
95+
if (po) {
96+
report = bindReporter(onReport, metric, CLSThresholds, opts.reportAllChanges);
9697

97-
onHidden(stopListening);
98+
onHidden(() => {
99+
handleEntries(po.takeRecords() as CLSMetric['entries']);
100+
report(true);
101+
});
98102

99-
return stopListening;
100-
}
101-
102-
return;
103+
// Queue a task to report (if nothing else triggers a report first).
104+
// This allows CLS to be reported as soon as FCP fires when
105+
// `reportAllChanges` is true.
106+
setTimeout(report, 0);
107+
}
108+
}),
109+
);
103110
};

packages/tracing-internal/src/browser/web-vitals/getFID.ts

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,74 @@
1414
* limitations under the License.
1515
*/
1616

17+
import { WINDOW } from '../types';
1718
import { bindReporter } from './lib/bindReporter';
1819
import { getVisibilityWatcher } from './lib/getVisibilityWatcher';
1920
import { initMetric } from './lib/initMetric';
2021
import { observe } from './lib/observe';
2122
import { onHidden } from './lib/onHidden';
22-
import type { FIDMetric, PerformanceEventTiming, ReportCallback } from './types';
23+
import { runOnce } from './lib/runOnce';
24+
import { whenActivated } from './lib/whenActivated';
25+
import type {
26+
FIDMetric,
27+
FIDReportCallback,
28+
FirstInputPolyfillCallback,
29+
MetricRatingThresholds,
30+
ReportOpts,
31+
} from './types';
32+
33+
/** Thresholds for FID. See https://web.dev/articles/fid#what_is_a_good_fid_score */
34+
export const FIDThresholds: MetricRatingThresholds = [100, 300];
2335

2436
/**
25-
* Calculates the [FID](https://web.dev/fid/) value for the current page and
37+
* Calculates the [FID](https://web.dev/articles/fid) value for the current page and
2638
* calls the `callback` function once the value is ready, along with the
2739
* relevant `first-input` performance entry used to determine the value. The
2840
* reported value is a `DOMHighResTimeStamp`.
2941
*
3042
* _**Important:** since FID is only reported after the user interacts with the
3143
* page, it's possible that it will not be reported for some page loads._
3244
*/
33-
export const onFID = (onReport: ReportCallback): void => {
34-
const visibilityWatcher = getVisibilityWatcher();
35-
const metric = initMetric('FID');
36-
// eslint-disable-next-line prefer-const
37-
let report: ReturnType<typeof bindReporter>;
38-
39-
const handleEntry = (entry: PerformanceEventTiming): void => {
40-
// Only report if the page wasn't hidden prior to the first input.
41-
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
42-
metric.value = entry.processingStart - entry.startTime;
43-
metric.entries.push(entry);
44-
report(true);
45-
}
46-
};
45+
export const onFID = (onReport: FIDReportCallback, opts: ReportOpts = {}): void => {
46+
whenActivated(() => {
47+
const visibilityWatcher = getVisibilityWatcher();
48+
const metric = initMetric('FID');
49+
// eslint-disable-next-line prefer-const
50+
let report: ReturnType<typeof bindReporter>;
4751

48-
const handleEntries = (entries: FIDMetric['entries']): void => {
49-
(entries as PerformanceEventTiming[]).forEach(handleEntry);
50-
};
52+
const handleEntry = (entry: PerformanceEventTiming) => {
53+
// Only report if the page wasn't hidden prior to the first input.
54+
if (entry.startTime < visibilityWatcher.firstHiddenTime) {
55+
metric.value = entry.processingStart - entry.startTime;
56+
metric.entries.push(entry);
57+
report(true);
58+
}
59+
};
5160

52-
const po = observe('first-input', handleEntries);
53-
report = bindReporter(onReport, metric);
61+
const handleEntries = (entries: FIDMetric['entries']) => {
62+
(entries as PerformanceEventTiming[]).forEach(handleEntry);
63+
};
5464

55-
if (po) {
56-
onHidden(() => {
57-
handleEntries(po.takeRecords() as FIDMetric['entries']);
58-
po.disconnect();
59-
}, true);
60-
}
65+
const po = observe('first-input', handleEntries);
66+
report = bindReporter(onReport, metric, FIDThresholds, opts.reportAllChanges);
67+
68+
if (po) {
69+
onHidden(
70+
runOnce(() => {
71+
handleEntries(po.takeRecords() as FIDMetric['entries']);
72+
po.disconnect();
73+
}),
74+
);
75+
}
76+
77+
if (WINDOW.__WEB_VITALS_POLYFILL__) {
78+
// eslint-disable-next-line no-console
79+
console.warn('The web-vitals "base+polyfill" build is deprecated. See: https://bit.ly/3aqzsGm');
80+
81+
// Prefer the native implementation if available,
82+
if (!po) {
83+
WINDOW.webVitals.firstInputPolyfill(handleEntry as FirstInputPolyfillCallback);
84+
}
85+
}
86+
});
6187
};

0 commit comments

Comments
 (0)