@@ -361,32 +361,87 @@ The Async Payload Records may be published lazily as requested, with the
361
361
internal state of the unpublished stream held by a Publisher Record unique to
362
362
the request.
363
363
364
- - {subsequentPayloads}: the set of Async Payload Records for this response that
365
- have not yet been published.
364
+ - {pending}: the set of Async Payload Records for this response that have not
365
+ yet been completed.
366
+ - {waiting}: the set of Async Payload Records for this response that have been
367
+ completed, but are waiting for a parent to complete.
368
+ - {waitingByParent}: an unordered map of uncompleted parent Async Payload
369
+ Records to sets of completed child Async Payload Records.
370
+ - {pushed}: a weakly held set of Async Payload Records for this response that
371
+ have been completed, but are waiting for a parent to complete.
372
+ - {current}: the set of Async Payload Records for this response that may be
373
+ yielded on the next request.
374
+ - {signal}: An asynchronous signal that can be awaited and triggered.
366
375
367
376
## Create Publisher
368
377
369
378
CreatePublisher():
370
379
371
380
- Let {publisherRecord} be a publisher record.
372
- - Initialize {subsequentPayloads} on {publisherRecord} to an empty set.
381
+ - Initialize {pending} on {publisherRecord} to an empty set.
382
+ - Initialize {waiting} on {publisherRecord} to an empty set.
383
+ - Initialize {waitingByParent} on {publisherRecord} to an empty unordered map.
384
+ - Initialize {pushed} on {publisherRecord} to an empty set.
385
+ - Initialize {current} on {publisherRecord} to an empty set.
386
+ - Initialize {signal}.
373
387
- Return {publisherRecord}.
374
388
375
389
## Has Subsequent Payloads
376
390
377
391
HasSubsequentPayloads(publisherRecord):
378
392
379
- - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
380
- - Let {size} be the number of payloads within {subsequentPayloads}.
393
+ - Let {pending} be the corresponding entry on {publisherRecord}.
394
+ - Let {size} be the number of payloads within {pending}.
395
+ - If {size} is greater than zero, return {true}.
396
+ - Let {waiting} be the corresponding entry on {publisherRecord}.
397
+ - Let {size} be the number of payloads within {waiting}.
398
+ - If {size} is greater than zero, return {true}.
399
+ - Let {current} be the corresponding entry on {publisherRecord}.
400
+ - Let {size} be the number of payloads within {current}.
381
401
- If {size} is greater than zero, return {true}.
382
402
- Return {false}.
383
403
384
404
## Add Payload
385
405
386
406
AddPayload(payload, publisherRecord):
387
407
388
- - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
389
- - Add {payload} to {subsequentPayloads}.
408
+ - Let {pending} be the corresponding entry on {publisherRecord}.
409
+ - Add {payload} to {pending}.
410
+
411
+ ## Complete Payload
412
+
413
+ CompletePayload(payload, publisherRecord):
414
+
415
+ - Let {pending} be the corresponding entry on {publisherRecord}.
416
+ - If {payload} is not within {pending}, return.
417
+ - Remove {payload} from {pending}.
418
+ - Let {parentContext} be the corresponding entry on {payload}.
419
+ - If {parentContext} is not defined:
420
+ - Call PushPayload(payload, publisherRecord).
421
+ - Let {signal} be the corresponding entry on {publisherRecord}.
422
+ - Trigger {signal}.
423
+ - Otherwise:
424
+ - Let {waiting} and {waitingByChildren} be the corresponding entries on
425
+ {publisherRecord}.
426
+ - Add {payload} to {waiting}.
427
+ - Let {children} be the set in {waitingByParent} for {parentContext}; if no
428
+ such set exists, create it as an empty set.
429
+ - Append {payload} to {children}.
430
+
431
+ ## Push Payload
432
+
433
+ PushPayload(payload, publisherRecord):
434
+
435
+ - Let {pushed}, {current}, and {waitingByParent} be the corresponding entries on
436
+ {publisherRecord}.
437
+ - Add {payload} to {pushed} and {current}.
438
+ - Let {children} be the set in {waitingByParent} for {parentContext}.
439
+ - If {children} is not defined, return.
440
+ - Let {waiting} be the corresponding entry on {publisherRecord}.
441
+ - For each {child} in {children}:
442
+ - Call {PushPayload(payload, publisherRecord)}.
443
+ - Remove {child} from {waiting}.
444
+ - Remove the set in {waitingByParent} for {parentContext}.
390
445
391
446
## Yield Subsequent Payloads
392
447
@@ -396,15 +451,13 @@ payloads should be processed.
396
451
397
452
YieldSubsequentPayloads(initialResponse, publisherRecord):
398
453
399
- - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
400
- - Let {initialRecords} be any items in {subsequentPayloads} with a completed
401
- {dataExecution}.
454
+ - Let {current} be the corresponding entry on {publisherRecord}.
402
455
- Initialize {initialIncremental} to an empty list.
403
- - For each {record} in {initialRecords }:
404
- - Remove {record} from {subsequentPayloads }.
456
+ - For each {record} in {current }:
457
+ - Remove {record} from {current }.
405
458
- If {isCompletedIterator} on {record} is {true}:
406
459
- Continue to the next record in {records}.
407
- - Let {payload} be the completed result returned by {dataExecution }.
460
+ - Let {payload} be the corresponding entry on {record }.
408
461
- Append {payload} to {initialIncremental}.
409
462
- If {initialIncremental} is not empty:
410
463
- Add an entry to {initialResponse} named ` incremental ` containing the value
@@ -416,17 +469,17 @@ YieldSubsequentPayloads(initialResponse, publisherRecord):
416
469
- If {record} contains {iterator}:
417
470
- Send a termination signal to {iterator}.
418
471
- Return.
419
- - Wait for at least one record in {subsequentPayloads} to have a completed
420
- {dataExecution}.
472
+ - Let {signal} be the corresponding entry on {publisherRecord}.
473
+ - Wait for {signal} to be triggered.
474
+ - Reinitialize {signal} on {publisherRecord}.
421
475
- Let {subsequentResponse} be an unordered map with an entry {incremental}
422
476
initialized to an empty list.
423
- - Let {records} be the items in {subsequentPayloads} with a completed
424
- {dataExecution}.
425
- - For each {record} in {records}:
477
+ - Let {current} be the corresponding entry on {publisherRecord}.
478
+ - For each {record} in {current}:
426
479
- Remove {record} from {subsequentPayloads}.
427
480
- If {isCompletedIterator} on {record} is {true}:
428
481
- Continue to the next record in {records}.
429
- - Let {payload} be the completed result returned by {dataExecution }.
482
+ - Let {payload} be the corresponding entry on {record }.
430
483
- Append {payload} to the {incremental} entry on {subsequentResponse}.
431
484
- If {subsequentPayloads} is empty:
432
485
- Add an entry to {subsequentResponse} named ` hasNext ` with the value
@@ -505,20 +558,34 @@ outside of {ExecuteDeferredFragment} or {ExecuteStreamField}.
505
558
506
559
FilterSubsequentPayloads(publisherRecord, nullPath, currentAsyncRecord):
507
560
508
- - Let {subsequentPayloads} be the corresponding entry on {publisherRecord}.
509
- - For each {asyncRecord} in {subsequentPayloads}:
510
- - If {asyncRecord} is the same record as {currentAsyncRecord}:
511
- - Continue to the next record in {subsequentPayloads}.
512
- - Initialize {index} to zero.
513
- - While {index} is less then the length of {nullPath}:
514
- - Initialize {nullPathItem} to the element at {index} in {nullPath}.
515
- - Initialize {asyncRecordPathItem} to the element at {index} in the {path}
516
- of {asyncRecord}.
517
- - If {nullPathItem} is not equivalent to {asyncRecordPathItem}:
518
- - Continue to the next record in {subsequentPayloads}.
519
- - Increment {index} by one.
520
- - Remove {asyncRecord} from {subsequentPayloads}. Optionally, cancel any
521
- incomplete work in the execution of {asyncRecord}.
561
+ - Let {pending}, {current}, {waiting}, and {waitingByParent} be the
562
+ corresponding entries on {publisherRecord}.
563
+ - For each {asyncRecord} in {pending} and {current}:
564
+ - If {ShouldKeepPayload(asyncRecord, nullPath, currentAsyncRecord)} is {true}:
565
+ - Continue to the next record in {set}.
566
+ - Remove {asyncRecord} from {set}. Optionally, cancel any incomplete work in
567
+ the execution of {asyncRecord}.
568
+ - For each {asyncRecord} in {waiting}:
569
+ - If {ShouldKeepPayload(asyncRecord, nullPath, currentAsyncRecord)} is {true}:
570
+ - Continue to the next record in {waiting}.
571
+ - Remove {asyncRecord} from {waiting}. Optionally, cancel any incomplete work
572
+ in the execution of {asyncRecord}.
573
+ - Let {parentContext} be the corresponding entry on {asyncRecord}.
574
+ - Let {children} be the set in {waitingByParent} for {parentContext}.
575
+ - Remove {asyncRecord} from {children}.
576
+
577
+ ShouldKeepPayload(asyncRecord, nullPath, currentAsyncRecord):
578
+
579
+ - If {asyncRecord} is the same record as {currentAsyncRecord}:
580
+ - Return {true}.
581
+ - Initialize {index} to zero.
582
+ - While {index} is less then the length of {nullPath}:
583
+ - Initialize {nullPathItem} to the element at {index} in {nullPath}.
584
+ - Initialize {asyncRecordPathItem} to the element at {index} in the {path} of
585
+ {asyncRecord}.
586
+ - If {nullPathItem} is not equivalent to {asyncRecordPathItem}:
587
+ - Return {true}.
588
+ - Increment {index} by one. Return {false}.
522
589
523
590
For example, assume the field ` alwaysThrows ` is a ` Non-Null ` type that always
524
591
raises a field error:
@@ -797,51 +864,49 @@ DoesFragmentTypeApply(objectType, fragmentType):
797
864
An Async Payload Record is either a Deferred Fragment Record or a Stream Record.
798
865
All Async Payload Records are structures containing:
799
866
867
+ - {parentRecord}: The generating parent Async Payload Record, not defined if
868
+ this Async Payload Record is spawned by the initial result.
800
869
- {label}: value derived from the corresponding ` @defer ` or ` @stream ` directive.
801
870
- {path}: a list of field names and indices from root to the location of the
802
871
corresponding ` @defer ` or ` @stream ` directive.
803
872
- {iterator}: The underlying iterator if created from a ` @stream ` directive.
804
873
- {isCompletedIterator}: a boolean indicating the payload record was generated
805
874
from an iterator that has completed.
806
875
- {errors}: a list of field errors encountered during execution.
807
- - {dataExecution}: A result that can notify when the corresponding execution has
808
- completed.
876
+ - {payload}: An unordered map containing the formatted payload.
809
877
810
878
#### Execute Deferred Fragment
811
879
812
880
ExecuteDeferredFragment(label, objectType, objectValue, groupedFieldSet, path,
813
881
variableValues, parentRecord, publisherRecord):
814
882
815
- - Let {deferRecord} be an async payload record created from {label} and {path}.
816
- - Initialize {errors} on {deferRecord} to an empty list.
817
- - Let {dataExecution} be the asynchronous future value of:
818
- - Let {payload} be an unordered map.
819
- - Initialize {resultMap} to an empty ordered map.
820
- - For each {groupedFieldSet} as {responseKey} and {fields}:
821
- - Let {fieldName} be the name of the first entry in {fields}. Note: This
822
- value is unaffected if an alias is used.
823
- - Let {fieldType} be the return type defined for the field {fieldName} of
824
- {objectType}.
825
- - If {fieldType} is defined:
826
- - Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
827
- fields, variableValues, path, publisherRecord, asyncRecord)}.
828
- - Set {responseValue} as the value for {responseKey} in {resultMap}.
829
- - Append any encountered field errors to {errors}.
830
- - If {parentRecord} is defined:
831
- - Wait for the result of {dataExecution} on {parentRecord}.
832
- - If {errors} is not empty:
833
- - Add an entry to {payload} named ` errors ` with the value {errors}.
834
- - If a field error was raised, causing a {null} to be propagated to
835
- {responseValue}:
836
- - Add an entry to {payload} named ` data ` with the value {null}.
837
- - Otherwise:
838
- - Add an entry to {payload} named ` data ` with the value {resultMap}.
839
- - If {label} is defined:
840
- - Add an entry to {payload} named ` label ` with the value {label}.
841
- - Add an entry to {payload} named ` path ` with the value {path}.
842
- - Return {payload}.
843
- - Set {dataExecution} on {deferredFragmentRecord}.
883
+ - Let {deferRecord} be an async payload record created from {parentRecord},
884
+ {label}, and {path}.
844
885
- Call {AddPayload(deferRecord, publisherRecord)}.
886
+ - Initialize {errors} on {deferRecord} to an empty list.
887
+ - Initialize {resultMap} to an empty ordered map.
888
+ - For each {groupedFieldSet} as {responseKey} and {fields}:
889
+ - Let {fieldName} be the name of the first entry in {fields}. Note: This value
890
+ is unaffected if an alias is used.
891
+ - Let {fieldType} be the return type defined for the field {fieldName} of
892
+ {objectType}.
893
+ - If {fieldType} is defined:
894
+ - Let {responseValue} be {ExecuteField(objectType, objectValue, fieldType,
895
+ fields, variableValues, path, publisherRecord, asyncRecord)}.
896
+ - Set {responseValue} as the value for {responseKey} in {resultMap}.
897
+ - Append any encountered field errors to {errors}.
898
+ - If {errors} is not empty:
899
+ - Add an entry to {payload} named ` errors ` with the value {errors}.
900
+ - If a field error was raised, causing a {null} to be propagated to
901
+ {responseValue}:
902
+ - Add an entry to {payload} named ` data ` with the value {null}.
903
+ - Otherwise:
904
+ - Add an entry to {payload} named ` data ` with the value {resultMap}.
905
+ - If {label} is defined:
906
+ - Add an entry to {payload} named ` label ` with the value {label}.
907
+ - Add an entry to {payload} named ` path ` with the value {path}.
908
+ - Set {payload} on {deferRecord}.
909
+ - Call {CompletePayload(payload, publisherRecord)}.
845
910
846
911
## Executing Fields
847
912
@@ -968,43 +1033,40 @@ yielded items satisfies `initialCount` specified on the `@stream` directive.
968
1033
ExecuteStreamField(label, iterator, index, fields, innerType, path,
969
1034
streamRecord, variableValues, publisherRecord):
970
1035
971
- - Let {streamRecord} be an async payload record created from {label}, {path},
972
- and {iterator}.
1036
+ - Let {streamRecord} be an async payload record created from {parentRecord},
1037
+ {label}, {path}, and {iterator}.
1038
+ - Call {AddPayload(streamRecord, publisherRecord)}.
973
1039
- Initialize {errors} on {streamRecord} to an empty list.
974
1040
- Let {itemPath} be {path} with {index} appended.
975
- - Let {dataExecution} be the asynchronous future value of:
976
- - Wait for the next item from {iterator}.
977
- - If an item is not retrieved because {iterator} has completed:
978
- - Set {isCompletedIterator} to {true} on {streamRecord}.
979
- - Return {null}.
980
- - Let {payload} be an unordered map.
981
- - If an item is not retrieved because of an error:
982
- - Append the encountered error to {errors}.
1041
+ - Wait for the next item from {iterator}.
1042
+ - If an item is not retrieved because {iterator} has completed:
1043
+ - Set {isCompletedIterator} to {true} on {streamRecord}.
1044
+ - Return {null}.
1045
+ - Let {payload} be an unordered map.
1046
+ - If an item is not retrieved because of an error:
1047
+ - Append the encountered error to {errors}.
1048
+ - Add an entry to {payload} named ` items ` with the value {null}.
1049
+ - Otherwise:
1050
+ - Let {item} be the item retrieved from {iterator}.
1051
+ - Let {data} be the result of calling {CompleteValue(innerType, fields, item,
1052
+ variableValues, itemPath, publisherRecord, parentRecord)}.
1053
+ - Append any encountered field errors to {errors}.
1054
+ - Increment {index}.
1055
+ - Call {ExecuteStreamField(label, iterator, index, fields, innerType, path,
1056
+ streamRecord, variableValues, publisherRecord)}.
1057
+ - If a field error was raised, causing a {null} to be propagated to {data},
1058
+ and {innerType} is a Non-Nullable type:
983
1059
- Add an entry to {payload} named ` items ` with the value {null}.
984
1060
- Otherwise:
985
- - Let {item} be the item retrieved from {iterator}.
986
- - Let {data} be the result of calling {CompleteValue(innerType, fields,
987
- item, variableValues, itemPath, publisherRecord, parentRecord)}.
988
- - Append any encountered field errors to {errors}.
989
- - Increment {index}.
990
- - Call {ExecuteStreamField(label, iterator, index, fields, innerType, path,
991
- streamRecord, variableValues, publisherRecord)}.
992
- - If a field error was raised, causing a {null} to be propagated to {data},
993
- and {innerType} is a Non-Nullable type:
994
- - Add an entry to {payload} named ` items ` with the value {null}.
995
- - Otherwise:
996
- - Add an entry to {payload} named ` items ` with a list containing the value
997
- {data}.
998
- - If {errors} is not empty:
999
- - Add an entry to {payload} named ` errors ` with the value {errors}.
1000
- - If {label} is defined:
1001
- - Add an entry to {payload} named ` label ` with the value {label}.
1002
- - Add an entry to {payload} named ` path ` with the value {itemPath}.
1003
- - If {parentRecord} is defined:
1004
- - Wait for the result of {dataExecution} on {parentRecord}.
1005
- - Return {payload}.
1006
- - Set {dataExecution} on {streamRecord}.
1007
- - Call {AddPayload(streamRecord, publisherRecord)}.
1061
+ - Add an entry to {payload} named ` items ` with a list containing the value
1062
+ {data}.
1063
+ - If {errors} is not empty:
1064
+ - Add an entry to {payload} named ` errors ` with the value {errors}.
1065
+ - If {label} is defined:
1066
+ - Add an entry to {payload} named ` label ` with the value {label}.
1067
+ - Add an entry to {payload} named ` path ` with the value {itemPath}.
1068
+ - Set {payload} on {streamRecord}.
1069
+ - Call {CompletePayload(streamRecord, publisherRecord)}.
1008
1070
1009
1071
CompleteValue(fieldType, fields, result, variableValues, path, publisherRecord,
1010
1072
asyncRecord):
0 commit comments