@@ -132,22 +132,22 @@ An initial value may be provided when executing a query operation.
132132
133133ExecuteQuery(query, schema, variableValues, initialValue):
134134
135- - Let {subsequentPayloads } be an empty list .
135+ - Let {publisherRecord } be the result of {CreatePublisher()} .
136136- Let {queryType} be the root Query type in {schema}.
137137- Assert: {queryType} is an Object type.
138138- Let {selectionSet} be the top level Selection Set in {query}.
139139- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
140- queryType, initialValue, variableValues, subsequentPayloads )} _ normally_
140+ queryType, initialValue, variableValues, publisherRecord )} _ normally_
141141 (allowing parallelization).
142142- Let {errors} be the list of all _ field error_ raised while executing the
143143 selection set.
144- - If {subsequentPayloads } is empty :
144+ - If {HasSubsequentPayloads(publisherRecord) } is {false} :
145145 - Return an unordered map containing {data} and {errors}.
146- - If {subsequentPayloads} is not empty :
146+ - Otherwise :
147147 - Let {initialResponse} be an unordered map containing {data}, {errors}, and
148148 an entry named {hasNext} with the value {true}.
149149 - Let {iterator} be the result of running
150- {YieldSubsequentPayloads(initialResponse, subsequentPayloads )}.
150+ {YieldSubsequentPayloads(initialResponse, publisherRecord )}.
151151 - For each {payload} yielded by {iterator}:
152152 - If a termination signal is received:
153153 - Send a termination signal to {iterator}.
@@ -167,21 +167,21 @@ mutations ensures against race conditions during these side-effects.
167167
168168ExecuteMutation(mutation, schema, variableValues, initialValue):
169169
170- - Let {subsequentPayloads } be an empty list .
170+ - Let {publisherRecord } be the result of {CreatePublisher()} .
171171- Let {mutationType} be the root Mutation type in {schema}.
172172- Assert: {mutationType} is an Object type.
173173- Let {selectionSet} be the top level Selection Set in {mutation}.
174174- Let {data} be the result of running {ExecuteSelectionSet(selectionSet,
175- mutationType, initialValue, variableValues, subsequentPayloads )} _ serially_ .
175+ mutationType, initialValue, variableValues, publisherRecord )} _ serially_ .
176176- Let {errors} be the list of all _ field error_ raised while executing the
177177 selection set.
178- - If {subsequentPayloads } is empty :
178+ - If {HasSubsequentPayloads(publisherRecord) } is {false} :
179179 - Return an unordered map containing {data} and {errors}.
180- - If {subsequentPayloads} is not empty :
180+ - Otherwise :
181181 - Let {initialResponse} be an unordered map containing {data}, {errors}, and
182182 an entry named {hasNext} with the value {true}.
183183 - Let {iterator} be the result of running
184- {YieldSubsequentPayloads(initialResponse, subsequentPayloads )}.
184+ {YieldSubsequentPayloads(initialResponse, publisherRecord )}.
185185 - For each {payload} yielded by {iterator}:
186186 - If a termination signal is received:
187187 - Send a termination signal to {iterator}.
@@ -353,14 +353,50 @@ Unsubscribe(responseStream):
353353
354354- Cancel {responseStream}
355355
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+
356391## Yield Subsequent Payloads
357392
358393If an operation contains subsequent payload records resulting from ` @stream ` or
359394` @defer ` directives, the {YieldSubsequentPayloads} algorithm defines how the
360395payloads should be processed.
361396
362- YieldSubsequentPayloads(initialResponse, subsequentPayloads ):
397+ YieldSubsequentPayloads(initialResponse, publisherRecord ):
363398
399+ - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
364400- Let {initialRecords} be any items in {subsequentPayloads} with a completed
365401 {dataExecution}.
366402- Initialize {initialIncremental} to an empty list.
@@ -411,10 +447,9 @@ represented field in the grouped field set produces an entry into a response
411447map.
412448
413449ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues, path,
414- subsequentPayloads , asyncRecord):
450+ publisherRecord , asyncRecord):
415451
416452- If {path} is not provided, initialize it to an empty list.
417- - If {subsequentPayloads} is not provided, initialize it to the empty set.
418453- Let {groupedFieldSet} and {deferredGroupedFieldsList} be the result of
419454 {CollectFields(objectType, selectionSet, variableValues, path, asyncRecord)}.
420455- Initialize {resultMap} to an empty ordered map.
@@ -425,12 +460,11 @@ subsequentPayloads, asyncRecord):
425460 {objectType}.
426461 - If {fieldType} is defined:
427462 - Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
428- fields, variableValues, path, subsequentPayloads , asyncRecord)}.
463+ fields, variableValues, path, publisherRecord , asyncRecord)}.
429464 - Set {responseValue} as the value for {responseKey} in {resultMap}.
430465- For each {deferredGroupFieldSet} and {label} in {deferredGroupedFieldsList}
431466 - Call {ExecuteDeferredFragment(label, objectType, objectValue,
432- deferredGroupFieldSet, path, variableValues, asyncRecord,
433- subsequentPayloads)}
467+ deferredGroupFieldSet, path, variableValues, asyncRecord, publisherRecord)}
434468- Return {resultMap}.
435469
436470Note: {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.
445479If this occurs, any sibling fields which have not yet executed or have not yet
446480yielded a value may be cancelled to avoid unnecessary work.
447481
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.
455489
456490Note: See [ Handling Field Errors] ( #sec-Handling-Field-Errors ) for more about
457491this behavior.
458492
459493### Filter Subsequent Payloads
460494
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.
465499
466500In {FilterSubsequentPayloads}, {nullPath} is the path which has resolved to null
467501after propagation as a result of a field error. {currentAsyncRecord} is the
468502async payload record where the field error was raised. {currentAsyncRecord} will
469503not be set for field errors that were raised during the initial execution
470504outside of {ExecuteDeferredFragment} or {ExecuteStreamField}.
471505
472- FilterSubsequentPayloads(subsequentPayloads , nullPath, currentAsyncRecord):
506+ FilterSubsequentPayloads(publisherRecord , nullPath, currentAsyncRecord):
473507
508+ - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
474509- For each {asyncRecord} in {subsequentPayloads}:
475510 - If {asyncRecord} is the same record as {currentAsyncRecord}:
476511 - Continue to the next record in {subsequentPayloads}.
@@ -775,7 +810,7 @@ All Async Payload Records are structures containing:
775810#### Execute Deferred Fragment
776811
777812ExecuteDeferredFragment(label, objectType, objectValue, groupedFieldSet, path,
778- variableValues, parentRecord, subsequentPayloads ):
813+ variableValues, parentRecord, publisherRecord ):
779814
780815- Let {deferRecord} be an async payload record created from {label} and {path}.
781816- Initialize {errors} on {deferRecord} to an empty list.
@@ -789,7 +824,7 @@ variableValues, parentRecord, subsequentPayloads):
789824 {objectType}.
790825 - If {fieldType} is defined:
791826 - Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
792- fields, variableValues, path, subsequentPayloads , asyncRecord)}.
827+ fields, variableValues, path, publisherRecord , asyncRecord)}.
793828 - Set {responseValue} as the value for {responseKey} in {resultMap}.
794829 - Append any encountered field errors to {errors}.
795830 - If {parentRecord} is defined:
@@ -806,7 +841,7 @@ variableValues, parentRecord, subsequentPayloads):
806841 - Add an entry to {payload} named ` path ` with the value {path}.
807842 - Return {payload}.
808843- Set {dataExecution} on {deferredFragmentRecord}.
809- - Append { deferRecord} to {subsequentPayloads }.
844+ - Call {AddPayload( deferRecord, publisherRecord) }.
810845
811846## Executing Fields
812847
@@ -817,7 +852,7 @@ finally completes that value either by recursively executing another selection
817852set or coercing a scalar value.
818853
819854ExecuteField(objectType, objectValue, fieldType, fields, variableValues, path,
820- subsequentPayloads , asyncRecord):
855+ publisherRecord , asyncRecord):
821856
822857- Let {field} be the first entry in {fields}.
823858- Let {fieldName} be the field name of {field}.
@@ -827,7 +862,7 @@ subsequentPayloads, asyncRecord):
827862- Let {resolvedValue} be {ResolveFieldValue(objectType, objectValue, fieldName,
828863 argumentValues)}.
829864- Let {result} be the result of calling {CompleteValue(fieldType, fields,
830- resolvedValue, variableValues, path, subsequentPayloads , asyncRecord)}.
865+ resolvedValue, variableValues, path, publisherRecord , asyncRecord)}.
831866- Return {result}.
832867
833868### Coercing Field Arguments
@@ -931,7 +966,7 @@ yielded items satisfies `initialCount` specified on the `@stream` directive.
931966#### Execute Stream Field
932967
933968ExecuteStreamField(label, iterator, index, fields, innerType, path,
934- streamRecord, variableValues, subsequentPayloads ):
969+ streamRecord, variableValues, publisherRecord ):
935970
936971- Let {streamRecord} be an async payload record created from {label}, {path},
937972 and {iterator}.
@@ -949,11 +984,11 @@ streamRecord, variableValues, subsequentPayloads):
949984 - Otherwise:
950985 - Let {item} be the item retrieved from {iterator}.
951986 - Let {data} be the result of calling {CompleteValue(innerType, fields,
952- item, variableValues, itemPath, subsequentPayloads , parentRecord)}.
987+ item, variableValues, itemPath, publisherRecord , parentRecord)}.
953988 - Append any encountered field errors to {errors}.
954989 - Increment {index}.
955990 - Call {ExecuteStreamField(label, iterator, index, fields, innerType, path,
956- streamRecord, variableValues, subsequentPayloads )}.
991+ streamRecord, variableValues, publisherRecord )}.
957992 - If a field error was raised, causing a {null} to be propagated to {data},
958993 and {innerType} is a Non-Nullable type:
959994 - Add an entry to {payload} named ` items ` with the value {null}.
@@ -969,10 +1004,10 @@ streamRecord, variableValues, subsequentPayloads):
9691004 - Wait for the result of {dataExecution} on {parentRecord}.
9701005 - Return {payload}.
9711006- Set {dataExecution} on {streamRecord}.
972- - Append { streamRecord} to {subsequentPayloads }.
1007+ - Call {AddPayload( streamRecord, publisherRecord) }.
9731008
974- CompleteValue(fieldType, fields, result, variableValues, path,
975- subsequentPayloads, asyncRecord):
1009+ CompleteValue(fieldType, fields, result, variableValues, path, publisherRecord,
1010+ asyncRecord):
9761011
9771012- If the {fieldType} is a Non-Null type:
9781013 - Let {innerType} be the inner type of {fieldType}.
@@ -1004,15 +1039,15 @@ subsequentPayloads, asyncRecord):
10041039 - If {streamDirective} is defined and {index} is greater than or equal to
10051040 {initialCount}:
10061041 - Call {ExecuteStreamField(label, iterator, index, fields, innerType,
1007- path, asyncRecord, subsequentPayloads )}.
1042+ path, asyncRecord, publisherRecord )}.
10081043 - Return {items}.
10091044 - Otherwise:
10101045 - Wait for the next item from {result} via the {iterator}.
10111046 - If an item is not retrieved because of an error, raise a _ field error_ .
10121047 - Let {resultItem} be the item retrieved from {result}.
10131048 - Let {itemPath} be {path} with {index} appended.
10141049 - Let {resolvedItem} be the result of calling {CompleteValue(innerType,
1015- fields, resultItem, variableValues, itemPath, subsequentPayloads ,
1050+ fields, resultItem, variableValues, itemPath, publisherRecord ,
10161051 asyncRecord)}.
10171052 - Append {resolvedItem} to {items}.
10181053 - Increment {index}.
@@ -1026,7 +1061,7 @@ subsequentPayloads, asyncRecord):
10261061 - Let {objectType} be {ResolveAbstractType(fieldType, result)}.
10271062 - Let {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}.
10281063 - Return the result of evaluating {ExecuteSelectionSet(subSelectionSet,
1029- objectType, result, variableValues, path, subsequentPayloads , asyncRecord)}
1064+ objectType, result, variableValues, path, publisherRecord , asyncRecord)}
10301065 _ normally_ (allowing for parallelization).
10311066
10321067** Coercing Results**
0 commit comments