Skip to content

Commit 1a930b1

Browse files
committed
Enhance trace context handling with status management
1 parent ccb0361 commit 1a930b1

File tree

1 file changed

+42
-39
lines changed

1 file changed

+42
-39
lines changed

packages/remix/src/client/serverTimingTracePropagation.ts

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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
1116
let 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
*/
137141
export 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

Comments
 (0)