@graphql-tools/delegate 12.0.17-alpha-cc6e877dd43c47e20e00638efc161a0bed4e2ccb → 12.0.17

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/CHANGELOG.md CHANGED
@@ -1,40 +1,13 @@
1
1
  # @graphql-tools/delegate
2
2
 
3
- ## 12.0.17-alpha-cc6e877dd43c47e20e00638efc161a0bed4e2ccb
3
+ ## 12.0.17
4
4
  ### Patch Changes
5
5
 
6
6
 
7
7
 
8
- - [#2351](https://github.com/graphql-hive/gateway/pull/2351) [`96a4674`](https://github.com/graphql-hive/gateway/commit/96a4674a514e93d69c299c07916e06f49feed477) Thanks [@ardatan](https://github.com/ardatan)! - Fix `@provides` so the gateway only requests the provided fields the client actually selected, and stops delegating to the owner subgraph when `@provides` already covers the request.
8
+ - [#2382](https://github.com/graphql-hive/gateway/pull/2382) [`3e774e0`](https://github.com/graphql-hive/gateway/commit/3e774e050bc2d3c33e0f36a258ab6a8d94bf0750) Thanks [@gonzo-32](https://github.com/gonzo-32)! - Fix cross-subgraph delegation for unavailable fields selected through inline fragments or fragment spreads on interface fields.
9
9
 
10
- Previously, when a subgraph declared `@provides(fields: "...")` on a field, the gateway would still:
11
-
12
- 1. Forward **every** field listed in `@provides` to that subgraph, even when the client never asked for them.
13
- 2. After receiving the response, plan additional delegations to the owner subgraph for `@provides`-covered fields whenever the providing subgraph declared them as `@external`, even though the data was already returned.
14
-
15
- For example with:
16
-
17
- ```graphql
18
- # subgraph B (provider)
19
- type Query {
20
- entity: Entity @provides(fields: "name description")
21
- }
22
-
23
- type Entity @key(fields: "id") {
24
- id: ID!
25
- name: String! @external
26
- description: String! @external
27
- }
28
- ```
29
-
30
- a client query of `{ entity { id name } }` would still cause the gateway to ask subgraph B for `description` *and* fetch `name` again from subgraph A (the owner of `Entity`).
31
-
32
- After this fix:
33
-
34
- - Only the `@provides` fields the client actually selected are forwarded to the providing subgraph (request side).
35
- - The delegation planner now recognises `@provides` declarations at every nested level (e.g. `@provides(fields: "categories { id name subCategories { id name } }")`) and `@provides` declarations made via inline fragments on union/interface members (e.g. `@provides(fields: "... on Book { title }")`), so the gateway no longer round-trips to the owner subgraph for fields that the providing subgraph has already returned.
36
-
37
- Aliases, fragments, fragment spreads, `@include`/`@skip` directives wrapping a `@provides` field, and nested `@provides` selections are preserved.
10
+ The fix in `extractUnavailableFieldsFromSelectionSet` within `extractUnavailableFields.ts` now preserves the fragment wrapper and type condition, so concrete-type fields do not lose their type context and get treated as missing.
38
11
 
39
12
  ## 12.0.16
40
13
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -590,7 +590,7 @@ const handleOverrideByDelegation = utils.memoize3(
590
590
  }
591
591
  );
592
592
 
593
- function finalizeGatewayDocument(targetSchema, originalDocument, fragments, operations, onOverlappingAliases, delegationContext) {
593
+ function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlappingAliases, delegationContext) {
594
594
  let usedVariables = [];
595
595
  let usedFragments = [];
596
596
  const newOperations = [];
@@ -687,102 +687,38 @@ function finalizeGatewayDocument(targetSchema, originalDocument, fragments, oper
687
687
  definitions: [...newOperations, ...newFragments]
688
688
  };
689
689
  const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
690
- if (stitchingInfo != null && subschemaHasProvidedSelections(
691
- stitchingInfo,
692
- delegationContext.subschema
693
- )) {
694
- const {
695
- selectionSetsByPath: originalFieldSelectionSetsByPath,
696
- fragments: originalFragmentsByName
697
- } = collectFieldSelectionSetsByPath(originalDocument);
690
+ if (stitchingInfo != null) {
698
691
  const typeInfo = getTypeInfo(targetSchema);
699
- const pathStack = [];
700
- const inlineFragmentCounterStack = [];
701
692
  newDocument = graphql.visit(
702
693
  newDocument,
703
694
  graphql.visitWithTypeInfo(typeInfo, {
704
- [graphql.Kind.SELECTION_SET]: {
705
- enter() {
706
- inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
707
- },
708
- leave() {
709
- inlineFragmentCounterStack.pop();
710
- }
711
- },
712
- [graphql.Kind.OPERATION_DEFINITION]: {
713
- enter(node) {
714
- pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
715
- },
716
- leave() {
717
- pathStack.pop();
718
- }
719
- },
720
- [graphql.Kind.FRAGMENT_DEFINITION]: {
721
- enter(node) {
722
- pathStack.push(`frag:${node.name.value}`);
723
- },
724
- leave() {
725
- pathStack.pop();
726
- }
727
- },
728
- [graphql.Kind.INLINE_FRAGMENT]: {
729
- enter(node) {
730
- pathStack.push(
731
- nextInlineFragmentPathSegment(
732
- node,
733
- inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
734
- )
735
- );
736
- },
737
- leave() {
738
- pathStack.pop();
739
- }
740
- },
741
- [graphql.Kind.FIELD]: {
742
- enter(fieldNode) {
743
- pathStack.push(fieldNode.alias?.value ?? fieldNode.name.value);
744
- },
745
- leave(fieldNode) {
746
- try {
747
- const parentType = typeInfo.getParentType();
748
- if (!parentType) {
749
- return void 0;
750
- }
751
- const typeConfig = stitchingInfo?.mergedTypes?.[parentType.name];
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) {
752
701
  const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
753
702
  delegationContext.subschema
754
703
  );
755
- const providedSelection = providedSelectionsByField?.[fieldNode.name.value];
756
- if (!providedSelection) {
757
- return void 0;
758
- }
759
- const originalSelectionSet = lookupOriginalSelectionSet(
760
- pathStack,
761
- fieldNode,
762
- originalFieldSelectionSetsByPath
763
- );
764
- const requestedProvidedSelections = intersectProvidedSelections(
765
- providedSelection.selections,
766
- originalSelectionSet,
767
- originalFragmentsByName
768
- );
769
- if (!requestedProvidedSelections.length) {
770
- return void 0;
771
- }
772
- return {
773
- ...fieldNode,
774
- selectionSet: {
775
- kind: graphql.Kind.SELECTION_SET,
776
- selections: [
777
- ...requestedProvidedSelections,
778
- ...fieldNode.selectionSet?.selections ?? []
779
- ]
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
+ };
780
717
  }
781
- };
782
- } finally {
783
- pathStack.pop();
718
+ }
784
719
  }
785
720
  }
721
+ return fieldNode;
786
722
  }
787
723
  })
788
724
  );
@@ -797,7 +733,6 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
797
733
  const { targetSchema } = delegationContext;
798
734
  const { usedVariables, newDocument } = finalizeGatewayDocument(
799
735
  targetSchema,
800
- originalRequest.document,
801
736
  fragments,
802
737
  operations,
803
738
  onOverlappingAliases,
@@ -826,251 +761,6 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
826
761
  function isTypeNameField(selection) {
827
762
  return selection.kind === graphql.Kind.FIELD && !selection.alias && selection.name.value === "__typename";
828
763
  }
829
- function collectFieldSelectionSetsByPathImpl(document) {
830
- const selectionSetsByPath = /* @__PURE__ */ new Map();
831
- const fragments = /* @__PURE__ */ new Map();
832
- for (const def of document.definitions) {
833
- if (def.kind === graphql.Kind.FRAGMENT_DEFINITION) {
834
- fragments.set(def.name.value, def);
835
- }
836
- }
837
- const pathStack = [];
838
- const inlineFragmentCounterStack = [];
839
- graphql.visit(document, {
840
- [graphql.Kind.SELECTION_SET]: {
841
- enter() {
842
- inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
843
- },
844
- leave() {
845
- inlineFragmentCounterStack.pop();
846
- }
847
- },
848
- [graphql.Kind.OPERATION_DEFINITION]: {
849
- enter(node) {
850
- pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
851
- },
852
- leave() {
853
- pathStack.pop();
854
- }
855
- },
856
- [graphql.Kind.FRAGMENT_DEFINITION]: {
857
- enter(node) {
858
- pathStack.push(`frag:${node.name.value}`);
859
- },
860
- leave() {
861
- pathStack.pop();
862
- }
863
- },
864
- [graphql.Kind.INLINE_FRAGMENT]: {
865
- enter(node) {
866
- pathStack.push(
867
- nextInlineFragmentPathSegment(
868
- node,
869
- inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
870
- )
871
- );
872
- },
873
- leave() {
874
- pathStack.pop();
875
- }
876
- },
877
- [graphql.Kind.FIELD]: {
878
- enter(node) {
879
- pathStack.push(node.alias?.value ?? node.name.value);
880
- if (node.selectionSet) {
881
- selectionSetsByPath.set(pathStack.join(">"), node.selectionSet);
882
- if (node.alias) {
883
- const namePath = [...pathStack.slice(0, -1), node.name.value].join(
884
- ">"
885
- );
886
- if (!selectionSetsByPath.has(namePath)) {
887
- selectionSetsByPath.set(namePath, node.selectionSet);
888
- }
889
- }
890
- }
891
- },
892
- leave() {
893
- pathStack.pop();
894
- }
895
- }
896
- });
897
- return { selectionSetsByPath, fragments };
898
- }
899
- const collectFieldSelectionSetsByPath = utils.memoize1(
900
- collectFieldSelectionSetsByPathImpl
901
- );
902
- function nextInlineFragmentPathSegment(node, counter) {
903
- const typeName = node.typeCondition?.name.value ?? "";
904
- if (!counter) {
905
- return `inline:${typeName}:0`;
906
- }
907
- const idx = counter.get(typeName) ?? 0;
908
- counter.set(typeName, idx + 1);
909
- return `inline:${typeName}:${idx}`;
910
- }
911
- function subschemaHasProvidedSelectionsImpl(stitchingInfo, subschema) {
912
- const mergedTypes = stitchingInfo.mergedTypes;
913
- if (!mergedTypes) {
914
- return false;
915
- }
916
- for (const typeConfig of Object.values(mergedTypes)) {
917
- if (typeConfig?.providedSelectionsByField?.has(subschema)) {
918
- return true;
919
- }
920
- }
921
- return false;
922
- }
923
- const subschemaHasProvidedSelections = utils.memoize2(
924
- subschemaHasProvidedSelectionsImpl
925
- );
926
- function lookupOriginalSelectionSet(pathStack, fieldNode, selectionSetsByPath) {
927
- const exact = selectionSetsByPath.get(pathStack.join(">"));
928
- if (exact || !fieldNode.alias) {
929
- return exact;
930
- }
931
- const fallback = [...pathStack.slice(0, -1), fieldNode.name.value].join(">");
932
- return selectionSetsByPath.get(fallback);
933
- }
934
- function intersectProvidedSelections(providedSelections, originalSelectionSet, fragments) {
935
- if (!originalSelectionSet) {
936
- return [];
937
- }
938
- const providedFieldsByName = /* @__PURE__ */ new Map();
939
- const otherProvided = [];
940
- for (const provided of providedSelections) {
941
- if (provided.kind === graphql.Kind.FIELD) {
942
- providedFieldsByName.set(provided.name.value, provided);
943
- } else {
944
- otherProvided.push(provided);
945
- }
946
- }
947
- const result = [];
948
- collectMatchingOriginalSelections(
949
- originalSelectionSet,
950
- providedFieldsByName,
951
- fragments,
952
- /* @__PURE__ */ new Set(),
953
- /* @__PURE__ */ new Set(),
954
- result
955
- );
956
- return [...result, ...otherProvided];
957
- }
958
- function collectMatchingOriginalSelections(selectionSet, providedFieldsByName, fragments, seenFieldNames, seenFragmentNames, result) {
959
- for (const sel of selectionSet.selections) {
960
- if (sel.kind === graphql.Kind.FIELD) {
961
- const provided = providedFieldsByName.get(sel.name.value);
962
- if (!provided) {
963
- continue;
964
- }
965
- const dedupKey = sel.alias?.value ?? sel.name.value;
966
- if (seenFieldNames.has(dedupKey)) {
967
- continue;
968
- }
969
- seenFieldNames.add(dedupKey);
970
- if (provided.selectionSet && sel.selectionSet) {
971
- const nested = intersectProvidedSelections(
972
- provided.selectionSet.selections,
973
- sel.selectionSet,
974
- fragments
975
- );
976
- if (!nested.length) {
977
- continue;
978
- }
979
- result.push({
980
- ...sel,
981
- selectionSet: {
982
- kind: graphql.Kind.SELECTION_SET,
983
- selections: nested
984
- }
985
- });
986
- } else {
987
- result.push(sel);
988
- }
989
- } else if (sel.kind === graphql.Kind.INLINE_FRAGMENT && sel.selectionSet) {
990
- const hasDirectives = (sel.directives?.length ?? 0) > 0;
991
- const hasTypeCondition = sel.typeCondition != null;
992
- if (hasDirectives || hasTypeCondition) {
993
- const inner = [];
994
- collectMatchingOriginalSelections(
995
- sel.selectionSet,
996
- providedFieldsByName,
997
- fragments,
998
- /* @__PURE__ */ new Set(),
999
- /* @__PURE__ */ new Set(),
1000
- inner
1001
- );
1002
- if (inner.length === 0) {
1003
- continue;
1004
- }
1005
- result.push({
1006
- kind: graphql.Kind.INLINE_FRAGMENT,
1007
- typeCondition: sel.typeCondition,
1008
- directives: sel.directives,
1009
- selectionSet: {
1010
- kind: graphql.Kind.SELECTION_SET,
1011
- selections: inner
1012
- }
1013
- });
1014
- } else {
1015
- collectMatchingOriginalSelections(
1016
- sel.selectionSet,
1017
- providedFieldsByName,
1018
- fragments,
1019
- seenFieldNames,
1020
- seenFragmentNames,
1021
- result
1022
- );
1023
- }
1024
- } else if (sel.kind === graphql.Kind.FRAGMENT_SPREAD) {
1025
- const fragmentDef = fragments.get(sel.name.value);
1026
- if (!fragmentDef) {
1027
- continue;
1028
- }
1029
- const hasSpreadDirectives = (sel.directives?.length ?? 0) > 0;
1030
- const hasFragmentDirectives = (fragmentDef.directives?.length ?? 0) > 0;
1031
- if (hasSpreadDirectives || hasFragmentDirectives) {
1032
- const inner = [];
1033
- collectMatchingOriginalSelections(
1034
- fragmentDef.selectionSet,
1035
- providedFieldsByName,
1036
- fragments,
1037
- /* @__PURE__ */ new Set(),
1038
- /* @__PURE__ */ new Set(),
1039
- inner
1040
- );
1041
- if (inner.length === 0) {
1042
- continue;
1043
- }
1044
- const combinedDirectives = [
1045
- ...sel.directives ?? [],
1046
- ...fragmentDef.directives ?? []
1047
- ];
1048
- result.push({
1049
- kind: graphql.Kind.INLINE_FRAGMENT,
1050
- typeCondition: fragmentDef.typeCondition,
1051
- directives: combinedDirectives.length > 0 ? combinedDirectives : void 0,
1052
- selectionSet: {
1053
- kind: graphql.Kind.SELECTION_SET,
1054
- selections: inner
1055
- }
1056
- });
1057
- } else {
1058
- if (seenFragmentNames.has(sel.name.value)) {
1059
- continue;
1060
- }
1061
- seenFragmentNames.add(sel.name.value);
1062
- collectMatchingOriginalSelections(
1063
- fragmentDef.selectionSet,
1064
- providedFieldsByName,
1065
- fragments,
1066
- seenFieldNames,
1067
- seenFragmentNames,
1068
- result
1069
- );
1070
- }
1071
- }
1072
- }
1073
- }
1074
764
  function filterTypenameFields(selections) {
1075
765
  let hasTypeNameField = false;
1076
766
  const filteredSelections = selections.filter((selection) => {
@@ -3205,6 +2895,7 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
3205
2895
  });
3206
2896
  }
3207
2897
  } else if (graphql.isObjectType(subFieldType) || graphql.isInterfaceType(subFieldType)) {
2898
+ const unavailableSubSelections = [];
3208
2899
  const subFieldTypeFields = subFieldType.getFields();
3209
2900
  for (const subSelection of selection.selectionSet.selections) {
3210
2901
  if (subSelection.kind === graphql.Kind.FIELD) {
@@ -3215,11 +2906,20 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
3215
2906
  const subSelectionField = subFieldTypeFields[subSelection.name.value];
3216
2907
  if (!subSelectionField) {
3217
2908
  if (shouldAdd(subFieldType, subSelection)) {
3218
- unavailableSelections.push(subSelection);
2909
+ unavailableSubSelections.push(subSelection);
3219
2910
  }
3220
2911
  }
3221
2912
  }
3222
2913
  }
2914
+ if (unavailableSubSelections.length) {
2915
+ unavailableSelections.push({
2916
+ ...selection,
2917
+ selectionSet: {
2918
+ kind: graphql.Kind.SELECTION_SET,
2919
+ selections: unavailableSubSelections
2920
+ }
2921
+ });
2922
+ }
3223
2923
  }
3224
2924
  } else if (selection.kind === graphql.Kind.FRAGMENT_SPREAD) {
3225
2925
  const fragment = fragments[selection.name.value];
package/dist/index.js CHANGED
@@ -590,7 +590,7 @@ const handleOverrideByDelegation = memoize3(
590
590
  }
591
591
  );
592
592
 
593
- function finalizeGatewayDocument(targetSchema, originalDocument, fragments, operations, onOverlappingAliases, delegationContext) {
593
+ function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlappingAliases, delegationContext) {
594
594
  let usedVariables = [];
595
595
  let usedFragments = [];
596
596
  const newOperations = [];
@@ -687,102 +687,38 @@ function finalizeGatewayDocument(targetSchema, originalDocument, fragments, oper
687
687
  definitions: [...newOperations, ...newFragments]
688
688
  };
689
689
  const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
690
- if (stitchingInfo != null && subschemaHasProvidedSelections(
691
- stitchingInfo,
692
- delegationContext.subschema
693
- )) {
694
- const {
695
- selectionSetsByPath: originalFieldSelectionSetsByPath,
696
- fragments: originalFragmentsByName
697
- } = collectFieldSelectionSetsByPath(originalDocument);
690
+ if (stitchingInfo != null) {
698
691
  const typeInfo = getTypeInfo(targetSchema);
699
- const pathStack = [];
700
- const inlineFragmentCounterStack = [];
701
692
  newDocument = visit(
702
693
  newDocument,
703
694
  visitWithTypeInfo(typeInfo, {
704
- [Kind.SELECTION_SET]: {
705
- enter() {
706
- inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
707
- },
708
- leave() {
709
- inlineFragmentCounterStack.pop();
710
- }
711
- },
712
- [Kind.OPERATION_DEFINITION]: {
713
- enter(node) {
714
- pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
715
- },
716
- leave() {
717
- pathStack.pop();
718
- }
719
- },
720
- [Kind.FRAGMENT_DEFINITION]: {
721
- enter(node) {
722
- pathStack.push(`frag:${node.name.value}`);
723
- },
724
- leave() {
725
- pathStack.pop();
726
- }
727
- },
728
- [Kind.INLINE_FRAGMENT]: {
729
- enter(node) {
730
- pathStack.push(
731
- nextInlineFragmentPathSegment(
732
- node,
733
- inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
734
- )
735
- );
736
- },
737
- leave() {
738
- pathStack.pop();
739
- }
740
- },
741
- [Kind.FIELD]: {
742
- enter(fieldNode) {
743
- pathStack.push(fieldNode.alias?.value ?? fieldNode.name.value);
744
- },
745
- leave(fieldNode) {
746
- try {
747
- const parentType = typeInfo.getParentType();
748
- if (!parentType) {
749
- return void 0;
750
- }
751
- const typeConfig = stitchingInfo?.mergedTypes?.[parentType.name];
695
+ [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) {
752
701
  const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
753
702
  delegationContext.subschema
754
703
  );
755
- const providedSelection = providedSelectionsByField?.[fieldNode.name.value];
756
- if (!providedSelection) {
757
- return void 0;
758
- }
759
- const originalSelectionSet = lookupOriginalSelectionSet(
760
- pathStack,
761
- fieldNode,
762
- originalFieldSelectionSetsByPath
763
- );
764
- const requestedProvidedSelections = intersectProvidedSelections(
765
- providedSelection.selections,
766
- originalSelectionSet,
767
- originalFragmentsByName
768
- );
769
- if (!requestedProvidedSelections.length) {
770
- return void 0;
771
- }
772
- return {
773
- ...fieldNode,
774
- selectionSet: {
775
- kind: Kind.SELECTION_SET,
776
- selections: [
777
- ...requestedProvidedSelections,
778
- ...fieldNode.selectionSet?.selections ?? []
779
- ]
704
+ if (providedSelectionsByField) {
705
+ const providedSelection = providedSelectionsByField[fieldNode.name.value];
706
+ if (providedSelection) {
707
+ return {
708
+ ...fieldNode,
709
+ selectionSet: {
710
+ kind: Kind.SELECTION_SET,
711
+ selections: [
712
+ ...providedSelection.selections,
713
+ ...fieldNode.selectionSet?.selections ?? []
714
+ ]
715
+ }
716
+ };
780
717
  }
781
- };
782
- } finally {
783
- pathStack.pop();
718
+ }
784
719
  }
785
720
  }
721
+ return fieldNode;
786
722
  }
787
723
  })
788
724
  );
@@ -797,7 +733,6 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
797
733
  const { targetSchema } = delegationContext;
798
734
  const { usedVariables, newDocument } = finalizeGatewayDocument(
799
735
  targetSchema,
800
- originalRequest.document,
801
736
  fragments,
802
737
  operations,
803
738
  onOverlappingAliases,
@@ -826,251 +761,6 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
826
761
  function isTypeNameField(selection) {
827
762
  return selection.kind === Kind.FIELD && !selection.alias && selection.name.value === "__typename";
828
763
  }
829
- function collectFieldSelectionSetsByPathImpl(document) {
830
- const selectionSetsByPath = /* @__PURE__ */ new Map();
831
- const fragments = /* @__PURE__ */ new Map();
832
- for (const def of document.definitions) {
833
- if (def.kind === Kind.FRAGMENT_DEFINITION) {
834
- fragments.set(def.name.value, def);
835
- }
836
- }
837
- const pathStack = [];
838
- const inlineFragmentCounterStack = [];
839
- visit(document, {
840
- [Kind.SELECTION_SET]: {
841
- enter() {
842
- inlineFragmentCounterStack.push(/* @__PURE__ */ new Map());
843
- },
844
- leave() {
845
- inlineFragmentCounterStack.pop();
846
- }
847
- },
848
- [Kind.OPERATION_DEFINITION]: {
849
- enter(node) {
850
- pathStack.push(`op:${node.operation}:${node.name?.value ?? ""}`);
851
- },
852
- leave() {
853
- pathStack.pop();
854
- }
855
- },
856
- [Kind.FRAGMENT_DEFINITION]: {
857
- enter(node) {
858
- pathStack.push(`frag:${node.name.value}`);
859
- },
860
- leave() {
861
- pathStack.pop();
862
- }
863
- },
864
- [Kind.INLINE_FRAGMENT]: {
865
- enter(node) {
866
- pathStack.push(
867
- nextInlineFragmentPathSegment(
868
- node,
869
- inlineFragmentCounterStack[inlineFragmentCounterStack.length - 1]
870
- )
871
- );
872
- },
873
- leave() {
874
- pathStack.pop();
875
- }
876
- },
877
- [Kind.FIELD]: {
878
- enter(node) {
879
- pathStack.push(node.alias?.value ?? node.name.value);
880
- if (node.selectionSet) {
881
- selectionSetsByPath.set(pathStack.join(">"), node.selectionSet);
882
- if (node.alias) {
883
- const namePath = [...pathStack.slice(0, -1), node.name.value].join(
884
- ">"
885
- );
886
- if (!selectionSetsByPath.has(namePath)) {
887
- selectionSetsByPath.set(namePath, node.selectionSet);
888
- }
889
- }
890
- }
891
- },
892
- leave() {
893
- pathStack.pop();
894
- }
895
- }
896
- });
897
- return { selectionSetsByPath, fragments };
898
- }
899
- const collectFieldSelectionSetsByPath = memoize1(
900
- collectFieldSelectionSetsByPathImpl
901
- );
902
- function nextInlineFragmentPathSegment(node, counter) {
903
- const typeName = node.typeCondition?.name.value ?? "";
904
- if (!counter) {
905
- return `inline:${typeName}:0`;
906
- }
907
- const idx = counter.get(typeName) ?? 0;
908
- counter.set(typeName, idx + 1);
909
- return `inline:${typeName}:${idx}`;
910
- }
911
- function subschemaHasProvidedSelectionsImpl(stitchingInfo, subschema) {
912
- const mergedTypes = stitchingInfo.mergedTypes;
913
- if (!mergedTypes) {
914
- return false;
915
- }
916
- for (const typeConfig of Object.values(mergedTypes)) {
917
- if (typeConfig?.providedSelectionsByField?.has(subschema)) {
918
- return true;
919
- }
920
- }
921
- return false;
922
- }
923
- const subschemaHasProvidedSelections = memoize2(
924
- subschemaHasProvidedSelectionsImpl
925
- );
926
- function lookupOriginalSelectionSet(pathStack, fieldNode, selectionSetsByPath) {
927
- const exact = selectionSetsByPath.get(pathStack.join(">"));
928
- if (exact || !fieldNode.alias) {
929
- return exact;
930
- }
931
- const fallback = [...pathStack.slice(0, -1), fieldNode.name.value].join(">");
932
- return selectionSetsByPath.get(fallback);
933
- }
934
- function intersectProvidedSelections(providedSelections, originalSelectionSet, fragments) {
935
- if (!originalSelectionSet) {
936
- return [];
937
- }
938
- const providedFieldsByName = /* @__PURE__ */ new Map();
939
- const otherProvided = [];
940
- for (const provided of providedSelections) {
941
- if (provided.kind === Kind.FIELD) {
942
- providedFieldsByName.set(provided.name.value, provided);
943
- } else {
944
- otherProvided.push(provided);
945
- }
946
- }
947
- const result = [];
948
- collectMatchingOriginalSelections(
949
- originalSelectionSet,
950
- providedFieldsByName,
951
- fragments,
952
- /* @__PURE__ */ new Set(),
953
- /* @__PURE__ */ new Set(),
954
- result
955
- );
956
- return [...result, ...otherProvided];
957
- }
958
- function collectMatchingOriginalSelections(selectionSet, providedFieldsByName, fragments, seenFieldNames, seenFragmentNames, result) {
959
- for (const sel of selectionSet.selections) {
960
- if (sel.kind === Kind.FIELD) {
961
- const provided = providedFieldsByName.get(sel.name.value);
962
- if (!provided) {
963
- continue;
964
- }
965
- const dedupKey = sel.alias?.value ?? sel.name.value;
966
- if (seenFieldNames.has(dedupKey)) {
967
- continue;
968
- }
969
- seenFieldNames.add(dedupKey);
970
- if (provided.selectionSet && sel.selectionSet) {
971
- const nested = intersectProvidedSelections(
972
- provided.selectionSet.selections,
973
- sel.selectionSet,
974
- fragments
975
- );
976
- if (!nested.length) {
977
- continue;
978
- }
979
- result.push({
980
- ...sel,
981
- selectionSet: {
982
- kind: Kind.SELECTION_SET,
983
- selections: nested
984
- }
985
- });
986
- } else {
987
- result.push(sel);
988
- }
989
- } else if (sel.kind === Kind.INLINE_FRAGMENT && sel.selectionSet) {
990
- const hasDirectives = (sel.directives?.length ?? 0) > 0;
991
- const hasTypeCondition = sel.typeCondition != null;
992
- if (hasDirectives || hasTypeCondition) {
993
- const inner = [];
994
- collectMatchingOriginalSelections(
995
- sel.selectionSet,
996
- providedFieldsByName,
997
- fragments,
998
- /* @__PURE__ */ new Set(),
999
- /* @__PURE__ */ new Set(),
1000
- inner
1001
- );
1002
- if (inner.length === 0) {
1003
- continue;
1004
- }
1005
- result.push({
1006
- kind: Kind.INLINE_FRAGMENT,
1007
- typeCondition: sel.typeCondition,
1008
- directives: sel.directives,
1009
- selectionSet: {
1010
- kind: Kind.SELECTION_SET,
1011
- selections: inner
1012
- }
1013
- });
1014
- } else {
1015
- collectMatchingOriginalSelections(
1016
- sel.selectionSet,
1017
- providedFieldsByName,
1018
- fragments,
1019
- seenFieldNames,
1020
- seenFragmentNames,
1021
- result
1022
- );
1023
- }
1024
- } else if (sel.kind === Kind.FRAGMENT_SPREAD) {
1025
- const fragmentDef = fragments.get(sel.name.value);
1026
- if (!fragmentDef) {
1027
- continue;
1028
- }
1029
- const hasSpreadDirectives = (sel.directives?.length ?? 0) > 0;
1030
- const hasFragmentDirectives = (fragmentDef.directives?.length ?? 0) > 0;
1031
- if (hasSpreadDirectives || hasFragmentDirectives) {
1032
- const inner = [];
1033
- collectMatchingOriginalSelections(
1034
- fragmentDef.selectionSet,
1035
- providedFieldsByName,
1036
- fragments,
1037
- /* @__PURE__ */ new Set(),
1038
- /* @__PURE__ */ new Set(),
1039
- inner
1040
- );
1041
- if (inner.length === 0) {
1042
- continue;
1043
- }
1044
- const combinedDirectives = [
1045
- ...sel.directives ?? [],
1046
- ...fragmentDef.directives ?? []
1047
- ];
1048
- result.push({
1049
- kind: Kind.INLINE_FRAGMENT,
1050
- typeCondition: fragmentDef.typeCondition,
1051
- directives: combinedDirectives.length > 0 ? combinedDirectives : void 0,
1052
- selectionSet: {
1053
- kind: Kind.SELECTION_SET,
1054
- selections: inner
1055
- }
1056
- });
1057
- } else {
1058
- if (seenFragmentNames.has(sel.name.value)) {
1059
- continue;
1060
- }
1061
- seenFragmentNames.add(sel.name.value);
1062
- collectMatchingOriginalSelections(
1063
- fragmentDef.selectionSet,
1064
- providedFieldsByName,
1065
- fragments,
1066
- seenFieldNames,
1067
- seenFragmentNames,
1068
- result
1069
- );
1070
- }
1071
- }
1072
- }
1073
- }
1074
764
  function filterTypenameFields(selections) {
1075
765
  let hasTypeNameField = false;
1076
766
  const filteredSelections = selections.filter((selection) => {
@@ -3205,6 +2895,7 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
3205
2895
  });
3206
2896
  }
3207
2897
  } else if (isObjectType(subFieldType) || isInterfaceType(subFieldType)) {
2898
+ const unavailableSubSelections = [];
3208
2899
  const subFieldTypeFields = subFieldType.getFields();
3209
2900
  for (const subSelection of selection.selectionSet.selections) {
3210
2901
  if (subSelection.kind === Kind.FIELD) {
@@ -3215,11 +2906,20 @@ function extractUnavailableFieldsFromSelectionSet(schema, fieldType, fieldSelect
3215
2906
  const subSelectionField = subFieldTypeFields[subSelection.name.value];
3216
2907
  if (!subSelectionField) {
3217
2908
  if (shouldAdd(subFieldType, subSelection)) {
3218
- unavailableSelections.push(subSelection);
2909
+ unavailableSubSelections.push(subSelection);
3219
2910
  }
3220
2911
  }
3221
2912
  }
3222
2913
  }
2914
+ if (unavailableSubSelections.length) {
2915
+ unavailableSelections.push({
2916
+ ...selection,
2917
+ selectionSet: {
2918
+ kind: Kind.SELECTION_SET,
2919
+ selections: unavailableSubSelections
2920
+ }
2921
+ });
2922
+ }
3223
2923
  }
3224
2924
  } else if (selection.kind === Kind.FRAGMENT_SPREAD) {
3225
2925
  const fragment = fragments[selection.name.value];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-tools/delegate",
3
- "version": "12.0.17-alpha-cc6e877dd43c47e20e00638efc161a0bed4e2ccb",
3
+ "version": "12.0.17",
4
4
  "type": "module",
5
5
  "description": "A set of utils for faster development of GraphQL tools",
6
6
  "repository": {