@@ -7,12 +7,18 @@ export interface ServerTimingTraceContext {
77 baggage : string ;
88}
99
10+ type NavigationTraceResult =
11+ | { status : 'pending' }
12+ | { status : 'unavailable' }
13+ | { status : 'available' ; data : ServerTimingTraceContext } ;
14+
1015// Cache for navigation trace to avoid repeated parsing
1116let navigationTraceCache : ServerTimingTraceContext | null | undefined ;
1217
13- // 5 attempts × 10ms = ~50ms max wait for Performance API to process navigation entries
14- const DEFAULT_RETRY_ATTEMPTS = 5 ;
15- const DEFAULT_RETRY_DELAY_MS = 10 ;
18+ // 40 attempts × 50ms = 2 seconds max wait for Performance API to process navigation entries
19+ // Aligned with react-router's hydration polling pattern (see hydratedRouter.ts)
20+ const MAX_RETRY_ATTEMPTS = 40 ;
21+ const RETRY_INTERVAL_MS = 50 ;
1622
1723/**
1824 * Check if Server-Timing API is supported in the current browser.
@@ -69,33 +75,32 @@ function parseServerTimingTrace(serverTiming: readonly PerformanceServerTiming[]
6975 return { sentryTrace, baggage } ;
7076}
7177
72- // Returns trace context, null if not available yet, or false if definitely not available
73- function tryGetNavigationTraceContext ( ) : ServerTimingTraceContext | null | false {
78+ function tryGetNavigationTraceContext ( ) : NavigationTraceResult {
7479 try {
7580 const navEntries = WINDOW . performance . getEntriesByType ( 'navigation' ) ;
7681
7782 if ( ! navEntries || navEntries . length === 0 ) {
78- return false ;
83+ return { status : 'unavailable' } ;
7984 }
8085
8186 const navEntry = navEntries [ 0 ] as PerformanceNavigationTiming ;
8287
8388 // responseStart === 0 means headers haven't been processed yet
8489 if ( navEntry . responseStart === 0 ) {
85- return null ;
90+ return { status : 'pending' } ;
8691 }
8792
8893 const serverTiming = navEntry . serverTiming ;
8994
9095 if ( ! serverTiming || serverTiming . length === 0 ) {
91- return false ;
96+ return { status : 'unavailable' } ;
9297 }
9398
9499 const result = parseServerTimingTrace ( serverTiming ) ;
95100
96- return result ?? false ;
101+ return result ? { status : 'available' , data : result } : { status : 'unavailable' } ;
97102 } catch {
98- return false ;
103+ return { status : 'unavailable' } ;
99104 }
100105}
101106
@@ -117,17 +122,16 @@ export function getNavigationTraceContext(): ServerTimingTraceContext | null {
117122
118123 const result = tryGetNavigationTraceContext ( ) ;
119124
120- if ( result === false ) {
121- navigationTraceCache = null ;
122- return null ;
123- }
124-
125- if ( result === null ) {
126- return null ;
125+ switch ( result . status ) {
126+ case 'unavailable' :
127+ navigationTraceCache = null ;
128+ return null ;
129+ case 'pending' :
130+ return null ;
131+ case 'available' :
132+ navigationTraceCache = result . data ;
133+ return result . data ;
127134 }
128-
129- navigationTraceCache = result ;
130- return result ;
131135}
132136
133137/**
@@ -136,8 +140,8 @@ export function getNavigationTraceContext(): ServerTimingTraceContext | null {
136140 */
137141export function getNavigationTraceContextAsync (
138142 callback : ( trace : ServerTimingTraceContext | null ) => void ,
139- maxAttempts : number = DEFAULT_RETRY_ATTEMPTS ,
140- delayMs : number = DEFAULT_RETRY_DELAY_MS ,
143+ maxAttempts : number = MAX_RETRY_ATTEMPTS ,
144+ delayMs : number = RETRY_INTERVAL_MS ,
141145) : void {
142146 if ( navigationTraceCache !== undefined ) {
143147 callback ( navigationTraceCache ) ;
@@ -157,25 +161,24 @@ export function getNavigationTraceContextAsync(
157161 attempts ++ ;
158162 const result = tryGetNavigationTraceContext ( ) ;
159163
160- if ( result === false ) {
161- navigationTraceCache = null ;
162- callback ( null ) ;
163- return ;
164- }
165-
166- if ( result === null ) {
167- if ( attempts < maxAttempts ) {
168- setTimeout ( tryGet , delayMs ) ;
164+ switch ( result . status ) {
165+ case 'unavailable' :
166+ navigationTraceCache = null ;
167+ callback ( null ) ;
169168 return ;
170- }
171- DEBUG_BUILD && debug . log ( '[Server-Timing] Max retry attempts reached' ) ;
172- navigationTraceCache = null ;
173- callback ( null ) ;
174- return ;
169+ case 'pending' :
170+ if ( attempts < maxAttempts ) {
171+ setTimeout ( tryGet , delayMs ) ;
172+ return ;
173+ }
174+ DEBUG_BUILD && debug . log ( '[Server-Timing] Max retry attempts reached' ) ;
175+ navigationTraceCache = null ;
176+ callback ( null ) ;
177+ return ;
178+ case 'available' :
179+ navigationTraceCache = result . data ;
180+ callback ( result . data ) ;
175181 }
176-
177- navigationTraceCache = result ;
178- callback ( result ) ;
179182 } ;
180183
181184 tryGet ( ) ;
0 commit comments