@@ -9,24 +9,49 @@ import type {
9
9
ReconcilableDeferredGroupedFieldSetResult ,
10
10
SubsequentResultRecord ,
11
11
} from './types.js' ;
12
- import {
13
- isDeferredFragmentRecord ,
14
- isDeferredGroupedFieldSetRecord ,
15
- } from './types.js' ;
12
+ import { isDeferredGroupedFieldSetRecord } from './types.js' ;
13
+
14
+ interface DeferredFragmentNode {
15
+ deferredFragmentRecord : DeferredFragmentRecord ;
16
+ expectedReconcilableResults : number ;
17
+ results : Array < DeferredGroupedFieldSetResult > ;
18
+ reconcilableResults : Array < ReconcilableDeferredGroupedFieldSetResult > ;
19
+ children : Array < DeferredFragmentNode > ;
20
+ }
21
+
22
+ function isDeferredFragmentNode (
23
+ node : DeferredFragmentNode | undefined ,
24
+ ) : node is DeferredFragmentNode {
25
+ return node !== undefined ;
26
+ }
27
+
28
+ function isStreamNode (
29
+ subsequentResultNode : SubsequentResultNode ,
30
+ ) : subsequentResultNode is SubsequentResultRecord {
31
+ return 'path' in subsequentResultNode ;
32
+ }
33
+
34
+ type SubsequentResultNode = DeferredFragmentNode | SubsequentResultRecord ;
16
35
17
36
/**
18
37
* @internal
19
38
*/
20
39
export class IncrementalGraph {
21
- private _pending : Set < SubsequentResultRecord > ;
22
- private _newPending : Set < SubsequentResultRecord > ;
40
+ private _pending : Set < SubsequentResultNode > ;
41
+ private _deferredFragmentNodes : Map <
42
+ DeferredFragmentRecord ,
43
+ DeferredFragmentNode
44
+ > ;
45
+
46
+ private _newPending : Set < SubsequentResultNode > ;
23
47
private _completedQueue : Array < IncrementalDataRecordResult > ;
24
48
private _nextQueue : Array <
25
49
( iterable : IteratorResult < Iterable < IncrementalDataRecordResult > > ) => void
26
50
> ;
27
51
28
52
constructor ( ) {
29
53
this . _pending = new Set ( ) ;
54
+ this . _deferredFragmentNodes = new Map ( ) ;
30
55
this . _newPending = new Set ( ) ;
31
56
this . _completedQueue = [ ] ;
32
57
this . _nextQueue = [ ] ;
@@ -38,9 +63,10 @@ export class IncrementalGraph {
38
63
for ( const incrementalDataRecord of incrementalDataRecords ) {
39
64
if ( isDeferredGroupedFieldSetRecord ( incrementalDataRecord ) ) {
40
65
for ( const deferredFragmentRecord of incrementalDataRecord . deferredFragmentRecords ) {
41
- deferredFragmentRecord . expectedReconcilableResults ++ ;
42
-
43
- this . _addDeferredFragmentRecord ( deferredFragmentRecord ) ;
66
+ const deferredFragmentNode = this . _addDeferredFragmentNode (
67
+ deferredFragmentRecord ,
68
+ ) ;
69
+ deferredFragmentNode . expectedReconcilableResults ++ ;
44
70
}
45
71
46
72
const result = incrementalDataRecord . result ;
@@ -73,21 +99,33 @@ export class IncrementalGraph {
73
99
}
74
100
}
75
101
102
+ addCompletedReconcilableDeferredGroupedFieldSet (
103
+ reconcilableResult : ReconcilableDeferredGroupedFieldSetResult ,
104
+ ) : void {
105
+ const deferredFragmentNodes : Array < DeferredFragmentNode > =
106
+ reconcilableResult . deferredFragmentRecords
107
+ . map ( ( deferredFragmentRecord ) =>
108
+ this . _deferredFragmentNodes . get ( deferredFragmentRecord ) ,
109
+ )
110
+ . filter < DeferredFragmentNode > ( isDeferredFragmentNode ) ;
111
+ for ( const deferredFragmentNode of deferredFragmentNodes ) {
112
+ deferredFragmentNode . reconcilableResults . push ( reconcilableResult ) ;
113
+ }
114
+ }
115
+
76
116
getNewPending ( ) : ReadonlyArray < SubsequentResultRecord > {
77
- const newPending = [ ] ;
117
+ const newPending : Array < SubsequentResultRecord > = [ ] ;
78
118
for ( const node of this . _newPending ) {
79
- if ( isDeferredFragmentRecord ( node ) ) {
80
- if ( node . expectedReconcilableResults ) {
81
- this . _pending . add ( node ) ;
82
- newPending . push ( node ) ;
83
- continue ;
84
- }
119
+ if ( isStreamNode ( node ) ) {
120
+ this . _pending . add ( node ) ;
121
+ newPending . push ( node ) ;
122
+ } else if ( node . expectedReconcilableResults ) {
123
+ this . _pending . add ( node ) ;
124
+ newPending . push ( node . deferredFragmentRecord ) ;
125
+ } else {
85
126
for ( const child of node . children ) {
86
127
this . _newPending . add ( child ) ;
87
128
}
88
- } else {
89
- this . _pending . add ( node ) ;
90
- newPending . push ( node ) ;
91
129
}
92
130
}
93
131
this . _newPending . clear ( ) ;
@@ -134,15 +172,23 @@ export class IncrementalGraph {
134
172
completeDeferredFragment (
135
173
deferredFragmentRecord : DeferredFragmentRecord ,
136
174
) : Array < ReconcilableDeferredGroupedFieldSetResult > | undefined {
137
- const reconcilableResults = deferredFragmentRecord . reconcilableResults ;
175
+ const deferredFragmentNode = this . _deferredFragmentNodes . get (
176
+ deferredFragmentRecord ,
177
+ ) ;
178
+ // TODO: add test case?
179
+ /* c8 ignore next 3 */
180
+ if ( deferredFragmentNode === undefined ) {
181
+ return undefined ;
182
+ }
183
+ const reconcilableResults = deferredFragmentNode . reconcilableResults ;
138
184
if (
139
- deferredFragmentRecord . expectedReconcilableResults !==
185
+ deferredFragmentNode . expectedReconcilableResults !==
140
186
reconcilableResults . length
141
187
) {
142
188
return ;
143
189
}
144
- this . removeSubsequentResultRecord ( deferredFragmentRecord ) ;
145
- for ( const child of deferredFragmentRecord . children ) {
190
+ this . _removePending ( deferredFragmentNode ) ;
191
+ for ( const child of deferredFragmentNode . children ) {
146
192
this . _newPending . add ( child ) ;
147
193
for ( const result of child . results ) {
148
194
this . _enqueue ( result ) ;
@@ -151,53 +197,86 @@ export class IncrementalGraph {
151
197
return reconcilableResults ;
152
198
}
153
199
154
- removeSubsequentResultRecord (
155
- subsequentResultRecord : SubsequentResultRecord ,
156
- ) : void {
157
- this . _pending . delete ( subsequentResultRecord ) ;
200
+ removeDeferredFragment ( deferredFragmentRecord : DeferredFragmentRecord ) : void {
201
+ const deferredFragmentNode = this . _deferredFragmentNodes . get (
202
+ deferredFragmentRecord ,
203
+ ) ;
204
+ // TODO: add test case?
205
+ /* c8 ignore next 3 */
206
+ if ( deferredFragmentNode === undefined ) {
207
+ return ;
208
+ }
209
+ this . _removePending ( deferredFragmentNode ) ;
210
+ this . _deferredFragmentNodes . delete ( deferredFragmentRecord ) ;
211
+ // TODO: add test case for an erroring deferred fragment with child defers
212
+ /* c8 ignore next 3 */
213
+ for ( const child of deferredFragmentNode . children ) {
214
+ this . removeDeferredFragment ( child . deferredFragmentRecord ) ;
215
+ }
216
+ }
217
+
218
+ removeStream ( streamRecord : SubsequentResultRecord ) : void {
219
+ this . _removePending ( streamRecord ) ;
220
+ }
221
+
222
+ private _removePending ( subsequentResultNode : SubsequentResultNode ) : void {
223
+ this . _pending . delete ( subsequentResultNode ) ;
158
224
if ( this . _pending . size === 0 ) {
159
225
for ( const resolve of this . _nextQueue ) {
160
226
resolve ( { value : undefined , done : true } ) ;
161
227
}
162
228
}
163
229
}
164
230
165
- private _addDeferredFragmentRecord (
231
+ private _addDeferredFragmentNode (
166
232
deferredFragmentRecord : DeferredFragmentRecord ,
167
- ) : void {
233
+ ) : DeferredFragmentNode {
234
+ let deferredFragmentNode = this . _deferredFragmentNodes . get (
235
+ deferredFragmentRecord ,
236
+ ) ;
237
+ if ( deferredFragmentNode !== undefined ) {
238
+ return deferredFragmentNode ;
239
+ }
240
+ deferredFragmentNode = {
241
+ deferredFragmentRecord,
242
+ expectedReconcilableResults : 0 ,
243
+ results : [ ] ,
244
+ reconcilableResults : [ ] ,
245
+ children : [ ] ,
246
+ } ;
247
+ this . _deferredFragmentNodes . set (
248
+ deferredFragmentRecord ,
249
+ deferredFragmentNode ,
250
+ ) ;
168
251
const parent = deferredFragmentRecord . parent ;
169
252
if ( parent === undefined ) {
170
- // Below is equivalent and slightly faster version of:
171
- // if (this._pending.has(deferredFragmentRecord)) { ... }
172
- // as all released deferredFragmentRecords have ids.
173
- if ( deferredFragmentRecord . id !== undefined ) {
174
- return ;
175
- }
176
-
177
- this . _newPending . add ( deferredFragmentRecord ) ;
178
- return ;
253
+ this . _newPending . add ( deferredFragmentNode ) ;
254
+ return deferredFragmentNode ;
179
255
}
180
-
181
- if ( parent . children . has ( deferredFragmentRecord ) ) {
182
- return ;
183
- }
184
-
185
- parent . children . add ( deferredFragmentRecord ) ;
186
-
187
- this . _addDeferredFragmentRecord ( parent ) ;
256
+ const parentNode = this . _addDeferredFragmentNode ( parent ) ;
257
+ parentNode . children . push ( deferredFragmentNode ) ;
258
+ return deferredFragmentNode ;
188
259
}
189
260
190
261
private _enqueueCompletedDeferredGroupedFieldSet (
191
262
result : DeferredGroupedFieldSetResult ,
192
263
) : void {
193
- let hasPendingParent = false ;
264
+ let isPending = false ;
194
265
for ( const deferredFragmentRecord of result . deferredFragmentRecords ) {
195
- if ( deferredFragmentRecord . id !== undefined ) {
196
- hasPendingParent = true ;
266
+ const deferredFragmentNode = this . _deferredFragmentNodes . get (
267
+ deferredFragmentRecord ,
268
+ ) ;
269
+ // TODO: add test case?
270
+ /* c8 ignore next 3 */
271
+ if ( deferredFragmentNode === undefined ) {
272
+ continue ;
273
+ }
274
+ if ( this . _pending . has ( deferredFragmentNode ) ) {
275
+ isPending = true ;
197
276
}
198
- deferredFragmentRecord . results . push ( result ) ;
277
+ deferredFragmentNode . results . push ( result ) ;
199
278
}
200
- if ( hasPendingParent ) {
279
+ if ( isPending ) {
201
280
this . _enqueue ( result ) ;
202
281
}
203
282
}
0 commit comments