1
1
import {
2
2
Fetcher ,
3
- FetcherResultPayload ,
4
3
formatError ,
5
4
formatResult ,
6
5
isAsyncIterable ,
7
6
isObservable ,
8
7
Unsubscribable ,
9
8
} from '@graphiql/toolkit' ;
10
- import { ExecutionResult , FragmentDefinitionNode , print } from 'graphql' ;
9
+ import {
10
+ ExecutionResult ,
11
+ FragmentDefinitionNode ,
12
+ GraphQLError ,
13
+ print ,
14
+ } from 'graphql' ;
11
15
import { getFragmentDependenciesForAST } from 'graphql-language-service' ;
12
16
import { ReactNode , useCallback , useMemo , useRef , useState } from 'react' ;
13
17
import setValue from 'set-value' ;
@@ -183,7 +187,7 @@ export function ExecutionContextProvider({
183
187
} ) ;
184
188
185
189
try {
186
- let fullResponse : FetcherResultPayload = { data : { } } ;
190
+ const fullResponse : ExecutionResult = { } ;
187
191
const handleResponse = ( result : ExecutionResult ) => {
188
192
// A different query was dispatched in the meantime, so don't
189
193
// show the results of this one.
@@ -202,40 +206,8 @@ export function ExecutionContextProvider({
202
206
}
203
207
204
208
if ( maybeMultipart ) {
205
- const payload : FetcherResultPayload = {
206
- data : fullResponse . data ,
207
- } ;
208
- const maybeErrors = [
209
- ...( fullResponse ?. errors || [ ] ) ,
210
- ...maybeMultipart . flatMap ( i => i . errors ) . filter ( Boolean ) ,
211
- ] ;
212
-
213
- if ( maybeErrors . length ) {
214
- payload . errors = maybeErrors ;
215
- }
216
-
217
209
for ( const part of maybeMultipart ) {
218
- // We pull out errors here, so we dont include it later
219
- const { path, data, errors, ...rest } = part ;
220
- if ( path ) {
221
- if ( ! data ) {
222
- throw new Error (
223
- `Expected part to contain a data property, but got ${ part } ` ,
224
- ) ;
225
- }
226
-
227
- setValue ( payload . data , path , data , { merge : true } ) ;
228
- } else if ( data ) {
229
- // If there is no path, we don't know what to do with the payload,
230
- // so we just set it.
231
- payload . data = data ;
232
- }
233
-
234
- // Ensures we also bring extensions and alike along for the ride
235
- fullResponse = {
236
- ...payload ,
237
- ...rest ,
238
- } ;
210
+ mergeIncrementalResult ( fullResponse , part ) ;
239
211
}
240
212
241
213
setIsFetching ( false ) ;
@@ -361,3 +333,61 @@ function tryParseJsonObject({
361
333
}
362
334
return parsed ;
363
335
}
336
+
337
+ type IncrementalResult = {
338
+ data ?: Record < string , unknown > | null ;
339
+ errors ?: ReadonlyArray < GraphQLError > ;
340
+ extensions ?: Record < string , unknown > ;
341
+ hasNext ?: boolean ;
342
+ path ?: ReadonlyArray < string | number > ;
343
+ incremental ?: ReadonlyArray < IncrementalResult > ;
344
+ label ?: string ;
345
+ items ?: ReadonlyArray < Record < string , unknown > > | null ;
346
+ } ;
347
+
348
+ /**
349
+ * @param executionResult The complete execution result object which will be
350
+ * mutated by merging the contents of the incremental result.
351
+ * @param incrementalResult The incremental result that will be merged into the
352
+ * complete execution result.
353
+ */
354
+ function mergeIncrementalResult (
355
+ executionResult : ExecutionResult ,
356
+ incrementalResult : IncrementalResult ,
357
+ ) : void {
358
+ const path = [ 'data' , ...( incrementalResult . path ?? [ ] ) ] ;
359
+
360
+ if ( incrementalResult . items ) {
361
+ for ( const item of incrementalResult . items ) {
362
+ setValue ( executionResult , path . join ( '.' ) , item ) ;
363
+ // Increment the last path segment (the array index) to merge the next item at the next index
364
+ // eslint-disable-next-line unicorn/prefer-at -- cannot mutate the array using Array.at()
365
+ ( path [ path . length - 1 ] as number ) ++ ;
366
+ }
367
+ }
368
+
369
+ if ( incrementalResult . data ) {
370
+ setValue ( executionResult , path . join ( '.' ) , incrementalResult . data , {
371
+ merge : true ,
372
+ } ) ;
373
+ }
374
+
375
+ if ( incrementalResult . errors ) {
376
+ executionResult . errors ||= [ ] ;
377
+ ( executionResult . errors as GraphQLError [ ] ) . push (
378
+ ...incrementalResult . errors ,
379
+ ) ;
380
+ }
381
+
382
+ if ( incrementalResult . extensions ) {
383
+ setValue ( executionResult , 'extensions' , incrementalResult . extensions , {
384
+ merge : true ,
385
+ } ) ;
386
+ }
387
+
388
+ if ( incrementalResult . incremental ) {
389
+ for ( const incrementalSubResult of incrementalResult . incremental ) {
390
+ mergeIncrementalResult ( executionResult , incrementalSubResult ) ;
391
+ }
392
+ }
393
+ }
0 commit comments