1
1
import { BoxedPromiseOrValue } from '../jsutils/BoxedPromiseOrValue.js' ;
2
+ import { invariant } from '../jsutils/invariant.js' ;
2
3
import { isPromise } from '../jsutils/isPromise.js' ;
3
4
import { promiseWithResolvers } from '../jsutils/promiseWithResolvers.js' ;
4
5
@@ -20,19 +21,13 @@ interface DeferredFragmentNode {
20
21
deferredFragmentRecord : DeferredFragmentRecord ;
21
22
deferredGroupedFieldSetRecords : Set < DeferredGroupedFieldSetRecord > ;
22
23
reconcilableResults : Set < ReconcilableDeferredGroupedFieldSetResult > ;
23
- children : Array < DeferredFragmentNode > ;
24
+ children : Set < SubsequentResultNode > ;
24
25
}
25
26
26
27
function isDeferredFragmentNode (
27
- node : DeferredFragmentNode | undefined ,
28
+ node : SubsequentResultNode | undefined ,
28
29
) : node is DeferredFragmentNode {
29
- return node !== undefined ;
30
- }
31
-
32
- function isStreamNode (
33
- record : SubsequentResultNode | IncrementalDataRecord ,
34
- ) : record is StreamRecord {
35
- return 'streamItemQueue' in record ;
30
+ return node !== undefined && 'deferredFragmentRecord' in node ;
36
31
}
37
32
38
33
type SubsequentResultNode = DeferredFragmentNode | StreamRecord ;
@@ -47,8 +42,6 @@ export class IncrementalGraph {
47
42
DeferredFragmentNode
48
43
> ;
49
44
50
- private _newPending : Set < SubsequentResultNode > ;
51
- private _newIncrementalDataRecords : Set < IncrementalDataRecord > ;
52
45
private _completedQueue : Array < IncrementalDataRecordResult > ;
53
46
private _nextQueue : Array <
54
47
( iterable : IteratorResult < Iterable < IncrementalDataRecordResult > > ) => void
@@ -57,87 +50,42 @@ export class IncrementalGraph {
57
50
constructor ( ) {
58
51
this . _pending = new Set ( ) ;
59
52
this . _deferredFragmentNodes = new Map ( ) ;
60
- this . _newIncrementalDataRecords = new Set ( ) ;
61
- this . _newPending = new Set ( ) ;
62
53
this . _completedQueue = [ ] ;
63
54
this . _nextQueue = [ ] ;
64
55
}
65
56
66
- addIncrementalDataRecords (
57
+ getNewPending (
67
58
incrementalDataRecords : ReadonlyArray < IncrementalDataRecord > ,
68
- ) : void {
69
- for ( const incrementalDataRecord of incrementalDataRecords ) {
70
- if ( isDeferredGroupedFieldSetRecord ( incrementalDataRecord ) ) {
71
- this . _addDeferredGroupedFieldSetRecord ( incrementalDataRecord ) ;
72
- } else {
73
- this . _addStreamRecord ( incrementalDataRecord ) ;
74
- }
75
- }
59
+ ) : ReadonlyArray < SubsequentResultRecord > {
60
+ const newPending = new Set < SubsequentResultNode > ( ) ;
61
+ this . _addIncrementalDataRecords (
62
+ incrementalDataRecords ,
63
+ undefined ,
64
+ newPending ,
65
+ ) ;
66
+ return this . _pendingNodesToResults ( newPending ) ;
76
67
}
77
68
78
69
addCompletedReconcilableDeferredGroupedFieldSet (
79
70
reconcilableResult : ReconcilableDeferredGroupedFieldSetResult ,
80
71
) : void {
81
- const deferredFragmentNodes : Array < DeferredFragmentNode > =
82
- reconcilableResult . deferredGroupedFieldSetRecord . deferredFragmentRecords
83
- . map ( ( deferredFragmentRecord ) =>
84
- this . _deferredFragmentNodes . get ( deferredFragmentRecord ) ,
85
- )
86
- . filter < DeferredFragmentNode > ( isDeferredFragmentNode ) ;
87
- for ( const deferredFragmentNode of deferredFragmentNodes ) {
72
+ for ( const deferredFragmentNode of this . _fragmentsToNodes (
73
+ reconcilableResult . deferredGroupedFieldSetRecord . deferredFragmentRecords ,
74
+ ) ) {
88
75
deferredFragmentNode . deferredGroupedFieldSetRecords . delete (
89
76
reconcilableResult . deferredGroupedFieldSetRecord ,
90
77
) ;
91
78
deferredFragmentNode . reconcilableResults . add ( reconcilableResult ) ;
92
79
}
93
- }
94
-
95
- getNewPending ( ) : ReadonlyArray < SubsequentResultRecord > {
96
- const newPending : Array < SubsequentResultRecord > = [ ] ;
97
- for ( const node of this . _newPending ) {
98
- if ( isStreamNode ( node ) ) {
99
- this . _pending . add ( node ) ;
100
- newPending . push ( node ) ;
101
- this . _newIncrementalDataRecords . add ( node ) ;
102
- } else if ( node . deferredGroupedFieldSetRecords . size > 0 ) {
103
- for ( const deferredGroupedFieldSetNode of node . deferredGroupedFieldSetRecords ) {
104
- this . _newIncrementalDataRecords . add ( deferredGroupedFieldSetNode ) ;
105
- }
106
- this . _pending . add ( node ) ;
107
- newPending . push ( node . deferredFragmentRecord ) ;
108
- } else {
109
- for ( const child of node . children ) {
110
- this . _newPending . add ( child ) ;
111
- }
112
- }
113
- }
114
- this . _newPending . clear ( ) ;
115
-
116
- for ( const incrementalDataRecord of this . _newIncrementalDataRecords ) {
117
- if ( isStreamNode ( incrementalDataRecord ) ) {
118
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
119
- this . _onStreamItems (
120
- incrementalDataRecord ,
121
- incrementalDataRecord . streamItemQueue ,
122
- ) ;
123
- } else {
124
- const deferredGroupedFieldSetResult = incrementalDataRecord . result ;
125
- const result =
126
- deferredGroupedFieldSetResult instanceof BoxedPromiseOrValue
127
- ? deferredGroupedFieldSetResult . value
128
- : deferredGroupedFieldSetResult ( ) . value ;
129
80
130
- if ( isPromise ( result ) ) {
131
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
132
- result . then ( ( resolved ) => this . _enqueue ( resolved ) ) ;
133
- } else {
134
- this . _enqueue ( result ) ;
135
- }
136
- }
81
+ const incrementalDataRecords = reconcilableResult . incrementalDataRecords ;
82
+ if ( incrementalDataRecords !== undefined ) {
83
+ this . _addIncrementalDataRecords (
84
+ incrementalDataRecords ,
85
+ reconcilableResult . deferredGroupedFieldSetRecord
86
+ . deferredFragmentRecords ,
87
+ ) ;
137
88
}
138
- this . _newIncrementalDataRecords . clear ( ) ;
139
-
140
- return newPending ;
141
89
}
142
90
143
91
completedIncrementalData ( ) {
@@ -177,9 +125,12 @@ export class IncrementalGraph {
177
125
return this . _pending . size > 0 ;
178
126
}
179
127
180
- completeDeferredFragment (
181
- deferredFragmentRecord : DeferredFragmentRecord ,
182
- ) : Array < ReconcilableDeferredGroupedFieldSetResult > | undefined {
128
+ completeDeferredFragment ( deferredFragmentRecord : DeferredFragmentRecord ) :
129
+ | {
130
+ newPending : ReadonlyArray < SubsequentResultRecord > ;
131
+ reconcilableResults : ReadonlyArray < ReconcilableDeferredGroupedFieldSetResult > ;
132
+ }
133
+ | undefined {
183
134
const deferredFragmentNode = this . _deferredFragmentNodes . get (
184
135
deferredFragmentRecord ,
185
136
) ;
@@ -194,25 +145,21 @@ export class IncrementalGraph {
194
145
const reconcilableResults = Array . from (
195
146
deferredFragmentNode . reconcilableResults ,
196
147
) ;
148
+ this . _removePending ( deferredFragmentNode ) ;
197
149
for ( const reconcilableResult of reconcilableResults ) {
198
- for ( const otherDeferredFragmentRecord of reconcilableResult
199
- . deferredGroupedFieldSetRecord . deferredFragmentRecords ) {
200
- const otherDeferredFragmentNode = this . _deferredFragmentNodes . get (
201
- otherDeferredFragmentRecord ,
202
- ) ;
203
- if ( otherDeferredFragmentNode === undefined ) {
204
- continue ;
205
- }
150
+ for ( const otherDeferredFragmentNode of this . _fragmentsToNodes (
151
+ reconcilableResult . deferredGroupedFieldSetRecord
152
+ . deferredFragmentRecords ,
153
+ ) ) {
206
154
otherDeferredFragmentNode . reconcilableResults . delete (
207
155
reconcilableResult ,
208
156
) ;
209
157
}
210
158
}
211
- this . _removePending ( deferredFragmentNode ) ;
212
- for ( const child of deferredFragmentNode . children ) {
213
- this . _newPending . add ( child ) ;
214
- }
215
- return reconcilableResults ;
159
+ const newPending = this . _pendingNodesToResults (
160
+ deferredFragmentNode . children ,
161
+ ) ;
162
+ return { newPending, reconcilableResults } ;
216
163
}
217
164
218
165
removeDeferredFragment (
@@ -227,9 +174,11 @@ export class IncrementalGraph {
227
174
this . _removePending ( deferredFragmentNode ) ;
228
175
this . _deferredFragmentNodes . delete ( deferredFragmentRecord ) ;
229
176
// TODO: add test case for an erroring deferred fragment with child defers
230
- /* c8 ignore next 3 */
177
+ /* c8 ignore next 5 */
231
178
for ( const child of deferredFragmentNode . children ) {
232
- this . removeDeferredFragment ( child . deferredFragmentRecord ) ;
179
+ if ( isDeferredFragmentNode ( child ) ) {
180
+ this . removeDeferredFragment ( child . deferredFragmentRecord ) ;
181
+ }
233
182
}
234
183
return true ;
235
184
}
@@ -247,28 +196,92 @@ export class IncrementalGraph {
247
196
}
248
197
}
249
198
250
- private _addDeferredGroupedFieldSetRecord (
251
- deferredGroupedFieldSetRecord : DeferredGroupedFieldSetRecord ,
199
+ private _addIncrementalDataRecords (
200
+ incrementalDataRecords : ReadonlyArray < IncrementalDataRecord > ,
201
+ parents : ReadonlyArray < DeferredFragmentRecord > | undefined ,
202
+ newPending ?: Set < SubsequentResultNode > | undefined ,
252
203
) : void {
253
- for ( const deferredFragmentRecord of deferredGroupedFieldSetRecord . deferredFragmentRecords ) {
254
- const deferredFragmentNode = this . _addDeferredFragmentNode (
255
- deferredFragmentRecord ,
256
- ) ;
257
- if ( this . _pending . has ( deferredFragmentNode ) ) {
258
- this . _newIncrementalDataRecords . add ( deferredGroupedFieldSetRecord ) ;
204
+ for ( const incrementalDataRecord of incrementalDataRecords ) {
205
+ if ( isDeferredGroupedFieldSetRecord ( incrementalDataRecord ) ) {
206
+ for ( const deferredFragmentRecord of incrementalDataRecord . deferredFragmentRecords ) {
207
+ const deferredFragmentNode = this . _addDeferredFragmentNode (
208
+ deferredFragmentRecord ,
209
+ newPending ,
210
+ ) ;
211
+ deferredFragmentNode . deferredGroupedFieldSetRecords . add (
212
+ incrementalDataRecord ,
213
+ ) ;
214
+ }
215
+ if ( this . _hasPendingFragment ( incrementalDataRecord ) ) {
216
+ this . _onDeferredGroupedFieldSet ( incrementalDataRecord ) ;
217
+ }
218
+ } else if ( parents === undefined ) {
219
+ invariant ( newPending !== undefined ) ;
220
+ newPending . add ( incrementalDataRecord ) ;
221
+ } else {
222
+ for ( const parent of parents ) {
223
+ const deferredFragmentNode = this . _addDeferredFragmentNode (
224
+ parent ,
225
+ newPending ,
226
+ ) ;
227
+ deferredFragmentNode . children . add ( incrementalDataRecord ) ;
228
+ }
259
229
}
260
- deferredFragmentNode . deferredGroupedFieldSetRecords . add (
261
- deferredGroupedFieldSetRecord ,
262
- ) ;
263
230
}
264
231
}
265
232
266
- private _addStreamRecord ( streamRecord : StreamRecord ) : void {
267
- this . _newPending . add ( streamRecord ) ;
233
+ private _pendingNodesToResults (
234
+ newPendingNodes : Set < SubsequentResultNode > ,
235
+ ) : ReadonlyArray < SubsequentResultRecord > {
236
+ const newPendingResults : Array < SubsequentResultRecord > = [ ] ;
237
+ for ( const node of newPendingNodes ) {
238
+ if ( isDeferredFragmentNode ( node ) ) {
239
+ if ( node . deferredGroupedFieldSetRecords . size > 0 ) {
240
+ for ( const deferredGroupedFieldSetRecord of node . deferredGroupedFieldSetRecords ) {
241
+ if ( ! this . _hasPendingFragment ( deferredGroupedFieldSetRecord ) ) {
242
+ this . _onDeferredGroupedFieldSet ( deferredGroupedFieldSetRecord ) ;
243
+ }
244
+ }
245
+ this . _pending . add ( node ) ;
246
+ newPendingResults . push ( node . deferredFragmentRecord ) ;
247
+ continue ;
248
+ }
249
+ this . _deferredFragmentNodes . delete ( node . deferredFragmentRecord ) ;
250
+ for ( const child of node . children ) {
251
+ newPendingNodes . add ( child ) ;
252
+ }
253
+ } else {
254
+ this . _pending . add ( node ) ;
255
+ newPendingResults . push ( node ) ;
256
+
257
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
258
+ this . _onStreamItems ( node ) ;
259
+ }
260
+ }
261
+ return newPendingResults ;
262
+ }
263
+
264
+ private _hasPendingFragment (
265
+ deferredGroupedFieldSetRecord : DeferredGroupedFieldSetRecord ,
266
+ ) : boolean {
267
+ return this . _fragmentsToNodes (
268
+ deferredGroupedFieldSetRecord . deferredFragmentRecords ,
269
+ ) . some ( ( node ) => this . _pending . has ( node ) ) ;
270
+ }
271
+
272
+ private _fragmentsToNodes (
273
+ deferredFragmentRecords : ReadonlyArray < DeferredFragmentRecord > ,
274
+ ) : Array < DeferredFragmentNode > {
275
+ return deferredFragmentRecords
276
+ . map ( ( deferredFragmentRecord ) =>
277
+ this . _deferredFragmentNodes . get ( deferredFragmentRecord ) ,
278
+ )
279
+ . filter < DeferredFragmentNode > ( isDeferredFragmentNode ) ;
268
280
}
269
281
270
282
private _addDeferredFragmentNode (
271
283
deferredFragmentRecord : DeferredFragmentRecord ,
284
+ newPending : Set < SubsequentResultNode > | undefined ,
272
285
) : DeferredFragmentNode {
273
286
let deferredFragmentNode = this . _deferredFragmentNodes . get (
274
287
deferredFragmentRecord ,
@@ -280,29 +293,45 @@ export class IncrementalGraph {
280
293
deferredFragmentRecord,
281
294
deferredGroupedFieldSetRecords : new Set ( ) ,
282
295
reconcilableResults : new Set ( ) ,
283
- children : [ ] ,
296
+ children : new Set ( ) ,
284
297
} ;
285
298
this . _deferredFragmentNodes . set (
286
299
deferredFragmentRecord ,
287
300
deferredFragmentNode ,
288
301
) ;
289
302
const parent = deferredFragmentRecord . parent ;
290
303
if ( parent === undefined ) {
291
- this . _newPending . add ( deferredFragmentNode ) ;
304
+ invariant ( newPending !== undefined ) ;
305
+ newPending . add ( deferredFragmentNode ) ;
292
306
return deferredFragmentNode ;
293
307
}
294
- const parentNode = this . _addDeferredFragmentNode ( parent ) ;
295
- parentNode . children . push ( deferredFragmentNode ) ;
308
+ const parentNode = this . _addDeferredFragmentNode ( parent , newPending ) ;
309
+ parentNode . children . add ( deferredFragmentNode ) ;
296
310
return deferredFragmentNode ;
297
311
}
298
312
299
- private async _onStreamItems (
300
- streamRecord : StreamRecord ,
301
- streamItemQueue : Array < StreamItemRecord > ,
302
- ) : Promise < void > {
313
+ private _onDeferredGroupedFieldSet (
314
+ deferredGroupedFieldSetRecord : DeferredGroupedFieldSetRecord ,
315
+ ) : void {
316
+ const deferredGroupedFieldSetResult = deferredGroupedFieldSetRecord . result ;
317
+ const result =
318
+ deferredGroupedFieldSetResult instanceof BoxedPromiseOrValue
319
+ ? deferredGroupedFieldSetResult . value
320
+ : deferredGroupedFieldSetResult ( ) . value ;
321
+
322
+ if ( isPromise ( result ) ) {
323
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
324
+ result . then ( ( resolved ) => this . _enqueue ( resolved ) ) ;
325
+ } else {
326
+ this . _enqueue ( result ) ;
327
+ }
328
+ }
329
+
330
+ private async _onStreamItems ( streamRecord : StreamRecord ) : Promise < void > {
303
331
let items : Array < unknown > = [ ] ;
304
332
let errors : Array < GraphQLError > = [ ] ;
305
333
let incrementalDataRecords : Array < IncrementalDataRecord > = [ ] ;
334
+ const streamItemQueue = streamRecord . streamItemQueue ;
306
335
let streamItemRecord : StreamItemRecord | undefined ;
307
336
while ( ( streamItemRecord = streamItemQueue . shift ( ) ) !== undefined ) {
308
337
let result =
0 commit comments