Skip to content

Commit 7fa7711

Browse files
committed
feat: added softnav util and update metric APIs
1 parent 6c383c4 commit 7fa7711

File tree

4 files changed

+79
-9
lines changed

4 files changed

+79
-9
lines changed

packages/browser-utils/src/metrics/web-vitals/lib/initMetric.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,33 @@ import type { MetricType } from '../types';
1919
import { generateUniqueID } from './generateUniqueID';
2020
import { getActivationStart } from './getActivationStart';
2121
import { getNavigationEntry } from './getNavigationEntry';
22+
import { getSoftNavigationEntry } from './softNavs';
2223

23-
export const initMetric = <MetricName extends MetricType['name']>(name: MetricName, value: number = -1) => {
24-
const navEntry = getNavigationEntry();
24+
export const initMetric = <MetricName extends MetricType['name']>(
25+
name: MetricName,
26+
value: number = -1,
27+
navigation?: MetricType['navigationType'],
28+
navigationId?: string,
29+
) => {
30+
const hardNavId = getNavigationEntry()?.navigationId || '1';
31+
const hardNavEntry = getNavigationEntry();
2532
let navigationType: MetricType['navigationType'] = 'navigate';
2633

27-
if (navEntry) {
34+
if (navigation) {
35+
// If it was passed in, then use that
36+
navigationType = navigation;
37+
} else if (hardNavEntry) {
2838
if (WINDOW.document?.prerendering || getActivationStart() > 0) {
2939
navigationType = 'prerender';
3040
} else if (WINDOW.document?.wasDiscarded) {
3141
navigationType = 'restore';
32-
} else if (navEntry.type) {
33-
navigationType = navEntry.type.replace(/_/g, '-') as MetricType['navigationType'];
42+
} else if (hardNavEntry.type) {
43+
navigationType = hardNavEntry.type.replace(/_/g, '-') as MetricType['navigationType'];
3444
}
3545
}
3646

3747
// Use `entries` type specific for the metric.
3848
const entries: Extract<MetricType, { name: MetricName }>['entries'] = [];
39-
4049
return {
4150
name,
4251
value,
@@ -45,5 +54,7 @@ export const initMetric = <MetricName extends MetricType['name']>(name: MetricNa
4554
entries,
4655
id: generateUniqueID(),
4756
navigationType,
57+
navigationId: navigationId || hardNavId,
58+
navigationURL: getSoftNavigationEntry(navigationId)?.name || getNavigationEntry()?.name,
4859
};
4960
};

packages/browser-utils/src/metrics/web-vitals/lib/observe.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
import { softNavs } from './softNavs';
1617

1718
interface PerformanceEntryMap {
1819
event: PerformanceEventTiming[];
1920
'first-input': PerformanceEventTiming[];
21+
'interaction-contentful-paint': InteractionContentfulPaint[];
2022
'layout-shift': LayoutShift[];
2123
'largest-contentful-paint': LargestContentfulPaint[];
2224
'long-animation-frame': PerformanceLongAnimationFrameTiming[];
2325
paint: PerformancePaintTiming[];
2426
navigation: PerformanceNavigationTiming[];
2527
resource: PerformanceResourceTiming[];
28+
'soft-navigation': SoftNavigationEntry[];
2629
// Sentry-specific change:
2730
// We add longtask as a supported entry type as we use this in
2831
// our `instrumentPerformanceObserver` function also observes 'longtask'
@@ -46,6 +49,8 @@ export const observe = <K extends keyof PerformanceEntryMap>(
4649
callback: (entries: PerformanceEntryMap[K]) => void,
4750
opts: PerformanceObserverInit = {},
4851
): PerformanceObserver | undefined => {
52+
const includeSoftNavigationObservations = softNavs(opts);
53+
4954
try {
5055
if (PerformanceObserver.supportedEntryTypes.includes(type)) {
5156
const po = new PerformanceObserver(list => {
@@ -57,7 +62,13 @@ export const observe = <K extends keyof PerformanceEntryMap>(
5762
callback(list.getEntries() as PerformanceEntryMap[K]);
5863
});
5964
});
60-
po.observe({ type, buffered: true, ...opts });
65+
po.observe({
66+
type,
67+
buffered: true,
68+
includeSoftNavigationObservations,
69+
...opts,
70+
} as PerformanceObserverInit);
71+
6172
return po;
6273
}
6374
} catch {

packages/browser-utils/src/metrics/web-vitals/lib/polyfills/interactionCountPolyfill.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
16+
import { getNavigationEntry } from '../getNavigationEntry';
1717
import { observe } from '../observe';
1818

1919
declare global {
@@ -25,10 +25,23 @@ declare global {
2525
let interactionCountEstimate = 0;
2626
let minKnownInteractionId = Infinity;
2727
let maxKnownInteractionId = 0;
28+
let currentNavId = '';
29+
let softNavsEnabled = false;
2830

2931
const updateEstimate = (entries: PerformanceEventTiming[]) => {
32+
if (!currentNavId) {
33+
currentNavId = getNavigationEntry()?.navigationId || '1';
34+
}
35+
3036
entries.forEach(e => {
3137
if (e.interactionId) {
38+
if (softNavsEnabled && e.navigationId && e.navigationId !== currentNavId) {
39+
currentNavId = e.navigationId;
40+
interactionCountEstimate = 0;
41+
minKnownInteractionId = Infinity;
42+
maxKnownInteractionId = 0;
43+
}
44+
3245
minKnownInteractionId = Math.min(minKnownInteractionId, e.interactionId);
3346
maxKnownInteractionId = Math.max(maxKnownInteractionId, e.interactionId);
3447

@@ -50,12 +63,15 @@ export const getInteractionCount = (): number => {
5063
/**
5164
* Feature detects native support or initializes the polyfill if needed.
5265
*/
53-
export const initInteractionCountPolyfill = (): void => {
66+
export const initInteractionCountPolyfill = (softNavs?: boolean) => {
5467
if ('interactionCount' in performance || po) return;
5568

69+
softNavsEnabled = softNavs || false;
70+
5671
po = observe('event', updateEstimate, {
5772
type: 'event',
5873
buffered: true,
5974
durationThreshold: 0,
75+
includeSoftNavigationObservations: softNavsEnabled,
6076
} as PerformanceObserverInit);
6177
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2023 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import type { ReportOpts } from '../types';
18+
19+
export const softNavs = (opts?: ReportOpts) => {
20+
return PerformanceObserver.supportedEntryTypes.includes('soft-navigation') && opts && opts.reportSoftNavs;
21+
};
22+
23+
export const getSoftNavigationEntry = (navigationId?: string): SoftNavigationEntry | undefined => {
24+
if (!navigationId) return;
25+
26+
const softNavEntry = globalThis.performance
27+
.getEntriesByType('soft-navigation')
28+
.filter(entry => entry.navigationId === navigationId);
29+
if (softNavEntry) return softNavEntry[0];
30+
31+
return;
32+
};

0 commit comments

Comments
 (0)