1
1
import { sortBy } from "es-toolkit" ;
2
2
import { get , isArray , isEmpty , set } from "es-toolkit/compat" ;
3
3
import type { TadaDocumentNode } from "gql.tada" ;
4
- import { type ArgumentNode , type DocumentNode , Kind , parse , type SelectionNode , visit } from "graphql" ;
4
+ import { type ArgumentNode , type DocumentNode , Kind , parse , visit } from "graphql" ;
5
5
import type { GraphQLClient , RequestDocument , RequestOptions , Variables } from "graphql-request" ;
6
6
7
7
// Constants for TheGraph limits
@@ -10,16 +10,12 @@ const FIRST_ARG = "first";
10
10
const SKIP_ARG = "skip" ;
11
11
const FETCH_ALL_DIRECTIVE = "fetchAll" ;
12
12
13
- interface ListField {
13
+ interface ListFieldWithFetchAllDirective {
14
14
path : string [ ] ;
15
15
fieldName : string ;
16
- alias ?: string ;
17
16
firstValue ?: number ;
18
17
skipValue ?: number ;
19
18
otherArgs : ArgumentNode [ ] ;
20
- selections ?: ReadonlyArray < SelectionNode > ;
21
- hasFetchAllDirective ?: boolean ;
22
- firstValueIsDefault ?: boolean ; // Track if first value was defaulted
23
19
}
24
20
25
21
/**
@@ -115,8 +111,8 @@ function extractFetchAllFields(
115
111
document : DocumentNode ,
116
112
variables ?: Variables ,
117
113
fetchAllFields ?: Set < string > ,
118
- ) : ListField [ ] {
119
- const fields : ListField [ ] = [ ] ;
114
+ ) : ListFieldWithFetchAllDirective [ ] {
115
+ const fields : ListFieldWithFetchAllDirective [ ] = [ ] ;
120
116
const pathStack : string [ ] = [ ] ;
121
117
122
118
visit ( document , {
@@ -174,13 +170,9 @@ function extractFetchAllFields(
174
170
fields . push ( {
175
171
path : [ ...pathStack ] ,
176
172
fieldName : node . name . value ,
177
- alias : node . alias ?. value ,
178
- firstValue : hasFetchAllDirective && ( firstValue ?? THE_GRAPH_LIMIT ) ,
179
- skipValue : hasFetchAllDirective && ( skipValue ?? 0 ) ,
173
+ firstValue : firstValue ?? THE_GRAPH_LIMIT ,
174
+ skipValue : skipValue ?? 0 ,
180
175
otherArgs,
181
- selections : node . selectionSet ?. selections ,
182
- hasFetchAllDirective,
183
- firstValueIsDefault : hasFetchAllDirective ? firstValue === undefined : false ,
184
176
} ) ;
185
177
}
186
178
} ,
@@ -196,7 +188,7 @@ function extractFetchAllFields(
196
188
// Create a query for a single field with specific pagination
197
189
function createSingleFieldQuery (
198
190
document : DocumentNode ,
199
- targetField : ListField ,
191
+ targetField : ListFieldWithFetchAllDirective ,
200
192
skip : number ,
201
193
first : number ,
202
194
) : DocumentNode {
@@ -251,7 +243,7 @@ function createSingleFieldQuery(
251
243
}
252
244
253
245
// Create query without list fields
254
- function createNonListQuery ( document : DocumentNode , listFields : ListField [ ] ) : DocumentNode | null {
246
+ function createNonListQuery ( document : DocumentNode , listFields : ListFieldWithFetchAllDirective [ ] ) : DocumentNode | null {
255
247
let hasFields = false ;
256
248
const pathStack : string [ ] = [ ] ;
257
249
@@ -319,7 +311,8 @@ export function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLC
319
311
async function executeListFieldPagination (
320
312
document : DocumentNode ,
321
313
variables : Variables | undefined ,
322
- field : ListField ,
314
+ field : ListFieldWithFetchAllDirective ,
315
+ requestHeaders ?: HeadersInit ,
323
316
) : Promise < unknown [ ] > {
324
317
const results : unknown [ ] = [ ] ;
325
318
let currentSkip = field . skipValue || 0 ;
@@ -332,11 +325,15 @@ export function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLC
332
325
while ( hasMore ) {
333
326
const query = createSingleFieldQuery ( document , field , currentSkip , batchSize ) ;
334
327
const existingVariables = filterVariables ( variables , query ) ?? { } ;
335
- const response = await theGraphClient . request ( query , {
336
- ...existingVariables ,
337
- first : batchSize ,
338
- skip : currentSkip ,
339
- } ) ;
328
+ const response = await theGraphClient . request (
329
+ query ,
330
+ {
331
+ ...existingVariables ,
332
+ first : batchSize ,
333
+ skip : currentSkip ,
334
+ } ,
335
+ requestHeaders ,
336
+ ) ;
340
337
341
338
// Use array path format for es-toolkit's get function
342
339
const data = get ( response , field . path ) ?? get ( response , field . fieldName ) ;
@@ -352,24 +349,8 @@ export function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLC
352
349
if ( isArray ( data ) && data . length > 0 ) {
353
350
results . push ( ...data ) ;
354
351
355
- // Continue fetching if:
356
- // 1. We have @fetchAll directive (fetch everything)
357
- // 2. We have an explicit first value > THE_GRAPH_LIMIT and haven't reached it
358
- // 3. We have a defaulted first value and got a full batch (treating it as "no explicit value")
359
- // 4. We have no first value and got a full batch
360
- if ( field . hasFetchAllDirective ) {
361
- // With @fetchAll , continue if we got a full batch
362
- hasMore = data . length === batchSize ;
363
- } else if ( field . firstValue && ! field . firstValueIsDefault ) {
364
- // With explicit first value (not defaulted), only continue if:
365
- // - We haven't reached the requested amount yet
366
- // - We got a full batch (indicating more data might exist)
367
- hasMore = data . length === batchSize && results . length < field . firstValue ;
368
- } else {
369
- // When first is not specified or was defaulted (using default batch size),
370
- // continue if we got a full batch (standard TheGraph pagination behavior)
371
- hasMore = data . length === batchSize ;
372
- }
352
+ // With @fetchAll , continue if we got a full batch
353
+ hasMore = data . length === batchSize ;
373
354
} else {
374
355
hasMore = false ;
375
356
}
@@ -384,16 +365,20 @@ export function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLC
384
365
async query < TResult , TVariables extends Variables > (
385
366
documentOrOptions : TadaDocumentNode < TResult , TVariables > | RequestDocument | RequestOptions < TVariables , TResult > ,
386
367
variablesRaw ?: Omit < TVariables , "skip" | "first" > ,
368
+ requestHeadersRaw ?: HeadersInit ,
387
369
) : Promise < TResult > {
388
370
let document : TadaDocumentNode < TResult , TVariables > | RequestDocument ;
389
371
let variables : Omit < TVariables , "skip" | "first" > ;
372
+ let requestHeaders : HeadersInit | undefined ;
390
373
391
374
if ( isRequestOptions ( documentOrOptions ) ) {
392
375
document = documentOrOptions . document ;
393
- variables = documentOrOptions . variables as TVariables ;
376
+ variables = ( documentOrOptions . variables ?? { } ) as TVariables ;
377
+ requestHeaders = documentOrOptions . requestHeaders ;
394
378
} else {
395
379
document = documentOrOptions ;
396
380
variables = variablesRaw ?? ( { } as TVariables ) ;
381
+ requestHeaders = requestHeadersRaw ;
397
382
}
398
383
399
384
// First, detect and strip @fetchAll directives
@@ -404,7 +389,7 @@ export function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLC
404
389
405
390
// If no list fields, execute normally
406
391
if ( listFields . length === 0 ) {
407
- return theGraphClient . request ( processedDocument , variables as Variables ) ;
392
+ return theGraphClient . request ( processedDocument , variables as Variables , requestHeaders ) ;
408
393
}
409
394
410
395
// Execute paginated queries for all list fields
@@ -416,7 +401,7 @@ export function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLC
416
401
// Process list fields in parallel for better performance
417
402
const fieldDataPromises = sortedFields . map ( async ( field ) => ( {
418
403
field,
419
- data : await executeListFieldPagination ( processedDocument , variables , field ) ,
404
+ data : await executeListFieldPagination ( processedDocument , variables , field , requestHeaders ) ,
420
405
} ) ) ;
421
406
422
407
const fieldResults = await Promise . all ( fieldDataPromises ) ;
@@ -434,6 +419,7 @@ export function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLC
434
419
const nonListResult = await theGraphClient . request (
435
420
nonListQuery ,
436
421
filterVariables ( variables , nonListQuery ) ?? { } ,
422
+ requestHeaders ,
437
423
) ;
438
424
439
425
// Merge results, preserving list data
@@ -447,5 +433,5 @@ export function createTheGraphClientWithPagination(theGraphClient: Pick<GraphQLC
447
433
}
448
434
449
435
function isRequestOptions ( args : unknown ) : args is RequestOptions < Variables , unknown > {
450
- return typeof args === "object" && args !== null && "document" in args && "variables" in args ;
436
+ return typeof args === "object" && args !== null && "document" in args ;
451
437
}
0 commit comments