@graphql-tools/delegate 12.0.16 → 12.0.17-alpha-e7a313b236c7e6405179af89eea446448ee97587

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,5 +1,31 @@
1
1
  # @graphql-tools/delegate
2
2
 
3
+ ## 12.0.17-alpha-e7a313b236c7e6405179af89eea446448ee97587
4
+ ### Patch Changes
5
+
6
+
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.
9
+
10
+ Previously, when a subgraph declared `@provides(fields: "...")` on a query field, the gateway would forward **every** field listed in the `@provides` argument to that subgraph, even when the client never asked for those fields. For example with:
11
+
12
+ ```graphql
13
+ # subgraph B
14
+ type Query {
15
+ entity: Entity @provides(fields: "name description")
16
+ }
17
+
18
+ type Entity @key(fields: "id") {
19
+ id: ID!
20
+ name: String! @external
21
+ description: String! @external
22
+ }
23
+ ```
24
+
25
+ a client query of `{ entity { id name } }` would still cause the gateway to ask subgraph B for `description`. After this fix the gateway only forwards `name` (because that is what the client asked for and what `@provides` allows the subgraph to resolve directly), bringing the behavior in line with Federation's `@provides` semantics where it acts as a hint that the providing subgraph **can** resolve those fields locally — not a directive to always fetch them.
26
+
27
+ Aliases, fragments, fragment spreads, and nested `@provides` selections are preserved.
28
+
3
29
  ## 12.0.16
4
30
  ### Patch Changes
5
31
 
package/dist/index.cjs CHANGED
@@ -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 = [];
@@ -687,38 +687,102 @@ function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlapp
687
687
  definitions: [...newOperations, ...newFragments]
688
688
  };
689
689
  const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
690
- if (stitchingInfo != null) {
690
+ if (stitchingInfo != null && subschemaHasProvidedSelections(
691
+ stitchingInfo,
692
+ delegationContext.subschema
693
+ )) {
694
+ const {
695
+ selectionSetsByPath: originalFieldSelectionSetsByPath,
696
+ fragments: originalFragmentsByName
697
+ } = collectFieldSelectionSetsByPath(originalDocument);
691
698
  const typeInfo = getTypeInfo(targetSchema);
699
+ const pathStack = [];
700
+ const inlineFragmentCounterStack = [];
692
701
  newDocument = graphql.visit(
693
702
  newDocument,
694
703
  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) {
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];
701
752
  const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
702
753
  delegationContext.subschema
703
754
  );
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
- }
755
+ const providedSelection = providedSelectionsByField?.[fieldNode.name.value];
756
+ if (!providedSelection) {
757
+ return void 0;
718
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
+ ]
780
+ }
781
+ };
782
+ } finally {
783
+ pathStack.pop();
719
784
  }
720
785
  }
721
- return fieldNode;
722
786
  }
723
787
  })
724
788
  );
@@ -733,6 +797,7 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
733
797
  const { targetSchema } = delegationContext;
734
798
  const { usedVariables, newDocument } = finalizeGatewayDocument(
735
799
  targetSchema,
800
+ originalRequest.document,
736
801
  fragments,
737
802
  operations,
738
803
  onOverlappingAliases,
@@ -761,6 +826,251 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
761
826
  function isTypeNameField(selection) {
762
827
  return selection.kind === graphql.Kind.FIELD && !selection.alias && selection.name.value === "__typename";
763
828
  }
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
+ }
764
1074
  function filterTypenameFields(selections) {
765
1075
  let hasTypeNameField = false;
766
1076
  const filteredSelections = selections.filter((selection) => {
package/dist/index.js CHANGED
@@ -590,7 +590,7 @@ const handleOverrideByDelegation = 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 = [];
@@ -687,38 +687,102 @@ function finalizeGatewayDocument(targetSchema, fragments, operations, onOverlapp
687
687
  definitions: [...newOperations, ...newFragments]
688
688
  };
689
689
  const stitchingInfo = delegationContext.info?.schema?.extensions?.["stitchingInfo"];
690
- if (stitchingInfo != null) {
690
+ if (stitchingInfo != null && subschemaHasProvidedSelections(
691
+ stitchingInfo,
692
+ delegationContext.subschema
693
+ )) {
694
+ const {
695
+ selectionSetsByPath: originalFieldSelectionSetsByPath,
696
+ fragments: originalFragmentsByName
697
+ } = collectFieldSelectionSetsByPath(originalDocument);
691
698
  const typeInfo = getTypeInfo(targetSchema);
699
+ const pathStack = [];
700
+ const inlineFragmentCounterStack = [];
692
701
  newDocument = visit(
693
702
  newDocument,
694
703
  visitWithTypeInfo(typeInfo, {
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) {
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];
701
752
  const providedSelectionsByField = typeConfig?.providedSelectionsByField?.get(
702
753
  delegationContext.subschema
703
754
  );
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
- };
717
- }
755
+ const providedSelection = providedSelectionsByField?.[fieldNode.name.value];
756
+ if (!providedSelection) {
757
+ return void 0;
718
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
+ ]
780
+ }
781
+ };
782
+ } finally {
783
+ pathStack.pop();
719
784
  }
720
785
  }
721
- return fieldNode;
722
786
  }
723
787
  })
724
788
  );
@@ -733,6 +797,7 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
733
797
  const { targetSchema } = delegationContext;
734
798
  const { usedVariables, newDocument } = finalizeGatewayDocument(
735
799
  targetSchema,
800
+ originalRequest.document,
736
801
  fragments,
737
802
  operations,
738
803
  onOverlappingAliases,
@@ -761,6 +826,251 @@ function finalizeGatewayRequest(originalRequest, delegationContext, onOverlappin
761
826
  function isTypeNameField(selection) {
762
827
  return selection.kind === Kind.FIELD && !selection.alias && selection.name.value === "__typename";
763
828
  }
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
+ }
764
1074
  function filterTypenameFields(selections) {
765
1075
  let hasTypeNameField = false;
766
1076
  const filteredSelections = selections.filter((selection) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-tools/delegate",
3
- "version": "12.0.16",
3
+ "version": "12.0.17-alpha-e7a313b236c7e6405179af89eea446448ee97587",
4
4
  "type": "module",
5
5
  "description": "A set of utils for faster development of GraphQL tools",
6
6
  "repository": {