1- import { Agent } from 'undici'
21import type { HttpResponse , RetryConfig , CustomFetch , CustomFetchResponse } from '../types/http'
32import { isNetworkError } from '../types/http'
43
@@ -16,13 +15,40 @@ const DEFAULT_RETRY_CONFIG: RetryConfig = {
1615}
1716
1817/**
19- * HTTP agent with keepAlive disabled to prevent hanging connections
20- * This ensures the process exits immediately after requests complete
18+ * Type for the undici Agent - represents what we need from the Agent instance
19+ * We don't need to match all properties, just what's required for the dispatcher
2120 */
22- const httpAgent = new Agent ( {
23- keepAliveTimeout : 1 , // Close connections after 1ms of idle time
24- keepAliveMaxTimeout : 1 , // Maximum time to keep connections alive
25- } )
21+ type UndiciAgent = {
22+ // This is an opaque type - we just need to be able to pass it as a dispatcher
23+ readonly [ key : string ] : unknown
24+ }
25+
26+ /**
27+ * Cached HTTP agent to prevent creating multiple agents
28+ * null = not initialized, undefined = browser env, UndiciAgent = Node.js env
29+ */
30+ let httpAgent : UndiciAgent | undefined | null = null
31+
32+ /**
33+ * Gets the HTTP agent for Node.js environments or undefined for browser environments.
34+ * Uses dynamic import to avoid loading undici in browser environments.
35+ */
36+ async function getHttpAgent ( ) : Promise < UndiciAgent | undefined > {
37+ if ( httpAgent === null ) {
38+ if ( typeof process !== 'undefined' && process . versions ?. node ) {
39+ // We're in Node.js - dynamically import undici
40+ const { Agent } = await import ( 'undici' )
41+ httpAgent = new Agent ( {
42+ keepAliveTimeout : 1 , // Close connections after 1ms of idle time
43+ keepAliveMaxTimeout : 1 , // Maximum time to keep connections alive
44+ } ) as unknown as UndiciAgent
45+ } else {
46+ // We're in browser - no agent needed
47+ httpAgent = undefined
48+ }
49+ }
50+ return httpAgent
51+ }
2652
2753/**
2854 * Converts Headers object to a plain object
@@ -141,7 +167,7 @@ export async function fetchWithRetry<T = unknown>(args: {
141167 ...fetchOptions ,
142168 signal : requestSignal ,
143169 // @ts -expect-error - dispatcher is a valid option for Node.js fetch but not in the TS types
144- dispatcher : httpAgent ,
170+ dispatcher : await getHttpAgent ( ) ,
145171 } )
146172 fetchResponse = convertResponseToCustomFetch ( nativeResponse )
147173 }
0 commit comments