diff --git a/README.md b/README.md index 429e0e9b..164a1a19 100644 --- a/README.md +++ b/README.md @@ -761,6 +761,20 @@ Metric-specific subclasses: - [`LCPMetricWithAttribution`](/src/types/lcp.ts#:~:text=interface%20LCPMetricWithAttribution) - [`TTFBMetricWithAttribution`](/src/types/ttfb.ts#:~:text=interface%20TTFBMetricWithAttribution) +#### `MetricThresholds` + +```ts +/** + * The ranges defining a metric's "good", "needs improvement", and "poor" + * thresholds: + * + * - Metric values ≦ [0] are "good" + * - Metric values > [0] and ≦ [1] are "needs improvement" + * - Metric values > [1] are "poor". + */ +type MetricThresholds = [number, number]; +``` + #### `ReportCallback` ```ts diff --git a/src/index.ts b/src/index.ts index 375dd0ab..22ea7c91 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -export {onCLS} from './onCLS.js'; -export {onFCP} from './onFCP.js'; -export {onFID} from './onFID.js'; -export {onINP} from './onINP.js'; -export {onLCP} from './onLCP.js'; -export {onTTFB} from './onTTFB.js'; +export {onCLS, CLSThresholds} from './onCLS.js'; +export {onFCP, FCPThresholds} from './onFCP.js'; +export {onFID, FIDThresholds} from './onFID.js'; +export {onINP, INPThresholds} from './onINP.js'; +export {onLCP, LCPThresholds} from './onLCP.js'; +export {onTTFB, TTFBThresholds} from './onTTFB.js'; export * from './deprecated.js'; export * from './types.js'; diff --git a/src/lib/bindReporter.ts b/src/lib/bindReporter.ts index d758055c..f3c42903 100644 --- a/src/lib/bindReporter.ts +++ b/src/lib/bindReporter.ts @@ -14,9 +14,12 @@ * limitations under the License. */ -import {Metric, ReportCallback} from '../types.js'; +import {Metric, MetricThresholds, ReportCallback} from '../types.js'; -const getRating = (value: number, thresholds: number[]) => { +const getRating = ( + value: number, + thresholds: MetricThresholds +): Metric['rating'] => { if (value > thresholds[1]) { return 'poor'; } diff --git a/src/onCLS.ts b/src/onCLS.ts index cea38fdb..29ac5ab1 100644 --- a/src/onCLS.ts +++ b/src/onCLS.ts @@ -22,7 +22,15 @@ import {doubleRAF} from './lib/doubleRAF.js'; import {onHidden} from './lib/onHidden.js'; import {runOnce} from './lib/runOnce.js'; import {onFCP} from './onFCP.js'; -import {CLSMetric, CLSReportCallback, ReportOpts} from './types.js'; +import { + CLSMetric, + CLSReportCallback, + MetricThresholds, + ReportOpts, +} from './types.js'; + +// https://web.dev/cls/#what-is-a-good-cls-score +export const CLSThresholds: MetricThresholds = [0.1, 0.25]; /** * Calculates the [CLS](https://web.dev/cls/) value for the current page and @@ -53,9 +61,6 @@ export const onCLS = (onReport: CLSReportCallback, opts?: ReportOpts) => { // Note: this is done to match the current behavior of CrUX. onFCP( runOnce(() => { - // https://web.dev/cls/#what-is-a-good-cls-score - const thresholds = [0.1, 0.25]; - let metric = initMetric('CLS', 0); let report: ReturnType; @@ -102,7 +107,7 @@ export const onCLS = (onReport: CLSReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + CLSThresholds, opts!.reportAllChanges ); @@ -119,7 +124,7 @@ export const onCLS = (onReport: CLSReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + CLSThresholds, opts!.reportAllChanges ); diff --git a/src/onFCP.ts b/src/onFCP.ts index fb804838..86a8b6e2 100644 --- a/src/onFCP.ts +++ b/src/onFCP.ts @@ -22,7 +22,15 @@ import {getVisibilityWatcher} from './lib/getVisibilityWatcher.js'; import {initMetric} from './lib/initMetric.js'; import {observe} from './lib/observe.js'; import {whenActivated} from './lib/whenActivated.js'; -import {FCPMetric, FCPReportCallback, ReportOpts} from './types.js'; +import { + FCPMetric, + FCPReportCallback, + MetricThresholds, + ReportOpts, +} from './types.js'; + +// https://web.dev/fcp/#what-is-a-good-fcp-score +export const FCPThresholds: MetricThresholds = [1800, 3000]; /** * Calculates the [FCP](https://web.dev/fcp/) value for the current page and @@ -35,9 +43,6 @@ export const onFCP = (onReport: FCPReportCallback, opts?: ReportOpts) => { opts = opts || {}; whenActivated(() => { - // https://web.dev/fcp/#what-is-a-good-fcp-score - const thresholds = [1800, 3000]; - const visibilityWatcher = getVisibilityWatcher(); let metric = initMetric('FCP'); let report: ReturnType; @@ -67,7 +72,7 @@ export const onFCP = (onReport: FCPReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + FCPThresholds, opts!.reportAllChanges ); @@ -78,7 +83,7 @@ export const onFCP = (onReport: FCPReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + FCPThresholds, opts!.reportAllChanges ); diff --git a/src/onFID.ts b/src/onFID.ts index 90fa704b..74f22673 100644 --- a/src/onFID.ts +++ b/src/onFID.ts @@ -29,10 +29,14 @@ import {whenActivated} from './lib/whenActivated.js'; import { FIDMetric, FirstInputPolyfillCallback, + MetricThresholds, ReportCallback, ReportOpts, } from './types.js'; +// https://web.dev/fid/#what-is-a-good-fid-score +export const FIDThresholds: MetricThresholds = [100, 300]; + /** * Calculates the [FID](https://web.dev/fid/) value for the current page and * calls the `callback` function once the value is ready, along with the @@ -47,9 +51,6 @@ export const onFID = (onReport: ReportCallback, opts?: ReportOpts) => { opts = opts || {}; whenActivated(() => { - // https://web.dev/fid/#what-is-a-good-fid-score - const thresholds = [100, 300]; - const visibilityWatcher = getVisibilityWatcher(); let metric = initMetric('FID'); let report: ReturnType; @@ -68,7 +69,12 @@ export const onFID = (onReport: ReportCallback, opts?: ReportOpts) => { }; const po = observe('first-input', handleEntries); - report = bindReporter(onReport, metric, thresholds, opts!.reportAllChanges); + report = bindReporter( + onReport, + metric, + FIDThresholds, + opts!.reportAllChanges + ); if (po) { onHidden( @@ -95,7 +101,7 @@ export const onFID = (onReport: ReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + FIDThresholds, opts!.reportAllChanges ); @@ -112,7 +118,7 @@ export const onFID = (onReport: ReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + FIDThresholds, opts!.reportAllChanges ); diff --git a/src/onINP.ts b/src/onINP.ts index 95b38f5d..9c306106 100644 --- a/src/onINP.ts +++ b/src/onINP.ts @@ -24,7 +24,12 @@ import { initInteractionCountPolyfill, } from './lib/polyfills/interactionCountPolyfill.js'; import {whenActivated} from './lib/whenActivated.js'; -import {INPMetric, ReportCallback, ReportOpts} from './types.js'; +import { + INPMetric, + MetricThresholds, + ReportCallback, + ReportOpts, +} from './types.js'; interface Interaction { id: number; @@ -32,6 +37,9 @@ interface Interaction { entries: PerformanceEventTiming[]; } +// https://web.dev/inp/#what-is-a-good-inp-score +export const INPThresholds: MetricThresholds = [200, 500]; + // Used to store the interaction count after a bfcache restore, since p98 // interaction latencies should only consider the current navigation. let prevInteractionCount = 0; @@ -146,9 +154,6 @@ export const onINP = (onReport: ReportCallback, opts?: ReportOpts) => { opts = opts || {}; whenActivated(() => { - // https://web.dev/inp/#what-is-a-good-inp-score - const thresholds = [200, 500]; - // TODO(philipwalton): remove once the polyfill is no longer needed. initInteractionCountPolyfill(); @@ -205,7 +210,12 @@ export const onINP = (onReport: ReportCallback, opts?: ReportOpts) => { durationThreshold: opts!.durationThreshold || 40, } as PerformanceObserverInit); - report = bindReporter(onReport, metric, thresholds, opts!.reportAllChanges); + report = bindReporter( + onReport, + metric, + INPThresholds, + opts!.reportAllChanges + ); if (po) { // Also observe entries of type `first-input`. This is useful in cases @@ -237,7 +247,7 @@ export const onINP = (onReport: ReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + INPThresholds, opts!.reportAllChanges ); }); diff --git a/src/onLCP.ts b/src/onLCP.ts index c7586929..e54bea17 100644 --- a/src/onLCP.ts +++ b/src/onLCP.ts @@ -24,7 +24,15 @@ import {observe} from './lib/observe.js'; import {onHidden} from './lib/onHidden.js'; import {runOnce} from './lib/runOnce.js'; import {whenActivated} from './lib/whenActivated.js'; -import {LCPMetric, ReportCallback, ReportOpts} from './types.js'; +import { + LCPMetric, + MetricThresholds, + ReportCallback, + ReportOpts, +} from './types.js'; + +// https://web.dev/lcp/#what-is-a-good-lcp-score +export const LCPThresholds: MetricThresholds = [2500, 4000]; const reportedMetricIDs: Record = {}; @@ -44,9 +52,6 @@ export const onLCP = (onReport: ReportCallback, opts?: ReportOpts) => { opts = opts || {}; whenActivated(() => { - // https://web.dev/lcp/#what-is-a-good-lcp-score - const thresholds = [2500, 4000]; - const visibilityWatcher = getVisibilityWatcher(); let metric = initMetric('LCP'); let report: ReturnType; @@ -77,7 +82,7 @@ export const onLCP = (onReport: ReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + LCPThresholds, opts!.reportAllChanges ); @@ -106,7 +111,7 @@ export const onLCP = (onReport: ReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + LCPThresholds, opts!.reportAllChanges ); diff --git a/src/onTTFB.ts b/src/onTTFB.ts index 3f8407c0..bcf7eb65 100644 --- a/src/onTTFB.ts +++ b/src/onTTFB.ts @@ -18,10 +18,13 @@ import {bindReporter} from './lib/bindReporter.js'; import {initMetric} from './lib/initMetric.js'; import {onBFCacheRestore} from './lib/bfcache.js'; import {getNavigationEntry} from './lib/getNavigationEntry.js'; -import {ReportCallback, ReportOpts} from './types.js'; +import {MetricThresholds, ReportCallback, ReportOpts} from './types.js'; import {getActivationStart} from './lib/getActivationStart.js'; import {whenActivated} from './lib/whenActivated.js'; +// https://web.dev/ttfb/#what-is-a-good-ttfb-score +export const TTFBThresholds: MetricThresholds = [800, 1800]; + /** * Runs in the next task after the page is done loading and/or prerendering. * @param callback @@ -56,14 +59,11 @@ export const onTTFB = (onReport: ReportCallback, opts?: ReportOpts) => { // Set defaults opts = opts || {}; - // https://web.dev/ttfb/#what-is-a-good-ttfb-score - const thresholds = [800, 1800]; - let metric = initMetric('TTFB'); let report = bindReporter( onReport, metric, - thresholds, + TTFBThresholds, opts.reportAllChanges ); @@ -97,7 +97,7 @@ export const onTTFB = (onReport: ReportCallback, opts?: ReportOpts) => { report = bindReporter( onReport, metric, - thresholds, + TTFBThresholds, opts!.reportAllChanges ); diff --git a/src/types/base.ts b/src/types/base.ts index 70250f4a..6550c133 100644 --- a/src/types/base.ts +++ b/src/types/base.ts @@ -19,6 +19,16 @@ import { NavigationTimingPolyfillEntry, } from './polyfills.js'; +/** + * The ranges defining a metric's "good", "needs improvement", and "poor" + * thresholds: + * + * - Metric values ≦ [0] are "good" + * - Metric values > [0] and ≦ [1] are "needs improvement" + * - Metric values > [1] are "poor". + */ +export type MetricThresholds = [number, number]; + export interface Metric { /** * The name of the metric (in acronym form).