11/**
2- * GraphQL client configuration and execution (Node.js with undici )
2+ * GraphQL client configuration and execution (Node.js with native http/https )
33 *
44 * This is the RUNTIME code that gets copied to generated output.
5- * Uses undici fetch with dispatcher support for localhost DNS resolution .
5+ * Uses native Node.js http/https modules .
66 *
77 * NOTE: This file is read at codegen time and written to output.
88 * Any changes here will affect all generated clients.
99 */
1010
11- import dns from 'node:dns ' ;
12- import { Agent , fetch , type RequestInit } from 'undici ' ;
11+ import http from 'node:http ' ;
12+ import https from 'node:https ' ;
1313
1414// ============================================================================
15- // Localhost DNS Resolution
15+ // HTTP Request Helper
1616// ============================================================================
1717
18- /**
19- * Check if a hostname is localhost or a localhost subdomain
20- */
21- function isLocalhostHostname ( hostname : string ) : boolean {
22- return hostname === 'localhost' || hostname . endsWith ( '.localhost' ) ;
18+ interface HttpResponse {
19+ statusCode : number ;
20+ statusMessage : string ;
21+ data : string ;
2322}
2423
2524/**
26- * Create an undici Agent that resolves *.localhost to 127.0.0.1
27- * This fixes DNS resolution issues on macOS where subdomains like api.localhost
28- * don't resolve automatically (unlike browsers which handle *.localhost).
25+ * Make an HTTP/HTTPS request using native Node modules
2926 */
30- function createLocalhostAgent ( ) : Agent {
31- return new Agent ( {
32- connect : {
33- lookup ( hostname , opts , cb ) {
34- if ( isLocalhostHostname ( hostname ) ) {
35- // When opts.all is true, callback expects an array of {address, family} objects
36- // When opts.all is false/undefined, callback expects (err, address, family)
37- if ( opts . all ) {
38- cb ( null , [ { address : '127.0.0.1' , family : 4 } ] ) ;
39- } else {
40- cb ( null , '127.0.0.1' , 4 ) ;
41- }
42- return ;
43- }
44- dns . lookup ( hostname , opts , cb ) ;
45- } ,
46- } ,
27+ function makeRequest (
28+ url : URL ,
29+ options : http . RequestOptions ,
30+ body : string
31+ ) : Promise < HttpResponse > {
32+ return new Promise ( ( resolve , reject ) => {
33+ const protocol = url . protocol === 'https:' ? https : http ;
34+
35+ const req = protocol . request ( url , options , ( res ) => {
36+ let data = '' ;
37+ res . setEncoding ( 'utf8' ) ;
38+ res . on ( 'data' , ( chunk : string ) => {
39+ data += chunk ;
40+ } ) ;
41+ res . on ( 'end' , ( ) => {
42+ resolve ( {
43+ statusCode : res . statusCode || 0 ,
44+ statusMessage : res . statusMessage || '' ,
45+ data,
46+ } ) ;
47+ } ) ;
48+ } ) ;
49+
50+ req . on ( 'error' , reject ) ;
51+ req . write ( body ) ;
52+ req . end ( ) ;
4753 } ) ;
4854}
4955
50- let localhostAgent : Agent | null = null ;
51-
52- function getLocalhostAgent ( ) : Agent {
53- if ( ! localhostAgent ) {
54- localhostAgent = createLocalhostAgent ( ) ;
55- }
56- return localhostAgent ;
57- }
58-
59- /**
60- * Get fetch options with localhost agent if needed
61- */
62- function getFetchOptions (
63- endpoint : string ,
64- baseOptions : RequestInit
65- ) : RequestInit {
66- const url = new URL ( endpoint ) ;
67- if ( isLocalhostHostname ( url . hostname ) ) {
68- const options : RequestInit = {
69- ...baseOptions ,
70- dispatcher : getLocalhostAgent ( ) ,
71- } ;
72- // Set Host header for localhost subdomains to preserve routing
73- if ( url . hostname !== 'localhost' ) {
74- options . headers = {
75- ...( baseOptions . headers as Record < string , string > ) ,
76- Host : url . hostname ,
77- } ;
78- }
79- return options ;
80- }
81- return baseOptions ;
82- }
83-
8456// ============================================================================
8557// Configuration
8658// ============================================================================
@@ -174,7 +146,7 @@ export class GraphQLClientError extends Error {
174146 constructor (
175147 message : string ,
176148 public errors : GraphQLError [ ] ,
177- public response ?: Response
149+ public statusCode ?: number
178150 ) {
179151 super ( message ) ;
180152 this . name = 'GraphQLClientError' ;
@@ -188,8 +160,6 @@ export class GraphQLClientError extends Error {
188160export interface ExecuteOptions {
189161 /** Override headers for this request */
190162 headers ?: Record < string , string > ;
191- /** AbortSignal for request cancellation */
192- signal ?: AbortSignal ;
193163}
194164
195165/**
@@ -212,25 +182,29 @@ export async function execute<
212182 options ?: ExecuteOptions
213183) : Promise < TData > {
214184 const config = getConfig ( ) ;
185+ const url = new URL ( config . endpoint ) ;
186+
187+ const body = JSON . stringify ( {
188+ query : document ,
189+ variables,
190+ } ) ;
215191
216- const baseOptions : RequestInit = {
192+ const requestOptions : http . RequestOptions = {
217193 method : 'POST' ,
218194 headers : {
219195 'Content-Type' : 'application/json' ,
220196 ...config . headers ,
221197 ...options ?. headers ,
222198 } ,
223- body : JSON . stringify ( {
224- query : document ,
225- variables,
226- } ) ,
227- signal : options ?. signal ,
228199 } ;
229200
230- const fetchOptions = getFetchOptions ( config . endpoint , baseOptions ) ;
231- const response = await fetch ( config . endpoint , fetchOptions ) ;
201+ const response = await makeRequest ( url , requestOptions , body ) ;
202+
203+ if ( response . statusCode < 200 || response . statusCode >= 300 ) {
204+ throw new Error ( `HTTP ${ response . statusCode } : ${ response . statusMessage } ` ) ;
205+ }
232206
233- const json = ( await response . json ( ) ) as {
207+ const json = JSON . parse ( response . data ) as {
234208 data ?: TData ;
235209 errors ?: GraphQLError [ ] ;
236210 } ;
@@ -239,7 +213,7 @@ export async function execute<
239213 throw new GraphQLClientError (
240214 json . errors [ 0 ] . message || 'GraphQL request failed' ,
241215 json . errors ,
242- response as unknown as Response
216+ response . statusCode
243217 ) ;
244218 }
245219
@@ -259,25 +233,32 @@ export async function executeWithErrors<
259233 options ?: ExecuteOptions
260234) : Promise < { data : TData | null ; errors : GraphQLError [ ] | null } > {
261235 const config = getConfig ( ) ;
236+ const url = new URL ( config . endpoint ) ;
262237
263- const baseOptions : RequestInit = {
238+ const body = JSON . stringify ( {
239+ query : document ,
240+ variables,
241+ } ) ;
242+
243+ const requestOptions : http . RequestOptions = {
264244 method : 'POST' ,
265245 headers : {
266246 'Content-Type' : 'application/json' ,
267247 ...config . headers ,
268248 ...options ?. headers ,
269249 } ,
270- body : JSON . stringify ( {
271- query : document ,
272- variables,
273- } ) ,
274- signal : options ?. signal ,
275250 } ;
276251
277- const fetchOptions = getFetchOptions ( config . endpoint , baseOptions ) ;
278- const response = await fetch ( config . endpoint , fetchOptions ) ;
252+ const response = await makeRequest ( url , requestOptions , body ) ;
253+
254+ if ( response . statusCode < 200 || response . statusCode >= 300 ) {
255+ return {
256+ data : null ,
257+ errors : [ { message : `HTTP ${ response . statusCode } : ${ response . statusMessage } ` } ] ,
258+ } ;
259+ }
279260
280- const json = ( await response . json ( ) ) as {
261+ const json = JSON . parse ( response . data ) as {
281262 data ?: TData ;
282263 errors ?: GraphQLError [ ] ;
283264 } ;
0 commit comments