1
1
import { isPromise } from '../jsutils/isPromise.js' ;
2
2
import { promiseWithResolvers } from '../jsutils/promiseWithResolvers.js' ;
3
3
4
+ import type { GraphQLError } from '../error/GraphQLError.js' ;
5
+
4
6
import type {
5
7
CompletedDeferredGroupedFieldSet ,
6
8
CompletedIncrementalData ,
7
9
CompletedReconcilableDeferredGroupedFieldSet ,
8
10
DeferredFragmentRecord ,
9
11
DeferredGroupedFieldSetRecord ,
10
12
IncrementalDataRecord ,
11
- StreamItemsRecord ,
13
+ StreamItemRecord ,
12
14
StreamRecord ,
13
15
SubsequentResultRecord ,
14
16
} from './types.js' ;
15
- import {
16
- isDeferredGroupedFieldSetRecord ,
17
- isStreamItemsRecord ,
18
- } from './types.js' ;
17
+ import { isDeferredGroupedFieldSetRecord } from './types.js' ;
19
18
20
19
interface DeferredFragmentNode {
21
20
deferredFragmentRecord : DeferredFragmentRecord ;
@@ -31,9 +30,9 @@ function isDeferredFragmentNode(
31
30
}
32
31
33
32
function isStreamNode (
34
- subsequentResultNode : SubsequentResultNode ,
35
- ) : subsequentResultNode is StreamRecord {
36
- return 'path ' in subsequentResultNode ;
33
+ record : SubsequentResultNode | IncrementalDataRecord ,
34
+ ) : record is StreamRecord {
35
+ return 'streamItemRecords ' in record ;
37
36
}
38
37
39
38
type SubsequentResultNode = DeferredFragmentNode | StreamRecord ;
@@ -71,7 +70,7 @@ export class IncrementalGraph {
71
70
if ( isDeferredGroupedFieldSetRecord ( incrementalDataRecord ) ) {
72
71
this . _addDeferredGroupedFieldSetRecord ( incrementalDataRecord ) ;
73
72
} else {
74
- this . _addStreamItemsRecord ( incrementalDataRecord ) ;
73
+ this . _addStreamRecord ( incrementalDataRecord ) ;
75
74
}
76
75
}
77
76
}
@@ -101,6 +100,7 @@ export class IncrementalGraph {
101
100
if ( isStreamNode ( node ) ) {
102
101
this . _pending . add ( node ) ;
103
102
newPending . add ( node ) ;
103
+ this . _newIncrementalDataRecords . add ( node ) ;
104
104
} else if ( node . deferredGroupedFieldSetRecords . size > 0 ) {
105
105
for ( const deferredGroupedFieldSetNode of node . deferredGroupedFieldSetRecords ) {
106
106
this . _newIncrementalDataRecords . add ( deferredGroupedFieldSetNode ) ;
@@ -116,22 +116,12 @@ export class IncrementalGraph {
116
116
this . _newPending . clear ( ) ;
117
117
118
118
for ( const incrementalDataRecord of this . _newIncrementalDataRecords ) {
119
- if ( isStreamItemsRecord ( incrementalDataRecord ) ) {
120
- const result = incrementalDataRecord . streamItemsResult . value ;
121
- if ( isPromise ( result ) ) {
122
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
123
- result . then ( ( resolved ) =>
124
- this . _enqueue ( {
125
- streamItemsRecord : incrementalDataRecord ,
126
- streamItemsResult : resolved ,
127
- } ) ,
128
- ) ;
129
- } else {
130
- this . _enqueue ( {
131
- streamItemsRecord : incrementalDataRecord ,
132
- streamItemsResult : result ,
133
- } ) ;
134
- }
119
+ if ( isStreamNode ( incrementalDataRecord ) ) {
120
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
121
+ this . _onStreamItems (
122
+ incrementalDataRecord ,
123
+ incrementalDataRecord . streamItemRecords ,
124
+ ) ;
135
125
} else {
136
126
const result =
137
127
incrementalDataRecord . deferredGroupedFieldSetResult . value ;
@@ -274,12 +264,8 @@ export class IncrementalGraph {
274
264
}
275
265
}
276
266
277
- private _addStreamItemsRecord ( streamItemsRecord : StreamItemsRecord ) : void {
278
- const streamRecord = streamItemsRecord . streamRecord ;
279
- if ( ! this . _pending . has ( streamRecord ) ) {
280
- this . _newPending . add ( streamRecord ) ;
281
- }
282
- this . _newIncrementalDataRecords . add ( streamItemsRecord ) ;
267
+ private _addStreamRecord ( streamRecord : StreamRecord ) : void {
268
+ this . _newPending . add ( streamRecord ) ;
283
269
}
284
270
285
271
private _addDeferredFragmentNode (
@@ -311,6 +297,71 @@ export class IncrementalGraph {
311
297
return deferredFragmentNode ;
312
298
}
313
299
300
+ private async _onStreamItems (
301
+ streamRecord : StreamRecord ,
302
+ streamItemRecords : Array < StreamItemRecord > ,
303
+ ) : Promise < void > {
304
+ let items : Array < unknown > = [ ] ;
305
+ let errors : Array < GraphQLError > = [ ] ;
306
+ let incrementalDataRecords : Array < IncrementalDataRecord > = [ ] ;
307
+ let streamItemRecord : StreamItemRecord | undefined ;
308
+ while ( ( streamItemRecord = streamItemRecords . shift ( ) ) !== undefined ) {
309
+ let result = streamItemRecord . value ;
310
+ if ( isPromise ( result ) ) {
311
+ if ( items . length > 0 ) {
312
+ this . _enqueue ( {
313
+ streamRecord,
314
+ streamItemsResult : {
315
+ result :
316
+ // TODO add additional test case or rework for coverage
317
+ errors . length > 0 /* c8 ignore start */
318
+ ? { items, errors } /* c8 ignore stop */
319
+ : { items } ,
320
+ incrementalDataRecords,
321
+ } ,
322
+ } ) ;
323
+ items = [ ] ;
324
+ errors = [ ] ;
325
+ incrementalDataRecords = [ ] ;
326
+ }
327
+ // eslint-disable-next-line no-await-in-loop
328
+ result = await result ;
329
+ // wait an additional tick to coalesce resolving additional promises
330
+ // within the queue
331
+ // eslint-disable-next-line no-await-in-loop
332
+ await Promise . resolve ( ) ;
333
+ }
334
+ if ( result . item === undefined ) {
335
+ if ( items . length > 0 ) {
336
+ this . _enqueue ( {
337
+ streamRecord,
338
+ streamItemsResult : {
339
+ result : errors . length > 0 ? { items, errors } : { items } ,
340
+ incrementalDataRecords,
341
+ } ,
342
+ } ) ;
343
+ }
344
+ this . _enqueue ( {
345
+ streamRecord,
346
+ streamItemsResult :
347
+ result . errors === undefined
348
+ ? { }
349
+ : {
350
+ errors : result . errors ,
351
+ } ,
352
+ } ) ;
353
+ return ;
354
+ }
355
+ items . push ( result . item ) ;
356
+ if ( result . errors !== undefined ) {
357
+ errors . push ( ...result . errors ) ;
358
+ }
359
+ if ( result . incrementalDataRecords !== undefined ) {
360
+ incrementalDataRecords . push ( ...result . incrementalDataRecords ) ;
361
+ }
362
+ }
363
+ }
364
+
314
365
private * _yieldCurrentCompletedIncrementalData (
315
366
first : CompletedIncrementalData ,
316
367
) : Generator < CompletedIncrementalData > {
0 commit comments