@graphql-tools/federation 4.4.7 → 4.4.8-alpha-14a4e85c81dd57d1bf167c8400c225508072e334

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,46 @@
1
1
  # @graphql-tools/federation
2
2
 
3
+ ## 4.4.8-alpha-14a4e85c81dd57d1bf167c8400c225508072e334
4
+ ### Patch Changes
5
+
6
+
7
+
8
+ - [#2351](https://github.com/graphql-hive/gateway/pull/2351) [`2554b20`](https://github.com/graphql-hive/gateway/commit/2554b20cc90495cd9c6c4187d057e110d3b482b7) 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.
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: "nested { nestedNested { name description } }")`) 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
+ - Fragment spreads in the client query are correctly handled when selecting nested `@provides`-covered fields. Previously, using a fragment spread (e.g. `...MyFrag`) for nested `@external` fields could cause an unnecessary delegation to the owner because selection subtraction compared only the spread name with the explicit `@provides` fields. The planner now resolves fragment spreads before subtracting provided selections, while preserving the fragment type condition and directives when only part of a fragment remains.
37
+
38
+ Aliases, direct field selections, fragments, fragment spreads, `@include`/`@skip` directives wrapping a `@provides` field, and nested `@provides` selections are preserved without unnecessary owner delegations.
39
+ - Updated dependencies [[`2554b20`](https://github.com/graphql-hive/gateway/commit/2554b20cc90495cd9c6c4187d057e110d3b482b7)]:
40
+ - @graphql-tools/delegate@12.0.19-alpha-14a4e85c81dd57d1bf167c8400c225508072e334
41
+ - @graphql-tools/stitch@10.1.24-alpha-14a4e85c81dd57d1bf167c8400c225508072e334
42
+ - @graphql-tools/wrap@11.1.18-alpha-14a4e85c81dd57d1bf167c8400c225508072e334
43
+
3
44
  ## 4.4.7
4
45
  ### Patch Changes
5
46
 
package/dist/index.cjs CHANGED
@@ -713,7 +713,28 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
713
713
  (argumentNode) => argumentNode.name.value === "provides"
714
714
  );
715
715
  if (providedExtraField?.value?.kind === graphql.Kind.STRING) {
716
- let handleSelection2 = function(fieldNodeTypeName2, selection) {
716
+ let registerProvidedSelectionForField2 = function(parentTypeName, fieldName, selectionSet) {
717
+ let fieldMap = typeNameFieldProvidedSelectionMap.get(parentTypeName);
718
+ if (!fieldMap) {
719
+ fieldMap = /* @__PURE__ */ new Map();
720
+ typeNameFieldProvidedSelectionMap.set(
721
+ parentTypeName,
722
+ fieldMap
723
+ );
724
+ }
725
+ const existing = fieldMap.get(fieldName);
726
+ if (existing) {
727
+ fieldMap.set(fieldName, {
728
+ kind: graphql.Kind.SELECTION_SET,
729
+ selections: [
730
+ ...existing.selections,
731
+ ...selectionSet.selections
732
+ ]
733
+ });
734
+ } else {
735
+ fieldMap.set(fieldName, selectionSet);
736
+ }
737
+ }, handleSelection2 = function(fieldNodeTypeName2, selection) {
717
738
  switch (selection.kind) {
718
739
  case graphql.Kind.FIELD:
719
740
  {
@@ -742,6 +763,11 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
742
763
  }
743
764
  providedFields.add(selection.name.value);
744
765
  if (selection.selectionSet) {
766
+ registerProvidedSelectionForField2(
767
+ fieldNodeTypeName2,
768
+ selection.name.value,
769
+ selection.selectionSet
770
+ );
745
771
  const extraFieldNodeNamedType = getNamedTypeNode(
746
772
  extraFieldNodeInType.type
747
773
  );
@@ -780,15 +806,8 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
780
806
  typeNameFieldProvidedSelectionMap
781
807
  );
782
808
  }
783
- let fieldProvidedSelectionMap = typeNameFieldProvidedSelectionMap.get(typeNode.name.value);
784
- if (!fieldProvidedSelectionMap) {
785
- fieldProvidedSelectionMap = /* @__PURE__ */ new Map();
786
- typeNameFieldProvidedSelectionMap.set(
787
- typeNode.name.value,
788
- fieldProvidedSelectionMap
789
- );
790
- }
791
- fieldProvidedSelectionMap.set(
809
+ registerProvidedSelectionForField2(
810
+ typeNode.name.value,
792
811
  fieldNode.name.value,
793
812
  providesSelectionSet
794
813
  );
package/dist/index.js CHANGED
@@ -711,7 +711,28 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
711
711
  (argumentNode) => argumentNode.name.value === "provides"
712
712
  );
713
713
  if (providedExtraField?.value?.kind === Kind.STRING) {
714
- let handleSelection2 = function(fieldNodeTypeName2, selection) {
714
+ let registerProvidedSelectionForField2 = function(parentTypeName, fieldName, selectionSet) {
715
+ let fieldMap = typeNameFieldProvidedSelectionMap.get(parentTypeName);
716
+ if (!fieldMap) {
717
+ fieldMap = /* @__PURE__ */ new Map();
718
+ typeNameFieldProvidedSelectionMap.set(
719
+ parentTypeName,
720
+ fieldMap
721
+ );
722
+ }
723
+ const existing = fieldMap.get(fieldName);
724
+ if (existing) {
725
+ fieldMap.set(fieldName, {
726
+ kind: Kind.SELECTION_SET,
727
+ selections: [
728
+ ...existing.selections,
729
+ ...selectionSet.selections
730
+ ]
731
+ });
732
+ } else {
733
+ fieldMap.set(fieldName, selectionSet);
734
+ }
735
+ }, handleSelection2 = function(fieldNodeTypeName2, selection) {
715
736
  switch (selection.kind) {
716
737
  case Kind.FIELD:
717
738
  {
@@ -740,6 +761,11 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
740
761
  }
741
762
  providedFields.add(selection.name.value);
742
763
  if (selection.selectionSet) {
764
+ registerProvidedSelectionForField2(
765
+ fieldNodeTypeName2,
766
+ selection.name.value,
767
+ selection.selectionSet
768
+ );
743
769
  const extraFieldNodeNamedType = getNamedTypeNode(
744
770
  extraFieldNodeInType.type
745
771
  );
@@ -778,15 +804,8 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
778
804
  typeNameFieldProvidedSelectionMap
779
805
  );
780
806
  }
781
- let fieldProvidedSelectionMap = typeNameFieldProvidedSelectionMap.get(typeNode.name.value);
782
- if (!fieldProvidedSelectionMap) {
783
- fieldProvidedSelectionMap = /* @__PURE__ */ new Map();
784
- typeNameFieldProvidedSelectionMap.set(
785
- typeNode.name.value,
786
- fieldProvidedSelectionMap
787
- );
788
- }
789
- fieldProvidedSelectionMap.set(
807
+ registerProvidedSelectionForField2(
808
+ typeNode.name.value,
790
809
  fieldNode.name.value,
791
810
  providesSelectionSet
792
811
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-tools/federation",
3
- "version": "4.4.7",
3
+ "version": "4.4.8-alpha-14a4e85c81dd57d1bf167c8400c225508072e334",
4
4
  "type": "module",
5
5
  "description": "Useful tools to create and manipulate GraphQL schemas.",
6
6
  "repository": {
@@ -38,14 +38,14 @@
38
38
  "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@graphql-tools/delegate": "^12.0.18",
41
+ "@graphql-tools/delegate": "12.0.19-alpha-14a4e85c81dd57d1bf167c8400c225508072e334",
42
42
  "@graphql-tools/executor": "^1.4.13",
43
43
  "@graphql-tools/executor-http": "^3.3.0",
44
44
  "@graphql-tools/merge": "^9.1.5",
45
45
  "@graphql-tools/schema": "^10.0.29",
46
- "@graphql-tools/stitch": "^10.1.23",
46
+ "@graphql-tools/stitch": "10.1.24-alpha-14a4e85c81dd57d1bf167c8400c225508072e334",
47
47
  "@graphql-tools/utils": "^11.0.0",
48
- "@graphql-tools/wrap": "^11.1.17",
48
+ "@graphql-tools/wrap": "11.1.18-alpha-14a4e85c81dd57d1bf167c8400c225508072e334",
49
49
  "@graphql-yoga/typed-event-target": "^3.0.2",
50
50
  "@whatwg-node/disposablestack": "^0.0.6",
51
51
  "@whatwg-node/events": "^0.1.2",