@depup/graphql-tools__delegate 12.0.13-depup.0 → 12.1.0-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -382,7 +382,7 @@ function resolveExternalObject(type, object, unpathedErrors, subschema, context,
382
382
  sourceSubschema,
383
383
  targetSubschemas
384
384
  ] of potentialMergedTypeInfo.targetSubschemas) {
385
- if (targetSubschemas.length && sourceSubschema.name === subschema.name) {
385
+ if (targetSubschemas.length && sourceSubschema.name != null && sourceSubschema.name === subschema.name) {
386
386
  subschema = sourceSubschema;
387
387
  mergedTypeInfo = potentialMergedTypeInfo;
388
388
  }
@@ -590,7 +590,7 @@ const handleOverrideByDelegation = utils.memoize3(
590
590
  }
591
591
  );
592
592
 
593
- function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlappingAliases, delegationContext) {
593
+ function finalizeGatewayDocument(targetSchema, originalDocument, fragments, operations, onOverlappingAliases, delegationContext) {
594
594
  let usedVariables = [];
595
595
  let usedFragments = [];
596
596
  const newOperations = [];
@@ -682,47 +682,128 @@ function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlapp
682
682
  }
683
683
  );
684
684
  }
685
+ const includedFragmentNames = new Set(newFragments.map((f) => f.name.value));
686
+ const hasDroppedFragments = includedFragmentNames.size < usedFragments.length;
685
687
  let newDocument = {
686
688
  kind: graphql.Kind.DOCUMENT,
687
689
  definitions: [...newOperations, ...newFragments]
688
690
  };
689
691
  const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
690
- if (stitchingInfo != null) {
692
+ if (stitchingInfo != null && subschemaHasProvidedSelections(
693
+ stitchingInfo,
694
+ delegationContext.subschema
695
+ )) {
696
+ const {
697
+ selectionSetsByPath: originalFieldSelectionSetsByPath,
698
+ fragments: originalFragmentsByName
699
+ } = collectFieldSelectionSetsByPath(originalDocument);
691
700
  const typeInfo = getTypeInfo(targetSchema);
701
+ const pathStack = [];
702
+ const inlineFragmentCounterStack = [];
692
703
  newDocument = graphql.visit(
693
704
  newDocument,
694
705
  graphql.visitWithTypeInfo(typeInfo, {
695
- [graphql.Kind.FIELD](fieldNode) {
696
- const parentType = typeInfo.getParentType();
697
- if (parentType) {
698
- const parentTypeName = parentType.name;
699
- const typeConfig = stitchingInfo?.mergedTypes?.[parentTypeName];
700
- if (typeConfig) {
706
+ [graphql.Kind.SELECTION_SET]: {
707
+ enter() {
708
+ inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
709
+ },
710
+ leave() {
711
+ inlineFragmentCounterStack.pop();
712
+ }
713
+ },
714
+ [graphql.Kind.OPERATION_DEFINITION]: {
715
+ enter(node) {
716
+ pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
717
+ },
718
+ leave() {
719
+ pathStack.pop();
720
+ }
721
+ },
722
+ [graphql.Kind.FRAGMENT_DEFINITION]: {
723
+ enter(node) {
724
+ pathStack.push(`frag:${node.name.value}`);
725
+ },
726
+ leave() {
727
+ pathStack.pop();
728
+ }
729
+ },
730
+ [graphql.Kind.INLINE_FRAGMENT]: {
731
+ enter(node) {
732
+ pathStack.push(
733
+ nextInlineFragmentPathSegment(
734
+ node,
735
+ inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
736
+ )
737
+ );
738
+ },
739
+ leave() {
740
+ pathStack.pop();
741
+ }
742
+ },
743
+ [graphql.Kind.FIELD]: {
744
+ enter(fieldNode) {
745
+ pathStack.push(fieldNode.alias?.value ?? fieldNode.name.value);
746
+ },
747
+ leave(fieldNode) {
748
+ try {
749
+ const parentType = typeInfo.getParentType();
750
+ if (!parentType) {
751
+ return void 0;
752
+ }
753
+ const typeConfig = stitchingInfo?.mergedTypes?.[parentType.name];
701
754
  const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
702
755
  delegationContext.subschema
703
756
  );
704
- if (providedSelectionsByField) {
705
- const providedSelection = providedSelectionsByField[fieldNode.name.value];
706
- if (providedSelection) {
707
- return {
708
- ...fieldNode,
709
- selectionSet: {
710
- kind: graphql.Kind.SELECTION_SET,
711
- selections: [
712
- ...providedSelection.selections,
713
- ...fieldNode.selectionSet?.selections ?? []
714
- ]
715
- }
716
- };
717
- }
757
+ const providedSelection = providedSelectionsByField?.[fieldNode.name.value];
758
+ if (!providedSelection) {
759
+ return void 0;
718
760
  }
761
+ const originalSelectionSet = lookupOriginalSelectionSet(
762
+ pathStack,
763
+ fieldNode,
764
+ originalFieldSelectionSetsByPath
765
+ );
766
+ const requestedProvidedSelections = intersectProvidedSelections(
767
+ providedSelection.selections,
768
+ originalSelectionSet,
769
+ originalFragmentsByName
770
+ );
771
+ if (!requestedProvidedSelections.length) {
772
+ return void 0;
773
+ }
774
+ return {
775
+ ...fieldNode,
776
+ selectionSet: {
777
+ kind: graphql.Kind.SELECTION_SET,
778
+ selections: [
779
+ ...requestedProvidedSelections,
780
+ ...fieldNode.selectionSet?.selections ?? []
781
+ ]
782
+ }
783
+ };
784
+ } finally {
785
+ pathStack.pop();
719
786
  }
720
787
  }
721
- return fieldNode;
722
788
  }
723
789
  })
724
790
  );
725
791
  }
792
+ if (hasDroppedFragments) {
793
+ const ops = newDocument.definitions.filter(
794
+ (d) => d.kind === graphql.Kind.OPERATION_DEFINITION
795
+ );
796
+ const frags = newDocument.definitions.filter(
797
+ (d) => d.kind === graphql.Kind.FRAGMENT_DEFINITION
798
+ );
799
+ newDocument = {
800
+ kind: graphql.Kind.DOCUMENT,
801
+ definitions: [
802
+ ...removeDeadFragmentSpreads(ops, includedFragmentNames),
803
+ ...frags
804
+ ]
805
+ };
806
+ }
726
807
  return {
727
808
  usedVariables,
728
809
  newDocument
@@ -733,6 +814,7 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
733
814
  const { targetSchema } = delegationContext;
734
815
  const { usedVariables, newDocument } = finalizeGatewayDocument(
735
816
  targetSchema,
817
+ originalRequest.document,
736
818
  fragments,
737
819
  operations,
738
820
  onOverlappingAliases,
@@ -758,9 +840,293 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
758
840
  variables: newVariables
759
841
  };
760
842
  }
843
+ function removeDeadFragmentSpreads(operations, included) {
844
+ function cleanSelections(selections) {
845
+ const out = [];
846
+ for (const sel of selections) {
847
+ if (sel.kind === graphql.Kind.FRAGMENT_SPREAD) {
848
+ if (included.has(sel.name.value)) {
849
+ out.push(sel);
850
+ }
851
+ } else if ((sel.kind === graphql.Kind.FIELD || sel.kind === graphql.Kind.INLINE_FRAGMENT) && sel.selectionSet) {
852
+ const cleaned = cleanSelections(sel.selectionSet.selections);
853
+ if (cleaned.length === 0 && sel.kind === graphql.Kind.INLINE_FRAGMENT) {
854
+ continue;
855
+ }
856
+ if (cleaned.length === 0 && sel.kind === graphql.Kind.FIELD) {
857
+ continue;
858
+ }
859
+ out.push(
860
+ cleaned === sel.selectionSet.selections ? sel : {
861
+ ...sel,
862
+ selectionSet: {
863
+ kind: graphql.Kind.SELECTION_SET,
864
+ selections: cleaned
865
+ }
866
+ }
867
+ );
868
+ } else {
869
+ out.push(sel);
870
+ }
871
+ }
872
+ return out;
873
+ }
874
+ return operations.map((op) => ({
875
+ ...op,
876
+ selectionSet: {
877
+ ...op.selectionSet,
878
+ selections: cleanSelections(op.selectionSet.selections)
879
+ }
880
+ }));
881
+ }
761
882
  function isTypeNameField(selection) {
762
883
  return selection.kind === graphql.Kind.FIELD && !selection.alias && selection.name.value === "__typename";
763
884
  }
885
+ function collectFieldSelectionSetsByPathImpl(document) {
886
+ const selectionSetsByPath = /* @__PURE__ */ new Map();
887
+ const fragments = /* @__PURE__ */ new Map();
888
+ for (const def of document.definitions) {
889
+ if (def.kind === graphql.Kind.FRAGMENT_DEFINITION) {
890
+ fragments.set(def.name.value, def);
891
+ }
892
+ }
893
+ const pathStack = [];
894
+ const inlineFragmentCounterStack = [];
895
+ graphql.visit(document, {
896
+ [graphql.Kind.SELECTION_SET]: {
897
+ enter() {
898
+ inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
899
+ },
900
+ leave() {
901
+ inlineFragmentCounterStack.pop();
902
+ }
903
+ },
904
+ [graphql.Kind.OPERATION_DEFINITION]: {
905
+ enter(node) {
906
+ pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
907
+ },
908
+ leave() {
909
+ pathStack.pop();
910
+ }
911
+ },
912
+ [graphql.Kind.FRAGMENT_DEFINITION]: {
913
+ enter(node) {
914
+ pathStack.push(`frag:${node.name.value}`);
915
+ },
916
+ leave() {
917
+ pathStack.pop();
918
+ }
919
+ },
920
+ [graphql.Kind.INLINE_FRAGMENT]: {
921
+ enter(node) {
922
+ pathStack.push(
923
+ nextInlineFragmentPathSegment(
924
+ node,
925
+ inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
926
+ )
927
+ );
928
+ },
929
+ leave() {
930
+ pathStack.pop();
931
+ }
932
+ },
933
+ [graphql.Kind.FIELD]: {
934
+ enter(node) {
935
+ pathStack.push(node.alias?.value ?? node.name.value);
936
+ if (node.selectionSet) {
937
+ selectionSetsByPath.set(pathStack.join(">"), node.selectionSet);
938
+ if (node.alias) {
939
+ const namePath = [...pathStack.slice(0, -1), node.name.value].join(
940
+ ">"
941
+ );
942
+ if (!selectionSetsByPath.has(namePath)) {
943
+ selectionSetsByPath.set(namePath, node.selectionSet);
944
+ }
945
+ }
946
+ }
947
+ },
948
+ leave() {
949
+ pathStack.pop();
950
+ }
951
+ }
952
+ });
953
+ return { selectionSetsByPath, fragments };
954
+ }
955
+ const collectFieldSelectionSetsByPath = utils.memoize1(
956
+ collectFieldSelectionSetsByPathImpl
957
+ );
958
+ function nextInlineFragmentPathSegment(node, counter) {
959
+ const typeName = node.typeCondition?.name.value ?? "";
960
+ if (!counter) {
961
+ return `inline:${typeName}:0`;
962
+ }
963
+ const idx = counter.get(typeName) ?? 0;
964
+ counter.set(typeName, idx + 1);
965
+ return `inline:${typeName}:${idx}`;
966
+ }
967
+ function subschemaHasProvidedSelectionsImpl(stitchingInfo, subschema) {
968
+ const mergedTypes = stitchingInfo.mergedTypes;
969
+ if (!mergedTypes) {
970
+ return false;
971
+ }
972
+ for (const typeConfig of Object.values(mergedTypes)) {
973
+ if (typeConfig?.providedSelectionsByField?.has(subschema)) {
974
+ return true;
975
+ }
976
+ }
977
+ return false;
978
+ }
979
+ const subschemaHasProvidedSelections = utils.memoize2(
980
+ subschemaHasProvidedSelectionsImpl
981
+ );
982
+ function lookupOriginalSelectionSet(pathStack, fieldNode, selectionSetsByPath) {
983
+ const exact = selectionSetsByPath.get(pathStack.join(">"));
984
+ if (exact || !fieldNode.alias) {
985
+ return exact;
986
+ }
987
+ const fallback = [...pathStack.slice(0, -1), fieldNode.name.value].join(">");
988
+ return selectionSetsByPath.get(fallback);
989
+ }
990
+ function intersectProvidedSelections(providedSelections, originalSelectionSet, fragments) {
991
+ if (!originalSelectionSet) {
992
+ return [];
993
+ }
994
+ const providedFieldsByName = /* @__PURE__ */ new Map();
995
+ const otherProvided = [];
996
+ for (const provided of providedSelections) {
997
+ if (provided.kind === graphql.Kind.FIELD) {
998
+ providedFieldsByName.set(provided.name.value, provided);
999
+ } else {
1000
+ otherProvided.push(provided);
1001
+ }
1002
+ }
1003
+ const result = [];
1004
+ collectMatchingOriginalSelections(
1005
+ originalSelectionSet,
1006
+ providedFieldsByName,
1007
+ fragments,
1008
+ /* @__PURE__ */ new Set(),
1009
+ /* @__PURE__ */ new Set(),
1010
+ result
1011
+ );
1012
+ return [...result, ...otherProvided];
1013
+ }
1014
+ function collectMatchingOriginalSelections(selectionSet, providedFieldsByName, fragments, seenFieldNames, seenFragmentNames, result) {
1015
+ for (const sel of selectionSet.selections) {
1016
+ if (sel.kind === graphql.Kind.FIELD) {
1017
+ const provided = providedFieldsByName.get(sel.name.value);
1018
+ if (!provided) {
1019
+ continue;
1020
+ }
1021
+ const dedupKey = sel.alias?.value ?? sel.name.value;
1022
+ if (seenFieldNames.has(dedupKey)) {
1023
+ continue;
1024
+ }
1025
+ seenFieldNames.add(dedupKey);
1026
+ if (provided.selectionSet && sel.selectionSet) {
1027
+ const nested = intersectProvidedSelections(
1028
+ provided.selectionSet.selections,
1029
+ sel.selectionSet,
1030
+ fragments
1031
+ );
1032
+ if (!nested.length) {
1033
+ continue;
1034
+ }
1035
+ result.push({
1036
+ ...sel,
1037
+ selectionSet: {
1038
+ kind: graphql.Kind.SELECTION_SET,
1039
+ selections: nested
1040
+ }
1041
+ });
1042
+ } else {
1043
+ result.push(sel);
1044
+ }
1045
+ } else if (sel.kind === graphql.Kind.INLINE_FRAGMENT && sel.selectionSet) {
1046
+ const hasDirectives = (sel.directives?.length ?? 0) > 0;
1047
+ const hasTypeCondition = sel.typeCondition != null;
1048
+ if (hasDirectives || hasTypeCondition) {
1049
+ const inner = [];
1050
+ collectMatchingOriginalSelections(
1051
+ sel.selectionSet,
1052
+ providedFieldsByName,
1053
+ fragments,
1054
+ /* @__PURE__ */ new Set(),
1055
+ /* @__PURE__ */ new Set(),
1056
+ inner
1057
+ );
1058
+ if (inner.length === 0) {
1059
+ continue;
1060
+ }
1061
+ result.push({
1062
+ kind: graphql.Kind.INLINE_FRAGMENT,
1063
+ typeCondition: sel.typeCondition,
1064
+ directives: sel.directives,
1065
+ selectionSet: {
1066
+ kind: graphql.Kind.SELECTION_SET,
1067
+ selections: inner
1068
+ }
1069
+ });
1070
+ } else {
1071
+ collectMatchingOriginalSelections(
1072
+ sel.selectionSet,
1073
+ providedFieldsByName,
1074
+ fragments,
1075
+ seenFieldNames,
1076
+ seenFragmentNames,
1077
+ result
1078
+ );
1079
+ }
1080
+ } else if (sel.kind === graphql.Kind.FRAGMENT_SPREAD) {
1081
+ const fragmentDef = fragments.get(sel.name.value);
1082
+ if (!fragmentDef) {
1083
+ continue;
1084
+ }
1085
+ const hasSpreadDirectives = (sel.directives?.length ?? 0) > 0;
1086
+ const hasFragmentDirectives = (fragmentDef.directives?.length ?? 0) > 0;
1087
+ if (hasSpreadDirectives || hasFragmentDirectives) {
1088
+ const inner = [];
1089
+ collectMatchingOriginalSelections(
1090
+ fragmentDef.selectionSet,
1091
+ providedFieldsByName,
1092
+ fragments,
1093
+ /* @__PURE__ */ new Set(),
1094
+ /* @__PURE__ */ new Set(),
1095
+ inner
1096
+ );
1097
+ if (inner.length === 0) {
1098
+ continue;
1099
+ }
1100
+ const combinedDirectives = [
1101
+ ...sel.directives ?? [],
1102
+ ...fragmentDef.directives ?? []
1103
+ ];
1104
+ result.push({
1105
+ kind: graphql.Kind.INLINE_FRAGMENT,
1106
+ typeCondition: fragmentDef.typeCondition,
1107
+ directives: combinedDirectives.length > 0 ? combinedDirectives : void 0,
1108
+ selectionSet: {
1109
+ kind: graphql.Kind.SELECTION_SET,
1110
+ selections: inner
1111
+ }
1112
+ });
1113
+ } else {
1114
+ if (seenFragmentNames.has(sel.name.value)) {
1115
+ continue;
1116
+ }
1117
+ seenFragmentNames.add(sel.name.value);
1118
+ collectMatchingOriginalSelections(
1119
+ fragmentDef.selectionSet,
1120
+ providedFieldsByName,
1121
+ fragments,
1122
+ seenFieldNames,
1123
+ seenFragmentNames,
1124
+ result
1125
+ );
1126
+ }
1127
+ }
1128
+ }
1129
+ }
764
1130
  function filterTypenameFields(selections) {
765
1131
  let hasTypeNameField = false;
766
1132
  const filteredSelections = selections.filter((selection) => {
@@ -809,15 +1175,17 @@ function collectFragmentVariables(targetSchema, fragmentSet, validFragments, val
809
1175
  usedVariables = union(usedVariables, fragmentUsedVariables);
810
1176
  if (name && !(name in fragmentSet)) {
811
1177
  fragmentSet[name] = true;
812
- newFragments.push({
813
- kind: graphql.Kind.FRAGMENT_DEFINITION,
814
- name: {
815
- kind: graphql.Kind.NAME,
816
- value: name
817
- },
818
- typeCondition: fragment.typeCondition,
819
- selectionSet
820
- });
1178
+ if (selectionSet.selections.length > 0) {
1179
+ newFragments.push({
1180
+ kind: graphql.Kind.FRAGMENT_DEFINITION,
1181
+ name: {
1182
+ kind: graphql.Kind.NAME,
1183
+ value: name
1184
+ },
1185
+ typeCondition: fragment.typeCondition,
1186
+ selectionSet
1187
+ });
1188
+ }
821
1189
  }
822
1190
  }
823
1191
  }
@@ -1986,10 +2354,18 @@ function createRequest({
1986
2354
  const existingArgNode = fieldNode?.arguments?.find(
1987
2355
  (argNode) => argNode.name.value === argName
1988
2356
  );
1989
- if (existingArgNode?.value.kind === graphql.Kind.VARIABLE) {
2357
+ if (existingArgNode?.value.kind === graphql.Kind.VARIABLE && argInstance) {
1990
2358
  const varName = existingArgNode.value.name.value;
1991
2359
  const varValue = newVariables[varName];
1992
- if (varValue === argValue) {
2360
+ const variableDefinition = variableDefinitions.find(
2361
+ (definition) => definition.variable.name.value === varName
2362
+ );
2363
+ const variableType = variableDefinition && targetSchema ? graphql.typeFromAST(info?.schema ?? targetSchema, variableDefinition.type) : void 0;
2364
+ if (varValue === argValue && variableType && graphql.isTypeSubTypeOf(
2365
+ info?.schema ?? targetSchema,
2366
+ variableType,
2367
+ argInstance.type
2368
+ )) {
1993
2369
  argNodes.push(existingArgNode);
1994
2370
  continue;
1995
2371
  }
@@ -2010,6 +2386,16 @@ function createRequest({
2010
2386
  varName = `_${i++}_${rootFieldName}_${argName}`;
2011
2387
  }
2012
2388
  }
2389
+ if (existingArgNode?.value.kind === graphql.Kind.VARIABLE) {
2390
+ const existingVarName = existingArgNode.value.name.value;
2391
+ const existingVarIndex = variableDefinitions.findIndex(
2392
+ (definition) => definition.variable.name.value === existingVarName
2393
+ );
2394
+ if (existingVarIndex !== -1) {
2395
+ variableDefinitions.splice(existingVarIndex, 1);
2396
+ delete newVariables[existingVarName];
2397
+ }
2398
+ }
2013
2399
  variableDefinitions.push({
2014
2400
  kind: graphql.Kind.VARIABLE_DEFINITION,
2015
2401
  variable: {
@@ -2181,6 +2567,9 @@ function defaultMergedResolver(parent, args, context, info) {
2181
2567
  }
2182
2568
  return deferred.promise;
2183
2569
  }
2570
+ if (Object.prototype.hasOwnProperty.call(parent, info.fieldName)) {
2571
+ return graphql.defaultFieldResolver(parent, args, context, info);
2572
+ }
2184
2573
  return void 0;
2185
2574
  }
2186
2575
  return handleResult(parent, responseKey, context, info);
@@ -2609,7 +2998,7 @@ function delegateToSchema(options) {
2609
2998
  const request = createRequest({
2610
2999
  subgraphName: schema.name,
2611
3000
  fragments,
2612
- targetSchema,
3001
+ targetSchema: targetSchema.getQueryType()?.getFields()[fieldName] == null && isSubschemaConfig(schema) ? schema.schema : targetSchema,
2613
3002
  rootValue,
2614
3003
  targetOperationName: operationName,
2615
3004
  targetOperation: operation,
@@ -2647,9 +3036,9 @@ function delegateRequest(options) {
2647
3036
  () => getExecutor(delegationContext)(processedRequest),
2648
3037
  function handleExecutorResult(executorResult) {
2649
3038
  if (utils.isAsyncIterable(executorResult)) {
2650
- if (delegationContext.operation === "query" && graphql.isListType(delegationContext.returnType)) {
3039
+ if (delegationContext.operation === "query" && graphql.isListType(graphql.getNullableType(delegationContext.returnType))) {
2651
3040
  return new repeater.Repeater(async (push, stop) => {
2652
- const pushed = /* @__PURE__ */ new WeakSet();
3041
+ let pushedCount = 0;
2653
3042
  let stopped = false;
2654
3043
  stop.finally(() => {
2655
3044
  stopped = true;
@@ -2681,15 +3070,12 @@ function delegateRequest(options) {
2681
3070
  }
2682
3071
  const transformedResult = await transformer.transformResult(result);
2683
3072
  if (Array.isArray(transformedResult)) {
2684
- for (const individualResult$ of transformedResult) {
3073
+ for (let i = pushedCount; i < transformedResult.length; i++) {
2685
3074
  if (stopped) {
2686
3075
  break;
2687
3076
  }
2688
- const individualResult = await individualResult$;
2689
- if (!pushed.has(individualResult)) {
2690
- pushed.add(individualResult);
2691
- await push(individualResult);
2692
- }
3077
+ await push(await transformedResult[i]);
3078
+ pushedCount = i + 1;
2693
3079
  }
2694
3080
  } else {
2695
3081
  await push(await transformedResult);
@@ -2765,7 +3151,9 @@ function getDelegationContext({
2765
3151
  ),
2766
3152
  transforms,
2767
3153
  transformedSchema: transformedSchema ?? subschemaOrSubschemaConfig,
2768
- skipTypeMerging
3154
+ skipTypeMerging,
3155
+ onLocatedError,
3156
+ args
2769
3157
  };
2770
3158
  }
2771
3159
  function validateRequest(delegationContext, document) {
@@ -2896,6 +3284,7 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
2896
3284
  });
2897
3285
  }
2898
3286
  } else if (graphql.isObjectType(subFieldType) || graphql.isInterfaceType(subFieldType)) {
3287
+ const unavailableSubSelections = [];
2899
3288
  const subFieldTypeFields = subFieldType.getFields();
2900
3289
  for (const subSelection of selection.selectionSet.selections) {
2901
3290
  if (subSelection.kind === graphql.Kind.FIELD) {
@@ -2906,11 +3295,20 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
2906
3295
  const subSelectionField = subFieldTypeFields[subSelection.name.value];
2907
3296
  if (!subSelectionField) {
2908
3297
  if (shouldAdd(subFieldType, subSelection)) {
2909
- unavailableSelections.push(subSelection);
3298
+ unavailableSubSelections.push(subSelection);
2910
3299
  }
2911
3300
  }
2912
3301
  }
2913
3302
  }
3303
+ if (unavailableSubSelections.length) {
3304
+ unavailableSelections.push({
3305
+ ...selection,
3306
+ selectionSet: {
3307
+ kind: graphql.Kind.SELECTION_SET,
3308
+ selections: unavailableSubSelections
3309
+ }
3310
+ });
3311
+ }
2914
3312
  }
2915
3313
  } else if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
2916
3314
  const fragment = fragments[selection.name.value];
@@ -2958,7 +3356,7 @@ function extractUnavailableFields(schema, field, fieldNode, shouldAdd, fragments
2958
3356
  }
2959
3357
  return [];
2960
3358
  }
2961
- function subtractSelectionSets(selectionSetA, selectionSetB) {
3359
+ function subtractSelectionSets(selectionSetA, selectionSetB, fragments = {}) {
2962
3360
  const newSelections = [];
2963
3361
  for (const selectionA of selectionSetA.selections) {
2964
3362
  switch (selectionA.kind) {
@@ -2974,7 +3372,7 @@ function subtractSelectionSets(selectionSetA, selectionSetB) {
2974
3372
  );
2975
3373
  if (fieldsInOtherSelectionSet.length > 0 && fieldA.selectionSet?.selections?.length) {
2976
3374
  const newSubSelection = fieldsInOtherSelectionSet.reduce(
2977
- (acc, fieldB) => fieldB.selectionSet ? subtractSelectionSets(acc, fieldB.selectionSet) : acc,
3375
+ (acc, fieldB) => fieldB.selectionSet ? subtractSelectionSets(acc, fieldB.selectionSet, fragments) : acc,
2978
3376
  {
2979
3377
  kind: graphql.Kind.SELECTION_SET,
2980
3378
  selections: fieldA.selectionSet.selections
@@ -3004,7 +3402,11 @@ function subtractSelectionSets(selectionSetA, selectionSetB) {
3004
3402
  );
3005
3403
  if (inlineFragmentsFromB.length > 0) {
3006
3404
  const newSubSelection = inlineFragmentsFromB.reduce(
3007
- (acc, subselectionB) => subselectionB.selectionSet ? subtractSelectionSets(acc, subselectionB.selectionSet) : acc,
3405
+ (acc, subselectionB) => subselectionB.selectionSet ? subtractSelectionSets(
3406
+ acc,
3407
+ subselectionB.selectionSet,
3408
+ fragments
3409
+ ) : acc,
3008
3410
  {
3009
3411
  kind: graphql.Kind.SELECTION_SET,
3010
3412
  selections: inlineFragmentA.selectionSet.selections
@@ -3032,7 +3434,25 @@ function subtractSelectionSets(selectionSetA, selectionSetB) {
3032
3434
  }
3033
3435
  case graphql.Kind.FRAGMENT_SPREAD: {
3034
3436
  const fragmentSpreadA = selectionA;
3035
- if (!selectionSetB.selections.some(
3437
+ const fragment = fragments[fragmentSpreadA.name.value];
3438
+ if (fragment) {
3439
+ const newSubSelection = subtractSelectionSets(
3440
+ fragment.selectionSet,
3441
+ selectionSetB,
3442
+ fragments
3443
+ );
3444
+ if (newSubSelection.selections.length) {
3445
+ newSelections.push({
3446
+ kind: graphql.Kind.INLINE_FRAGMENT,
3447
+ typeCondition: fragment.typeCondition,
3448
+ directives: [
3449
+ ...fragment.directives ?? [],
3450
+ ...fragmentSpreadA.directives ?? []
3451
+ ],
3452
+ selectionSet: newSubSelection
3453
+ });
3454
+ }
3455
+ } else if (!selectionSetB.selections.some(
3036
3456
  (subselectionB) => subselectionB.kind === graphql.Kind.FRAGMENT_SPREAD && subselectionB.name.value === fragmentSpreadA.name.value
3037
3457
  )) {
3038
3458
  newSelections.push(selectionA);