@@ -132,22 +132,22 @@ An initial value may be provided when executing a query operation.
132
132
133
133
ExecuteQuery(query, schema, variableValues, initialValue):
134
134
135
- - Let {subsequentPayloads } be an empty list .
135
+ - Let {publisherRecord } be the result of {CreatePublisher()} .
136
136
- Let {queryType} be the root Query type in {schema}.
137
137
- Assert: {queryType} is an Object type.
138
138
- Let {selectionSet} be the top level Selection Set in {query}.
139
139
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
140
- queryType, initialValue, variableValues, subsequentPayloads )} _ normally_
140
+ queryType, initialValue, variableValues, publisherRecord )} _ normally_
141
141
(allowing parallelization).
142
142
- Let {errors} be the list of all _ field error_ raised while executing the
143
143
selection set.
144
- - If {subsequentPayloads } is empty :
144
+ - If {HasSubsequentPayloads(publisherRecord) } is {false} :
145
145
- Return an unordered map containing {data} and {errors}.
146
- - If {subsequentPayloads} is not empty :
146
+ - Otherwise :
147
147
- Let {initialResponse} be an unordered map containing {data}, {errors}, and
148
148
an entry named {hasNext} with the value {true}.
149
149
- Let {iterator} be the result of running
150
- {YieldSubsequentPayloads(initialResponse, subsequentPayloads )}.
150
+ {YieldSubsequentPayloads(initialResponse, publisherRecord )}.
151
151
- For each {payload} yielded by {iterator}:
152
152
- If a termination signal is received:
153
153
- Send a termination signal to {iterator}.
@@ -167,21 +167,21 @@ mutations ensures against race conditions during these side-effects.
167
167
168
168
ExecuteMutation(mutation, schema, variableValues, initialValue):
169
169
170
- - Let {subsequentPayloads } be an empty list .
170
+ - Let {publisherRecord } be the result of {CreatePublisher()} .
171
171
- Let {mutationType} be the root Mutation type in {schema}.
172
172
- Assert: {mutationType} is an Object type.
173
173
- Let {selectionSet} be the top level Selection Set in {mutation}.
174
174
- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
175
- mutationType, initialValue, variableValues, subsequentPayloads )} _ serially_ .
175
+ mutationType, initialValue, variableValues, publisherRecord )} _ serially_ .
176
176
- Let {errors} be the list of all _ field error_ raised while executing the
177
177
selection set.
178
- - If {subsequentPayloads } is empty :
178
+ - If {HasSubsequentPayloads(publisherRecord) } is {false} :
179
179
- Return an unordered map containing {data} and {errors}.
180
- - If {subsequentPayloads} is not empty :
180
+ - Otherwise :
181
181
- Let {initialResponse} be an unordered map containing {data}, {errors}, and
182
182
an entry named {hasNext} with the value {true}.
183
183
- Let {iterator} be the result of running
184
- {YieldSubsequentPayloads(initialResponse, subsequentPayloads )}.
184
+ {YieldSubsequentPayloads(initialResponse, publisherRecord )}.
185
185
- For each {payload} yielded by {iterator}:
186
186
- If a termination signal is received:
187
187
- Send a termination signal to {iterator}.
@@ -353,14 +353,50 @@ Unsubscribe(responseStream):
353
353
354
354
- Cancel {responseStream}
355
355
356
+ ## Publisher Record
357
+
358
+ If an operation contains ` @defer ` or ` @stream ` directives, execution may also
359
+ result in an Async Payload Record stream in addition to the initial response.
360
+ The Async Payload Records may be published lazily as requested, with the
361
+ internal state of the unpublished stream held by a Publisher Record unique to
362
+ the request.
363
+
364
+ - {subsequentPayloads}: the set of Async Payload Records for this response that
365
+ have not yet been published.
366
+
367
+ ## Create Publisher
368
+
369
+ CreatePublisher():
370
+
371
+ - Let {publisherRecord} be a publisher record.
372
+ - Initialize {subsequentPayloads} on {publisherRecord} to an empty set.
373
+ - Return {publisherRecord}.
374
+
375
+ ## Has Subsequent Payloads
376
+
377
+ HasSubsequentPayloads(publisherRecord):
378
+
379
+ - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
380
+ - Let {size} be the number of payloads within {subsequentPayloads}.
381
+ - If {size} is greater than zero, return {true}.
382
+ - Return {false}.
383
+
384
+ ## Add Payload
385
+
386
+ AddPayload(payload, publisherRecord):
387
+
388
+ - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
389
+ - Add {payload} to {subsequentPayloads}.
390
+
356
391
## Yield Subsequent Payloads
357
392
358
393
If an operation contains subsequent payload records resulting from ` @stream ` or
359
394
` @defer ` directives, the {YieldSubsequentPayloads} algorithm defines how the
360
395
payloads should be processed.
361
396
362
- YieldSubsequentPayloads(initialResponse, subsequentPayloads ):
397
+ YieldSubsequentPayloads(initialResponse, publisherRecord ):
363
398
399
+ - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
364
400
- Let {initialRecords} be any items in {subsequentPayloads} with a completed
365
401
{dataExecution}.
366
402
- Initialize {initialIncremental} to an empty list.
@@ -411,10 +447,9 @@ represented field in the grouped field set produces an entry into a response
411
447
map.
412
448
413
449
ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues, path,
414
- subsequentPayloads , asyncRecord):
450
+ publisherRecord , asyncRecord):
415
451
416
452
- If {path} is not provided, initialize it to an empty list.
417
- - If {subsequentPayloads} is not provided, initialize it to the empty set.
418
453
- Let {groupedFieldSet} and {deferredGroupedFieldsList} be the result of
419
454
{CollectFields(objectType, selectionSet, variableValues, path, asyncRecord)}.
420
455
- Initialize {resultMap} to an empty ordered map.
@@ -425,12 +460,11 @@ subsequentPayloads, asyncRecord):
425
460
{objectType}.
426
461
- If {fieldType} is defined:
427
462
- Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
428
- fields, variableValues, path, subsequentPayloads , asyncRecord)}.
463
+ fields, variableValues, path, publisherRecord , asyncRecord)}.
429
464
- Set {responseValue} as the value for {responseKey} in {resultMap}.
430
465
- For each {deferredGroupFieldSet} and {label} in {deferredGroupedFieldsList}
431
466
- Call {ExecuteDeferredFragment(label, objectType, objectValue,
432
- deferredGroupFieldSet, path, variableValues, asyncRecord,
433
- subsequentPayloads)}
467
+ deferredGroupFieldSet, path, variableValues, asyncRecord, publisherRecord)}
434
468
- Return {resultMap}.
435
469
436
470
Note: {resultMap} is ordered by which fields appear first in the operation. This
@@ -445,32 +479,33 @@ either resolving to {null} if allowed or further propagated to a parent field.
445
479
If this occurs, any sibling fields which have not yet executed or have not yet
446
480
yielded a value may be cancelled to avoid unnecessary work.
447
481
448
- Additionally, async payload records in {subsequentPayloads} must be filtered if
449
- their path points to a location that has resolved to {null} due to propagation
450
- of a field error. This is described in
451
- [ Filter Subsequent Payloads] ( #sec-Filter-Subsequent-Payloads ) . These async
452
- payload records must be removed from {subsequentPayloads} and their result must
453
- not be sent to clients. If these async records have not yet executed or have not
454
- yet yielded a value they may also be cancelled to avoid unnecessary work.
482
+ Additionally, unpublished Async Payload Records must be filtered if their path
483
+ points to a location that has resolved to {null} due to propagation of a field
484
+ error. This is described in
485
+ [ Filter Subsequent Payloads] ( #sec-Filter-Subsequent-Payloads ) . The result of
486
+ these async payload records must not be sent to clients. If these async records
487
+ have not yet executed or have not yet yielded a value they may also be cancelled
488
+ to avoid unnecessary work.
455
489
456
490
Note: See [ Handling Field Errors] ( #sec-Handling-Field-Errors ) for more about
457
491
this behavior.
458
492
459
493
### Filter Subsequent Payloads
460
494
461
- When a field error is raised, there may be async payload records in
462
- {subsequentPayloads} with a path that points to a location that has been removed
463
- or set to null due to null propagation. These async payload records must be
464
- removed from subsequent payloads and their results must not be sent to clients.
495
+ When a field error is raised, there may be unpublished async payload records
496
+ with a path that points to a location that has been removed or set to null due
497
+ to null propagation. The results of these async payload records must not be sent
498
+ to clients.
465
499
466
500
In {FilterSubsequentPayloads}, {nullPath} is the path which has resolved to null
467
501
after propagation as a result of a field error. {currentAsyncRecord} is the
468
502
async payload record where the field error was raised. {currentAsyncRecord} will
469
503
not be set for field errors that were raised during the initial execution
470
504
outside of {ExecuteDeferredFragment} or {ExecuteStreamField}.
471
505
472
- FilterSubsequentPayloads(subsequentPayloads , nullPath, currentAsyncRecord):
506
+ FilterSubsequentPayloads(publisherRecord , nullPath, currentAsyncRecord):
473
507
508
+ - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
474
509
- For each {asyncRecord} in {subsequentPayloads}:
475
510
- If {asyncRecord} is the same record as {currentAsyncRecord}:
476
511
- Continue to the next record in {subsequentPayloads}.
@@ -775,7 +810,7 @@ All Async Payload Records are structures containing:
775
810
#### Execute Deferred Fragment
776
811
777
812
ExecuteDeferredFragment(label, objectType, objectValue, groupedFieldSet, path,
778
- variableValues, parentRecord, subsequentPayloads ):
813
+ variableValues, parentRecord, publisherRecord ):
779
814
780
815
- Let {deferRecord} be an async payload record created from {label} and {path}.
781
816
- Initialize {errors} on {deferRecord} to an empty list.
@@ -789,7 +824,7 @@ variableValues, parentRecord, subsequentPayloads):
789
824
{objectType}.
790
825
- If {fieldType} is defined:
791
826
- Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
792
- fields, variableValues, path, subsequentPayloads , asyncRecord)}.
827
+ fields, variableValues, path, publisherRecord , asyncRecord)}.
793
828
- Set {responseValue} as the value for {responseKey} in {resultMap}.
794
829
- Append any encountered field errors to {errors}.
795
830
- If {parentRecord} is defined:
@@ -806,7 +841,7 @@ variableValues, parentRecord, subsequentPayloads):
806
841
- Add an entry to {payload} named ` path ` with the value {path}.
807
842
- Return {payload}.
808
843
- Set {dataExecution} on {deferredFragmentRecord}.
809
- - Append { deferRecord} to {subsequentPayloads }.
844
+ - Call {AddPayload( deferRecord, publisherRecord) }.
810
845
811
846
## Executing Fields
812
847
@@ -817,7 +852,7 @@ finally completes that value either by recursively executing another selection
817
852
set or coercing a scalar value.
818
853
819
854
ExecuteField(objectType, objectValue, fieldType, fields, variableValues, path,
820
- subsequentPayloads , asyncRecord):
855
+ publisherRecord , asyncRecord):
821
856
822
857
- Let {field} be the first entry in {fields}.
823
858
- Let {fieldName} be the field name of {field}.
@@ -827,7 +862,7 @@ subsequentPayloads, asyncRecord):
827
862
- Let {resolvedValue} be {ResolveFieldValue(objectType, objectValue, fieldName,
828
863
argumentValues)}.
829
864
- Let {result} be the result of calling {CompleteValue(fieldType, fields,
830
- resolvedValue, variableValues, path, subsequentPayloads , asyncRecord)}.
865
+ resolvedValue, variableValues, path, publisherRecord , asyncRecord)}.
831
866
- Return {result}.
832
867
833
868
### Coercing Field Arguments
@@ -931,7 +966,7 @@ yielded items satisfies `initialCount` specified on the `@stream` directive.
931
966
#### Execute Stream Field
932
967
933
968
ExecuteStreamField(label, iterator, index, fields, innerType, path,
934
- streamRecord, variableValues, subsequentPayloads ):
969
+ streamRecord, variableValues, publisherRecord ):
935
970
936
971
- Let {streamRecord} be an async payload record created from {label}, {path},
937
972
and {iterator}.
@@ -949,11 +984,11 @@ streamRecord, variableValues, subsequentPayloads):
949
984
- Otherwise:
950
985
- Let {item} be the item retrieved from {iterator}.
951
986
- Let {data} be the result of calling {CompleteValue(innerType, fields,
952
- item, variableValues, itemPath, subsequentPayloads , parentRecord)}.
987
+ item, variableValues, itemPath, publisherRecord , parentRecord)}.
953
988
- Append any encountered field errors to {errors}.
954
989
- Increment {index}.
955
990
- Call {ExecuteStreamField(label, iterator, index, fields, innerType, path,
956
- streamRecord, variableValues, subsequentPayloads )}.
991
+ streamRecord, variableValues, publisherRecord )}.
957
992
- If a field error was raised, causing a {null} to be propagated to {data},
958
993
and {innerType} is a Non-Nullable type:
959
994
- Add an entry to {payload} named ` items ` with the value {null}.
@@ -969,10 +1004,10 @@ streamRecord, variableValues, subsequentPayloads):
969
1004
- Wait for the result of {dataExecution} on {parentRecord}.
970
1005
- Return {payload}.
971
1006
- Set {dataExecution} on {streamRecord}.
972
- - Append { streamRecord} to {subsequentPayloads }.
1007
+ - Call {AddPayload( streamRecord, publisherRecord) }.
973
1008
974
- CompleteValue(fieldType, fields, result, variableValues, path,
975
- subsequentPayloads, asyncRecord):
1009
+ CompleteValue(fieldType, fields, result, variableValues, path, publisherRecord,
1010
+ asyncRecord):
976
1011
977
1012
- If the {fieldType} is a Non-Null type:
978
1013
- Let {innerType} be the inner type of {fieldType}.
@@ -1004,15 +1039,15 @@ subsequentPayloads, asyncRecord):
1004
1039
- If {streamDirective} is defined and {index} is greater than or equal to
1005
1040
{initialCount}:
1006
1041
- Call {ExecuteStreamField(label, iterator, index, fields, innerType,
1007
- path, asyncRecord, subsequentPayloads )}.
1042
+ path, asyncRecord, publisherRecord )}.
1008
1043
- Return {items}.
1009
1044
- Otherwise:
1010
1045
- Wait for the next item from {result} via the {iterator}.
1011
1046
- If an item is not retrieved because of an error, raise a _ field error_ .
1012
1047
- Let {resultItem} be the item retrieved from {result}.
1013
1048
- Let {itemPath} be {path} with {index} appended.
1014
1049
- Let {resolvedItem} be the result of calling {CompleteValue(innerType,
1015
- fields, resultItem, variableValues, itemPath, subsequentPayloads ,
1050
+ fields, resultItem, variableValues, itemPath, publisherRecord ,
1016
1051
asyncRecord)}.
1017
1052
- Append {resolvedItem} to {items}.
1018
1053
- Increment {index}.
@@ -1026,7 +1061,7 @@ subsequentPayloads, asyncRecord):
1026
1061
- Let {objectType} be {ResolveAbstractType(fieldType, result)}.
1027
1062
- Let {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}.
1028
1063
- Return the result of evaluating {ExecuteSelectionSet(subSelectionSet,
1029
- objectType, result, variableValues, path, subsequentPayloads , asyncRecord)}
1064
+ objectType, result, variableValues, path, publisherRecord , asyncRecord)}
1030
1065
_ normally_ (allowing for parallelization).
1031
1066
1032
1067
** Coercing Results**
0 commit comments