diff --git a/.changeset/dull-gorillas-drive.md b/.changeset/dull-gorillas-drive.md
deleted file mode 100644
index e5b84d9ed..000000000
--- a/.changeset/dull-gorillas-drive.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-"@apollo/gateway": patch
----
-
-Remove extraneous call to `span.setStatus()` on a span which has already ended.
-
-In cases where a subgraph responded with an error, we would sometimes try to set
-the status of a span which had already ended. This resulted in a warning log to
-the console (but no effect otherwise). This warning should no longer happen.
-  
\ No newline at end of file
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 72cdab40e..16f8769d9 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -19,16 +19,18 @@ jobs:
           node-version: << parameters.node-version >>
       # node v14 defaults to npm 6, which is too old for our package-lock.json
       # should be able to remove this step when we drop node v14
-      - run: npm install -g npm@latest
+      - run: npm install -g npm@9
       - node/install-packages
       - run:
           name: Run tests
           command: npm run test:ci
+      - run:
+          name: Upload coverage
+          command: npm run coverage:upload
       - store_test_results:
           path: junit.xml
 
 workflows:
-  version: 2
   Build:
     jobs:
       - Test:
@@ -53,3 +55,6 @@ workflows:
       - node/run:
           name: Check Spelling
           npm-run: spell:check
+      - node/run:
+          name: Check Prettier (tests)
+          npm-run: prettier:check
diff --git a/.cspell/cspell-dict.txt b/.cspell/cspell-dict.txt
index 517d06da6..396cfd18d 100644
--- a/.cspell/cspell-dict.txt
+++ b/.cspell/cspell-dict.txt
@@ -35,6 +35,7 @@ caes
 cand
 Childs
 childs
+codecov
 compabitle
 compatbility
 Compatiblity
@@ -43,6 +44,7 @@ comple
 condiditions
 correcly
 cyclesf
+Cypher
 dedented
 Dedupe
 defered
@@ -241,6 +243,7 @@ unaliased
 uncacheable
 uncesssary
 uncomposable
+unconfigured
 undici
 unecesasry
 unecessarally
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index f02884cee..4d841d837 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -1,2 +1,4 @@
 # Run prettier
 f2f1d6041cd67c67fdacabe0570fe2ce642c052c
+# Enforce prettier on all test files https://github.com/apollographql/federation/pull/2785
+dca3d464c8a699deaa1109ae69f376750b48c249
\ No newline at end of file
diff --git a/.git-hooks/pre-commit b/.git-hooks/pre-commit
new file mode 100755
index 000000000..8912dd546
--- /dev/null
+++ b/.git-hooks/pre-commit
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Grab prettier output, filter out garbage output lines (lines which aren't a
+# file path), and remove the " 0ms" from the end of each line (everything after
+# whitespace)
+FILES=$(npm run prettier:fix | grep 'src' | sed 's/ .*//')
+
+# Add the files to the commit
+echo "$FILES" | xargs git add
\ No newline at end of file
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 000000000..fa6fee59b
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,8 @@
+coverage:
+  status:
+    project:
+      default:
+        threshold: 1%
+    patch:
+      default:
+        target: 0%
\ No newline at end of file
diff --git a/composition-js/CHANGELOG.md b/composition-js/CHANGELOG.md
index ad425316d..a1f33397e 100644
--- a/composition-js/CHANGELOG.md
+++ b/composition-js/CHANGELOG.md
@@ -1,5 +1,41 @@
 # CHANGELOG for `@apollo/composition`
 
+## 2.5.5
+### Patch Changes
+
+- Updated dependencies []:
+  - @apollo/federation-internals@2.5.5
+  - @apollo/query-graphs@2.5.5
+
+## 2.5.4
+### Patch Changes
+
+- Updated dependencies []:
+  - @apollo/federation-internals@2.5.4
+  - @apollo/query-graphs@2.5.4
+
+## 2.5.3
+### Patch Changes
+
+
+- Modifies the type for the argument of the `@requiresScopes` from `[federation__Scope!]!` to `[[federation__Scope!]!]!`. ([#2738](https://github.com/apollographql/federation/pull/2738))
+  
+  The `@requiresScopes` directives has been pre-emptively introduced in 2.5.0 to support an upcoming Apollo Router
+  feature around scoped accesses. The argument for `@requiresScopes` in that upcoming feature is changed to accommodate a
+  new semantic. Note that this technically a breaking change to the `@requiresScopes` directive definition, but as the
+  full feature using that directive has been released yet, this directive cannot effectively be used and this should have
+  no concrete impact.
+- Updated dependencies [[`4b9a512b`](https://github.com/apollographql/federation/commit/4b9a512b62e02544d7854fa198942aac33b93feb), [`c6e0e76d`](https://github.com/apollographql/federation/commit/c6e0e76dbc62662c2aa6ff7f657e374047b11255), [`1add932c`](https://github.com/apollographql/federation/commit/1add932c5cd1297853fb5af9a3a6aaa71243f63a), [`6f1fddb2`](https://github.com/apollographql/federation/commit/6f1fddb25d49262b2ebf6db953371a559dd62e9c)]:
+  - @apollo/federation-internals@2.5.3
+  - @apollo/query-graphs@2.5.3
+
+## 2.5.2
+### Patch Changes
+
+- Updated dependencies [[`35179f08`](https://github.com/apollographql/federation/commit/35179f086ce973e9ae7bb455f7ea7d73cdc10f69)]:
+  - @apollo/federation-internals@2.5.2
+  - @apollo/query-graphs@2.5.2
+
 ## 2.5.1
 ### Patch Changes
 
diff --git a/composition-js/package.json b/composition-js/package.json
index 8d6086f46..61f7570ec 100644
--- a/composition-js/package.json
+++ b/composition-js/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@apollo/composition",
-  "version": "2.5.1",
+  "version": "2.5.5",
   "description": "Apollo Federation composition utilities",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",
@@ -27,8 +27,8 @@
     "access": "public"
   },
   "dependencies": {
-    "@apollo/federation-internals": "2.5.1",
-    "@apollo/query-graphs": "2.5.1"
+    "@apollo/federation-internals": "2.5.5",
+    "@apollo/query-graphs": "2.5.5"
   },
   "peerDependencies": {
     "graphql": "^16.5.0"
diff --git a/composition-js/src/__tests__/compose.test.ts b/composition-js/src/__tests__/compose.test.ts
index a92320874..71c09d67e 100644
--- a/composition-js/src/__tests__/compose.test.ts
+++ b/composition-js/src/__tests__/compose.test.ts
@@ -4493,7 +4493,7 @@ describe('composition', () => {
         "https://specs.apollo.dev/requiresScopes/v0.1"
       );
       expect(printDirectiveDefinition(result.schema.directive('requiresScopes')!)).toMatchString(`
-        directive @requiresScopes(scopes: [requiresScopes__Scope!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+        directive @requiresScopes(scopes: [[requiresScopes__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
       `);
     });
 
@@ -4527,7 +4527,7 @@ describe('composition', () => {
         const invalidDefinition = {
           typeDefs: gql`
             scalar federation__Scope
-            directive @requiresScopes(scopes: [federation__Scope!]!) on ENUM_VALUE
+            directive @requiresScopes(scopes: [[federation__Scope!]!]!) on ENUM_VALUE
 
             type Query {
               a: Int
@@ -4565,7 +4565,7 @@ describe('composition', () => {
         const result = composeAsFed2Subgraphs([invalidDefinition]);
         expect(errors(result)[0]).toEqual([
           "DIRECTIVE_DEFINITION_INVALID",
-          "[invalidDefinition] Invalid definition for directive \"@requiresScopes\": argument \"scopes\" should have type \"[federation__Scope!]!\" but found type \"[federation__Scope]!\"",
+          "[invalidDefinition] Invalid definition for directive \"@requiresScopes\": argument \"scopes\" should have type \"[[federation__Scope!]!]!\" but found type \"[federation__Scope]!\"",
         ]);
       });
 
diff --git a/docs/source/_redirects b/docs/source/_redirects
index cd5a4504c..6512e2e69 100644
--- a/docs/source/_redirects
+++ b/docs/source/_redirects
@@ -21,4 +21,4 @@
 /quickstart-pt-4/ /docs/federation/v2/quickstart/local-subgraphs/
 
 /managed-federation/monitoring/ /docs/federation/performance/monitoring
-/api/apollo-federation/ /docs/federation/api/apollo-subgraph/
+/api/apollo-federation/ /docs/federation/api/apollo-subgraph/
\ No newline at end of file
diff --git a/docs/source/building-supergraphs/supported-subgraphs.md b/docs/source/building-supergraphs/supported-subgraphs.md
index 4c3abc957..8a85c0375 100644
--- a/docs/source/building-supergraphs/supported-subgraphs.md
+++ b/docs/source/building-supergraphs/supported-subgraphs.md
@@ -15,6 +15,22 @@ The following open-source GraphQL server libraries and hosted solutions support
 | ❌    | Critical functionality is NOT supported              |
 | 🔲    | Additional federation functionality is NOT supported |
 
+## Ballerina
+
+<table>
+<thead>
+<tr><th width="300">Library</th><th>Federation 1 Support</th><th>Federation 2 Support</th></tr>
+</thead>
+<tbody>
+<tr><th colspan="3"><big><a href="https://ballerina.io/spec/graphql">Ballerina GraphQL Module</a></big></th></tr>
+<tr><td>A spec-compliant, production-ready, Standard Library module for building and interacting with GraphQL APIs using Ballerina.</br></br>Github: <a href="https://github.com/ballerina-platform/module-ballerina-graphql">ballerina-platform/module-ballerina-graphql</a></br>
+Type: Code first</br>
+Stars: 83 ⭐</br>
+Last Release: 2023-06-30</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🔲</td></tr><tr><th><code>@provides</code></th><td>🔲</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+</tbody>
+</table>
+
+
 ## C# / .NET
 
 <table>
@@ -25,13 +41,13 @@ The following open-source GraphQL server libraries and hosted solutions support
 <tr><th colspan="3"><big><a href="https://graphql-dotnet.github.io">GraphQL for .NET</a></big></th></tr>
 <tr><td>GraphQL for .NET</br></br>Github: <a href="https://github.com/graphql-dotnet/graphql-dotnet">graphql-dotnet/graphql-dotnet</a></br>
 Type: Code first | SDL first</br>
-Stars: 5.5k ⭐</br>
-Last Release: 2023-02-27</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🔲</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Stars: 5.6k ⭐</br>
+Last Release: 2023-08-11</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🔲</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://chillicream.com/docs/hotchocolate">Hot Chocolate</a></big></th></tr>
 <tr><td>Open-source GraphQL server for the Microsoft .NET platform that takes the complexity away and lets you focus on delivering the next big thing.</br></br>Github: <a href="https://github.com/ChilliCream/graphql-platform">ChilliCream/graphql-platform</a></br>
 Type: Code first | SDL first</br>
-Stars: 4.3k ⭐</br>
-Last Release: 2023-03-22</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🔲</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Stars: 4.6k ⭐</br>
+Last Release: 2023-08-25</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🔲</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 </tbody>
 </table>
 
@@ -46,7 +62,7 @@ Last Release: 2023-03-22</br></br></td><td><table><tr><th><code>_service</code><
 <tr><td>The GraphQL toolkit for Elixir</br></br>Github: <a href="https://github.com/absinthe-graphql/absinthe">absinthe-graphql/absinthe</a></br>
 Type: Code first</br>
 Stars: 4.1k ⭐</br>
-Last Release: 2021-09-28</br></br>Federation Library: <a href="https://github.com/DivvyPayHQ/absinthe_federation">Absinthe.Federation</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Last Release: 2021-09-28</br></br>Federation Library: <a href="https://github.com/DivvyPayHQ/absinthe_federation">DivvyPayHQ/absinthe_federation</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 </tbody>
 </table>
 
@@ -60,12 +76,12 @@ Last Release: 2021-09-28</br></br>Federation Library: <a href="https://github.co
 <tr><th colspan="3"><big><a href="https://gqlgen.com">gqlgen</a></big></th></tr>
 <tr><td>go generate based graphql server library</br></br>Github: <a href="https://github.com/99designs/gqlgen">99designs/gqlgen</a></br>
 Type: SDL first</br>
-Stars: 8.7k ⭐</br>
-Last Release: 2023-03-20</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🔲</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Stars: 9.1k ⭐</br>
+Last Release: 2023-07-27</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🔲</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://github.com/dariuszkuc/graphql#this-is-fork-of-graphql-gographql-that-adds-apollo-federation-support">GraphQL Go (fork)</a></big></th></tr>
 <tr><td>This is a fork of graphql-go/graphql that adds Federation support</br></br>Github: <a href="https://github.com/dariuszkuc/graphql">dariuszkuc/graphql</a></br>
 Type: Code first</br>
-Stars: 1 ⭐</br>
+Stars: 2 ⭐</br>
 Last Release: 2022-11-11</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 </tbody>
 </table>
@@ -80,23 +96,23 @@ Last Release: 2022-11-11</br></br></td><td><table><tr><th><code>_service</code><
 <tr><th colspan="3"><big><a href="https://netflix.github.io/dgs/federation/">dgs-framework</a></big></th></tr>
 <tr><td>GraphQL for Java with Spring Boot made easy.</br></br>Github: <a href="https://github.com/netflix/dgs-framework/">netflix/dgs-framework</a></br>
 Type: SDL first</br>
-Stars: 2.6k ⭐</br>
-Last Release: 2023-02-17</br></br>Core Library: <a href="https://github.com/graphql-java/graphql-java">GraphQL Java</a></br>Federation Library: <a href="https://github.com/apollographql/federation-jvm">Federation JVM</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Stars: 2.7k ⭐</br>
+Last Release: 2023-07-27</br></br>Core Library: <a href="https://github.com/graphql-java/graphql-java">GraphQL Java</a></br>Federation Library: <a href="https://github.com/apollographql/federation-jvm">apollographql/federation-jvm</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://github.com/graphql-java-kickstart/graphql-spring-boot">GraphQL Java Kickstart (Spring Boot)</a></big></th></tr>
 <tr><td>GraphQL and GraphiQL Spring Framework Boot Starters - Forked from oembedler/graphql-spring-boot due to inactivity.</br></br>Github: <a href="https://github.com/graphql-java-kickstart/graphql-spring-boot">graphql-java-kickstart/graphql-spring-boot</a></br>
-Type: Schema first</br>
+Type: SDL first</br>
 Stars: 1.5k ⭐</br>
-Last Release: 2022-12-05</br></br>Core Library: <a href="https://github.com/graphql-java/graphql-java">GraphQL Java</a></br>Federation Library: <a href="https://github.com/apollographql/federation-jvm">Federation JVM</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Last Release: 2022-12-05</br></br>Core Library: <a href="https://github.com/graphql-java/graphql-java">GraphQL Java</a></br>Federation Library: <a href="https://github.com/apollographql/federation-jvm">apollographql/federation-jvm</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://github.com/ExpediaGroup/graphql-kotlin">GraphQL Kotlin</a></big></th></tr>
 <tr><td>Libraries for running GraphQL in Kotlin</br></br>Github: <a href="https://github.com/ExpediaGroup/graphql-kotlin">ExpediaGroup/graphql-kotlin</a></br>
 Type: Code first</br>
 Stars: 1.6k ⭐</br>
-Last Release: 2023-03-15</br></br>Core Library: <a href="https://github.com/graphql-java/graphql-java">GraphQL Java</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Last Release: 2023-06-19</br></br>Core Library: <a href="https://github.com/graphql-java/graphql-java">GraphQL Java</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://docs.spring.io/spring-graphql/docs/current/reference/html/">Spring GraphQL</a></big></th></tr>
 <tr><td>Spring Integration for GraphQL </br></br>Github: <a href="https://github.com/spring-projects/spring-graphql">spring-projects/spring-graphql</a></br>
-Type: Schema first</br>
-Stars: 1.3k ⭐</br>
-Last Release: 2023-03-21</br></br>Core Library: <a href="https://github.com/graphql-java/graphql-java">GraphQL Java</a></br>Federation Library: <a href="https://github.com/apollographql/federation-jvm">Federation JVM</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Type: SDL first</br>
+Stars: 1.4k ⭐</br>
+Last Release: 2023-07-18</br></br>Core Library: <a href="https://github.com/graphql-java/graphql-java">GraphQL Java</a></br>Federation Library: <a href="https://github.com/apollographql/federation-jvm">apollographql/federation-jvm</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 </tbody>
 </table>
 
@@ -110,8 +126,8 @@ Last Release: 2023-03-21</br></br>Core Library: <a href="https://github.com/grap
 <tr><th colspan="3"><big><a href="https://www.apollographql.com/docs/federation/">Apollo Server</a></big></th></tr>
 <tr><td>🌍  Spec-compliant and production ready JavaScript GraphQL server that lets you develop in a schema-first way. Built for Express, Connect, Hapi, Koa, and more.</br></br>Github: <a href="https://github.com/apollographql/apollo-server">apollographql/apollo-server</a></br>
 Type: SDL first</br>
-Stars: 13.3k ⭐</br>
-Last Release: 2023-03-15</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Stars: 13.5k ⭐</br>
+Last Release: 2023-08-24</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://github.com/graphql/express-graphql">express-graphql</a></big></th></tr>
 <tr><td>Create a GraphQL HTTP server with Express.</br></br>Github: <a href="https://github.com/graphql/express-graphql">graphql/express-graphql</a></br>
 Type: SDL first</br>
@@ -120,33 +136,33 @@ Last Release: 2020-11-19</br></br>Core Library: <a href="https://www.npmjs.com/p
 <tr><th colspan="3"><big><a href="https://www.the-guild.dev/graphql/yoga-server/v3/features/apollo-federation">GraphQL Yoga</a></big></th></tr>
 <tr><td>The fully-featured GraphQL server with focus on easy setup, performance and great developer experience.</br></br>Github: <a href="https://github.com/dotansimha/graphql-yoga/">dotansimha/graphql-yoga</a></br>
 Type: SDL first</br>
-Stars: 7.5k ⭐</br>
-Last Release: 2023-03-14</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Stars: 7.7k ⭐</br>
+Last Release: 2023-08-22</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://graphql-helix.vercel.app">GraphQL Helix</a></big></th></tr>
 <tr><td>A highly evolved and framework-agnostic GraphQL HTTP server.</br></br>Github: <a href="https://github.com/contra/graphql-helix">contra/graphql-helix</a></br>
 Type: SDL first</br>
-Stars: 810 ⭐</br>
+Stars: 822 ⭐</br>
 Last Release: 2022-07-09</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://mercurius.dev/#/">Mercurius</a></big></th></tr>
 <tr><td>Implement GraphQL servers and gateways with Fastify</br></br>Github: <a href="https://github.com/mercurius-js/mercurius">mercurius-js/mercurius</a></br>
 Type: SDL first</br>
-Stars: 2.1k ⭐</br>
-Last Release: 2023-02-22</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Stars: 2.2k ⭐</br>
+Last Release: 2023-07-12</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://nestjs.com">NestJS (code first)</a></big></th></tr>
 <tr><td>A progressive Node.js framework for building efficient, reliable and scalable server-side applications.</br></br>Github: <a href="https://github.com/nestjs/graphql">nestjs/graphql</a></br>
 Type: Code first</br>
 Stars: 1.3k ⭐</br>
-Last Release: 2023-03-20</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Last Release: 2023-06-16</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🔲</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://nestjs.com">NestJS (SDL First)</a></big></th></tr>
 <tr><td>A progressive Node.js framework for building efficient, reliable and scalable server-side applications.</br></br>Github: <a href="https://github.com/nestjs/graphql">nestjs/graphql</a></br>
 Type: SDL first</br>
 Stars: 1.3k ⭐</br>
-Last Release: 2023-03-20</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Last Release: 2023-06-16</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://pothos-graphql.dev/docs/plugins/federation">Pothos GraphQL</a></big></th></tr>
 <tr><td>Plugin based GraphQL schema builder that makes building graphql schemas with TypeScript easy, fast and enjoyable.</br></br>Github: <a href="https://github.com/hayes/pothos">hayes/pothos</a></br>
 Type: Code first</br>
-Stars: 1.8k ⭐</br>
-Last Release: 2023-03-24</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Stars: 2.0k ⭐</br>
+Last Release: 2023-08-11</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 </tbody>
 </table>
 
@@ -160,13 +176,13 @@ Last Release: 2023-03-24</br></br>Core Library: <a href="https://www.npmjs.com/p
 <tr><th colspan="3"><big><a href="https://lighthouse-php.com/">Lighthouse (Laravel)</a></big></th></tr>
 <tr><td>A framework for serving GraphQL from Laravel</br></br>Github: <a href="https://github.com/nuwave/lighthouse">nuwave/lighthouse</a></br>
 Type: SDL first</br>
-Stars: 3.1k ⭐</br>
-Last Release: 2023-03-24</br></br>Core Library: <a href="https://github.com/webonyx/graphql-php">graphql-php</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Stars: 3.2k ⭐</br>
+Last Release: 2023-08-04</br></br>Core Library: <a href="https://github.com/webonyx/graphql-php">webonyx/graphql-php</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://github.com/Skillshare/apollo-federation-php">GraphQL PHP</a></big></th></tr>
 <tr><td>PHP implementation of the GraphQL specification based on the reference implementation in JavaScript</br></br>Github: <a href="https://github.com/webonyx/graphql-php">webonyx/graphql-php</a></br>
 Type: Code first</br>
 Stars: 4.5k ⭐</br>
-Last Release: 2023-03-20</br></br>Federation Library: <a href="https://github.com/Skillshare/apollo-federation-php">Apollo Federation PHP</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Last Release: 2023-08-08</br></br>Federation Library: <a href="https://github.com/Skillshare/apollo-federation-php">Skillshare/apollo-federation-php</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>❌</td></tr><tr><th><code>@shareable</code></th><td>🔲</td></tr><tr><th><code>@tag</code></th><td>🔲</td></tr><tr><th><code>@override</code></th><td>🔲</td></tr><tr><th><code>@inaccessible</code></th><td>🔲</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 </tbody>
 </table>
 
@@ -180,18 +196,18 @@ Last Release: 2023-03-20</br></br>Federation Library: <a href="https://github.co
 <tr><th colspan="3"><big><a href="https://ariadnegraphql.org/docs/apollo-federation">Ariadne</a></big></th></tr>
 <tr><td>Python library for implementing GraphQL servers using schema-first approach.</br></br>Github: <a href="https://github.com/mirumee/ariadne">mirumee/ariadne</a></br>
 Type: SDL first</br>
-Stars: 2.0k ⭐</br>
-Last Release: 2023-02-22</br></br>Core Library: <a href="https://github.com/graphql-python/graphql-core">GraphQL-core 3</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Stars: 2.1k ⭐</br>
+Last Release: 2023-06-27</br></br>Core Library: <a href="https://github.com/graphql-python/graphql-core">GraphQL-core 3</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://graphene-python.org/">Graphene</a></big></th></tr>
 <tr><td>GraphQL framework for Python</br></br>Github: <a href="https://github.com/graphql-python/graphene">graphql-python/graphene</a></br>
 Type: Code first</br>
-Stars: 7.6k ⭐</br>
-Last Release: 2023-03-13</br></br>Core Library: <a href="https://github.com/graphql-python/graphql-core">GraphQL-core 3</a></br>Federation Library: <a href="https: //github.com/graphql-python/graphene-federation">graphene-federation</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Stars: 7.8k ⭐</br>
+Last Release: 2023-07-26</br></br>Core Library: <a href="https://github.com/graphql-python/graphql-core">GraphQL-core 3</a></br>Federation Library: <a href="https://github.com/graphql-python/graphene-federation">graphql-python/graphene-federation</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://strawberry.rocks">Strawberry</a></big></th></tr>
 <tr><td>A GraphQL library for Python that leverages type annotations 🍓</br></br>Github: <a href="https://github.com/strawberry-graphql/strawberry">strawberry-graphql/strawberry</a></br>
 Type: Code first</br>
-Stars: 3.0k ⭐</br>
-Last Release: 2023-03-25</br></br>Core Library: <a href="https://github.com/graphql-python/graphql-core">GraphQL-core 3</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Stars: 3.4k ⭐</br>
+Last Release: 2023-08-24</br></br>Core Library: <a href="https://github.com/graphql-python/graphql-core">GraphQL-core 3</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 </tbody>
 </table>
 
@@ -205,7 +221,7 @@ Last Release: 2023-03-25</br></br>Core Library: <a href="https://github.com/grap
 <tr><th colspan="3"><big><a href="https://graphql-ruby.org/">GraphQL Ruby</a></big></th></tr>
 <tr><td>Ruby implementation of GraphQL </br></br>Github: <a href="https://github.com/rmosolgo/graphql-ruby">rmosolgo/graphql-ruby</a></br>
 Type: Code first</br>
-Stars: 5.2k ⭐</br>
+Stars: 5.3k ⭐</br>
 Last Release: 2021-02-12</br></br>Federation Library: <a href="https://github.com/Gusto/apollo-federation-ruby/">Gusto/apollo-federation-ruby</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 </tbody>
 </table>
@@ -220,7 +236,7 @@ Last Release: 2021-02-12</br></br>Federation Library: <a href="https://github.co
 <tr><th colspan="3"><big><a href="https://async-graphql.github.io/async-graphql/en/apollo_federation.html">async-graphql</a></big></th></tr>
 <tr><td>A GraphQL server library implemented in Rust</br></br>Github: <a href="https://github.com/async-graphql/async-graphql">async-graphql/async-graphql</a></br>
 Type: Code first</br>
-Stars: 2.8k ⭐</br>
+Stars: 3.0k ⭐</br>
 Last Release: 2022-11-28</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 </tbody>
 </table>
@@ -235,13 +251,13 @@ Last Release: 2022-11-28</br></br></td><td><table><tr><th><code>_service</code><
 <tr><th colspan="3"><big><a href="https://ghostdogpr.github.io/caliban/docs/federation.html">Caliban</a></big></th></tr>
 <tr><td>Functional GraphQL library for Scala</br></br>Github: <a href="https://github.com/ghostdogpr/caliban">ghostdogpr/caliban</a></br>
 Type: Code first</br>
-Stars: 861 ⭐</br>
-Last Release: 2022-12-19</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Stars: 880 ⭐</br>
+Last Release: 2023-08-06</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🔲</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://sangria-graphql.github.io/learn/#graphql-federation">Sangria</a></big></th></tr>
 <tr><td>Scala GraphQL implementation</br></br>Github: <a href="https://github.com/sangria-graphql/sangria">sangria-graphql/sangria</a></br>
 Type: Code first</br>
-Stars: 1.9k ⭐</br>
-Last Release: 2023-03-09</br></br>Federation Library: <a href="https://github.com/sangria-graphql/sangria-federated">Sangria Federated</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Stars: 2.0k ⭐</br>
+Last Release: 2023-06-20</br></br>Federation Library: <a href="https://github.com/sangria-graphql/sangria-federated">sangria-graphql/sangria-federated</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 </tbody>
 </table>
 
@@ -255,8 +271,8 @@ Last Release: 2023-03-09</br></br>Federation Library: <a href="https://github.co
 <tr><th colspan="3"><big><a href="https://github.com/GraphQLSwift/Graphiti">Graphiti</a></big></th></tr>
 <tr><td>The Swift GraphQL Schema framework for macOS and Linux</br></br>Github: <a href="https://github.com/GraphQLSwift/Graphiti">GraphQLSwift/Graphiti</a></br>
 Type: SDL first</br>
-Stars: 493 ⭐</br>
-Last Release: 2023-03-19</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
+Stars: 507 ⭐</br>
+Last Release: 2023-07-31</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 </tbody>
 </table>
 
@@ -274,9 +290,14 @@ Last Release: 2023-03-19</br></br></td><td><table><tr><th><code>_service</code><
 <tr><th colspan="3"><big><a href="https://www.the-guild.dev/graphql/mesh">GraphQL Mesh</a></big></th></tr>
 <tr><td>Executable GraphQL schema from multiple data sources, query anything, run anywhere.</br></br>Github: <a href="https://github.com/Urigo/graphql-mesh">Urigo/graphql-mesh</a></br>
 Type: undefined</br>
-Stars: 2.8k ⭐</br>
-Last Release: 2023-03-23</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+Stars: 3.0k ⭐</br>
+Last Release: 2023-08-24</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+<tr><th colspan="3"><big><a href="https://neo4j.com/docs/graphql-manual/current/">Neo4J Graph Database</a></big></th></tr>
+<tr><td>A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations.</br></br>Github: <a href="https://github.com/neo4j/graphql">neo4j/graphql</a></br>
+Type: Code first | SDL first</br>
+Stars: 445 ⭐</br>
+Last Release: 2023-08-22</br></br>Core Library: <a href="https://www.npmjs.com/package/graphql">GraphQL.js</a></br>Federation Library: <a href="https://www.npmjs.com/package/@apollo/subgraph">Apollo Subgraph</a></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🟢</td></tr><tr><th><code>repeatable @key</code></th><td>🟢</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🟢</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🟢</td></tr><tr><th><code>@interfaceObject</code></th><td>🟢</td></tr></table></td></tr>
 <tr><th colspan="3"><big><a href="https://stepzen.com/apollo-stepzen">StepZen, an IBM Company</a></big></th></tr>
-<tr><td>Build GraphQL APIs for all your data in a declarative way. Federate across any data source, including GraphQL.</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🔲</td></tr><tr><th><code>repeatable @key</code></th><td>🔲</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🟢</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
+<tr><td>Build GraphQL APIs for all your data in a declarative way. Federate across any data source, including GraphQL.</br></br></td><td><table><tr><th><code>_service</code></th><td>🟢</td></tr><tr><th><code>@key (single)</code></th><td>🟢</td></tr><tr><th><code>@key (multi)</code></th><td>🟢</td></tr><tr><th><code>@key (composite)</code></th><td>🔲</td></tr><tr><th><code>repeatable @key</code></th><td>🔲</td></tr><tr><th><code>@requires</code></th><td>🟢</td></tr><tr><th><code>@provides</code></th><td>🔲</td></tr><tr><th><code>federated tracing</code></th><td>🔲</td></tr></table></td><td><table><tr><th><code>@link</code></th><td>🟢</td></tr><tr><th><code>@shareable</code></th><td>🟢</td></tr><tr><th><code>@tag</code></th><td>🟢</td></tr><tr><th><code>@override</code></th><td>🟢</td></tr><tr><th><code>@inaccessible</code></th><td>🟢</td></tr><tr><th><code>@composeDirective</code></th><td>🔲</td></tr><tr><th><code>@interfaceObject</code></th><td>🔲</td></tr></table></td></tr>
 </tbody>
 </table>
diff --git a/docs/source/config.json b/docs/source/config.json
index a51612e1b..c2192b246 100644
--- a/docs/source/config.json
+++ b/docs/source/config.json
@@ -15,6 +15,7 @@
     "Changelog": "/federation-versions",
     "Interactive tutorial": "https://www.apollographql.com/tutorials/voyage-part1",
     "Demo app": "https://github.com/apollographql/supergraph-demo-fed2",
+    "Federated architecture": "https://graphql.com/learn/federated-architecture/",
     "Quickstart": {
       "1️⃣ Project setup": "/quickstart/setup",
       "2️⃣ Composition in Apollo Studio": "/quickstart/studio-composition",
diff --git a/docs/source/entities-advanced.mdx b/docs/source/entities-advanced.mdx
index ebe329f7f..7debc595e 100644
--- a/docs/source/entities-advanced.mdx
+++ b/docs/source/entities-advanced.mdx
@@ -4,15 +4,41 @@ title: Advanced topics on federated entities
 
 This article describes complex behaviors of federated entities beyond those covered in [entity basics](./entities/).
 
-## Advanced `@key`s
+## Using advanced `@key`s
 
-A single entity can have multiple `@key`s. Additionally, a `@key` can include multiple fields, and even arbitrarily nested fields.
+Depending on your entities' fields and usage, you may need to use more advanced `@key`s. For example, you may need to define a [compound `@key`](#compound-keys) if multiple fields are required to uniquely identify an entity. If different subgraphs interact with different fields an entity, you may need to define [multiple](#multiple-keys)&mdash;and sometimes [differing](#differing-keys-across-subgraphs)&mdash;`@key`s for the entity.
+
+### Compound `@key`s
+
+A single `@key` can consist of multiple fields, the combination of which uniquely identifies an entity. This is called a **compound** or composite key. In the following example, the combination of both `username` and `domain` fields is required to uniquely identify the `User` entity:
+
+```graphql {1} title="Users subgraph"
+type User @key(fields: "username domain") {
+  username: String!
+  domain: String!
+}
+```
+
+#### Nested fields in compound `@key`s
+
+Compound keys can also include _nested_ fields. In the following example, the `User` entity's `@key` consists of both a user's `id` _and_ the `id` of that user's associated `Organization`:
+
+```graphql {1} title="Users subgraph"
+type User @key(fields: "id organization { id }") {
+  id: ID!
+  organization: Organization!
+}
+
+type Organization {
+  id: ID!
+}
+```
 
 ### Multiple `@key`s
 
-You can define more than one `@key` for an entity, when applicable.
+When different subgraphs interact with different fields of an entity, you may need to define multiple `@key`s for the entity. For example, a Reviews subgraph might refer to products by their ID, whereas an Inventory subgraph might use SKUs.
 
-In this example, a `Product` entity can be uniquely identified by either its `id` _or_ its `sku`:
+In the following example, the `Product` entity can be uniquely identified by _either_ its `id` _or_ its `sku`:
 
 ```graphql {1} title="Products subgraph"
 type Product @key(fields: "id") @key(fields: "sku") {
@@ -22,10 +48,34 @@ type Product @key(fields: "id") @key(fields: "sku") {
   price: Int
 }
 ```
+<blockquote>
+
+**Note:** If you include multiple sets of `@key` fields, the query planner uses the most efficient set for entity resolution. For example, suppose you allow a type to be identified by `@key(fields: "id")` _or_ `@key(fields: "id sku")`:
+
+```graphql {1}
+type Product @key(fields: "id") @key(fields: "id sku") {
+  # ...
+}
+```
+
+That means either `id` or (`id` _and_ `sku`) is enough to uniquely identify the entity. Since `id` alone is enough, the query planner will use only that field to resolve the entity, and `@key(fields: "id sku")` is effectively ignored.
+
+</blockquote>
+
+#### Referencing entities with multiple keys
 
-This pattern is helpful when different subgraphs interact with different fields of an entity. For example, a Reviews subgraph might refer to products by their ID, whereas an Inventory subgraph might use SKUs.
+A subgraph that [references an entity without contributing any fields](./entities/#referencing-an-entity-without-contributing-fields) can use any `@key` fields in its stub definition. For example, if the Products subgraph defines the `Product` entity like this:
+
+```graphql {1} title="Products subgraph"
+type Product @key(fields: "id") @key(fields: "sku") {
+  id: ID!
+  sku: String!
+  name: String!
+  price: Int
+}
+```
 
-A subgraph that [references an entity without contributing any fields](./entities/#referencing-an-entity-without-contributing-fields) can include the fields of any `@key` in its stub definition:
+Then, a Reviews subgraph can use either `id` or `sku` in the stub definition:
 
 ```graphql title="Reviews subgraph"
 # Either:
@@ -40,20 +90,129 @@ type Product @key(fields: "sku", resolvable: false) {
 
 ```
 
-### Compound `@key`s
+### Differing `@key`s across subgraphs
 
-A single `@key` can consist of multiple fields, and even _nested_ fields.
+Although an entity commonly uses the exact same `@key` field(s) across subgraphs, you can alternatively use different `@key`s with different fields. For example, you can define a `Product` entity shared between subgraphs, one with `sku` and `upc` as its `@key`s, and the other with only `upc` as the `@key` field:
 
-In this example, the `User` entity's primary key consists of both a user's `id` _and_ the `id` of that user's associated `Organization`:
+```graphql title="Products subgraph"
+type Product @key(fields: "sku") @key(fields: "upc") {
+  sku: ID!
+  upc: String!
+  name: String!
+  price: Int
+}
+```
 
-```graphql {1} title="Users subgraph"
-type User @key(fields: "id organization { id }") {
-  id: ID!
-  organization: Organization!
+```graphql title="Inventory subgraph"
+type Product @key(fields: "upc") {
+  upc: String!
+  inStock: Boolean!
 }
+```
 
-type Organization {
-  id: ID!
+To merge entities between subgraphs, the entity must have at least one shared field between subgraphs. For example, operations can't merge the `Product` entity defined in the following subgraphs because they don't share any fields specified in the `@key` selection set:
+
+<p style="margin-bottom: 0">❌</p>
+
+<CodeColumns>
+
+```graphql title="Products subgraph"
+type Product @key(fields: "sku") {
+  sku: ID!
+  name: String!
+  price: Int
+}
+```
+
+```graphql title="Inventory subgraph"
+type Product @key(fields: "upc") {
+  upc: String!
+  inStock: Boolean!
+}
+```
+
+</CodeColumns>
+
+#### Operations with differing `@key`s
+
+Differing keys across subgraphs affect which of the entity's fields can be resolved from each subgraph. Requests can resolve fields **if there is a traversable path from the root query to the fields**.
+
+Take these subgraph schemas as an example:
+
+<CodeColumns>
+
+```graphql title="Products subgraph"
+type Product @key(fields: "sku") {
+  sku: ID!
+  upc: String!
+  name: String!
+  price: Int
+}
+
+type Query {
+  product(sku: ID!): Product
+  products: [Product!]!
+}
+
+```
+
+```graphql title="Inventory subgraph"
+type Product @key(fields: "upc") {
+  upc: String!
+  inStock: Boolean!
+}
+```
+</CodeColumns>
+
+The queries defined in the Products subgraph can always resolve all product fields because the product entity can be joined via the `upc` field present in both schemas.
+
+On the other hand, queries added to the Inventory subgraph can't resolve fields from the Products subgraph:
+
+<CodeColumns>
+
+```graphql title="Products subgraph"
+type Product @key(fields: "sku") {
+  sku: ID!
+  upc: String!
+  name: String!
+  price: Int
+}
+```
+
+```graphql title="Inventory subgraph"
+type Product @key(fields: "upc") {
+  upc: String!
+  inStock: Boolean!
+}
+
+type Query {
+  productsInStock: [Product!]!
+}
+```
+
+</CodeColumns>
+
+The `productsInStock` query can't resolve fields from the Products subgraph since the Products subgraph's `Product` type definition doesn't include `upc` as a key field, and `sku` isn't present in the Inventory subgraph.
+
+If the Products subgraph includes `@key(fields: "upc")`, all queries from the Inventory subgraph can resolve all product fields:
+
+```graphql title="Products subgraph"
+type Product @key(fields: "sku") @key(fields: "upc") {
+  sku: ID!
+  upc: String!
+  name: String!
+  price: Int
+}
+```
+
+```graphql title="Inventory subgraph"
+type Product @key(fields: "upc") {
+  upc: String!
+  inStock: Boolean!
+}
+
+type Query {
+  productsInStock: [Product!]!
 }
 ```
 
@@ -492,4 +651,4 @@ const resolvers = {
 
 A basic implementation of the `fetchProductByID` function might make a database call each time it's called. If we need to resolve `Product.price` for `N` different products, this results in `N` database calls. These calls are made _in addition to_ the call made by the Reviews subgraph to fetch the initial list of reviews (and the `id` of each product). This is where the "N+1" problem gets its name. If not prevented, this problem can cause performance problems or even enable denial-of-service attacks.
 
-This problem is not limited to reference resolvers! In fact, it can occur with any resolver that fetches from a data store. To handle this problem, we strongly recommend using [the dataloader pattern](https://github.com/graphql/dataloader). Nearly every GraphQL server library provides a dataloader implementation, and you should use it in **every resolver**. This is true even for resolvers that _aren't_ for entities and that _don't_ return a list. These resolvers can _still_ cause N+1 issues via [batched requests](/enterprise-guide/graph-security/#batched-requests).
+This problem is not limited to reference resolvers! In fact, it can occur with any resolver that fetches from a data store. To handle this problem, we strongly recommend using [the dataloader pattern](https://github.com/graphql/dataloader). Nearly every GraphQL server library provides a dataloader implementation, and you should use it in **every resolver**. This is true even for resolvers that _aren't_ for entities and that _don't_ return a list. These resolvers can _still_ cause N+1 issues via [batched requests](/technotes/TN0021-graph-security/#batched-requests).
diff --git a/docs/source/entities.mdx b/docs/source/entities.mdx
index 46f743318..700ab5fc2 100644
--- a/docs/source/entities.mdx
+++ b/docs/source/entities.mdx
@@ -53,20 +53,43 @@ type Product @key(fields: "id") {
 }
 ```
 
-The `@key` directive defines the entity's **primary key**, which consists of one or more of the type's `fields`. In the example above, the `Product` entity's primary key is its `id` field.
+The `@key` directive defines an entity's **unique key**, which consists of one or more of the type's `fields`. In the example above, the `Product` entity's unique key is its `id` field. **Every instance of an entity must be uniquely identifiable by its `@key` fields.** This is what enables your router to associate field data from _different_ subgraphs with the _same_ entity instance.
 
-**Every instance of an entity must be uniquely identifiable by its `@key` fields.** This is what enables your router to associate field data from _different_ subgraphs with the _same_ entity instance.
+In most cases, the `@key` field(s) for the same entity will be the same across subgraphs. For example, if one subgraph uses `id` as the `@key` field for the `Product` entity, other subgraphs should do the same. However, this [isn't strictly required](./entities-advanced#differing-keys-across-subgraphs).
+
+> If coming from a database context, it can be helpful to think of a `@key` as an entity's [primary key](https://en.wikipedia.org/wiki/Primary_key). This term isn't completely accurate for entities since a single entity can have [multiple `@key`s](./entities-advanced#multiple-keys). The field(s) you select for an entity's `@key` must, however, uniquely identify the entity. In that way, `@key`s are similar to [candidate keys](https://en.wikipedia.org/wiki/Candidate_key).
+
+<CodeColumns>
+
+```graphql title="Products subgraph"
+type Product @key(fields: "id") {
+  id: ID!
+  name: String!
+  price: Int
+}
+```
+
+```graphql title="Inventory subgraph"
+type Product @key(fields: "id") {
+  id: ID!
+  inStock: Boolean!
+}
+```
+
+</CodeColumns>
 
 > **An entity's `@key` _cannot_ include:**
 >
 > * Fields that return a union or interface
 > * Fields that take arguments
->
-> [See advanced options for `@key`s.](./entities-advanced/#advanced-keys)
+
+Though not strictly required, it's best to use non-nullable fields for keys. If you use fields that return `null` values, GraphOS may encounter issues resolving the entity.
+
+For more information on advanced key options, like how to define [multiple keys](./entities-advanced/#multiple-keys) or [compound keys](/entities-advanced/#compound-keys), see [Advanced topics for federation entities](./entities-advanced).
 
 ### 2. Define a reference resolver
 
-The `@key` directive effectively tells the router, "This subgraph can resolve an instance of this entity if you provide its primary key." In order for this to be true, the subgraph needs to define a **reference resolver** for the entity.
+The `@key` directive effectively tells the router, "This subgraph can resolve an instance of this entity if you provide its unique key." In order for this to be true, the subgraph needs to define a **reference resolver** for the entity.
 
 > ⚠️ **This section describes how to create reference resolvers in Apollo Server.** If you're using another [subgraph-compatible library](./building-supergraphs/supported-subgraphs/), see its documentation for creating reference resolvers (or the equivalent functionality).
 
diff --git a/docs/source/federated-types/composition.mdx b/docs/source/federated-types/composition.mdx
index 8fe500a86..3172529f4 100644
--- a/docs/source/federated-types/composition.mdx
+++ b/docs/source/federated-types/composition.mdx
@@ -47,6 +47,8 @@ graph LR;
 
 To learn how to perform composition with managed federation, see the [quickstart](../quickstart/studio-composition/).
 
+> GraphOS also provides a [schema linter](/graphos/delivery/schema-linter) with [composition specific rules](/graphos/delivery/linter-rules#composition-rules) to help you follow best practices. You can set up schema checks for your graph in GraphOS Studio or perform one-off linting with the Rover CLI. Check out the [schema linting](/graphos/delivery/schema-linter) docs to learn more.
+
 ### Manually with the Rover CLI
 
 The [Rover CLI](https://www.apollographql.com/docs/rover/) supports a `supergraph compose` command that you can use to compose a supergraph schema from a collection of subgraph schemas:
@@ -216,7 +218,7 @@ This is a useful solution for shared types that encapsulate simple scalar data.
 
 > You can use the `@inaccessible` directive to incrementally add a value type field to multiple subgraphs _without_ breaking composition. [Learn more.](./sharing-types/#adding-new-shared-fields)
 
-#### Make the shared type an entity.
+#### Make the shared type an entity
 
 <p style="margin-bottom: 0">✅</p>
 
@@ -363,7 +365,6 @@ As you can see, the supergraph schema includes only the input fields and argumen
 >
 > When defining input types and field arguments in multiple subgraphs, make sure that every non-nullable field and argument is consistent in _every subgraph_. For examples, see [Arguments](sharing-types#arguments).
 
-
 ### Enums
 
 If an enum definition differs between subgraphs, the [composition strategy](#merging-types-from-multiple-subgraphs) depends on how the enum is used:
diff --git a/docs/source/federated-types/federated-directives.mdx b/docs/source/federated-types/federated-directives.mdx
index a80877359..6b6d6378a 100644
--- a/docs/source/federated-types/federated-directives.mdx
+++ b/docs/source/federated-types/federated-directives.mdx
@@ -27,10 +27,10 @@ If an imported directive's default name matches one of your own custom directive
 ```graphql
 extend schema
   @link(url: "https://specs.apollo.dev/federation/v2.3",
-        import: [{ name: "@key", as: "@primaryKey"}, "@shareable"])
+        import: [{ name: "@key", as: "@uniqueKey"}, "@shareable"])
 ```
 
-This example subgraph schema uses `@primaryKey` for the federated directive usually named [`@key`](#key).
+This example subgraph schema uses `@uniqueKey` for the federated directive usually named [`@key`](#key).
 
 ### Namespaced directives
 
@@ -134,7 +134,7 @@ To check whether your subgraph library supports repeatable directives, see the `
 </td>
 <td>
 
-**Required.** A GraphQL selection set (provided as a string) of fields and subfields that contribute to the entity's primary key.
+**Required.** A GraphQL selection set (provided as a string) of fields and subfields that contribute to the entity's unique key.
 
 Examples:
 
@@ -142,7 +142,7 @@ Examples:
 * `"username region"`
 * `"name organization { id }"`
 
-See also [Advanced `@key`s](../entities-advanced/#advanced-keys).
+See also [Advanced `@key`s](../entities-advanced/#using-advanced-keys).
 
 </td>
 </tr>
@@ -349,6 +349,74 @@ For more information, see [Migrating entities and fields](../entities-advanced/#
 </tbody>
 </table>
 
+## Controlling access
+
+### `@authenticated`
+
+> ⚠️ **This directive is available in Apollo Federation 2.5 and later.**
+> **It is an [Enterprise feature](/router/enterprise-features) of the Apollo Router** and requires an organization with a [GraphOS Enterprise plan](https://www.apollographql.com/pricing/).<br/>
+>
+> If your organization _doesn't_ currently have an Enterprise plan, you can test out this functionality by signing up for a free [Enterprise trial](/graphos/org/plans/#enterprise-trials).
+
+```graphql
+directive @authenticated on
+    FIELD_DEFINITION
+  | OBJECT
+  | INTERFACE
+  | SCALAR
+  | ENUM
+```
+
+Indicates to composition that the target element is accessible only to the authenticated supergraph users. For more granular access control, see the [`@requiresScopes`](#requiresScopes) directive below. Refer to the [Apollo Router article](/router/configuration/authorization#authenticated) for additional details.
+
+### `@requiresScopes`
+
+> ⚠️ **This directive is available in Apollo Federation 2.5 and later.**
+> **Is is an [Enterprise feature](/router/enterprise-features) of the Apollo Router** and requires an organization with a [GraphOS Enterprise plan](https://www.apollographql.com/pricing/).<br/>
+>
+> If your organization _doesn't_ currently have an Enterprise plan, you can test out this functionality by signing up for a free [Enterprise trial](/graphos/org/plans/#enterprise-trials).
+
+```graphql
+directive @requiresScopes(scopes: [[federation__Scope!]!]!) on
+    FIELD_DEFINITION
+  | OBJECT
+  | INTERFACE
+  | SCALAR
+  | ENUM
+```
+
+Indicates to composition that the target element is accessible only to the authenticated supergraph users with the appropriate JWT scopes. Refer to the [Apollo Router article](/router/configuration/authorization#requiresscopes) for additional details.
+
+#### Arguments
+
+<table class="field-table">
+  <thead>
+    <tr>
+      <th>Name /<br/>Type</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+
+<tbody>
+
+<tr class="required">
+<td>
+
+##### `scopes`
+
+`[federation__Scope!]!`
+
+</td>
+<td>
+
+**Required.** List of JWT scopes that must be granted to the user in order to access the underlying element data.
+
+</td>
+</tr>
+
+</tbody>
+</table>
+
 ## Referencing external fields
 
 ### `@external`
diff --git a/docs/source/federation-2/new-in-federation-2.mdx b/docs/source/federation-2/new-in-federation-2.mdx
index 7a0cc4158..7dc89c8c3 100644
--- a/docs/source/federation-2/new-in-federation-2.mdx
+++ b/docs/source/federation-2/new-in-federation-2.mdx
@@ -236,7 +236,7 @@ type Bill @key(fields: "id") {
 
 </CodeColumns>
 
-For details, see [Entity migration](../entities-advanced/#entity-migration).
+For details, see [Entity migration](../entities-advanced/#migrating-entities-and-fields).
 
 ## Interfaces implementing interfaces
 
diff --git a/docs/source/federation-versions.mdx b/docs/source/federation-versions.mdx
index b62f0e047..4d08c6446 100644
--- a/docs/source/federation-versions.mdx
+++ b/docs/source/federation-versions.mdx
@@ -21,6 +21,126 @@ This article describes notable changes and additions introduced in each minor ve
 
 - **If you maintain a [subgraph-compatible library](./building-supergraphs/supported-subgraphs/),** consult this article to stay current with recently added directives. All of these directive definitions are also listed in the [subgraph specification](./subgraph-spec/#subgraph-schema-additions).
 
+## v2.5
+
+<hr/>
+
+<CodeColumns cols="3">
+
+<div>
+
+First release
+
+**July 2023**
+
+</div>
+
+<div>
+
+Available in GraphOS?
+
+**Yes**
+
+</div>
+
+<div>
+
+Minimum router version
+
+**`1.29.1`**
+
+</div>
+
+</CodeColumns>
+
+<hr/>
+
+#### Directive changes
+
+<table>
+  <thead>
+    <tr>
+      <th>Topic</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+
+<tbody>
+<tr>
+<td>
+
+##### `@authenticated`
+
+</td>
+<td>
+
+
+Introduced. [Learn more.](/router/configuration/authorization)
+
+```graphql
+directive @authenticated on
+    FIELD_DEFINITION
+  | OBJECT
+  | INTERFACE
+  | SCALAR
+  | ENUM
+```
+
+</td>
+</tr>
+
+<tr>
+<td>
+
+##### `@requiresScopes`
+
+</td>
+<td>
+
+
+Introduced. [Learn more.](/router/configuration/authorization)
+
+```graphql
+directive @requiresScopes(scopes: [[Scope!]!]!) on
+    FIELD_DEFINITION
+  | OBJECT
+  | INTERFACE
+  | SCALAR
+  | ENUM
+```
+
+</td>
+</tr>
+
+</tbody>
+</table>
+
+#### Subgraph changes
+
+<table>
+  <thead>
+    <tr>
+      <th>Topic</th>
+      <th>Description</th>
+    </tr>
+  </thead>
+
+<tbody>
+<tr>
+<td>
+
+Scope
+
+</td>
+<td>
+
+- Custom scalar representing a JWT scope. Used by new `@requiresScopes` directive.
+
+</td>
+</tr>
+</tbody>
+</table>
+
 ## v2.4
 
 <hr/>
diff --git a/docs/source/hints.md b/docs/source/hints.md
deleted file mode 100644
index 6d255e300..000000000
--- a/docs/source/hints.md
+++ /dev/null
@@ -1,72 +0,0 @@
----
-title: Composition hints
----
-
-When you successfully [compose](./federated-types/composition) the schemas provided by your [subgraphs](./building-supergraphs/subgraphs-overview/) into a supergraph schema, the composition process might output **hints** that provide additional information about the result. Hints are primarily informative and _do not_ necessarily indicate that a problem needs to be fixed.
-
-Hints are categorized under the following levels:
-
-* `WARN`: Indicates a situation that might be expected but is usually temporary and should be double-checked. Typically, composition might have needed to ignore some elements from some subgraph when creating the supergraph.
-* `INFO`: Suggests a potentially helpful improvement or highlights a noteworthy resolution made by composition. Can otherwise be ignored.
-* `DEBUG`: Lower-level information that provides insight into the composition. These hints are of lesser importance/impact.
-
-Note that hints are first and foremost informative and don't necessarily correspond to a problem to be fixed.
-
-This document lists the hints that can be generated for each level, with a description of why each is generated.
-
-
-The following hints might be generated during composition:
-
-## `WARN`
-
-<div class="sticky-table">
-
-| Code | Description | Level |
-|---|---|---|
-| `INCONSISTENT_DEFAULT_VALUE_PRESENCE` | Indicates that an argument definition (of a field/input field/directive definition) has a default value in only some of the subgraphs that define the argument. | `WARN` |
-| `INCONSISTENT_INPUT_OBJECT_FIELD` | Indicates that a field of an input object type definition is only defined in a subset of the subgraphs that declare the input object. | `WARN` |
-| `INCONSISTENT_ENUM_VALUE_FOR_INPUT_ENUM` | Indicates that a value of an enum type definition (that is only used as an Input type) has not been merged into the supergraph because it is defined in only a subset of the subgraphs that declare the enum | `WARN` |
-| `INCONSISTENT_EXECUTABLE_DIRECTIVE_PRESENCE` | Indicates that an executable directive definition is declared in only some of the subgraphs. | `WARN` |
-| `NO_EXECUTABLE_DIRECTIVE_INTERSECTION` | Indicates that, for an executable directive definition, no location for it appears in all subgraphs. | `WARN` |
-| `INCONSISTENT_EXECUTABLE_DIRECTIVE_REPEATABLE` | Indicates that an executable directive definition is marked repeatable in only a subset of the subgraphs (and will not be repeatable in the supergraph). | `WARN` |
-| `INCONSISTENT_EXECUTABLE_DIRECTIVE_LOCATIONS` | Indicates that an executiable directive definition is declared with inconsistent locations across subgraphs (and will use the intersection of all locations in the supergraph). | `WARN` |
-| `INCONSISTENT_DESCRIPTION` | Indicates that an element has a description in more than one subgraph, and the descriptions are not equal. | `WARN` |
-| `INCONSISTENT_ARGUMENT_PRESENCE` | Indicates that an optional argument (of a field or directive definition) is not present in all subgraphs and will not be part of the supergraph. | `WARN` |
-| `FROM_SUBGRAPH_DOES_NOT_EXIST` | Source subgraph specified by @override directive does not exist | `WARN` |
-| `INCONSISTENT_NON_REPEATABLE_DIRECTIVE_ARGUMENTS` | A non-repeatable directive is applied to a schema element in different subgraphs but with arguments that are different. | `WARN` |
-| `DIRECTIVE_COMPOSITION_WARN` | Indicates that an issue was detected when composing custom directives. | `WARN` |
-| `INCONSISTENT_RUNTIME_TYPES_FOR_SHAREABLE_RETURN` | Indicates that a @shareable field returns different sets of runtime types in the different subgraphs in which it is defined. | `WARN` |
-
-</div>
-
-## `INFO`
-
-<div class="sticky-table">
-
-| Code | Description | Level |
-|---|---|---|
-| `INCONSISTENT_BUT_COMPATIBLE_FIELD_TYPE` | Indicates that a field does not have the exact same types in all subgraphs, but that the types are "compatible" (2 types are compatible if one is a non-nullable version of the other, a list version, a subtype, or a combination of the former). | `INFO` |
-| `INCONSISTENT_BUT_COMPATIBLE_ARGUMENT_TYPE` | Indicates that an argument type (of a field/input field/directive definition) does not have the exact same type in all subgraphs, but that the types are "compatible" (two types are compatible if one is a non-nullable version of the other, a list version, a subtype, or a combination of the former). | `INFO` |
-| `INCONSISTENT_ENTITY` | Indicates that an object is declared as an entity (has a `@key`) in only some of the subgraphs in which the object is defined. | `INFO` |
-| `OVERRIDDEN_FIELD_CAN_BE_REMOVED` | Field has been overridden by another subgraph. Consider removing. | `INFO` |
-| `OVERRIDE_DIRECTIVE_CAN_BE_REMOVED` | Field with @override directive no longer exists in source subgraph, the directive can be safely removed | `INFO` |
-| `MERGED_NON_REPEATABLE_DIRECTIVE_ARGUMENTS` | A non-repeatable directive has been applied to a schema element in different subgraphs with different arguments and the arguments values were merged using the directive configured strategies. | `INFO` |
-| `DIRECTIVE_COMPOSITION_INFO` | Indicates that an issue was detected when composing custom directives. | `INFO` |
-
-</div>
-
-## `DEBUG`
-
-<div class="sticky-table">
-
-| Code | Description | Level |
-|---|---|---|
-| `INCONSISTENT_OBJECT_VALUE_TYPE_FIELD` | Indicates that a field of an object "value type" (has no `@key` in any subgraph) is not defined in all the subgraphs that declare the type. | `DEBUG` |
-| `INCONSISTENT_INTERFACE_VALUE_TYPE_FIELD` | Indicates that a field of an interface "value type" (has no `@key` in any subgraph) is not defined in all the subgraphs that declare the type. | `DEBUG` |
-| `INCONSISTENT_UNION_MEMBER` | Indicates that a member of a union type definition is only defined in a subset of the subgraphs that declare the union. | `DEBUG` |
-| `INCONSISTENT_ENUM_VALUE_FOR_OUTPUT_ENUM` | Indicates that a value of an enum type definition (that is only used as an Output type, or is unused) has been merged in the supergraph but is defined in only a subset of the subgraphs that declare the enum | `DEBUG` |
-| `INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_REPEATABLE` | Indicates that a type system directive definition is marked repeatable in only a subset of the subgraphs that declare the directive (and will be repeatable in the supergraph). | `DEBUG` |
-| `INCONSISTENT_TYPE_SYSTEM_DIRECTIVE_LOCATIONS` | Indicates that a type system directive definition is declared with inconsistent locations across subgraphs (and will use the union of all locations in the supergraph). | `DEBUG` |
-| `UNUSED_ENUM_TYPE` | Indicates that an enum type is defined in some subgraphs but is unused (no field/argument references it). All the values from subgraphs defining that enum will be included in the supergraph. | `DEBUG` |
-
-</div>
diff --git a/docs/source/hints.mdx b/docs/source/hints.mdx
new file mode 100644
index 000000000..053a94b69
--- /dev/null
+++ b/docs/source/hints.mdx
@@ -0,0 +1,23 @@
+---
+title: Composition hints
+---
+
+When you successfully [compose](./federated-types/composition) the schemas provided by your [subgraphs](./building-supergraphs/subgraphs-overview/) into a supergraph schema, the composition process can flag potential improvements or **hints**. Hints are violations of the [GraphOS schema linter's](/graphos/delivery/schema-linter) [composition rules](/graphos/delivery/linter-rules#composition-rules). You can review them on the [Checks](/graphos/delivery/schema-checks) page in GraphOS Studio or via the [Rover CLI](/rover/).
+
+> Composition hints only appear in GraphOS Studio and via the `rover subgraph check` command for graphs on [federation version `2.4`](/federation/federation-versions/#v24) or later. You can update a graph's version from its **Settings** page in [GraphOS Studio](https://studio.apollographql.com?referrer=docs-content).
+
+The [`rover subgraph check`](/rover/commands/subgraphs#subgraph-check) command outputs rule violations with the [severity levels](/graphos/delivery/schema-linter/#setting-severity-levels) you've configured for your graph variant. The [`rover supergraph compose`](/rover/commands/supergraphs#supergraph-compose) command outputs rule violations for _all_ local subgraph schemas.
+
+See below for a list of composition rules categorized by rule type. The heading for each rule is the code that GraphOS returns for the rule violation. Refer to the [rules reference page](/graphos/delivery/linter-rules) for a comprehensive list of linter rules.
+
+### Inconsistent elements
+
+<InconsistentCompositionRules />
+
+### Overridden and unused elements
+
+<OverriddenCompositionRules />
+
+### Directives
+
+<DirectiveCompositionRules />
\ No newline at end of file
diff --git a/docs/source/managed-federation/deployment.mdx b/docs/source/managed-federation/deployment.mdx
index 0b8b5c884..48d6b7ce0 100644
--- a/docs/source/managed-federation/deployment.mdx
+++ b/docs/source/managed-federation/deployment.mdx
@@ -5,11 +5,11 @@ description: Best practices
 
 When rolling out changes to a [subgraph](../building-supergraphs/subgraphs-overview/), we recommend the following workflow:
 
-1. Confirm the backward compatibility of each change by [running `rover subgraph check`](/rover/commands/subgraphs/#checking-subgraph-schema-changes) in your CI pipeline.
+1. Confirm the backward compatibility of each change by [running `rover subgraph check`](/rover/commands/subgraphs/#validating-subgraph-schema-changes) in your CI pipeline.
 2. Merge backward compatible changes that successfully pass schema checks.
 3. Deploy changes to the subgraph in your infrastructure.
 4. Wait until all replicas finish deploying.
-5. Run [`rover subgraph publish`](/rover/commands/subgraphs/#publishing-a-subgraph-schema-to-apollo-studio) to update your managed federation configuration:
+5. Run [`rover subgraph publish`](/rover/commands/subgraphs/#publishing-a-subgraph-schema-to-graphos) to update your managed federation configuration:
 
 ```bash
 rover subgraph publish my-supergraph@my-variant \
@@ -119,7 +119,7 @@ The next time it starts up or polls, your router obtains an updated configuratio
 
 With managed federation, you can control which version of your graph a fleet of routers are using. In the majority of cases, rolling over all of your router instances to a new schema version is safe, assuming you've used [schema checks](./federated-schema-checks/) to confirm that your changes are backward compatible.
 
-However, changes at the router level might involve a variety of different updates, such as [migrating entities](../entities-advanced/#migrating-entities-and-fields) from one subgraph to another. If your infrastructure requires a more advanced deployment process, you can use [graph variants](/graphos/graphs/overview/#variants) to manage different fleets of routers running with different configurations.
+However, changes at the router level might involve a variety of different updates, such as [migrating entities](../entities-advanced/#migrating-entities-and-fields) from one subgraph to another. If your infrastructure requires a more advanced deployment process, you can use [graph variants](/graphos/graphs/#variants) to manage different fleets of routers running with different configurations.
 
 ### Example
 
@@ -143,7 +143,7 @@ To configure a canary deployment, you might maintain two production graph varian
     rover subgraph publish my-supergraph@prod --name=launches --schema ./launches/schema.graphql
     ```
 
-If your canary variant [reports metrics to GraphOS](/graphos/metrics/usage-reporting/), you can use [Apollo Studio](https://studio.apollographql.com) to verify a canary's performance before rolling out changes to the rest of the graph. You can also use variants to support a variety of other advanced deployment workflows, such as blue/green deployments.
+If your canary variant [reports metrics to GraphOS](/graphos/metrics/), you can use [Apollo Studio](https://studio.apollographql.com?referrer=docs-content) to verify a canary's performance before rolling out changes to the rest of the graph. You can also use variants to support a variety of other advanced deployment workflows, such as blue/green deployments.
 
 ## Modifying query-planning logic
 
diff --git a/docs/source/managed-federation/uplink.mdx b/docs/source/managed-federation/uplink.mdx
index 0b27cf959..5ed0e84ca 100644
--- a/docs/source/managed-federation/uplink.mdx
+++ b/docs/source/managed-federation/uplink.mdx
@@ -140,4 +140,4 @@ APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT=https://aws.uplink.api.apollographql.com/
 
 Supergraph schemas provided by Uplink cannot exceed 6MB in size. The _vast_ majority of supergraph schemas are well below this limit.
 
-If your supergraph schema _does_ exceed 6MB, you can set up a [build status webhook](/graphos/notifications/build-status-notification/) for your graph. Whenever you're notified of a successful supergraph schema composition, your webhook can fetch the latest supergraph schema [via the Rover CLI](/rover/commands/supergraphs#supergraph-fetch).
+If your supergraph schema _does_ exceed 6MB, you can set up a [build status webhook](/graphos/metrics/notifications/build-status-notification/) for your graph. Whenever you're notified of a successful supergraph schema composition, your webhook can fetch the latest supergraph schema [via the Rover CLI](/rover/commands/supergraphs#supergraph-fetch).
diff --git a/docs/source/migrating-from-stitching.md b/docs/source/migrating-from-stitching.md
index ac289b7b4..4f392dfa5 100644
--- a/docs/source/migrating-from-stitching.md
+++ b/docs/source/migrating-from-stitching.md
@@ -122,7 +122,7 @@ resolvers: {
 
 This resolver calls `Query.user` on the `userSchema` to look up a `User`. It adds that user to the `Reservation.user` field that was previously defined at the gateway. This code can all remain. You don't need to remove it from the stitched gateway. In fact, if you did that, the stitched gateway would break.
 
-On the other hand, a _federated_ architecture defines its resolvers at the subgraph level. These resolvers rely on **entities**, which are identified by a primary key. For example, the Reservation subgraph must define the `Reservation` type as an entity to allow other subgraphs to extend it. These other subgraphs use the `Reservation`'s `@key` fields to uniquely identify a given instance:
+On the other hand, a _federated_ architecture defines its resolvers at the subgraph level. These resolvers rely on **entities**, which are identified by a unique key. For example, the Reservation subgraph must define the `Reservation` type as an entity to allow other subgraphs to extend it. These other subgraphs use the `Reservation`'s `@key` fields to uniquely identify a given instance:
 
 ```graphql
 type Reservation @key(fields: "id") {
diff --git a/docs/source/performance/monitoring.mdx b/docs/source/performance/monitoring.mdx
index 305f6b8a9..2870ba2bd 100644
--- a/docs/source/performance/monitoring.mdx
+++ b/docs/source/performance/monitoring.mdx
@@ -39,7 +39,7 @@ Operation-level statistics are still collected for operations sent by clients, a
 
 ### Setup
 
-To enable federated tracing, you [set the `APOLLO_KEY` environment variable](/apollo-server/monitoring/metrics/#connecting-to-apollo-studio) in your _gateway's_ environment. **Do not** set this environment variable for your subgraph servers.
+To enable federated tracing, you [set the `APOLLO_KEY` environment variable](/apollo-server/monitoring/metrics/#connecting-to-graphos) in your _gateway's_ environment. **Do not** set this environment variable for your subgraph servers.
 
 > If _other_ features require you to set `APOLLO_KEY` in your subgraph servers, [disable usage reporting](/apollo-server/api/plugin/usage-reporting/#disabling-the-plugin) in those servers.
 
diff --git a/docs/source/subgraph-spec.mdx b/docs/source/subgraph-spec.mdx
index b25ae223b..aadf554f9 100644
--- a/docs/source/subgraph-spec.mdx
+++ b/docs/source/subgraph-spec.mdx
@@ -30,6 +30,7 @@ union _Entity
 scalar _Any
 scalar FieldSet
 scalar link__Import
+scalar federation__Scope
 
 enum link__Purpose {
   """
@@ -63,6 +64,8 @@ directive @tag(name: String!) repeatable on FIELD_DEFINITION | INTERFACE | OBJEC
 directive @override(from: String!) on FIELD_DEFINITION
 directive @composeDirective(name: String!) repeatable on SCHEMA
 directive @interfaceObject on OBJECT
+directive @authenticated on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
+directive @requiresScopes(scopes: [[federation__Scope!]!]!) on FIELD_DEFINITION | OBJECT | INTERFACE | SCALAR | ENUM
 
 # This definition is required only for libraries that don't support
 # GraphQL's built-in `extend` keyword
@@ -445,6 +448,10 @@ This string-serialized scalar represents a set of fields that's passed to a fede
 
 Grammatically, a `FieldSet` is a [selection set](http://spec.graphql.org/draft/#sec-Selection-Sets) minus the outermost curly braces. It can represent a single field (`"upc"`), multiple fields (`"id countryCode"`), and even nested selection sets (`"id organization { id }"`).
 
+#### `scalar Scope`
+
+This string-serialized scalar represents a JWT scope.
+
 ### Directives
 
 See [Federation-specific GraphQL directives](./federated-types/federated-directives/).
diff --git a/federation-integration-testsuite-js/CHANGELOG.md b/federation-integration-testsuite-js/CHANGELOG.md
index 90922c22a..17a0f03ac 100644
--- a/federation-integration-testsuite-js/CHANGELOG.md
+++ b/federation-integration-testsuite-js/CHANGELOG.md
@@ -1,5 +1,13 @@
 # CHANGELOG for `federation-integration-testsuite-js`
 
+## 2.5.5
+
+## 2.5.4
+
+## 2.5.3
+
+## 2.5.2
+
 ## 2.5.1
 
 ## 2.5.0
diff --git a/federation-integration-testsuite-js/package.json b/federation-integration-testsuite-js/package.json
index 81ded4017..5b956be46 100644
--- a/federation-integration-testsuite-js/package.json
+++ b/federation-integration-testsuite-js/package.json
@@ -1,7 +1,7 @@
 {
   "name": "apollo-federation-integration-testsuite",
   "private": true,
-  "version": "2.5.1",
+  "version": "2.5.5",
   "description": "Apollo Federation Integrations / Test Fixtures",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",
diff --git a/gateway-js/CHANGELOG.md b/gateway-js/CHANGELOG.md
index 00c89822f..eb0c6f9c9 100644
--- a/gateway-js/CHANGELOG.md
+++ b/gateway-js/CHANGELOG.md
@@ -1,5 +1,88 @@
 # CHANGELOG for `@apollo/gateway`
 
+## 2.5.5
+### Patch Changes
+
+
+- Fix specific case for requesting __typename on interface entity type ([#2775](https://github.com/apollographql/federation/pull/2775))
+  
+  In certain cases, when resolving a __typename on an interface entity (due to it actual being requested in the operation), that fetch group could previously be trimmed / treated as useless. At a glance, it appears to be a redundant step, i.e.:
+  ```
+  { ... on Product { __typename id }} => { ... on Product { __typename} }
+  ```
+  It's actually necessary to preserve this in the case that we're coming from an interface object to an (entity) interface so that we can resolve the concrete __typename correctly.
+
+- Don't preserve useless fetches which downgrade __typename from a concrete type back to its interface type. ([#2778](https://github.com/apollographql/federation/pull/2778))
+  
+  In certain cases, the query planner was preserving some fetches which were "useless" that would rewrite __typename from its already-resolved concrete type back to its interface type. This could result in (at least) requested fields being "filtered" from the final result due to the interface's __typename in the data where the concrete type's __typename was expected.
+  
+  Specifically, the solution was compute the path between newly created groups and their parents when we know that it's trivial (`[]`). Further along in the planning process, this allows to actually remove the known-useless group.
+
+- Propagate type information when renaming entity fields ([#2776](https://github.com/apollographql/federation/pull/2776))
+
+  Aliased entity fields might have been incorrectly overwritten if multiple fields/aliases shared the same name. Query planner automatically renames conflicting fields to ensure we can always generate a valid GraphQL query. The underlying issue was that this key rewriting logic was assuming the same type of an object. In case of entity queries asking for those aliased fields, we ended up always attempting to apply field renaming logic regardless, whether or not a given entity was of the correct type. This fix ensures that the query planner logic correctly accounts for the object type when applying field renaming logic.
+
+- Updated dependencies [[`66d7e4ce`](https://github.com/apollographql/federation/commit/66d7e4ced9a6ccd170d5e228741332395a2dd553), [`a37bbbf6`](https://github.com/apollographql/federation/commit/a37bbbf6501032f16382ccb64d1dfa0dcddac789)]:
+  - @apollo/query-planner@2.5.5
+  - @apollo/composition@2.5.5
+  - @apollo/federation-internals@2.5.5
+
+## 2.5.4
+### Patch Changes
+
+
+- Adds header to change the format of exposed query plans, and allows formatting it as json. ([#2724](https://github.com/apollographql/federation/pull/2724))
+  
+  When the gateway is configured to allow it, adding the `Apollo-Query-Plan-Experimental` header to a request already allowed a "prettified" text version of the query plan used for the query is returned in the response extension. This changes adds support for a new (optional) accompanying header, `Apollo-Query-Plan-Experimental-Format`, which can be set to the value "internal" to have the query plan returned as a json object (that correspond to the internal representation of that query plan) instead of the text version otherwise sent. Note that if that new header is not provided, then the query plan continues to be send in the previous prettified text version.
+
+- Fix some potentially incorrect query plans with `@requires` when some dependencies are involved. ([#2726](https://github.com/apollographql/federation/pull/2726))
+  
+  In some rare case of `@requires`, an over-eager optimisation was incorrectly considering that
+  a dependency between 2 subgraph fetches was unnecessary, leading to doing 2 subgraphs queries
+  in parallel when those should be done sequentially (because the 2nd query rely on results
+  from the 1st one). This effectively resulted in the required fields not being provided (the
+  consequence of which depends a bit on the resolver detail, but if the resolver expected
+  the required fields to be populated (as they should), then this could typically result
+  in a message of the form `GraphQLError: Cannot read properties of null`).
+- Updated dependencies [[`203b0a44`](https://github.com/apollographql/federation/commit/203b0a444782f6d6ca2f2dbb68c6e4eb59de6d45)]:
+  - @apollo/query-planner@2.5.4
+  - @apollo/composition@2.5.4
+  - @apollo/federation-internals@2.5.4
+
+## 2.5.3
+### Patch Changes
+
+
+- Fix execution error in some cases where aliases are used and some values are `null`. ([#2716](https://github.com/apollographql/federation/pull/2716))
+  
+  The error would manifest itself as an `INTERNAL_SERVER_ERROR` with a message of the form `Cannot read properties of null`.
+- Updated dependencies [[`4b9a512b`](https://github.com/apollographql/federation/commit/4b9a512b62e02544d7854fa198942aac33b93feb), [`c6e0e76d`](https://github.com/apollographql/federation/commit/c6e0e76dbc62662c2aa6ff7f657e374047b11255), [`1add932c`](https://github.com/apollographql/federation/commit/1add932c5cd1297853fb5af9a3a6aaa71243f63a), [`6f1fddb2`](https://github.com/apollographql/federation/commit/6f1fddb25d49262b2ebf6db953371a559dd62e9c)]:
+  - @apollo/composition@2.5.3
+  - @apollo/federation-internals@2.5.3
+  - @apollo/query-planner@2.5.3
+
+## 2.5.2
+### Patch Changes
+
+
+- Remove extraneous call to `span.setStatus()` on a span which has already ended. ([#2697](https://github.com/apollographql/federation/pull/2697))
+  
+  In cases where a subgraph responded with an error, we would sometimes try to set
+  the status of a span which had already ended. This resulted in a warning log to
+  the console (but no effect otherwise). This warning should no longer happen.
+
+- Fix `fallbackPollIntervalInMs` behavior. ([#2709](https://github.com/apollographql/federation/pull/2709))
+  
+  The `fallbackPollIntervalInMs` serves 2 purposes:
+  * it allows users to provide an Uplink poll interval if Uplink doesn't provide one
+  * it allows users to use a longer poll interval that what's prescribed by Uplink
+  
+  The second bullet is how the configuration option is documented, but not how it was previously implemented. This change corrects the behavior to respect this configuration if it's provided AND is longer than the Uplink interval.
+- Updated dependencies [[`35179f08`](https://github.com/apollographql/federation/commit/35179f086ce973e9ae7bb455f7ea7d73cdc10f69)]:
+  - @apollo/query-planner@2.5.2
+  - @apollo/federation-internals@2.5.2
+  - @apollo/composition@2.5.2
+
 ## 2.5.1
 ### Patch Changes
 
diff --git a/gateway-js/package.json b/gateway-js/package.json
index edec042ca..c5ef0061b 100644
--- a/gateway-js/package.json
+++ b/gateway-js/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@apollo/gateway",
-  "version": "2.5.1",
+  "version": "2.5.5",
   "description": "Apollo Gateway",
   "author": "Apollo <packages@apollographql.com>",
   "main": "dist/index.js",
@@ -25,9 +25,9 @@
     "access": "public"
   },
   "dependencies": {
-    "@apollo/composition": "2.5.1",
-    "@apollo/federation-internals": "2.5.1",
-    "@apollo/query-planner": "2.5.1",
+    "@apollo/composition": "2.5.5",
+    "@apollo/federation-internals": "2.5.5",
+    "@apollo/query-planner": "2.5.5",
     "@apollo/server-gateway-interface": "^1.1.0",
     "@apollo/usage-reporting-protobuf": "^4.1.0",
     "@apollo/utils.createhash": "^2.0.0",
diff --git a/gateway-js/src/__tests__/executeQueryPlan.conditions.test.ts b/gateway-js/src/__tests__/executeQueryPlan.conditions.test.ts
index 4881952a9..68ff85446 100644
--- a/gateway-js/src/__tests__/executeQueryPlan.conditions.test.ts
+++ b/gateway-js/src/__tests__/executeQueryPlan.conditions.test.ts
@@ -1,13 +1,23 @@
 import gql from 'graphql-tag';
-import { getFederatedTestingSchema, ServiceDefinitionModule } from './execution-utils';
+import {
+  getFederatedTestingSchema,
+  ServiceDefinitionModule,
+} from './execution-utils';
 import {
   astSerializer,
   queryPlanSerializer,
 } from 'apollo-federation-integration-testsuite';
-import { Operation, parseOperation, Schema } from '@apollo/federation-internals';
+import {
+  Operation,
+  parseOperation,
+  Schema,
+} from '@apollo/federation-internals';
 import { QueryPlan } from '@apollo/query-planner';
 import { LocalGraphQLDataSource } from '../datasources';
-import { GatewayExecutionResult, GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
+import {
+  GatewayExecutionResult,
+  GatewayGraphQLRequestContext,
+} from '@apollo/server-gateway-interface';
 import { buildOperationContext } from '../operationContext';
 import { executeQueryPlan } from '../executeQueryPlan';
 
@@ -15,7 +25,9 @@ expect.addSnapshotSerializer(astSerializer);
 expect.addSnapshotSerializer(queryPlanSerializer);
 
 describe('Execution tests for @include/@skip', () => {
-  function buildRequestContext(variables: Record<string, any>): GatewayGraphQLRequestContext {
+  function buildRequestContext(
+    variables: Record<string, any>,
+  ): GatewayGraphQLRequestContext {
     // eslint-disable-next-line @typescript-eslint/ban-ts-comment
     // @ts-ignore
     return {
@@ -28,7 +40,7 @@ describe('Execution tests for @include/@skip', () => {
     };
   }
 
-  let s2Queries: {id : number}[] = [];
+  let s2Queries: { id: number }[] = [];
   /**
    * Simple subgraph schemas reused by a number of tests. This declares a simple interface `T` with 2 implems `T1` and `T2`.
    * There is a simple operation that returns a list of 3 simple objects:
@@ -70,9 +82,9 @@ describe('Execution tests for @include/@skip', () => {
               { __typename: 'T2', id: 2, a2: 20 },
               { __typename: 'T1', id: 3, a1: 30 },
             ];
-          }
+          },
         },
-      }
+      },
     },
     {
       name: 'S2',
@@ -100,8 +112,8 @@ describe('Execution tests for @include/@skip', () => {
             return { ...ref, b2: 100 * ref.id };
           },
         },
-      }
-    }
+      },
+    },
   ];
 
   async function executePlan(
@@ -114,7 +126,9 @@ describe('Execution tests for @include/@skip', () => {
     const apiSchema = schema.toAPISchema();
     const operationContext = buildOperationContext({
       schema: apiSchema.toGraphQLJSSchema(),
-      operationDocument: gql`${operation.toString()}`,
+      operationDocument: gql`
+        ${operation.toString()}
+      `,
     });
     return executeQueryPlan(
       queryPlan,
@@ -127,50 +141,71 @@ describe('Execution tests for @include/@skip', () => {
   }
 
   describe('Constant conditions optimisation', () => {
-    const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema(simpleInterfaceSchemas);
+    const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema(
+      simpleInterfaceSchemas,
+    );
 
     it('top-level @include never included', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           t @include(if: false) {
             id
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       expect(queryPlan).toMatchInlineSnapshot(`QueryPlan {}`);
 
-      const response = await executePlan(queryPlan, operation, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`Object {}`);
     });
 
     it('top-level @skip always skipped', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           t @skip(if: true) {
             id
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       expect(queryPlan).toMatchInlineSnapshot(`QueryPlan {}`);
 
-      const response = await executePlan(queryPlan, operation, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`Object {}`);
     });
 
     it('top-level @include always included', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           t @include(if: true) {
             id
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // Note that due to how we handle constant conditions, those don't get removed in the fetch nodes (which only matter when things are
@@ -190,7 +225,12 @@ describe('Execution tests for @include/@skip', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -210,13 +250,16 @@ describe('Execution tests for @include/@skip', () => {
     });
 
     it('top-level @skip always included', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           t @skip(if: false) {
             id
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // Same as for @include: the @skip within the fetch is not cleared, but that's harmless.
@@ -233,7 +276,12 @@ describe('Execution tests for @include/@skip', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -253,7 +301,9 @@ describe('Execution tests for @include/@skip', () => {
     });
 
     it('Non top-level @include never included', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           t {
             ... on T1 {
@@ -262,7 +312,8 @@ describe('Execution tests for @include/@skip', () => {
             }
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // Importantly, we only bother querying S1
@@ -283,7 +334,12 @@ describe('Execution tests for @include/@skip', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -301,7 +357,9 @@ describe('Execution tests for @include/@skip', () => {
     });
 
     it('Non top-level @skip always skipped', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           t {
             ... on T1 {
@@ -310,7 +368,8 @@ describe('Execution tests for @include/@skip', () => {
             }
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // Importantly, we only bother querying S1
@@ -331,7 +390,12 @@ describe('Execution tests for @include/@skip', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -349,7 +413,9 @@ describe('Execution tests for @include/@skip', () => {
     });
 
     it('Non top-level @include always included', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           t {
             ... on T1 {
@@ -358,7 +424,8 @@ describe('Execution tests for @include/@skip', () => {
             }
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // Again, constantly included is the one case that still show up in the fetch, but that's harmless. The point here is
@@ -397,7 +464,12 @@ describe('Execution tests for @include/@skip', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -417,7 +489,9 @@ describe('Execution tests for @include/@skip', () => {
     });
 
     it('Non top-level @skip always included', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           t {
             ... on T1 {
@@ -426,7 +500,8 @@ describe('Execution tests for @include/@skip', () => {
             }
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // Again, constantly included is the one case that still show up in the fetch, but that's harmless. The point here is
@@ -465,7 +540,12 @@ describe('Execution tests for @include/@skip', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -486,10 +566,14 @@ describe('Execution tests for @include/@skip', () => {
   });
 
   describe('Simple variable conditions handling', () => {
-    const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema(simpleInterfaceSchemas);
+    const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema(
+      simpleInterfaceSchemas,
+    );
 
     it('handles default values for condition variables', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         query ($if: Boolean! = false){
           t {
             id
@@ -498,7 +582,8 @@ describe('Execution tests for @include/@skip', () => {
             }
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // We validate that the condition has been extracted.
@@ -540,7 +625,12 @@ describe('Execution tests for @include/@skip', () => {
 
       s2Queries = [];
       // No variables: the default (not included) should be used.
-      let response = await executePlan(queryPlan, operation, schema, serviceMap);
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -561,7 +651,9 @@ describe('Execution tests for @include/@skip', () => {
 
       s2Queries = [];
       // Checks that the overriding of the default does work.
-      response = await executePlan(queryPlan, operation, schema, serviceMap, { if: true });
+      response = await executePlan(queryPlan, operation, schema, serviceMap, {
+        if: true,
+      });
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -584,7 +676,9 @@ describe('Execution tests for @include/@skip', () => {
     });
 
     it('handles condition on named fragments spread', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         query ($if: Boolean!){
           t {
             id
@@ -595,7 +689,8 @@ describe('Execution tests for @include/@skip', () => {
         fragment GetB1 on T1 {
           b1
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       expect(queryPlan).toMatchInlineSnapshot(`
@@ -635,7 +730,13 @@ describe('Execution tests for @include/@skip', () => {
       `);
 
       s2Queries = [];
-      let response = await executePlan(queryPlan, operation, schema, serviceMap, { if: true });
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+        { if: true },
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -658,7 +759,9 @@ describe('Execution tests for @include/@skip', () => {
 
       s2Queries = [];
       // Checks that the overriding of the default does work.
-      response = await executePlan(queryPlan, operation, schema, serviceMap, { if: false });
+      response = await executePlan(queryPlan, operation, schema, serviceMap, {
+        if: false,
+      });
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -679,7 +782,9 @@ describe('Execution tests for @include/@skip', () => {
     });
 
     it('handles condition inside named fragments', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         query ($if: Boolean!){
           t {
             id
@@ -690,7 +795,8 @@ describe('Execution tests for @include/@skip', () => {
         fragment OtherGetB1 on T1 {
           b1 @include(if: $if)
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       expect(queryPlan).toMatchInlineSnapshot(`
@@ -730,7 +836,13 @@ describe('Execution tests for @include/@skip', () => {
       `);
 
       s2Queries = [];
-      let response = await executePlan(queryPlan, operation, schema, serviceMap, { if: true });
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+        { if: true },
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -753,7 +865,9 @@ describe('Execution tests for @include/@skip', () => {
 
       s2Queries = [];
       // Checks that the overriding of the default does work.
-      response = await executePlan(queryPlan, operation, schema, serviceMap, { if: false });
+      response = await executePlan(queryPlan, operation, schema, serviceMap, {
+        if: false,
+      });
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -772,13 +886,17 @@ describe('Execution tests for @include/@skip', () => {
       `);
       expect(s2Queries).toHaveLength(0);
     });
-  })
+  });
 
   describe('Fetches with multiple top-level conditioned types', () => {
-    const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema(simpleInterfaceSchemas);
+    const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema(
+      simpleInterfaceSchemas,
+    );
 
     it('creates a condition node when a condition covers a whole fetch', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         query ($if: Boolean!){
           t {
             id
@@ -790,7 +908,8 @@ describe('Execution tests for @include/@skip', () => {
             }
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // We validate that the condition has been extracted.
@@ -842,7 +961,13 @@ describe('Execution tests for @include/@skip', () => {
       `);
 
       s2Queries = [];
-      let response = await executePlan(queryPlan, operation, schema, serviceMap, { if: true });
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+        { if: true },
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -866,7 +991,9 @@ describe('Execution tests for @include/@skip', () => {
       expect(s2Queries).toHaveLength(3);
 
       s2Queries = [];
-      response = await executePlan(queryPlan, operation, schema, serviceMap, { if: false });
+      response = await executePlan(queryPlan, operation, schema, serviceMap, {
+        if: false,
+      });
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -888,7 +1015,9 @@ describe('Execution tests for @include/@skip', () => {
     });
 
     it('does _not_ creates a condition node when no single condition covers the whole fetch', async () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         query ($if1: Boolean!, $if2: Boolean!){
           t {
             id
@@ -900,7 +1029,8 @@ describe('Execution tests for @include/@skip', () => {
             }
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // We validate that the condition has been extracted.
@@ -950,7 +1080,13 @@ describe('Execution tests for @include/@skip', () => {
       `);
 
       s2Queries = [];
-      let response = await executePlan(queryPlan, operation, schema, serviceMap, { if1: true, if2: true });
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+        { if1: true, if2: true },
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -974,7 +1110,10 @@ describe('Execution tests for @include/@skip', () => {
       expect(s2Queries).toHaveLength(3);
 
       s2Queries = [];
-      response = await executePlan(queryPlan, operation, schema, serviceMap, { if1: false, if2: false });
+      response = await executePlan(queryPlan, operation, schema, serviceMap, {
+        if1: false,
+        if2: false,
+      });
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -999,7 +1138,10 @@ describe('Execution tests for @include/@skip', () => {
       expect(s2Queries).toHaveLength(3);
 
       s2Queries = [];
-      response = await executePlan(queryPlan, operation, schema, serviceMap, { if1: false, if2: true });
+      response = await executePlan(queryPlan, operation, schema, serviceMap, {
+        if1: false,
+        if2: true,
+      });
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -1024,7 +1166,9 @@ describe('Execution tests for @include/@skip', () => {
 
     it('handles "nested" conditions', async () => {
       // Shows that as long as the condition matches, even "nested" @include gets handled as a condition node.
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         query ($if1: Boolean!, $if2: Boolean!){
           t {
             id
@@ -1036,7 +1180,8 @@ describe('Execution tests for @include/@skip', () => {
             }
           }
         }
-      `);
+      `,
+      );
 
       const queryPlan = queryPlanner.buildQueryPlan(operation);
       // We validate that the condition has been extracted.
@@ -1090,7 +1235,13 @@ describe('Execution tests for @include/@skip', () => {
       `);
 
       s2Queries = [];
-      let response = await executePlan(queryPlan, operation, schema, serviceMap, { if1: true, if2: true });
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        schema,
+        serviceMap,
+        { if1: true, if2: true },
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -1114,7 +1265,10 @@ describe('Execution tests for @include/@skip', () => {
       expect(s2Queries).toHaveLength(3);
 
       s2Queries = [];
-      response = await executePlan(queryPlan, operation, schema, serviceMap, { if1: false, if2: true });
+      response = await executePlan(queryPlan, operation, schema, serviceMap, {
+        if1: false,
+        if2: true,
+      });
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -1135,7 +1289,10 @@ describe('Execution tests for @include/@skip', () => {
       expect(s2Queries).toHaveLength(0);
 
       s2Queries = [];
-      response = await executePlan(queryPlan, operation, schema, serviceMap, { if1: true, if2: false });
+      response = await executePlan(queryPlan, operation, schema, serviceMap, {
+        if1: true,
+        if2: false,
+      });
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -1157,7 +1314,9 @@ describe('Execution tests for @include/@skip', () => {
 
     describe('same element having both @skip and @include', () => {
       it('with constant conditions, neither excluding', async () => {
-        const operation = parseOperation(schema, `
+        const operation = parseOperation(
+          schema,
+          `
           {
             t {
               id
@@ -1166,7 +1325,8 @@ describe('Execution tests for @include/@skip', () => {
               }
             }
           }
-        `);
+        `,
+        );
 
         const queryPlan = queryPlanner.buildQueryPlan(operation);
         expect(queryPlan).toMatchInlineSnapshot(`
@@ -1208,7 +1368,12 @@ describe('Execution tests for @include/@skip', () => {
         `);
 
         s2Queries = [];
-        const response = await executePlan(queryPlan, operation, schema, serviceMap);
+        const response = await executePlan(
+          queryPlan,
+          operation,
+          schema,
+          serviceMap,
+        );
         expect(response.errors).toBeUndefined();
         expect(response.data).toMatchInlineSnapshot(`
           Object {
@@ -1231,7 +1396,9 @@ describe('Execution tests for @include/@skip', () => {
       });
 
       it('with constant conditions, both excluding', async () => {
-        const operation = parseOperation(schema, `
+        const operation = parseOperation(
+          schema,
+          `
           {
             t {
               id
@@ -1240,7 +1407,8 @@ describe('Execution tests for @include/@skip', () => {
               }
             }
           }
-        `);
+        `,
+        );
 
         const queryPlan = queryPlanner.buildQueryPlan(operation);
         expect(queryPlan).toMatchInlineSnapshot(`
@@ -1261,7 +1429,12 @@ describe('Execution tests for @include/@skip', () => {
         `);
 
         s2Queries = [];
-        const response = await executePlan(queryPlan, operation, schema, serviceMap);
+        const response = await executePlan(
+          queryPlan,
+          operation,
+          schema,
+          serviceMap,
+        );
         expect(response.errors).toBeUndefined();
         expect(response.data).toMatchInlineSnapshot(`
           Object {
@@ -1282,7 +1455,9 @@ describe('Execution tests for @include/@skip', () => {
       });
 
       it('with constant conditions, first excluding', async () => {
-        const operation = parseOperation(schema, `
+        const operation = parseOperation(
+          schema,
+          `
           {
             t {
               id
@@ -1291,7 +1466,8 @@ describe('Execution tests for @include/@skip', () => {
               }
             }
           }
-        `);
+        `,
+        );
 
         const queryPlan = queryPlanner.buildQueryPlan(operation);
         expect(queryPlan).toMatchInlineSnapshot(`
@@ -1312,7 +1488,12 @@ describe('Execution tests for @include/@skip', () => {
         `);
 
         s2Queries = [];
-        const response = await executePlan(queryPlan, operation, schema, serviceMap);
+        const response = await executePlan(
+          queryPlan,
+          operation,
+          schema,
+          serviceMap,
+        );
         expect(response.errors).toBeUndefined();
         expect(response.data).toMatchInlineSnapshot(`
           Object {
@@ -1333,7 +1514,9 @@ describe('Execution tests for @include/@skip', () => {
       });
 
       it('with constant conditions, second excluding', async () => {
-        const operation = parseOperation(schema, `
+        const operation = parseOperation(
+          schema,
+          `
           {
             t {
               id
@@ -1342,7 +1525,8 @@ describe('Execution tests for @include/@skip', () => {
               }
             }
           }
-        `);
+        `,
+        );
 
         const queryPlan = queryPlanner.buildQueryPlan(operation);
         expect(queryPlan).toMatchInlineSnapshot(`
@@ -1363,7 +1547,12 @@ describe('Execution tests for @include/@skip', () => {
         `);
 
         s2Queries = [];
-        const response = await executePlan(queryPlan, operation, schema, serviceMap);
+        const response = await executePlan(
+          queryPlan,
+          operation,
+          schema,
+          serviceMap,
+        );
         expect(response.errors).toBeUndefined();
         expect(response.data).toMatchInlineSnapshot(`
           Object {
@@ -1384,7 +1573,9 @@ describe('Execution tests for @include/@skip', () => {
       });
 
       it('with variable conditions', async () => {
-        const operation = parseOperation(schema, `
+        const operation = parseOperation(
+          schema,
+          `
           query ($if1: Boolean!, $if2: Boolean!) {
             t {
               id
@@ -1393,7 +1584,8 @@ describe('Execution tests for @include/@skip', () => {
               }
             }
           }
-        `);
+        `,
+        );
 
         const queryPlan = queryPlanner.buildQueryPlan(operation);
         // Ensures both @skip and @include have condition nodes.
@@ -1441,7 +1633,13 @@ describe('Execution tests for @include/@skip', () => {
 
         s2Queries = [];
         // With data included by both conditions
-        let response = await executePlan(queryPlan, operation, schema, serviceMap, { if1: true, if2: false });
+        let response = await executePlan(
+          queryPlan,
+          operation,
+          schema,
+          serviceMap,
+          { if1: true, if2: false },
+        );
         expect(response.errors).toBeUndefined();
         expect(response.data).toMatchInlineSnapshot(`
           Object {
@@ -1464,7 +1662,10 @@ describe('Execution tests for @include/@skip', () => {
 
         s2Queries = [];
         // With data excluded by one condition
-        response = await executePlan(queryPlan, operation, schema, serviceMap, { if1: true, if2: true });
+        response = await executePlan(queryPlan, operation, schema, serviceMap, {
+          if1: true,
+          if2: true,
+        });
         expect(response.errors).toBeUndefined();
         expect(response.data).toMatchInlineSnapshot(`
           Object {
@@ -1483,6 +1684,6 @@ describe('Execution tests for @include/@skip', () => {
         `);
         expect(s2Queries).toHaveLength(0);
       });
-    })
-  })
+    });
+  });
 });
diff --git a/gateway-js/src/__tests__/executeQueryPlan.introspection.test.ts b/gateway-js/src/__tests__/executeQueryPlan.introspection.test.ts
index 86e2e2baa..8202dcc32 100644
--- a/gateway-js/src/__tests__/executeQueryPlan.introspection.test.ts
+++ b/gateway-js/src/__tests__/executeQueryPlan.introspection.test.ts
@@ -1,13 +1,25 @@
 import gql from 'graphql-tag';
-import { getFederatedTestingSchema, ServiceDefinitionModule } from './execution-utils';
-import { Operation, parseOperation, Schema } from "@apollo/federation-internals";
+import {
+  getFederatedTestingSchema,
+  ServiceDefinitionModule,
+} from './execution-utils';
+import {
+  Operation,
+  parseOperation,
+  Schema,
+} from '@apollo/federation-internals';
 import { QueryPlan } from '@apollo/query-planner';
 import { LocalGraphQLDataSource } from '../datasources';
-import { GatewayExecutionResult, GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
+import {
+  GatewayExecutionResult,
+  GatewayGraphQLRequestContext,
+} from '@apollo/server-gateway-interface';
 import { buildOperationContext } from '../operationContext';
 import { executeQueryPlan } from '../executeQueryPlan';
 
-function buildRequestContext(variables: Record<string, any>): GatewayGraphQLRequestContext {
+function buildRequestContext(
+  variables: Record<string, any>,
+): GatewayGraphQLRequestContext {
   // eslint-disable-next-line @typescript-eslint/ban-ts-comment
   // @ts-ignore
   return {
@@ -30,7 +42,9 @@ async function executePlan(
   const apiSchema = schema.toAPISchema();
   const operationContext = buildOperationContext({
     schema: apiSchema.toGraphQLJSSchema(),
-    operationDocument: gql`${operation.toString()}`,
+    operationDocument: gql`
+      ${operation.toString()}
+    `,
   });
   return executeQueryPlan(
     queryPlan,
@@ -67,20 +81,29 @@ describe('handling of introspection queries', () => {
       `,
     },
   ];
-  const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema(typeDefs);
+  const { serviceMap, schema, queryPlanner } =
+    getFederatedTestingSchema(typeDefs);
 
   it('it handles aliases on introspection fields', async () => {
-    const operation = parseOperation(schema, `
+    const operation = parseOperation(
+      schema,
+      `
       {
         myAlias: __type(name: "T1") {
           kind
           name
         }
       }
-    `);
+    `,
+    );
 
     const queryPlan = queryPlanner.buildQueryPlan(operation);
-    const response = await executePlan(queryPlan, operation, schema, serviceMap);
+    const response = await executePlan(
+      queryPlan,
+      operation,
+      schema,
+      serviceMap,
+    );
     expect(response.errors).toBeUndefined();
     expect(response.data).toMatchInlineSnapshot(`
       Object {
@@ -93,17 +116,25 @@ describe('handling of introspection queries', () => {
   });
 
   it('it handles aliases inside introspection fields', async () => {
-    const operation = parseOperation(schema, `
+    const operation = parseOperation(
+      schema,
+      `
       {
         __type(name: "T1") {
           myKind: kind
           name
         }
       }
-    `);
+    `,
+    );
 
     const queryPlan = queryPlanner.buildQueryPlan(operation);
-    const response = await executePlan(queryPlan, operation, schema, serviceMap);
+    const response = await executePlan(
+      queryPlan,
+      operation,
+      schema,
+      serviceMap,
+    );
     expect(response.errors).toBeUndefined();
     expect(response.data).toMatchInlineSnapshot(`
       Object {
@@ -116,17 +147,26 @@ describe('handling of introspection queries', () => {
   });
 
   it('it handles variables passed to introspection fields', async () => {
-    const operation = parseOperation(schema, `
+    const operation = parseOperation(
+      schema,
+      `
       query ($name: String!) {
         __type(name: $name) {
           kind
           name
         }
       }
-    `);
+    `,
+    );
 
     const queryPlan = queryPlanner.buildQueryPlan(operation);
-    const response = await executePlan(queryPlan, operation, schema, serviceMap, { name: "T1" });
+    const response = await executePlan(
+      queryPlan,
+      operation,
+      schema,
+      serviceMap,
+      { name: 'T1' },
+    );
     expect(response.errors).toBeUndefined();
     expect(response.data).toMatchInlineSnapshot(`
       Object {
diff --git a/gateway-js/src/__tests__/executeQueryPlan.test.ts b/gateway-js/src/__tests__/executeQueryPlan.test.ts
index 99642a458..160f4cf0e 100644
--- a/gateway-js/src/__tests__/executeQueryPlan.test.ts
+++ b/gateway-js/src/__tests__/executeQueryPlan.test.ts
@@ -18,12 +18,21 @@ import { QueryPlan, QueryPlanner } from '@apollo/query-planner';
 import { ApolloGateway } from '..';
 import { ApolloServer } from '@apollo/server';
 import { getFederatedTestingSchema } from './execution-utils';
-import { Schema, Operation, parseOperation, arrayEquals, Supergraph } from '@apollo/federation-internals';
+import {
+  Schema,
+  Operation,
+  parseOperation,
+  arrayEquals,
+  Supergraph,
+} from '@apollo/federation-internals';
 import {
   addResolversToSchema,
   GraphQLResolverMap,
 } from '@apollo/subgraph/src/schema-helper';
-import {GatewayExecutionResult, GatewayGraphQLRequestContext} from '@apollo/server-gateway-interface';
+import {
+  GatewayExecutionResult,
+  GatewayGraphQLRequestContext,
+} from '@apollo/server-gateway-interface';
 import { unwrapSingleResultKind } from './gateway/testUtils';
 
 expect.addSnapshotSerializer(astSerializer);
@@ -35,26 +44,35 @@ describe('executeQueryPlan', () => {
   };
 
   const parseOp = (operation: string, operationSchema?: Schema): Operation => {
-    return parseOperation((operationSchema ?? schema), operation);
-  }
+    return parseOperation(operationSchema ?? schema, operation);
+  };
 
-  const buildPlan = (operation: string | Operation, operationQueryPlanner?: QueryPlanner, operationSchema?: Schema): QueryPlan => {
-    const op = typeof operation === 'string' ? parseOp(operation, operationSchema): operation;
+  const buildPlan = (
+    operation: string | Operation,
+    operationQueryPlanner?: QueryPlanner,
+    operationSchema?: Schema,
+  ): QueryPlan => {
+    const op =
+      typeof operation === 'string'
+        ? parseOp(operation, operationSchema)
+        : operation;
     return (operationQueryPlanner ?? queryPlanner).buildQueryPlan(op);
-  }
+  };
 
   async function executePlan(
     queryPlan: QueryPlan,
     operation: Operation,
     executeRequestContext?: GatewayGraphQLRequestContext,
     executeSchema?: Schema,
-    executeServiceMap?: { [serviceName: string]: LocalGraphQLDataSource }
+    executeServiceMap?: { [serviceName: string]: LocalGraphQLDataSource },
   ): Promise<GatewayExecutionResult> {
     const supergraphSchema = executeSchema ?? schema;
     const apiSchema = supergraphSchema.toAPISchema();
     const operationContext = buildOperationContext({
       schema: apiSchema.toGraphQLJSSchema(),
-      operationDocument: gql`${operation.toString()}`,
+      operationDocument: gql`
+        ${operation.toString()}
+      `,
     });
     return executeQueryPlan(
       queryPlan,
@@ -66,10 +84,13 @@ describe('executeQueryPlan', () => {
     );
   }
 
-  async function executeOperation(operationString: string, requestContext?: GatewayGraphQLRequestContext): Promise<GatewayExecutionResult> {
-      const operation = parseOp(operationString);
-      const queryPlan = buildPlan(operation);
-      return executePlan(queryPlan, operation, requestContext);
+  async function executeOperation(
+    operationString: string,
+    requestContext?: GatewayGraphQLRequestContext,
+  ): Promise<GatewayExecutionResult> {
+    const operation = parseOp(operationString);
+    const queryPlan = buildPlan(operation);
+    return executePlan(queryPlan, operation, requestContext);
   }
 
   function overrideResolversInService(
@@ -155,7 +176,7 @@ describe('executeQueryPlan', () => {
         'errors.0.message',
         'Something went wrong',
       );
-      expect(response).toHaveProperty('errors.0.path', ["me"]);
+      expect(response).toHaveProperty('errors.0.path', ['me']);
       expect(response).toHaveProperty(
         'errors.0.extensions.code',
         'UNAUTHENTICATED',
@@ -1276,7 +1297,12 @@ describe('executeQueryPlan', () => {
       const operation = parseOp(`${getIntrospectionQuery()}`, schema);
       queryPlanner = new QueryPlanner(supergraph);
       const queryPlan = queryPlanner.buildQueryPlan(operation);
-      const response = await executePlan(queryPlan, operation, undefined, schema);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+      );
 
       expect(response.data).toHaveProperty('__schema');
       expect(response.errors).toBeUndefined();
@@ -1310,7 +1336,12 @@ describe('executeQueryPlan', () => {
       queryPlanner = new QueryPlanner(supergraph);
       const queryPlan = queryPlanner.buildQueryPlan(operation);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+      );
       expect(response.data).toBeUndefined();
       expect(response.errors).toMatchInlineSnapshot(`
         Array [
@@ -1390,7 +1421,12 @@ describe('executeQueryPlan', () => {
       queryPlanner = new QueryPlanner(supergraph);
       const queryPlan = queryPlanner.buildQueryPlan(operation);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+      );
 
       expect(response.data?.vehicle).toEqual(null);
       // This kind of error is only found by the post-processing of the gateway, and post-processing errors are currently not returned
@@ -1425,24 +1461,24 @@ describe('executeQueryPlan', () => {
         }
       `;
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
         { name: 'S1', typeDefs: s1 },
-        { name: 'S2', typeDefs: s2 }
+        { name: 'S2', typeDefs: s2 },
       ]);
 
       addResolversToSchema(serviceMap['S1'].schema, {
         Query: {
           getA() {
             return {
-              getA: {}
+              getA: {},
             };
           },
         },
         A: {
           q() {
             return Object.create(null);
-          }
-        }
+          },
+        },
       });
 
       addResolversToSchema(serviceMap['S2'].schema, {
@@ -1453,7 +1489,8 @@ describe('executeQueryPlan', () => {
         },
       });
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           getA {
             q {
@@ -1461,7 +1498,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       const queryPlan = buildPlan(operation, queryPlanner);
 
@@ -1490,7 +1529,13 @@ describe('executeQueryPlan', () => {
         }
         `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
 
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -1501,66 +1546,69 @@ describe('executeQueryPlan', () => {
           },
         }
       `);
-    })
+    });
 
     it('can query other subgraphs when the Query type is the type of a field after a mutation', async () => {
-        const s1 = gql`
-          type Query {
-            one: Int
-          }
+      const s1 = gql`
+        type Query {
+          one: Int
+        }
 
-          type Mutation {
-            mutateSomething: Query
-          }
-        `;
+        type Mutation {
+          mutateSomething: Query
+        }
+      `;
 
-        const s2 = gql`
-          type Query {
-            two: Int
-          }
-        `;
+      const s2 = gql`
+        type Query {
+          two: Int
+        }
+      `;
 
-        const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
-          { name: 'S1', typeDefs: s1 },
-          { name: 'S2', typeDefs: s2 }
-        ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        { name: 'S1', typeDefs: s1 },
+        { name: 'S2', typeDefs: s2 },
+      ]);
 
-        let hasMutated = false;
+      let hasMutated = false;
 
-        addResolversToSchema(serviceMap['S1'].schema, {
-          Query: {
-            one() {
-              return 1;
-            },
+      addResolversToSchema(serviceMap['S1'].schema, {
+        Query: {
+          one() {
+            return 1;
           },
-          Mutation: {
-            mutateSomething() {
-              hasMutated = true;
-              return {};
-            },
-          }
-        });
+        },
+        Mutation: {
+          mutateSomething() {
+            hasMutated = true;
+            return {};
+          },
+        },
+      });
 
-        addResolversToSchema(serviceMap['S2'].schema, {
-          Query: {
-            two() {
-              return 2;
-            },
+      addResolversToSchema(serviceMap['S2'].schema, {
+        Query: {
+          two() {
+            return 2;
           },
-        });
+        },
+      });
 
-        const operation = parseOp(`
+      const operation = parseOp(
+        `
           mutation {
             mutateSomething {
               one
               two
             }
           }
-          `, schema);
+          `,
+        schema,
+      );
 
-        const queryPlan = buildPlan(operation, queryPlanner);
+      const queryPlan = buildPlan(operation, queryPlanner);
 
-        expect(queryPlan).toMatchInlineSnapshot(`
+      expect(queryPlan).toMatchInlineSnapshot(`
           QueryPlan {
             Sequence {
               Fetch(service: "S1") {
@@ -1584,10 +1632,16 @@ describe('executeQueryPlan', () => {
           }
         `);
 
-        const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
 
-        expect(hasMutated).toBeTruthy();
-        expect(response.data).toMatchInlineSnapshot(`
+      expect(hasMutated).toBeTruthy();
+      expect(response.data).toMatchInlineSnapshot(`
           Object {
             "mutateSomething": Object {
               "one": 1,
@@ -1595,7 +1649,7 @@ describe('executeQueryPlan', () => {
             },
           }
         `);
-    })
+    });
 
     it('can mutate other subgraphs when the Mutation type is the type of a field', async () => {
       const s1 = gql`
@@ -1618,9 +1672,9 @@ describe('executeQueryPlan', () => {
         }
       `;
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
         { name: 'S1', typeDefs: s1 },
-        { name: 'S2', typeDefs: s2 }
+        { name: 'S2', typeDefs: s2 },
       ]);
 
       let mutateOneCalled = false;
@@ -1630,21 +1684,21 @@ describe('executeQueryPlan', () => {
         Query: {
           getA() {
             return {
-              getA: {}
+              getA: {},
             };
           },
         },
         A: {
           m() {
             return Object.create(null);
-          }
+          },
         },
         Mutation: {
           mutateOne() {
             mutateOneCalled = true;
             return 1;
-          }
-        }
+          },
+        },
       });
 
       addResolversToSchema(serviceMap['S2'].schema, {
@@ -1656,7 +1710,8 @@ describe('executeQueryPlan', () => {
         },
       });
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           getA {
             m {
@@ -1665,7 +1720,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       const queryPlan = buildPlan(operation, queryPlanner);
 
@@ -1695,7 +1752,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(mutateOneCalled).toBeTruthy();
       expect(mutateTwoCalled).toBeTruthy();
       expect(response.data).toMatchInlineSnapshot(`
@@ -1708,57 +1771,58 @@ describe('executeQueryPlan', () => {
           },
         }
       `);
-    })
+    });
 
     it('can mutate other subgraphs when the Mutation type is the type of a field after a mutation', async () => {
-        const s1 = gql`
-          type Query {
-            one: Int
-          }
+      const s1 = gql`
+        type Query {
+          one: Int
+        }
 
-          type Mutation {
-            mutateSomething: Mutation
-          }
-        `;
+        type Mutation {
+          mutateSomething: Mutation
+        }
+      `;
 
-        const s2 = gql`
-          type Mutation {
-            mutateTwo: Int
-          }
-        `;
+      const s2 = gql`
+        type Mutation {
+          mutateTwo: Int
+        }
+      `;
 
-        const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
-          { name: 'S1', typeDefs: s1 },
-          { name: 'S2', typeDefs: s2 }
-        ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        { name: 'S1', typeDefs: s1 },
+        { name: 'S2', typeDefs: s2 },
+      ]);
 
-        let somethingMutationCount = 0;
-        let hasMutatedTwo = false;
+      let somethingMutationCount = 0;
+      let hasMutatedTwo = false;
 
-        addResolversToSchema(serviceMap['S1'].schema, {
-          Query: {
-            one() {
-              return 1;
-            },
+      addResolversToSchema(serviceMap['S1'].schema, {
+        Query: {
+          one() {
+            return 1;
           },
-          Mutation: {
-            mutateSomething() {
-              ++somethingMutationCount;
-              return {};
-            },
-          }
-        });
+        },
+        Mutation: {
+          mutateSomething() {
+            ++somethingMutationCount;
+            return {};
+          },
+        },
+      });
 
-        addResolversToSchema(serviceMap['S2'].schema, {
-          Mutation: {
-            mutateTwo() {
-              hasMutatedTwo = true;
-              return 2;
-            },
+      addResolversToSchema(serviceMap['S2'].schema, {
+        Mutation: {
+          mutateTwo() {
+            hasMutatedTwo = true;
+            return 2;
           },
-        });
+        },
+      });
 
-        const operation = parseOp(`
+      const operation = parseOp(
+        `
           mutation {
             mutateSomething {
               mutateSomething {
@@ -1766,11 +1830,13 @@ describe('executeQueryPlan', () => {
               }
             }
           }
-          `, schema);
+          `,
+        schema,
+      );
 
-        const queryPlan = buildPlan(operation, queryPlanner);
+      const queryPlan = buildPlan(operation, queryPlanner);
 
-        expect(queryPlan).toMatchInlineSnapshot(`
+      expect(queryPlan).toMatchInlineSnapshot(`
           QueryPlan {
             Sequence {
               Fetch(service: "S1") {
@@ -1795,11 +1861,17 @@ describe('executeQueryPlan', () => {
           }
         `);
 
-        const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
 
-        expect(somethingMutationCount).toBe(2);
-        expect(hasMutatedTwo).toBeTruthy();
-        expect(response.data).toMatchInlineSnapshot(`
+      expect(somethingMutationCount).toBe(2);
+      expect(hasMutatedTwo).toBeTruthy();
+      expect(response.data).toMatchInlineSnapshot(`
           Object {
             "mutateSomething": Object {
               "mutateSomething": Object {
@@ -1808,7 +1880,7 @@ describe('executeQueryPlan', () => {
             },
           }
         `);
-    })
+    });
   });
 
   describe('interfaces on interfaces', () => {
@@ -1847,7 +1919,8 @@ describe('executeQueryPlan', () => {
           c: String
         }
 
-        type T4 implements SubInterface1 & SubInterface2 & TopInterface @key(fields: "a") {
+        type T4 implements SubInterface1 & SubInterface2 & TopInterface
+          @key(fields: "a") {
           a: Int
           b: String @external
           c: String @external
@@ -1867,24 +1940,34 @@ describe('executeQueryPlan', () => {
         }
       `;
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
         { name: 'S1', typeDefs: s1 },
-        { name: 'S2', typeDefs: s2 }
+        { name: 'S2', typeDefs: s2 },
       ]);
 
-      const t1s_s1: any[] = [{ __typename: 'T1', a: 1, b: 'T1_v1'}, {__typename: 'T1', a: 2, b: 'T1_v2'}];
-      const t2s_s1: any[] = [{__typename: 'T2', b: 'k1'}, {__typename: 'T2', b: 'k2'}];
-      const t3s_s1: any[] = [{__typename: 'T3', a: 42, c: 'T3_v1'}];
-      const t4s_s1: any[] = [{__typename: 'T4', a: 0}, {__typename: 'T4', a: 10}, {__typename: 'T4', a: 20}];
+      const t1s_s1: any[] = [
+        { __typename: 'T1', a: 1, b: 'T1_v1' },
+        { __typename: 'T1', a: 2, b: 'T1_v2' },
+      ];
+      const t2s_s1: any[] = [
+        { __typename: 'T2', b: 'k1' },
+        { __typename: 'T2', b: 'k2' },
+      ];
+      const t3s_s1: any[] = [{ __typename: 'T3', a: 42, c: 'T3_v1' }];
+      const t4s_s1: any[] = [
+        { __typename: 'T4', a: 0 },
+        { __typename: 'T4', a: 10 },
+        { __typename: 'T4', a: 20 },
+      ];
 
-      const t2s_s2 = new Map<string, {a: number, b: string}>();
-      t2s_s2.set('k1', {a: 12 , b: 'k1'});
-      t2s_s2.set('k2', {a: 24 , b: 'k2'});
+      const t2s_s2 = new Map<string, { a: number; b: string }>();
+      t2s_s2.set('k1', { a: 12, b: 'k1' });
+      t2s_s2.set('k2', { a: 24, b: 'k2' });
 
-      const t4s_s2 = new Map<number, {a: number, b: string, c: string}>();
-      t4s_s2.set(0, {a: 0, b: 'b_0', c: 'c_0'});
-      t4s_s2.set(10, {a: 10, b: 'b_10', c: 'c_10'});
-      t4s_s2.set(20, {a: 20, b: 'b_20', c: 'c_20'});
+      const t4s_s2 = new Map<number, { a: number; b: string; c: string }>();
+      t4s_s2.set(0, { a: 0, b: 'b_0', c: 'c_0' });
+      t4s_s2.set(10, { a: 10, b: 'b_10', c: 'c_10' });
+      t4s_s2.set(20, { a: 20, b: 'b_20', c: 'c_20' });
 
       addResolversToSchema(serviceMap['S1'].schema, {
         Query: {
@@ -1898,16 +1981,17 @@ describe('executeQueryPlan', () => {
         T2: {
           __resolveReference(ref) {
             return t2s_s2.get(ref.b);
-          }
+          },
         },
         T4: {
           __resolveReference(ref) {
             return t4s_s2.get(ref.a);
-          }
+          },
         },
       });
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           allValues {
             a
@@ -1919,7 +2003,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       const queryPlan = buildPlan(operation, queryPlanner);
 
@@ -1976,7 +2062,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
 
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -2058,7 +2150,8 @@ describe('executeQueryPlan', () => {
           c: String
         }
 
-        type T4 implements SubInterface1 & SubInterface2 & TopInterface @key(fields: "a") {
+        type T4 implements SubInterface1 & SubInterface2 & TopInterface
+          @key(fields: "a") {
           a: Int
           b: String @external
           c: String @external
@@ -2078,24 +2171,34 @@ describe('executeQueryPlan', () => {
         }
       `;
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
         { name: 'S1', typeDefs: s1 },
-        { name: 'S2', typeDefs: s2 }
+        { name: 'S2', typeDefs: s2 },
       ]);
 
-      const t1s_s1: any[] = [{ __typename: 'T1', a: 1, b: 'T1_v1'}, {__typename: 'T1', a: 2, b: 'T1_v2'}];
-      const t2s_s1: any[] = [{__typename: 'T2', a: 12}, {__typename: 'T2', a: 24}];
-      const t3s_s1: any[] = [{__typename: 'T3', a: 42, c: 'T3_v1'}];
-      const t4s_s1: any[] = [{__typename: 'T4', a: 0}, {__typename: 'T4', a: 10}, {__typename: 'T4', a: 20}];
+      const t1s_s1: any[] = [
+        { __typename: 'T1', a: 1, b: 'T1_v1' },
+        { __typename: 'T1', a: 2, b: 'T1_v2' },
+      ];
+      const t2s_s1: any[] = [
+        { __typename: 'T2', a: 12 },
+        { __typename: 'T2', a: 24 },
+      ];
+      const t3s_s1: any[] = [{ __typename: 'T3', a: 42, c: 'T3_v1' }];
+      const t4s_s1: any[] = [
+        { __typename: 'T4', a: 0 },
+        { __typename: 'T4', a: 10 },
+        { __typename: 'T4', a: 20 },
+      ];
 
-      const t2s_s2 = new Map<number, {a: number, b: string}>();
-      t2s_s2.set(12, {a: 12 , b: 'k1'});
-      t2s_s2.set(24, {a: 24 , b: 'k2'});
+      const t2s_s2 = new Map<number, { a: number; b: string }>();
+      t2s_s2.set(12, { a: 12, b: 'k1' });
+      t2s_s2.set(24, { a: 24, b: 'k2' });
 
-      const t4s_s2 = new Map<number, {a: number, b: string, c: string}>();
-      t4s_s2.set(0, {a: 0, b: 'b_0', c: 'c_0'});
-      t4s_s2.set(10, {a: 10, b: 'b_10', c: 'c_10'});
-      t4s_s2.set(20, {a: 20, b: 'b_20', c: 'c_20'});
+      const t4s_s2 = new Map<number, { a: number; b: string; c: string }>();
+      t4s_s2.set(0, { a: 0, b: 'b_0', c: 'c_0' });
+      t4s_s2.set(10, { a: 10, b: 'b_10', c: 'c_10' });
+      t4s_s2.set(20, { a: 20, b: 'b_20', c: 'c_20' });
 
       addResolversToSchema(serviceMap['S1'].schema, {
         Query: {
@@ -2109,22 +2212,25 @@ describe('executeQueryPlan', () => {
         T2: {
           __resolveReference(ref) {
             return t2s_s2.get(ref.b);
-          }
+          },
         },
         T4: {
           __resolveReference(ref) {
             return t4s_s2.get(ref.a);
-          }
+          },
         },
       });
 
-      let operation = parseOp(`
+      let operation = parseOp(
+        `
         query {
           allValues {
             a
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       let queryPlan = buildPlan(operation, queryPlanner);
 
@@ -2141,7 +2247,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.data).toMatchInlineSnapshot(`
         Object {
           "allValues": Array [
@@ -2173,7 +2285,8 @@ describe('executeQueryPlan', () => {
         }
         `);
 
-      operation = parseOp(`
+      operation = parseOp(
+        `
         query {
           allValues {
             ... on SubInterface1 {
@@ -2181,7 +2294,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       queryPlan = buildPlan(operation, queryPlanner);
 
@@ -2209,7 +2324,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.data).toMatchInlineSnapshot(`
         Object {
           "allValues": Array [
@@ -2261,8 +2382,8 @@ describe('executeQueryPlan', () => {
         type MyTypeB implements MyInterface {
           name: String
         }
-      `
-    }
+      `,
+    };
 
     const s2 = {
       name: 'S2',
@@ -2274,15 +2395,18 @@ describe('executeQueryPlan', () => {
         type MyTypeC implements MyInterface {
           name: String
         }
-      `
-    }
+      `,
+    };
 
-    const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+    const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+      s1,
+      s2,
+    ]);
 
     addResolversToSchema(serviceMap['S1'].schema, {
       Query: {
         myField() {
-          return { __typename: 'MyTypeA', name: "foo" };
+          return { __typename: 'MyTypeA', name: 'foo' };
         },
       },
     });
@@ -2290,13 +2414,16 @@ describe('executeQueryPlan', () => {
     // First, we just query the field without conditions.
     // Note that there is no reason to type-explode this: clearly, `myField` will never return a `MyTypeC` since
     // it's resolved by S1 which doesn't know that type, but that doesn't impact the plan.
-    let operation = parseOp(`
+    let operation = parseOp(
+      `
       query {
         myField {
           name
         }
       }
-      `, schema);
+      `,
+      schema,
+    );
     let queryPlan = buildPlan(operation, queryPlanner);
     expect(queryPlan).toMatchInlineSnapshot(`
       QueryPlan {
@@ -2310,7 +2437,13 @@ describe('executeQueryPlan', () => {
         },
       }
     `);
-    let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+    let response = await executePlan(
+      queryPlan,
+      operation,
+      undefined,
+      schema,
+      serviceMap,
+    );
     expect(response.data).toMatchInlineSnapshot(`
       Object {
         "myField": Object {
@@ -2321,7 +2454,8 @@ describe('executeQueryPlan', () => {
 
     // Now forcing the query planning to notice that `MyTypeC` can never happen and making
     // sure it doesn't ask it from S1, which doesn't know it.
-    operation = parseOp(`
+    operation = parseOp(
+      `
       query {
         myField {
           ... on MyTypeA {
@@ -2332,7 +2466,9 @@ describe('executeQueryPlan', () => {
           }
         }
       }
-      `, schema);
+      `,
+      schema,
+    );
     queryPlan = buildPlan(operation, queryPlanner);
     expect(queryPlan).toMatchInlineSnapshot(`
       QueryPlan {
@@ -2349,7 +2485,13 @@ describe('executeQueryPlan', () => {
       }
     `);
 
-    response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+    response = await executePlan(
+      queryPlan,
+      operation,
+      undefined,
+      schema,
+      serviceMap,
+    );
     expect(response.data).toMatchInlineSnapshot(`
       Object {
         "myField": Object {
@@ -2358,10 +2500,10 @@ describe('executeQueryPlan', () => {
       }
       `);
 
-
     // Testing only getting name for `MyTypeB`, which is known by S1, but not returned
     // by `myField` in practice (so the result is "empty").
-    operation = parseOp(`
+    operation = parseOp(
+      `
       query {
         myField {
           ... on MyTypeB {
@@ -2369,7 +2511,9 @@ describe('executeQueryPlan', () => {
           }
         }
       }
-      `, schema);
+      `,
+      schema,
+    );
 
     queryPlan = buildPlan(operation, queryPlanner);
     expect(queryPlan).toMatchInlineSnapshot(`
@@ -2386,14 +2530,21 @@ describe('executeQueryPlan', () => {
         },
       }
     `);
-    response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+    response = await executePlan(
+      queryPlan,
+      operation,
+      undefined,
+      schema,
+      serviceMap,
+    );
     expect(response.data).toMatchInlineSnapshot(`
       Object {
         "myField": Object {},
       }
       `);
 
-    operation = parseOp(`
+    operation = parseOp(
+      `
       query {
         myField {
           ... on MyTypeC {
@@ -2401,7 +2552,9 @@ describe('executeQueryPlan', () => {
           }
         }
       }
-      `, schema);
+      `,
+      schema,
+    );
 
     // Lastly, same with only getting name for `MyTypeC`. It isn't known by S1 so the condition should not
     // be included in the query, but we should still query `myField` to know if it resolve to "something"
@@ -2421,7 +2574,13 @@ describe('executeQueryPlan', () => {
       }
     `);
 
-    response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+    response = await executePlan(
+      queryPlan,
+      operation,
+      undefined,
+      schema,
+      serviceMap,
+    );
     expect(response.data).toMatchInlineSnapshot(`
       Object {
         "myField": Object {},
@@ -2432,9 +2591,9 @@ describe('executeQueryPlan', () => {
   describe('@requires', () => {
     test('handles null in required field correctly (with nullable fields)', async () => {
       const s1_data = [
-        { id: 0, f1: "foo" },
+        { id: 0, f1: 'foo' },
         { id: 1, f1: null },
-        { id: 2, f1: "bar" },
+        { id: 2, f1: 'bar' },
       ];
 
       const s1 = {
@@ -2447,12 +2606,12 @@ describe('executeQueryPlan', () => {
         `,
         resolvers: {
           T1: {
-            __resolveReference(ref: {id: number}) {
+            __resolveReference(ref: { id: number }) {
               return s1_data[ref.id];
             },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -2474,7 +2633,7 @@ describe('executeQueryPlan', () => {
         resolvers: {
           Query: {
             getT1s() {
-              return [{id: 0}, {id: 1}, {id: 2}];
+              return [{ id: 0 }, { id: 1 }, { id: 2 }];
             },
           },
           T1: {
@@ -2484,14 +2643,18 @@ describe('executeQueryPlan', () => {
             },
             f2(o: { f1: string }) {
               return o.f1 === null ? null : { a: `t1:${o.f1}` };
-            }
-          }
-        }
-      }
+            },
+          },
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           getT1s {
             id
@@ -2501,7 +2664,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
@@ -2550,7 +2715,13 @@ describe('executeQueryPlan', () => {
           },
         }
       `);
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.data).toMatchInlineSnapshot(`
         Object {
           "getT1s": Array [
@@ -2581,9 +2752,9 @@ describe('executeQueryPlan', () => {
 
     test('handles null in required field correctly (with @require field non-nullable)', async () => {
       const s1_data = [
-        { id: 0, f1: "foo" },
+        { id: 0, f1: 'foo' },
         { id: 1, f1: null },
-        { id: 2, f1: "bar" },
+        { id: 2, f1: 'bar' },
       ];
 
       const s1 = {
@@ -2600,8 +2771,8 @@ describe('executeQueryPlan', () => {
               return s1_data[ref.id];
             },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -2623,7 +2794,7 @@ describe('executeQueryPlan', () => {
         resolvers: {
           Query: {
             getT1s() {
-              return [{id: 0}, {id: 1}, {id: 2}];
+              return [{ id: 0 }, { id: 1 }, { id: 2 }];
             },
           },
           T1: {
@@ -2633,14 +2804,18 @@ describe('executeQueryPlan', () => {
             },
             f2(o: { f1: string }) {
               return o.f1 === null ? null : { a: `t1:${o.f1}` };
-            }
-          }
-        }
-      }
+            },
+          },
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           getT1s {
             id
@@ -2650,7 +2825,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
@@ -2700,7 +2877,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       // `null` should bubble up since `f2` is now non-nullable. But we should still get the `id: 0` response.
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -2725,7 +2908,9 @@ describe('executeQueryPlan', () => {
         `);
 
       // We returning `null` for f2 which isn't nullable, so it bubbled up and we should have an error
-      expect(response.errors?.map((e) => e.message)).toStrictEqual(['Cannot return null for non-nullable field T1.f2.']);
+      expect(response.errors?.map((e) => e.message)).toStrictEqual([
+        'Cannot return null for non-nullable field T1.f2.',
+      ]);
     });
 
     test('handles null in required field correctly (with non-nullable required field)', async () => {
@@ -2739,12 +2924,12 @@ describe('executeQueryPlan', () => {
         `,
         resolvers: {
           T1: {
-            __resolveReference(ref: { id: number}) {
+            __resolveReference(ref: { id: number }) {
               return s1_data[ref.id];
             },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -2766,7 +2951,7 @@ describe('executeQueryPlan', () => {
         resolvers: {
           Query: {
             getT1s() {
-              return [{id: 0}, {id: 1}, {id: 2}];
+              return [{ id: 0 }, { id: 1 }, { id: 2 }];
             },
           },
           T1: {
@@ -2776,20 +2961,24 @@ describe('executeQueryPlan', () => {
             },
             f2(o: { f1: string }) {
               return o.f1 === null ? null : { a: `t1:${o.f1}` };
-            }
-          }
-        }
-      }
+            },
+          },
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
       const s1_data = [
-        { id: 0, f1: "foo" },
+        { id: 0, f1: 'foo' },
         { id: 1, f1: null },
-        { id: 2, f1: "bar" },
+        { id: 2, f1: 'bar' },
       ];
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           getT1s {
             id
@@ -2799,7 +2988,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
@@ -2849,7 +3040,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       // `null` should bubble up since `f2` is now non-nullable. But we should still get the `id: 0` response.
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -2872,7 +3069,9 @@ describe('executeQueryPlan', () => {
           ],
         }
         `);
-      expect(response.errors?.map((e) => e.message)).toStrictEqual(['Cannot return null for non-nullable field T1.f1.']);
+      expect(response.errors?.map((e) => e.message)).toStrictEqual([
+        'Cannot return null for non-nullable field T1.f1.',
+      ]);
     });
 
     test('handles errors in required field correctly (with nullable fields)', async () => {
@@ -2891,15 +3090,19 @@ describe('executeQueryPlan', () => {
             },
             f1(o: { id: number }) {
               switch (o.id) {
-                case 0: return "foo";
-                case 1: return [ "invalid" ]; // This will effectively throw
-                case 2: return "bar";
-                default: throw new Error('Not handled');
+                case 0:
+                  return 'foo';
+                case 1:
+                  return ['invalid']; // This will effectively throw
+                case 2:
+                  return 'bar';
+                default:
+                  throw new Error('Not handled');
               }
-            }
+            },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -2921,7 +3124,7 @@ describe('executeQueryPlan', () => {
         resolvers: {
           Query: {
             getT1s() {
-              return [{id: 0}, {id: 1}, {id: 2}];
+              return [{ id: 0 }, { id: 1 }, { id: 2 }];
             },
           },
           T1: {
@@ -2931,14 +3134,18 @@ describe('executeQueryPlan', () => {
             },
             f2(o: { f1: string }) {
               return o.f1 === null ? null : { a: `t1:${o.f1}` };
-            }
-          }
-        }
-      }
+            },
+          },
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           getT1s {
             id
@@ -2948,7 +3155,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
@@ -2997,7 +3206,13 @@ describe('executeQueryPlan', () => {
           },
         }
       `);
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.data).toMatchInlineSnapshot(`
         Object {
           "getT1s": Array [
@@ -3023,7 +3238,9 @@ describe('executeQueryPlan', () => {
           ],
         }
         `);
-      expect(response.errors?.map((e) => e.message)).toStrictEqual(['String cannot represent value: ["invalid"]']);
+      expect(response.errors?.map((e) => e.message)).toStrictEqual([
+        'String cannot represent value: ["invalid"]',
+      ]);
     });
 
     test('ensures type condition on inaccessible type in @require works correctly', async () => {
@@ -3031,7 +3248,10 @@ describe('executeQueryPlan', () => {
         name: 'data',
         typeDefs: gql`
           extend schema
-          @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.0"
+              import: ["@key", "@shareable"]
+            )
 
           type Entity @key(fields: "id") {
             id: ID!
@@ -3053,29 +3273,28 @@ describe('executeQueryPlan', () => {
           }
         `,
         resolvers: {
-            Query: {
-              dummy() {
-                return {};
-              },
+          Query: {
+            dummy() {
+              return {};
             },
-            Entity: {
-              __resolveReference() {
-                return {};
-              },
-              id() {
-                return "id";
-              },
-              data() {
-                return {
-                  __typename: "Data",
-                  foo: "foo",
-                  bar: "bar",
-                };
-              },
+          },
+          Entity: {
+            __resolveReference() {
+              return {};
             },
-
-        }
-      }
+            id() {
+              return 'id';
+            },
+            data() {
+              return {
+                __typename: 'Data',
+                foo: 'foo',
+                bar: 'bar',
+              };
+            },
+          },
+        },
+      };
 
       let requirerRepresentation: any = undefined;
 
@@ -3083,10 +3302,16 @@ describe('executeQueryPlan', () => {
         name: 'requirer',
         typeDefs: gql`
           extend schema
-          @link(
-            url: "https://specs.apollo.dev/federation/v2.0",
-            import: ["@key", "@shareable", "@external", "@requires", "@inaccessible"]
-          )
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.0"
+              import: [
+                "@key"
+                "@shareable"
+                "@external"
+                "@requires"
+                "@inaccessible"
+              ]
+            )
 
           type Query {
             dummy: Entity
@@ -3095,7 +3320,8 @@ describe('executeQueryPlan', () => {
           type Entity @key(fields: "id") {
             id: ID!
             data: Foo @external
-            requirer: String! @requires(fields: "data { foo ... on Bar { bar } }")
+            requirer: String!
+              @requires(fields: "data { foo ... on Bar { bar } }")
           }
 
           interface Foo {
@@ -3124,24 +3350,30 @@ describe('executeQueryPlan', () => {
               return {};
             },
             id() {
-              return "id";
+              return 'id';
             },
             requirer() {
-              return "requirer";
+              return 'requirer';
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           dummy {
             requirer
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
@@ -3202,7 +3434,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.data).toMatchInlineSnapshot(`
         Object {
           "dummy": Object {
@@ -3259,14 +3497,16 @@ describe('executeQueryPlan', () => {
           },
           One: {
             __resolveReference(ref: { id: string }) {
-              return ref.id === entityOne.id ? { ...entityOne, ...ref } : undefined;
+              return ref.id === entityOne.id
+                ? { ...entityOne, ...ref }
+                : undefined;
             },
             computed(parent: any) {
               return `computed value: ${parent.two.external}`;
             },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -3282,18 +3522,24 @@ describe('executeQueryPlan', () => {
               return ref.id === entityTwo.id ? entityTwo : undefined;
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         {
           one {
             computed
           }
         }
-      `, schema);
+      `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
@@ -3346,7 +3592,13 @@ describe('executeQueryPlan', () => {
           },
         }
       `);
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -3406,40 +3658,43 @@ describe('executeQueryPlan', () => {
         defaultValue: 24,
         valuePassed: 42,
       },
-    ])('requires on field with argument: $name', async ({
-      argNullable,
-      defaultValue,
-      valuePassed,
-    }: {
-      argNullable: boolean,
-      defaultValue?: number,
-      valuePassed?: number,
-    }) => {
-
-      const argType = `Int${argNullable ? '' : '!'}${defaultValue ? ` = ${defaultValue}` : ''}`;
-      const s1 = {
-        name: 'S1',
-        typeDefs: gql`
+    ])(
+      'requires on field with argument: $name',
+      async ({
+        argNullable,
+        defaultValue,
+        valuePassed,
+      }: {
+        argNullable: boolean;
+        defaultValue?: number;
+        valuePassed?: number;
+      }) => {
+        const argType = `Int${argNullable ? '' : '!'}${
+          defaultValue ? ` = ${defaultValue}` : ''
+        }`;
+        const s1 = {
+          name: 'S1',
+          typeDefs: gql`
           type T @key(fields: "id") {
             id: Int!
             x(opt: ${argType}): String!
           }
         `,
-        resolvers: {
-          T: {
-            __resolveReference(ref: { id: number}) {
-              return ref;
+          resolvers: {
+            T: {
+              __resolveReference(ref: { id: number }) {
+                return ref;
+              },
+              x(_: any, args: any) {
+                return `args: ${JSON.stringify(args)}`;
+              },
             },
-            x(_: any, args: any) {
-              return `args: ${JSON.stringify(args)}`;
-            }
           },
-        }
-      }
+        };
 
-      const s2 = {
-        name: 'S2',
-        typeDefs: gql`
+        const s2 = {
+          name: 'S2',
+          typeDefs: gql`
           type Query {
             t: T
           }
@@ -3447,49 +3702,57 @@ describe('executeQueryPlan', () => {
           type T @key(fields: "id") {
             id: Int!
             x(opt: ${argType}): String! @external
-            y: String @requires(fields: "x${valuePassed ? `(opt: ${valuePassed})` : ''}")
+            y: String @requires(fields: "x${
+              valuePassed ? `(opt: ${valuePassed})` : ''
+            }")
           }
 
         `,
-        resolvers: {
-          Query: {
-            t() {
-              return {id: 0};
+          resolvers: {
+            Query: {
+              t() {
+                return { id: 0 };
+              },
             },
-          },
-          T: {
-            __resolveReference(ref: { id: number }) {
-              // the ref has already the id and f1 is a require is triggered, and we resolve f2 below
-              return ref;
+            T: {
+              __resolveReference(ref: { id: number }) {
+                // the ref has already the id and f1 is a require is triggered, and we resolve f2 below
+                return ref;
+              },
+              y(parent: any) {
+                return `x: ${parent.x}`;
+              },
             },
-            y(parent: any) {
-              return `x: ${parent.x}`;
-            }
-          }
-        }
-      }
+          },
+        };
 
-      if (!argNullable && !defaultValue && !valuePassed) {
-        // We test all combination of `argNullable`, `defaultValue` set/unset and `valuePassed` set/unset, and all should be allowed
-        // except if the value is non-nullable and has neither a default nor a value passed. In that case, just ensure the error message
-        // is meaningful.
-        expect(() => getFederatedTestingSchema([ s1, s2 ])).toThrowError(
-          '[S2] On field "T.y", for @requires(fields: "x"): Missing mandatory value for argument "opt" of field "T.x" in selection "x"'
-        );
-        return;
-      }
+        if (!argNullable && !defaultValue && !valuePassed) {
+          // We test all combination of `argNullable`, `defaultValue` set/unset and `valuePassed` set/unset, and all should be allowed
+          // except if the value is non-nullable and has neither a default nor a value passed. In that case, just ensure the error message
+          // is meaningful.
+          expect(() => getFederatedTestingSchema([s1, s2])).toThrowError(
+            '[S2] On field "T.y", for @requires(fields: "x"): Missing mandatory value for argument "opt" of field "T.x" in selection "x"',
+          );
+          return;
+        }
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+        const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+          s1,
+          s2,
+        ]);
 
-      const operation = parseOp(`
+        const operation = parseOp(
+          `
         {
           t {
             y
           }
         }
-        `, schema);
-      const queryPlan = buildPlan(operation, queryPlanner);
-      expect(queryPlan).toMatchInlineSnapshot(`
+        `,
+          schema,
+        );
+        const queryPlan = buildPlan(operation, queryPlanner);
+        expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
           Sequence {
             Fetch(service: "S2") {
@@ -3535,96 +3798,216 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
-      expect(response.errors).toBeUndefined();
-      expect(response.data).toMatchInlineSnapshot(`
+        const response = await executePlan(
+          queryPlan,
+          operation,
+          undefined,
+          schema,
+          serviceMap,
+        );
+        expect(response.errors).toBeUndefined();
+        expect(response.data).toMatchInlineSnapshot(`
         Object {
           "t": Object {
-            "y": "x: args: {${valuePassed ? `\\"opt\\":${valuePassed}` : (defaultValue ? `\\"opt\\":${defaultValue}` : '')}}",
+            "y": "x: args: {${
+              valuePassed
+                ? `\\"opt\\":${valuePassed}`
+                : defaultValue
+                ? `\\"opt\\":${defaultValue}`
+                : ''
+            }}",
           },
         }
       `);
-    });
-  });
+      },
+    );
 
-  describe('@key', () => {
-    test('Works on a list of scalar', async () => {
-      const s1_data = [
-        { id: [0, 1], f1: "foo" },
-        { id: [2, 3], f1: "bar" },
-        { id: [4, 5], f1: "baz" },
+    test('requires with nested field example from #2683 is fixed', async () => {
+      const parentItems = [
+        { __typename: 'ParentItem', id: '1', name: 'Parent Item #1' },
+        { __typename: 'ParentItem', id: '2', name: 'Parent Item #2' },
+        { __typename: 'ParentItem', id: '3', name: 'Parent Item #3' },
+      ];
+
+      const childItems = [
+        { __typename: 'ChildItem', id: '1', name: 'Child Item #1' },
+        { __typename: 'ChildItem', id: '2', name: 'Child Item #2' },
+        { __typename: 'ChildItem', id: '3', name: 'Child Item #3' },
       ];
 
       const s1 = {
         name: 'S1',
         typeDefs: gql`
-          type T1 @key(fields: "id") {
-            id: [Int]
-            f1: String
+          type Query {
+            parentItems: [ParentItem!]!
+          }
+
+          type ParentItem @key(fields: "id") {
+            id: ID!
+            name: String!
           }
         `,
         resolvers: {
-          T1: {
-            __resolveReference(ref: { id: number[] }) {
-              return s1_data.find((e) => arrayEquals(e.id, ref.id));
+          Query: {
+            parentItems() {
+              return parentItems;
             },
           },
-        }
-      }
+          ParentItem: {
+            __resolveReference(ref: { id: string }) {
+              return parentItems.find(({ id }) => ref.id === id);
+            },
+          },
+        },
+      };
 
       const s2 = {
         name: 'S2',
         typeDefs: gql`
-          type Query {
-            getT1s: [T1]
+          type ChildItem @key(fields: "id") {
+            id: ID!
+            name: String!
+            parentItem: ParentItem
           }
 
-          type T1 {
-            id: [Int]
+          type ParentItem @key(fields: "id") {
+            id: ID!
+            childItems: [ChildItem!]!
           }
         `,
         resolvers: {
-          Query: {
-            getT1s() {
-              return [{id: [2, 3]}, {id: [4, 5]}];
+          ChildItem: {
+            __resolveReference(ref: { id: string }) {
+              return childItems.find(({ id }) => ref.id === id);
+            },
+            parentItem(ref: { id: string }) {
+              return parentItems.find(({ id }) => ref.id === id);
             },
           },
-        }
-      }
+          ParentItem: {
+            __resolveReference(ref: { id: string }) {
+              return parentItems.find(({ id }) => ref.id === id);
+            },
+            childItems(ref: { id: string }) {
+              return [childItems.find(({ id }) => ref.id === id)];
+            },
+          },
+        },
+      };
+
+      const s3 = {
+        name: 'S3',
+        typeDefs: gql`
+          type ChildItem @key(fields: "id") {
+            id: ID!
+            name: String! @external
+            parentItem: ParentItem @external
+            message: String! @requires(fields: "name parentItem { name }")
+          }
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+          type ParentItem {
+            name: String! @external
+          }
+        `,
+        resolvers: {
+          ChildItem: {
+            __resolveReference(ref: {
+              id: string;
+              name: string;
+              parentItem: { name: string };
+            }) {
+              return {
+                ...ref,
+                message: `${ref.parentItem.name} | ${ref.name}`,
+              };
+            },
+          },
+        },
+      };
 
-      const operation = parseOp(`
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+        s3,
+      ]);
+      let operation = parseOp(
+        `
         query {
-          getT1s {
-            id
-            f1
+          parentItems {
+            childItems {
+              message
+            }
           }
         }
-        `, schema);
-      const queryPlan = buildPlan(operation, queryPlanner);
+      `,
+        schema,
+      );
+
+      let queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
           Sequence {
-            Fetch(service: "S2") {
+            Fetch(service: "S1") {
               {
-                getT1s {
+                parentItems {
                   __typename
                   id
                 }
               }
             },
-            Flatten(path: "getT1s.@") {
+            Flatten(path: "parentItems.@") {
+              Fetch(service: "S2") {
+                {
+                  ... on ParentItem {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on ParentItem {
+                    childItems {
+                      __typename
+                      id
+                      name
+                      parentItem {
+                        __typename
+                        id
+                      }
+                    }
+                  }
+                }
+              },
+            },
+            Flatten(path: "parentItems.@.childItems.@.parentItem") {
               Fetch(service: "S1") {
                 {
-                  ... on T1 {
+                  ... on ParentItem {
                     __typename
                     id
                   }
                 } =>
                 {
-                  ... on T1 {
-                    f1
+                  ... on ParentItem {
+                    name
+                  }
+                }
+              },
+            },
+            Flatten(path: "parentItems.@.childItems.@") {
+              Fetch(service: "S3") {
+                {
+                  ... on ChildItem {
+                    __typename
+                    name
+                    parentItem {
+                      name
+                    }
+                    id
+                  }
+                } =>
+                {
+                  ... on ChildItem {
+                    message
                   }
                 }
               },
@@ -3632,59 +4015,228 @@ describe('executeQueryPlan', () => {
           },
         }
       `);
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+      expect(response.errors).toBeUndefined();
+      expect(response.data).toMatchInlineSnapshot(`
+        Object {
+          "parentItems": Array [
+            Object {
+              "childItems": Array [
+                Object {
+                  "message": "Parent Item #1 | Child Item #1",
+                },
+              ],
+            },
+            Object {
+              "childItems": Array [
+                Object {
+                  "message": "Parent Item #2 | Child Item #2",
+                },
+              ],
+            },
+            Object {
+              "childItems": Array [
+                Object {
+                  "message": "Parent Item #3 | Child Item #3",
+                },
+              ],
+            },
+          ],
+        }
+      `);
+
+      operation = parseOp(
+        `
+        query {
+          parentItems {
+            childItems {
+              ParentItem: parentItem {
+                name
+                id
+              }
+              message
+            }
+          }
+        }
+      `,
+        schema,
+      );
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      queryPlan = buildPlan(operation, queryPlanner);
+      expect(queryPlan).toMatchInlineSnapshot(`
+        QueryPlan {
+          Sequence {
+            Fetch(service: "S1") {
+              {
+                parentItems {
+                  __typename
+                  id
+                }
+              }
+            },
+            Flatten(path: "parentItems.@") {
+              Fetch(service: "S2") {
+                {
+                  ... on ParentItem {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on ParentItem {
+                    childItems {
+                      __typename
+                      id
+                      ParentItem: parentItem {
+                        __typename
+                        id
+                      }
+                      name
+                      parentItem {
+                        __typename
+                        id
+                      }
+                    }
+                  }
+                }
+              },
+            },
+            Parallel {
+              Flatten(path: "parentItems.@.childItems.@.ParentItem") {
+                Fetch(service: "S1") {
+                  {
+                    ... on ParentItem {
+                      __typename
+                      id
+                    }
+                  } =>
+                  {
+                    ... on ParentItem {
+                      name
+                    }
+                  }
+                },
+              },
+              Sequence {
+                Flatten(path: "parentItems.@.childItems.@.parentItem") {
+                  Fetch(service: "S1") {
+                    {
+                      ... on ParentItem {
+                        __typename
+                        id
+                      }
+                    } =>
+                    {
+                      ... on ParentItem {
+                        name
+                      }
+                    }
+                  },
+                },
+                Flatten(path: "parentItems.@.childItems.@") {
+                  Fetch(service: "S3") {
+                    {
+                      ... on ChildItem {
+                        __typename
+                        name
+                        parentItem {
+                          name
+                        }
+                        id
+                      }
+                    } =>
+                    {
+                      ... on ChildItem {
+                        message
+                      }
+                    }
+                  },
+                },
+              },
+            },
+          },
+        }
+      `);
+      response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+      expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
-          "getT1s": Array [
+          "parentItems": Array [
             Object {
-              "f1": "bar",
-              "id": Array [
-                2,
-                3,
+              "childItems": Array [
+                Object {
+                  "ParentItem": Object {
+                    "id": "1",
+                    "name": "Parent Item #1",
+                  },
+                  "message": "Parent Item #1 | Child Item #1",
+                },
               ],
             },
             Object {
-              "f1": "baz",
-              "id": Array [
-                4,
-                5,
+              "childItems": Array [
+                Object {
+                  "ParentItem": Object {
+                    "id": "2",
+                    "name": "Parent Item #2",
+                  },
+                  "message": "Parent Item #2 | Child Item #2",
+                },
+              ],
+            },
+            Object {
+              "childItems": Array [
+                Object {
+                  "ParentItem": Object {
+                    "id": "3",
+                    "name": "Parent Item #3",
+                  },
+                  "message": "Parent Item #3 | Child Item #3",
+                },
               ],
             },
           ],
         }
-        `);
+      `);
     });
+  });
 
-    test('Works on a list of objects', async () => {
+  describe('@key', () => {
+    test('Works on a list of scalar', async () => {
       const s1_data = [
-        { o: [{a: 0, b: "b0", c: "zero"}, {a: 1, b: "b1", c: "one"}], f1: "foo" },
-        { o: [{a: 2, b: "b2", c: "two"}], f1: "bar" },
-        { o: [{a: 3, b: "b3", c: "three"}, {a: 4, b: "b4", c: "four"}], f1: "baz" },
+        { id: [0, 1], f1: 'foo' },
+        { id: [2, 3], f1: 'bar' },
+        { id: [4, 5], f1: 'baz' },
       ];
 
       const s1 = {
         name: 'S1',
         typeDefs: gql`
-          type T1 @key(fields: "o { a c }") {
-            o: [O]
+          type T1 @key(fields: "id") {
+            id: [Int]
             f1: String
           }
-
-          type O {
-            a: Int
-            b: String
-            c: String
-          }
         `,
         resolvers: {
           T1: {
-            __resolveReference(ref: { o: {a : number, c: string}[] }) {
-              return s1_data.find((e) => arrayEquals(e.o, ref.o, (x, y) => x.a === y.a && x.c === y.c));
+            __resolveReference(ref: { id: number[] }) {
+              return s1_data.find((e) => arrayEquals(e.id, ref.id));
             },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -3694,27 +4246,179 @@ describe('executeQueryPlan', () => {
           }
 
           type T1 {
-            o: [O]
-          }
-
-          type O {
-            a: Int
-            b: String
-            c: String
+            id: [Int]
           }
         `,
         resolvers: {
           Query: {
             getT1s() {
-              return [{o: [{a: 2, b: "b2", c: "two"}]}, {o: [{a: 3, b: "b3", c: "three"}, {a: 4, b: "b4", c: "four"}]}];
+              return [{ id: [2, 3] }, { id: [4, 5] }];
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
+        query {
+          getT1s {
+            id
+            f1
+          }
+        }
+        `,
+        schema,
+      );
+      const queryPlan = buildPlan(operation, queryPlanner);
+      expect(queryPlan).toMatchInlineSnapshot(`
+        QueryPlan {
+          Sequence {
+            Fetch(service: "S2") {
+              {
+                getT1s {
+                  __typename
+                  id
+                }
+              }
+            },
+            Flatten(path: "getT1s.@") {
+              Fetch(service: "S1") {
+                {
+                  ... on T1 {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on T1 {
+                    f1
+                  }
+                }
+              },
+            },
+          },
+        }
+      `);
+
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+      expect(response.data).toMatchInlineSnapshot(`
+        Object {
+          "getT1s": Array [
+            Object {
+              "f1": "bar",
+              "id": Array [
+                2,
+                3,
+              ],
+            },
+            Object {
+              "f1": "baz",
+              "id": Array [
+                4,
+                5,
+              ],
+            },
+          ],
+        }
+        `);
+    });
+
+    test('Works on a list of objects', async () => {
+      const s1_data = [
+        {
+          o: [
+            { a: 0, b: 'b0', c: 'zero' },
+            { a: 1, b: 'b1', c: 'one' },
+          ],
+          f1: 'foo',
+        },
+        { o: [{ a: 2, b: 'b2', c: 'two' }], f1: 'bar' },
+        {
+          o: [
+            { a: 3, b: 'b3', c: 'three' },
+            { a: 4, b: 'b4', c: 'four' },
+          ],
+          f1: 'baz',
+        },
+      ];
+
+      const s1 = {
+        name: 'S1',
+        typeDefs: gql`
+          type T1 @key(fields: "o { a c }") {
+            o: [O]
+            f1: String
+          }
+
+          type O {
+            a: Int
+            b: String
+            c: String
+          }
+        `,
+        resolvers: {
+          T1: {
+            __resolveReference(ref: { o: { a: number; c: string }[] }) {
+              return s1_data.find((e) =>
+                arrayEquals(e.o, ref.o, (x, y) => x.a === y.a && x.c === y.c),
+              );
+            },
+          },
+        },
+      };
+
+      const s2 = {
+        name: 'S2',
+        typeDefs: gql`
+          type Query {
+            getT1s: [T1]
+          }
+
+          type T1 {
+            o: [O]
+          }
+
+          type O {
+            a: Int
+            b: String
+            c: String
+          }
+        `,
+        resolvers: {
+          Query: {
+            getT1s() {
+              return [
+                { o: [{ a: 2, b: 'b2', c: 'two' }] },
+                {
+                  o: [
+                    { a: 3, b: 'b3', c: 'three' },
+                    { a: 4, b: 'b4', c: 'four' },
+                  ],
+                },
+              ];
+            },
+          },
+        },
+      };
+
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
+
+      const operation = parseOp(
+        `
         query {
           getT1s {
             o {
@@ -3725,7 +4429,9 @@ describe('executeQueryPlan', () => {
             f1
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
@@ -3764,7 +4470,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       // `null` should bubble up since `f2` is now non-nullable. But we should still get the `id: 0` response.
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -3806,39 +4518,42 @@ describe('executeQueryPlan', () => {
     }: {
       s1?: {
         // additional resolvers for interface I
-        iResolversExtra?: any,
+        iResolversExtra?: any;
         // provide a default __resolveReference for the interface
-        hasIResolveReference?: boolean,
+        hasIResolveReference?: boolean;
         // turn an id into extra data returned by __resolveReference (if hasIResolveReference is true)
-        iResolveReferenceExtra?: (id: string) => { [k: string]: any },
+        iResolveReferenceExtra?: (id: string) => { [k: string]: any };
         // additional resolvers for type A
-        aResolversExtra?: any,
+        aResolversExtra?: any;
         // additional resolvers for type B
-        bResolversExtra?: any,
-      }
+        bResolversExtra?: any;
+      };
     }) => {
-
       // The example uses 2 entities:
       //  - one of type A with id='idA' (x=1, y=2, z=3)
       //  - one of type B with id='idB' (x=10, y=20, w=30)
 
-      const s1IBaseResolvers = (s1?.hasIResolveReference ?? true)
-        ? {
-          __resolveReference(ref: { id: string }) {
-          const extraFct = s1?.iResolveReferenceExtra;
-          const extraData = extraFct ? extraFct(ref.id) : {};
-          return ref.id === 'idA'
-            ? { id: ref.id, x: 1, z: 3, ...extraData }
-            : { id: ref.id, x: 10, w: 30, ...extraData };
-          }
-        }
-        : {};
+      const s1IBaseResolvers =
+        s1?.hasIResolveReference ?? true
+          ? {
+              __resolveReference(ref: { id: string }) {
+                const extraFct = s1?.iResolveReferenceExtra;
+                const extraData = extraFct ? extraFct(ref.id) : {};
+                return ref.id === 'idA'
+                  ? { id: ref.id, x: 1, z: 3, ...extraData }
+                  : { id: ref.id, x: 10, w: 30, ...extraData };
+              },
+            }
+          : {};
 
       const subgraph1 = {
         name: 'S1',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key"]
+            )
 
           type Query {
             iFromS1: I
@@ -3865,7 +4580,7 @@ describe('executeQueryPlan', () => {
           Query: {
             iFromS1() {
               return { __typename: 'A', id: 'idA' };
-            }
+            },
           },
           I: {
             ...s1IBaseResolvers,
@@ -3877,14 +4592,17 @@ describe('executeQueryPlan', () => {
           B: {
             ...(s1?.bResolversExtra ?? {}),
           },
-        }
-      }
+        },
+      };
 
       const subgraph2 = {
         name: 'S2',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key", "@interfaceObject"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key", "@interfaceObject"]
+            )
 
           type Query {
             iFromS2: I
@@ -3903,28 +4621,38 @@ describe('executeQueryPlan', () => {
                 id: 'idB',
                 y: 20,
               };
-            }
+            },
           },
           I: {
             __resolveReference(ref: { id: string }) {
               return {
                 id: ref.id,
                 y: ref.id === 'idA' ? 2 : 20,
-              }
+              };
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([ subgraph1, subgraph2 ]);
-      return async (op: string): Promise<{ plan: QueryPlan, response: GatewayExecutionResult }> => {
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        subgraph1,
+        subgraph2,
+      ]);
+      return async (
+        op: string,
+      ): Promise<{ plan: QueryPlan; response: GatewayExecutionResult }> => {
         const operation = parseOp(op, schema);
         const plan = buildPlan(operation, queryPlanner);
-        const response = await executePlan(plan, operation, undefined, schema, serviceMap);
+        const response = await executePlan(
+          plan,
+          operation,
+          undefined,
+          schema,
+          serviceMap,
+        );
         return { plan, response };
       };
-    }
-
+    };
 
     test('handles __typename rewriting when using @key to @interfaceObject', async () => {
       // We don't need extra resolving from S1 in this case.
@@ -4084,135 +4812,144 @@ describe('executeQueryPlan', () => {
       `);
     });
 
-    test.each([{
-      name: 'with manual __typename',
-      s1: {
-        iResolveReferenceExtra: (id: string) => ({ __typename: id === 'idA' ? 'A' : 'B' }),
-      },
-    }, {
-      name: 'with __resolveType',
-      s1: {
-        iResolversExtra: {
-          __resolveType(ref: { id: string }) {
-            return ref.id === 'idA' ? 'A' : 'B';
-          }
+    test.each([
+      {
+        name: 'with manual __typename',
+        s1: {
+          iResolveReferenceExtra: (id: string) => ({
+            __typename: id === 'idA' ? 'A' : 'B',
+          }),
         },
       },
-    }, {
-      name: 'with isTypeOf',
-      s1: {
-        aResolversExtra: {
-          __isTypeOf(ref: { id: string }) {
-            return ref.id === 'idA';
-          }
-        },
-        bResolversExtra: {
-          __isTypeOf(ref: { id: string }) {
-            // Same remark as above.
-            return ref.id === 'idB';
-          }
+      {
+        name: 'with __resolveType',
+        s1: {
+          iResolversExtra: {
+            __resolveType(ref: { id: string }) {
+              return ref.id === 'idA' ? 'A' : 'B';
+            },
+          },
         },
       },
-    }, {
-      name: 'with only a __resolveType on the interface but per-runtime-types __resolveReference',
-      s1: {
-        hasIResolveReference: false,
-        iResolversExtra: {
-          __resolveType(ref: { id: string }) {
-            return ref.id === 'idA' ? 'A' : 'B';
-          }
-        },
-        aResolversExtra: {
-          __resolveReference(ref: { id: string }) {
-            return ref.id === 'idA'
-              ? { id: ref.id, x: 1, z: 3 }
-              : undefined;
-          }
-        },
-        bResolversExtra: {
-          __resolveReference(ref: { id: string }) {
-            return ref.id === 'idB'
-              ? { id: ref.id, x: 10, w: 30 }
-              : undefined;
-          }
+      {
+        name: 'with isTypeOf',
+        s1: {
+          aResolversExtra: {
+            __isTypeOf(ref: { id: string }) {
+              return ref.id === 'idA';
+            },
+          },
+          bResolversExtra: {
+            __isTypeOf(ref: { id: string }) {
+              // Same remark as above.
+              return ref.id === 'idB';
+            },
+          },
         },
       },
-    }, {
-      name: 'errors when nothing provides the runtime type',
-      expectedErrors: [
-        'Abstract type "I" `__resolveReference` method must resolve to an Object type at runtime. '
-        + 'Either the object returned by "I.__resolveReference" must include a valid `__typename` field, '
-        + 'or the "I" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.'
-      ],
-    }, {
-      name: 'with an async __resolveReference and a non-default __resolveType',
-      s1: {
-        iResolversExtra: {
-          async __resolveReference(ref: { id: string }) {
-            return ref.id === 'idA'
-              ? { id: ref.id, x: 1, z: 3 }
-              : { id: ref.id, x: 10, w: 30 };
-          },
-          __resolveType(ref: { id: string }) {
-            switch (ref.id) {
-              case 'idA':
-                return 'A';
-              case 'idB':
-                return 'B';
-              default:
-                throw new Error('Unknown type: ' + ref.id);
-            }
+      {
+        name: 'with only a __resolveType on the interface but per-runtime-types __resolveReference',
+        s1: {
+          hasIResolveReference: false,
+          iResolversExtra: {
+            __resolveType(ref: { id: string }) {
+              return ref.id === 'idA' ? 'A' : 'B';
+            },
+          },
+          aResolversExtra: {
+            __resolveReference(ref: { id: string }) {
+              return ref.id === 'idA' ? { id: ref.id, x: 1, z: 3 } : undefined;
+            },
+          },
+          bResolversExtra: {
+            __resolveReference(ref: { id: string }) {
+              return ref.id === 'idB'
+                ? { id: ref.id, x: 10, w: 30 }
+                : undefined;
+            },
           },
         },
       },
-    }, {
-      name: 'with an async __resolveReference and a non-default async __resolveType',
-      s1: {
-        iResolversExtra: {
-          async __resolveReference(ref: { id: string }) {
-            return ref.id === 'idA'
-              ? { id: ref.id, x: 1, z: 3 }
-              : { id: ref.id, x: 10, w: 30 };
-          },
-          async __resolveType(ref: { id: string }) {
-            switch (ref.id) {
-              case 'idA':
-                return 'A';
-              case 'idB':
-                return 'B';
-              default:
-                throw new Error('Unknown type: ' + ref.id);
-            }
+      {
+        name: 'errors when nothing provides the runtime type',
+        expectedErrors: [
+          'Abstract type "I" `__resolveReference` method must resolve to an Object type at runtime. ' +
+            'Either the object returned by "I.__resolveReference" must include a valid `__typename` field, ' +
+            'or the "I" type should provide a "resolveType" function or each possible type should provide an "isTypeOf" function.',
+        ],
+      },
+      {
+        name: 'with an async __resolveReference and a non-default __resolveType',
+        s1: {
+          iResolversExtra: {
+            async __resolveReference(ref: { id: string }) {
+              return ref.id === 'idA'
+                ? { id: ref.id, x: 1, z: 3 }
+                : { id: ref.id, x: 10, w: 30 };
+            },
+            __resolveType(ref: { id: string }) {
+              switch (ref.id) {
+                case 'idA':
+                  return 'A';
+                case 'idB':
+                  return 'B';
+                default:
+                  throw new Error('Unknown type: ' + ref.id);
+              }
+            },
           },
         },
       },
-    }, {
-      name: 'with an async __resolveReference and async __isTypeOf on implementations',
-      s1: {
-        iResolversExtra: {
-          async __resolveReference(ref: {
-            id: string;
-            // I don't understand the TypeScript error that occurs when the
-            // return type is removed here (like all the others); it surfaces
-            // because `aResolversExtra` is defined, which I can't explain.
-          }): Promise<Record<string, any>> {
-            return ref.id === 'idA'
-              ? { id: ref.id, x: 1, z: 3 }
-              : { id: ref.id, x: 10, w: 30 };
+      {
+        name: 'with an async __resolveReference and a non-default async __resolveType',
+        s1: {
+          iResolversExtra: {
+            async __resolveReference(ref: { id: string }) {
+              return ref.id === 'idA'
+                ? { id: ref.id, x: 1, z: 3 }
+                : { id: ref.id, x: 10, w: 30 };
+            },
+            async __resolveType(ref: { id: string }) {
+              switch (ref.id) {
+                case 'idA':
+                  return 'A';
+                case 'idB':
+                  return 'B';
+                default:
+                  throw new Error('Unknown type: ' + ref.id);
+              }
+            },
           },
         },
-        aResolversExtra: {
-          async __isTypeOf(ref: { id: string }) {
-            return ref.id === 'idA';
+      },
+      {
+        name: 'with an async __resolveReference and async __isTypeOf on implementations',
+        s1: {
+          iResolversExtra: {
+            async __resolveReference(ref: {
+              id: string;
+              // I don't understand the TypeScript error that occurs when the
+              // return type is removed here (like all the others); it surfaces
+              // because `aResolversExtra` is defined, which I can't explain.
+            }): Promise<Record<string, any>> {
+              return ref.id === 'idA'
+                ? { id: ref.id, x: 1, z: 3 }
+                : { id: ref.id, x: 10, w: 30 };
+            },
           },
-        },
-        bResolversExtra: {
-          async __isTypeOf(ref: { id: string }) {
-            return ref.id === 'idB';
+          aResolversExtra: {
+            async __isTypeOf(ref: { id: string }) {
+              return ref.id === 'idA';
+            },
+          },
+          bResolversExtra: {
+            async __isTypeOf(ref: { id: string }) {
+              return ref.id === 'idB';
+            },
           },
         },
       },
-    }])('resolving an interface @key $name', async ({ s1, expectedErrors }) => {
+    ])('resolving an interface @key $name', async ({ s1, expectedErrors }) => {
       const tester = defineSchema({ s1 });
 
       const { plan, response } = await tester(`
@@ -4328,7 +5065,11 @@ describe('executeQueryPlan', () => {
 
     test('handles __typename rewriting after forced resolution of implementation type', async () => {
       const tester = defineSchema({
-        s1: { iResolveReferenceExtra: (id: string) => ({ __typename: id === 'idA' ? 'A' : 'B' }), },
+        s1: {
+          iResolveReferenceExtra: (id: string) => ({
+            __typename: id === 'idA' ? 'A' : 'B',
+          }),
+        },
       });
 
       const { plan, response } = await tester(`
@@ -4399,17 +5140,17 @@ describe('executeQueryPlan', () => {
     test('handles querying fields of an implementation type coming from an @interfaceObject subgraph', async () => {
       const products = [
         {
-          id: "1",
-          title: "Jane Eyre",
+          id: '1',
+          title: 'Jane Eyre',
           price: 12.99,
-          author: "Charlotte Bronte",
-          ISBN: "9780743273565",
+          author: 'Charlotte Bronte',
+          ISBN: '9780743273565',
         },
         {
-          id: "2",
-          title: "Good Will Hunting",
+          id: '2',
+          title: 'Good Will Hunting',
           price: 14.99,
-          director: "Gus Van Sant",
+          director: 'Gus Van Sant',
           duration: 126,
         },
       ];
@@ -4418,7 +5159,10 @@ describe('executeQueryPlan', () => {
         name: 'products',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key"]
+            )
 
           type Query {
             products: [Product!]!
@@ -4449,9 +5193,9 @@ describe('executeQueryPlan', () => {
           Product: {
             __resolveType(product: any) {
               if (product.author) {
-                return "Book";
+                return 'Book';
               } else if (product.director) {
-                return "Movie";
+                return 'Movie';
               } else {
                 return null;
               }
@@ -4460,14 +5204,17 @@ describe('executeQueryPlan', () => {
               return products.find((obj) => obj.id === reference.id);
             },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'reviews',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.3", import: ["@key", "@interfaceObject"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key", "@interfaceObject"]
+            )
 
           type Query {
             allReviewedProducts: [Product!]!
@@ -4487,13 +5234,17 @@ describe('executeQueryPlan', () => {
         resolvers: {
           Query: {
             allReviewedProducts: () => products,
-          }
-        }
-      }
+          },
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      let operation = parseOp(`
+      let operation = parseOp(
+        `
         {
           allReviewedProducts {
             ... on Book {
@@ -4501,7 +5252,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       let queryPlan = buildPlan(operation, queryPlanner);
       // We're going check again with almost the query but requesting the `id` field. And the
@@ -4540,7 +5293,13 @@ describe('executeQueryPlan', () => {
       `;
       expect(queryPlan).toMatchInlineSnapshot(expectedPlan);
 
-      let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       // Note that the 2nd product is a Movie, so we should get an empty object
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -4553,7 +5312,8 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      operation = parseOp(`
+      operation = parseOp(
+        `
         {
           allReviewedProducts {
             ... on Book {
@@ -4562,13 +5322,21 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       queryPlan = buildPlan(operation, queryPlanner);
       // As said above, we should get the same plan as the previous time.
       expect(queryPlan).toMatchInlineSnapshot(expectedPlan);
 
-      response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       // But now we should have the "id" of the book (and still nothing for the movie).
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -4583,7 +5351,8 @@ describe('executeQueryPlan', () => {
       `);
 
       // Now with __typename just for the book
-      operation = parseOp(`
+      operation = parseOp(
+        `
         {
           allReviewedProducts {
             ... on Book {
@@ -4592,7 +5361,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       queryPlan = buildPlan(operation, queryPlanner);
       // The plan is almost the exact same as the previous one, but in this case we do end up asking for __typename
@@ -4632,7 +5403,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.data).toMatchInlineSnapshot(`
         Object {
           "allReviewedProducts": Array [
@@ -4646,7 +5423,8 @@ describe('executeQueryPlan', () => {
       `);
 
       // And lastly with __typename but for all products
-      operation = parseOp(`
+      operation = parseOp(
+        `
         {
           allReviewedProducts {
             __typename
@@ -4655,13 +5433,21 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
 
       queryPlan = buildPlan(operation, queryPlanner);
       // As said above, we should get the same plan as the previous time.
       expect(queryPlan).toMatchInlineSnapshot(expectedPlan);
 
-      response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.data).toMatchInlineSnapshot(`
         Object {
           "allReviewedProducts": Array [
@@ -4682,7 +5468,10 @@ describe('executeQueryPlan', () => {
         name: 's1',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.4"
+              import: ["@key"]
+            )
 
           type Query {
             ts: [T!]!
@@ -4698,16 +5487,19 @@ describe('executeQueryPlan', () => {
         `,
         resolvers: {
           Query: {
-            ts: () => [ { id: '2' }, { id: '4' }, { id: '1' } ]
+            ts: () => [{ id: '2' }, { id: '4' }, { id: '1' }],
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 's2',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key", "@interfaceObject", "@external", "@requires"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.4"
+              import: ["@key", "@interfaceObject", "@external", "@requires"]
+            )
 
           type I @key(fields: "id") @interfaceObject {
             id: ID!
@@ -4719,39 +5511,490 @@ describe('executeQueryPlan', () => {
             __resolveReference(ref: any) {
               return {
                 ...ref,
-                v: `id=${ref.id}`
+                v: `id=${ref.id}`,
               };
             },
+          },
+        },
+      };
+
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
+
+      const operation = parseOp(
+        `
+        {
+          ts {
+            v
           }
         }
-      }
+        `,
+        schema,
+      );
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const queryPlan = buildPlan(operation, queryPlanner);
+      const expectedPlan = `
+        QueryPlan {
+          Sequence {
+            Fetch(service: "s1") {
+              {
+                ts {
+                  __typename
+                  id
+                }
+              }
+            },
+            Flatten(path: "ts.@") {
+              Fetch(service: "s2") {
+                {
+                  ... on T {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on I {
+                    v
+                  }
+                }
+              },
+            },
+          },
+        }
+      `;
+      expect(queryPlan).toMatchInlineSnapshot(expectedPlan);
+
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+      expect(response.data).toMatchInlineSnapshot(`
+        Object {
+          "ts": Array [
+            Object {
+              "v": "id=2",
+            },
+            Object {
+              "v": "id=4",
+            },
+            Object {
+              "v": "id=1",
+            },
+          ],
+        }
+      `);
+    });
 
-      let operation = parseOp(`
+    test('handles querying @interfaceObject from a specific implementation (even when the subgraph does not have the corresponding interface)', async () => {
+      const s1 = {
+        name: 's1',
+        typeDefs: gql`
+          extend schema
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.4"
+              import: ["@key"]
+            )
+
+          type Query {
+            ts: [T!]!
+          }
+
+          type T @key(fields: "id", resolvable: false) {
+            id: ID!
+          }
+        `,
+        resolvers: {
+          Query: {
+            ts: () => [{ id: '2' }, { id: '4' }, { id: '1' }],
+          },
+        },
+      };
+
+      const s2 = {
+        name: 's2',
+        typeDefs: gql`
+          extend schema
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.4"
+              import: ["@key", "@interfaceObject"]
+            )
+
+          interface I @key(fields: "id") {
+            id: ID!
+            required: String
+          }
+
+          type T implements I @key(fields: "id") {
+            id: ID!
+            required: String
+          }
+        `,
+        resolvers: {
+          I: {
+            __resolveReference(ref: any) {
+              return [
+                { id: '1', __typename: 'T', required: 'r1' },
+                { id: '2', __typename: 'T', required: 'r2' },
+                { id: '3', __typename: 'T', required: 'r3' },
+                { id: '4', __typename: 'T', required: 'r4' },
+              ].find(({ id }) => id === ref.id);
+            },
+          },
+        },
+      };
+
+      const s3 = {
+        name: 's3',
+        typeDefs: gql`
+          extend schema
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.4"
+              import: ["@key", "@interfaceObject", "@external", "@requires"]
+            )
+
+          type I @key(fields: "id") @interfaceObject {
+            id: ID!
+            required: String @external
+            v: String @requires(fields: "required")
+          }
+        `,
+        resolvers: {
+          I: {
+            __resolveReference(ref: any) {
+              return {
+                ...ref,
+                v: `req=${ref.required}`,
+              };
+            },
+          },
+        },
+      };
+
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+        s3,
+      ]);
+
+      const operation = parseOp(
+        `
         {
           ts {
             v
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
+
+      const queryPlan = buildPlan(operation, queryPlanner);
+      const expectedPlan = `
+        QueryPlan {
+          Sequence {
+            Fetch(service: "s1") {
+              {
+                ts {
+                  __typename
+                  id
+                }
+              }
+            },
+            Flatten(path: "ts.@") {
+              Fetch(service: "s2") {
+                {
+                  ... on I {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on I {
+                    __typename
+                    required
+                  }
+                }
+              },
+            },
+            Flatten(path: "ts.@") {
+              Fetch(service: "s3") {
+                {
+                  ... on I {
+                    __typename
+                    required
+                    id
+                  }
+                } =>
+                {
+                  ... on I {
+                    v
+                  }
+                }
+              },
+            },
+          },
+        }
+      `;
+      expect(queryPlan).toMatchInlineSnapshot(expectedPlan);
+
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+      expect(response.data).toMatchInlineSnapshot(`
+        Object {
+          "ts": Array [
+            Object {
+              "v": "req=r2",
+            },
+            Object {
+              "v": "req=r4",
+            },
+            Object {
+              "v": "req=r1",
+            },
+          ],
+        }
+      `);
+    });
+
+    test('handles @requires on @interfaceObject that applies to only one of the queried implementation', async () => {
+      // The case this test is that where the @interfaceObject in s2 has a @requires, but the query we send requests the field on which
+      // there is this @require only for one of the implementation type, which it request another field with no require for another implementation.
+      // And we're making sure the requirements only get queried for T1, the first type.
+      const s1 = {
+        name: 's1',
+        typeDefs: gql`
+          extend schema
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.4"
+              import: ["@key"]
+            )
+
+          type Query {
+            is: [I!]!
+          }
+
+          interface I @key(fields: "id") {
+            id: ID!
+            name: String
+            req: Req
+          }
+
+          type T1 implements I @key(fields: "id") {
+            id: ID!
+            name: String
+            req: Req
+          }
+
+          type T2 implements I @key(fields: "id") {
+            id: ID!
+            name: String
+            req: Req
+          }
+
+          type Req {
+            id: ID!
+          }
+        `,
+        resolvers: {
+          Query: {
+            is: () => [
+              { __typename: 'T1', id: '2', name: 'e2', req: { id: 'r1' } },
+              { __typename: 'T2', id: '4', name: 'e4', req: { id: 'r2' } },
+              { __typename: 'T1', id: '1', name: 'e1', req: { id: 'r3' } },
+            ],
+          },
+        },
+      };
+
+      const s2 = {
+        name: 's2',
+        typeDefs: gql`
+          extend schema
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.4"
+              import: ["@key", "@interfaceObject", "@external", "@requires"]
+            )
+
+          type I @key(fields: "id") @interfaceObject {
+            id: ID!
+            req: Req @external
+            v: String! @requires(fields: "req { id }")
+          }
+
+          type Req {
+            id: ID! @external
+          }
+        `,
+        resolvers: {
+          I: {
+            __resolveReference(ref: any) {
+              return {
+                ...ref,
+                v: `req=${ref.req.id}`,
+              };
+            },
+          },
+        },
+      };
+
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
+
+      let operation = parseOp(
+        `
+        {
+          is {
+            ... on T1 {
+              v
+            }
+            ... on T2 {
+              name
+            }
+          }
+        }
+        `,
+        schema,
+      );
+
+      let queryPlan = buildPlan(operation, queryPlanner);
+      expect(queryPlan).toMatchInlineSnapshot(`
+        QueryPlan {
+          Sequence {
+            Fetch(service: "s1") {
+              {
+                is {
+                  __typename
+                  ... on T1 {
+                    __typename
+                    id
+                    req {
+                      id
+                    }
+                  }
+                  ... on T2 {
+                    name
+                  }
+                }
+              }
+            },
+            Flatten(path: "is.@") {
+              Fetch(service: "s2") {
+                {
+                  ... on T1 {
+                    __typename
+                    id
+                  }
+                  ... on I {
+                    __typename
+                    req {
+                      id
+                    }
+                  }
+                } =>
+                {
+                  ... on I {
+                    v
+                  }
+                }
+              },
+            },
+          },
+        }
+      `);
+
+      let response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+      expect(response.errors).toBeUndefined();
+      expect(response.data).toMatchInlineSnapshot(`
+        Object {
+          "is": Array [
+            Object {
+              "v": "req=r1",
+            },
+            Object {
+              "name": "e4",
+            },
+            Object {
+              "v": "req=r3",
+            },
+          ],
+        }
+      `);
+
+      // Sanity checking that if we ask for `v` (the field with @requires), then everything still works.
+      operation = parseOp(
+        `
+        {
+          is {
+            ... on T1 {
+              v
+            }
+            ... on T2 {
+              v
+              name
+            }
+          }
+        }
+        `,
+        schema,
+      );
 
-      let queryPlan = buildPlan(operation, queryPlanner);
-      const expectedPlan = `
+      queryPlan = buildPlan(operation, queryPlanner);
+      expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
           Sequence {
             Fetch(service: "s1") {
               {
-                ts {
+                is {
                   __typename
-                  id
+                  ... on T1 {
+                    __typename
+                    id
+                    req {
+                      id
+                    }
+                  }
+                  ... on T2 {
+                    __typename
+                    id
+                    req {
+                      id
+                    }
+                    name
+                  }
                 }
               }
             },
-            Flatten(path: "ts.@") {
+            Flatten(path: "is.@") {
               Fetch(service: "s2") {
                 {
-                  ... on T {
+                  ... on T1 {
+                    __typename
+                    id
+                  }
+                  ... on I {
+                    __typename
+                    req {
+                      id
+                    }
+                  }
+                  ... on T2 {
                     __typename
                     id
                   }
@@ -4765,398 +6008,357 @@ describe('executeQueryPlan', () => {
             },
           },
         }
-      `;
-      expect(queryPlan).toMatchInlineSnapshot(expectedPlan);
+      `);
 
-      let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+      expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
-          "ts": Array [
+          "is": Array [
             Object {
-              "v": "id=2",
+              "v": "req=r1",
             },
             Object {
-              "v": "id=4",
+              "name": "e4",
+              "v": "req=r2",
             },
             Object {
-              "v": "id=1",
+              "v": "req=r3",
             },
           ],
         }
       `);
     });
 
-    test('handles querying @interfaceObject from a specific implementation (even when the subgraph does not have the corresponding interface)', async () => {
-      const s1 = {
-        name: 's1',
+    test('handles resolving concrete `__typename`s of an @interfaceObject via the interface itself (issue #2743)', async () => {
+      const iList = [
+        {
+          id: '1',
+          tField: 'field on a T',
+        },
+      ];
+      const S1 = {
+        name: 'S1',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key", "@interfaceObject"]
+            )
 
-          type Query {
-            ts: [T!]!
+          interface I @key(fields: "id") {
+            id: ID!
           }
 
-          type T @key(fields: "id", resolvable: false) {
+          type T implements I @key(fields: "id") {
             id: ID!
+            tField: String
           }
         `,
         resolvers: {
+          I: {
+            __resolveReference: ({ id }: { id: string }) => {
+              return iList.find((e) => e.id === id);
+            },
+            __resolveType: () => {
+              return 'T';
+            },
+          },
           Query: {
-            ts: () => [ { id: '2' }, { id: '4' }, { id: '1' } ]
+            allI: () => iList,
           },
-        }
-      }
+        },
+      };
 
-      const s2 = {
-        name: 's2',
+      const S2 = {
+        name: 'S2',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key", "@interfaceObject"])
-
-          interface I @key(fields: "id") {
-            id: ID!
-            required: String
-          }
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key", "@interfaceObject"]
+            )
 
-          type T implements I @key(fields: "id") {
+          type I @key(fields: "id") @interfaceObject {
             id: ID!
-            required: String
+            iField: String
           }
-
         `,
         resolvers: {
           I: {
-            __resolveReference(ref: any) {
-              return [
-                { id: '1', __typename: "T", required: "r1" },
-                { id: '2', __typename: "T", required: "r2" },
-                { id: '3', __typename: "T", required: "r3" },
-                { id: '4', __typename: "T", required: "r4" },
-              ].find(({id}) => id === ref.id);
-            },
+            __resolveReference: ({ id }: { id: string }) =>
+              iList.find((e) => e.id === id),
+            iField: () => "field on I's",
           },
-        }
-      }
+        },
+      };
 
-      const s3 = {
-        name: 's3',
+      const S3 = {
+        name: 'S3',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key", "@interfaceObject", "@external", "@requires"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key", "@interfaceObject"]
+            )
 
           type I @key(fields: "id") @interfaceObject {
             id: ID!
-            required: String @external
-            v: String @requires(fields: "required")
+          }
+
+          type Query {
+            searchIs: [I!]!
           }
         `,
         resolvers: {
           I: {
-            __resolveReference(ref: any) {
-              return {
-                ...ref,
-                v: `req=${ref.required}`
-              };
-            },
-          }
-        }
-      }
-
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2, s3 ]);
+            __resolveReference: ({ id }: { id: string }) =>
+              iList.find((e) => e.id === id),
+          },
+          Query: {
+            searchIs: () => iList,
+          },
+        },
+      };
 
-      let operation = parseOp(`
-        {
-          ts {
-            v
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        S1,
+        S2,
+        S3,
+      ]);
+      const operation = parseOp(
+        `#graphql
+          {
+            searchIs {
+              __typename
+              id
+              iField
+            }
           }
-        }
-        `, schema);
+        `,
+        schema,
+      );
 
-      let queryPlan = buildPlan(operation, queryPlanner);
-      const expectedPlan = `
+      const queryPlan = buildPlan(operation, queryPlanner);
+      expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
           Sequence {
-            Fetch(service: "s1") {
+            Fetch(service: "S3") {
               {
-                ts {
+                searchIs {
                   __typename
                   id
                 }
               }
             },
-            Flatten(path: "ts.@") {
-              Fetch(service: "s2") {
-                {
-                  ... on I {
-                    __typename
-                    id
-                  }
-                } =>
-                {
-                  ... on I {
-                    __typename
-                    required
+            Parallel {
+              Flatten(path: "searchIs.@") {
+                Fetch(service: "S1") {
+                  {
+                    ... on I {
+                      __typename
+                      id
+                    }
+                  } =>
+                  {
+                    ... on I {
+                      __typename
+                    }
                   }
-                }
+                },
               },
-            },
-            Flatten(path: "ts.@") {
-              Fetch(service: "s3") {
-                {
-                  ... on I {
-                    __typename
-                    required
-                    id
-                  }
-                } =>
-                {
-                  ... on I {
-                    v
+              Flatten(path: "searchIs.@") {
+                Fetch(service: "S2") {
+                  {
+                    ... on I {
+                      __typename
+                      id
+                    }
+                  } =>
+                  {
+                    ... on I {
+                      iField
+                    }
                   }
-                }
+                },
               },
             },
           },
         }
-      `;
-      expect(queryPlan).toMatchInlineSnapshot(expectedPlan);
+      `);
 
-      let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
-      expect(response.data).toMatchInlineSnapshot(`
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+
+      expect(response).toMatchInlineSnapshot(`
         Object {
-          "ts": Array [
-            Object {
-              "v": "req=r2",
-            },
-            Object {
-              "v": "req=r4",
-            },
-            Object {
-              "v": "req=r1",
-            },
-          ],
+          "data": Object {
+            "searchIs": Array [
+              Object {
+                "__typename": "T",
+                "iField": "field on I's",
+                "id": "1",
+              },
+            ],
+          },
         }
       `);
     });
 
-    test('handles @requires on @interfaceObject that applies to only one of the queried implementation', async () => {
-      // The case this test is that where the @interfaceObject in s2 has a @requires, but the query we send requests the field on which
-      // there is this @require only for one of the implementation type, which it request another field with no require for another implementation.
-      // And we're making sure the requirements only get queried for T1, the first type.
-      const s1 = {
-        name: 's1',
+    test("useless interface fetches aren't preserved when `__typename` has been resolved to its concrete type (issue #2743)", async () => {
+      const store = [
+        {
+          id: '1',
+          tField: 'field on a T',
+        },
+      ];
+
+      const S1 = {
+        name: 'S1',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key"])
-
-          type Query {
-            is: [I!]!
-          }
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key"]
+            )
 
           interface I @key(fields: "id") {
             id: ID!
-            name: String
-            req: Req
           }
 
-          type T1 implements I @key(fields: "id") {
+          type T implements I @key(fields: "id") {
             id: ID!
-            name: String
-            req: Req
+            tField: String
           }
+        `,
+        resolvers: {
+          I: {
+            __resolveReference: ({ id }: { id: string }) =>
+              store.find((e) => e.id === id),
+            __resolveType: () => 'T',
+          },
+        },
+      };
 
-          type T2 implements I @key(fields: "id") {
-            id: ID!
-            name: String
-            req: Req
-          }
+      const S2 = {
+        name: 'S2',
+        typeDefs: gql`
+          extend schema
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key", "@interfaceObject"]
+            )
 
-          type Req {
+          type I @key(fields: "id") @interfaceObject {
             id: ID!
+            iField: String!
           }
         `,
         resolvers: {
-          Query: {
-            is: () => [
-              { __typename: 'T1', id: '2', name: 'e2', req: { id: 'r1'} },
-              { __typename: 'T2', id: '4', name: 'e4', req: { id: 'r2'} },
-              { __typename: 'T1', id: '1', name: 'e1', req: { id: 'r3'} }
-            ]
+          I: {
+            __resolveReference: ({ id }: { id: string }) =>
+              store.find((e) => e.id === id),
+            iField: () => 'field on an I',
           },
-        }
-      }
+        },
+      };
 
-      const s2 = {
-        name: 's2',
+      const S3 = {
+        name: 'S3',
         typeDefs: gql`
           extend schema
-            @link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key", "@interfaceObject", "@external", "@requires"])
+            @link(
+              url: "https://specs.apollo.dev/federation/v2.3"
+              import: ["@key", "@interfaceObject"]
+            )
 
           type I @key(fields: "id") @interfaceObject {
             id: ID!
-            req: Req @external
-            v: String! @requires(fields: "req { id }")
           }
 
-          type Req {
-            id: ID! @external
+          type Query {
+            getIs: [I!]!
           }
         `,
         resolvers: {
           I: {
-            __resolveReference(ref: any) {
-              return {
-                ...ref,
-                v: `req=${ref.req.id}`
-              };
-            },
-          }
-        }
-      }
-
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+            __resolveReference: ({ id }: { id: string }) =>
+              store.find((e) => e.id === id),
+          },
+          Query: {
+            getIs: () => store,
+          },
+        },
+      };
 
-      let operation = parseOp(`
-        {
-          is {
-            ... on T1 {
-              v
-            }
-            ... on T2 {
-              name
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        S1,
+        S2,
+        S3,
+      ]);
+      const operation = parseOp(
+        `#graphql
+          {
+            getIs {
+              id
+              ... on T {
+                iField
+              }
             }
           }
-        }
-        `, schema);
+        `,
+        schema,
+      );
 
-      let queryPlan = buildPlan(operation, queryPlanner);
+      const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
           Sequence {
-            Fetch(service: "s1") {
+            Fetch(service: "S3") {
               {
-                is {
+                getIs {
                   __typename
-                  ... on T1 {
-                    __typename
-                    id
-                    req {
-                      id
-                    }
-                  }
-                  ... on T2 {
-                    name
-                  }
+                  id
                 }
               }
             },
-            Flatten(path: "is.@") {
-              Fetch(service: "s2") {
+            Flatten(path: "getIs.@") {
+              Fetch(service: "S1") {
                 {
-                  ... on T1 {
-                    __typename
-                    id
-                  }
                   ... on I {
                     __typename
-                    req {
-                      id
-                    }
+                    id
                   }
                 } =>
                 {
                   ... on I {
-                    v
-                  }
-                }
-              },
-            },
-          },
-        }
-      `);
-
-      let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
-      expect(response.errors).toBeUndefined();
-      expect(response.data).toMatchInlineSnapshot(`
-        Object {
-          "is": Array [
-            Object {
-              "v": "req=r1",
-            },
-            Object {
-              "name": "e4",
-            },
-            Object {
-              "v": "req=r3",
-            },
-          ],
-        }
-      `);
-
-      // Sanity checking that if we ask for `v` (the field with @requires), then everything still works.
-      operation = parseOp(`
-        {
-          is {
-            ... on T1 {
-              v
-            }
-            ... on T2 {
-              v
-              name
-            }
-          }
-        }
-        `, schema);
-
-      global.console = require('console');
-      queryPlan = buildPlan(operation, queryPlanner);
-      expect(queryPlan).toMatchInlineSnapshot(`
-        QueryPlan {
-          Sequence {
-            Fetch(service: "s1") {
-              {
-                is {
-                  __typename
-                  ... on T1 {
-                    __typename
-                    id
-                    req {
-                      id
-                    }
-                  }
-                  ... on T2 {
                     __typename
-                    id
-                    req {
-                      id
-                    }
-                    name
                   }
                 }
-              }
+              },
             },
-            Flatten(path: "is.@") {
-              Fetch(service: "s2") {
+            Flatten(path: "getIs.@") {
+              Fetch(service: "S2") {
                 {
-                  ... on T1 {
-                    __typename
-                    id
-                  }
                   ... on I {
-                    __typename
-                    req {
-                      id
-                    }
-                  }
-                  ... on T2 {
                     __typename
                     id
                   }
                 } =>
                 {
                   ... on I {
-                    v
+                    iField
                   }
                 }
               },
@@ -5165,22 +6367,24 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
-      expect(response.errors).toBeUndefined();
-      expect(response.data).toMatchInlineSnapshot(`
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
+
+      expect(response).toMatchInlineSnapshot(`
         Object {
-          "is": Array [
-            Object {
-              "v": "req=r1",
-            },
-            Object {
-              "name": "e4",
-              "v": "req=r2",
-            },
-            Object {
-              "v": "req=r3",
-            },
-          ],
+          "data": Object {
+            "getIs": Array [
+              Object {
+                "iField": "field on an I",
+                "id": "1",
+              },
+            ],
+          },
         }
       `);
     });
@@ -5214,10 +6418,10 @@ describe('executeQueryPlan', () => {
                 { __typename: 'A', id: 'keyA', g: 1 },
                 { __typename: 'B', id: 'keyB', g: 'foo' },
               ];
-            }
+            },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -5236,21 +6440,33 @@ describe('executeQueryPlan', () => {
         `,
         resolvers: {
           A: {
-            __resolveReference(ref: { id: string, g: any }) {
-              return { __typename: 'A', id: ref.id, f: `g is type ${typeof ref.g}` };
+            __resolveReference(ref: { id: string; g: any }) {
+              return {
+                __typename: 'A',
+                id: ref.id,
+                f: `g is type ${typeof ref.g}`,
+              };
             },
           },
           B: {
-            __resolveReference(ref: { id: string, g: any }) {
-              return { __typename: 'B', id: ref.id, f: `g is type ${typeof ref.g}` };
+            __resolveReference(ref: { id: string; g: any }) {
+              return {
+                __typename: 'B',
+                id: ref.id,
+                f: `g is type ${typeof ref.g}`,
+              };
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           us {
             ... on A {
@@ -5261,7 +6477,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       // In the initial fetch, it's important that one of the `g` is aliased, since it's queried twice at the same level
       // but with different types.
@@ -5313,7 +6531,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -5361,10 +6585,10 @@ describe('executeQueryPlan', () => {
                 { __typename: 'A', id: 'keyA', g: 1 },
                 { __typename: 'B', id: 'keyB', g: 'foo' },
               ];
-            }
+            },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -5383,27 +6607,41 @@ describe('executeQueryPlan', () => {
         `,
         resolvers: {
           A: {
-            __resolveReference(ref: { id: string, g: any }) {
-              return { __typename: 'A', id: ref.id, f: `g is type ${typeof ref.g}` };
+            __resolveReference(ref: { id: string; g: any }) {
+              return {
+                __typename: 'A',
+                id: ref.id,
+                f: `g is type ${typeof ref.g}`,
+              };
             },
           },
           B: {
-            __resolveReference(ref: { id: string, g: any }) {
-              return { __typename: 'B', id: ref.id, f: `g is type ${typeof ref.g}` };
+            __resolveReference(ref: { id: string; g: any }) {
+              return {
+                __typename: 'B',
+                id: ref.id,
+                f: `g is type ${typeof ref.g}`,
+              };
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           us {
             f
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       // In the initial fetch, it's important that one of the `g` is aliased, since it's queried twice at the same level
       // but with different types.
@@ -5455,7 +6693,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -5500,10 +6744,10 @@ describe('executeQueryPlan', () => {
                 { __typename: 'A', g: 'foo' },
                 { __typename: 'B', g: 1 },
               ];
-            }
+            },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -5521,26 +6765,40 @@ describe('executeQueryPlan', () => {
         resolvers: {
           A: {
             __resolveReference(ref: { g: string }) {
-              return { __typename: 'A', g: ref.g, f: ref.g == 'foo' ? 'fA' : '<error>' };
+              return {
+                __typename: 'A',
+                g: ref.g,
+                f: ref.g == 'foo' ? 'fA' : '<error>',
+              };
             },
           },
           B: {
             __resolveReference(ref: { g: number }) {
-              return { __typename: 'B', g: ref.g, f: ref.g === 1 ? 'fB' : '<error>' };
+              return {
+                __typename: 'B',
+                g: ref.g,
+                f: ref.g === 1 ? 'fB' : '<error>',
+              };
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           us {
             f
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       // In the initial fetch, it's important that one of the `g` is aliased, since it's queried twice at the same level
       // but with different types.
@@ -5588,7 +6846,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -5629,13 +6893,13 @@ describe('executeQueryPlan', () => {
           Query: {
             us() {
               return [
-                { __typename: 'A', id: 'keyA', f: 'fA'},
+                { __typename: 'A', id: 'keyA', f: 'fA' },
                 { __typename: 'B', f: 'fB' },
               ];
-            }
+            },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -5651,18 +6915,24 @@ describe('executeQueryPlan', () => {
               return { __typename: 'A', id: ref.id, f: 'fA' };
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           us {
             f
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       // Here, the presence of the @provides "forces" the query planner to check type-explosion, and as type-exploding
       // is the most efficient solution, it is chosen. But as this result in `f` being queried twice at the same level
@@ -5686,7 +6956,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -5735,10 +7011,10 @@ describe('executeQueryPlan', () => {
                 { __typename: 'A', g: 'foo' },
                 { __typename: 'B', g: 1 },
               ];
-            }
+            },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -5764,25 +7040,37 @@ describe('executeQueryPlan', () => {
         resolvers: {
           Query: {
             t() {
-              return ({ id: 0 });
-            }
+              return { id: 0 };
+            },
           },
           A: {
             __resolveReference(ref: { g: string }) {
-              return { __typename: 'A', g: ref.g, f: ref.g == 'foo' ? 'fA' : '<error>' };
+              return {
+                __typename: 'A',
+                g: ref.g,
+                f: ref.g == 'foo' ? 'fA' : '<error>',
+              };
             },
           },
           B: {
             __resolveReference(ref: { g: number }) {
-              return { __typename: 'B', g: ref.g, f: ref.g === 1 ? 'fB' : '<error>' };
+              return {
+                __typename: 'B',
+                g: ref.g,
+                f: ref.g === 1 ? 'fB' : '<error>',
+              };
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           t {
             us {
@@ -5790,7 +7078,9 @@ describe('executeQueryPlan', () => {
             }
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       // In the 2nd fetch, it's important that one of the `g` is aliased, since it's queried twice at the same level
       // but with different types.
@@ -5856,7 +7146,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       expect(response.data).toMatchInlineSnapshot(`
         Object {
@@ -5912,10 +7208,10 @@ describe('executeQueryPlan', () => {
                 { __typename: 'A', id: 'keyA', g: 1, x: 'xA', y: 'yA' },
                 { __typename: 'B', id: 'keyB', g: 'foo', x: 'xB', y: 'yB' },
               ];
-            }
+            },
           },
-        }
-      }
+        },
+      };
 
       const s2 = {
         name: 'S2',
@@ -5934,24 +7230,36 @@ describe('executeQueryPlan', () => {
         `,
         resolvers: {
           A: {
-            __resolveReference(ref: { id: string, g: any }) {
-              return { __typename: 'A', id: ref.id, f: `g is type ${typeof ref.g}` };
+            __resolveReference(ref: { id: string; g: any }) {
+              return {
+                __typename: 'A',
+                id: ref.id,
+                f: `g is type ${typeof ref.g}`,
+              };
             },
           },
           B: {
-            __resolveReference(ref: { id: string, g: any }) {
-              return { __typename: 'B', id: ref.id, f: `g is type ${typeof ref.g}` };
+            __resolveReference(ref: { id: string; g: any }) {
+              return {
+                __typename: 'B',
+                id: ref.id,
+                f: `g is type ${typeof ref.g}`,
+              };
             },
           },
-        }
-      }
+        },
+      };
 
-      const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
+      const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+        s1,
+        s2,
+      ]);
 
       // We known that `g` will need to be aliased in the 2nd occurrence on B, and by default it would be aliased
       // as `g__alias_0`. So we query something with that exact alias to check that we avoid the conflict. We
       // also use alias `g__alias_1` to further ensure multiple possible conflict are handled.
-      const operation = parseOp(`
+      const operation = parseOp(
+        `
         query {
           us {
             g__alias_0: x
@@ -5959,7 +7267,9 @@ describe('executeQueryPlan', () => {
             g__alias_1: y
           }
         }
-        `, schema);
+        `,
+        schema,
+      );
       const queryPlan = buildPlan(operation, queryPlanner);
       expect(queryPlan).toMatchInlineSnapshot(`
         QueryPlan {
@@ -6011,7 +7321,13 @@ describe('executeQueryPlan', () => {
         }
       `);
 
-      const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+      const response = await executePlan(
+        queryPlan,
+        operation,
+        undefined,
+        schema,
+        serviceMap,
+      );
       expect(response.errors).toBeUndefined();
       // We double-check that the final aliases are the one from the query
       expect(response.data).toMatchInlineSnapshot(`
@@ -6033,7 +7349,7 @@ describe('executeQueryPlan', () => {
     });
   });
 
-  it(`surface post-processing errors as extensions in the response`, async () => {
+  it('surface post-processing errors as extensions in the response', async () => {
     // This test is such that the first subgraph return some object with a key, but
     // then the 2nd one is queried for additional field `x`, but the reference resolver
     // returns `null`, so that response is ignored, and the data internally to the
@@ -6059,11 +7375,11 @@ describe('executeQueryPlan', () => {
       resolvers: {
         Query: {
           t() {
-            return { "__typename": "T", "id": 0 };
-          }
-        }
-      }
-    }
+            return { __typename: 'T', id: 0 };
+          },
+        },
+      },
+    };
 
     const s2 = {
       name: 'S2',
@@ -6077,20 +7393,26 @@ describe('executeQueryPlan', () => {
         T: {
           __resolveReference() {
             return null;
-          }
-        }
-      }
-    }
+          },
+        },
+      },
+    };
 
-    const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([ s1, s2 ]);
-    const operation = parseOp(`
+    const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+      s1,
+      s2,
+    ]);
+    const operation = parseOp(
+      `
       {
        t {
          id
          x
        }
       }
-      `, schema);
+      `,
+      schema,
+    );
 
     const queryPlan = buildPlan(operation, queryPlanner);
     expect(queryPlan).toMatchInlineSnapshot(`
@@ -6122,7 +7444,13 @@ describe('executeQueryPlan', () => {
         },
       }
     `);
-    const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
+    const response = await executePlan(
+      queryPlan,
+      operation,
+      undefined,
+      schema,
+      serviceMap,
+    );
     expect(response.data).toMatchInlineSnapshot(`
       Object {
         "t": null,
@@ -6140,4 +7468,257 @@ describe('executeQueryPlan', () => {
       }
     `);
   });
+
+  it('handles post-query rewrites correctly even when null values are involved', async () => {
+    // Tests reproducing the issue of #2715
+    const s1 = {
+      name: 'S1',
+      typeDefs: gql`
+        type Query {
+          tests: [Test!]!
+        }
+
+        type Test {
+          node: Node
+        }
+
+        interface Node {
+          id: ID!
+        }
+
+        type Foo implements Node {
+          id: ID!
+          foo: String
+        }
+
+        type Bar implements Node {
+          id: ID!
+          bar: String
+        }
+      `,
+      resolvers: {
+        Query: {
+          tests() {
+            return [{ node: null }];
+          },
+        },
+        Node: {
+          __resolveType(obj: any) {
+            if (obj['foo']) {
+              return 'Foo';
+            }
+            if (obj['bar']) {
+              return 'Bar';
+            }
+            return undefined;
+          },
+        },
+      },
+    };
+
+    const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+      s1,
+    ]);
+    const operation = parseOp(
+      `
+      query {
+        tests {
+          node {
+            ... on Foo { test: foo }
+            ... on Bar { test: bar }
+          }
+        }
+      }
+    `,
+      schema,
+    );
+
+    const queryPlan = buildPlan(operation, queryPlanner);
+    expect(queryPlan).toMatchInlineSnapshot(`
+      QueryPlan {
+        Fetch(service: "S1") {
+          {
+            tests {
+              node {
+                __typename
+                ... on Foo {
+                  test: foo
+                }
+                ... on Bar {
+                  test__alias_0: bar
+                }
+              }
+            }
+          }
+        },
+      }
+    `);
+    const response = await executePlan(
+      queryPlan,
+      operation,
+      undefined,
+      schema,
+      serviceMap,
+    );
+    expect(response.errors).toBeUndefined();
+    expect(response.extensions).toBeUndefined();
+    expect(response.data).toMatchInlineSnapshot(`
+      Object {
+        "tests": Array [
+          Object {
+            "node": null,
+          },
+        ],
+      }
+    `);
+  });
+
+  it(`handles duplicate aliased fields on entities correctly`, async () => {
+    const s1Data = [
+      { id: 't1', type: 'T1' },
+      { id: 't2', type: 'T2' },
+    ];
+    const s1 = {
+      name: 'S1',
+      typeDefs: gql`
+        type Query {
+          testQuery(id: String!): I
+        }
+
+        interface I {
+          id: String!
+        }
+
+        type T1 implements I @key(fields: "id", resolvable: false) {
+          id: String!
+        }
+
+        type T2 implements I @key(fields: "id", resolvable: false) {
+          id: String!
+        }
+      `,
+      resolvers: {
+        Query: {
+          testQuery(_: any, args: any) {
+            return s1Data.find((obj) => obj.id === args.id);
+          },
+        },
+        I: {
+          __resolveType(obj: any) {
+            return obj.type;
+          },
+        },
+      },
+    };
+
+    const t1 = {
+      id: 't1',
+      foo: {
+        field: 'field1',
+      },
+    };
+
+    const t2 = {
+      id: 't2',
+      bar: {
+        field: 'field2',
+      },
+    };
+    const s2 = {
+      name: 'S2',
+      typeDefs: gql`
+        interface I {
+          id: String!
+        }
+
+        type Test {
+          field: String!
+        }
+
+        type T1 implements I @key(fields: "id") {
+          id: String!
+          foo: Test
+        }
+
+        type T2 implements I @key(fields: "id") {
+          id: String!
+          bar: Test
+        }
+      `,
+      resolvers: {
+        Query: {},
+        Test: {},
+        T1: {
+          __resolveReference() {
+            return t1;
+          },
+        },
+        T2: {
+          __resolveReference() {
+            return t2;
+          },
+        },
+      },
+    };
+
+    const { serviceMap, schema, queryPlanner } = getFederatedTestingSchema([
+      s1,
+      s2,
+    ]);
+
+    const operation = parseOp(
+      `
+          query AliasedQuery($tId: String!) {
+            testQuery(id: $tId) {
+              ... on T1 {
+                foo {
+                  field
+                }
+              }
+              ... on T2 {
+                foo: bar {
+                  field
+                }
+              }
+            }
+          }
+        `,
+      schema,
+    );
+
+    const queryPlan = buildPlan(operation, queryPlanner);
+    const requestContext = buildRequestContext();
+    requestContext.request.variables = { tId: 't1' };
+
+    let response = await executePlan(
+      queryPlan,
+      operation,
+      requestContext,
+      schema,
+      serviceMap,
+    );
+    expect(response.data).toMatchObject({
+      testQuery: {
+        foo: {
+          field: 'field1',
+        },
+      },
+    });
+
+    requestContext.request.variables = { tId: 't2' };
+    response = await executePlan(
+      queryPlan,
+      operation,
+      requestContext,
+      schema,
+      serviceMap,
+    );
+    expect(response.data).toMatchObject({
+      testQuery: {
+        foo: {
+          field: 'field2',
+        },
+      },
+    });
+  });
 });
diff --git a/gateway-js/src/__tests__/gateway/endToEnd.test.ts b/gateway-js/src/__tests__/gateway/endToEnd.test.ts
index dbd359898..254471a94 100644
--- a/gateway-js/src/__tests__/gateway/endToEnd.test.ts
+++ b/gateway-js/src/__tests__/gateway/endToEnd.test.ts
@@ -2,7 +2,7 @@ import { fixtures } from 'apollo-federation-integration-testsuite';
 import { buildSchema, ObjectType } from '@apollo/federation-internals';
 import gql from 'graphql-tag';
 import { printSchema } from 'graphql';
-import { startSubgraphsAndGateway, Services } from './testUtils'
+import { startSubgraphsAndGateway, Services } from './testUtils';
 import { InMemoryLRUCache } from '@apollo/utils.keyvaluecache';
 import { QueryPlan } from '@apollo/query-planner';
 import { createHash } from '@apollo/utils.createhash';
@@ -20,11 +20,15 @@ afterEach(async () => {
   }
 });
 
-
 describe('caching', () => {
-  const cache = new InMemoryLRUCache<QueryPlan>({maxSize: Math.pow(2, 20) * (30), sizeCalculation: approximateObjectSize});
+  const cache = new InMemoryLRUCache<QueryPlan>({
+    maxSize: Math.pow(2, 20) * 30,
+    sizeCalculation: approximateObjectSize,
+  });
   beforeEach(async () => {
-    services = await startSubgraphsAndGateway(fixtures, { gatewayConfig: { queryPlannerConfig: { cache } } });
+    services = await startSubgraphsAndGateway(fixtures, {
+      gatewayConfig: { queryPlannerConfig: { cache } },
+    });
   });
 
   it(`cached query plan`, async () => {
@@ -43,7 +47,7 @@ describe('caching', () => {
     `;
 
     await services.queryGateway(query);
-    const queryHash:string = createHash('sha256').update(query).digest('hex');
+    const queryHash: string = createHash('sha256').update(query).digest('hex');
     expect(await cache.get(queryHash)).toBeTruthy();
   });
 
diff --git a/gateway-js/src/__tests__/gateway/extensions.test.ts b/gateway-js/src/__tests__/gateway/extensions.test.ts
index c1a630a0f..e862c3b70 100644
--- a/gateway-js/src/__tests__/gateway/extensions.test.ts
+++ b/gateway-js/src/__tests__/gateway/extensions.test.ts
@@ -19,7 +19,9 @@ describe('addExtensions', () => {
 
   it('has extensions on loaded schemas', async () => {
     const { schema } = await gateway.load();
-    expect(schema.extensions).toEqual({ apollo: { gateway: { version: version } } });
+    expect(schema.extensions).toEqual({
+      apollo: { gateway: { version: version } },
+    });
   });
 
   it('has extensions on schema updates', async () => {
@@ -32,6 +34,8 @@ describe('addExtensions', () => {
     gateway.load();
 
     const { apiSchema } = await schemaChangeBlocker;
-    expect(apiSchema.extensions).toEqual({ apollo: { gateway: { version: version } } });
+    expect(apiSchema.extensions).toEqual({
+      apollo: { gateway: { version: version } },
+    });
   });
 });
diff --git a/gateway-js/src/__tests__/gateway/lifecycle-hooks.test.ts b/gateway-js/src/__tests__/gateway/lifecycle-hooks.test.ts
index 9b5bf0cb2..9ffa0790f 100644
--- a/gateway-js/src/__tests__/gateway/lifecycle-hooks.test.ts
+++ b/gateway-js/src/__tests__/gateway/lifecycle-hooks.test.ts
@@ -138,7 +138,9 @@ describe('lifecycle hooks', () => {
     const [firstCall, secondCall] = mockDidUpdate.mock.calls;
 
     // Note that we've composing our usual test fixtures here
-    const expectedFirstId = createHash('sha256').update(getTestingSupergraphSdl()).digest('hex');
+    const expectedFirstId = createHash('sha256')
+      .update(getTestingSupergraphSdl())
+      .digest('hex');
     expect(firstCall[0]!.compositionId).toEqual(expectedFirstId);
     // first call should have no second "previous" argument
     expect(firstCall[1]).toBeUndefined();
diff --git a/gateway-js/src/__tests__/gateway/opentelemetry.test.ts b/gateway-js/src/__tests__/gateway/opentelemetry.test.ts
index 03199e0ce..cb15b091d 100644
--- a/gateway-js/src/__tests__/gateway/opentelemetry.test.ts
+++ b/gateway-js/src/__tests__/gateway/opentelemetry.test.ts
@@ -1,8 +1,14 @@
 import gql from 'graphql-tag';
-import {ApolloGateway, LocalGraphQLDataSource} from '../../';
-import {fixtures, spanSerializer} from 'apollo-federation-integration-testsuite';
-import {InMemorySpanExporter, SimpleSpanProcessor} from '@opentelemetry/tracing'
-import {NodeTracerProvider} from '@opentelemetry/node';
+import { ApolloGateway, LocalGraphQLDataSource } from '../../';
+import {
+  fixtures,
+  spanSerializer,
+} from 'apollo-federation-integration-testsuite';
+import {
+  InMemorySpanExporter,
+  SimpleSpanProcessor,
+} from '@opentelemetry/tracing';
+import { NodeTracerProvider } from '@opentelemetry/node';
 import { buildSubgraphSchema } from '@apollo/subgraph';
 
 expect.addSnapshotSerializer(spanSerializer);
@@ -17,7 +23,12 @@ beforeEach(() => {
 });
 
 describe('opentelemetry', () => {
-  async function execute(executor: any, source: string, variables: any, operationName: string) {
+  async function execute(
+    executor: any,
+    source: string,
+    variables: any,
+    operationName: string,
+  ) {
     await executor({
       source,
       document: gql(source),
@@ -56,7 +67,7 @@ describe('opentelemetry', () => {
         }
       });
 
-      const {executor} = await gateway.load();
+      const { executor } = await gateway.load();
       return executor;
     }
 
@@ -161,7 +172,6 @@ describe('opentelemetry', () => {
     })
   });
 
-
   it('receives spans on fetch failure', async () => {
     const gateway = new ApolloGateway({
       localServiceList: fixtures,
diff --git a/gateway-js/src/__tests__/gateway/queryPlanCache.test.ts b/gateway-js/src/__tests__/gateway/queryPlanCache.test.ts
index 851ae5f05..914b5cdeb 100644
--- a/gateway-js/src/__tests__/gateway/queryPlanCache.test.ts
+++ b/gateway-js/src/__tests__/gateway/queryPlanCache.test.ts
@@ -9,7 +9,10 @@ import { QueryPlanner } from '@apollo/query-planner';
 import { unwrapSingleResultKind } from '../gateway/testUtils';
 
 it('caches the query plan for a request', async () => {
-  const buildQueryPlanSpy = jest.spyOn(QueryPlanner.prototype, 'buildQueryPlan');
+  const buildQueryPlanSpy = jest.spyOn(
+    QueryPlanner.prototype,
+    'buildQueryPlan',
+  );
 
   const localDataSources = Object.fromEntries(
     fixtures.map((f) => [
diff --git a/gateway-js/src/__tests__/gateway/queryPlannerConfig.test.ts b/gateway-js/src/__tests__/gateway/queryPlannerConfig.test.ts
index 8df2f5425..a7ad72ded 100644
--- a/gateway-js/src/__tests__/gateway/queryPlannerConfig.test.ts
+++ b/gateway-js/src/__tests__/gateway/queryPlannerConfig.test.ts
@@ -1,5 +1,5 @@
 import gql from 'graphql-tag';
-import { startSubgraphsAndGateway, Services } from './testUtils'
+import { startSubgraphsAndGateway, Services } from './testUtils';
 
 let services: Services;
 
@@ -33,10 +33,10 @@ describe('`debug.bypassPlannerForSingleSubgraph` config', () => {
           b: {
             x: 1,
             y: 'foo',
-          }
+          },
         }),
-      }
-    }
+      },
+    },
   };
 
   const query = `
@@ -72,22 +72,21 @@ describe('`debug.bypassPlannerForSingleSubgraph` config', () => {
 
     const queryPlanner = services.gateway.__testing().queryPlanner!;
     // If the query planner is genuinely used, we shoud have evaluated 1 plan.
-    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(1);
+    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(
+      1,
+    );
   });
 
   it('works when enabled', async () => {
-    services = await startSubgraphsAndGateway(
-      [subgraph],
-      {
-        gatewayConfig: {
-          queryPlannerConfig: {
-            debug: {
-              bypassPlannerForSingleSubgraph: true,
-            }
-          }
-        }
-      }
-    );
+    services = await startSubgraphsAndGateway([subgraph], {
+      gatewayConfig: {
+        queryPlannerConfig: {
+          debug: {
+            bypassPlannerForSingleSubgraph: true,
+          },
+        },
+      },
+    });
 
     const response = await services.queryGateway(query);
     const result = await response.json();
@@ -95,7 +94,8 @@ describe('`debug.bypassPlannerForSingleSubgraph` config', () => {
 
     const queryPlanner = services.gateway.__testing().queryPlanner!;
     // The `bypassPlannerForSingleSubgraph` doesn't evaluate anything. It's use is the only case where `evaluatedPlanCount` can be 0.
-    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(0);
+    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(
+      0,
+    );
   });
 });
-
diff --git a/gateway-js/src/__tests__/gateway/reporting.test.ts b/gateway-js/src/__tests__/gateway/reporting.test.ts
index 3be495036..c2f278469 100644
--- a/gateway-js/src/__tests__/gateway/reporting.test.ts
+++ b/gateway-js/src/__tests__/gateway/reporting.test.ts
@@ -92,22 +92,19 @@ describe('reporting', () => {
         return 'ok';
       });
 
-    services = await startSubgraphsAndGateway(
-      fixtures,
-      {
-        gatewayServerConfig: {
-          apollo: {
-            key: 'service:foo:bar',
-            graphRef: 'foo@current',
-          },
-          plugins: [
-            ApolloServerPluginUsageReporting({
-              sendReportsImmediately: true,
-            }),
-          ],
+    services = await startSubgraphsAndGateway(fixtures, {
+      gatewayServerConfig: {
+        apollo: {
+          key: 'service:foo:bar',
+          graphRef: 'foo@current',
         },
-      }
-    );
+        plugins: [
+          ApolloServerPluginUsageReporting({
+            sendReportsImmediately: true,
+          }),
+        ],
+      },
+    });
   });
 
   afterEach(async () => {
@@ -134,9 +131,12 @@ describe('reporting', () => {
     `;
 
     const result = await toPromise(
-      execute(createHttpLink({ uri: services.gatewayUrl, fetch: fetch as any }), {
-        query,
-      }),
+      execute(
+        createHttpLink({ uri: services.gatewayUrl, fetch: fetch as any }),
+        {
+          query,
+        },
+      ),
     );
     expect(result).toMatchInlineSnapshot(`
       Object {
diff --git a/gateway-js/src/__tests__/gateway/supergraphSdl.test.ts b/gateway-js/src/__tests__/gateway/supergraphSdl.test.ts
index 1854d0bdf..4e441fdab 100644
--- a/gateway-js/src/__tests__/gateway/supergraphSdl.test.ts
+++ b/gateway-js/src/__tests__/gateway/supergraphSdl.test.ts
@@ -4,7 +4,10 @@ import {
   SubgraphHealthCheckFunction,
   SupergraphSdlUpdateFunction,
 } from '@apollo/gateway';
-import { accounts, fixturesWithUpdate } from 'apollo-federation-integration-testsuite';
+import {
+  accounts,
+  fixturesWithUpdate,
+} from 'apollo-federation-integration-testsuite';
 import { createHash } from '@apollo/utils.createhash';
 import { ApolloServer } from '@apollo/server';
 import { startStandaloneServer } from '@apollo/server/standalone';
@@ -54,7 +57,9 @@ afterEach(async () => {
   }
 });
 
-const testingFixturesDefaultCompositionId = createHash('sha256').update(getTestingSupergraphSdl()).digest('hex');
+const testingFixturesDefaultCompositionId = createHash('sha256')
+  .update(getTestingSupergraphSdl())
+  .digest('hex');
 
 describe('Using supergraphSdl static configuration', () => {
   it('successfully starts and serves requests to the proper services', async () => {
@@ -64,7 +69,6 @@ describe('Using supergraphSdl static configuration', () => {
       .post('/', { query: '{me{username}}', variables: {} })
       .reply(200, { data: { me: { username: '@apollo-user' } } });
 
-
     const result = await server.executeOperation({
       query: '{ me { username } }',
     });
@@ -185,9 +189,7 @@ describe('Using supergraphSdl dynamic configuration', () => {
     await gateway.load();
     const { state, compositionId } = gateway.__testing();
     expect(state.phase).toEqual('loaded');
-    expect(compositionId).toEqual(
-      testingFixturesDefaultCompositionId,
-    );
+    expect(compositionId).toEqual(testingFixturesDefaultCompositionId);
 
     await gateway.stop();
     expect(cleanup).toHaveBeenCalledTimes(1);
@@ -210,9 +212,7 @@ describe('Using supergraphSdl dynamic configuration', () => {
     await gateway.load();
     const { state, compositionId } = gateway.__testing();
     expect(state.phase).toEqual('loaded');
-    expect(compositionId).toEqual(
-      testingFixturesDefaultCompositionId,
-    );
+    expect(compositionId).toEqual(testingFixturesDefaultCompositionId);
 
     await expect(healthCheckCallback!(supergraphSdl)).resolves.toBeUndefined();
   });
@@ -292,9 +292,7 @@ describe('Using supergraphSdl dynamic configuration', () => {
       await gateway.load();
       const { state, compositionId } = gateway.__testing();
       expect(state.phase).toEqual('loaded');
-      expect(compositionId).toEqual(
-        testingFixturesDefaultCompositionId
-      );
+      expect(compositionId).toEqual(testingFixturesDefaultCompositionId);
 
       await expect(healthCheckCallback!(supergraphSdl)).rejects.toThrowError(
         /The gateway subgraphs health check failed\. Updating to the provided `supergraphSdl` will likely result in future request failures to subgraphs\. The following error occurred during the health check/,
diff --git a/gateway-js/src/__tests__/httpSpec.test.ts b/gateway-js/src/__tests__/httpSpec.test.ts
index 5dfa09fc3..0955b2c1d 100644
--- a/gateway-js/src/__tests__/httpSpec.test.ts
+++ b/gateway-js/src/__tests__/httpSpec.test.ts
@@ -12,7 +12,7 @@ describe('httpSpecTests.ts', () => {
   beforeAll(async () => {
     gatewayServer = new ApolloServer({
       gateway: new ApolloGateway({
-        supergraphSdl: getTestingSupergraphSdl()
+        supergraphSdl: getTestingSupergraphSdl(),
       }),
       // The test doesn't know we should send apollo-require-preflight along
       // with GETs. We could override `fetchFn` to add it but this seems simple enough.
diff --git a/gateway-js/src/__tests__/integration/abstract-types.test.ts b/gateway-js/src/__tests__/integration/abstract-types.test.ts
index 9decdf99d..8cc4ed0e3 100644
--- a/gateway-js/src/__tests__/integration/abstract-types.test.ts
+++ b/gateway-js/src/__tests__/integration/abstract-types.test.ts
@@ -1,6 +1,10 @@
 import { execute } from '../execution-utils';
 
-import { astSerializer, fed2gql as gql, queryPlanSerializer } from 'apollo-federation-integration-testsuite';
+import {
+  astSerializer,
+  fed2gql as gql,
+  queryPlanSerializer,
+} from 'apollo-federation-integration-testsuite';
 
 expect.addSnapshotSerializer(astSerializer);
 expect.addSnapshotSerializer(queryPlanSerializer);
@@ -823,7 +827,7 @@ describe('unions', () => {
 });
 
 describe("doesn't result in duplicate fetches", () => {
-  it("when exploding types", async () => {
+  it('when exploding types', async () => {
     const query = `#graphql
       query {
         topProducts {
@@ -1045,7 +1049,7 @@ describe("doesn't result in duplicate fetches", () => {
     `);
   });
 
-it("when including the same nested fields under different type conditions", async () => {
+  it('when including the same nested fields under different type conditions', async () => {
     const query = `#graphql
       query {
         topProducts {
@@ -1245,8 +1249,8 @@ it("when including the same nested fields under different type conditions", asyn
     `);
   });
 
-it('when including multiple nested fields to the same service under different type conditions', async () => {
-  const query = `#graphql
+  it('when including multiple nested fields to the same service under different type conditions', async () => {
+    const query = `#graphql
     query {
       topProducts {
         ... on Book {
@@ -1479,10 +1483,10 @@ it('when including multiple nested fields to the same service under different ty
         },
       }
     `);
-});
+  });
 
-it('when exploding types through multiple levels', async () => {
-  const query = `#graphql
+  it('when exploding types through multiple levels', async () => {
+    const query = `#graphql
     query {
       productsByCategory {
         name
@@ -1506,90 +1510,90 @@ it('when exploding types through multiple levels', async () => {
     }
   `;
 
-  const { queryPlan, errors } = await execute({ query }, [
-    {
-      name: 'accounts',
-      typeDefs: gql`
-        type User @key(fields: "id") {
-          id: ID!
-          name: String
-          username: String @shareable
-        }
-      `,
-    },
-    {
-      name: 'products',
-      typeDefs: gql`
-        type Book implements Product @key(fields: "isbn") {
-          isbn: String!
-          title: String
-          year: Int
-          name: String
-          price: Int
-        }
+    const { queryPlan, errors } = await execute({ query }, [
+      {
+        name: 'accounts',
+        typeDefs: gql`
+          type User @key(fields: "id") {
+            id: ID!
+            name: String
+            username: String @shareable
+          }
+        `,
+      },
+      {
+        name: 'products',
+        typeDefs: gql`
+          type Book implements Product @key(fields: "isbn") {
+            isbn: String!
+            title: String
+            year: Int
+            name: String
+            price: Int
+          }
 
-        type Furniture implements Product @key(fields: "sku") {
-          sku: String!
-          name: String
-          price: Int
-          weight: Int
-        }
+          type Furniture implements Product @key(fields: "sku") {
+            sku: String!
+            name: String
+            price: Int
+            weight: Int
+          }
 
-        interface Product {
-          name: String
-          price: Int
-        }
+          interface Product {
+            name: String
+            price: Int
+          }
 
-        extend type Query {
-          productsByCategory: [ProductCategory]
-        }
+          extend type Query {
+            productsByCategory: [ProductCategory]
+          }
 
-        interface ProductCategory {
-          name: String!
-        }
+          interface ProductCategory {
+            name: String!
+          }
 
-        type BookCategory implements ProductCategory {
-          name: String!
-          items: [Book]
-        }
+          type BookCategory implements ProductCategory {
+            name: String!
+            items: [Book]
+          }
 
-        type FurnitureCategory implements ProductCategory {
-          name: String!
-          items: [Furniture]
-        }
-      `,
-    },
-    {
-      name: 'reviews',
-      typeDefs: gql`
-        extend type Book implements Product @key(fields: "isbn") {
-          isbn: String! @external
-          reviews: [Review]
-        }
-        extend type Furniture implements Product @key(fields: "sku") {
-          sku: String! @external
-          reviews: [Review]
-        }
-        extend interface Product {
-          reviews: [Review]
-        }
-        type Review @key(fields: "id") {
-          id: ID!
-          body: String
-          author: User @provides(fields: "username")
-          product: Product
-        }
-        extend type User @key(fields: "id") {
-          id: ID! @external
-          username: String @external
-          reviews: [Review]
-        }
-      `,
-    },
-  ]);
+          type FurnitureCategory implements ProductCategory {
+            name: String!
+            items: [Furniture]
+          }
+        `,
+      },
+      {
+        name: 'reviews',
+        typeDefs: gql`
+          extend type Book implements Product @key(fields: "isbn") {
+            isbn: String! @external
+            reviews: [Review]
+          }
+          extend type Furniture implements Product @key(fields: "sku") {
+            sku: String! @external
+            reviews: [Review]
+          }
+          extend interface Product {
+            reviews: [Review]
+          }
+          type Review @key(fields: "id") {
+            id: ID!
+            body: String
+            author: User @provides(fields: "username")
+            product: Product
+          }
+          extend type User @key(fields: "id") {
+            id: ID! @external
+            username: String @external
+            reviews: [Review]
+          }
+        `,
+      },
+    ]);
 
-  expect(errors).toBeUndefined();
-  expect(queryPlan).toMatchInlineSnapshot(`
+    expect(errors).toBeUndefined();
+    expect(queryPlan).toMatchInlineSnapshot(`
     QueryPlan {
       Sequence {
         Fetch(service: "products") {
@@ -1643,9 +1647,9 @@ it('when exploding types through multiple levels', async () => {
       },
     }
   `);
-});
+  });
 
-it("when including the same nested fields under different type conditions that are split between services", async () => {
+  it('when including the same nested fields under different type conditions that are split between services', async () => {
     const query = `#graphql
       query {
         topProducts {
diff --git a/gateway-js/src/__tests__/integration/boolean.test.ts b/gateway-js/src/__tests__/integration/boolean.test.ts
index 2f82477d3..1ede900e2 100644
--- a/gateway-js/src/__tests__/integration/boolean.test.ts
+++ b/gateway-js/src/__tests__/integration/boolean.test.ts
@@ -1,5 +1,8 @@
 import { execute } from '../execution-utils';
-import { astSerializer, queryPlanSerializer } from 'apollo-federation-integration-testsuite';
+import {
+  astSerializer,
+  queryPlanSerializer,
+} from 'apollo-federation-integration-testsuite';
 
 expect.addSnapshotSerializer(astSerializer);
 expect.addSnapshotSerializer(queryPlanSerializer);
@@ -91,11 +94,26 @@ describe('@skip', () => {
 
     expect(data).toEqual({
       topReviews: [
-        { body: 'Love it!', author: { name: { first: 'Ada', last: 'Lovelace' } } },
-        { body: 'Too expensive.', author: { name: { first: 'Ada', last: 'Lovelace' } } },
-        { body: 'Could be better.', author: { name: { first: 'Alan', last: 'Turing' } } },
-        { body: 'Prefer something else.', author: { name: { first: 'Alan', last: 'Turing' } } },
-        { body: 'Wish I had read this before.', author: { name: { first: 'Alan', last: 'Turing' } } },
+        {
+          body: 'Love it!',
+          author: { name: { first: 'Ada', last: 'Lovelace' } },
+        },
+        {
+          body: 'Too expensive.',
+          author: { name: { first: 'Ada', last: 'Lovelace' } },
+        },
+        {
+          body: 'Could be better.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
+        {
+          body: 'Prefer something else.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
+        {
+          body: 'Wish I had read this before.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
       ],
     });
 
@@ -127,15 +145,33 @@ describe('@skip', () => {
 
     expect(data).toEqual({
       topReviews: [
-        { body: 'Love it!', author: { name: { first: 'Ada', last: 'Lovelace' } } },
-        { body: 'Too expensive.', author: { name: { first: 'Ada', last: 'Lovelace' } } },
-        { body: 'Could be better.', author: { name: { first: 'Alan', last: 'Turing' } } },
-        { body: 'Prefer something else.', author: { name: { first: 'Alan', last: 'Turing' } } },
-        { body: 'Wish I had read this before.', author: { name: { first: 'Alan', last: 'Turing' } } },
+        {
+          body: 'Love it!',
+          author: { name: { first: 'Ada', last: 'Lovelace' } },
+        },
+        {
+          body: 'Too expensive.',
+          author: { name: { first: 'Ada', last: 'Lovelace' } },
+        },
+        {
+          body: 'Could be better.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
+        {
+          body: 'Prefer something else.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
+        {
+          body: 'Wish I had read this before.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
       ],
     });
 
-    const variables = { definitions: operation.variableDefinitions, values: {skip} };
+    const variables = {
+      definitions: operation.variableDefinitions,
+      values: { skip },
+    };
     expect(queryPlan).toCallService('reviews', variables);
     expect(queryPlan).toCallService('accounts', variables);
   });
@@ -227,11 +263,26 @@ describe('@include', () => {
 
     expect(data).toEqual({
       topReviews: [
-        { body: 'Love it!', author: { name: { first: 'Ada', last: 'Lovelace' } } },
-        { body: 'Too expensive.', author: { name: { first: 'Ada', last: 'Lovelace' } } },
-        { body: 'Could be better.', author: { name: { first: 'Alan', last: 'Turing' } } },
-        { body: 'Prefer something else.', author: { name: { first: 'Alan', last: 'Turing' } } },
-        { body: 'Wish I had read this before.', author: { name: { first: 'Alan', last: 'Turing' } } },
+        {
+          body: 'Love it!',
+          author: { name: { first: 'Ada', last: 'Lovelace' } },
+        },
+        {
+          body: 'Too expensive.',
+          author: { name: { first: 'Ada', last: 'Lovelace' } },
+        },
+        {
+          body: 'Could be better.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
+        {
+          body: 'Prefer something else.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
+        {
+          body: 'Wish I had read this before.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
       ],
     });
 
@@ -264,15 +315,33 @@ describe('@include', () => {
 
     expect(data).toEqual({
       topReviews: [
-        { body: 'Love it!', author: { name: { first: 'Ada', last: 'Lovelace' } } },
-        { body: 'Too expensive.', author: { name: { first: 'Ada', last: 'Lovelace' } } },
-        { body: 'Could be better.', author: { name: { first: 'Alan', last: 'Turing' } } },
-        { body: 'Prefer something else.', author: { name: { first: 'Alan', last: 'Turing' } } },
-        { body: 'Wish I had read this before.', author: { name: { first: 'Alan', last: 'Turing' } } },
+        {
+          body: 'Love it!',
+          author: { name: { first: 'Ada', last: 'Lovelace' } },
+        },
+        {
+          body: 'Too expensive.',
+          author: { name: { first: 'Ada', last: 'Lovelace' } },
+        },
+        {
+          body: 'Could be better.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
+        {
+          body: 'Prefer something else.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
+        {
+          body: 'Wish I had read this before.',
+          author: { name: { first: 'Alan', last: 'Turing' } },
+        },
       ],
     });
 
-    const variables = { definitions: operation.variableDefinitions, values: {include} };
+    const variables = {
+      definitions: operation.variableDefinitions,
+      values: { include },
+    };
     expect(queryPlan).toCallService('accounts', variables);
     expect(queryPlan).toCallService('reviews', variables);
   });
diff --git a/gateway-js/src/__tests__/integration/complex-key.test.ts b/gateway-js/src/__tests__/integration/complex-key.test.ts
index 4ded8ba97..999b0b98f 100644
--- a/gateway-js/src/__tests__/integration/complex-key.test.ts
+++ b/gateway-js/src/__tests__/integration/complex-key.test.ts
@@ -1,6 +1,9 @@
 import gql from 'graphql-tag';
 import { execute, ServiceDefinitionModule } from '../execution-utils';
-import { astSerializer, queryPlanSerializer } from 'apollo-federation-integration-testsuite';
+import {
+  astSerializer,
+  queryPlanSerializer,
+} from 'apollo-federation-integration-testsuite';
 
 expect.addSnapshotSerializer(astSerializer);
 expect.addSnapshotSerializer(queryPlanSerializer);
@@ -85,13 +88,13 @@ const userService: ServiceDefinitionModule = {
     User: {
       __resolveReference(reference) {
         return users.find(
-          user =>
+          (user) =>
             user.id === reference.id &&
             user.organizationId === reference.organization.id,
         );
       },
       organization(user) {
-        return organizations.find(org => org.id === user.organizationId);
+        return organizations.find((org) => org.id === user.organizationId);
       },
     },
   },
diff --git a/gateway-js/src/__tests__/integration/configuration.test.ts b/gateway-js/src/__tests__/integration/configuration.test.ts
index 5af71bec1..cbe5e7114 100644
--- a/gateway-js/src/__tests__/integration/configuration.test.ts
+++ b/gateway-js/src/__tests__/integration/configuration.test.ts
@@ -201,9 +201,9 @@ describe('gateway startup errors', () => {
     }
 
     const expected =
-      "A valid schema couldn't be composed. The following composition errors were found:\n"
-    + '	[accounts] On type "User", for @key(fields: "id"): Cannot query field "id" on type "User" (the field should either be added to this subgraph or, if it should not be resolved by this subgraph, you need to add it to this subgraph with @external).\n'
-    + '	[accounts] On type "Account", for @key(fields: "id"): Cannot query field "id" on type "Account" (the field should either be added to this subgraph or, if it should not be resolved by this subgraph, you need to add it to this subgraph with @external).'
+      "A valid schema couldn't be composed. The following composition errors were found:\n" +
+      '	[accounts] On type "User", for @key(fields: "id"): Cannot query field "id" on type "User" (the field should either be added to this subgraph or, if it should not be resolved by this subgraph, you need to add it to this subgraph with @external).\n' +
+      '	[accounts] On type "Account", for @key(fields: "id"): Cannot query field "id" on type "Account" (the field should either be added to this subgraph or, if it should not be resolved by this subgraph, you need to add it to this subgraph with @external).';
     expect(err.message).toBe(expected);
   });
 });
@@ -352,7 +352,7 @@ describe('deprecation warnings', () => {
 
     try {
       await gateway.load();
-    // gateway will throw since we're not providing an actual service list, disregard
+      // gateway will throw since we're not providing an actual service list, disregard
     } catch {}
 
     expect(logger.warn).toHaveBeenCalledWith(
diff --git a/gateway-js/src/__tests__/integration/custom-directives.test.ts b/gateway-js/src/__tests__/integration/custom-directives.test.ts
index f6915fd9a..22caea0bb 100644
--- a/gateway-js/src/__tests__/integration/custom-directives.test.ts
+++ b/gateway-js/src/__tests__/integration/custom-directives.test.ts
@@ -98,7 +98,7 @@ describe('custom executable directives', () => {
   });
 
   // With relaxed composition, instead of erroring out if a directive is not declared everywhere, we compose but don't
-  // include the directive in the supergraph and generate a hint. So the following test will complain that @stream 
+  // include the directive in the supergraph and generate a hint. So the following test will complain that @stream
   // is unknown in the query. Not that the hints tests do test we properly raise an hint in that case.
   it.skip("returns validation errors when directives aren't present across all services", async () => {
     const invalidService = {
diff --git a/gateway-js/src/__tests__/integration/fragments.test.ts b/gateway-js/src/__tests__/integration/fragments.test.ts
index 939cac59c..9c08c1b1e 100644
--- a/gateway-js/src/__tests__/integration/fragments.test.ts
+++ b/gateway-js/src/__tests__/integration/fragments.test.ts
@@ -131,7 +131,7 @@ it('supports multiple named fragments (one level, mixed ordering)', async () =>
       name: {
         first: 'Ada',
         last: 'Lovelace',
-      }
+      },
     },
   });
 
diff --git a/gateway-js/src/__tests__/integration/list-key.test.ts b/gateway-js/src/__tests__/integration/list-key.test.ts
index cb7e84b90..5f51baa4e 100644
--- a/gateway-js/src/__tests__/integration/list-key.test.ts
+++ b/gateway-js/src/__tests__/integration/list-key.test.ts
@@ -1,6 +1,9 @@
 import gql from 'graphql-tag';
 import { execute, ServiceDefinitionModule } from '../execution-utils';
-import { astSerializer, queryPlanSerializer } from 'apollo-federation-integration-testsuite';
+import {
+  astSerializer,
+  queryPlanSerializer,
+} from 'apollo-federation-integration-testsuite';
 
 expect.addSnapshotSerializer(astSerializer);
 expect.addSnapshotSerializer(queryPlanSerializer);
@@ -62,7 +65,7 @@ const userService: ServiceDefinitionModule = {
   resolvers: {
     User: {
       __resolveReference(reference) {
-        return users.find(user => listsAreEqual(user.id, reference.id));
+        return users.find((user) => listsAreEqual(user.id, reference.id));
       },
     },
   },
diff --git a/gateway-js/src/__tests__/integration/logger.test.ts b/gateway-js/src/__tests__/integration/logger.test.ts
index c323b9c7a..d990673a9 100644
--- a/gateway-js/src/__tests__/integration/logger.test.ts
+++ b/gateway-js/src/__tests__/integration/logger.test.ts
@@ -1,16 +1,17 @@
 import { ApolloGateway } from '../..';
 import type { Logger } from '@apollo/utils.logger';
-import { PassThrough } from "stream";
+import { PassThrough } from 'stream';
 
-import * as winston from "winston";
+import * as winston from 'winston';
 import WinstonTransport from 'winston-transport';
-import * as bunyan from "bunyan";
-import * as loglevel from "loglevel";
-import * as log4js from "log4js";
+import * as bunyan from 'bunyan';
+import * as loglevel from 'loglevel';
+import * as log4js from 'log4js';
 
-const LOWEST_LOG_LEVEL = "debug";
+const LOWEST_LOG_LEVEL = 'debug';
 
-const KNOWN_DEBUG_MESSAGE = "Gateway successfully initialized (but not yet loaded)";
+const KNOWN_DEBUG_MESSAGE =
+  'Gateway successfully initialized (but not yet loaded)';
 
 async function triggerKnownDebugMessage(logger: Logger) {
   // Trigger a known error.
@@ -18,13 +19,13 @@ async function triggerKnownDebugMessage(logger: Logger) {
   // message outside of the constructor, but it seemed worth testing
   // the compatibility with `ApolloGateway` itself rather than generically.
   // The error does not matter, so it is caught and ignored.
-  await new ApolloGateway({ logger }).load().catch(_e => undefined);
+  await new ApolloGateway({ logger }).load().catch((_e) => undefined);
 }
 
-describe("logger", () => {
+describe('logger', () => {
   it("works with 'winston'", async () => {
     const sink = jest.fn();
-    const transport = new class extends WinstonTransport {
+    const transport = new (class extends WinstonTransport {
       constructor() {
         super({
           format: winston.format.json(),
@@ -34,16 +35,18 @@ describe("logger", () => {
       log(info: any) {
         sink(info);
       }
-    };
+    })();
 
     const logger = winston.createLogger({ level: 'debug' }).add(transport);
 
     await triggerKnownDebugMessage(logger);
 
-    expect(sink).toHaveBeenCalledWith(expect.objectContaining({
-      level: LOWEST_LOG_LEVEL,
-      message: KNOWN_DEBUG_MESSAGE,
-    }));
+    expect(sink).toHaveBeenCalledWith(
+      expect.objectContaining({
+        level: LOWEST_LOG_LEVEL,
+        message: KNOWN_DEBUG_MESSAGE,
+      }),
+    );
   });
 
   it("works with 'bunyan'", async () => {
@@ -51,30 +54,36 @@ describe("logger", () => {
 
     // Bunyan uses streams for its logging implementations.
     const writable = new PassThrough();
-    writable.on("data", data => sink(JSON.parse(data.toString())));
+    writable.on('data', (data) => sink(JSON.parse(data.toString())));
 
     const logger = bunyan.createLogger({
-      name: "test-logger-bunyan",
-      streams: [{
-        level: LOWEST_LOG_LEVEL,
-        stream: writable,
-      }]
+      name: 'test-logger-bunyan',
+      streams: [
+        {
+          level: LOWEST_LOG_LEVEL,
+          stream: writable,
+        },
+      ],
     });
 
     await triggerKnownDebugMessage(logger);
 
-    expect(sink).toHaveBeenCalledWith(expect.objectContaining({
-      level: bunyan.DEBUG,
-      msg: KNOWN_DEBUG_MESSAGE,
-    }));
+    expect(sink).toHaveBeenCalledWith(
+      expect.objectContaining({
+        level: bunyan.DEBUG,
+        msg: KNOWN_DEBUG_MESSAGE,
+      }),
+    );
   });
 
   it("works with 'loglevel'", async () => {
     const sink = jest.fn();
 
-    const logger = loglevel.getLogger("test-logger-loglevel")
-    logger.methodFactory = (_methodName, level): loglevel.LoggingMethod =>
-      (message) => sink({ level, message });
+    const logger = loglevel.getLogger('test-logger-loglevel');
+    logger.methodFactory =
+      (_methodName, level): loglevel.LoggingMethod =>
+      (message) =>
+        sink({ level, message });
 
     // The `setLevel` method must be called after overwriting `methodFactory`.
     // This is an intentional API design pattern of the loglevel package:
@@ -96,17 +105,17 @@ describe("logger", () => {
       appenders: {
         custom: {
           type: {
-            configure: () =>
-              (loggingEvent: log4js.LoggingEvent) => sink(loggingEvent)
-          }
-        }
+            configure: () => (loggingEvent: log4js.LoggingEvent) =>
+              sink(loggingEvent),
+          },
+        },
       },
       categories: {
         default: {
           appenders: ['custom'],
           level: LOWEST_LOG_LEVEL,
-        }
-      }
+        },
+      },
     });
 
     const logger = log4js.getLogger();
@@ -114,9 +123,11 @@ describe("logger", () => {
 
     await triggerKnownDebugMessage(logger);
 
-    expect(sink).toHaveBeenCalledWith(expect.objectContaining({
-      level: log4js.levels.DEBUG,
-      data: [KNOWN_DEBUG_MESSAGE],
-    }));
+    expect(sink).toHaveBeenCalledWith(
+      expect.objectContaining({
+        level: log4js.levels.DEBUG,
+        data: [KNOWN_DEBUG_MESSAGE],
+      }),
+    );
   });
 });
diff --git a/gateway-js/src/__tests__/integration/managed.test.ts b/gateway-js/src/__tests__/integration/managed.test.ts
index 07d5b2b3b..af48a3dc0 100644
--- a/gateway-js/src/__tests__/integration/managed.test.ts
+++ b/gateway-js/src/__tests__/integration/managed.test.ts
@@ -262,7 +262,7 @@ describe('Managed gateway with explicit UplinkSupergraphManager', () => {
 
       gateway = new ApolloGateway({
         logger,
-        supergraphSdl: uplinkManager
+        supergraphSdl: uplinkManager,
       });
 
       await expect(gateway.load()).resolves.not.toThrow();
diff --git a/gateway-js/src/__tests__/integration/multiple-key.test.ts b/gateway-js/src/__tests__/integration/multiple-key.test.ts
index 772dbced1..e4100bddc 100644
--- a/gateway-js/src/__tests__/integration/multiple-key.test.ts
+++ b/gateway-js/src/__tests__/integration/multiple-key.test.ts
@@ -1,6 +1,9 @@
 import gql from 'graphql-tag';
 import { execute, ServiceDefinitionModule } from '../execution-utils';
-import { astSerializer, queryPlanSerializer } from 'apollo-federation-integration-testsuite';
+import {
+  astSerializer,
+  queryPlanSerializer,
+} from 'apollo-federation-integration-testsuite';
 
 expect.addSnapshotSerializer(astSerializer);
 expect.addSnapshotSerializer(queryPlanSerializer);
@@ -45,7 +48,7 @@ const reviewService: ServiceDefinitionModule = {
     },
     User: {
       reviews(user) {
-        return reviews.filter(review => review.authorId === user.id);
+        return reviews.filter((review) => review.authorId === user.id);
       },
     },
     Review: {
@@ -107,8 +110,8 @@ const userService: ServiceDefinitionModule = {
       group: () => ({ id: 1, name: 'Apollo GraphQL' }),
       __resolveReference(reference) {
         if (reference.ssn)
-          return users.find(user => user.ssn === reference.ssn);
-        else return users.find(user => user.id === reference.id);
+          return users.find((user) => user.ssn === reference.ssn);
+        else return users.find((user) => user.id === reference.id);
       },
     },
   },
@@ -234,7 +237,7 @@ it('fetches keys as needed to reduce round trip queries', async () => {
     {
       query,
     },
-    [userService, reviewService, actuaryService]
+    [userService, reviewService, actuaryService],
   );
 
   expect(errors).toBeFalsy();
diff --git a/gateway-js/src/__tests__/integration/networkRequests.test.ts b/gateway-js/src/__tests__/integration/networkRequests.test.ts
index bd3826e6f..0dd055cbf 100644
--- a/gateway-js/src/__tests__/integration/networkRequests.test.ts
+++ b/gateway-js/src/__tests__/integration/networkRequests.test.ts
@@ -455,9 +455,9 @@ describe('Downstream service health checks', () => {
 
       // Update
       mockSupergraphSdlRequestSuccessIfAfter(
-          'originalId-1234',
-          'updatedId-5678',
-          getTestingSupergraphSdl(fixturesWithUpdate),
+        'originalId-1234',
+        'updatedId-5678',
+        getTestingSupergraphSdl(fixturesWithUpdate),
       );
       mockAllServicesHealthCheckSuccess();
 
@@ -499,8 +499,8 @@ describe('Downstream service health checks', () => {
 
       // Update (with one health check failure)
       mockSupergraphSdlRequestSuccessIfAfter(
-          'originalId-1234',
-          'updatedId-5678',
+        'originalId-1234',
+        'updatedId-5678',
         getTestingSupergraphSdl(fixturesWithUpdate),
       );
       mockServiceHealthCheck(accounts).reply(500);
diff --git a/gateway-js/src/__tests__/integration/provides.test.ts b/gateway-js/src/__tests__/integration/provides.test.ts
index 14c925139..f1473d1d9 100644
--- a/gateway-js/src/__tests__/integration/provides.test.ts
+++ b/gateway-js/src/__tests__/integration/provides.test.ts
@@ -12,7 +12,7 @@ it('does not have to go to another service when field is given', async () => {
     }
   `;
 
-  const { data, queryPlan } = await execute( {
+  const { data, queryPlan } = await execute({
     query,
   });
 
@@ -63,11 +63,30 @@ it('does not load fields provided even when going to other service', async () =>
 
   expect(data).toEqual({
     topReviews: [
-      { author: { username: '@ada', name: { first: 'Ada', last: 'Lovelace' } } },
-      { author: { username: '@ada', name: { first: 'Ada', last: 'Lovelace' } } },
-      { author: { username: '@complete', name: { first: 'Alan', last: 'Turing' } } },
-      { author: { username: '@complete', name: { first: 'Alan', last: 'Turing' } } },
-      { author: { username: '@complete', name: { first: 'Alan', last: 'Turing' } } },
+      {
+        author: { username: '@ada', name: { first: 'Ada', last: 'Lovelace' } },
+      },
+      {
+        author: { username: '@ada', name: { first: 'Ada', last: 'Lovelace' } },
+      },
+      {
+        author: {
+          username: '@complete',
+          name: { first: 'Alan', last: 'Turing' },
+        },
+      },
+      {
+        author: {
+          username: '@complete',
+          name: { first: 'Alan', last: 'Turing' },
+        },
+      },
+      {
+        author: {
+          username: '@complete',
+          name: { first: 'Alan', last: 'Turing' },
+        },
+      },
     ],
   });
 
diff --git a/gateway-js/src/__tests__/integration/requires.test.ts b/gateway-js/src/__tests__/integration/requires.test.ts
index 3e24d1b6f..e9bc39c5e 100644
--- a/gateway-js/src/__tests__/integration/requires.test.ts
+++ b/gateway-js/src/__tests__/integration/requires.test.ts
@@ -1,6 +1,9 @@
 import gql from 'graphql-tag';
 import { execute } from '../execution-utils';
-import { astSerializer, queryPlanSerializer } from 'apollo-federation-integration-testsuite';
+import {
+  astSerializer,
+  queryPlanSerializer,
+} from 'apollo-federation-integration-testsuite';
 
 expect.addSnapshotSerializer(astSerializer);
 expect.addSnapshotSerializer(queryPlanSerializer);
diff --git a/gateway-js/src/__tests__/integration/scope.test.ts b/gateway-js/src/__tests__/integration/scope.test.ts
index 9bcad9569..e730c9856 100644
--- a/gateway-js/src/__tests__/integration/scope.test.ts
+++ b/gateway-js/src/__tests__/integration/scope.test.ts
@@ -343,7 +343,7 @@ describe('scope', () => {
     `);
   });
 
-  it("merges nested conditions when possible", async () => {
+  it('merges nested conditions when possible', async () => {
     const query = `#graphql
     query GetProducts {
       topProducts {
diff --git a/gateway-js/src/__tests__/integration/unions.test.ts b/gateway-js/src/__tests__/integration/unions.test.ts
index bc8bd7fda..145325af4 100644
--- a/gateway-js/src/__tests__/integration/unions.test.ts
+++ b/gateway-js/src/__tests__/integration/unions.test.ts
@@ -1,5 +1,8 @@
 import gql from 'graphql-tag';
-import { astSerializer, queryPlanSerializer } from 'apollo-federation-integration-testsuite';
+import {
+  astSerializer,
+  queryPlanSerializer,
+} from 'apollo-federation-integration-testsuite';
 import { execute } from '../execution-utils';
 
 expect.addSnapshotSerializer(astSerializer);
@@ -24,35 +27,32 @@ it('handles multiple union type conditions that share a response name (media)',
     }
   `;
 
-  const { queryPlan, errors } = await execute(
-    { query },
-    [
-      {
-        name: 'contentService',
-        typeDefs: gql`
-          extend type Query {
-            content: Content
-          }
-          union Content = Audio | Video
-          type Audio {
-            media: AudioURL
-          }
-          type AudioURL {
-            url: String
-          }
-          type Video {
-            media: VideoAspectRatio
-          }
-          type VideoAspectRatio {
-            aspectRatio: String
-          }
-        `,
-        resolvers: {
-          Query: {},
-        },
+  const { queryPlan, errors } = await execute({ query }, [
+    {
+      name: 'contentService',
+      typeDefs: gql`
+        extend type Query {
+          content: Content
+        }
+        union Content = Audio | Video
+        type Audio {
+          media: AudioURL
+        }
+        type AudioURL {
+          url: String
+        }
+        type Video {
+          media: VideoAspectRatio
+        }
+        type VideoAspectRatio {
+          aspectRatio: String
+        }
+      `,
+      resolvers: {
+        Query: {},
       },
-    ],
-  );
+    },
+  ]);
 
   expect(errors).toBeUndefined();
   expect(queryPlan).toMatchInlineSnapshot(`
diff --git a/gateway-js/src/__tests__/integration/variables.test.ts b/gateway-js/src/__tests__/integration/variables.test.ts
index 15209f44f..ed70a7006 100644
--- a/gateway-js/src/__tests__/integration/variables.test.ts
+++ b/gateway-js/src/__tests__/integration/variables.test.ts
@@ -109,7 +109,7 @@ it('works with default variables in the schema', async () => {
         name: {
           first: 'Ada',
           last: 'Lovelace',
-        }
+        },
       },
     },
   });
diff --git a/gateway-js/src/__tests__/queryPlanCucumber.test.ts b/gateway-js/src/__tests__/queryPlanCucumber.test.ts
index 9b4648e43..8715db16e 100644
--- a/gateway-js/src/__tests__/queryPlanCucumber.test.ts
+++ b/gateway-js/src/__tests__/queryPlanCucumber.test.ts
@@ -7,13 +7,10 @@ import { getFederatedTestingSchema } from './execution-utils';
 import { operationFromDocument } from '@apollo/federation-internals';
 
 const buildQueryPlanFeature = loadFeature(
-  './gateway-js/src/__tests__/build-query-plan.feature'
+  './gateway-js/src/__tests__/build-query-plan.feature',
 );
 
-
-const features = [
-  buildQueryPlanFeature
-];
+const features = [buildQueryPlanFeature];
 
 features.forEach((feature) => {
   defineFeature(feature, (test) => {
@@ -28,26 +25,31 @@ features.forEach((feature) => {
         const givenQuery = () => {
           given(/^query$/im, (operation: string) => {
             operationDocument = gql(operation);
-          })
-        }
+          });
+        };
 
         const thenQueryPlanShouldBe = () => {
           then(/^query plan$/i, (expectedQueryPlan: string) => {
-            queryPlan = queryPlanner.buildQueryPlan(operationFromDocument(schema, operationDocument));
+            queryPlan = queryPlanner.buildQueryPlan(
+              operationFromDocument(schema, operationDocument),
+            );
 
             const parsedExpectedPlan = JSON.parse(expectedQueryPlan);
 
             expect(queryPlan).toEqual(parsedExpectedPlan);
-          })
-        }
+          });
+        };
 
         // step over each defined step in the .feature and execute the correct
         // matching step fn defined above
         scenario.steps.forEach(({ stepText }) => {
           const title = stepText.toLocaleLowerCase();
-          if (title === "query") givenQuery();
-          else if (title === "query plan") thenQueryPlanShouldBe();
-          else throw new Error(`Unrecognized steps used in "build-query-plan.feature"`);
+          if (title === 'query') givenQuery();
+          else if (title === 'query plan') thenQueryPlanShouldBe();
+          else
+            throw new Error(
+              `Unrecognized steps used in "build-query-plan.feature"`,
+            );
         });
       });
     });
diff --git a/gateway-js/src/__tests__/resultShaping.test.ts b/gateway-js/src/__tests__/resultShaping.test.ts
index 47a88d66c..a33602964 100644
--- a/gateway-js/src/__tests__/resultShaping.test.ts
+++ b/gateway-js/src/__tests__/resultShaping.test.ts
@@ -1,6 +1,9 @@
-import { buildSchemaFromAST, parseOperation } from "@apollo/federation-internals"
-import { gql } from "apollo-federation-integration-testsuite";
-import { computeResponse } from "../resultShaping";
+import {
+  buildSchemaFromAST,
+  parseOperation,
+} from '@apollo/federation-internals';
+import { gql } from 'apollo-federation-integration-testsuite';
+import { computeResponse } from '../resultShaping';
 
 const introspectionHandling = () => null;
 
@@ -39,27 +42,32 @@ describe('gateway post-processing', () => {
     `);
 
     const input = {
-      "t": {
-        "a": 0,
-        "b": 'testData',
-        "c": [{
-          __typename: 'P1',
-          id: 'foo',
-          x: 1,
-          y: 2,
-        }, {
-          __typename: 'P2',
-          x: 10,
-          y: 20,
-          w: 30,
-          z: 40,
-        }],
-        "d": 1,
+      t: {
+        a: 0,
+        b: 'testData',
+        c: [
+          {
+            __typename: 'P1',
+            id: 'foo',
+            x: 1,
+            y: 2,
+          },
+          {
+            __typename: 'P2',
+            x: 10,
+            y: 20,
+            w: 30,
+            z: 40,
+          },
+        ],
+        d: 1,
       },
-      "v": 42
-    }
+      v: 42,
+    };
 
-    const operation = parseOperation(schema, `
+    const operation = parseOperation(
+      schema,
+      `
       {
         t {
           a
@@ -75,13 +83,16 @@ describe('gateway post-processing', () => {
           }
         }
       }
-    `);
+    `,
+    );
 
-    expect(computeResponse({
-      operation,
-      input,
-      introspectionHandling,
-    })).toMatchInlineSnapshot(`
+    expect(
+      computeResponse({
+        operation,
+        input,
+        introspectionHandling,
+      }),
+    ).toMatchInlineSnapshot(`
       Object {
         "data": Object {
           "t": Object {
@@ -121,33 +132,38 @@ describe('gateway post-processing', () => {
     `);
 
     const tObj = {
-      "a": null,
-      "b": null,
-      "c": [24, null, 42, null],
-      "d": [24, null, 42, null],
-      "e": [24, null, 42, null],
-      "f": [24, null, 42, null],
-    }
+      a: null,
+      b: null,
+      c: [24, null, 42, null],
+      d: [24, null, 42, null],
+      e: [24, null, 42, null],
+      f: [24, null, 42, null],
+    };
 
     const input = {
-      "tNullable": tObj,
-      "tNonNullable": tObj,
-    }
+      tNullable: tObj,
+      tNonNullable: tObj,
+    };
 
     test('no propagation on nullable (non-list) type', () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           tNonNullable {
             a
           }
         }
-      `);
-
-      expect(computeResponse({
-        operation,
-        input,
-        introspectionHandling,
-      })).toMatchInlineSnapshot(`
+      `,
+      );
+
+      expect(
+        computeResponse({
+          operation,
+          input,
+          introspectionHandling,
+        }),
+      ).toMatchInlineSnapshot(`
         Object {
           "data": Object {
             "tNonNullable": Object {
@@ -160,13 +176,16 @@ describe('gateway post-processing', () => {
     });
 
     test('propagation on non-nullable (non-list) type', () => {
-      const operationNonNullable = parseOperation(schema, `
+      const operationNonNullable = parseOperation(
+        schema,
+        `
         {
           tNonNullable {
             b
           }
         }
-      `);
+      `,
+      );
 
       let res = computeResponse({
         operation: operationNonNullable,
@@ -183,14 +202,16 @@ describe('gateway post-processing', () => {
       `);
       expect(res.errors[0].path).toStrictEqual(['tNonNullable', 'b']);
 
-
-      const operationNullable = parseOperation(schema, `
+      const operationNullable = parseOperation(
+        schema,
+        `
         {
           tNullable {
             b
           }
         }
-      `);
+      `,
+      );
 
       res = computeResponse({
         operation: operationNullable,
@@ -211,19 +232,24 @@ describe('gateway post-processing', () => {
     });
 
     test('no propagation on nullable list type', () => {
-      const operation = parseOperation(schema, `
+      const operation = parseOperation(
+        schema,
+        `
         {
           tNonNullable {
             c
           }
         }
-      `);
-
-      expect(computeResponse({
-        operation,
-        input,
-        introspectionHandling,
-      })).toMatchInlineSnapshot(`
+      `,
+      );
+
+      expect(
+        computeResponse({
+          operation,
+          input,
+          introspectionHandling,
+        }),
+      ).toMatchInlineSnapshot(`
         Object {
           "data": Object {
             "tNonNullable": Object {
@@ -241,13 +267,16 @@ describe('gateway post-processing', () => {
     });
 
     test('no propagation on null elements of non-nullable list type with nullable inner element type', () => {
-      const operationNonNullable = parseOperation(schema, `
+      const operationNonNullable = parseOperation(
+        schema,
+        `
         {
           tNonNullable {
             d
           }
         }
-      `);
+      `,
+      );
 
       const res = computeResponse({
         operation: operationNonNullable,
@@ -272,13 +301,16 @@ describe('gateway post-processing', () => {
     });
 
     test('propagation on null elements of list type with non-nullable inner element type', () => {
-      const operationNonNullable = parseOperation(schema, `
+      const operationNonNullable = parseOperation(
+        schema,
+        `
         {
           tNonNullable {
             e
           }
         }
-      `);
+      `,
+      );
 
       const res = computeResponse({
         operation: operationNonNullable,
@@ -303,13 +335,16 @@ describe('gateway post-processing', () => {
     });
 
     test('propagation on null elements of non-nullable list type with non-nullable inner element type', () => {
-      const operationNonNullable = parseOperation(schema, `
+      const operationNonNullable = parseOperation(
+        schema,
+        `
         {
           tNonNullable {
             f
           }
         }
-      `);
+      `,
+      );
 
       let res = computeResponse({
         operation: operationNonNullable,
@@ -328,13 +363,16 @@ describe('gateway post-processing', () => {
       expect(res.errors[0].path).toStrictEqual(['tNonNullable', 'f', 1]);
       expect(res.errors[1].path).toStrictEqual(['tNonNullable', 'f', 3]);
 
-      const operationNullable = parseOperation(schema, `
+      const operationNullable = parseOperation(
+        schema,
+        `
         {
           tNullable {
             f
           }
         }
-      `);
+      `,
+      );
 
       res = computeResponse({
         operation: operationNullable,
@@ -365,14 +403,17 @@ describe('gateway post-processing', () => {
     `);
 
     const input = {
-      "x": 'foo',
-    }
+      x: 'foo',
+    };
 
-    const operation = parseOperation(schema, `
+    const operation = parseOperation(
+      schema,
+      `
       {
         x
       }
-    `);
+    `,
+    );
 
     const res = computeResponse({
       operation,
@@ -403,21 +444,23 @@ describe('gateway post-processing', () => {
     `);
 
     const input = {
-      "t": {
-        "a": 42,
-        "q": {
-          "t": {
-            "q": {
-              "t": {
-                "a": 24
+      t: {
+        a: 42,
+        q: {
+          t: {
+            q: {
+              t: {
+                a: 24,
               },
             },
           },
         },
       },
-    }
+    };
 
-    const operation = parseOperation(schema, `
+    const operation = parseOperation(
+      schema,
+      `
       {
         __typename
         t {
@@ -435,13 +478,16 @@ describe('gateway post-processing', () => {
           }
         }
       }
-    `);
+    `,
+    );
 
-    expect(computeResponse({
-      operation,
-      input,
-      introspectionHandling,
-    })).toMatchInlineSnapshot(`
+    expect(
+      computeResponse({
+        operation,
+        input,
+        introspectionHandling,
+      }),
+    ).toMatchInlineSnapshot(`
       Object {
         "data": Object {
           "__typename": "Query",
@@ -485,19 +531,21 @@ describe('gateway post-processing', () => {
     `);
 
     const input = {
-      "i": [
+      i: [
         {
-          "__typename": "A",
-          "x": 24,
+          __typename: 'A',
+          x: 24,
         },
         {
-          "__typename": "B",
-          "x": 42,
+          __typename: 'B',
+          x: 42,
         },
-      ]
-    }
+      ],
+    };
 
-    const operation = parseOperation(schema, `
+    const operation = parseOperation(
+      schema,
+      `
       {
         i {
           ... on I {
@@ -506,13 +554,16 @@ describe('gateway post-processing', () => {
           }
         }
       }
-    `);
+    `,
+    );
 
-    expect(computeResponse({
-      operation,
-      input,
-      introspectionHandling,
-    })).toMatchInlineSnapshot(`
+    expect(
+      computeResponse({
+        operation,
+        input,
+        introspectionHandling,
+      }),
+    ).toMatchInlineSnapshot(`
       Object {
         "data": Object {
           "i": Array [
@@ -543,12 +594,15 @@ describe('gateway post-processing', () => {
       included: 'world',
     };
 
-    const operation = parseOperation(schema, `#graphql
+    const operation = parseOperation(
+      schema,
+      `#graphql
       query DefaultedIfCondition($if: Boolean = true) {
         skipped: hello @skip(if: $if)
         included: hello @include(if: $if)
       }
-    `);
+    `,
+    );
 
     expect(
       computeResponse({
@@ -578,13 +632,16 @@ describe('gateway post-processing', () => {
       included: 'world',
     };
 
-    const operation = parseOperation(schema, `#graphql
+    const operation = parseOperation(
+      schema,
+      `#graphql
         # note that the default conditional is inverted from the previous test
       query DefaultedIfCondition($if: Boolean = false) {
         skipped: hello @skip(if: $if)
         included: hello @include(if: $if)
       }
-    `);
+    `,
+    );
 
     expect(
       computeResponse({
@@ -602,4 +659,4 @@ describe('gateway post-processing', () => {
       }
     `);
   });
-})
+});
diff --git a/gateway-js/src/dataRewrites.ts b/gateway-js/src/dataRewrites.ts
index f1d713f94..fafc8f9c6 100644
--- a/gateway-js/src/dataRewrites.ts
+++ b/gateway-js/src/dataRewrites.ts
@@ -35,8 +35,11 @@ function rewriteAtPathFunction(rewrite: FetchDataRewrite, fieldAtPath: string):
       };
     case 'KeyRenamer':
       return (obj) => {
-        obj[rewrite.renameKeyTo] = obj[fieldAtPath];
-        obj[fieldAtPath] = undefined;
+        const objAtPath = obj[fieldAtPath];
+        if (objAtPath) {
+          obj[rewrite.renameKeyTo] = obj[fieldAtPath];
+          obj[fieldAtPath] = undefined;
+        }
       };
   }
 }
@@ -63,7 +66,7 @@ function applyAtPath(schema: GraphQLSchema, path: string[], value: any, fct: (ob
     return;
   }
 
-  if (typeof value !== 'object') {
+  if (typeof value !== 'object' || value === null) {
     return;
   }
 
diff --git a/gateway-js/src/datasources/__tests__/LocalGraphQLDataSource.test.ts b/gateway-js/src/datasources/__tests__/LocalGraphQLDataSource.test.ts
index 2ffeb89fb..64c645dde 100644
--- a/gateway-js/src/datasources/__tests__/LocalGraphQLDataSource.test.ts
+++ b/gateway-js/src/datasources/__tests__/LocalGraphQLDataSource.test.ts
@@ -42,7 +42,7 @@ describe('constructing requests', () => {
       },
       incomingRequestContext: {
         context: { userId: 2 },
-      } as GatewayGraphQLRequestContext<{userId: number}>,
+      } as GatewayGraphQLRequestContext<{ userId: number }>,
       context: { userId: 2 },
     });
 
diff --git a/gateway-js/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts b/gateway-js/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts
index 537dc798e..74482c057 100644
--- a/gateway-js/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts
+++ b/gateway-js/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts
@@ -231,7 +231,11 @@ describe('constructing requests', () => {
 
     nock('https://api.example.com')
       .post('/foo', { query: '{ me { name } }' })
-      .reply(200, { data: { me: 'james' } }, {'content-type': 'application/graphql-response+json'});
+      .reply(
+        200,
+        { data: { me: 'james' } },
+        { 'content-type': 'application/graphql-response+json' },
+      );
 
     const { data } = await DataSource.process({
       ...defaultProcessOptions,
diff --git a/gateway-js/src/executeQueryPlan.ts b/gateway-js/src/executeQueryPlan.ts
index 53af6557c..0a6eacfa0 100644
--- a/gateway-js/src/executeQueryPlan.ts
+++ b/gateway-js/src/executeQueryPlan.ts
@@ -521,6 +521,11 @@ async function executeFetch(
 
         for (let i = 0; i < entities.length; i++) {
           const receivedEntity = receivedEntities[i];
+          const existingEntity = entities[representationToEntity[i]];
+          if (receivedEntity && !receivedEntity["__typename"]) {
+            const typename = existingEntity["__typename"];
+            receivedEntity["__typename"] = typename;
+          }
           applyRewrites(context.supergraphSchema, fetch.outputRewrites, receivedEntity);
           deepMerge(entities[representationToEntity[i]], receivedEntity);
         }
diff --git a/gateway-js/src/index.ts b/gateway-js/src/index.ts
index 0a7f9b35a..83814dacb 100644
--- a/gateway-js/src/index.ts
+++ b/gateway-js/src/index.ts
@@ -859,6 +859,13 @@ export class ApolloGateway implements GatewayInterface {
           }
 
           if (shouldShowQueryPlan) {
+            const queryPlanFormat =
+              request.http &&
+              request.http.headers &&
+              request.http.headers.has('Apollo-Query-Plan-Experimental-Format')
+                ? request.http.headers.get('Apollo-Query-Plan-Experimental-Format')
+                : 'prettified'
+
             // TODO: expose the query plan in a more flexible JSON format in the future
             // and rename this to `queryPlan`. Playground should cutover to use the new
             // option once we've built a way to print that representation.
@@ -867,7 +874,12 @@ export class ApolloGateway implements GatewayInterface {
             // still want to respond to Playground with something truthy since it depends
             // on this to decide that query plans are supported by this gateway.
             response.extensions = {
-              __queryPlanExperimental: serializedQueryPlan || true,
+              __queryPlanExperimental:
+                queryPlanFormat === 'prettified'
+                  ? serializedQueryPlan || true
+                  : queryPlanFormat === 'internal'
+                      ? queryPlan
+                      : true
             };
           }
           if (response.errors) {
diff --git a/gateway-js/src/schema-helper/__tests__/addExtensions.test.ts b/gateway-js/src/schema-helper/__tests__/addExtensions.test.ts
index 7d3f6fbc9..53b2553de 100644
--- a/gateway-js/src/schema-helper/__tests__/addExtensions.test.ts
+++ b/gateway-js/src/schema-helper/__tests__/addExtensions.test.ts
@@ -4,12 +4,14 @@ import { ApolloGraphQLSchemaExtensions } from '../../typings/graphql';
 const { version } = require('../../../package.json');
 
 describe('addExtensions', () => {
-
   it('adds gateway extensions to a schema', async () => {
     const schema = buildSchema('type Query { hello: String }');
     expect(schema.extensions).toEqual({});
-    const actualExtensions: ApolloGraphQLSchemaExtensions = addExtensions(schema).extensions;
-    expect(actualExtensions).toEqual({ apollo: { gateway: { version: version } } });
+    const actualExtensions: ApolloGraphQLSchemaExtensions =
+      addExtensions(schema).extensions;
+    expect(actualExtensions).toEqual({
+      apollo: { gateway: { version: version } },
+    });
   });
 
   it('does not delete existing extensions', async () => {
@@ -19,18 +21,19 @@ describe('addExtensions', () => {
       foo: 'bar',
       apollo: {
         gateway: {
-          version: 'hello'
-        }
-      }
+          version: 'hello',
+        },
+      },
     };
-    const actualExtensions: ApolloGraphQLSchemaExtensions = addExtensions(schema).extensions;
+    const actualExtensions: ApolloGraphQLSchemaExtensions =
+      addExtensions(schema).extensions;
     expect(actualExtensions).toEqual({
       foo: 'bar',
       apollo: {
         gateway: {
-          version: version
-        }
-      }
+          version: version,
+        },
+      },
     });
   });
 
@@ -38,15 +41,16 @@ describe('addExtensions', () => {
     const schema = buildSchema('type Query { hello: String }');
     expect(schema.extensions).toEqual({});
     schema.extensions = {
-      apollo: undefined
+      apollo: undefined,
     };
-    const actualExtensions: ApolloGraphQLSchemaExtensions = addExtensions(schema).extensions;
+    const actualExtensions: ApolloGraphQLSchemaExtensions =
+      addExtensions(schema).extensions;
     expect(actualExtensions).toEqual({
       apollo: {
         gateway: {
-          version: version
-        }
-      }
+          version: version,
+        },
+      },
     });
   });
 
@@ -55,16 +59,17 @@ describe('addExtensions', () => {
     expect(schema.extensions).toEqual({});
     schema.extensions = {
       apollo: {
-        gateway: undefined
-      }
+        gateway: undefined,
+      },
     };
-    const actualExtensions: ApolloGraphQLSchemaExtensions = addExtensions(schema).extensions;
+    const actualExtensions: ApolloGraphQLSchemaExtensions =
+      addExtensions(schema).extensions;
     expect(actualExtensions).toEqual({
       apollo: {
         gateway: {
-          version: version
-        }
-      }
+          version: version,
+        },
+      },
     });
   });
 });
diff --git a/gateway-js/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts b/gateway-js/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts
index b13b3a3d5..5873afd81 100644
--- a/gateway-js/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts
+++ b/gateway-js/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts
@@ -3,11 +3,17 @@ import {
   fixtures,
   fixturesWithUpdate,
 } from 'apollo-federation-integration-testsuite';
-import { nockBeforeEach, nockAfterEach } from '../../../__tests__/nockAssertions';
+import {
+  nockBeforeEach,
+  nockAfterEach,
+} from '../../../__tests__/nockAssertions';
 import { RemoteGraphQLDataSource, ServiceEndpointDefinition } from '../../..';
 import { IntrospectAndCompose } from '..';
 import { mockAllServicesSdlQuerySuccess } from '../../../__tests__/integration/nockMocks';
-import { getTestingSupergraphSdl, wait } from '../../../__tests__/execution-utils';
+import {
+  getTestingSupergraphSdl,
+  wait,
+} from '../../../__tests__/execution-utils';
 import resolvable from '@josephg/resolvable';
 import type { Logger } from '@apollo/utils.logger';
 
@@ -182,10 +188,7 @@ describe('IntrospectAndCompose', () => {
       },
     });
 
-    await Promise.all([
-      healthCheckPromiseOnLoad,
-      healthCheckPromiseOnUpdate,
-    ]);
+    await Promise.all([healthCheckPromiseOnLoad, healthCheckPromiseOnUpdate]);
 
     expect(healthCheckSpy).toHaveBeenNthCalledWith(
       1,
diff --git a/gateway-js/src/supergraphManagers/IntrospectAndCompose/__tests__/loadServicesFromRemoteEndpoint.test.ts b/gateway-js/src/supergraphManagers/IntrospectAndCompose/__tests__/loadServicesFromRemoteEndpoint.test.ts
index c13a5d131..3fb686d73 100644
--- a/gateway-js/src/supergraphManagers/IntrospectAndCompose/__tests__/loadServicesFromRemoteEndpoint.test.ts
+++ b/gateway-js/src/supergraphManagers/IntrospectAndCompose/__tests__/loadServicesFromRemoteEndpoint.test.ts
@@ -10,7 +10,7 @@ describe('loadServicesFromRemoteEndpoint', () => {
       loadServicesFromRemoteEndpoint({
         serviceList,
         serviceSdlCache,
-        getServiceIntrospectionHeaders: async () => ({})
+        getServiceIntrospectionHeaders: async () => ({}),
       }),
     ).rejects.toThrowError(
       "Tried to load schema for 'test' but no 'url' was specified.",
diff --git a/gateway-js/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts b/gateway-js/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts
index 19e46b6f2..9411055de 100644
--- a/gateway-js/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts
+++ b/gateway-js/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts
@@ -1,6 +1,12 @@
 import mockedEnv from 'mocked-env';
 
 import { UplinkSupergraphManager } from '@apollo/gateway';
+import nock from 'nock';
+import { SUPERGRAPH_SDL_QUERY } from '../loadSupergraphSdlFromStorage';
+import {
+  nockBeforeEach,
+  nockAfterEach,
+} from '../../../__tests__/nockAssertions';
 
 let cleanUp: (() => void) | undefined;
 
@@ -29,7 +35,9 @@ describe('UplinkSupergraphManager', () => {
     it('uses default uplink URLs', async () => {
       const manager = new UplinkSupergraphManager({ apiKey, graphRef, logger });
 
-      expect(manager.uplinkEndpoints).toEqual(UplinkSupergraphManager.DEFAULT_UPLINK_ENDPOINTS);
+      expect(manager.uplinkEndpoints).toEqual(
+        UplinkSupergraphManager.DEFAULT_UPLINK_ENDPOINTS,
+      );
     });
 
     it('can set uplink URLs via config', async () => {
@@ -62,4 +70,63 @@ describe('UplinkSupergraphManager', () => {
       expect(manager.uplinkEndpoints).toEqual([uplinkUrl]);
     });
   });
+
+  describe('fallbackPollIntervalInMs', () => {
+    beforeEach(nockBeforeEach);
+    afterEach(nockAfterEach);
+
+    it('uses the provided fallback interval when Uplink provides a shorter interval', async () => {
+      // These are the two interesting values for the test. We just want to make
+      // sure that when the UplinkSupergraphManager receives 5s from Uplink that
+      // it opts for the fallbackInterval since it's longer.
+      const fallbackPollIntervalInMs = 15_000;
+      // This is used in the mock response from Uplink below
+      const minDelaySeconds = 5;
+
+      const manager = new UplinkSupergraphManager({
+        apiKey,
+        graphRef,
+        fallbackPollIntervalInMs,
+      });
+
+      mockUplinkResponse(minDelaySeconds);
+
+      const { supergraphSdl, cleanup } = await manager.initialize({
+        // These aren't really used for anything, just keeping TS happy
+        update: jest.fn(),
+        healthCheck: jest.fn(),
+        getDataSource: jest.fn(),
+      });
+      cleanUp = cleanup;
+
+      // validates we got a response from "Uplink" that we're happy with
+      expect(supergraphSdl).toEqual('supergraph sdl');
+
+      // validates that the fallback interval was used instead of the one from Uplink
+      expect(manager['pollIntervalMs']).toEqual(fallbackPollIntervalInMs);
+    });
+  });
 });
+
+function mockUplinkResponse(minDelaySeconds: number) {
+  nock('https://uplink.api.apollographql.com/')
+    .post('/', {
+      query: SUPERGRAPH_SDL_QUERY,
+      variables: {
+        ref: graphRef,
+        apiKey,
+        ifAfterId: null,
+      },
+    })
+    .reply(200, {
+      data: {
+        __typename: 'Query',
+        routerConfig: {
+          __typename: 'RouterConfigResult',
+          id: '123',
+          supergraphSdl: 'supergraph sdl',
+          minDelaySeconds,
+        },
+      },
+    });
+}
diff --git a/gateway-js/src/supergraphManagers/UplinkSupergraphManager/__tests__/loadSupergraphSdlFromStorage.test.ts b/gateway-js/src/supergraphManagers/UplinkSupergraphManager/__tests__/loadSupergraphSdlFromStorage.test.ts
index 720676ba6..90672ed87 100644
--- a/gateway-js/src/supergraphManagers/UplinkSupergraphManager/__tests__/loadSupergraphSdlFromStorage.test.ts
+++ b/gateway-js/src/supergraphManagers/UplinkSupergraphManager/__tests__/loadSupergraphSdlFromStorage.test.ts
@@ -93,7 +93,9 @@ describe('loadSupergraphSdlFromStorage', () => {
   });
 
   it('Queries alternate Uplink URL if first one times out', async () => {
-    mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1).delay(120_000).reply(500);
+    mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1)
+      .delay(120_000)
+      .reply(500);
     mockSupergraphSdlRequestIfAfter(
       'originalId-1234',
       mockCloudConfigUrl2,
diff --git a/gateway-js/src/supergraphManagers/UplinkSupergraphManager/index.ts b/gateway-js/src/supergraphManagers/UplinkSupergraphManager/index.ts
index 83d8409c1..4edb97cce 100644
--- a/gateway-js/src/supergraphManagers/UplinkSupergraphManager/index.ts
+++ b/gateway-js/src/supergraphManagers/UplinkSupergraphManager/index.ts
@@ -64,6 +64,7 @@ export class UplinkSupergraphManager implements SupergraphManager {
     UplinkSupergraphManager.DEFAULT_REQUEST_TIMEOUT_MS;
   private initialMaxRetries: number;
   private pollIntervalMs: number = UplinkSupergraphManager.MIN_POLL_INTERVAL_MS;
+  private fallbackPollIntervalInMs?: number;
   private logger: Logger;
   private update?: SupergraphSdlUpdateFunction;
   private shouldRunSubgraphHealthcheck: boolean = false;
@@ -117,6 +118,7 @@ export class UplinkSupergraphManager implements SupergraphManager {
     this.initialMaxRetries = initialMaxRetries ?? this.maxRetries;
 
     this.pollIntervalMs = fallbackPollIntervalInMs ?? this.pollIntervalMs;
+    this.fallbackPollIntervalInMs = fallbackPollIntervalInMs;
     if (this.pollIntervalMs < UplinkSupergraphManager.MIN_POLL_INTERVAL_MS) {
       this.logger.warn(
         'Polling Apollo services at a frequency of less than once per 10 seconds (10000) is disallowed. Instead, the minimum allowed pollInterval of 10000 will be used. Please reconfigure your `fallbackPollIntervalInMs` accordingly. If this is problematic for your team, please contact support.',
@@ -229,6 +231,17 @@ export class UplinkSupergraphManager implements SupergraphManager {
       supergraphSdl = result.supergraphSdl;
       if (result?.minDelaySeconds) {
         this.pollIntervalMs = result.minDelaySeconds * 1000;
+
+        // We only want to take the max of the two _if_ a fallback interval is
+        // configured. If we take the max above unconditionally, then a gateway
+        // with an unconfigured fallback interval will only ever lengthen its
+        // poll interval rather than adapt to changes coming from Uplink.
+        if (this.fallbackPollIntervalInMs) {
+          this.pollIntervalMs = Math.max(
+            this.pollIntervalMs,
+            this.fallbackPollIntervalInMs,
+          );
+        }
       }
     } catch (e) {
       this.logger.debug(
diff --git a/internals-js/CHANGELOG.md b/internals-js/CHANGELOG.md
index 8a3e60b58..64db9168b 100644
--- a/internals-js/CHANGELOG.md
+++ b/internals-js/CHANGELOG.md
@@ -1,5 +1,83 @@
 # CHANGELOG for `@apollo/federation-internals`
 
+## 2.5.5
+
+## 2.5.4
+
+## 2.5.3
+### Patch Changes
+
+
+- Modifies the type for the argument of the `@requiresScopes` from `[federation__Scope!]!` to `[[federation__Scope!]!]!`. ([#2738](https://github.com/apollographql/federation/pull/2738))
+  
+  The `@requiresScopes` directives has been pre-emptively introduced in 2.5.0 to support an upcoming Apollo Router
+  feature around scoped accesses. The argument for `@requiresScopes` in that upcoming feature is changed to accommodate a
+  new semantic. Note that this technically a breaking change to the `@requiresScopes` directive definition, but as the
+  full feature using that directive has been released yet, this directive cannot effectively be used and this should have
+  no concrete impact.
+
+- Fix potential assertion error for named fragment on abstract types when the abstract type does not have the same ([#2725](https://github.com/apollographql/federation/pull/2725))
+  possible runtime types in all subgraphs.
+  
+  The error manifested itself during query planning with an error message of the form `Cannot normalize X at Y ...`.
+
+- Expands over-eager merging of field fix to handle `@defer` consistently ([#2720](https://github.com/apollographql/federation/pull/2720))
+  
+  The previously committed [#2713](https://github.com/apollographql/federation/pull/2713) fixed an issue introduced by
+  [#2387](https://github.com/apollographql/federation/pull/2387), ensuring that querying the same field with different
+  directives applications was not merged, similar to what was/is done for fragments. But the exact behaviour slightly
+  differs between fields and fragments when it comes to `@defer` in that for fragments, we never merge 2 similar fragments
+  where both have `@defer`, which we do merge for fields. Or to put it more concretely, in the following query:
+  ```graphq
+  query Test($skipField: Boolean!) {
+    x {
+      ... on X @defer {
+        a
+      }
+      ... on X @defer {
+        b
+      }
+    }
+  }
+  ```
+  the 2 `... on X @defer` are not merged, resulting in 2 deferred sections that can run in parallel. But following
+  [#2713](https://github.com/apollographql/federation/pull/2713), query:
+  ```graphq
+  query Test($skipField: Boolean!) {
+    x @defer {
+      a
+    }
+    x @defer {
+      b
+    }
+  }
+  ```
+  _will_ merge both `x @defer`, resulting in a single deferred section.
+  
+  This fix changes that later behaviour so that the 2 `x @defer` are not merged and result in 2 deferred sections,
+  consistently with both 1) the case of fragments and 2) the behaviour prior to
+  [#2387](https://github.com/apollographql/federation/pull/2387).
+
+## 2.5.2
+### Patch Changes
+
+
+- Fix over-eager merging of fields with different directive applications ([#2713](https://github.com/apollographql/federation/pull/2713))
+  
+  Previously, the following query would incorrectly combine the selection set of `hello`, with both fields ending up under the `@skip` condition:
+  ```graphql
+  query Test($skipField: Boolean!) {
+    hello @skip(if: $skipField) {
+      world
+    }
+    hello {
+      goodbye
+    }
+  }
+  ```
+  
+  This change identifies those two selections on `hello` as unique while constructing our operation representation so they aren't merged at all, leaving it to the subgraph to handle the operation as-is.
+
 ## 2.5.1
 ### Patch Changes
 
diff --git a/internals-js/README.md b/internals-js/README.md
new file mode 100644
index 000000000..3e47ca123
--- /dev/null
+++ b/internals-js/README.md
@@ -0,0 +1,10 @@
+# @apollo/federation-internals
+
+This is an **internal** package for core Federation components. This package may ship breaking changes at any time and does not make the same efforts to avoid breaking changes as our other Federation packages.
+
+If you are looking to create a subgraph in JavaScript, use [Apollo Server with @apollo/subgraph](https://www.apollographql.com/docs/apollo-server/using-federation/apollo-subgraph-setup).
+
+If you are looking to run a supergraph, use [Apollo Router or Apollo Gateway](https://www.apollographql.com/docs/federation/building-supergraphs/router).
+
+If you want to run composition locally, use the [Rover command `supergraph compose`](https://www.apollographql.com/docs/rover/commands/supergraphs#composing-a-supergraph-schema)
+
diff --git a/internals-js/package.json b/internals-js/package.json
index 9aa3a3afd..c25172ffa 100644
--- a/internals-js/package.json
+++ b/internals-js/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@apollo/federation-internals",
-  "version": "2.5.1",
+  "version": "2.5.5",
   "description": "Apollo Federation internal utilities",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",
diff --git a/internals-js/src/__tests__/operations.test.ts b/internals-js/src/__tests__/operations.test.ts
index c9c92404f..7022225ca 100644
--- a/internals-js/src/__tests__/operations.test.ts
+++ b/internals-js/src/__tests__/operations.test.ts
@@ -9,6 +9,7 @@ import { FragmentRestrictionAtType, MutableSelectionSet, NamedFragmentDefinition
 import './matchers';
 import { DocumentNode, FieldNode, GraphQLError, Kind, OperationDefinitionNode, OperationTypeNode, parse, SelectionNode, SelectionSetNode, validate } from 'graphql';
 import { assert } from '../utils';
+import gql from 'graphql-tag';
 
 function parseSchema(schema: string): Schema {
   try {
@@ -1987,6 +1988,142 @@ describe('fragments optimization', () => {
         }
       `);
     });
+
+    test('due to the trimmed selection of nested fragments', () => {
+      const schema = parseSchema(`
+        type Query {
+          u1: U
+          u2: U
+          u3: U
+        }
+
+        union U = S | T
+
+        type T  {
+          id: ID!
+          vt: Int
+        }
+
+        interface I {
+          vs: Int
+        }
+
+        type S implements I {
+          vs: Int!
+        }
+      `);
+      const gqlSchema = schema.toGraphQLJSSchema();
+
+      const operation = parseOperation(schema, `
+        {
+          u1 {
+            ...F1
+          }
+          u2 {
+            ...F3
+          }
+          u3 {
+            ...F3
+          }
+        }
+
+        fragment F1 on U {
+           ... on S {
+             __typename
+             vs
+           }
+           ... on T {
+             __typename
+             vt
+           }
+        }
+
+        fragment F2 on T {
+           __typename
+           vt
+        }
+
+        fragment F3 on U {
+           ... on I {
+             vs
+           }
+           ...F2
+        }
+      `);
+      expect(validate(gqlSchema, parse(operation.toString()))).toStrictEqual([]);
+
+      const withoutFragments = operation.expandAllFragments();
+      expect(withoutFragments.toString()).toMatchString(`
+        {
+          u1 {
+            ... on S {
+              __typename
+              vs
+            }
+            ... on T {
+              __typename
+              vt
+            }
+          }
+          u2 {
+            ... on I {
+              vs
+            }
+            ... on T {
+              __typename
+              vt
+            }
+          }
+          u3 {
+            ... on I {
+              vs
+            }
+            ... on T {
+              __typename
+              vt
+            }
+          }
+        }
+      `);
+
+      // We use `mapToExpandedSelectionSets` with a no-op mapper because this will still expand the selections
+      // and re-optimize them, which 1) happens to match what happens in the query planner and 2) is necessary
+      // for reproducing a bug that this test was initially added to cover.
+      const newFragments = operation.fragments!.mapToExpandedSelectionSets((s) => s);
+      const optimized = withoutFragments.optimize(newFragments, 2);
+      expect(validate(gqlSchema, parse(optimized.toString()))).toStrictEqual([]);
+
+      expect(optimized.toString()).toMatchString(`
+        fragment F3 on U {
+          ... on I {
+            vs
+          }
+          ... on T {
+            __typename
+            vt
+          }
+        }
+
+        {
+          u1 {
+            ... on S {
+              __typename
+              vs
+            }
+            ... on T {
+              __typename
+              vt
+            }
+          }
+          u2 {
+            ...F3
+          }
+          u3 {
+            ...F3
+          }
+        }
+      `);
+    });
   });
 
   test('does not leave unused fragments', () => {
@@ -2475,6 +2612,8 @@ describe('basic operations', () => {
       b1: Int
       b2: T
     }
+
+    directive @customSkip(if: Boolean!, label: String!) on FIELD | INLINE_FRAGMENT
   `);
 
   const operation = parseOperation(schema, `
@@ -2519,6 +2658,310 @@ describe('basic operations', () => {
       ['T', 'v2'],
     ]);
   })
+
+  describe('same field merging', () => {
+    test('do merge when same field and no directive', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test {
+          t {
+            v1
+          }
+          t {
+            v2
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test {
+          t {
+            v1
+            v2
+          }
+        }
+      `);
+    });
+
+    test('do merge when both have the _same_ directive', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test($skipIf: Boolean!) {
+          t @skip(if: $skipIf) {
+            v1
+          }
+          t @skip(if: $skipIf) {
+            v2
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test($skipIf: Boolean!) {
+          t @skip(if: $skipIf) {
+            v1
+            v2
+          }
+        }
+      `);
+    });
+
+    test('do merge when both have the _same_ directive, even if argument order differs', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test($skipIf: Boolean!) {
+          t @customSkip(if: $skipIf, label: "foo") {
+            v1
+          }
+          t @customSkip(label: "foo", if: $skipIf) {
+            v2
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test($skipIf: Boolean!) {
+          t @customSkip(if: $skipIf, label: "foo") {
+            v1
+            v2
+          }
+        }
+      `);
+    });
+
+    test('do not merge when one has a directive and the other do not', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test($skipIf: Boolean!) {
+          t {
+            v1
+          }
+          t @skip(if: $skipIf) {
+            v2
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test($skipIf: Boolean!) {
+          t {
+            v1
+          }
+          t @skip(if: $skipIf) {
+            v2
+          }
+        }
+      `);
+    });
+
+    test('do not merge when both have _differing_ directives', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test($skip1: Boolean!, $skip2: Boolean!) {
+          t @skip(if: $skip1) {
+            v1
+          }
+          t @skip(if: $skip2) {
+            v2
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test($skip1: Boolean!, $skip2: Boolean!) {
+          t @skip(if: $skip1) {
+            v1
+          }
+          t @skip(if: $skip2) {
+            v2
+          }
+        }
+      `);
+    });
+
+    test('do not merge @defer directive, even if applied the same way', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test {
+          t @defer {
+            v1
+          }
+          t @defer {
+            v2
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test {
+          t @defer {
+            v1
+          }
+          t @defer {
+            v2
+          }
+        }
+      `);
+    });
+  });
+  
+  describe('same fragment merging', () => {
+    test('do merge when same fragment and no directive', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test {
+          t {
+            ... on T {
+              v1
+            }
+            ... on T {
+              v2
+            }
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test {
+          t {
+            ... on T {
+              v1
+              v2
+            }
+          }
+        }
+      `);
+    });
+
+    test('do merge when both have the _same_ directive', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test($skipIf: Boolean!) {
+          t {
+            ... on T @skip(if: $skipIf) {
+              v1
+            }
+            ... on T @skip(if: $skipIf) {
+              v2
+            }
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test($skipIf: Boolean!) {
+          t {
+            ... on T @skip(if: $skipIf) {
+              v1
+              v2
+            }
+          }
+        }
+      `);
+    });
+
+    test('do merge when both have the _same_ directive, even if argument order differs', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test($skipIf: Boolean!) {
+          t {
+            ... on T @customSkip(if: $skipIf, label: "foo") {
+              v1
+            }
+            ... on T @customSkip(label: "foo", if: $skipIf) {
+              v2
+            }
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test($skipIf: Boolean!) {
+          t {
+            ... on T @customSkip(if: $skipIf, label: "foo") {
+              v1
+              v2
+            }
+          }
+        }
+      `);
+    });
+
+    test('do not merge when one has a directive and the other do not', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test($skipIf: Boolean!) {
+          t {
+            ... on T {
+              v1
+            }
+            ... on T @skip(if: $skipIf) {
+              v2
+            }
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test($skipIf: Boolean!) {
+          t {
+            ... on T {
+              v1
+            }
+            ... on T @skip(if: $skipIf) {
+              v2
+            }
+          }
+        }
+      `);
+    });
+
+    test('do not merge when both have _differing_ directives', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test($skip1: Boolean!, $skip2: Boolean!) {
+          t {
+            ... on T @skip(if: $skip1) {
+              v1
+            }
+            ... on T @skip(if: $skip2) {
+              v2
+            }
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test($skip1: Boolean!, $skip2: Boolean!) {
+          t {
+            ... on T @skip(if: $skip1) {
+              v1
+            }
+            ... on T @skip(if: $skip2) {
+              v2
+            }
+          }
+        }
+      `);
+    });
+
+    test('do not merge @defer directive, even if applied the same way', () => {
+      const operation = operationFromDocument(schema, gql`
+        query Test {
+          t {
+            ... on T @defer {
+              v1
+            }
+            ... on T @defer {
+              v2
+            }
+          }
+        }
+      `);
+
+      expect(operation.toString()).toMatchString(`
+        query Test {
+          t {
+            ... on T @defer {
+              v1
+            }
+            ... on T @defer {
+              v2
+            }
+          }
+        }
+      `);
+    });
+  });
 });
 
 describe('MutableSelectionSet', () => {
@@ -2787,12 +3230,25 @@ describe('named fragment selection set restrictions at type', () => {
 
     const frag = operation.fragments?.get('FonU1')!;
 
-    // Note that with unions, the fragments inside the unions can be "lifted" and so they everyting normalize to just the
+    // Note that with unions, the fragments inside the unions can be "lifted" and so that everything normalizes to just the
     // possible runtimes.
 
     let { selectionSet, validator } = expandAtType(frag, schema, 'U1');
     expect(selectionSet.toString()).toBe('{ ... on T1 { x y } ... on T2 { z w } }');
-    expect(validator?.toString()).toBeUndefined();
+    // Similar remarks than on interfaces (the validator is strictly speaking not necessary, but
+    // this happens due to the "lifting" of selection mentioned above, is a bit hard to avoid,
+    // and is essentially harmess (it may result in a bit more cpu cycles in some cases but
+    // that is likely negligible).
+    expect(validator?.toString()).toMatchString(`
+      {
+        y: [
+          T1.y
+        ]
+        w: [
+          T2.w
+        ]
+      }
+    `);
 
     ({ selectionSet, validator } = expandAtType(frag, schema, 'U2'));
     expect(selectionSet.toString()).toBe('{ ... on T1 { x y } }');
@@ -2801,6 +3257,9 @@ describe('named fragment selection set restrictions at type', () => {
         z: [
           T2.z
         ]
+        y: [
+          T1.y
+        ]
         w: [
           T2.w
         ]
@@ -2817,6 +3276,9 @@ describe('named fragment selection set restrictions at type', () => {
         y: [
           T1.y
         ]
+        w: [
+          T2.w
+        ]
       }
     `);
 
@@ -2828,12 +3290,12 @@ describe('named fragment selection set restrictions at type', () => {
         x: [
           T1.x
         ]
-        y: [
-          T1.y
-        ]
         z: [
           T2.z
         ]
+        y: [
+          T1.y
+        ]
         w: [
           T2.w
         ]
@@ -3235,9 +3697,7 @@ describe('named fragment rebasing on subgraphs', () => {
         t {
           x
           y
-          ... on T {
-            z
-          }
+          z
         }
       }
     `);
diff --git a/internals-js/src/operations.ts b/internals-js/src/operations.ts
index 771c4725c..b00f2664d 100644
--- a/internals-js/src/operations.ts
+++ b/internals-js/src/operations.ts
@@ -113,6 +113,10 @@ abstract class AbstractOperationElement<T extends AbstractOperationElement<T>> e
       }
     }
   }
+
+  protected keyForDirectives(): string {
+    return this.appliedDirectives.map((d) => keyForDirective(d)).join(' ');
+  }
 }
 
 export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> extends AbstractOperationElement<Field<TArgs>> {
@@ -146,7 +150,7 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
   }
 
   key(): string {
-    return this.responseName();
+    return this.responseName() + this.keyForDirectives();
   }
 
   asPathElement(): string {
@@ -394,7 +398,7 @@ export class Field<TArgs extends {[key: string]: any} = {[key: string]: any}> ex
  * 2. we sort the argument (by their name) before converting them to string, since argument order does not matter in graphQL.
  */
 function keyForDirective(
-  directive: Directive<OperationElement>,
+  directive: Directive<AbstractOperationElement<any>>,
   directivesNeverEqualToThemselves: string[] = [ 'defer' ],
 ): string {
   if (directivesNeverEqualToThemselves.includes(directive.name)) {
@@ -436,8 +440,7 @@ export class FragmentElement extends AbstractOperationElement<FragmentElement> {
     if (!this.computedKey) {
       // The key is such that 2 fragments with the same key within a selection set gets merged together. So the type-condition
       // is include, but so are the directives.
-      const keyForDirectives = this.appliedDirectives.map((d) => keyForDirective(d)).join(' ');
-      this.computedKey = '...' + (this.typeCondition ? ' on ' + this.typeCondition.name : '') + keyForDirectives;
+      this.computedKey = '...' + (this.typeCondition ? ' on ' + this.typeCondition.name : '') + this.keyForDirectives();
     }
     return this.computedKey;
   }
@@ -1070,13 +1073,6 @@ export class NamedFragmentDefinition extends DirectiveTargetElement<NamedFragmen
     return this._selectionSet;
   }
 
-  expandedSelectionSet(): SelectionSet {
-    if (!this._expandedSelectionSet) {
-      this._expandedSelectionSet = this.selectionSet.expandFragments().normalize({ parentType: this.typeCondition });
-    }
-    return this._expandedSelectionSet;
-  }
-
   withUpdatedSelectionSet(newSelectionSet: SelectionSet): NamedFragmentDefinition {
     return new NamedFragmentDefinition(this.schema(), this.name, this.typeCondition).setSelectionSet(newSelectionSet);
   }
@@ -1165,6 +1161,13 @@ export class NamedFragmentDefinition extends DirectiveTargetElement<NamedFragmen
     return isObjectType(type) || isUnionType(this.typeCondition);
   }
 
+  private expandedSelectionSet(): SelectionSet {
+    if (!this._expandedSelectionSet) {
+      this._expandedSelectionSet = this.selectionSet.expandFragments();
+    }
+    return this._expandedSelectionSet;
+  }
+
   /**
    * This methods *assumes* that `this.canApplyDirectlyAtType(type)` is `true` (and may crash if this is not true), and returns
    * a version fo this named fragment selection set that corresponds to the "expansion" of this named fragment at `type`
@@ -1184,11 +1187,6 @@ export class NamedFragmentDefinition extends DirectiveTargetElement<NamedFragmen
    * us that part.
    */
   expandedSelectionSetAtType(type: CompositeType): FragmentRestrictionAtType {
-    // First, if the candidate condition is an object or is the type passed, then there isn't any restriction to do.
-    if (sameType(type, this.typeCondition) || isObjectType(this.typeCondition)) {
-      return { selectionSet: this.expandedSelectionSet() };
-    }
-
     let cached = this.expandedSelectionSetsAtTypesCache.get(type.name);
     if (!cached) {
       cached = this.computeExpandedSelectionSetAtType(type);
@@ -1199,9 +1197,7 @@ export class NamedFragmentDefinition extends DirectiveTargetElement<NamedFragmen
 
   private computeExpandedSelectionSetAtType(type: CompositeType): FragmentRestrictionAtType {
     const expandedSelectionSet = this.expandedSelectionSet();
-    // Note that what we want is get any simplification coming from normalizing at `type`, but any such simplication
-    // stops as soon as we traverse a field, so no point in being recursive.
-    const selectionSet = expandedSelectionSet.normalize({ parentType: type, recursive: false });
+    const selectionSet = expandedSelectionSet.normalize({ parentType: type });
 
     // Note that `trimmed` is the difference of 2 selections that may not have been normalized on the same parent type,
     // so in practice, it is possible that `trimmed` contains some of the selections that `selectionSet` contains, but
@@ -1403,7 +1399,10 @@ export class NamedFragments {
         return undefined;
       }
 
-      const rebasedSelection = fragment.selectionSet.rebaseOn({ parentType: rebasedType, fragments: newFragments, errorIfCannotRebase: false });
+      let rebasedSelection = fragment.selectionSet.rebaseOn({ parentType: rebasedType, fragments: newFragments, errorIfCannotRebase: false });
+      // Rebasing can leave some inefficiencies in some case (particularly when a spread has to be "expanded", see `FragmentSpreadSelection.rebaseOn`),
+      // so we do a top-level normalization to keep things clean.
+      rebasedSelection = rebasedSelection.normalize({ parentType: rebasedType });
       return this.selectionSetIsWorthUsing(rebasedSelection)
         ? new NamedFragmentDefinition(schema, fragment.name, rebasedType).setSelectionSet(rebasedSelection)
         : undefined;
@@ -1418,9 +1417,11 @@ export class NamedFragments {
         // dependency order, we know that `newFragments` will have every fragments that should be
         // kept/not expanded.
         const updatedSelectionSet = fragment.selectionSet.expandFragments(newFragments);
+        // Note that if we did expanded some fragments (the updated selection is not the original one), then the
+        // results may not be fully normalized, so we do it to be sure.
         return updatedSelectionSet === fragment.selectionSet
           ? fragment
-          : fragment.withUpdatedSelectionSet(updatedSelectionSet);
+          : fragment.withUpdatedSelectionSet(updatedSelectionSet.normalize({ parentType: updatedSelectionSet.parentType}));
       } else {
         return undefined;
       }
@@ -3555,7 +3556,8 @@ class FragmentSpreadSelection extends FragmentSelection {
     // If we're rebasing on a _different_ schema, then we *must* have fragments, since reusing
     // `this.fragments` would be incorrect. If we're on the same schema though, we're happy to default
     // to `this.fragments`.
-    assert(fragments || this.parentType.schema() === parentType.schema(), `Must provide fragments is rebasing on other schema`);
+    const rebaseOnSameSchema = this.parentType.schema() === parentType.schema();
+    assert(fragments || rebaseOnSameSchema, `Must provide fragments is rebasing on other schema`);
     const newFragments = fragments ?? this.fragments;
     const namedFragment = newFragments.get(this.namedFragment.name);
     // If we're rebasing on another schema (think a subgraph), then named fragments will have been rebased on that, and some
@@ -3566,6 +3568,31 @@ class FragmentSpreadSelection extends FragmentSelection {
       validate(!errorIfCannotRebase, () => `Cannot rebase ${this.toString(false)} if it isn't part of the provided fragments`);
       return undefined;
     }
+
+    // Lastly, if we rebase on a different schema, it's possible the fragment type does not intersect the
+    // parent type. For instance, the parent type could be some object type T while the fragment is an
+    // interface I, and T may implement I in the supergraph, but not in a particular subgraph (of course,
+    // if I don't exist at all in the subgraph, then we'll have exited above, but I may exist in the
+    // subgraph, just not be implemented by T for some reason). In that case, we can't reuse the fragment
+    // as its spread is essentially invalid in that position, so we have to replace it by the expansion
+    // of that fragment, which we rebase on the parentType (which in turn, will remove anythings within
+    // the fragment selection that needs removing, potentially everything).
+    if (!rebaseOnSameSchema && !runtimeTypesIntersects(parentType, namedFragment.typeCondition)) {
+      // Note that we've used the rebased `namedFragment` to check the type intersection because we needed to
+      // compare runtime types "for the schema we're rebasing into". But now that we're deciding to not reuse
+      // this rebased fragment, what we rebase is the selection set of the non-rebased fragment. And that's
+      // important because the very logic we're hitting here may need to happen inside the rebase do the
+      // fragment selection, but that logic would not be triggered if we used the rebased `namedFragment` since
+      // `rebaseOnSameSchema` would then be 'true'.
+      const expanded = this.namedFragment.selectionSet.rebaseOn({ parentType, fragments, errorIfCannotRebase });
+      // In theory, we could return the selection set directly, but making `Selection.rebaseOn` sometimes
+      // return a `SelectionSet` complicate things quite a bit. So instead, we encapsulate the selection set
+      // in an "empty" inline fragment. This make for non-really-optimal selection sets in the (relatively
+      // rare) case where this is triggered, but in practice this "inefficiency" is removed by future calls
+      // to `normalize`.
+      return expanded.isEmpty() ? undefined : new InlineFragmentSelection(new FragmentElement(parentType), expanded);
+    }
+
     return new FragmentSpreadSelection(
       parentType,
       newFragments,
diff --git a/internals-js/src/requiresScopesSpec.ts b/internals-js/src/requiresScopesSpec.ts
index f0bdb918a..2d0744eb3 100644
--- a/internals-js/src/requiresScopesSpec.ts
+++ b/internals-js/src/requiresScopesSpec.ts
@@ -41,7 +41,7 @@ export class RequiresScopesSpecDefinition extends FeatureDefinition {
           const scopeName = feature.typeNameInSchema(RequiresScopesTypeName.SCOPE);
           const scopeType = schema.type(scopeName);
           assert(scopeType, () => `Expected "${scopeName}" to be defined`);
-          return new NonNullType(new ListType(new NonNullType(scopeType)));
+          return new NonNullType(new ListType(new NonNullType(new ListType(new NonNullType(scopeType)))));
         },
         compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.UNION,
       }],
diff --git a/jest.config.base.js b/jest.config.base.js
index 359a5d884..485f148b9 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -22,5 +22,6 @@ module.exports = {
         tsconfig: "<rootDir>/tsconfig.test.json",
         diagnostics: false
       }
-    }
+    },
+    prettierPath: require.resolve("prettier-2"),
 };
diff --git a/package-lock.json b/package-lock.json
index 91eecb43d..f6ab49f31 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -17,8 +17,8 @@
       ],
       "devDependencies": {
         "@apollo/cache-control-types": "1.0.3",
-        "@apollo/client": "3.7.17",
-        "@apollo/server": "4.9.0",
+        "@apollo/client": "3.8.4",
+        "@apollo/server": "4.9.4",
         "@apollo/utils.fetcher": "2.0.1",
         "@changesets/changelog-github": "0.4.8",
         "@changesets/cli": "2.26.2",
@@ -28,52 +28,54 @@
         "@josephg/resolvable": "1.0.1",
         "@opentelemetry/node": "0.24.0",
         "@svitejs/changesets-changelog-github-compact": "1.1.0",
-        "@types/async-retry": "1.4.5",
-        "@types/bunyan": "1.8.8",
-        "@types/deep-equal": "1.0.1",
-        "@types/jest": "29.5.3",
+        "@types/async-retry": "1.4.6",
+        "@types/bunyan": "1.8.9",
+        "@types/deep-equal": "1.0.2",
+        "@types/jest": "29.5.5",
         "@types/js-levenshtein": "1.1.1",
         "@types/loglevel": "1.5.4",
-        "@types/make-fetch-happen": "10.0.1",
+        "@types/make-fetch-happen": "10.0.2",
         "@types/nock": "10.0.3",
-        "@types/node": "14.18.54",
-        "@types/node-fetch": "2.6.4",
-        "@types/uuid": "9.0.2",
+        "@types/node": "14.18.63",
+        "@types/node-fetch": "2.6.6",
+        "@types/uuid": "9.0.4",
         "@typescript-eslint/eslint-plugin": "5.62.0",
         "bunyan": "1.8.15",
-        "cspell": "6.31.2",
-        "graphql": "16.7.1",
-        "graphql-http": "1.21.0",
+        "codecov": "3.8.3",
+        "cspell": "6.31.3",
+        "graphql": "16.8.1",
+        "graphql-http": "1.22.0",
         "graphql-tag": "2.12.6",
-        "jest": "29.6.2",
-        "jest-config": "29.6.2",
+        "jest": "29.7.0",
+        "jest-config": "29.7.0",
         "jest-cucumber": "3.0.1",
         "jest-junit": "16.0.0",
         "log4js": "6.9.1",
         "mocked-env": "1.3.5",
-        "nock": "13.3.2",
-        "node-fetch": "2.6.12",
-        "prettier": "3.0.0",
+        "nock": "13.3.3",
+        "node-fetch": "2.7.0",
+        "prettier": "3.0.3",
+        "prettier-2": "npm:prettier@2.8.8",
         "semver": "7.5.4",
         "strip-indent": "3.0.0",
         "ts-jest": "29.1.1",
         "ts-node": "10.9.1",
-        "typescript": "5.1.6",
+        "typescript": "5.2.2",
         "winston": "3.10.0",
         "winston-transport": "4.5.0"
       },
       "engines": {
         "node": ">=14.15.0",
-        "npm": "<10"
+        "npm": "<11"
       }
     },
     "composition-js": {
       "name": "@apollo/composition",
-      "version": "2.5.1",
+      "version": "2.5.5",
       "license": "SEE LICENSE IN ./LICENSE",
       "dependencies": {
-        "@apollo/federation-internals": "2.5.1",
-        "@apollo/query-graphs": "2.5.1"
+        "@apollo/federation-internals": "2.5.5",
+        "@apollo/query-graphs": "2.5.5"
       },
       "engines": {
         "node": ">=14.15.0"
@@ -84,7 +86,7 @@
     },
     "federation-integration-testsuite-js": {
       "name": "apollo-federation-integration-testsuite",
-      "version": "2.5.1",
+      "version": "2.5.5",
       "license": "SEE LICENSE IN ./LICENSE",
       "dependencies": {
         "graphql-tag": "^2.12.6",
@@ -93,12 +95,12 @@
     },
     "gateway-js": {
       "name": "@apollo/gateway",
-      "version": "2.5.1",
+      "version": "2.5.5",
       "license": "SEE LICENSE IN ./LICENSE",
       "dependencies": {
-        "@apollo/composition": "2.5.1",
-        "@apollo/federation-internals": "2.5.1",
-        "@apollo/query-planner": "2.5.1",
+        "@apollo/composition": "2.5.5",
+        "@apollo/federation-internals": "2.5.5",
+        "@apollo/query-planner": "2.5.5",
         "@apollo/server-gateway-interface": "^1.1.0",
         "@apollo/usage-reporting-protobuf": "^4.1.0",
         "@apollo/utils.createhash": "^2.0.0",
@@ -124,7 +126,7 @@
     },
     "internals-js": {
       "name": "@apollo/federation-internals",
-      "version": "2.5.1",
+      "version": "2.5.5",
       "license": "SEE LICENSE IN ./LICENSE",
       "dependencies": {
         "@types/uuid": "^9.0.0",
@@ -196,18 +198,18 @@
       }
     },
     "node_modules/@apollo/client": {
-      "version": "3.7.17",
-      "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.7.17.tgz",
-      "integrity": "sha512-0EErSHEtKPNl5wgWikHJbKFAzJ/k11O0WO2QyqZSHpdxdAnw7UWHY4YiLbHCFG7lhrD+NTQ3Z/H9Jn4rcikoJA==",
+      "version": "3.8.4",
+      "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.8.4.tgz",
+      "integrity": "sha512-QFXE4ylSHUa6LgYoOGsPysJCm4YJOOM1NwHyF6msZdZXIerqUVpLvxQOdQEXgS0RWvYiBMC1wGOWKzJKSWBdAg==",
       "dev": true,
       "dependencies": {
         "@graphql-typed-document-node/core": "^3.1.1",
-        "@wry/context": "^0.7.0",
-        "@wry/equality": "^0.5.0",
-        "@wry/trie": "^0.4.0",
+        "@wry/context": "^0.7.3",
+        "@wry/equality": "^0.5.6",
+        "@wry/trie": "^0.4.3",
         "graphql-tag": "^2.12.6",
         "hoist-non-react-statics": "^3.3.2",
-        "optimism": "^0.16.2",
+        "optimism": "^0.17.5",
         "prop-types": "^15.7.2",
         "response-iterator": "^0.2.6",
         "symbol-observable": "^4.0.0",
@@ -237,18 +239,6 @@
         }
       }
     },
-    "node_modules/@apollo/client/node_modules/@wry/trie": {
-      "version": "0.4.3",
-      "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz",
-      "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==",
-      "dev": true,
-      "dependencies": {
-        "tslib": "^2.3.0"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/@apollo/composition": {
       "resolved": "composition-js",
       "link": true
@@ -293,9 +283,9 @@
       "link": true
     },
     "node_modules/@apollo/server": {
-      "version": "4.9.0",
-      "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.9.0.tgz",
-      "integrity": "sha512-ca1tO5no7PC402ArVP2qDhjpwnZm0ThpEJTfrKoY62mzPaKH0tLjy7Mt909QGyLziwf6c15Q+V+hQYVZKdcUrw==",
+      "version": "4.9.4",
+      "resolved": "https://registry.npmjs.org/@apollo/server/-/server-4.9.4.tgz",
+      "integrity": "sha512-lopNDM3sZerTcYH/P85QX5HqSNV4HoVbtX3zOrf0ak7eplhPDiGVyF0jQWRbL64znG6KXW+nMuLDTyFTMQnvgA==",
       "dev": true,
       "dependencies": {
         "@apollo/cache-control-types": "^1.0.3",
@@ -1812,9 +1802,9 @@
       }
     },
     "node_modules/@cspell/cspell-bundled-dicts": {
-      "version": "6.31.2",
-      "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-6.31.2.tgz",
-      "integrity": "sha512-rQ5y/U1Ah5AaduIh3NU2z371hRrOr1cmNdhhP8oiuz2E4VqmcoVHflXIct9DgY8uIJpwsSCdR6ypOQWZYXYnwA==",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-6.31.3.tgz",
+      "integrity": "sha512-KXy3qKWYzXOGYwqOGMCXHem3fV39iEmoKLiNhoWWry/SFdvAafmeY+LIDcQTXAcOQLkMDCwP2/rY/NadcWnrjg==",
       "dev": true,
       "dependencies": {
         "@cspell/dict-ada": "^4.0.1",
@@ -1868,34 +1858,49 @@
         "node": ">=14"
       }
     },
+    "node_modules/@cspell/cspell-json-reporter": {
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-6.31.3.tgz",
+      "integrity": "sha512-ZJwj2vT4lxncYxduXcxy0dCvjjMvXIfphbLSCN5CXvufrtupB4KlcjZUnOofCi4pfpp8qocCSn1lf2DU9xgUXA==",
+      "dev": true,
+      "dependencies": {
+        "@cspell/cspell-types": "6.31.3"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
     "node_modules/@cspell/cspell-pipe": {
-      "version": "6.31.1",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-6.31.3.tgz",
+      "integrity": "sha512-Lv/y4Ya/TJyU1pf66yl1te7LneFZd3lZg1bN5oe1cPrKSmfWdiX48v7plTRecWd/OWyLGd0yN807v79A+/0W7A==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">=14"
       }
     },
     "node_modules/@cspell/cspell-service-bus": {
-      "version": "6.31.1",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-6.31.3.tgz",
+      "integrity": "sha512-x5j8j3n39KN8EXOAlv75CpircdpF5WEMCC5pcO916o6GBmJBy8SrdzdsBGJhVcYGGilqy6pf8R9RCZ3yAmG8gQ==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">=14"
       }
     },
     "node_modules/@cspell/cspell-types": {
-      "version": "6.31.1",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-6.31.3.tgz",
+      "integrity": "sha512-wZ+t+lUsQJB65M31btZM4fH3K1CkRgE8pSeTiCwxYcnCL19pi4TMcEEMKdO8yFZMdocW4B7VRwzxNoQMw2ewBg==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">=14"
       }
     },
     "node_modules/@cspell/dict-ada": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.1.tgz",
-      "integrity": "sha512-/E9o3nHrXOhYmQE43deKbxZcR3MIJAsa+66IzP9TXGHheKEx8b9dVMVVqydDDH8oom1H0U20NRPtu6KRVbT9xw==",
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.2.tgz",
+      "integrity": "sha512-0kENOWQeHjUlfyId/aCM/mKXtkEgV0Zu2RhUXCBr4hHo9F9vph+Uu8Ww2b0i5a4ZixoIkudGA+eJvyxrG1jUpA==",
       "dev": true
     },
     "node_modules/@cspell/dict-aws": {
@@ -1911,15 +1916,15 @@
       "dev": true
     },
     "node_modules/@cspell/dict-companies": {
-      "version": "3.0.17",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.17.tgz",
-      "integrity": "sha512-vo1jbozgZWSzz2evIL26kLd35tVb+5kW/UTvTzAwaWutSWRloRyKx38nj2CaLJ2IFxBdiATteCFGTzKCvJJl6A==",
+      "version": "3.0.19",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.19.tgz",
+      "integrity": "sha512-hO7rS4DhFA333qyvf89wIVoclCtXe/2sftY6aS0oMIH1bMZLjLx2B2sQJj6dCiu6gG/By1S9YZ0fXabiPk2Tkg==",
       "dev": true
     },
     "node_modules/@cspell/dict-cpp": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.0.3.tgz",
-      "integrity": "sha512-7sx/RFsf0hB3q8chx8OHYl9Kd+g0pqA1laphwaAQ+/jPwoAreYT3kNQWbJ3bIt/rMoORetFSQxckSbaJXwwqpw==",
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.0.4.tgz",
+      "integrity": "sha512-Vmz/CCb2d91ES5juaO8+CFWeTa2AFsbpR8bkCPJq+P8cRP16+37tY0zNXEBSK/1ur4MakaRf76jeQBijpZxw0Q==",
       "dev": true
     },
     "node_modules/@cspell/dict-cryptocurrencies": {
@@ -1941,15 +1946,15 @@
       "dev": true
     },
     "node_modules/@cspell/dict-dart": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.0.2.tgz",
-      "integrity": "sha512-jigcODm7Z4IFZ4vParwwP3IT0fIgRq/9VoxkXfrxBMsLBGGM2QltHBj7pl+joX+c4cOHxfyZktGJK1B1wFtR4Q==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.0.3.tgz",
+      "integrity": "sha512-cLkwo1KT5CJY5N5RJVHks2genFkNCl/WLfj+0fFjqNR+tk3tBI1LY7ldr9piCtSFSm4x9pO1x6IV3kRUY1lLiw==",
       "dev": true
     },
     "node_modules/@cspell/dict-data-science": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-1.0.7.tgz",
-      "integrity": "sha512-Q9VUFaarUpqM6CAmR8peP4o9alk0XQ4rgVoE2R2XalpC2cqPI8Hmg6QwMU2UPioSUcWMJCqLc/KzJti0gBMuxA==",
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-1.0.10.tgz",
+      "integrity": "sha512-7ZsRCnW0f4Bdo6Cqq8V4gHr8K58h+MP8majcDeMNhpMFUPiiSnvKsDuG9V5jciI/0t+lptPrZwGGIVEDF4Kqtg==",
       "dev": true
     },
     "node_modules/@cspell/dict-django": {
@@ -1959,9 +1964,9 @@
       "dev": true
     },
     "node_modules/@cspell/dict-docker": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.6.tgz",
-      "integrity": "sha512-zCCiRTZ6EOQpBnSOm0/3rnKW1kCcAUDUA7SxJG3SuH6iZvKi3I8FEg8+O83WQUeXg0SyPNerD9F40JLnnJjJig==",
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.7.tgz",
+      "integrity": "sha512-XlXHAr822euV36GGsl2J1CkBIVg3fZ6879ZOg5dxTIssuhUOCiV2BuzKZmt6aIFmcdPmR14+9i9Xq+3zuxeX0A==",
       "dev": true
     },
     "node_modules/@cspell/dict-dotnet": {
@@ -1977,9 +1982,9 @@
       "dev": true
     },
     "node_modules/@cspell/dict-en_us": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.4.tgz",
-      "integrity": "sha512-mR2yqWmFip1zTKja2SqyVMbzuqEThqkEJk9M32bMDziPJpEyOIPvLA0UPmj3cyRKJkRuVF0bhDCE33O+at38hw==",
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.6.tgz",
+      "integrity": "sha512-odhgsjNZI9BtEOJdvqfAuv/3yz5aB1ngfBNaph7WSnYVt//9e3fhrElZ6/pIIkoyuGgeQPwz1fXt+tMgcnLSEQ==",
       "dev": true
     },
     "node_modules/@cspell/dict-en-common-misspellings": {
@@ -2085,9 +2090,9 @@
       "dev": true
     },
     "node_modules/@cspell/dict-npm": {
-      "version": "5.0.7",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.0.7.tgz",
-      "integrity": "sha512-6SegF0HsVaBTl6PlHjeErG8Av+tRYkUG1yaXUQIGWXU0A8oxhI0o4PuL65UWH5lkCKhJyGai69Cd0iytL0oVFg==",
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.0.8.tgz",
+      "integrity": "sha512-KuqH8tEsFD6DPKqKwIfWr9E+admE3yghaC0AKXG8jPaf77N0lkctKaS3dm0oxWUXkYKA/eXj6LCtz3VcTyxFPg==",
       "dev": true
     },
     "node_modules/@cspell/dict-php": {
@@ -2103,15 +2108,15 @@
       "dev": true
     },
     "node_modules/@cspell/dict-public-licenses": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.2.tgz",
-      "integrity": "sha512-baKkbs/WGEV2lCWZoL0KBPh3uiPcul5GSDwmXEBAsR5McEW52LF94/b7xWM0EmSAc/y8ODc5LnPYC7RDRLi6LQ==",
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.3.tgz",
+      "integrity": "sha512-JSLEdpEYufQ1H+93UHi+axlqQm1fhgK6kpdLHp6uPHu//CsvETcqNVawjB+qOdI/g38JTMw5fBqSd0aGNxa6Dw==",
       "dev": true
     },
     "node_modules/@cspell/dict-python": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.1.2.tgz",
-      "integrity": "sha512-Whcn4K8R0Ux/hcx/P9Fbx6i29GwTaXgT3LTt95AuCnV5RRLrzsqoyZkz851hcg5z4kjUQVMduDl3HECGgW/FNw==",
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.1.5.tgz",
+      "integrity": "sha512-wWUWyHdyJtx5iG6Fz9rBQ17BtdpEsB17vmutao+gixQD28Jzb6XoLgDQ6606M0RnFjBSFhs5iT4CJBzlD2Kq6g==",
       "dev": true,
       "dependencies": {
         "@cspell/dict-data-science": "^1.0.0"
@@ -2142,15 +2147,15 @@
       "dev": true
     },
     "node_modules/@cspell/dict-software-terms": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.2.0.tgz",
-      "integrity": "sha512-RI6sv4Bc4i42YH/ofVelv8lXpJRhCyS9IhI2BtejUoMXKhKA9gC01ATXOylx+oaQmj3t5ark4R50xKFRvC7ENA==",
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.2.1.tgz",
+      "integrity": "sha512-+QXmyoONVc/3aNgKW+0F0u3XUCRTfNRkWKLZQA78i+9fOfde8ZT4JmROmZgRveH/MxD4n6pNFceIRcYI6C8WuQ==",
       "dev": true
     },
     "node_modules/@cspell/dict-sql": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.1.0.tgz",
-      "integrity": "sha512-Bb+TNWUrTNNABO0bmfcYXiTlSt0RD6sB2MIY+rNlaMyIwug43jUjeYmkLz2tPkn3+2uvySeFEOMVYhMVfcuDKg==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.1.1.tgz",
+      "integrity": "sha512-v1mswi9NF40+UDUMuI148YQPEQvWjac72P6ZsjlRdLjEiQEEMEsTQ+zlkIdnzC9QCNyJaqD5Liq9Mn78/8Zxtw==",
       "dev": true
     },
     "node_modules/@cspell/dict-svelte": {
@@ -2178,9 +2183,10 @@
       "dev": true
     },
     "node_modules/@cspell/dynamic-import": {
-      "version": "6.31.1",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-6.31.3.tgz",
+      "integrity": "sha512-A6sT00+6UNGFksQ5SxW2ohNl6vUutai8F4jwJMHTjZL/9vivQpU7y5V4PpsfoPZtx3WZcbrzuTvJ+tLfdbWc4A==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "import-meta-resolve": "^2.2.2"
       },
@@ -2189,9 +2195,10 @@
       }
     },
     "node_modules/@cspell/strong-weak-map": {
-      "version": "6.31.1",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-6.31.3.tgz",
+      "integrity": "sha512-znwc9IlgGUPioHGshP/zyM8HsuYg1OY5S7HSiVXARh5H8RqcyBsnyn8abc0PPhqPrfDy9Fh5xHsAEPZ55dl1vQ==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">=14.6"
       }
@@ -2278,9 +2285,10 @@
       "peer": true
     },
     "node_modules/@eslint/eslintrc/node_modules/globals": {
-      "version": "13.20.0",
+      "version": "13.22.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
+      "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
       "dev": true,
-      "license": "MIT",
       "peer": true,
       "dependencies": {
         "type-fest": "^0.20.2"
@@ -2977,16 +2985,16 @@
       }
     },
     "node_modules/@jest/console": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz",
-      "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
+      "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
       "dev": true,
       "dependencies": {
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
         "chalk": "^4.0.0",
-        "jest-message-util": "^29.6.2",
-        "jest-util": "^29.6.2",
+        "jest-message-util": "^29.7.0",
+        "jest-util": "^29.7.0",
         "slash": "^3.0.0"
       },
       "engines": {
@@ -3009,37 +3017,37 @@
       }
     },
     "node_modules/@jest/core": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz",
-      "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
+      "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
       "dev": true,
       "dependencies": {
-        "@jest/console": "^29.6.2",
-        "@jest/reporters": "^29.6.2",
-        "@jest/test-result": "^29.6.2",
-        "@jest/transform": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/console": "^29.7.0",
+        "@jest/reporters": "^29.7.0",
+        "@jest/test-result": "^29.7.0",
+        "@jest/transform": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
         "ansi-escapes": "^4.2.1",
         "chalk": "^4.0.0",
         "ci-info": "^3.2.0",
         "exit": "^0.1.2",
         "graceful-fs": "^4.2.9",
-        "jest-changed-files": "^29.5.0",
-        "jest-config": "^29.6.2",
-        "jest-haste-map": "^29.6.2",
-        "jest-message-util": "^29.6.2",
-        "jest-regex-util": "^29.4.3",
-        "jest-resolve": "^29.6.2",
-        "jest-resolve-dependencies": "^29.6.2",
-        "jest-runner": "^29.6.2",
-        "jest-runtime": "^29.6.2",
-        "jest-snapshot": "^29.6.2",
-        "jest-util": "^29.6.2",
-        "jest-validate": "^29.6.2",
-        "jest-watcher": "^29.6.2",
+        "jest-changed-files": "^29.7.0",
+        "jest-config": "^29.7.0",
+        "jest-haste-map": "^29.7.0",
+        "jest-message-util": "^29.7.0",
+        "jest-regex-util": "^29.6.3",
+        "jest-resolve": "^29.7.0",
+        "jest-resolve-dependencies": "^29.7.0",
+        "jest-runner": "^29.7.0",
+        "jest-runtime": "^29.7.0",
+        "jest-snapshot": "^29.7.0",
+        "jest-util": "^29.7.0",
+        "jest-validate": "^29.7.0",
+        "jest-watcher": "^29.7.0",
         "micromatch": "^4.0.4",
-        "pretty-format": "^29.6.2",
+        "pretty-format": "^29.7.0",
         "slash": "^3.0.0",
         "strip-ansi": "^6.0.0"
       },
@@ -3071,88 +3079,88 @@
       }
     },
     "node_modules/@jest/environment": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz",
-      "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
+      "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
       "dev": true,
       "dependencies": {
-        "@jest/fake-timers": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/fake-timers": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
-        "jest-mock": "^29.6.2"
+        "jest-mock": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/@jest/expect": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz",
-      "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
+      "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
       "dev": true,
       "dependencies": {
-        "expect": "^29.6.2",
-        "jest-snapshot": "^29.6.2"
+        "expect": "^29.7.0",
+        "jest-snapshot": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/@jest/expect-utils": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz",
-      "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
+      "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
       "dev": true,
       "dependencies": {
-        "jest-get-type": "^29.4.3"
+        "jest-get-type": "^29.6.3"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/@jest/fake-timers": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz",
-      "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
+      "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
       "dev": true,
       "dependencies": {
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "@sinonjs/fake-timers": "^10.0.2",
         "@types/node": "*",
-        "jest-message-util": "^29.6.2",
-        "jest-mock": "^29.6.2",
-        "jest-util": "^29.6.2"
+        "jest-message-util": "^29.7.0",
+        "jest-mock": "^29.7.0",
+        "jest-util": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/@jest/globals": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz",
-      "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
+      "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
       "dev": true,
       "dependencies": {
-        "@jest/environment": "^29.6.2",
-        "@jest/expect": "^29.6.2",
-        "@jest/types": "^29.6.1",
-        "jest-mock": "^29.6.2"
+        "@jest/environment": "^29.7.0",
+        "@jest/expect": "^29.7.0",
+        "@jest/types": "^29.6.3",
+        "jest-mock": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/@jest/reporters": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz",
-      "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
+      "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
       "dev": true,
       "dependencies": {
         "@bcoe/v8-coverage": "^0.2.3",
-        "@jest/console": "^29.6.2",
-        "@jest/test-result": "^29.6.2",
-        "@jest/transform": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/console": "^29.7.0",
+        "@jest/test-result": "^29.7.0",
+        "@jest/transform": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@jridgewell/trace-mapping": "^0.3.18",
         "@types/node": "*",
         "chalk": "^4.0.0",
@@ -3161,13 +3169,13 @@
         "glob": "^7.1.3",
         "graceful-fs": "^4.2.9",
         "istanbul-lib-coverage": "^3.0.0",
-        "istanbul-lib-instrument": "^5.1.0",
+        "istanbul-lib-instrument": "^6.0.0",
         "istanbul-lib-report": "^3.0.0",
         "istanbul-lib-source-maps": "^4.0.0",
         "istanbul-reports": "^3.1.3",
-        "jest-message-util": "^29.6.2",
-        "jest-util": "^29.6.2",
-        "jest-worker": "^29.6.2",
+        "jest-message-util": "^29.7.0",
+        "jest-util": "^29.7.0",
+        "jest-worker": "^29.7.0",
         "slash": "^3.0.0",
         "string-length": "^4.0.1",
         "strip-ansi": "^6.0.0",
@@ -3201,10 +3209,26 @@
         "url": "https://github.com/chalk/chalk?sponsor=1"
       }
     },
+    "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz",
+      "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.12.3",
+        "@babel/parser": "^7.14.7",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/@jest/schemas": {
-      "version": "29.6.0",
-      "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz",
-      "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==",
+      "version": "29.6.3",
+      "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
+      "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
       "dependencies": {
         "@sinclair/typebox": "^0.27.8"
       },
@@ -3213,9 +3237,9 @@
       }
     },
     "node_modules/@jest/source-map": {
-      "version": "29.6.0",
-      "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz",
-      "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==",
+      "version": "29.6.3",
+      "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
+      "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
       "dev": true,
       "dependencies": {
         "@jridgewell/trace-mapping": "^0.3.18",
@@ -3227,13 +3251,13 @@
       }
     },
     "node_modules/@jest/test-result": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz",
-      "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
+      "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
       "dev": true,
       "dependencies": {
-        "@jest/console": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/console": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@types/istanbul-lib-coverage": "^2.0.0",
         "collect-v8-coverage": "^1.0.0"
       },
@@ -3242,14 +3266,14 @@
       }
     },
     "node_modules/@jest/test-sequencer": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz",
-      "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
+      "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
       "dev": true,
       "dependencies": {
-        "@jest/test-result": "^29.6.2",
+        "@jest/test-result": "^29.7.0",
         "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.6.2",
+        "jest-haste-map": "^29.7.0",
         "slash": "^3.0.0"
       },
       "engines": {
@@ -3257,22 +3281,22 @@
       }
     },
     "node_modules/@jest/transform": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz",
-      "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
+      "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
       "dev": true,
       "dependencies": {
         "@babel/core": "^7.11.6",
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "@jridgewell/trace-mapping": "^0.3.18",
         "babel-plugin-istanbul": "^6.1.1",
         "chalk": "^4.0.0",
         "convert-source-map": "^2.0.0",
         "fast-json-stable-stringify": "^2.1.0",
         "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.6.2",
-        "jest-regex-util": "^29.4.3",
-        "jest-util": "^29.6.2",
+        "jest-haste-map": "^29.7.0",
+        "jest-regex-util": "^29.6.3",
+        "jest-util": "^29.7.0",
         "micromatch": "^4.0.4",
         "pirates": "^4.0.4",
         "slash": "^3.0.0",
@@ -3303,12 +3327,12 @@
       "license": "MIT"
     },
     "node_modules/@jest/types": {
-      "version": "29.6.1",
-      "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz",
-      "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==",
+      "version": "29.6.3",
+      "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
+      "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
       "dev": true,
       "dependencies": {
-        "@jest/schemas": "^29.6.0",
+        "@jest/schemas": "^29.6.3",
         "@types/istanbul-lib-coverage": "^2.0.0",
         "@types/istanbul-reports": "^3.0.0",
         "@types/node": "*",
@@ -3374,13 +3398,13 @@
       "license": "MIT"
     },
     "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.18",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
-      "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
+      "version": "0.3.19",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz",
+      "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==",
       "dev": true,
       "dependencies": {
-        "@jridgewell/resolve-uri": "3.1.0",
-        "@jridgewell/sourcemap-codec": "1.4.14"
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
       }
     },
     "node_modules/@manypkg/find-root": {
@@ -3784,9 +3808,10 @@
       "license": "MIT"
     },
     "node_modules/@types/async-retry": {
-      "version": "1.4.5",
+      "version": "1.4.6",
+      "resolved": "https://registry.npmjs.org/@types/async-retry/-/async-retry-1.4.6.tgz",
+      "integrity": "sha512-or8JPgYUtyPpO0ddHImwUWmSjVE/UalxgMm2d0r3698QhjzlM7eke0PT60bOxs1NG7HxU232RQ1vy1iQKGGRTw==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@types/retry": "*"
       }
@@ -3838,9 +3863,10 @@
       }
     },
     "node_modules/@types/bunyan": {
-      "version": "1.8.8",
+      "version": "1.8.9",
+      "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.9.tgz",
+      "integrity": "sha512-ZqS9JGpBxVOvsawzmVt30sP++gSQMTejCkIAQ3VdadOcRE8izTyW66hufvwLeH+YEGP6Js2AW7Gz+RMyvrEbmw==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@types/node": "*"
       }
@@ -3854,9 +3880,10 @@
       }
     },
     "node_modules/@types/deep-equal": {
-      "version": "1.0.1",
-      "dev": true,
-      "license": "MIT"
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@types/deep-equal/-/deep-equal-1.0.2.tgz",
+      "integrity": "sha512-pjMMQWhqEKL/rxzUWQKpnbM2oFhRIx4kJ/7mH7MtbJWOA0yrZK0h4WA+RgKa70IHS9amWT7+jvmVmEcL2nXm3A==",
+      "dev": true
     },
     "node_modules/@types/express": {
       "version": "4.17.17",
@@ -3926,9 +3953,9 @@
       }
     },
     "node_modules/@types/jest": {
-      "version": "29.5.3",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz",
-      "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==",
+      "version": "29.5.5",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz",
+      "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==",
       "dev": true,
       "dependencies": {
         "expect": "^29.0.0",
@@ -3966,9 +3993,10 @@
       "license": "MIT"
     },
     "node_modules/@types/make-fetch-happen": {
-      "version": "10.0.1",
+      "version": "10.0.2",
+      "resolved": "https://registry.npmjs.org/@types/make-fetch-happen/-/make-fetch-happen-10.0.2.tgz",
+      "integrity": "sha512-NSMHhLp2dpXGqN0aolc0SygCrmck6HYTZjlRd1ys51yCVLhoFwV/5xwSDe4XDkNmWpeqKIqpSrN+w/xXU3XPEw==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@types/node-fetch": "*",
         "@types/retry": "*",
@@ -3999,17 +4027,30 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "14.18.54",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.54.tgz",
-      "integrity": "sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw=="
+      "version": "14.18.63",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz",
+      "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ=="
     },
     "node_modules/@types/node-fetch": {
-      "version": "2.6.4",
-      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.4.tgz",
-      "integrity": "sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==",
+      "version": "2.6.6",
+      "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.6.tgz",
+      "integrity": "sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==",
       "dependencies": {
         "@types/node": "*",
-        "form-data": "^3.0.0"
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@types/node-fetch/node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
       }
     },
     "node_modules/@types/normalize-package-data": {
@@ -4076,9 +4117,9 @@
       "license": "MIT"
     },
     "node_modules/@types/uuid": {
-      "version": "9.0.2",
-      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz",
-      "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ=="
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.4.tgz",
+      "integrity": "sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA=="
     },
     "node_modules/@types/ws": {
       "version": "8.5.4",
@@ -4089,9 +4130,9 @@
       }
     },
     "node_modules/@types/yargs": {
-      "version": "17.0.24",
-      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
-      "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
+      "version": "17.0.26",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.26.tgz",
+      "integrity": "sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==",
       "dev": true,
       "dependencies": {
         "@types/yargs-parser": "*"
@@ -4465,9 +4506,9 @@
       }
     },
     "node_modules/@whatwg-node/fetch/node_modules/@types/node": {
-      "version": "18.17.1",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.1.tgz",
-      "integrity": "sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==",
+      "version": "18.18.3",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.3.tgz",
+      "integrity": "sha512-0OVfGupTl3NBFr8+iXpfZ8NR7jfFO+P1Q+IO/q0wbo02wYkP5gy36phojeYWpLQ6WAMjl+VfmqUk2YbUfp0irA==",
       "dev": true,
       "peer": true
     },
@@ -4487,9 +4528,10 @@
       }
     },
     "node_modules/@wry/context": {
-      "version": "0.7.0",
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.3.tgz",
+      "integrity": "sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "tslib": "^2.3.0"
       },
@@ -4498,9 +4540,10 @@
       }
     },
     "node_modules/@wry/equality": {
-      "version": "0.5.3",
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.6.tgz",
+      "integrity": "sha512-D46sfMTngaYlrH+OspKf8mIJETntFnf6Hsjb0V41jAXJ7Bx2kB8Rv8RCUujuVWYttFtHkUNp7g+FwxNQAr6mXA==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "tslib": "^2.3.0"
       },
@@ -4509,9 +4552,10 @@
       }
     },
     "node_modules/@wry/trie": {
-      "version": "0.3.2",
+      "version": "0.4.3",
+      "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz",
+      "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "tslib": "^2.3.0"
       },
@@ -4714,6 +4758,16 @@
         "sprintf-js": "~1.0.2"
       }
     },
+    "node_modules/argv": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz",
+      "integrity": "sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw==",
+      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+      "dev": true,
+      "engines": {
+        "node": ">=0.6.10"
+      }
+    },
     "node_modules/arr-diff": {
       "version": "4.0.0",
       "dev": true,
@@ -4884,15 +4938,15 @@
       }
     },
     "node_modules/babel-jest": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz",
-      "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
+      "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
       "dev": true,
       "dependencies": {
-        "@jest/transform": "^29.6.2",
+        "@jest/transform": "^29.7.0",
         "@types/babel__core": "^7.1.14",
         "babel-plugin-istanbul": "^6.1.1",
-        "babel-preset-jest": "^29.5.0",
+        "babel-preset-jest": "^29.6.3",
         "chalk": "^4.0.0",
         "graceful-fs": "^4.2.9",
         "slash": "^3.0.0"
@@ -4935,9 +4989,10 @@
       }
     },
     "node_modules/babel-plugin-jest-hoist": {
-      "version": "29.5.0",
+      "version": "29.6.3",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
+      "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@babel/template": "^7.3.3",
         "@babel/types": "^7.3.3",
@@ -5013,11 +5068,12 @@
       }
     },
     "node_modules/babel-preset-jest": {
-      "version": "29.5.0",
+      "version": "29.6.3",
+      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
+      "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
-        "babel-plugin-jest-hoist": "^29.5.0",
+        "babel-plugin-jest-hoist": "^29.6.3",
         "babel-preset-current-node-syntax": "^1.0.0"
       },
       "engines": {
@@ -5562,7 +5618,9 @@
       }
     },
     "node_modules/ci-info": {
-      "version": "3.8.0",
+      "version": "3.9.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+      "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
       "dev": true,
       "funding": [
         {
@@ -5570,7 +5628,6 @@
           "url": "https://github.com/sponsors/sibiraj-s"
         }
       ],
-      "license": "MIT",
       "engines": {
         "node": ">=8"
       }
@@ -5779,6 +5836,26 @@
         "node": ">= 0.12.0"
       }
     },
+    "node_modules/codecov": {
+      "version": "3.8.3",
+      "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.8.3.tgz",
+      "integrity": "sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA==",
+      "deprecated": "https://about.codecov.io/blog/codecov-uploader-deprecation-plan/",
+      "dev": true,
+      "dependencies": {
+        "argv": "0.0.2",
+        "ignore-walk": "3.0.4",
+        "js-yaml": "3.14.1",
+        "teeny-request": "7.1.1",
+        "urlgrey": "1.0.0"
+      },
+      "bin": {
+        "codecov": "bin/codecov"
+      },
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
     "node_modules/collect-v8-coverage": {
       "version": "1.0.1",
       "dev": true,
@@ -6020,6 +6097,43 @@
         "node": ">=10"
       }
     },
+    "node_modules/create-jest": {
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
+      "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
+      "dev": true,
+      "dependencies": {
+        "@jest/types": "^29.6.3",
+        "chalk": "^4.0.0",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.2.9",
+        "jest-config": "^29.7.0",
+        "jest-util": "^29.7.0",
+        "prompts": "^2.0.1"
+      },
+      "bin": {
+        "create-jest": "bin/create-jest.js"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+      }
+    },
+    "node_modules/create-jest/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
     "node_modules/create-require": {
       "version": "1.1.1",
       "dev": true,
@@ -6074,20 +6188,21 @@
       }
     },
     "node_modules/cspell": {
-      "version": "6.31.2",
-      "resolved": "https://registry.npmjs.org/cspell/-/cspell-6.31.2.tgz",
-      "integrity": "sha512-HJcQ8jqL/1N3Mj5dufFnIZCX3ACuRoFTSVY6h3Bo5wBqd2iiJTyeQ1SY9Zymlxtb2KyJ6jQRiFmkWeFx2HVs7w==",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/cspell/-/cspell-6.31.3.tgz",
+      "integrity": "sha512-VeeShDLWVM6YPiU/imeGy0lmg6ki63tbLEa6hz20BExhzzpmINOP5nSTYtpY0H9zX9TrF/dLbI38TuuYnyG3Uw==",
       "dev": true,
       "dependencies": {
-        "@cspell/cspell-pipe": "6.31.1",
-        "@cspell/cspell-types": "6.31.1",
-        "@cspell/dynamic-import": "6.31.1",
+        "@cspell/cspell-json-reporter": "6.31.3",
+        "@cspell/cspell-pipe": "6.31.3",
+        "@cspell/cspell-types": "6.31.3",
+        "@cspell/dynamic-import": "6.31.3",
         "chalk": "^4.1.2",
         "commander": "^10.0.0",
-        "cspell-gitignore": "6.31.2",
-        "cspell-glob": "6.31.2",
-        "cspell-io": "6.31.2",
-        "cspell-lib": "6.31.2",
+        "cspell-gitignore": "6.31.3",
+        "cspell-glob": "6.31.3",
+        "cspell-io": "6.31.3",
+        "cspell-lib": "6.31.3",
         "fast-glob": "^3.2.12",
         "fast-json-stable-stringify": "^2.1.0",
         "file-entry-cache": "^6.0.1",
@@ -6109,13 +6224,14 @@
       }
     },
     "node_modules/cspell-dictionary": {
-      "version": "6.31.1",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-6.31.3.tgz",
+      "integrity": "sha512-3w5P3Md/tbHLVGPKVL0ePl1ObmNwhdDiEuZ2TXfm2oAIwg4aqeIrw42A2qmhaKLcuAIywpqGZsrGg8TviNNhig==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
-        "@cspell/cspell-pipe": "6.31.1",
-        "@cspell/cspell-types": "6.31.1",
-        "cspell-trie-lib": "6.31.1",
+        "@cspell/cspell-pipe": "6.31.3",
+        "@cspell/cspell-types": "6.31.3",
+        "cspell-trie-lib": "6.31.3",
         "fast-equals": "^4.0.3",
         "gensequence": "^5.0.2"
       },
@@ -6124,12 +6240,12 @@
       }
     },
     "node_modules/cspell-gitignore": {
-      "version": "6.31.2",
-      "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-6.31.2.tgz",
-      "integrity": "sha512-B1i8aiXCIbb/08u0K3xnDyXtg0qD+lb5B2itOOXi7KXlPkKvIuN4hWyXxhVDweWyYWEzyXD5wBpPrqICVrStHQ==",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-6.31.3.tgz",
+      "integrity": "sha512-vCfVG4ZrdwJnsZHl/cdp8AY+YNPL3Ga+0KR9XJsaz69EkQpgI6porEqehuwle7hiXw5e3L7xFwNEbpCBlxgLRA==",
       "dev": true,
       "dependencies": {
-        "cspell-glob": "6.31.2",
+        "cspell-glob": "6.31.3",
         "find-up": "^5.0.0"
       },
       "bin": {
@@ -6197,9 +6313,9 @@
       }
     },
     "node_modules/cspell-glob": {
-      "version": "6.31.2",
-      "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-6.31.2.tgz",
-      "integrity": "sha512-ceTjHM4HaBgvG5S3oiB+PTPYq58EQYG6MmYpycDHzpR5I2H1NurK9lxWHfANmLbi0DsHn58tIZNDMUnnQj19Jw==",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-6.31.3.tgz",
+      "integrity": "sha512-+koUJPSCOittQwhR0T1mj4xXT3N+ZnY2qQ53W6Gz9HY3hVfEEy0NpbwE/Uy7sIvFMbc426fK0tGXjXyIj72uhQ==",
       "dev": true,
       "dependencies": {
         "micromatch": "^4.0.5"
@@ -6209,12 +6325,13 @@
       }
     },
     "node_modules/cspell-grammar": {
-      "version": "6.31.1",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-6.31.3.tgz",
+      "integrity": "sha512-TZYaOLIGAumyHlm4w7HYKKKcR1ZgEMKt7WNjCFqq7yGVW7U+qyjQqR8jqnLiUTZl7c2Tque4mca7n0CFsjVv5A==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
-        "@cspell/cspell-pipe": "6.31.1",
-        "@cspell/cspell-types": "6.31.1"
+        "@cspell/cspell-pipe": "6.31.3",
+        "@cspell/cspell-types": "6.31.3"
       },
       "bin": {
         "cspell-grammar": "bin.mjs"
@@ -6224,12 +6341,12 @@
       }
     },
     "node_modules/cspell-io": {
-      "version": "6.31.2",
-      "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-6.31.2.tgz",
-      "integrity": "sha512-Lp7LsF/f35LaOneROb/9mWiprShz2ONxjYFAt3bYP7gIxq41lWi8QhO+SN6spoqPp/wQXjSqJ7MuTZsemxPRnA==",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-6.31.3.tgz",
+      "integrity": "sha512-yCnnQ5bTbngUuIAaT5yNSdI1P0Kc38uvC8aynNi7tfrCYOQbDu1F9/DcTpbdhrsCv+xUn2TB1YjuCmm0STfJlA==",
       "dev": true,
       "dependencies": {
-        "@cspell/cspell-service-bus": "6.31.1",
+        "@cspell/cspell-service-bus": "6.31.3",
         "node-fetch": "^2.6.9"
       },
       "engines": {
@@ -6237,24 +6354,24 @@
       }
     },
     "node_modules/cspell-lib": {
-      "version": "6.31.2",
-      "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-6.31.2.tgz",
-      "integrity": "sha512-LqaB2ZfVfQHKL5aZzYoKU6/UxxAtWeXAYwpC9l+satXmajYyXtAh4kWmuW+y7kKRH2jA79rJQS3QE6ToeSqgQQ==",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-6.31.3.tgz",
+      "integrity": "sha512-Dv55aecaMvT/5VbNryKo0Zos8dtHon7e1K0z8DR4/kGZdQVT0bOFWeotSLhuaIqoNFdEt8ypfKbrIHIdbgt1Hg==",
       "dev": true,
       "dependencies": {
-        "@cspell/cspell-bundled-dicts": "6.31.2",
-        "@cspell/cspell-pipe": "6.31.1",
-        "@cspell/cspell-types": "6.31.1",
-        "@cspell/strong-weak-map": "6.31.1",
+        "@cspell/cspell-bundled-dicts": "6.31.3",
+        "@cspell/cspell-pipe": "6.31.3",
+        "@cspell/cspell-types": "6.31.3",
+        "@cspell/strong-weak-map": "6.31.3",
         "clear-module": "^4.1.2",
         "comment-json": "^4.2.3",
         "configstore": "^5.0.1",
         "cosmiconfig": "8.0.0",
-        "cspell-dictionary": "6.31.1",
-        "cspell-glob": "6.31.2",
-        "cspell-grammar": "6.31.1",
-        "cspell-io": "6.31.2",
-        "cspell-trie-lib": "6.31.1",
+        "cspell-dictionary": "6.31.3",
+        "cspell-glob": "6.31.3",
+        "cspell-grammar": "6.31.3",
+        "cspell-io": "6.31.3",
+        "cspell-trie-lib": "6.31.3",
         "fast-equals": "^4.0.3",
         "find-up": "^5.0.0",
         "gensequence": "^5.0.2",
@@ -6356,12 +6473,13 @@
       }
     },
     "node_modules/cspell-trie-lib": {
-      "version": "6.31.1",
+      "version": "6.31.3",
+      "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-6.31.3.tgz",
+      "integrity": "sha512-HNUcLWOZAvtM3E34U+7/mSSpO0F6nLd/kFlRIcvSvPb9taqKe8bnSa0Yyb3dsdMq9rMxUmuDQtF+J6arZK343g==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
-        "@cspell/cspell-pipe": "6.31.1",
-        "@cspell/cspell-types": "6.31.1",
+        "@cspell/cspell-pipe": "6.31.3",
+        "@cspell/cspell-types": "6.31.3",
         "gensequence": "^5.0.2"
       },
       "engines": {
@@ -6454,9 +6572,10 @@
       }
     },
     "node_modules/cucumber-messages/node_modules/@types/uuid": {
-      "version": "3.4.10",
-      "dev": true,
-      "license": "MIT"
+      "version": "3.4.11",
+      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.11.tgz",
+      "integrity": "sha512-CJNkbEu4IdVuBMRVaNC2GjASgJK7ziqDlVXWuJ1pvhOLADl7nzxhTKjHRdOmo2SuXuygcWBmzgYgn9foTX0UiA==",
+      "dev": true
     },
     "node_modules/cucumber-messages/node_modules/uuid": {
       "version": "3.4.0",
@@ -6739,9 +6858,9 @@
       }
     },
     "node_modules/diff-sequences": {
-      "version": "29.4.3",
-      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz",
-      "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==",
+      "version": "29.6.3",
+      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
+      "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==",
       "dev": true,
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -7239,9 +7358,9 @@
       }
     },
     "node_modules/eslint/node_modules/eslint-scope": {
-      "version": "7.2.1",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz",
-      "integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==",
+      "version": "7.2.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
       "dev": true,
       "peer": true,
       "dependencies": {
@@ -7281,9 +7400,10 @@
       }
     },
     "node_modules/eslint/node_modules/globals": {
-      "version": "13.20.0",
+      "version": "13.22.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
+      "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
       "dev": true,
-      "license": "MIT",
       "peer": true,
       "dependencies": {
         "type-fest": "^0.20.2"
@@ -7465,8 +7585,9 @@
     },
     "node_modules/execa": {
       "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "cross-spawn": "^7.0.3",
         "get-stream": "^6.0.0",
@@ -7618,17 +7739,16 @@
       "license": "MIT"
     },
     "node_modules/expect": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz",
-      "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
+      "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
       "dev": true,
       "dependencies": {
-        "@jest/expect-utils": "^29.6.2",
-        "@types/node": "*",
-        "jest-get-type": "^29.4.3",
-        "jest-matcher-utils": "^29.6.2",
-        "jest-message-util": "^29.6.2",
-        "jest-util": "^29.6.2"
+        "@jest/expect-utils": "^29.7.0",
+        "jest-get-type": "^29.6.3",
+        "jest-matcher-utils": "^29.7.0",
+        "jest-message-util": "^29.7.0",
+        "jest-util": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -7790,8 +7910,9 @@
     },
     "node_modules/fast-equals": {
       "version": "4.0.3",
-      "dev": true,
-      "license": "MIT"
+      "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-4.0.3.tgz",
+      "integrity": "sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==",
+      "dev": true
     },
     "node_modules/fast-glob": {
       "version": "3.2.12",
@@ -8016,6 +8137,7 @@
     },
     "node_modules/form-data": {
       "version": "3.0.1",
+      "dev": true,
       "license": "MIT",
       "dependencies": {
         "asynckit": "^0.4.0",
@@ -8067,22 +8189,22 @@
       }
     },
     "node_modules/fs-minipass": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz",
-      "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
+      "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
       "dependencies": {
-        "minipass": "^5.0.0"
+        "minipass": "^7.0.3"
       },
       "engines": {
         "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
       }
     },
     "node_modules/fs-minipass/node_modules/minipass": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
-      "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+      "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
       "engines": {
-        "node": ">=8"
+        "node": ">=16 || 14 >=14.17"
       }
     },
     "node_modules/fs.realpath": {
@@ -8131,8 +8253,9 @@
     },
     "node_modules/gensequence": {
       "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-5.0.2.tgz",
+      "integrity": "sha512-JlKEZnFc6neaeSVlkzBGGgkIoIaSxMgvdamRoPN8r3ozm2r9dusqxeKqYQ7lhzmj2UhFQP8nkyfCaiLQxiLrDA==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">=14"
       }
@@ -8186,8 +8309,9 @@
     },
     "node_modules/get-stream": {
       "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": ">=10"
       },
@@ -8341,9 +8465,9 @@
       "dev": true
     },
     "node_modules/graphql": {
-      "version": "16.7.1",
-      "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.7.1.tgz",
-      "integrity": "sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg==",
+      "version": "16.8.1",
+      "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz",
+      "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==",
       "engines": {
         "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
       }
@@ -8420,9 +8544,9 @@
       }
     },
     "node_modules/graphql-http": {
-      "version": "1.21.0",
-      "resolved": "https://registry.npmjs.org/graphql-http/-/graphql-http-1.21.0.tgz",
-      "integrity": "sha512-yrItPfHj5WeT4n7iusbVin+vGSQjXFAX6U/GnYytdCJRXVad1TWGtYFDZ2ROjCKpXQzIwvfbiWCEwfuXgR3B6A==",
+      "version": "1.22.0",
+      "resolved": "https://registry.npmjs.org/graphql-http/-/graphql-http-1.22.0.tgz",
+      "integrity": "sha512-9RBUlGJWBFqz9LwfpmAbjJL/8j/HCNkZwPBU5+Bfmwez+1Ay43DocMNQYpIWsWqH0Ftv6PTNAh2aRnnMCBJgLw==",
       "dev": true,
       "engines": {
         "node": ">=12"
@@ -8715,8 +8839,9 @@
     },
     "node_modules/human-signals": {
       "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
       "dev": true,
-      "license": "Apache-2.0",
       "engines": {
         "node": ">=10.17.0"
       }
@@ -8766,6 +8891,15 @@
         "node": ">= 4"
       }
     },
+    "node_modules/ignore-walk": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz",
+      "integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==",
+      "dev": true,
+      "dependencies": {
+        "minimatch": "^3.0.4"
+      }
+    },
     "node_modules/immutable": {
       "version": "3.7.6",
       "dev": true,
@@ -9547,15 +9681,15 @@
       }
     },
     "node_modules/jest": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz",
-      "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
+      "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
       "dev": true,
       "dependencies": {
-        "@jest/core": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/core": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "import-local": "^3.0.2",
-        "jest-cli": "^29.6.2"
+        "jest-cli": "^29.7.0"
       },
       "bin": {
         "jest": "bin/jest.js"
@@ -9573,11 +9707,13 @@
       }
     },
     "node_modules/jest-changed-files": {
-      "version": "29.5.0",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
+      "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "execa": "^5.0.0",
+        "jest-util": "^29.7.0",
         "p-limit": "^3.1.0"
       },
       "engines": {
@@ -9586,8 +9722,9 @@
     },
     "node_modules/jest-changed-files/node_modules/p-limit": {
       "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "yocto-queue": "^0.1.0"
       },
@@ -9599,28 +9736,28 @@
       }
     },
     "node_modules/jest-circus": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz",
-      "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
+      "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
       "dev": true,
       "dependencies": {
-        "@jest/environment": "^29.6.2",
-        "@jest/expect": "^29.6.2",
-        "@jest/test-result": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/environment": "^29.7.0",
+        "@jest/expect": "^29.7.0",
+        "@jest/test-result": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
         "chalk": "^4.0.0",
         "co": "^4.6.0",
         "dedent": "^1.0.0",
         "is-generator-fn": "^2.0.0",
-        "jest-each": "^29.6.2",
-        "jest-matcher-utils": "^29.6.2",
-        "jest-message-util": "^29.6.2",
-        "jest-runtime": "^29.6.2",
-        "jest-snapshot": "^29.6.2",
-        "jest-util": "^29.6.2",
+        "jest-each": "^29.7.0",
+        "jest-matcher-utils": "^29.7.0",
+        "jest-message-util": "^29.7.0",
+        "jest-runtime": "^29.7.0",
+        "jest-snapshot": "^29.7.0",
+        "jest-util": "^29.7.0",
         "p-limit": "^3.1.0",
-        "pretty-format": "^29.6.2",
+        "pretty-format": "^29.7.0",
         "pure-rand": "^6.0.0",
         "slash": "^3.0.0",
         "stack-utils": "^2.0.3"
@@ -9659,22 +9796,21 @@
       }
     },
     "node_modules/jest-cli": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz",
-      "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
+      "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
       "dev": true,
       "dependencies": {
-        "@jest/core": "^29.6.2",
-        "@jest/test-result": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/core": "^29.7.0",
+        "@jest/test-result": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "chalk": "^4.0.0",
+        "create-jest": "^29.7.0",
         "exit": "^0.1.2",
-        "graceful-fs": "^4.2.9",
         "import-local": "^3.0.2",
-        "jest-config": "^29.6.2",
-        "jest-util": "^29.6.2",
-        "jest-validate": "^29.6.2",
-        "prompts": "^2.0.1",
+        "jest-config": "^29.7.0",
+        "jest-util": "^29.7.0",
+        "jest-validate": "^29.7.0",
         "yargs": "^17.3.1"
       },
       "bin": {
@@ -9708,31 +9844,31 @@
       }
     },
     "node_modules/jest-config": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz",
-      "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
+      "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
       "dev": true,
       "dependencies": {
         "@babel/core": "^7.11.6",
-        "@jest/test-sequencer": "^29.6.2",
-        "@jest/types": "^29.6.1",
-        "babel-jest": "^29.6.2",
+        "@jest/test-sequencer": "^29.7.0",
+        "@jest/types": "^29.6.3",
+        "babel-jest": "^29.7.0",
         "chalk": "^4.0.0",
         "ci-info": "^3.2.0",
         "deepmerge": "^4.2.2",
         "glob": "^7.1.3",
         "graceful-fs": "^4.2.9",
-        "jest-circus": "^29.6.2",
-        "jest-environment-node": "^29.6.2",
-        "jest-get-type": "^29.4.3",
-        "jest-regex-util": "^29.4.3",
-        "jest-resolve": "^29.6.2",
-        "jest-runner": "^29.6.2",
-        "jest-util": "^29.6.2",
-        "jest-validate": "^29.6.2",
+        "jest-circus": "^29.7.0",
+        "jest-environment-node": "^29.7.0",
+        "jest-get-type": "^29.6.3",
+        "jest-regex-util": "^29.6.3",
+        "jest-resolve": "^29.7.0",
+        "jest-runner": "^29.7.0",
+        "jest-util": "^29.7.0",
+        "jest-validate": "^29.7.0",
         "micromatch": "^4.0.4",
         "parse-json": "^5.2.0",
-        "pretty-format": "^29.6.2",
+        "pretty-format": "^29.7.0",
         "slash": "^3.0.0",
         "strip-json-comments": "^3.1.1"
       },
@@ -10030,9 +10166,10 @@
       "license": "MIT"
     },
     "node_modules/jest-cucumber/node_modules/@types/yargs": {
-      "version": "15.0.15",
+      "version": "15.0.16",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.16.tgz",
+      "integrity": "sha512-2FeD5qezW3FvLpZ0JpfuaEWepgNLl9b2gQYiz/ce0NhoB1W/D+VZu98phITXkADYerfr/jb7JcDcVhITsc9bwg==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@types/yargs-parser": "*"
       }
@@ -10765,15 +10902,15 @@
       }
     },
     "node_modules/jest-diff": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz",
-      "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
+      "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
       "dev": true,
       "dependencies": {
         "chalk": "^4.0.0",
-        "diff-sequences": "^29.4.3",
-        "jest-get-type": "^29.4.3",
-        "pretty-format": "^29.6.2"
+        "diff-sequences": "^29.6.3",
+        "jest-get-type": "^29.6.3",
+        "pretty-format": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -10796,9 +10933,9 @@
       }
     },
     "node_modules/jest-docblock": {
-      "version": "29.4.3",
-      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz",
-      "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
+      "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
       "dev": true,
       "dependencies": {
         "detect-newline": "^3.0.0"
@@ -10808,16 +10945,16 @@
       }
     },
     "node_modules/jest-each": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz",
-      "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
+      "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
       "dev": true,
       "dependencies": {
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "chalk": "^4.0.0",
-        "jest-get-type": "^29.4.3",
-        "jest-util": "^29.6.2",
-        "pretty-format": "^29.6.2"
+        "jest-get-type": "^29.6.3",
+        "jest-util": "^29.7.0",
+        "pretty-format": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -10918,9 +11055,10 @@
       }
     },
     "node_modules/jest-environment-jsdom/node_modules/@types/yargs": {
-      "version": "15.0.15",
+      "version": "15.0.16",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.16.tgz",
+      "integrity": "sha512-2FeD5qezW3FvLpZ0JpfuaEWepgNLl9b2gQYiz/ce0NhoB1W/D+VZu98phITXkADYerfr/jb7JcDcVhITsc9bwg==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@types/yargs-parser": "*"
       }
@@ -11023,45 +11161,46 @@
       "license": "MIT"
     },
     "node_modules/jest-environment-node": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz",
-      "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
+      "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
       "dev": true,
       "dependencies": {
-        "@jest/environment": "^29.6.2",
-        "@jest/fake-timers": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/environment": "^29.7.0",
+        "@jest/fake-timers": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
-        "jest-mock": "^29.6.2",
-        "jest-util": "^29.6.2"
+        "jest-mock": "^29.7.0",
+        "jest-util": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/jest-get-type": {
-      "version": "29.4.3",
+      "version": "29.6.3",
+      "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz",
+      "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/jest-haste-map": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz",
-      "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
+      "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
       "dev": true,
       "dependencies": {
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "@types/graceful-fs": "^4.1.3",
         "@types/node": "*",
         "anymatch": "^3.0.3",
         "fb-watchman": "^2.0.0",
         "graceful-fs": "^4.2.9",
-        "jest-regex-util": "^29.4.3",
-        "jest-util": "^29.6.2",
-        "jest-worker": "^29.6.2",
+        "jest-regex-util": "^29.6.3",
+        "jest-util": "^29.7.0",
+        "jest-worker": "^29.7.0",
         "micromatch": "^4.0.4",
         "walker": "^1.0.8"
       },
@@ -11258,9 +11397,10 @@
       }
     },
     "node_modules/jest-jasmine2/node_modules/@types/yargs": {
-      "version": "15.0.15",
+      "version": "15.0.16",
+      "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.16.tgz",
+      "integrity": "sha512-2FeD5qezW3FvLpZ0JpfuaEWepgNLl9b2gQYiz/ce0NhoB1W/D+VZu98phITXkADYerfr/jb7JcDcVhITsc9bwg==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@types/yargs-parser": "*"
       }
@@ -11849,28 +11989,28 @@
       }
     },
     "node_modules/jest-leak-detector": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz",
-      "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
+      "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
       "dev": true,
       "dependencies": {
-        "jest-get-type": "^29.4.3",
-        "pretty-format": "^29.6.2"
+        "jest-get-type": "^29.6.3",
+        "pretty-format": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/jest-matcher-utils": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz",
-      "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
+      "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
       "dev": true,
       "dependencies": {
         "chalk": "^4.0.0",
-        "jest-diff": "^29.6.2",
-        "jest-get-type": "^29.4.3",
-        "pretty-format": "^29.6.2"
+        "jest-diff": "^29.7.0",
+        "jest-get-type": "^29.6.3",
+        "pretty-format": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -11892,18 +12032,18 @@
       }
     },
     "node_modules/jest-message-util": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz",
-      "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
+      "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
       "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.12.13",
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "@types/stack-utils": "^2.0.0",
         "chalk": "^4.0.0",
         "graceful-fs": "^4.2.9",
         "micromatch": "^4.0.4",
-        "pretty-format": "^29.6.2",
+        "pretty-format": "^29.7.0",
         "slash": "^3.0.0",
         "stack-utils": "^2.0.3"
       },
@@ -11927,14 +12067,14 @@
       }
     },
     "node_modules/jest-mock": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz",
-      "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
+      "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
       "dev": true,
       "dependencies": {
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
-        "jest-util": "^29.6.2"
+        "jest-util": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -11957,25 +12097,26 @@
       }
     },
     "node_modules/jest-regex-util": {
-      "version": "29.4.3",
+      "version": "29.6.3",
+      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
+      "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
       "dev": true,
-      "license": "MIT",
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
       }
     },
     "node_modules/jest-resolve": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz",
-      "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
+      "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
       "dev": true,
       "dependencies": {
         "chalk": "^4.0.0",
         "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.6.2",
+        "jest-haste-map": "^29.7.0",
         "jest-pnp-resolver": "^1.2.2",
-        "jest-util": "^29.6.2",
-        "jest-validate": "^29.6.2",
+        "jest-util": "^29.7.0",
+        "jest-validate": "^29.7.0",
         "resolve": "^1.20.0",
         "resolve.exports": "^2.0.0",
         "slash": "^3.0.0"
@@ -11985,13 +12126,13 @@
       }
     },
     "node_modules/jest-resolve-dependencies": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz",
-      "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
+      "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
       "dev": true,
       "dependencies": {
-        "jest-regex-util": "^29.4.3",
-        "jest-snapshot": "^29.6.2"
+        "jest-regex-util": "^29.6.3",
+        "jest-snapshot": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -12013,30 +12154,30 @@
       }
     },
     "node_modules/jest-runner": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz",
-      "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
+      "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
       "dev": true,
       "dependencies": {
-        "@jest/console": "^29.6.2",
-        "@jest/environment": "^29.6.2",
-        "@jest/test-result": "^29.6.2",
-        "@jest/transform": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/console": "^29.7.0",
+        "@jest/environment": "^29.7.0",
+        "@jest/test-result": "^29.7.0",
+        "@jest/transform": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
         "chalk": "^4.0.0",
         "emittery": "^0.13.1",
         "graceful-fs": "^4.2.9",
-        "jest-docblock": "^29.4.3",
-        "jest-environment-node": "^29.6.2",
-        "jest-haste-map": "^29.6.2",
-        "jest-leak-detector": "^29.6.2",
-        "jest-message-util": "^29.6.2",
-        "jest-resolve": "^29.6.2",
-        "jest-runtime": "^29.6.2",
-        "jest-util": "^29.6.2",
-        "jest-watcher": "^29.6.2",
-        "jest-worker": "^29.6.2",
+        "jest-docblock": "^29.7.0",
+        "jest-environment-node": "^29.7.0",
+        "jest-haste-map": "^29.7.0",
+        "jest-leak-detector": "^29.7.0",
+        "jest-message-util": "^29.7.0",
+        "jest-resolve": "^29.7.0",
+        "jest-runtime": "^29.7.0",
+        "jest-util": "^29.7.0",
+        "jest-watcher": "^29.7.0",
+        "jest-worker": "^29.7.0",
         "p-limit": "^3.1.0",
         "source-map-support": "0.5.13"
       },
@@ -12086,31 +12227,31 @@
       }
     },
     "node_modules/jest-runtime": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz",
-      "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==",
-      "dev": true,
-      "dependencies": {
-        "@jest/environment": "^29.6.2",
-        "@jest/fake-timers": "^29.6.2",
-        "@jest/globals": "^29.6.2",
-        "@jest/source-map": "^29.6.0",
-        "@jest/test-result": "^29.6.2",
-        "@jest/transform": "^29.6.2",
-        "@jest/types": "^29.6.1",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
+      "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
+      "dev": true,
+      "dependencies": {
+        "@jest/environment": "^29.7.0",
+        "@jest/fake-timers": "^29.7.0",
+        "@jest/globals": "^29.7.0",
+        "@jest/source-map": "^29.6.3",
+        "@jest/test-result": "^29.7.0",
+        "@jest/transform": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
         "chalk": "^4.0.0",
         "cjs-module-lexer": "^1.0.0",
         "collect-v8-coverage": "^1.0.0",
         "glob": "^7.1.3",
         "graceful-fs": "^4.2.9",
-        "jest-haste-map": "^29.6.2",
-        "jest-message-util": "^29.6.2",
-        "jest-mock": "^29.6.2",
-        "jest-regex-util": "^29.4.3",
-        "jest-resolve": "^29.6.2",
-        "jest-snapshot": "^29.6.2",
-        "jest-util": "^29.6.2",
+        "jest-haste-map": "^29.7.0",
+        "jest-message-util": "^29.7.0",
+        "jest-mock": "^29.7.0",
+        "jest-regex-util": "^29.6.3",
+        "jest-resolve": "^29.7.0",
+        "jest-snapshot": "^29.7.0",
+        "jest-util": "^29.7.0",
         "slash": "^3.0.0",
         "strip-bom": "^4.0.0"
       },
@@ -12146,9 +12287,9 @@
       }
     },
     "node_modules/jest-snapshot": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz",
-      "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
+      "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
       "dev": true,
       "dependencies": {
         "@babel/core": "^7.11.6",
@@ -12156,20 +12297,20 @@
         "@babel/plugin-syntax-jsx": "^7.7.2",
         "@babel/plugin-syntax-typescript": "^7.7.2",
         "@babel/types": "^7.3.3",
-        "@jest/expect-utils": "^29.6.2",
-        "@jest/transform": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/expect-utils": "^29.7.0",
+        "@jest/transform": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "babel-preset-current-node-syntax": "^1.0.0",
         "chalk": "^4.0.0",
-        "expect": "^29.6.2",
+        "expect": "^29.7.0",
         "graceful-fs": "^4.2.9",
-        "jest-diff": "^29.6.2",
-        "jest-get-type": "^29.4.3",
-        "jest-matcher-utils": "^29.6.2",
-        "jest-message-util": "^29.6.2",
-        "jest-util": "^29.6.2",
+        "jest-diff": "^29.7.0",
+        "jest-get-type": "^29.6.3",
+        "jest-matcher-utils": "^29.7.0",
+        "jest-message-util": "^29.7.0",
+        "jest-util": "^29.7.0",
         "natural-compare": "^1.4.0",
-        "pretty-format": "^29.6.2",
+        "pretty-format": "^29.7.0",
         "semver": "^7.5.3"
       },
       "engines": {
@@ -12192,12 +12333,12 @@
       }
     },
     "node_modules/jest-util": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz",
-      "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
+      "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
       "dev": true,
       "dependencies": {
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
         "chalk": "^4.0.0",
         "ci-info": "^3.2.0",
@@ -12224,17 +12365,17 @@
       }
     },
     "node_modules/jest-validate": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz",
-      "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
+      "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
       "dev": true,
       "dependencies": {
-        "@jest/types": "^29.6.1",
+        "@jest/types": "^29.6.3",
         "camelcase": "^6.2.0",
         "chalk": "^4.0.0",
-        "jest-get-type": "^29.4.3",
+        "jest-get-type": "^29.6.3",
         "leven": "^3.1.0",
-        "pretty-format": "^29.6.2"
+        "pretty-format": "^29.7.0"
       },
       "engines": {
         "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
@@ -12269,18 +12410,18 @@
       }
     },
     "node_modules/jest-watcher": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz",
-      "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
+      "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
       "dev": true,
       "dependencies": {
-        "@jest/test-result": "^29.6.2",
-        "@jest/types": "^29.6.1",
+        "@jest/test-result": "^29.7.0",
+        "@jest/types": "^29.6.3",
         "@types/node": "*",
         "ansi-escapes": "^4.2.1",
         "chalk": "^4.0.0",
         "emittery": "^0.13.1",
-        "jest-util": "^29.6.2",
+        "jest-util": "^29.7.0",
         "string-length": "^4.0.1"
       },
       "engines": {
@@ -12304,13 +12445,13 @@
       }
     },
     "node_modules/jest-worker": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz",
-      "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
+      "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
       "dev": true,
       "dependencies": {
         "@types/node": "*",
-        "jest-util": "^29.6.2",
+        "jest-util": "^29.7.0",
         "merge-stream": "^2.0.0",
         "supports-color": "^8.0.0"
       },
@@ -13490,9 +13631,9 @@
       }
     },
     "node_modules/nock": {
-      "version": "13.3.2",
-      "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.2.tgz",
-      "integrity": "sha512-CwbljitiWJhF1gL83NbanhoKs1l23TDlRioNraPTZrzZIEooPemrHRj5m0FZCPkB1ecdYCSWWGcHysJgX/ngnQ==",
+      "version": "13.3.3",
+      "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.3.tgz",
+      "integrity": "sha512-z+KUlILy9SK/RjpeXDiDUEAq4T94ADPHE3qaRkf66mpEhzc/ytOMm3Bwdrbq6k1tMWkbdujiKim3G2tfQARuJw==",
       "dev": true,
       "dependencies": {
         "debug": "^4.1.0",
@@ -13514,9 +13655,9 @@
       "license": "MIT"
     },
     "node_modules/node-fetch": {
-      "version": "2.6.12",
-      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
-      "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
       "dependencies": {
         "whatwg-url": "^5.0.0"
       },
@@ -13817,12 +13958,14 @@
       }
     },
     "node_modules/optimism": {
-      "version": "0.16.2",
+      "version": "0.17.5",
+      "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.17.5.tgz",
+      "integrity": "sha512-TEcp8ZwK1RczmvMnvktxHSF2tKgMWjJ71xEFGX5ApLh67VsMSTy1ZUlipJw8W+KaqgOmQ+4pqwkeivY89j+4Vw==",
       "dev": true,
-      "license": "MIT",
       "dependencies": {
         "@wry/context": "^0.7.0",
-        "@wry/trie": "^0.3.0"
+        "@wry/trie": "^0.4.3",
+        "tslib": "^2.3.0"
       }
     },
     "node_modules/optionator": {
@@ -14259,9 +14402,9 @@
       }
     },
     "node_modules/prettier": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz",
-      "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==",
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
+      "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
       "dev": true,
       "bin": {
         "prettier": "bin/prettier.cjs"
@@ -14273,12 +14416,28 @@
         "url": "https://github.com/prettier/prettier?sponsor=1"
       }
     },
+    "node_modules/prettier-2": {
+      "name": "prettier",
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+      "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+      "dev": true,
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
     "node_modules/pretty-format": {
-      "version": "29.6.2",
-      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz",
-      "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==",
+      "version": "29.7.0",
+      "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
+      "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
       "dependencies": {
-        "@jest/schemas": "^29.6.0",
+        "@jest/schemas": "^29.6.3",
         "ansi-styles": "^5.0.0",
         "react-is": "^18.0.0"
       },
@@ -14361,10 +14520,11 @@
       }
     },
     "node_modules/protobufjs": {
-      "version": "6.11.3",
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
       "dev": true,
       "hasInstallScript": true,
-      "license": "BSD-3-Clause",
       "dependencies": {
         "@protobufjs/aspromise": "^1.1.2",
         "@protobufjs/base64": "^1.1.2",
@@ -16064,6 +16224,15 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/stream-events": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
+      "integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
+      "dev": true,
+      "dependencies": {
+        "stubs": "^3.0.0"
+      }
+    },
     "node_modules/stream-transform": {
       "version": "2.1.3",
       "dev": true,
@@ -16242,6 +16411,12 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/stubs": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
+      "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
+      "dev": true
+    },
     "node_modules/supports-color": {
       "version": "7.2.0",
       "license": "MIT",
@@ -16335,6 +16510,54 @@
       "version": "4.0.0",
       "license": "ISC"
     },
+    "node_modules/teeny-request": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.1.1.tgz",
+      "integrity": "sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg==",
+      "dev": true,
+      "dependencies": {
+        "http-proxy-agent": "^4.0.0",
+        "https-proxy-agent": "^5.0.0",
+        "node-fetch": "^2.6.1",
+        "stream-events": "^1.0.5",
+        "uuid": "^8.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/teeny-request/node_modules/@tootallnate/once": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+      "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/teeny-request/node_modules/http-proxy-agent": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+      "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+      "dev": true,
+      "dependencies": {
+        "@tootallnate/once": "1",
+        "agent-base": "6",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/teeny-request/node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "dev": true,
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
     "node_modules/term-size": {
       "version": "2.2.1",
       "dev": true,
@@ -16775,9 +16998,9 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.1.6",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
-      "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+      "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
       "dev": true,
       "bin": {
         "tsc": "bin/tsc",
@@ -17031,6 +17254,15 @@
         "requires-port": "^1.0.0"
       }
     },
+    "node_modules/urlgrey": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-1.0.0.tgz",
+      "integrity": "sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w==",
+      "dev": true,
+      "dependencies": {
+        "fast-url-parser": "^1.1.3"
+      }
+    },
     "node_modules/urlpattern-polyfill": {
       "version": "6.0.2",
       "dev": true,
@@ -17061,8 +17293,13 @@
       }
     },
     "node_modules/uuid": {
-      "version": "9.0.0",
-      "license": "MIT",
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
       "bin": {
         "uuid": "dist/bin/uuid"
       }
@@ -17073,19 +17310,25 @@
       "license": "MIT"
     },
     "node_modules/v8-to-istanbul": {
-      "version": "9.1.0",
-      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz",
-      "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==",
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.2.tgz",
+      "integrity": "sha512-ZGBe7VAivuuoQXTeckpbYKTdtjXGcm3ZUHXC0PAk0CzFyuYvwi73a58iEKI3GkGD1c3EHc+EgfR1w5pgbfzJlQ==",
       "dev": true,
       "dependencies": {
         "@jridgewell/trace-mapping": "^0.3.12",
         "@types/istanbul-lib-coverage": "^2.0.1",
-        "convert-source-map": "^1.6.0"
+        "convert-source-map": "^2.0.0"
       },
       "engines": {
         "node": ">=10.12.0"
       }
     },
+    "node_modules/v8-to-istanbul/node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true
+    },
     "node_modules/validate-npm-package-license": {
       "version": "3.0.4",
       "dev": true,
@@ -17503,10 +17746,10 @@
     },
     "query-graphs-js": {
       "name": "@apollo/query-graphs",
-      "version": "2.5.1",
+      "version": "2.5.5",
       "license": "SEE LICENSE IN ./LICENSE",
       "dependencies": {
-        "@apollo/federation-internals": "2.5.1",
+        "@apollo/federation-internals": "2.5.5",
         "deep-equal": "^2.0.5",
         "ts-graphviz": "^1.5.4",
         "uuid": "^9.0.0"
@@ -17520,11 +17763,11 @@
     },
     "query-planner-js": {
       "name": "@apollo/query-planner",
-      "version": "2.5.1",
+      "version": "2.5.5",
       "license": "SEE LICENSE IN ./LICENSE",
       "dependencies": {
-        "@apollo/federation-internals": "2.5.1",
-        "@apollo/query-graphs": "2.5.1",
+        "@apollo/federation-internals": "2.5.5",
+        "@apollo/query-graphs": "2.5.5",
         "@apollo/utils.keyvaluecache": "^2.1.0",
         "chalk": "^4.1.0",
         "deep-equal": "^2.0.5",
@@ -17553,11 +17796,11 @@
     },
     "subgraph-js": {
       "name": "@apollo/subgraph",
-      "version": "2.5.1",
+      "version": "2.5.5",
       "license": "MIT",
       "dependencies": {
         "@apollo/cache-control-types": "^1.0.2",
-        "@apollo/federation-internals": "2.5.1"
+        "@apollo/federation-internals": "2.5.5"
       },
       "engines": {
         "node": ">=14.15.0"
diff --git a/package.json b/package.json
index e38c8567e..736faad28 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,9 @@
     "test:clean": "jest --clearCache",
     "test:watch": "jest --verbose --watchAll",
     "testonly": "npm test",
-    "test:ci": "npm test -- --ci --maxWorkers=2  --reporters=default --reporters=jest-junit",
+    "test:ci": "npm run coverage -- --ci --maxWorkers=2  --reporters=default --reporters=jest-junit",
+    "coverage": "npm test -- --coverage",
+    "coverage:upload": "codecov",
     "codegen": "graphql-codegen --config codegen.yml",
     "codegen:check": "npm run codegen && git diff --exit-code",
     "lint": "eslint . --ext .ts",
@@ -25,11 +27,15 @@
     "changeset-version": "changeset version && npm i",
     "build-and-publish": "npm run compile && changeset publish",
     "spell:check": "cspell lint --no-progress --config .cspell/cspell.yml  || (echo 'Add any real words to ./cspell/cspell-dict.txt.'; exit 1)",
-    "use-repo-git-hooks": "git config core.hooksPath .git-hooks"
+    "//": "This only needs to use prettier@2 for as long as jest disallows using prettier@3",
+    "prettier:check": "node ./node_modules/prettier-2/bin-prettier.js --check ./**/__tests__/**/*.test.ts",
+    "prettier:fix": "node ./node_modules/prettier-2/bin-prettier.js --write ./**/__tests__/**/*.test.ts",
+    "//": "Optional: run this to configure git hooks and blame ignore revs",
+    "git:configure": "git config core.hooksPath .git-hooks; git config blame.ignoreRevsFile .git-blame-ignore-revs"
   },
   "engines": {
     "node": ">=14.15.0",
-    "npm": "<10"
+    "npm": "<11"
   },
   "workspaces": [
     "internals-js",
@@ -42,8 +48,8 @@
   ],
   "devDependencies": {
     "@apollo/cache-control-types": "1.0.3",
-    "@apollo/client": "3.7.17",
-    "@apollo/server": "4.9.0",
+    "@apollo/client": "3.8.4",
+    "@apollo/server": "4.9.4",
     "@apollo/utils.fetcher": "2.0.1",
     "@changesets/changelog-github": "0.4.8",
     "@changesets/cli": "2.26.2",
@@ -53,37 +59,39 @@
     "@josephg/resolvable": "1.0.1",
     "@opentelemetry/node": "0.24.0",
     "@svitejs/changesets-changelog-github-compact": "1.1.0",
-    "@types/async-retry": "1.4.5",
-    "@types/bunyan": "1.8.8",
-    "@types/deep-equal": "1.0.1",
-    "@types/jest": "29.5.3",
+    "@types/async-retry": "1.4.6",
+    "@types/bunyan": "1.8.9",
+    "@types/deep-equal": "1.0.2",
+    "@types/jest": "29.5.5",
     "@types/js-levenshtein": "1.1.1",
     "@types/loglevel": "1.5.4",
-    "@types/make-fetch-happen": "10.0.1",
+    "@types/make-fetch-happen": "10.0.2",
     "@types/nock": "10.0.3",
-    "@types/node": "14.18.54",
-    "@types/node-fetch": "2.6.4",
-    "@types/uuid": "9.0.2",
+    "@types/node": "14.18.63",
+    "@types/node-fetch": "2.6.6",
+    "@types/uuid": "9.0.4",
     "@typescript-eslint/eslint-plugin": "5.62.0",
     "bunyan": "1.8.15",
-    "cspell": "6.31.2",
-    "graphql": "16.7.1",
-    "graphql-http": "1.21.0",
+    "codecov": "3.8.3",
+    "cspell": "6.31.3",
+    "graphql": "16.8.1",
+    "graphql-http": "1.22.0",
     "graphql-tag": "2.12.6",
-    "jest": "29.6.2",
-    "jest-config": "29.6.2",
+    "jest": "29.7.0",
+    "jest-config": "29.7.0",
     "jest-cucumber": "3.0.1",
     "jest-junit": "16.0.0",
     "log4js": "6.9.1",
     "mocked-env": "1.3.5",
-    "nock": "13.3.2",
-    "node-fetch": "2.6.12",
-    "prettier": "3.0.0",
+    "nock": "13.3.3",
+    "node-fetch": "2.7.0",
+    "prettier": "3.0.3",
+    "prettier-2": "npm:prettier@2.8.8",
     "semver": "7.5.4",
     "strip-indent": "3.0.0",
     "ts-jest": "29.1.1",
     "ts-node": "10.9.1",
-    "typescript": "5.1.6",
+    "typescript": "5.2.2",
     "winston": "3.10.0",
     "winston-transport": "4.5.0"
   },
@@ -98,7 +106,7 @@
     ]
   },
   "volta": {
-    "node": "18.17.0",
-    "npm": "9.8.1"
+    "node": "18.18.0",
+    "npm": "10.1.0"
   }
 }
diff --git a/query-graphs-js/CHANGELOG.md b/query-graphs-js/CHANGELOG.md
index 6c6d94708..7d564e16d 100644
--- a/query-graphs-js/CHANGELOG.md
+++ b/query-graphs-js/CHANGELOG.md
@@ -1,5 +1,35 @@
 # CHANGELOG for `@apollo/query-graphs`
 
+## 2.5.5
+### Patch Changes
+
+- Updated dependencies []:
+  - @apollo/federation-internals@2.5.5
+
+## 2.5.4
+### Patch Changes
+
+- Updated dependencies []:
+  - @apollo/federation-internals@2.5.4
+
+## 2.5.3
+### Patch Changes
+
+
+- More aggressive ignoring of indirect paths from root types when a more direct alternative exists. This optimisation ([#2669](https://github.com/apollographql/federation/pull/2669))
+  slightly generalize an existing heuristic of the query planner, allowing it to ignore some known inefficient options
+  earlier in its process. When this optimisation can be used, this yield faster query plan computation, but by reducing
+  the number of plans to be consider, this can sometimes prevent the planner to degrade it's output when it consider
+  there is too many plans to consider, which can result in more optimal query plans too.
+- Updated dependencies [[`4b9a512b`](https://github.com/apollographql/federation/commit/4b9a512b62e02544d7854fa198942aac33b93feb), [`c6e0e76d`](https://github.com/apollographql/federation/commit/c6e0e76dbc62662c2aa6ff7f657e374047b11255), [`1add932c`](https://github.com/apollographql/federation/commit/1add932c5cd1297853fb5af9a3a6aaa71243f63a)]:
+  - @apollo/federation-internals@2.5.3
+
+## 2.5.2
+### Patch Changes
+
+- Updated dependencies [[`35179f08`](https://github.com/apollographql/federation/commit/35179f086ce973e9ae7bb455f7ea7d73cdc10f69)]:
+  - @apollo/federation-internals@2.5.2
+
 ## 2.5.1
 ### Patch Changes
 
diff --git a/query-graphs-js/package.json b/query-graphs-js/package.json
index 442165388..45aaf81bc 100644
--- a/query-graphs-js/package.json
+++ b/query-graphs-js/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@apollo/query-graphs",
-  "version": "2.5.1",
+  "version": "2.5.5",
   "description": "Apollo Federation library to work with 'query graphs'",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",
@@ -23,7 +23,7 @@
     "node": ">=14.15.0"
   },
   "dependencies": {
-    "@apollo/federation-internals": "2.5.1",
+    "@apollo/federation-internals": "2.5.5",
     "deep-equal": "^2.0.5",
     "ts-graphviz": "^1.5.4",
     "uuid": "^9.0.0"
diff --git a/query-graphs-js/src/graphPath.ts b/query-graphs-js/src/graphPath.ts
index ba8ef2ea1..6fb38ba55 100644
--- a/query-graphs-js/src/graphPath.ts
+++ b/query-graphs-js/src/graphPath.ts
@@ -419,7 +419,9 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n
     assert(conditionsResolution.satisfied, 'Should add to a path if the conditions cannot be satisfied');
     assert(!edge || edge.conditions || !conditionsResolution.pathTree, () => `Shouldn't have conditions paths (got ${conditionsResolution.pathTree}) for edge without conditions (edge: ${edge})`);
 
-    let subgraphEnteringEdge = this.subgraphEnteringEdge;
+    // We clear `subgraphEnteringEdge` as we enter a @defer: that is because `subgraphEnteringEdge` is used to eliminate some
+    // non-optimal paths, but we don't want those optimizations to bypass a defer.
+    let subgraphEnteringEdge = defer ? undefined : this.subgraphEnteringEdge;
 
     if (edge) {
       if (edge.transition.kind === 'DownCast' && this.props.edgeToTail) {
@@ -471,20 +473,16 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n
         }
       }
 
-      // `subgraphEnteringEdge` is used to be able to eliminate some options when we can detect that going to subgraph
-      // was ineffectient (see `advancePathWithNonCollectingAndTypePreservingTransitions` for details). So first,
-      // we shouldn't change it for a "key to self" due to a @defer since the assumption of `subgraphEnteringEdge` is
-      // that the source is different from the destination. But really, if we use `@defer`, we should never rely on the
-      // optimization that `subgraphEnteringEdge`, so we clear it entirely.
-      if (edge.isKeyOrRootTypeEdgeToSelf()) {
-        subgraphEnteringEdge = undefined;
-      } else if (edge.transition.kind === 'KeyResolution') {
+      // Again, we don't want to set `subgraphEnteringEdge` if we're entering a @defer (see above).
+      if (!defer && edge.changesSubgraph()) {
         subgraphEnteringEdge = {
           index: this.size,
           edge,
           cost: conditionsResolution.cost,
         };
+      }
 
+      if (edge.transition.kind === 'KeyResolution') {
         // We're adding a key edge. If the last edge to that point is an @interfaceObject fake downcast, and if our destination
         // type is not an @interfaceObject itself, then we can eliminate that last edge as it does nothing useful, but also,
         // it has conditions and we don't need/want the key we're following to depend on those conditions, since it doesn't have
@@ -560,13 +558,26 @@ export class GraphPath<TTrigger, RV extends Vertex = Vertex, TNullEdge extends n
 
   checkDirectPathFromPreviousSubgraphTo(
     typeName: string,
-    triggerToEdge: (graph: QueryGraph, vertex: Vertex, t: TTrigger) => Edge | null | undefined
+    triggerToEdge: (graph: QueryGraph, vertex: Vertex, t: TTrigger) => Edge | null | undefined,
+    prevSubgraphStartingVertex?: Vertex,
   ): Vertex | undefined {
     const enteringEdge = this.subgraphEnteringEdge;
     if (!enteringEdge) {
       return undefined;
     }
-    let prevSubgraphVertex = enteringEdge.edge.head;
+
+    // Usually, the starting subgraph in which we want to look for a direct path is the head of
+    // `subgraphEnteringEdge`, that is, where we were just before coming to the current subgraph.
+    // But for subgraph entering edges, we're not coming from a subgraph, so instead we pass the
+    // "root" vertex of the subgraph of interest in `prevSubgraphStartingVertex`. And if that
+    // is undefined (for a subgraph entering edge), then that means the subgraph does not have
+    // the root type in question (say, no mutation type), and so there can be no direct path in
+    // that subgraph.
+    if (enteringEdge.edge.transition.kind === 'SubgraphEnteringTransition' && !prevSubgraphStartingVertex) {
+      return undefined;
+    }
+
+    let prevSubgraphVertex = prevSubgraphStartingVertex ?? enteringEdge.edge.head;
     for (let i = enteringEdge.index + 1; i < this.size; i++) {
       const triggerToMatch = this.props.edgeTriggers[i];
       const prevSubgraphMatchingEdge = triggerToEdge(this.graph, prevSubgraphVertex, triggerToMatch);
@@ -1394,9 +1405,20 @@ function advancePathWithNonCollectingAndTypePreservingTransitions<TTrigger, V ex
         // Note that we ignore the case where the "entering edge" is the "current" type as we might end up in an infinite
         // loop when calling `hasValidDirectKeyEdge` in that case without additional care and it's not useful because this
         // very method already ensure we don't create unnecessary chains of keys for the "current type"
-        if (subgraphEnteringEdge && edge.transition.kind === 'KeyResolution' && subgraphEnteringEdge.edge.tail.type.name !== typeName) {
-          const prevSubgraphVertex = toAdvance.checkDirectPathFromPreviousSubgraphTo(edge.tail.type.name, triggerToEdge);
-          const backToPreviousSubgraph = subgraphEnteringEdge.edge.head.source === edge.tail.source;
+        if (subgraphEnteringEdge && subgraphEnteringEdge.edge.tail.type.name !== typeName) {
+          let prevSubgraphEnteringVertex: Vertex | undefined = undefined;
+          let backToPreviousSubgraph: boolean;
+          if (subgraphEnteringEdge.edge.transition.kind === 'SubgraphEnteringTransition') {
+            assert(toAdvance.root instanceof RootVertex, () => `${toAdvance} should be a root path if it starts with subgraph entering edge ${subgraphEnteringEdge.edge}`);
+            prevSubgraphEnteringVertex = rootVertexForSubgraph(toAdvance.graph, edge.tail.source, toAdvance.root.rootKind);
+            // If the entering edge is the root entering of subgraphs, then the "prev subgraph" is really `edge.tail.source` and
+            // so `edge` always get us back to that (but `subgraphEnteringEdge.edge.head.source` would be `FEDERATED_GRAPH_ROOT_SOURCE`,
+            // so the test we do in the `else` branch would not work here).
+            backToPreviousSubgraph = true;
+          } else {
+            backToPreviousSubgraph = subgraphEnteringEdge.edge.head.source === edge.tail.source;
+          }
+          const prevSubgraphVertex = toAdvance.checkDirectPathFromPreviousSubgraphTo(edge.tail.type.name, triggerToEdge, prevSubgraphEnteringVertex);
           const maxCost = toAdvance.subgraphEnteringEdge.cost + (backToPreviousSubgraph ? 0 : conditionResolution.cost);
           if (prevSubgraphVertex
             && (
@@ -1468,6 +1490,13 @@ function advancePathWithNonCollectingAndTypePreservingTransitions<TTrigger, V ex
   }
 }
 
+function rootVertexForSubgraph(graph: QueryGraph, subgraphName: string, rootKind: SchemaRootKind): Vertex | undefined {
+  const root = graph.root(rootKind);
+  assert(root, () => `Should not have ask for ${rootKind} as the graph does not have one`);
+  const subgraphRootEdge = graph.outEdges(root).find((e) => e.tail.source === subgraphName);
+  return subgraphRootEdge?.tail;
+}
+
 function conditionHasOverriddenFieldsInSource(schema: Schema, condition: SelectionSet): boolean {
   const externalDirective = federationMetadata(schema)!.externalDirective();
   return allFieldDefinitionsInSelectionSet(condition).some((field) => {
diff --git a/query-graphs-js/src/index.ts b/query-graphs-js/src/index.ts
index 3907233a7..e4ee41a36 100644
--- a/query-graphs-js/src/index.ts
+++ b/query-graphs-js/src/index.ts
@@ -6,3 +6,4 @@ export * from './transition';
 export * from './pathContext';
 export * from './conditionsCaching';
 export * from './conditionsValidation';
+export * from './mermaid';
diff --git a/query-graphs-js/src/mermaid.ts b/query-graphs-js/src/mermaid.ts
new file mode 100644
index 000000000..6eb5aa62d
--- /dev/null
+++ b/query-graphs-js/src/mermaid.ts
@@ -0,0 +1,116 @@
+/* Functions used to output query graphs as [mermaid graphs](https://mermaid.js.org/syntax/flowchart.html). */
+
+import { ObjectType } from "@apollo/federation-internals";
+import { Edge, FEDERATED_GRAPH_ROOT_SOURCE, QueryGraph, Vertex, isFederatedGraphRootType, simpleTraversal } from "./querygraph";
+
+export type MermaidOptions = {
+  includeRootTypeLinks?: boolean,
+}
+
+export class MermaidGraph {
+  private readonly before: string[] = [];
+  private readonly after: string[] = [];
+  private readonly subgraphs = new Map<string, string[]>();
+
+  private isBuilt = false;
+
+  constructor(
+    private readonly graph: QueryGraph,
+    private readonly options: MermaidOptions = {},
+  ) {
+    for (const name of graph.sources.keys()) {
+      if (name === this.graph.name || name === FEDERATED_GRAPH_ROOT_SOURCE) {
+        continue;
+      }
+      this.subgraphs.set(name, []);
+    }
+  }
+
+  private subgraphName(vertex: Vertex): string | undefined {
+    if (vertex.source === this.graph.name || vertex.source === FEDERATED_GRAPH_ROOT_SOURCE) {
+      return undefined;
+    }
+    return vertex.source;
+  }
+
+  private vertexName(vertex: Vertex): string {
+    if (isFederatedGraphRootType(vertex.type)) {
+      return `root-${vertex.type.name.slice(1, vertex.type.name.length-1)}`;
+    }
+    const sg = this.subgraphName(vertex);
+    const n = sg ? `${vertex.type.name}-${sg}` : `${vertex.type.name}`;
+    return vertex.provideId ? `${n}-${vertex.provideId}` : n;
+  }
+
+  addVertex(vertex: Vertex): void {
+    const sg = this.subgraphName(vertex);
+    const addTo = sg ? this.subgraphs.get(sg)! : this.before;
+    if (isFederatedGraphRootType(vertex.type)) {
+      addTo.push(`${this.vertexName(vertex)}(["root(${vertex.type.name.slice(1, vertex.type.name.length)})"])`);
+    } else {
+      addTo.push(`${this.vertexName(vertex)}["${vertex.toString()}"]`);
+    }
+  }
+
+  addEdge(edge: Edge): boolean {
+    switch (edge.transition.kind) {
+      case 'FieldCollection':
+        if (edge.transition.definition.name.startsWith('_')) {
+          return false;
+        }
+        break;
+      case 'RootTypeResolution':
+        if (!(this.options.includeRootTypeLinks ?? true)) {
+          return false;
+        }
+        break;
+      case 'SubgraphEnteringTransition':
+        const rt = edge.tail.type as ObjectType;
+        if (rt.fields().filter((f) => !f.name.startsWith('_')).length === 0) {
+          return false;
+        }
+        break;
+    }
+
+    const head = this.vertexName(edge.head);
+    const tail = this.vertexName(edge.tail);
+    const addTo = edge.head.source !== this.graph.name && edge.head.source === edge.tail.source
+      ? this.subgraphs.get(edge.head.source)!
+      : this.after;
+    const label = edge.label();
+    if (label.length === 0) {
+      addTo.push(`${head} --> ${tail}`);
+    } else {
+      addTo.push(`${head} -->|"${label}"| ${tail}`);
+    }
+    return true;
+  }
+
+  build(): void {
+    if (this.isBuilt) {
+      return;
+    }
+
+    simpleTraversal(
+      this.graph,
+      (v) => this.addVertex(v),
+      (e) => this.addEdge(e),
+    );
+
+    this.isBuilt = true;
+  }
+
+  toString(): string {
+    this.build();
+
+    const final = [ 'flowchart TD' ];
+    this.before.forEach((b) => final.push('  ' + b));
+    for (const [name, data] of this.subgraphs.entries()) {
+      final.push(`  subgraph ${name}`);
+      data.forEach((d) => final.push('    ' + d));
+      final.push('  end');
+    }
+    this.after.forEach((a) => final.push('  ' + a));
+    return final.join('\n');
+  }
+}
diff --git a/query-graphs-js/src/querygraph.ts b/query-graphs-js/src/querygraph.ts
index 7fc88525c..2a890c792 100644
--- a/query-graphs-js/src/querygraph.ts
+++ b/query-graphs-js/src/querygraph.ts
@@ -61,6 +61,13 @@ export function isFederatedGraphRootType(type: NamedType) {
  */
 export class Vertex {
   hasReachableCrossSubgraphEdges: boolean = false;
+  // @provides works by creating duplicates of the vertex/type involved in the provides and adding the provided
+  // edges only to those copy. This means that with @provides, you can have more than one vertex per-type-and-subgraph
+  // in a query graph. Which is fined, but this `provideId` allows to distinguish if a vertex was created as part of
+  // this @provides duplication or not. The value of this field has no other meaning than to be unique per-@provide,
+  // and so all the vertex copied for a given @provides application will have the same `provideId`. Overall, this
+  // mostly exists for debugging visualization.
+  provideId: number | undefined;
 
   constructor(
     /** Index used for this vertex in the query graph it is part of. */
@@ -75,7 +82,8 @@ export class Vertex {
   ) {}
 
   toString(): string {
-    return `${this.type}(${this.source})`;
+    const label = `${this.type}(${this.source})`;
+    return this.provideId ? `${label}-${this.provideId}` : label;
   }
 }
 
@@ -741,6 +749,7 @@ function federateSubgraphs(supergraph: Schema, subgraphs: QueryGraph[]): QueryGr
     );
   }
   // Now we handle @provides
+  let provideId = 0;
   for (const [i, subgraph] of subgraphs.entries()) {
     const subgraphSchema = schemas[i];
     const subgraphMetadata = federationMetadata(subgraphSchema);
@@ -756,6 +765,7 @@ function federateSubgraphs(supergraph: Schema, subgraphs: QueryGraph[]): QueryGr
           const field = e.transition.definition;
           assert(isCompositeType(type), () => `Non composite type "${type}" should not have field collection edge ${e}`);
           for (const providesApplication of field.appliedDirectivesOf(providesDirective)) {
+            ++provideId;
             const fieldType = baseType(field.type!);
             assert(isCompositeType(fieldType), () => `Invalid @provide on field "${field}" whose type "${fieldType}" is not a composite type`)
             const provided = parseFieldSetArgument({ parentType: fieldType, directive: providesApplication });
@@ -766,9 +776,9 @@ function federateSubgraphs(supergraph: Schema, subgraphs: QueryGraph[]): QueryGr
             const copiedEdge = builder.edge(head, e.index);
             // We make a copy of the `fieldType` vertex (with all the same edges), and we change this particular edge to point to the
             // new copy. From that, we can add all the provides edges to the copy.
-            const copiedTail = builder.makeCopy(tail);
+            const copiedTail = builder.makeCopy(tail, provideId);
             builder.updateEdgeTail(copiedEdge, copiedTail);
-            addProvidesEdges(subgraphSchema, builder, copiedTail, provided);
+            addProvidesEdges(subgraphSchema, builder, copiedTail, provided, provideId);
           }
         }
         return true; // Always traverse edges
@@ -832,7 +842,7 @@ function federateSubgraphs(supergraph: Schema, subgraphs: QueryGraph[]): QueryGr
   return builder.build(FEDERATED_GRAPH_ROOT_SOURCE);
 }
 
-function addProvidesEdges(schema: Schema, builder: GraphBuilder, from: Vertex, provided: SelectionSet) {
+function addProvidesEdges(schema: Schema, builder: GraphBuilder, from: Vertex, provided: SelectionSet, provideId: number) {
   const stack: [Vertex, SelectionSet][] = [[from, provided]];
   const source = from.source;
   while (stack.length > 0) {
@@ -847,7 +857,7 @@ function addProvidesEdges(schema: Schema, builder: GraphBuilder, from: Vertex, p
           // If this is a leaf field, then we don't really have anything to do. Otherwise, we need to copy
           // the tail and continue propagating the provides from there.
           if (selection.selectionSet) {
-            const copiedTail = builder.makeCopy(existingEdge.tail);
+            const copiedTail = builder.makeCopy(existingEdge.tail, provideId);
             builder.updateEdgeTail(existingEdge, copiedTail);
             stack.push([copiedTail, selection.selectionSet]);
           }
@@ -860,7 +870,7 @@ function addProvidesEdges(schema: Schema, builder: GraphBuilder, from: Vertex, p
           // If the field is a leaf, then just create the new edge and we're done. Othewise, we
           // should copy the vertex (unless we just created it), add the edge and continue.
           if (selection.selectionSet) {
-            const copiedTail = existingTail ? builder.makeCopy(existingTail) : newTail;
+            const copiedTail = existingTail ? builder.makeCopy(existingTail, provideId) : newTail;
             builder.addEdge(v, copiedTail, new FieldCollection(fieldDef, true));
             stack.push([copiedTail, selection.selectionSet]);
           } else {
@@ -875,7 +885,7 @@ function addProvidesEdges(schema: Schema, builder: GraphBuilder, from: Vertex, p
           // @provides shouldn't have validated in the first place (another way to put it is, contrary to fields, there is no way currently
           // to mark a full type as @external).
           assert(existingEdge, () => `Shouldn't have ${selection} with no corresponding edge on ${v} (edges are: [${builder.edges(v)}])`);
-          const copiedTail = builder.makeCopy(existingEdge.tail);
+          const copiedTail = builder.makeCopy(existingEdge.tail, provideId);
           builder.updateEdgeTail(existingEdge, copiedTail);
           stack.push([copiedTail, selection.selectionSet!]);
         } else {
@@ -1030,10 +1040,13 @@ class GraphBuilder {
    * Creates a new vertex that is a full copy of the provided one, including having the same out-edge, but with no incoming edges.
    *
    * @param vertex - the vertex to copy.
+   * @param provideId - if the vertex is copied for the sake of a `@provides`, an id that identify that provide and will be set on
+   *   the newly copied vertex.
    * @returns the newly created copy.
    */
-  makeCopy(vertex: Vertex): Vertex {
+  makeCopy(vertex: Vertex, provideId?: number): Vertex {
     const newVertex = this.createNewVertex(vertex.type, vertex.source, this.sources.get(vertex.source)!);
+    newVertex.provideId = provideId;
     newVertex.hasReachableCrossSubgraphEdges = vertex.hasReachableCrossSubgraphEdges;
     for (const edge of this.outEdges[vertex.index]) {
       this.addEdge(newVertex, edge.tail, edge.transition, edge.conditions);
@@ -1092,8 +1105,7 @@ class GraphBuilderFromSchema extends GraphBuilder {
     private readonly supergraph?: { apiSchema: Schema, isFed1: boolean },
   ) {
     super();
-    this.isFederatedSubgraph = isFederationSubgraphSchema(schema);
-    assert(!this.isFederatedSubgraph || supergraph, `Missing supergraph schema for building the federated subgraph graph`);
+    this.isFederatedSubgraph = !!supergraph && isFederationSubgraphSchema(schema);
   }
 
   private hasDirective(elt: FieldDefinition<any> | NamedType, directiveFct: (metadata: FederationMetadata) => DirectiveDefinition): boolean {
diff --git a/query-planner-js/CHANGELOG.md b/query-planner-js/CHANGELOG.md
index 964919e9f..0ec617fb1 100644
--- a/query-planner-js/CHANGELOG.md
+++ b/query-planner-js/CHANGELOG.md
@@ -1,5 +1,84 @@
 # CHANGELOG for `@apollo/query-planner`
 
+## 2.5.5
+### Patch Changes
+
+
+- Fix specific case for requesting __typename on interface entity type ([#2775](https://github.com/apollographql/federation/pull/2775))
+  
+  In certain cases, when resolving a __typename on an interface entity (due to it actual being requested in the operation), that fetch group could previously be trimmed / treated as useless. At a glance, it appears to be a redundant step, i.e.:
+  ```
+  { ... on Product { __typename id }} => { ... on Product { __typename} }
+  ```
+  It's actually necessary to preserve this in the case that we're coming from an interface object to an (entity) interface so that we can resolve the concrete __typename correctly.
+
+- Don't preserve useless fetches which downgrade __typename from a concrete type back to its interface type. ([#2778](https://github.com/apollographql/federation/pull/2778))
+  
+  In certain cases, the query planner was preserving some fetches which were "useless" that would rewrite __typename from its already-resolved concrete type back to its interface type. This could result in (at least) requested fields being "filtered" from the final result due to the interface's __typename in the data where the concrete type's __typename was expected.
+  
+  Specifically, the solution was compute the path between newly created groups and their parents when we know that it's trivial (`[]`). Further along in the planning process, this allows to actually remove the known-useless group.
+- Updated dependencies []:
+  - @apollo/federation-internals@2.5.5
+  - @apollo/query-graphs@2.5.5
+
+## 2.5.4
+### Patch Changes
+
+
+- Fix some potentially incorrect query plans with `@requires` when some dependencies are involved. ([#2726](https://github.com/apollographql/federation/pull/2726))
+  
+  In some rare case of `@requires`, an over-eager optimisation was incorrectly considering that
+  a dependency between 2 subgraph fetches was unnecessary, leading to doing 2 subgraphs queries
+  in parallel when those should be done sequentially (because the 2nd query rely on results
+  from the 1st one). This effectively resulted in the required fields not being provided (the
+  consequence of which depends a bit on the resolver detail, but if the resolver expected
+  the required fields to be populated (as they should), then this could typically result
+  in a message of the form `GraphQLError: Cannot read properties of null`).
+- Updated dependencies []:
+  - @apollo/federation-internals@2.5.4
+  - @apollo/query-graphs@2.5.4
+
+## 2.5.3
+### Patch Changes
+
+
+- Fix potential assertion error for named fragment on abstract types when the abstract type does not have the same ([#2725](https://github.com/apollographql/federation/pull/2725))
+  possible runtime types in all subgraphs.
+  
+  The error manifested itself during query planning with an error message of the form `Cannot normalize X at Y ...`.
+
+- More aggressive ignoring of indirect paths from root types when a more direct alternative exists. This optimisation ([#2669](https://github.com/apollographql/federation/pull/2669))
+  slightly generalize an existing heuristic of the query planner, allowing it to ignore some known inefficient options
+  earlier in its process. When this optimisation can be used, this yield faster query plan computation, but by reducing
+  the number of plans to be consider, this can sometimes prevent the planner to degrade it's output when it consider
+  there is too many plans to consider, which can result in more optimal query plans too.
+- Updated dependencies [[`4b9a512b`](https://github.com/apollographql/federation/commit/4b9a512b62e02544d7854fa198942aac33b93feb), [`c6e0e76d`](https://github.com/apollographql/federation/commit/c6e0e76dbc62662c2aa6ff7f657e374047b11255), [`1add932c`](https://github.com/apollographql/federation/commit/1add932c5cd1297853fb5af9a3a6aaa71243f63a), [`6f1fddb2`](https://github.com/apollographql/federation/commit/6f1fddb25d49262b2ebf6db953371a559dd62e9c)]:
+  - @apollo/federation-internals@2.5.3
+  - @apollo/query-graphs@2.5.3
+
+## 2.5.2
+### Patch Changes
+
+
+- Fix over-eager merging of fields with different directive applications ([#2713](https://github.com/apollographql/federation/pull/2713))
+  
+  Previously, the following query would incorrectly combine the selection set of `hello`, with both fields ending up under the `@skip` condition:
+  ```graphql
+  query Test($skipField: Boolean!) {
+    hello @skip(if: $skipField) {
+      world
+    }
+    hello {
+      goodbye
+    }
+  }
+  ```
+  
+  This change identifies those two selections on `hello` as unique while constructing our operation representation so they aren't merged at all, leaving it to the subgraph to handle the operation as-is.
+- Updated dependencies [[`35179f08`](https://github.com/apollographql/federation/commit/35179f086ce973e9ae7bb455f7ea7d73cdc10f69)]:
+  - @apollo/federation-internals@2.5.2
+  - @apollo/query-graphs@2.5.2
+
 ## 2.5.1
 ### Patch Changes
 
diff --git a/query-planner-js/package.json b/query-planner-js/package.json
index 2b7780d1f..6f33d13fd 100644
--- a/query-planner-js/package.json
+++ b/query-planner-js/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@apollo/query-planner",
-  "version": "2.5.1",
+  "version": "2.5.5",
   "description": "Apollo Query Planner",
   "author": "Apollo <packages@apollographql.com>",
   "main": "dist/index.js",
@@ -25,8 +25,8 @@
     "access": "public"
   },
   "dependencies": {
-    "@apollo/federation-internals": "2.5.1",
-    "@apollo/query-graphs": "2.5.1",
+    "@apollo/federation-internals": "2.5.5",
+    "@apollo/query-graphs": "2.5.5",
     "@apollo/utils.keyvaluecache": "^2.1.0",
     "chalk": "^4.1.0",
     "deep-equal": "^2.0.5",
diff --git a/query-planner-js/src/__tests__/allFeatures.test.ts b/query-planner-js/src/__tests__/allFeatures.test.ts
index 4cfc265b2..698a21092 100644
--- a/query-planner-js/src/__tests__/allFeatures.test.ts
+++ b/query-planner-js/src/__tests__/allFeatures.test.ts
@@ -1,9 +1,7 @@
 import fs from 'fs';
 import { defineFeature, loadFeatures } from 'jest-cucumber';
 import path from 'path';
-import {
-  QueryPlan, QueryPlanner
-} from '..';
+import { QueryPlan, QueryPlanner } from '..';
 import {
   Operation,
   Schema,
@@ -12,7 +10,6 @@ import {
 } from '@apollo/federation-internals';
 import { parse, validate } from 'graphql';
 
-
 // This test looks over all directories under tests/features and finds "supergraphSdl.graphql" in
 // each of those directories. It runs all of the .feature cases in that directory against that schema.
 // To add test cases against new schemas, create a sub directory under "features" with the new schema
@@ -42,8 +39,12 @@ for (const directory of directories) {
         const supergraphSdl = fs.readFileSync(schemaPath, 'utf8');
         const supergraph = Supergraph.build(supergraphSdl);
         schema = supergraph.schema;
-        const exposeDocumentNodeInFetchNode = feature.title.endsWith("(with ExposeDocumentNodeInFetchNode)");
-        queryPlanner = new QueryPlanner(supergraph, { exposeDocumentNodeInFetchNode });
+        const exposeDocumentNodeInFetchNode = feature.title.endsWith(
+          '(with ExposeDocumentNodeInFetchNode)',
+        );
+        queryPlanner = new QueryPlanner(supergraph, {
+          exposeDocumentNodeInFetchNode,
+        });
       });
 
       feature.scenarios.forEach((scenario) => {
@@ -54,7 +55,9 @@ for (const directory of directories) {
           const givenQuery = () => {
             given(/^query$/im, (operationString: string) => {
               operation = parseOperation(schema, operationString);
-              expect(validate(schema.toGraphQLJSSchema(), parse(operationString))).toStrictEqual([]);
+              expect(
+                validate(schema.toGraphQLJSSchema(), parse(operationString)),
+              ).toStrictEqual([]);
             });
           };
 
diff --git a/query-planner-js/src/__tests__/buildPlan.defer.test.ts b/query-planner-js/src/__tests__/buildPlan.defer.test.ts
index e55da6bcb..bca4a418e 100644
--- a/query-planner-js/src/__tests__/buildPlan.defer.test.ts
+++ b/query-planner-js/src/__tests__/buildPlan.defer.test.ts
@@ -1,10 +1,21 @@
-import { operationFromDocument, Schema, ServiceDefinition } from '@apollo/federation-internals';
+import {
+  operationFromDocument,
+  Schema,
+  ServiceDefinition,
+} from '@apollo/federation-internals';
 import gql from 'graphql-tag';
 import { QueryPlanner } from '@apollo/query-planner';
-import { composeAndCreatePlanner, composeAndCreatePlannerWithOptions } from "./testHelper";
-
-function composeAndCreatePlannerWithDefer(...services: ServiceDefinition[]): [Schema, QueryPlanner] {
-  return composeAndCreatePlannerWithOptions(services, { incrementalDelivery: { enableDefer : true }});
+import {
+  composeAndCreatePlanner,
+  composeAndCreatePlannerWithOptions,
+} from './testHelper';
+
+function composeAndCreatePlannerWithDefer(
+  ...services: ServiceDefinition[]
+): [Schema, QueryPlanner] {
+  return composeAndCreatePlannerWithOptions(services, {
+    incrementalDelivery: { enableDefer: true },
+  });
 }
 
 describe('handles simple @defer', () => {
@@ -12,14 +23,14 @@ describe('handles simple @defer', () => {
     name: 'Subgraph1',
     typeDefs: gql`
       type Query {
-        t : T
+        t: T
       }
 
       type T @key(fields: "id") {
         id: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -29,21 +40,24 @@ describe('handles simple @defer', () => {
         v1: Int
         v2: Int
       }
-    `
-  }
+    `,
+  };
 
   test('without defer-support enabled', () => {
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v1
-          ... @defer {
-            v2
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v1
+            ... @defer {
+              v2
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     // Without defer-support enabled, we should get the same plan than if `@defer` wasn't there.
     const plan = queryPlanner.buildQueryPlan(operation);
@@ -80,17 +94,23 @@ describe('handles simple @defer', () => {
   });
 
   test('with defer-support enabled', () => {
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v1
-          ... @defer {
-            v2
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v1
+            ... @defer {
+              v2
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -161,14 +181,14 @@ describe('non-router-based-defer', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          t : T
+          t: T
         }
 
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -182,22 +202,28 @@ describe('non-router-based-defer', () => {
           a: Int
           b: Int
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v {
-            a
-            ... @defer {
-              b
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v {
+              a
+              ... @defer {
+                b
+              }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // We cannot handle a @defer on value type at the query planning level, so we expect nothing to be
@@ -259,15 +285,15 @@ describe('non-router-based-defer', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          t : T
+          t: T
         }
 
         type T @key(fields: "id", resolvable: false) {
           id: ID!
           v1: String
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -276,20 +302,26 @@ describe('non-router-based-defer', () => {
           id: ID!
           v2: String
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          ... @defer {
-            v1
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            ... @defer {
+              v1
+            }
+            v2
           }
-          v2
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // While the @defer in the operation is on an entity, the @key in the first subgraph
@@ -348,7 +380,7 @@ describe('non-router-based-defer', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          t : T
+          t: T
         }
 
         type T @key(fields: "id") {
@@ -359,8 +391,8 @@ describe('non-router-based-defer', () => {
           id: ID!
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -378,24 +410,30 @@ describe('non-router-based-defer', () => {
         type U @key(fields: "id") {
           id: ID!
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v {
-            a
-            ... @defer {
-              u {
-                x
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v {
+              a
+              ... @defer {
+                u {
+                  x
+                }
               }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // While we cannot defer the initial resolving of `u`, we can defer the fetch of it's `x` field,
@@ -478,7 +516,7 @@ test('@defer resuming in same subgraph', () => {
     name: 'Subgraph1',
     typeDefs: gql`
       type Query {
-        t : T
+        t: T
       }
 
       type T @key(fields: "id") {
@@ -486,20 +524,23 @@ test('@defer resuming in same subgraph', () => {
         v0: String
         v1: String
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        v0
-        ... @defer {
-          v1
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          v0
+          ... @defer {
+            v1
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -552,7 +593,7 @@ test('@defer multiple fields in different subgraphs', () => {
     name: 'Subgraph1',
     typeDefs: gql`
       type Query {
-        t : T
+        t: T
       }
 
       type T @key(fields: "id") {
@@ -560,8 +601,8 @@ test('@defer multiple fields in different subgraphs', () => {
         v0: String
         v1: String
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -570,8 +611,8 @@ test('@defer multiple fields in different subgraphs', () => {
         id: ID!
         v2: String
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -580,22 +621,29 @@ test('@defer multiple fields in different subgraphs', () => {
         id: ID!
         v3: String
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2, subgraph3);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        v0
-        ... @defer {
-          v1
-          v2
-          v3
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          v0
+          ... @defer {
+            v1
+            v2
+            v3
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -682,7 +730,7 @@ test('multiple (non-nested) @defer + label handling', () => {
     name: 'Subgraph1',
     typeDefs: gql`
       type Query {
-        t : T
+        t: T
       }
 
       type T @key(fields: "id") {
@@ -690,8 +738,8 @@ test('multiple (non-nested) @defer + label handling', () => {
         v0: String
         v1: String
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -705,8 +753,8 @@ test('multiple (non-nested) @defer + label handling', () => {
       type U @key(fields: "id") {
         id: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -716,29 +764,36 @@ test('multiple (non-nested) @defer + label handling', () => {
         x: Int
         y: Int
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2, subgraph3);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        v0
-        ... @defer(label: "defer_v1") {
-          v1
-        }
-        ... @defer {
-          v2
-        }
-        v3 {
-          x
-          ... @defer(label: "defer_in_v3") {
-            y
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          v0
+          ... @defer(label: "defer_v1") {
+            v1
+          }
+          ... @defer {
+            v2
+          }
+          v3 {
+            x
+            ... @defer(label: "defer_in_v3") {
+              y
+            }
           }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -870,15 +925,15 @@ describe('nested @defer', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          me : User
+          me: User
         }
 
         type User @key(fields: "id") {
           id: ID!
           name: String
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -893,30 +948,36 @@ describe('nested @defer', () => {
           body: String
           author: User
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        me {
-          name
-          ... on User @defer {
-            messages {
-              body
-              author {
-                name
-                ... @defer {
-                  messages {
-                    body
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          me {
+            name
+            ... on User @defer {
+              messages {
+                body
+                author {
+                  name
+                  ... @defer {
+                    messages {
+                      body
+                    }
                   }
                 }
               }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -1028,15 +1089,15 @@ describe('nested @defer', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          me : User
+          me: User
         }
 
         type User @key(fields: "id") {
           id: ID!
           name: String
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1055,25 +1116,31 @@ describe('nested @defer', () => {
           paragraphs: [String]
           lines: Int
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        me {
-          ... @defer {
-            messages {
-              ... @defer {
-                body {
-                  lines
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          me {
+            ... @defer {
+              messages {
+                ... @defer {
+                  body {
+                    lines
+                  }
                 }
               }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -1135,15 +1202,15 @@ describe('nested @defer', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          me : User
+          me: User
         }
 
         type User @key(fields: "id") {
           id: ID!
           name: String
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1153,23 +1220,29 @@ describe('nested @defer', () => {
           age: Int
           address: String
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        me {
-          name
-          ... @defer {
-            age
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          me {
+            name
             ... @defer {
-              address
+              age
+              ... @defer {
+                address
+              }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -1247,32 +1320,35 @@ describe('nested @defer', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          me : User
+          me: User
         }
 
-        type User  {
+        type User {
           id: ID!
           name: String
           age: Int
           address: String
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      {
-        me {
-          name
-          ... @defer {
-            age
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          me {
+            name
             ... @defer {
-              address
+              age
+              ... @defer {
+                address
+              }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -1320,7 +1396,7 @@ describe('nested @defer', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          t : T
+          t: T
         }
 
         type T {
@@ -1328,8 +1404,8 @@ describe('nested @defer', () => {
           a: Int
           b: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1337,22 +1413,28 @@ describe('nested @defer', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          ... @defer {
-            a
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
             ... @defer {
-              b
+              a
+              ... @defer {
+                b
+              }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Note that nothing can effectively be deferred, so everything is fetched in the very first fetch, but
@@ -1400,7 +1482,7 @@ describe('@defer on mutation', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          t : T
+          t: T
         }
 
         type Mutation {
@@ -1413,9 +1495,8 @@ describe('@defer on mutation', () => {
           v0: String
           v1: String
         }
-      `
-
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1424,27 +1505,33 @@ describe('@defer on mutation', () => {
           id: ID!
           v2: String
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      mutation mut {
-        update1 {
-          v0
-          ... @defer {
-            v1
-          }
-        }
-        update2 {
-          v1
-          ... @defer {
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        mutation mut {
+          update1 {
             v0
-            v2
+            ... @defer {
+              v1
+            }
+          }
+          update2 {
+            v1
+            ... @defer {
+              v0
+              v2
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // What matters here is that the updates (that go to different fields) are correctly done in sequence,
@@ -1545,7 +1632,7 @@ describe('@defer on mutation', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          t : T
+          t: T
         }
 
         type Mutation {
@@ -1557,8 +1644,8 @@ describe('@defer on mutation', () => {
           v0: String
           v1: String
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1571,27 +1658,33 @@ describe('@defer on mutation', () => {
           id: ID!
           v2: String
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      mutation mut {
-        update1 {
-          v0
-          ... @defer {
-            v1
-          }
-        }
-        update2 {
-          v1
-          ... @defer {
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        mutation mut {
+          update1 {
             v0
-            v2
+            ... @defer {
+              v1
+            }
+          }
+          update2 {
+            v1
+            ... @defer {
+              v0
+              v2
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // What matters here is that the updates (that go to different fields) are correctly done in sequence,
@@ -1720,8 +1813,8 @@ test('multi-dependency deferred section', () => {
         id0: ID!
         v1: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -1731,8 +1824,8 @@ test('multi-dependency deferred section', () => {
         id1: ID!
         v2: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -1742,8 +1835,8 @@ test('multi-dependency deferred section', () => {
         id2: ID!
         v3: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph4 = {
     name: 'Subgraph4',
@@ -1753,22 +1846,30 @@ test('multi-dependency deferred section', () => {
         id2: ID!
         v4: Int
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2, subgraph3, subgraph4);
-  let operation = operationFromDocument(api, gql`
-    {
-      t {
-        v1
-        v2
-        v3
-        ... @defer {
-          v4
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+    subgraph4,
+  );
+  let operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          v1
+          v2
+          v3
+          ... @defer {
+            v4
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   let plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -1854,16 +1955,19 @@ test('multi-dependency deferred section', () => {
     }
   `);
 
-  operation = operationFromDocument(api, gql`
-    {
-      t {
-        v1
-        ... @defer {
-          v4
+  operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          v1
+          ... @defer {
+            v4
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   plan = queryPlanner.buildQueryPlan(operation);
   // TODO: the following plan is admittedly not as effecient as it could be, as the 2 queries to
@@ -1871,7 +1975,7 @@ test('multi-dependency deferred section', () => {
   // key dependencies for the deferred block, so it would make more sense to defer those fetches
   // as well. It is however tricky to both improve this here _and_ maintain the plan generate
   // just above (which is admittedly optimial). More precisely, what the code currently does is
-  // that when it gets to a defer, then it defers the fetch that gets the deferred fields (the 
+  // that when it gets to a defer, then it defers the fetch that gets the deferred fields (the
   // fetch to subgraph 4 here), but it puts the "condition" resolution for the key of that fetch
   // in the non-deferred section. Here, resolving that fetch conditions is what creates the
   // dependency on the the fetches to subgraph 2 and 3, and so those get non-deferred.
@@ -1981,8 +2085,8 @@ describe('@require', () => {
           id: ID!
           v1: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1992,8 +2096,8 @@ describe('@require', () => {
           v2: Int @requires(fields: "v3")
           v3: Int @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'Subgraph3',
@@ -2002,20 +2106,27 @@ describe('@require', () => {
           id: ID!
           v3: Int
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2, subgraph3);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v1
-          ... @defer {
-            v2
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v1
+            ... @defer {
+              v2
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -2097,7 +2208,7 @@ describe('@provides', () => {
       name: 'Subgraph1',
       typeDefs: gql`
         type Query {
-          t : T @provides(fields: "v2")
+          t: T @provides(fields: "v2")
         }
 
         type T @key(fields: "id") {
@@ -2105,8 +2216,8 @@ describe('@provides', () => {
           v1: Int
           v2: Int @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -2115,20 +2226,26 @@ describe('@provides', () => {
           id: ID!
           v2: Int @shareable
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v1
-          ... @defer {
-            v2
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v1
+            ... @defer {
+              v2
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -2182,7 +2299,7 @@ test('@defer on query root type', () => {
     name: 'Subgraph1',
     typeDefs: gql`
       type Query {
-        op1 : Int
+        op1: Int
         op2: A
       }
 
@@ -2191,8 +2308,8 @@ test('@defer on query root type', () => {
         y: Int
         next: Query
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -2201,25 +2318,31 @@ test('@defer on query root type', () => {
         op3: Int
         op4: Int
       }
-    `
-  }
+    `,
+  };
 
-  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-  const operation = operationFromDocument(api, gql`
-    {
-      op2 {
-        x
-        y
-        next {
-          op3
-          ... @defer {
-            op1
-            op4
+  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+    subgraph1,
+    subgraph2,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        op2 {
+          x
+          y
+          next {
+            op3
+            ... @defer {
+              op1
+              op4
+            }
           }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -2302,8 +2425,8 @@ test('@defer on everything queried', () => {
         id: ID!
         x: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -2312,20 +2435,26 @@ test('@defer on everything queried', () => {
         id: ID!
         y: Int
       }
-    `
-  }
+    `,
+  };
 
-  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-  const operation = operationFromDocument(api, gql`
-    {
-      ... @defer {
-        t {
-          x
-          y
+  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+    subgraph1,
+    subgraph2,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        ... @defer {
+          t {
+            x
+            y
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -2390,8 +2519,8 @@ test('@defer everything within entity', () => {
         id: ID!
         x: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -2400,20 +2529,26 @@ test('@defer everything within entity', () => {
         id: ID!
         y: Int
       }
-    `
-  }
+    `,
+  };
 
-  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        ... @defer {
-          x
-          y
+  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+    subgraph1,
+    subgraph2,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          ... @defer {
+            x
+            y
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -2475,13 +2610,16 @@ test('@defer everything within entity', () => {
 });
 
 describe('defer with conditions', () => {
-  test.each([{
-    name: 'without explicit label',
-    label: undefined,
-  }, {
-    name: 'with explicit label',
-    label: 'testLabel',
-  }])('simple @defer with condition $name', ({label}) => {
+  test.each([
+    {
+      name: 'without explicit label',
+      label: undefined,
+    },
+    {
+      name: 'with explicit label',
+      label: 'testLabel',
+    },
+  ])('simple @defer with condition $name', ({ label }) => {
     const subgraph1 = {
       name: 'Subgraph1',
       typeDefs: gql`
@@ -2493,8 +2631,8 @@ describe('defer with conditions', () => {
           id: ID!
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -2503,11 +2641,16 @@ describe('defer with conditions', () => {
           id: ID!
           y: Int
         }
-      `
-    }
+      `,
+    };
 
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
       query($cond: Boolean) {
         t {
           x
@@ -2516,7 +2659,8 @@ describe('defer with conditions', () => {
           }
         }
       }
-    `);
+    `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -2540,7 +2684,9 @@ describe('defer with conditions', () => {
                   }
                 }
               }, [
-                Deferred(depends: [0], path: "t"${label ? `, label: "${label}"` : ''}) {
+                Deferred(depends: [0], path: "t"${
+                  label ? `, label: "${label}"` : ''
+                }) {
                   {
                     y
                   }:
@@ -2612,20 +2758,23 @@ describe('defer with conditions', () => {
           x: Int
           y: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      query($cond: Boolean) {
-        t {
-          x
-          ... @defer(if: $cond) {
-            y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query ($cond: Boolean) {
+          t {
+            x
+            ... @defer(if: $cond) {
+              y
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -2704,8 +2853,8 @@ describe('defer with conditions', () => {
           id: ID!
           a: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -2714,8 +2863,8 @@ describe('defer with conditions', () => {
           id: ID!
           y: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'Subgraph3',
@@ -2724,28 +2873,35 @@ describe('defer with conditions', () => {
           id: ID!
           b: Int
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2, subgraph3);
-    const operation = operationFromDocument(api, gql`
-      query($cond1: Boolean, $cond2: Boolean) {
-        t {
-          x
-          ... @defer(if: $cond1, label: "foo") {
-            y
-          }
-          ... @defer(if: $cond2, label: "bar") {
-            u {
-              a
-              ... @defer(if: $cond1) {
-                b
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query ($cond1: Boolean, $cond2: Boolean) {
+          t {
+            x
+            ... @defer(if: $cond1, label: "foo") {
+              y
+            }
+            ... @defer(if: $cond2, label: "bar") {
+              u {
+                a
+                ... @defer(if: $cond1) {
+                  b
+                }
               }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -3080,8 +3236,8 @@ test('defer when some interface has different definitions in different subgraphs
         a: Int
         c: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -3095,21 +3251,27 @@ test('defer when some interface has different definitions in different subgraphs
         a: Int @external
         b: Int @requires(fields: "a")
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-  const operation = operationFromDocument(api, gql`
-    query Dimensions {
-      i {
-        a
-        b
-        ... @defer {
-          c
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+    subgraph1,
+    subgraph2,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      query Dimensions {
+        i {
+          a
+          b
+          ... @defer {
+            c
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const queryPlan = queryPlanner.buildQueryPlan(operation);
   expect(queryPlan).toMatchInlineSnapshot(`
@@ -3180,8 +3342,8 @@ describe('named fragments', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -3191,22 +3353,28 @@ describe('named fragments', () => {
           x: Int
           y: Int
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          ...TestFragment @defer
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            ...TestFragment @defer
+          }
         }
-      }
 
-      fragment TestFragment on T {
-        x
-        y
-      }
-    `);
+        fragment TestFragment on T {
+          x
+          y
+        }
+      `,
+    );
 
     const queryPlan = queryPlanner.buildQueryPlan(operation);
     expect(queryPlan).toMatchInlineSnapshot(`
@@ -3264,8 +3432,8 @@ describe('named fragments', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -3276,28 +3444,34 @@ describe('named fragments', () => {
           y: Int
           z: Int
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          ...Fragment1
-          ...Fragment2 @defer
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            ...Fragment1
+            ...Fragment2 @defer
+          }
         }
-      }
 
-      fragment Fragment1 on T {
-        x
-        y
-      }
+        fragment Fragment1 on T {
+          x
+          y
+        }
 
-      fragment Fragment2 on T {
-        y
-        z
-      }
-    `);
+        fragment Fragment2 on T {
+          y
+          z
+        }
+      `,
+    );
 
     // Field 'y' is queried twice, both in the deferred and non-deferred section. The spec says that
     // means the field is requested twice, so ensures that's what we do.
@@ -3384,8 +3558,8 @@ describe('named fragments', () => {
           id: ID!
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -3394,23 +3568,29 @@ describe('named fragments', () => {
           id: ID!
           y: Int
         }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          ...OnT @defer
-          x
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+      subgraph1,
+      subgraph2,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            ...OnT @defer
+            x
+          }
         }
-      }
 
-      fragment OnT on T {
-        y
-        __typename
-      }
-    `);
+        fragment OnT on T {
+          y
+          __typename
+        }
+      `,
+    );
 
     const queryPlan = queryPlanner.buildQueryPlan(operation);
     expect(queryPlan).toMatchInlineSnapshot(`
@@ -3476,8 +3656,8 @@ test('do not merge query branches with @defer', () => {
         a: Int
         b: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -3486,24 +3666,30 @@ test('do not merge query branches with @defer', () => {
         id: ID!
         c: Int
       }
-    `
-  }
+    `,
+  };
 
-  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1, subgraph2);
+  const [api, queryPlanner] = composeAndCreatePlannerWithDefer(
+    subgraph1,
+    subgraph2,
+  );
   // We have 2 separate @defer, so we should 2 deferred parts, not 1 defer parts with parallel fetches.
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        a
-        ... @defer {
-          b
-        }
-        ... @defer {
-          c
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          a
+          ... @defer {
+            b
+          }
+          ... @defer {
+            c
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const queryPlan = queryPlanner.buildQueryPlan(operation);
   expect(queryPlan).toMatchInlineSnapshot(`
@@ -3576,27 +3762,30 @@ test('@defer only the key of an entity', () => {
     name: 'Subgraph1',
     typeDefs: gql`
       type Query {
-        t : T
+        t: T
       }
 
       type T @key(fields: "id") {
         id: ID!
         v0: String
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        v0
-        ... @defer {
-          id
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          v0
+          ... @defer {
+            id
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   // Making sure that the deferred part has no fetches since we only defer the key
@@ -3635,7 +3824,7 @@ test('the path in @defer includes traversed fragments', () => {
     name: 'Subgraph1',
     typeDefs: gql`
       type Query {
-        i : I
+        i: I
       }
 
       interface I {
@@ -3652,24 +3841,27 @@ test('the path in @defer includes traversed fragments', () => {
         v1: String
         v2: String
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlannerWithDefer(subgraph1);
-  const operation = operationFromDocument(api, gql`
-    {
-      i {
-        ... on A {
-          t {
-            v1
-            ... @defer {
-              v2
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        i {
+          ... on A {
+            t {
+              v1
+              ... @defer {
+                v2
+              }
             }
           }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
diff --git a/query-planner-js/src/__tests__/buildPlan.directiveMerging.test.ts b/query-planner-js/src/__tests__/buildPlan.directiveMerging.test.ts
new file mode 100644
index 000000000..11509a9c0
--- /dev/null
+++ b/query-planner-js/src/__tests__/buildPlan.directiveMerging.test.ts
@@ -0,0 +1,192 @@
+import { operationFromDocument } from '@apollo/federation-internals';
+import gql from 'graphql-tag';
+import { composeAndCreatePlanner } from './testHelper';
+
+describe('merging @skip / @include directives', () => {
+  const subgraph1 = {
+    name: 'S1',
+    typeDefs: gql`
+      type Query {
+        hello: Hello!
+        extraFieldToPreventSkipIncludeNodes: String!
+      }
+
+      type Hello {
+        world: String!
+        goodbye: String!
+      }
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
+
+  it('with fragment', () => {
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query Test($skipField: Boolean!) {
+          ...ConditionalSkipFragment
+          hello {
+            world
+          }
+          extraFieldToPreventSkipIncludeNodes
+        }
+
+        fragment ConditionalSkipFragment on Query {
+          hello @skip(if: $skipField) {
+            goodbye
+          }
+        }
+      `,
+    );
+
+    const plan = queryPlanner.buildQueryPlan(operation);
+    expect(plan).toMatchInlineSnapshot(`
+      QueryPlan {
+        Fetch(service: "S1") {
+          {
+            hello @skip(if: $skipField) {
+              goodbye
+            }
+            hello {
+              world
+            }
+            extraFieldToPreventSkipIncludeNodes
+          }
+        },
+      }
+    `);
+  });
+
+  it('without fragment', () => {
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query Test($skipField: Boolean!) {
+          hello @skip(if: $skipField) {
+            world
+          }
+          hello {
+            goodbye
+          }
+          extraFieldToPreventSkipIncludeNodes
+        }
+      `,
+    );
+
+    const plan = queryPlanner.buildQueryPlan(operation);
+    expect(plan).toMatchInlineSnapshot(`
+      QueryPlan {
+        Fetch(service: "S1") {
+          {
+            hello @skip(if: $skipField) {
+              world
+            }
+            hello {
+              goodbye
+            }
+            extraFieldToPreventSkipIncludeNodes
+          }
+        },
+      }
+    `);
+  });
+
+  it('multiple applications identical', () => {
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query Test($skipField: Boolean!, $includeField: Boolean!) {
+          hello @skip(if: $skipField) @include(if: $includeField) {
+            world
+          }
+          hello @skip(if: $skipField) @include(if: $includeField) {
+            goodbye
+          }
+          extraFieldToPreventSkipIncludeNodes
+        }
+      `,
+    );
+
+    const plan = queryPlanner.buildQueryPlan(operation);
+    expect(plan).toMatchInlineSnapshot(`
+      QueryPlan {
+        Fetch(service: "S1") {
+          {
+            hello @skip(if: $skipField) @include(if: $includeField) {
+              world
+              goodbye
+            }
+            extraFieldToPreventSkipIncludeNodes
+          }
+        },
+      }
+    `);
+  });
+
+  it('multiple applications differing order', () => {
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query Test($skipField: Boolean!, $includeField: Boolean!) {
+          hello @skip(if: $skipField) @include(if: $includeField) {
+            world
+          }
+          hello @include(if: $includeField) @skip(if: $skipField) {
+            goodbye
+          }
+          extraFieldToPreventSkipIncludeNodes
+        }
+      `,
+    );
+
+    const plan = queryPlanner.buildQueryPlan(operation);
+    expect(plan).toMatchInlineSnapshot(`
+      QueryPlan {
+        Fetch(service: "S1") {
+          {
+            hello @include(if: $includeField) @skip(if: $skipField) {
+              world
+              goodbye
+            }
+            extraFieldToPreventSkipIncludeNodes
+          }
+        },
+      }
+    `);
+  });
+
+  it('multiple applications differing quantity', () => {
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query Test($skipField: Boolean!, $includeField: Boolean!) {
+          hello @skip(if: $skipField) @include(if: $includeField) {
+            world
+          }
+          hello @include(if: $includeField) {
+            goodbye
+          }
+          extraFieldToPreventSkipIncludeNodes
+        }
+      `,
+    );
+
+    const plan = queryPlanner.buildQueryPlan(operation);
+    expect(plan).toMatchInlineSnapshot(`
+      QueryPlan {
+        Fetch(service: "S1") {
+          {
+            hello @skip(if: $skipField) @include(if: $includeField) {
+              world
+            }
+            hello @include(if: $includeField) {
+              goodbye
+            }
+            extraFieldToPreventSkipIncludeNodes
+          }
+        },
+      }
+    `);
+  });
+});
diff --git a/query-planner-js/src/__tests__/buildPlan.interfaceObject.test.ts b/query-planner-js/src/__tests__/buildPlan.interfaceObject.test.ts
index 27335501e..34847a1b5 100644
--- a/query-planner-js/src/__tests__/buildPlan.interfaceObject.test.ts
+++ b/query-planner-js/src/__tests__/buildPlan.interfaceObject.test.ts
@@ -1,7 +1,7 @@
 import { assert, operationFromDocument } from '@apollo/federation-internals';
 import gql from 'graphql-tag';
 import { isPlanNode } from '../QueryPlan';
-import { composeAndCreatePlanner, findFetchNodes } from "./testHelper";
+import { composeAndCreatePlanner, findFetchNodes } from './testHelper';
 
 describe('basic @key on interface/@interfaceObject handling', () => {
   const subgraph1 = {
@@ -27,8 +27,8 @@ describe('basic @key on interface/@interfaceObject handling', () => {
         x: Int
         w: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'S2',
@@ -41,21 +41,24 @@ describe('basic @key on interface/@interfaceObject handling', () => {
         id: ID!
         y: Int
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
 
   test('can use a @key on an @interfaceObject type', () => {
     // Start by ensuring we can use the key on an @interfaceObject type
-    const operation = operationFromDocument(api, gql`
-      {
-        iFromS1 {
-          x
-          y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          iFromS1 {
+            x
+            y
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -88,17 +91,20 @@ describe('basic @key on interface/@interfaceObject handling', () => {
         },
       }
     `);
-    });
+  });
 
   test('can use a @key on an interface "from" an @interfaceObject type', () => {
-    const operation = operationFromDocument(api, gql`
-      {
-        iFromS2 {
-          x
-          y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          iFromS2 {
+            x
+            y
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -135,13 +141,16 @@ describe('basic @key on interface/@interfaceObject handling', () => {
   });
 
   test('only uses an @interfaceObject if it can', () => {
-    const operation = operationFromDocument(api, gql`
-      {
-        iFromS2 {
-          y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          iFromS2 {
+            y
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -158,14 +167,17 @@ describe('basic @key on interface/@interfaceObject handling', () => {
   });
 
   test('does not rely on an @interfaceObject directly for `__typename`', () => {
-    const operation = operationFromDocument(api, gql`
-      {
-        iFromS2 {
-          __typename
-          y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          iFromS2 {
+            __typename
+            y
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -205,15 +217,18 @@ describe('basic @key on interface/@interfaceObject handling', () => {
     // fact that we "filter" a single implementation should act as if `__typename` was queried
     // (effectively, the gateway/router need that `__typename` to decide if the returned data
     // should be included or not.
-    const operation = operationFromDocument(api, gql`
-      {
-        iFromS2 {
-          ... on A {
-            y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          iFromS2 {
+            ... on A {
+              y
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -263,15 +278,18 @@ describe('basic @key on interface/@interfaceObject handling', () => {
   });
 
   test('can use a @key on an @interfaceObject type even for a concrete implementation', () => {
-    const operation = operationFromDocument(api, gql`
-      {
-        iFromS1 {
-          ... on A {
-            y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          iFromS1 {
+            ... on A {
+              y
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -319,15 +337,18 @@ describe('basic @key on interface/@interfaceObject handling', () => {
 
   test('handles query of an interface field (that is not on the `@interfaceObject`) for a specific implementation when query starts on the @interfaceObject', () => {
     // Here, we start on S2, but `x` is only in S1. Further, while `x` is on the `I` interface, we only query it for `A`.
-    const operation = operationFromDocument(api, gql`
-      {
-        iFromS2 {
-          ... on A {
-            x
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          iFromS2 {
+            ... on A {
+              x
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -377,8 +398,8 @@ it('avoids buffering @interfaceObject results that may have to filtered with lis
         id: ID!
         expansiveField: String
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'S2',
@@ -396,21 +417,24 @@ it('avoids buffering @interfaceObject results that may have to filtered with lis
         id: ID!
         b: Int
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
 
-  const operation = operationFromDocument(api, gql`
-    {
-      everything {
-        ... on A {
-          a
-          expansiveField
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        everything {
+          ... on A {
+            a
+            expansiveField
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -470,8 +494,8 @@ it('handles @requires on concrete type of field provided by interface object', (
         id: ID!
         x: Int @shareable
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'S2',
@@ -495,20 +519,23 @@ it('handles @requires on concrete type of field provided by interface object', (
         id: ID!
         x: Int @shareable
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
 
-  const operation = operationFromDocument(api, gql`
-    {
-      i {
-        ... on A {
-          y
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        i {
+          ... on A {
+            y
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -573,8 +600,8 @@ it('handles @interfaceObject in nested entity', () => {
       type T {
         relatedIs: [I]
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'S2',
@@ -597,22 +624,25 @@ it('handles @interfaceObject in nested entity', () => {
         id: ID!
         a: Int
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
 
-  const operation = operationFromDocument(api, gql`
-    {
-      i {
-        t {
-          relatedIs {
-            a
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        i {
+          t {
+            relatedIs {
+              a
+            }
           }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
diff --git a/query-planner-js/src/__tests__/buildPlan.subscription.test.ts b/query-planner-js/src/__tests__/buildPlan.subscription.test.ts
index 3f0e6c12a..33e103bd4 100644
--- a/query-planner-js/src/__tests__/buildPlan.subscription.test.ts
+++ b/query-planner-js/src/__tests__/buildPlan.subscription.test.ts
@@ -1,6 +1,9 @@
 import { operationFromDocument } from '@apollo/federation-internals';
 import gql from 'graphql-tag';
-import { composeAndCreatePlanner, composeAndCreatePlannerWithOptions } from './testHelper';
+import {
+  composeAndCreatePlanner,
+  composeAndCreatePlannerWithOptions,
+} from './testHelper';
 
 describe('subscription query plan tests', () => {
   it('basic subscription query plan', () => {
@@ -187,7 +190,10 @@ describe('subscription query plan tests', () => {
       `,
     };
 
-    const [api, queryPlanner] = composeAndCreatePlannerWithOptions([subgraphA, subgraphB], { incrementalDelivery: { enableDefer: true } });
+    const [api, queryPlanner] = composeAndCreatePlannerWithOptions(
+      [subgraphA, subgraphB],
+      { incrementalDelivery: { enableDefer: true } },
+    );
     const operation = operationFromDocument(
       api,
       gql`
@@ -202,6 +208,8 @@ describe('subscription query plan tests', () => {
         }
       `,
     );
-    expect(() => queryPlanner.buildQueryPlan(operation)).toThrow('@defer is not supported on subscriptions');
+    expect(() => queryPlanner.buildQueryPlan(operation)).toThrow(
+      '@defer is not supported on subscriptions',
+    );
   });
 });
diff --git a/query-planner-js/src/__tests__/buildPlan.test.ts b/query-planner-js/src/__tests__/buildPlan.test.ts
index 681de1fe9..d3cba514b 100644
--- a/query-planner-js/src/__tests__/buildPlan.test.ts
+++ b/query-planner-js/src/__tests__/buildPlan.test.ts
@@ -1,9 +1,17 @@
 import { QueryPlanner } from '@apollo/query-planner';
-import { assert, operationFromDocument, ServiceDefinition, Supergraph } from '@apollo/federation-internals';
+import {
+  assert,
+  operationFromDocument,
+  ServiceDefinition,
+  Supergraph,
+} from '@apollo/federation-internals';
 import gql from 'graphql-tag';
 import { FetchNode, FlattenNode, SequenceNode } from '../QueryPlan';
 import { FieldNode, OperationDefinitionNode, parse } from 'graphql';
-import { composeAndCreatePlanner, composeAndCreatePlannerWithOptions } from './testHelper';
+import {
+  composeAndCreatePlanner,
+  composeAndCreatePlannerWithOptions,
+} from './testHelper';
 import { enforceQueryPlannerConfigDefaults } from '../config';
 
 describe('shareable root fields', () => {
@@ -19,8 +27,8 @@ describe('shareable root fields', () => {
           id: ID!
           prop1: String
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -33,18 +41,21 @@ describe('shareable root fields', () => {
           id: ID!
           prop2: String
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        me {
-          prop1
-          prop2
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          me {
+            prop1
+            prop2
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Note that even though we have keys, it is faster to query both
@@ -82,8 +93,8 @@ describe('shareable root fields', () => {
           id: ID!
           ${fields.map((f) => `${f}: Int\n`)}
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -95,8 +106,8 @@ describe('shareable root fields', () => {
         type User @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'Subgraph3',
@@ -108,17 +119,24 @@ describe('shareable root fields', () => {
         type User @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
-    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
-    const operation = operationFromDocument(api, gql`
+    const [api, queryPlanner] = composeAndCreatePlanner(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
       {
         me {
           ${fields.map((f) => `${f}\n`)}
         }
       }
-    `);
+    `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -172,8 +190,8 @@ test('pick keys that minimize fetches', () => {
       type Country @key(fields: "iso") {
         iso: String!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -193,26 +211,29 @@ test('pick keys that minimize fetches', () => {
         name: String!
         sign: String!
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-  const operation = operationFromDocument(api, gql`
-    {
-      transfers {
-        from {
-          currency {
-            name
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        transfers {
+          from {
+            currency {
+              name
+            }
           }
-        }
-        to {
-          currency {
-            sign
+          to {
+            currency {
+              sign
+            }
           }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   // We want to make sure we use the key on Transfer just once, not 2 fetches using the keys
@@ -274,7 +295,10 @@ describe('@provides', () => {
       typeDefs: gql`
         type Query {
           doSomething: Response
-          doSomethingWithProvides: Response @provides(fields: "responseValue { subResponseValue { subSubResponseValue } }")
+          doSomethingWithProvides: Response
+            @provides(
+              fields: "responseValue { subResponseValue { subSubResponseValue } }"
+            )
         }
 
         type Response {
@@ -289,8 +313,8 @@ describe('@provides', () => {
           id: ID!
           subSubResponseValue: Int @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -299,21 +323,24 @@ describe('@provides', () => {
           id: ID!
           subSubResponseValue: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    let operation = operationFromDocument(api, gql`
-      {
-        doSomething {
-          responseValue {
-            subResponseValue {
-              subSubResponseValue
+    let operation = operationFromDocument(
+      api,
+      gql`
+        {
+          doSomething {
+            responseValue {
+              subResponseValue {
+                subSubResponseValue
+              }
             }
           }
         }
-      }
-      `);
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     // This is our sanity check: we first query _without_ the provides to make sure we _do_ need to
@@ -353,17 +380,20 @@ describe('@provides', () => {
       `);
 
     // And now make sure with the provides we do only get a fetch to subgraph1
-    operation = operationFromDocument(api, gql`
-      {
-        doSomethingWithProvides {
-          responseValue {
-            subResponseValue {
-              subSubResponseValue
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          doSomethingWithProvides {
+            responseValue {
+              subResponseValue {
+                subSubResponseValue
+              }
             }
           }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -409,8 +439,8 @@ describe('@provides', () => {
           id: ID!
           v: Value @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -429,19 +459,22 @@ describe('@provides', () => {
           id: ID!
           v: Value @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    let operation = operationFromDocument(api, gql`
-      {
-        noProvides {
-          v {
-            a
+    let operation = operationFromDocument(
+      api,
+      gql`
+        {
+          noProvides {
+            v {
+              a
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     // This is our sanity check: we first query _without_ the provides to make sure we _do_ need to
@@ -495,15 +528,18 @@ describe('@provides', () => {
       `);
 
     // Ensuring that querying only `a` can be done with subgraph1 only.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvides {
-          v {
-            a
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvides {
+            v {
+              a
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -522,16 +558,19 @@ describe('@provides', () => {
       `);
 
     // Sanity check that if we query `b` however we have to got to subgraph2.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvides {
-          v {
-            a
-            b
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvides {
+            v {
+              a
+              b
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -593,7 +632,8 @@ describe('@provides', () => {
         type Query {
           noProvides: U
           withProvidesForT1: U @provides(fields: "... on T1 { a }")
-          withProvidesForBoth: U @provides(fields: "... on T1 { a } ... on T2 {b}")
+          withProvidesForBoth: U
+            @provides(fields: "... on T1 { a } ... on T2 {b}")
         }
 
         union U = T1 | T2
@@ -608,8 +648,8 @@ describe('@provides', () => {
           a: Int
           b: Int @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -623,23 +663,26 @@ describe('@provides', () => {
           id: ID!
           b: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    let operation = operationFromDocument(api, gql`
-      {
-        noProvides {
-          ... on T1 {
-            a
-          }
-          ... on T2 {
-            a
-            b
+    let operation = operationFromDocument(
+      api,
+      gql`
+        {
+          noProvides {
+            ... on T1 {
+              a
+            }
+            ... on T2 {
+              a
+              b
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     // This is our sanity check: we first query _without_ the provides to make sure we _do_ need to
@@ -690,18 +733,21 @@ describe('@provides', () => {
       `);
 
     // Ensuring that querying only `a` can be done with subgraph1 only when provided.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvidesForT1 {
-          ... on T1 {
-            a
-          }
-          ... on T2 {
-            a
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvidesForT1 {
+            ... on T1 {
+              a
+            }
+            ... on T2 {
+              a
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -723,19 +769,22 @@ describe('@provides', () => {
       `);
 
     // But ensure that querying `b` still goes to subgraph2 if only a is provided.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvidesForT1 {
-          ... on T1 {
-            a
-          }
-          ... on T2 {
-            a
-            b
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvidesForT1 {
+            ... on T1 {
+              a
+            }
+            ... on T2 {
+              a
+              b
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -776,19 +825,22 @@ describe('@provides', () => {
       `);
 
     // Lastly, if both are provided, ensures we only hit subgraph1.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvidesForBoth {
-          ... on T1 {
-            a
-          }
-          ... on T2 {
-            a
-            b
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvidesForBoth {
+            ... on T1 {
+              a
+            }
+            ... on T2 {
+              a
+              b
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -837,8 +889,8 @@ describe('@provides', () => {
           a: Int @external
           b: Int @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -853,18 +905,21 @@ describe('@provides', () => {
           a: Int @shareable
           b: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    let operation = operationFromDocument(api, gql`
-      {
-        noProvides {
-          a
-          b
+    let operation = operationFromDocument(
+      api,
+      gql`
+        {
+          noProvides {
+            a
+            b
+          }
         }
-      }
-      `);
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     // This is our sanity check: we first query _without_ the provides to make sure we _do_ need to
@@ -916,13 +971,16 @@ describe('@provides', () => {
       `);
 
     // Ensuring that querying only `a` can be done with subgraph1 only.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvidesOnA {
-          a
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvidesOnA {
+            a
+          }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -944,13 +1002,16 @@ describe('@provides', () => {
       `);
 
     // Ensuring that for `b`, only the T2 value is provided by subgraph1.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvidesOnB {
-          b
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvidesOnB {
+            b
+          }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -990,15 +1051,18 @@ describe('@provides', () => {
       `);
 
     // But if we only query for T2, then no reason to go to subgraph2.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvidesOnB {
-          ... on T2 {
-            b
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvidesOnB {
+            ... on T2 {
+              b
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -1045,8 +1109,8 @@ describe('@provides', () => {
           id: ID!
           a: Int @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1071,25 +1135,28 @@ describe('@provides', () => {
           a: Int @shareable
           c: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    let operation = operationFromDocument(api, gql`
-      {
-        noProvides {
-          i {
-            a
-            ... on T1 {
-              b
-            }
-            ... on T2 {
-              c
+    let operation = operationFromDocument(
+      api,
+      gql`
+        {
+          noProvides {
+            i {
+              a
+              ... on T1 {
+                b
+              }
+              ... on T2 {
+                c
+              }
             }
           }
         }
-      }
-      `);
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     // This is our sanity check: we first query _without_ the provides to make sure we _do_ need to
@@ -1134,21 +1201,24 @@ describe('@provides', () => {
     `);
 
     // But the same operation with the provides allow to get what is provided from the first subgraph.
-    operation = operationFromDocument(api, gql`
-      {
-        withProvides {
-          i {
-            a
-            ... on T1 {
-              b
-            }
-            ... on T2 {
-              c
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          withProvides {
+            i {
+              a
+              ... on T1 {
+                b
+              }
+              ... on T2 {
+                c
+              }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -1224,8 +1294,8 @@ describe('@requires', () => {
           f: Int
           g: Int @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1241,17 +1311,20 @@ describe('@requires', () => {
           f: Int @external
           g: Int @requires(fields: "f")
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        is {
-          g
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          is {
+            g
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // The main goal of this test is to show that the 2 @requires for `f` gets handled seemlessly
@@ -1327,8 +1400,8 @@ describe('@requires', () => {
           id: ID!
           value: String
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -1345,21 +1418,24 @@ describe('@requires', () => {
           value: String @external
           computed: String @requires(fields: "value")
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        list {
-          computed
-          computed2
-          user {
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          list {
             computed
+            computed2
+            user {
+              computed
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // The main goal of this test is to show that the 2 @requires for `f` gets handled seemlessly
@@ -1421,7 +1497,7 @@ describe('@requires', () => {
         },
       }
     `);
-  })
+  });
 
   it('handles simple require chain (require that depends on another require)', () => {
     const subgraph1 = {
@@ -1435,19 +1511,19 @@ describe('@requires', () => {
           id: ID!
           v: Int!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
       typeDefs: gql`
         type T @key(fields: "id") {
-            id: ID!
-            v: Int! @external
-            inner: Int! @requires(fields: "v")
+          id: ID!
+          v: Int! @external
+          inner: Int! @requires(fields: "v")
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'Subgraph3',
@@ -1457,18 +1533,25 @@ describe('@requires', () => {
           inner: Int! @external
           outer: Int! @requires(fields: "inner")
         }
-      `
-    }
+      `,
+    };
 
-    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
+    const [api, queryPlanner] = composeAndCreatePlanner(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+    );
     // Ensures that if we only ask `outer`, we get everything needed in between.
-    let operation = operationFromDocument(api, gql`
-      {
-        t {
-          outer
+    let operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            outer
+          }
         }
-      }
-    `);
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -1523,15 +1606,18 @@ describe('@requires', () => {
     // (note: technically it happens to switch the order of fields in the inputs of "Subgraph2"
     // so the plans are not 100% the same "string", which is why we inline it in both cases,
     // but that's still the same plan and a perfectly valid output).
-    operation = operationFromDocument(api, gql`
-      {
-        t {
-          v
-          inner
-          outer
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v
+            inner
+            outer
+          }
         }
-      }
-    `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -1596,19 +1682,19 @@ describe('@requires', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
       typeDefs: gql`
         type T @key(fields: "id") {
-            id: ID!
-            v: Int! @external
-            inner: Int! @requires(fields: "v")
+          id: ID!
+          v: Int! @external
+          inner: Int! @requires(fields: "v")
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'Subgraph3',
@@ -1618,8 +1704,8 @@ describe('@requires', () => {
           inner: Int! @external
           outer: Int! @requires(fields: "inner")
         }
-      `
-    }
+      `,
+    };
 
     const subgraph4 = {
       name: 'Subgraph4',
@@ -1628,18 +1714,26 @@ describe('@requires', () => {
           id: ID!
           v: Int!
         }
-      `
-    }
+      `,
+    };
 
-    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3, subgraph4);
+    const [api, queryPlanner] = composeAndCreatePlanner(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+      subgraph4,
+    );
     // Ensures that if we only ask `outer`, we get everything needed in between.
-    let operation = operationFromDocument(api, gql`
-      {
-        t {
-          outer
-        }
-      }
-    `);
+    let operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            outer
+          }
+        }
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     const expectedPlan = `
@@ -1706,15 +1800,18 @@ describe('@requires', () => {
     expect(plan).toMatchInlineSnapshot(expectedPlan);
 
     // Ensures that manually asking for the required dependencies doesn't change anything.
-    operation = operationFromDocument(api, gql`
-      {
-        t {
-          v
-          inner
-          outer
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v
+            inner
+            outer
+          }
         }
-      }
-    `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(expectedPlan);
@@ -1732,8 +1829,8 @@ describe('@requires', () => {
           id: ID!
           v1: Int!
         }
-      `
-    }
+      `,
+    };
 
     const totalRequires = 10;
     const subgraphs: ServiceDefinition[] = [subgraph1];
@@ -1743,22 +1840,25 @@ describe('@requires', () => {
         typeDefs: gql`
           type T @key(fields: "id") {
               id: ID!
-              v${i-1}: Int! @external
-              v${i}: Int! @requires(fields: "v${i-1}")
+              v${i - 1}: Int! @external
+              v${i}: Int! @requires(fields: "v${i - 1}")
           }
-        `
+        `,
       });
     }
 
     const [api, queryPlanner] = composeAndCreatePlanner(...subgraphs);
     // Ensures that if we only ask `outer`, we get everything needed in between.
-    const operation = operationFromDocument(api, gql`
+    const operation = operationFromDocument(
+      api,
+      gql`
       {
         t {
           v${totalRequires}
         }
       }
-    `);
+    `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     const dependentFetches: string[] = [];
@@ -1768,7 +1868,7 @@ describe('@requires', () => {
               {
                 ... on T {
                   __typename
-                  v${i-1}
+                  v${i - 1}
                   id
                 }
               } =>
@@ -1778,8 +1878,7 @@ describe('@requires', () => {
                 }
               }
             },
-          },`
-      );
+          },`);
     }
     const expectedPlan = `
       QueryPlan {
@@ -1813,37 +1912,37 @@ describe('@requires', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
       typeDefs: gql`
         type T @key(fields: "id") {
-            id: ID!
-            inner1: Int!
-            inner2_required: Int!
+          id: ID!
+          inner1: Int!
+          inner2_required: Int!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'Subgraph3',
       typeDefs: gql`
         type T @key(fields: "id") {
-            id: ID!
-            inner2_required: Int! @external
-            inner2: Int! @requires(fields: "inner2_required")
+          id: ID!
+          inner2_required: Int! @external
+          inner2: Int! @requires(fields: "inner2_required")
         }
-      `
-    }
+      `,
+    };
 
     const subgraph4 = {
       name: 'Subgraph4',
       typeDefs: gql`
         type T @key(fields: "id") {
-            id: ID!
-            inner3: Inner3Type!
+          id: ID!
+          inner3: Inner3Type!
         }
 
         type Inner3Type @key(fields: "k3") {
@@ -1854,8 +1953,8 @@ describe('@requires', () => {
           k4: ID!
           inner4_required: Int!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph5 = {
       name: 'Subgraph5',
@@ -1867,54 +1966,68 @@ describe('@requires', () => {
           inner3: Inner3Type! @external
           inner4: Inner4Type! @external
           inner5: Int! @external
-          outer: Int! @requires(fields: "inner1 inner2 inner3 { inner3_nested } inner4 { inner4_nested } inner5")
+          outer: Int!
+            @requires(
+              fields: "inner1 inner2 inner3 { inner3_nested } inner4 { inner4_nested } inner5"
+            )
         }
 
-      type Inner3Type @key(fields: "k3") {
-        k3: ID!
-        inner3_nested: Int!
-      }
+        type Inner3Type @key(fields: "k3") {
+          k3: ID!
+          inner3_nested: Int!
+        }
 
-      type Inner4Type @key(fields: "k4") {
-        k4: ID!
-        inner4_nested: Int! @requires(fields: "inner4_required")
-        inner4_required: Int! @external
-      }
-      `
-    }
+        type Inner4Type @key(fields: "k4") {
+          k4: ID!
+          inner4_nested: Int! @requires(fields: "inner4_required")
+          inner4_required: Int! @external
+        }
+      `,
+    };
 
     const subgraph6 = {
       name: 'Subgraph6',
       typeDefs: gql`
         type T @key(fields: "id") {
-            id: ID!
-            inner4: Inner4Type!
+          id: ID!
+          inner4: Inner4Type!
         }
 
         type Inner4Type @key(fields: "k4") {
           k4: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph7 = {
       name: 'Subgraph7',
       typeDefs: gql`
         type T @key(fields: "id") {
-            id: ID!
-            inner5: Int!
-        }
-      `
-    }
-
-    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3, subgraph4, subgraph5, subgraph6, subgraph7);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          outer
+          id: ID!
+          inner5: Int!
+        }
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlanner(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+      subgraph4,
+      subgraph5,
+      subgraph6,
+      subgraph7,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            outer
+          }
         }
-      }
-    `);
+      `,
+    );
 
     // This is a big plan, but afaict, this is optimal. That is, there is 3 main steps:
     // 1. it get the `id` for `T`, which is needed for anything else.
@@ -2122,8 +2235,8 @@ describe('@requires', () => {
           v1: String
           v2: String
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -2139,26 +2252,32 @@ describe('@requires', () => {
           v1: String
           v2: String
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const op1 = operationFromDocument(api, gql`
-      {
-        entity {
-          f2
-          f3
+    const op1 = operationFromDocument(
+      api,
+      gql`
+        {
+          entity {
+            f2
+            f3
+          }
         }
-      }
-    `);
+      `,
+    );
 
-    const op2 = operationFromDocument(api, gql`
-      {
-        entity {
-          f3
+    const op2 = operationFromDocument(
+      api,
+      gql`
+        {
+          entity {
+            f3
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan1 = queryPlanner.buildQueryPlan(op2);
     const expectedPlan = `
@@ -2253,14 +2372,14 @@ describe('@requires', () => {
       name: 'A',
       typeDefs: gql`
         type Query {
-          t : T
+          t: T
         }
 
         type T @key(fields: "id1") {
           id1: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'B',
@@ -2271,8 +2390,8 @@ describe('@requires', () => {
           v1: Int
           v2: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'C',
@@ -2281,8 +2400,8 @@ describe('@requires', () => {
           id1: ID!
           v3: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph4 = {
       name: 'D',
@@ -2292,20 +2411,28 @@ describe('@requires', () => {
           v3: Int @external
           v4: Int @requires(fields: "v3")
         }
-      `
-    }
+      `,
+    };
 
-    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3, subgraph4);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v1
-          v2
-          v3
-          v4
+    const [api, queryPlanner] = composeAndCreatePlanner(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+      subgraph4,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v1
+            v2
+            v3
+            v4
+          }
         }
-      }
-    `);
+      `,
+    );
 
     // The optimal plan should:
     // 1. fetch id1 from A
@@ -2395,8 +2522,8 @@ describe('@requires', () => {
             id: ID!
             a: Int
           }
-        `
-      }
+        `,
+      };
 
       const subgraph2 = {
         name: 'Subgraph2',
@@ -2406,17 +2533,20 @@ describe('@requires', () => {
             a: Int @external
             b: Int @requires(fields: "a")
           }
-        `
-      }
+        `,
+      };
 
       const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-      const operation = operationFromDocument(api, gql`
-        query foo($test: Boolean!){
-          t @include(if: $test) {
-            b
+      const operation = operationFromDocument(
+        api,
+        gql`
+          query foo($test: Boolean!) {
+            t @include(if: $test) {
+              b
+            }
           }
-        }
-      `);
+        `,
+      );
 
       const plan = queryPlanner.buildQueryPlan(operation);
       expect(plan).toMatchInlineSnapshot(`
@@ -2466,8 +2596,8 @@ describe('@requires', () => {
             id: ID!
             a: Int
           }
-        `
-      }
+        `,
+      };
 
       const subgraph2 = {
         name: 'Subgraph2',
@@ -2477,17 +2607,20 @@ describe('@requires', () => {
             a: Int @external
             b: Int @requires(fields: "a")
           }
-        `
-      }
+        `,
+      };
 
       const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-      const operation = operationFromDocument(api, gql`
-        query foo($test: Boolean!){
-          t {
-            b @include(if: $test)
+      const operation = operationFromDocument(
+        api,
+        gql`
+          query foo($test: Boolean!) {
+            t {
+              b @include(if: $test)
+            }
           }
-        }
-      `);
+        `,
+      );
 
       const plan = queryPlanner.buildQueryPlan(operation);
       expect(plan).toMatchInlineSnapshot(`
@@ -2538,8 +2671,8 @@ describe('@requires', () => {
           type A @key(fields: "idA") {
             idA: ID!
           }
-        `
-      }
+        `,
+      };
 
       const subgraph2 = {
         name: 'Subgraph2',
@@ -2553,8 +2686,8 @@ describe('@requires', () => {
             idB: ID!
             required: Int
           }
-        `
-      }
+        `,
+      };
 
       const subgraph3 = {
         name: 'Subgraph3',
@@ -2564,19 +2697,26 @@ describe('@requires', () => {
             c: Int @requires(fields: "required")
             required: Int @external
           }
-        `
-      }
+        `,
+      };
 
-      const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
-      const operation = operationFromDocument(api, gql`
-        query foo($test1: Boolean!, $test2: Boolean!){
-          a @include(if: $test1) {
-            b @include(if: $test2) {
-              c
+      const [api, queryPlanner] = composeAndCreatePlanner(
+        subgraph1,
+        subgraph2,
+        subgraph3,
+      );
+      const operation = operationFromDocument(
+        api,
+        gql`
+          query foo($test1: Boolean!, $test2: Boolean!) {
+            a @include(if: $test1) {
+              b @include(if: $test2) {
+                c
+              }
             }
           }
-        }
-      `);
+        `,
+      );
 
       const plan = queryPlanner.buildQueryPlan(operation);
       expect(plan).toMatchInlineSnapshot(`
@@ -2655,8 +2795,8 @@ describe('@requires', () => {
           a: String @inaccessible
           onlyIn1: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -2668,20 +2808,23 @@ describe('@requires', () => {
         type One @key(fields: "id") {
           id: ID!
           a: String @external
-          b: String @requires(fields: "a" )
+          b: String @requires(fields: "a")
           onlyIn2: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        one {
-          b
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          one {
+            b
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -2728,15 +2871,15 @@ describe('@requires', () => {
       name: 'A',
       typeDefs: gql`
         type Query {
-          t : T
+          t: T
         }
 
         type T @key(fields: "id1") @key(fields: "req1") {
           id1: ID!
           req1: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'B',
@@ -2747,8 +2890,8 @@ describe('@requires', () => {
           req2: Int @external
           v: Int @requires(fields: "req1 req2")
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'C',
@@ -2757,17 +2900,24 @@ describe('@requires', () => {
           req1: Int
           req2: Int
         }
-      `
-    }
+      `,
+    };
 
-    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v
+    const [api, queryPlanner] = composeAndCreatePlanner(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -2832,8 +2982,8 @@ describe('fetch operation names', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'non-graphql-name',
@@ -2842,17 +2992,20 @@ describe('fetch operation names', () => {
           id: ID!
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      query myOp {
-        t {
-          x
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query myOp {
+          t {
+            x
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -2884,7 +3037,8 @@ describe('fetch operation names', () => {
         },
       }
     `);
-    const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode).node as FetchNode;
+    const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode)
+      .node as FetchNode;
     expect(fetch.operation).toMatch(/^query myOp__non_graphql_name__1.*/i);
   });
 
@@ -2899,8 +3053,8 @@ describe('fetch operation names', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'a-na&me-with-plen&ty-replace*ments',
@@ -2909,17 +3063,20 @@ describe('fetch operation names', () => {
           id: ID!
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      query myOp {
-        t {
-          x
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query myOp {
+          t {
+            x
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -2951,8 +3108,11 @@ describe('fetch operation names', () => {
         },
       }
     `);
-    const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode).node as FetchNode;
-    expect(fetch.operation).toMatch(/^query myOp__a_name_with_plenty_replacements__1.*/i);
+    const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode)
+      .node as FetchNode;
+    expect(fetch.operation).toMatch(
+      /^query myOp__a_name_with_plenty_replacements__1.*/i,
+    );
   });
 
   test('handle very non-graph subgraph name', () => {
@@ -2966,8 +3126,8 @@ describe('fetch operation names', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: '42!',
@@ -2976,17 +3136,20 @@ describe('fetch operation names', () => {
           id: ID!
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      query myOp {
-        t {
-          x
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query myOp {
+          t {
+            x
+          }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -3018,7 +3181,8 @@ describe('fetch operation names', () => {
         },
       }
     `);
-    const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode).node as FetchNode;
+    const fetch = ((plan.node as SequenceNode).nodes[1] as FlattenNode)
+      .node as FetchNode;
 
     expect(fetch.operation).toMatch(/^query myOp___42__1.*/i);
   });
@@ -3032,7 +3196,8 @@ test('Correctly handle case where there is too many plans to consider', () => {
   // gets very large very quickly). Obviously, there is no reason to do this in practice.
 
   // Each leaf field is reachable from 2 subgraphs, so doubles the number of plans.
-  const defaultMaxComputedPlans = enforceQueryPlannerConfigDefaults().debug.maxEvaluatedPlans!;
+  const defaultMaxComputedPlans =
+    enforceQueryPlannerConfigDefaults().debug.maxEvaluatedPlans!;
   const fieldCount = Math.ceil(Math.log2(defaultMaxComputedPlans)) + 1;
   const fields = [...Array(fieldCount).keys()].map((i) => `f${i}`);
 
@@ -3046,14 +3211,20 @@ test('Correctly handle case where there is too many plans to consider', () => {
     }
   `;
 
-  const [api, queryPlanner] = composeAndCreatePlanner({ name: 'S1', typeDefs }, { name: 'S2', typeDefs });
-  const operation = operationFromDocument(api, gql`
+  const [api, queryPlanner] = composeAndCreatePlanner(
+    { name: 'S1', typeDefs },
+    { name: 'S2', typeDefs },
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
     {
       t {
         ${fields.map((f) => `${f}\n`)}
       }
     }
-  `);
+  `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   // Note: The way the code that handle multiple plans currently work, it mess up the order of fields a bit. It's not a
@@ -3073,9 +3244,11 @@ test('Correctly handle case where there is too many plans to consider', () => {
   //     ... all fields
   //   }
   // }
-  const mainSelection = (fetchOp.definitions[0] as OperationDefinitionNode).selectionSet;
+  const mainSelection = (fetchOp.definitions[0] as OperationDefinitionNode)
+    .selectionSet;
   const subSelection = (mainSelection.selections[0] as FieldNode).selectionSet;
-  const queriedFields = subSelection?.selections.map((s) => (s as FieldNode).name.value) ?? [];
+  const queriedFields =
+    subSelection?.selections.map((s) => (s as FieldNode).name.value) ?? [];
   fields.sort(); // Note that alphabetical order is not numerical order, hence this
   queriedFields.sort();
   expect(queriedFields).toStrictEqual(fields);
@@ -3128,19 +3301,22 @@ describe('Field covariance and type-explosion', () => {
     const api = supergraph.apiSchema();
     const queryPlanner = new QueryPlanner(supergraph);
 
-    const operation = operationFromDocument(api, gql`
-      {
-        dummy {
-          field {
-            ... on Object {
-              field {
-                __typename
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          dummy {
+            field {
+              ... on Object {
+                field {
+                  __typename
+                }
               }
             }
           }
         }
-      }
-    `);
+      `,
+    );
     const queryPlan = queryPlanner.buildQueryPlan(operation);
     expect(queryPlan).toMatchInlineSnapshot(`
       QueryPlan {
@@ -3180,8 +3356,8 @@ describe('Field covariance and type-explosion', () => {
           field: Object @provides(fields: "x")
           x: Int @external
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -3190,23 +3366,26 @@ describe('Field covariance and type-explosion', () => {
           id: ID!
           x: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        dummy {
-          field {
-            ... on Object {
-              field {
-                __typename
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          dummy {
+            field {
+              ... on Object {
+                field {
+                  __typename
+                }
               }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -3229,7 +3408,7 @@ describe('Field covariance and type-explosion', () => {
       }
     `);
   });
-})
+});
 
 describe('handles non-intersecting fragment conditions', () => {
   test('with federation 1 supergraphs', () => {
@@ -3275,30 +3454,33 @@ describe('handles non-intersecting fragment conditions', () => {
       enum join__Graph {
         S1 @join__graph(name: "S1" url: "")
       }
-    `
+    `;
 
     const supergraph = Supergraph.build(supergraphSdl);
     const api = supergraph.apiSchema();
     const queryPlanner = new QueryPlanner(supergraph);
 
-    const operation = operationFromDocument(api, gql`
-      fragment OrangeYouGladIDidntSayBanana on Fruit {
-        ... on Banana {
-          inBunch
-        }
-        ... on Apple {
-          hasStem
+    const operation = operationFromDocument(
+      api,
+      gql`
+        fragment OrangeYouGladIDidntSayBanana on Fruit {
+          ... on Banana {
+            inBunch
+          }
+          ... on Apple {
+            hasStem
+          }
         }
-      }
 
-      query Fruitiness {
-        fruit {
-          ... on Apple {
-            ...OrangeYouGladIDidntSayBanana
+        query Fruitiness {
+          fruit {
+            ... on Apple {
+              ...OrangeYouGladIDidntSayBanana
+            }
           }
         }
-      }
-    `);
+      `,
+    );
     const queryPlan = queryPlanner.buildQueryPlan(operation);
     expect(queryPlan).toMatchInlineSnapshot(`
       QueryPlan {
@@ -3337,28 +3519,31 @@ describe('handles non-intersecting fragment conditions', () => {
         type Query {
           fruit: Fruit!
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      fragment OrangeYouGladIDidntSayBanana on Fruit {
-        ... on Banana {
-          inBunch
-        }
-        ... on Apple {
-          hasStem
+    const operation = operationFromDocument(
+      api,
+      gql`
+        fragment OrangeYouGladIDidntSayBanana on Fruit {
+          ... on Banana {
+            inBunch
+          }
+          ... on Apple {
+            hasStem
+          }
         }
-      }
 
-      query Fruitiness {
-        fruit {
-          ... on Apple {
-            ...OrangeYouGladIDidntSayBanana
+        query Fruitiness {
+          fruit {
+            ... on Apple {
+              ...OrangeYouGladIDidntSayBanana
+            }
           }
         }
-      }
-    `);
+      `,
+    );
     const queryPlan = queryPlanner.buildQueryPlan(operation);
     expect(queryPlan).toMatchInlineSnapshot(`
       QueryPlan {
@@ -3402,8 +3587,8 @@ test('avoids unnecessary fetches', () => {
       type A @key(fields: "idA2") {
         idA2: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -3413,12 +3598,11 @@ test('avoids unnecessary fetches', () => {
         u: U
       }
 
-
       type U @key(fields: "idU") {
         idU: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -3426,8 +3610,8 @@ test('avoids unnecessary fetches', () => {
       type A @key(fields: "idA1") {
         idA1: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph4 = {
     name: 'Subgraph4',
@@ -3436,8 +3620,8 @@ test('avoids unnecessary fetches', () => {
         idA1: ID!
         idA2: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph5 = {
     name: 'Subgraph5',
@@ -3446,22 +3630,31 @@ test('avoids unnecessary fetches', () => {
         idU: ID!
         v: Int
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3, subgraph4, subgraph5);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        u {
-          v
-        }
-        a {
-          idA1
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlanner(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+    subgraph4,
+    subgraph5,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          u {
+            v
+          }
+          a {
+            idA1
+          }
         }
       }
-    }
-  `);
+    `,
+  );
   const queryPlan = queryPlanner.buildQueryPlan(operation);
   expect(queryPlan).toMatchInlineSnapshot(`
     QueryPlan {
@@ -3585,15 +3778,18 @@ describe('Fed1 supergraph handling', () => {
     const api = supergraph.apiSchema();
     const queryPlanner = new QueryPlanner(supergraph);
 
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          nodes {
-            id
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            nodes {
+              id
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const queryPlan = queryPlanner.buildQueryPlan(operation);
     expect(queryPlan).toMatchInlineSnapshot(`
@@ -3647,48 +3843,51 @@ describe('Named fragments preservation', () => {
           child: Foo
           child2: Foo
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      query {
-        a {
-          ...on A1 {
-            ...FooSelect
-          }
-          ...on A2 {
-            ...FooSelect
-          }
-          ...on A3 {
-            ...FooSelect
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          a {
+            ... on A1 {
+              ...FooSelect
+            }
+            ... on A2 {
+              ...FooSelect
+            }
+            ... on A3 {
+              ...FooSelect
+            }
           }
         }
-      }
 
-      fragment FooSelect on Foo {
-        __typename
-        foo
-        child {
-          ...FooChildSelect
-        }
-        child2 {
-          ...FooChildSelect
+        fragment FooSelect on Foo {
+          __typename
+          foo
+          child {
+            ...FooChildSelect
+          }
+          child2 {
+            ...FooChildSelect
+          }
         }
-      }
 
-      fragment FooChildSelect on Foo {
-        __typename
-        foo
-        child {
+        fragment FooChildSelect on Foo {
+          __typename
+          foo
           child {
             child {
-              foo
+              child {
+                foo
+              }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -3757,8 +3956,8 @@ describe('Named fragments preservation', () => {
           b: Int
           c: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -3774,31 +3973,34 @@ describe('Named fragments preservation', () => {
           b: Int
           c: Int
         }
-      `
-    }
+      `,
+    };
 
     // We use a fragment which does save some on the original query, but as each
     // field gets to a different subgraph, the fragment would only be used one
     // on each sub-fetch and we make sure the fragment is not used in that case.
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    let operation = operationFromDocument(api, gql`
-      query {
-        t {
-          v1 {
-            ...OnV
-          }
-          v2 {
-            ...OnV
+    let operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          t {
+            v1 {
+              ...OnV
+            }
+            v2 {
+              ...OnV
+            }
           }
         }
-      }
 
-      fragment OnV on V {
-        a
-        b
-        c
-      }
-    `);
+        fragment OnV on V {
+          a
+          b
+          c
+        }
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -3842,24 +4044,27 @@ describe('Named fragments preservation', () => {
 
     // But double-check that if we query 2 fields from the same subgraph, then
     // the fragment gets used now.
-    operation = operationFromDocument(api, gql`
-      query {
-        t {
-          v2 {
-            ...OnV
-          }
-          v3 {
-            ...OnV
+    operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          t {
+            v2 {
+              ...OnV
+            }
+            v3 {
+              ...OnV
+            }
           }
         }
-      }
 
-      fragment OnV on V {
-        a
-        b
-        c
-      }
-    `);
+        fragment OnV on V {
+          a
+          b
+          c
+        }
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -3904,47 +4109,55 @@ describe('Named fragments preservation', () => {
     `);
   });
 
-  it.each([ true, false ])('respects query planner option "reuseQueryFragments=%p"', (reuseQueryFragments: boolean) => {
-    const subgraph1 = {
-      name: 'Subgraph1',
-      typeDefs: gql`
-        type Query {
-          t: T
-        }
-
-        type T {
-          a1: A
-          a2: A
-        }
+  it.each([true, false])(
+    'respects query planner option "reuseQueryFragments=%p"',
+    (reuseQueryFragments: boolean) => {
+      const subgraph1 = {
+        name: 'Subgraph1',
+        typeDefs: gql`
+          type Query {
+            t: T
+          }
 
-        type A {
-          x: Int
-          y: Int
-        }
-      `
-    }
+          type T {
+            a1: A
+            a2: A
+          }
 
-    const [api, queryPlanner] = composeAndCreatePlannerWithOptions([subgraph1], { reuseQueryFragments });
-    const operation = operationFromDocument(api, gql`
-      query {
-        t {
-          a1 {
-            ...Selection
+          type A {
+            x: Int
+            y: Int
           }
-          a2 {
-            ...Selection
+        `,
+      };
+
+      const [api, queryPlanner] = composeAndCreatePlannerWithOptions(
+        [subgraph1],
+        { reuseQueryFragments },
+      );
+      const operation = operationFromDocument(
+        api,
+        gql`
+          query {
+            t {
+              a1 {
+                ...Selection
+              }
+              a2 {
+                ...Selection
+              }
+            }
           }
-        }
-      }
 
-      fragment Selection on A {
-        x
-        y
-      }
-    `);
+          fragment Selection on A {
+            x
+            y
+          }
+        `,
+      );
 
-    const plan = queryPlanner.buildQueryPlan(operation);
-    const withReuse = `
+      const plan = queryPlanner.buildQueryPlan(operation);
+      const withReuse = `
       QueryPlan {
         Fetch(service: "Subgraph1") {
           {
@@ -3965,7 +4178,7 @@ describe('Named fragments preservation', () => {
         },
       }
     `;
-    const withoutReuse = `
+      const withoutReuse = `
       QueryPlan {
         Fetch(service: "Subgraph1") {
           {
@@ -3982,10 +4195,13 @@ describe('Named fragments preservation', () => {
           }
         },
       }
-    `
+    `;
 
-    expect(plan).toMatchInlineSnapshot(reuseQueryFragments ? withReuse : withoutReuse);
-  });
+      expect(plan).toMatchInlineSnapshot(
+        reuseQueryFragments ? withReuse : withoutReuse,
+      );
+    },
+  );
 
   it('works with nested fragments when only the nested fragment gets preserved', () => {
     const subgraph1 = {
@@ -3995,7 +4211,7 @@ describe('Named fragments preservation', () => {
           t: T
         }
 
-        type T @key(fields : "id") {
+        type T @key(fields: "id") {
           id: ID!
           a: V
           b: V
@@ -4005,32 +4221,34 @@ describe('Named fragments preservation', () => {
           v1: Int
           v2: Int
         }
-
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          ...OnT
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            ...OnT
+          }
         }
-      }
 
-      fragment OnT on T {
-        a {
-          ...OnV
-        }
-        b {
-          ...OnV
+        fragment OnT on T {
+          a {
+            ...OnV
+          }
+          b {
+            ...OnV
+          }
         }
-      }
 
-      fragment OnV on V {
-        v1
-        v2
-      }
-    `);
+        fragment OnV on V {
+          v1
+          v2
+        }
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -4064,28 +4282,31 @@ describe('Named fragments preservation', () => {
           t: T
         }
 
-        type T @key(fields : "id") {
+        type T @key(fields: "id") {
           id: ID!
           a: Int
           b: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      query test($if: Boolean) {
-        t {
-          id
-          ...OnT @include(if: $if)
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query test($if: Boolean) {
+          t {
+            id
+            ...OnT @include(if: $if)
+          }
         }
-      }
 
-      fragment OnT on T {
-        a
-        b
-      }
-    `);
+        fragment OnT on T {
+          a
+          b
+        }
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -4113,29 +4334,32 @@ describe('Named fragments preservation', () => {
           t: T
         }
 
-        type T @key(fields : "id") {
+        type T @key(fields: "id") {
           id: ID!
           a: Int
           b: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      query test($test1: Boolean, $test2: Boolean) {
-        t {
-          id
-          ...OnT @include(if: $test1)
-          ...OnT @include(if: $test2)
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query test($test1: Boolean, $test2: Boolean) {
+          t {
+            id
+            ...OnT @include(if: $test1)
+            ...OnT @include(if: $test2)
+          }
         }
-      }
 
-      fragment OnT on T {
-        a
-        b
-      }
-    `);
+        fragment OnT on T {
+          a
+          b
+        }
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -4184,8 +4408,8 @@ describe('Named fragments preservation', () => {
           a: Int
           b: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -4194,28 +4418,31 @@ describe('Named fragments preservation', () => {
           a: Int
           b: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      query {
-        i1 {
-          ... on T {
-            ...Frag
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          i1 {
+            ... on T {
+              ...Frag
+            }
           }
-        }
-        i2 {
-          ... on T {
-            ...Frag
+          i2 {
+            ... on T {
+              ...Frag
+            }
           }
         }
-      }
 
-      fragment Frag on I {
-        b
-      }
-    `);
+        fragment Frag on I {
+          b
+        }
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -4234,7 +4461,576 @@ describe('Named fragments preservation', () => {
                 b
               }
             }
-          }
+          }
+        },
+      }
+    `);
+  });
+
+  it('handles fragment rebasing in a subgraph where some subtyping relation differs', () => {
+    // This test is designed such that type `Outer` implements the interface `I` in `Subgraph1`
+    // but not in `Subgraph2`, yet `I` exists in `Subgraph2` (but only `Inner` implements it
+    // there). Further, the operations we test have a fragment on I (`IFrag` below) that is
+    // used "in the context of `Outer`" (at the top-level of fragment `OuterFrag`).
+    //
+    // What this all means is that `IFrag` can be rebased in `Subgraph2` "as is" because `I`
+    // exists there with all its fields, but as we rebase `OuterFrag` on `Subgraph2`, we
+    // cannot use `...IFrag` inside it (at the top-level), because `I` and `Outer` do
+    // no intersect in `Subgraph2` and this would be an invalid selection.
+    //
+    // Previous versions of the code were not handling this case and were error out by
+    // creating the invalid selection (#2721), and this test ensures this is fixed.
+    const subgraph1 = {
+      name: 'Subgraph1',
+      typeDefs: gql`
+        type V @shareable {
+          x: Int
+        }
+
+        interface I {
+          v: V
+        }
+
+        type Outer implements I @key(fields: "id") {
+          id: ID!
+          v: V
+        }
+      `,
+    };
+
+    const subgraph2 = {
+      name: 'Subgraph2',
+      typeDefs: gql`
+        type Query {
+          outer1: Outer
+          outer2: Outer
+        }
+
+        type V @shareable {
+          x: Int
+        }
+
+        interface I {
+          v: V
+          w: Int
+        }
+
+        type Inner implements I {
+          v: V
+          w: Int
+        }
+
+        type Outer @key(fields: "id") {
+          id: ID!
+          inner: Inner
+          w: Int
+        }
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
+    let operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          outer1 {
+            ...OuterFrag
+          }
+          outer2 {
+            ...OuterFrag
+          }
+        }
+
+        fragment OuterFrag on Outer {
+          ...IFrag
+          inner {
+            ...IFrag
+          }
+        }
+
+        fragment IFrag on I {
+          v {
+            x
+          }
+        }
+      `,
+    );
+
+    const expectedPlan = `
+      QueryPlan {
+        Sequence {
+          Fetch(service: "Subgraph2") {
+            {
+              outer1 {
+                __typename
+                ...OuterFrag
+                id
+              }
+              outer2 {
+                __typename
+                ...OuterFrag
+                id
+              }
+            }
+            
+            fragment OuterFrag on Outer {
+              inner {
+                v {
+                  x
+                }
+              }
+            }
+          },
+          Parallel {
+            Flatten(path: "outer1") {
+              Fetch(service: "Subgraph1") {
+                {
+                  ... on Outer {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on Outer {
+                    v {
+                      x
+                    }
+                  }
+                }
+              },
+            },
+            Flatten(path: "outer2") {
+              Fetch(service: "Subgraph1") {
+                {
+                  ... on Outer {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on Outer {
+                    v {
+                      x
+                    }
+                  }
+                }
+              },
+            },
+          },
+        },
+      }
+    `;
+    expect(queryPlanner.buildQueryPlan(operation)).toMatchInlineSnapshot(
+      expectedPlan,
+    );
+
+    // We very slighly modify the operation to add an artificial indirection within `IFrag`.
+    // This does not really change the query, and should result in the same plan, but
+    // ensure the code handle correctly such indirection.
+    operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          outer1 {
+            ...OuterFrag
+          }
+          outer2 {
+            ...OuterFrag
+          }
+        }
+
+        fragment OuterFrag on Outer {
+          ...IFrag
+          inner {
+            ...IFrag
+          }
+        }
+
+        fragment IFrag on I {
+          ...IFragDelegate
+        }
+
+        fragment IFragDelegate on I {
+          v {
+            x
+          }
+        }
+      `,
+    );
+
+    expect(queryPlanner.buildQueryPlan(operation)).toMatchInlineSnapshot(
+      expectedPlan,
+    );
+
+    // The previous cases tests the cases where nothing in the `...IFrag` spread at the
+    // top-level of `OuterFrag` applied at all: it all gets eliminated in the plan. But
+    // in the schema of `Subgraph2`, while `Outer` does not implement `I` (and does not
+    // have `v` in particular), it does contains field `w` that `I` also have, so we
+    // add that field to `IFrag` and make sure we still correctly query that field.
+    operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          outer1 {
+            ...OuterFrag
+          }
+          outer2 {
+            ...OuterFrag
+          }
+        }
+
+        fragment OuterFrag on Outer {
+          ...IFrag
+          inner {
+            ...IFrag
+          }
+        }
+
+        fragment IFrag on I {
+          v {
+            x
+          }
+          w
+        }
+      `,
+    );
+
+    expect(queryPlanner.buildQueryPlan(operation)).toMatchInlineSnapshot(`
+      QueryPlan {
+        Sequence {
+          Fetch(service: "Subgraph2") {
+            {
+              outer1 {
+                __typename
+                ...OuterFrag
+                id
+              }
+              outer2 {
+                __typename
+                ...OuterFrag
+                id
+              }
+            }
+            
+            fragment OuterFrag on Outer {
+              w
+              inner {
+                v {
+                  x
+                }
+                w
+              }
+            }
+          },
+          Parallel {
+            Flatten(path: "outer1") {
+              Fetch(service: "Subgraph1") {
+                {
+                  ... on Outer {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on Outer {
+                    v {
+                      x
+                    }
+                  }
+                }
+              },
+            },
+            Flatten(path: "outer2") {
+              Fetch(service: "Subgraph1") {
+                {
+                  ... on Outer {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on Outer {
+                    v {
+                      x
+                    }
+                  }
+                }
+              },
+            },
+          },
+        },
+      }
+    `);
+  });
+
+  it('handles fragment rebasing in a subgraph where some union membership relation differs', () => {
+    // This test is similar to the subtyping case (it tests the same problems), but test the case
+    // of unions instead of interfaces.
+    const subgraph1 = {
+      name: 'Subgraph1',
+      typeDefs: gql`
+        type V @shareable {
+          x: Int
+        }
+
+        union U = Outer
+
+        type Outer @key(fields: "id") {
+          id: ID!
+          v: Int
+        }
+      `,
+    };
+
+    const subgraph2 = {
+      name: 'Subgraph2',
+      typeDefs: gql`
+        type Query {
+          outer1: Outer
+          outer2: Outer
+        }
+
+        union U = Inner
+
+        type Inner {
+          v: Int
+          w: Int
+        }
+
+        type Outer @key(fields: "id") {
+          id: ID!
+          inner: Inner
+          w: Int
+        }
+      `,
+    };
+
+    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
+    let operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          outer1 {
+            ...OuterFrag
+          }
+          outer2 {
+            ...OuterFrag
+          }
+        }
+
+        fragment OuterFrag on Outer {
+          ...UFrag
+          inner {
+            ...UFrag
+          }
+        }
+
+        fragment UFrag on U {
+          ... on Outer {
+            v
+          }
+          ... on Inner {
+            v
+          }
+        }
+      `,
+    );
+
+    const expectedPlan = `
+      QueryPlan {
+        Sequence {
+          Fetch(service: "Subgraph2") {
+            {
+              outer1 {
+                __typename
+                ...OuterFrag
+                id
+              }
+              outer2 {
+                __typename
+                ...OuterFrag
+                id
+              }
+            }
+            
+            fragment OuterFrag on Outer {
+              inner {
+                v
+              }
+            }
+          },
+          Parallel {
+            Flatten(path: "outer1") {
+              Fetch(service: "Subgraph1") {
+                {
+                  ... on Outer {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on Outer {
+                    v
+                  }
+                }
+              },
+            },
+            Flatten(path: "outer2") {
+              Fetch(service: "Subgraph1") {
+                {
+                  ... on Outer {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on Outer {
+                    v
+                  }
+                }
+              },
+            },
+          },
+        },
+      }
+    `;
+    expect(queryPlanner.buildQueryPlan(operation)).toMatchInlineSnapshot(
+      expectedPlan,
+    );
+
+    // We very slighly modify the operation to add an artificial indirection within `IFrag`.
+    // This does not really change the query, and should result in the same plan, but
+    // ensure the code handle correctly such indirection.
+    operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          outer1 {
+            ...OuterFrag
+          }
+          outer2 {
+            ...OuterFrag
+          }
+        }
+
+        fragment OuterFrag on Outer {
+          ...UFrag
+          inner {
+            ...UFrag
+          }
+        }
+
+        fragment UFrag on U {
+          ...UFragDelegate
+        }
+
+        fragment UFragDelegate on U {
+          ... on Outer {
+            v
+          }
+          ... on Inner {
+            v
+          }
+        }
+      `,
+    );
+
+    expect(queryPlanner.buildQueryPlan(operation)).toMatchInlineSnapshot(
+      expectedPlan,
+    );
+
+    // The previous cases tests the cases where nothing in the `...IFrag` spread at the
+    // top-level of `OuterFrag` applied at all: it all gets eliminated in the plan. But
+    // in the schema of `Subgraph2`, while `Outer` does not implement `I` (and does not
+    // have `v` in particular), it does contains field `w` that `I` also have, so we
+    // add that field to `IFrag` and make sure we still correctly query that field.
+    operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          outer1 {
+            ...OuterFrag
+          }
+          outer2 {
+            ...OuterFrag
+          }
+        }
+
+        fragment OuterFrag on Outer {
+          ...UFrag
+          inner {
+            ...UFrag
+          }
+        }
+
+        fragment UFrag on U {
+          ... on Outer {
+            v
+            w
+          }
+          ... on Inner {
+            v
+          }
+        }
+      `,
+    );
+
+    expect(queryPlanner.buildQueryPlan(operation)).toMatchInlineSnapshot(`
+      QueryPlan {
+        Sequence {
+          Fetch(service: "Subgraph2") {
+            {
+              outer1 {
+                __typename
+                ...OuterFrag
+                id
+              }
+              outer2 {
+                __typename
+                ...OuterFrag
+                id
+              }
+            }
+            
+            fragment OuterFrag on Outer {
+              w
+              inner {
+                v
+              }
+            }
+          },
+          Parallel {
+            Flatten(path: "outer1") {
+              Fetch(service: "Subgraph1") {
+                {
+                  ... on Outer {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on Outer {
+                    v
+                  }
+                }
+              },
+            },
+            Flatten(path: "outer2") {
+              Fetch(service: "Subgraph1") {
+                {
+                  ... on Outer {
+                    __typename
+                    id
+                  }
+                } =>
+                {
+                  ... on Outer {
+                    v
+                  }
+                }
+              },
+            },
+          },
         },
       }
     `);
@@ -4252,18 +5048,18 @@ test('works with key chains', () => {
       type T @key(fields: "id1") {
         id1: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
     typeDefs: gql`
-      type T @key(fields: "id1")  @key(fields: "id2") {
+      type T @key(fields: "id1") @key(fields: "id2") {
         id1: ID!
         id2: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -4273,22 +5069,29 @@ test('works with key chains', () => {
         x: Int
         y: Int
       }
-    `
-  }
+    `,
+  };
 
-  const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
+  const [api, queryPlanner] = composeAndCreatePlanner(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+  );
   // Note: querying `id2` is only purpose, because there is 2 choice to get `id2` (either
   // from then 2nd or 3rd subgraph), and that create some choice in the query planning algorithm,
   // so excercices additional paths.
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        id2
-        x
-        y
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          id2
+          x
+          y
+        }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -4351,18 +5154,21 @@ describe('__typename handling', () => {
           id: ID!
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    let operation = operationFromDocument(api, gql`
-      query {
-        t {
-          foo: __typename
-          x
+    let operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          t {
+            foo: __typename
+            x
+          }
         }
-      }
-    `);
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -4378,15 +5184,18 @@ describe('__typename handling', () => {
       }
     `);
 
-    operation = operationFromDocument(api, gql`
-      query {
-        t {
-          foo: __typename
-          x
-          __typename
+    operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          t {
+            foo: __typename
+            x
+            __typename
+          }
         }
-      }
-    `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -4415,8 +5224,8 @@ describe('__typename handling', () => {
         type S @key(fields: "id") {
           id: ID
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -4429,8 +5238,8 @@ describe('__typename handling', () => {
         type T {
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'Subgraph3',
@@ -4444,28 +5253,37 @@ describe('__typename handling', () => {
           id: ID!
           y: Int
         }
-      `
-    }
+      `,
+    };
 
-    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
+    const [api, queryPlanner] = composeAndCreatePlanner(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+    );
     // This tests the patch from https://github.com/apollographql/federation/pull/2137.
     // Namely, the schema is such that `x` can only be fetched from one subgraph, but
     // technically __typename can be fetched from 2 subgraphs. However, the optimization
     // we test for is that we actually don't consider both choices for __typename and
     // instead only evaluate a single query plan (the assertion on `evaluatePlanCount`)
-    let operation = operationFromDocument(api, gql`
-      query {
-        s {
-          t {
-            __typename
-            x
+    let operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          s {
+            t {
+              __typename
+              x
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
-    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(1);
+    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(
+      1,
+    );
     expect(plan).toMatchInlineSnapshot(`
       QueryPlan {
         Sequence {
@@ -4506,22 +5324,27 @@ describe('__typename handling', () => {
     // in the implementation made this example forgo the optimization of the
     // __typename within `t`. We make sure this is not case (that we still only
     // consider a single choice of plan).
-    operation = operationFromDocument(api, gql`
-      query {
-        s {
-          __typename
-          ... on S {
-            t {
-              __typename
-              x
+    operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          s {
+            __typename
+            ... on S {
+              t {
+                __typename
+                x
+              }
             }
           }
         }
-      }
-    `);
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
-    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(1);
+    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(
+      1,
+    );
     expect(plan).toMatchInlineSnapshot(`
       QueryPlan {
         Sequence {
@@ -4570,8 +5393,8 @@ describe('mutations', () => {
         type Mutation {
           m1: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -4579,16 +5402,19 @@ describe('mutations', () => {
         type Mutation {
           m2: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      mutation {
-        m2
-        m1
-      }
-    `);
+    const operation = operationFromDocument(
+      api,
+      gql`
+        mutation {
+          m2
+          m1
+        }
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -4631,8 +5457,8 @@ describe('interface type-explosion', () => {
         type S @shareable {
           x: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -4646,19 +5472,22 @@ describe('interface type-explosion', () => {
           x: Int
           y: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        i {
-          s {
-            y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          i {
+            s {
+              y
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     // The schema is constructed in such a way that we *need* to type-explode interface `I`
     // to be able to find field `y`. Make sure that happens.
@@ -4720,8 +5549,8 @@ describe('interface type-explosion', () => {
           x: Int
           y: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -4735,19 +5564,22 @@ describe('interface type-explosion', () => {
           x: Int
           y: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        i {
-          s {
-            y
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          i {
+            s {
+              y
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     // This test is a small variation on the previous test ('handles non-matching ...'), we
     // we _can_ use the interface field directly and don't need to type-explode. So we
@@ -4770,7 +5602,9 @@ describe('interface type-explosion', () => {
         },
       }
     `);
-    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(1);
+    expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(
+      1,
+    );
   });
 });
 
@@ -4806,8 +5640,8 @@ describe('merged abstract types handling', () => {
         type C implements I {
           v: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -4819,19 +5653,22 @@ describe('merged abstract types handling', () => {
         type A implements I {
           v: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        u {
-          ... on I {
-            v
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          u {
+            ... on I {
+              v
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Type `A` can be returned by `u` and is a `I` *in the supergraph* but not in `Subgraph1`, so need to
@@ -4883,8 +5720,8 @@ describe('merged abstract types handling', () => {
         type C implements I {
           v: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -4894,19 +5731,22 @@ describe('merged abstract types handling', () => {
         type A {
           v: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        u {
-          ... on I {
-            v
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          u {
+            ... on I {
+              v
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // While `A` is a `U` in the supergraph while not in `Subgraph1`, since the `u`
@@ -4954,8 +5794,8 @@ describe('merged abstract types handling', () => {
         type C implements I {
           v: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -4965,21 +5805,24 @@ describe('merged abstract types handling', () => {
         type A {
           v: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        i {
-          ... on U {
-            ... on A {
-              v
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          i {
+            ... on U {
+              ... on A {
+                v
+              }
             }
           }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Type `A` can be returned by `i` and is a `U` *in the supergraph* but not in `Subgraph1`, so need to
@@ -5025,8 +5868,8 @@ describe('merged abstract types handling', () => {
         type C implements I {
           v: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -5038,21 +5881,24 @@ describe('merged abstract types handling', () => {
         type A implements I {
           v: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        i {
-          ... on U {
-            ... on A {
-              v
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          i {
+            ... on U {
+              ... on A {
+                v
+              }
             }
           }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Here, `A` is a `I` in the supergraph while not in `Subgraph1`, and since the `i` operation is resolved by
@@ -5097,8 +5943,8 @@ describe('merged abstract types handling', () => {
         type C implements I1 & I2 {
           v: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -5110,19 +5956,22 @@ describe('merged abstract types handling', () => {
         type A implements I2 {
           v: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        i1 {
-          ... on I2 {
-            v
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          i1 {
+            ... on I2 {
+              v
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Type `A` can be returned by `i1` and is a `I2` *in the supergraph* but not in `Subgraph1`, so need to
@@ -5176,8 +6025,8 @@ describe('merged abstract types handling', () => {
         type C implements I1 & I2 {
           v: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -5189,19 +6038,22 @@ describe('merged abstract types handling', () => {
         type A implements I1 {
           v: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        i1 {
-          ... on I2 {
-            v
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          i1 {
+            ... on I2 {
+              v
+            }
           }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // While `A` is a `I1` in the supergraph while not in `Subgraph1`, since the `i1`
@@ -5247,8 +6099,8 @@ describe('merged abstract types handling', () => {
         type C {
           v: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -5258,21 +6110,24 @@ describe('merged abstract types handling', () => {
         type A {
           v: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        u1 {
-          ... on U2 {
-            ... on A {
-              v
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          u1 {
+            ... on U2 {
+              ... on A {
+                v
+              }
             }
           }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Type `A` can be returned by `u1` and is a `U2` *in the supergraph* but not in `Subgraph1`, so need to
@@ -5315,8 +6170,8 @@ describe('merged abstract types handling', () => {
         type C {
           v: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -5326,21 +6181,24 @@ describe('merged abstract types handling', () => {
         type A {
           v: Int @shareable
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        u1 {
-          ... on U2 {
-            ... on A {
-              v
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          u1 {
+            ... on U2 {
+              ... on A {
+                v
+              }
             }
           }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Similar case than in the `interface/union` case: the whole `... on U2` sub-selection happens to be
@@ -5379,12 +6237,12 @@ test('handles spread unions correctly', () => {
         b: Int
       }
 
-      type C  @key(fields: "id") {
+      type C @key(fields: "id") {
         id: ID!
         c1: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -5404,19 +6262,22 @@ test('handles spread unions correctly', () => {
         id: ID!
         c2: Int
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-  const operation = operationFromDocument(api, gql`
-    {
-      u {
-        ... on C {
-          c1
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        u {
+          ... on C {
+            c1
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   // Note: it's important that the query below DO NOT include the `... on C` part. Because in
@@ -5433,7 +6294,7 @@ test('handles spread unions correctly', () => {
       },
     }
   `);
-})
+});
 
 test('handles case of key chains in parallel requires', () => {
   const subgraph1 = {
@@ -5453,18 +6314,18 @@ test('handles case of key chains in parallel requires', () => {
         id: ID!
         y: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
     typeDefs: gql`
-      type T1 @key(fields: "id1")  @key(fields: "id2") {
+      type T1 @key(fields: "id1") @key(fields: "id2") {
         id1: ID!
         id2: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -5479,22 +6340,29 @@ test('handles case of key chains in parallel requires', () => {
         y: Int @external
         z: Int @requires(fields: "y")
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        ... on T1 {
-          x
-        }
-        ... on T2 {
-          z
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlanner(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          ... on T1 {
+            x
+          }
+          ... on T2 {
+            z
+          }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -5600,8 +6468,8 @@ test('handles types with no common supertype at the same "mergeAt"', () => {
         id: ID!
         x: Int
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -5615,26 +6483,29 @@ test('handles types with no common supertype at the same "mergeAt"', () => {
         id: ID!
         y: Int
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        ... on T1 {
-          sub {
-            y
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          ... on T1 {
+            sub {
+              y
+            }
           }
-        }
-        ... on T2 {
-          sub {
-            y
+          ... on T2 {
+            sub {
+              y
+            }
           }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -5712,37 +6583,40 @@ test('does not error out handling fragments when interface subtyping is involved
         v1: Int!
         v2: Int!
       }
-    `
-  }
+    `,
+  };
 
   const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-  const operation = operationFromDocument(api, gql`
-    {
-      a {
-        ...F1
-        ...F2
-        ...F3
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        a {
+          ...F1
+          ...F2
+          ...F3
+        }
       }
-    }
 
-    fragment F1 on A {
-      b {
-        v2
+      fragment F1 on A {
+        b {
+          v2
+        }
       }
-    }
 
-    fragment F2 on IA {
-      b {
-        v1
+      fragment F2 on IA {
+        b {
+          v1
+        }
       }
-    }
 
-    fragment F3 on IA {
-      b {
-        __typename
+      fragment F3 on IA {
+        b {
+          __typename
+        }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -5762,7 +6636,7 @@ test('does not error out handling fragments when interface subtyping is involved
   `);
 });
 
-describe("named fragments", () => {
+describe('named fragments', () => {
   test('handles mix of fragments indirection and unions', () => {
     const subgraph1 = {
       name: 'Subgraph1',
@@ -5784,38 +6658,41 @@ describe("named fragments", () => {
         type Cat {
           name: String
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      query {
-        parent {
-          ...F_indirection1_parent
+    const operation = operationFromDocument(
+      api,
+      gql`
+        query {
+          parent {
+            ...F_indirection1_parent
+          }
         }
-      }
- 
-      fragment F_indirection1_parent on Parent {
-        ...F_indirection2_catOrPerson
-      }
 
-      fragment F_indirection2_catOrPerson on CatOrPerson {
-        ...F_catOrPerson
-      }
+        fragment F_indirection1_parent on Parent {
+          ...F_indirection2_catOrPerson
+        }
 
-      fragment F_catOrPerson on CatOrPerson {
-        __typename
-        ... on Cat {
-          name
+        fragment F_indirection2_catOrPerson on CatOrPerson {
+          ...F_catOrPerson
         }
-        ... on Parent {
-          childs {
-            __typename
-            id
+
+        fragment F_catOrPerson on CatOrPerson {
+          __typename
+          ... on Cat {
+            name
+          }
+          ... on Parent {
+            childs {
+              __typename
+              id
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -5873,47 +6750,50 @@ describe("named fragments", () => {
           id1: ID!
           id2: ID!
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    let operation = operationFromDocument(api, gql`
-      {
-        owner {
-          u {
-            ... on I {
-              id1
-              id2
+    let operation = operationFromDocument(
+      api,
+      gql`
+        {
+          owner {
+            u {
+              ... on I {
+                id1
+                id2
+              }
+              ...Fragment1
+              ...Fragment2
             }
-            ...Fragment1
-            ...Fragment2
           }
         }
-      }
 
-      fragment Fragment1 on T1 {
-        owner {
-          ... on Owner {
-            ...Fragment3
+        fragment Fragment1 on T1 {
+          owner {
+            ... on Owner {
+              ...Fragment3
+            }
           }
         }
-      }
 
-      fragment Fragment2 on T2 {
-        ...Fragment4
-        id1
-      }
+        fragment Fragment2 on T2 {
+          ...Fragment4
+          id1
+        }
 
-      fragment Fragment3 on OItf {
-        v0
-      }
+        fragment Fragment3 on OItf {
+          v0
+        }
 
-      fragment Fragment4 on I {
-        id1
-        id2
-        __typename
-      }
-    `);
+        fragment Fragment4 on I {
+          id1
+          id2
+          __typename
+        }
+      `,
+    );
 
     let plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -5945,42 +6825,45 @@ describe("named fragments", () => {
       }
     `);
 
-    operation = operationFromDocument(api, gql`
-      {
-        owner {
-          u {
-            ... on I {
-              id1
-              id2
+    operation = operationFromDocument(
+      api,
+      gql`
+        {
+          owner {
+            u {
+              ... on I {
+                id1
+                id2
+              }
+              ...Fragment1
+              ...Fragment2
             }
-            ...Fragment1
-            ...Fragment2
           }
         }
-      }
 
-      fragment Fragment1 on T1 {
-        owner {
-          ... on Owner {
-            ...Fragment3
+        fragment Fragment1 on T1 {
+          owner {
+            ... on Owner {
+              ...Fragment3
+            }
           }
         }
-      }
 
-      fragment Fragment2 on T2 {
-        ...Fragment4
-        id1
-      }
+        fragment Fragment2 on T2 {
+          ...Fragment4
+          id1
+        }
 
-      fragment Fragment3 on OItf {
-        v0
-      }
+        fragment Fragment3 on OItf {
+          v0
+        }
 
-      fragment Fragment4 on I {
-        id1
-        id2
-      }
-    `);
+        fragment Fragment4 on I {
+          id1
+          id2
+        }
+      `,
+    );
 
     plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -6033,34 +6916,35 @@ describe("named fragments", () => {
           other: T1!
         }
 
-
-        type T2 implements I
-        {
+        type T2 implements I {
           id: ID!
           other: T2!
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1);
-    const operation = operationFromDocument(api, gql`
-      {
-        t1 {
-          ...Fragment1
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t1 {
+            ...Fragment1
+          }
         }
-      }
 
-      fragment Fragment1 on I {
-        other {
-          ... on T1 {
-            id
-          }
-          ... on T2 {
-            id
+        fragment Fragment1 on I {
+          other {
+            ... on T1 {
+              id
+            }
+            ... on T2 {
+              id
+            }
           }
         }
-      }
-    `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -6069,6 +6953,7 @@ describe("named fragments", () => {
           {
             t1 {
               other {
+                __typename
                 id
               }
             }
@@ -6093,8 +6978,8 @@ describe("named fragments", () => {
           v1: Int
           v2: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -6103,27 +6988,30 @@ describe("named fragments", () => {
           id: ID!
           v3: Int
         }
-      `
-    }
+      `,
+    };
 
     const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2);
-    const operation = operationFromDocument(api, gql`
-      {
-        t1 {
-          ...allTFields
-        }
-        t2 {
-          ...allTFields
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t1 {
+            ...allTFields
+          }
+          t2 {
+            ...allTFields
+          }
         }
-      }
 
-      fragment allTFields on T {
-        v0
-        v1
-        v2
-        v3
-      }
-    `);
+        fragment allTFields on T {
+          v0
+          v1
+          v2
+          v3
+        }
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -6197,8 +7085,8 @@ describe("named fragments", () => {
         type T @key(fields: "id") {
           id: ID!
         }
-      `
-    }
+      `,
+    };
 
     const subgraph2 = {
       name: 'Subgraph2',
@@ -6207,7 +7095,6 @@ describe("named fragments", () => {
           id: ID!
           u1: U
           u2: U
-
         }
 
         type U @key(fields: "id") {
@@ -6215,8 +7102,8 @@ describe("named fragments", () => {
           v0: Int
           v1: Int
         }
-      `
-    }
+      `,
+    };
 
     const subgraph3 = {
       name: 'Subgraph3',
@@ -6226,29 +7113,36 @@ describe("named fragments", () => {
           v2: Int
           v3: Int
         }
-      `
-    }
+      `,
+    };
 
-    const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          u1 {
-            ...allUFields
-          }
-          u2 {
-            ...allUFields
+    const [api, queryPlanner] = composeAndCreatePlanner(
+      subgraph1,
+      subgraph2,
+      subgraph3,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            u1 {
+              ...allUFields
+            }
+            u2 {
+              ...allUFields
+            }
           }
         }
-      }
 
-      fragment allUFields on U {
-        v0
-        v1
-        v2
-        v3
-      }
-    `);
+        fragment allUFields on U {
+          v0
+          v1
+          v2
+          v3
+        }
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -6329,7 +7223,7 @@ describe("named fragments", () => {
       }
     `);
   });
-})
+});
 
 describe('`debug.maxEvaluatedPlans` configuration', () => {
   // Simple schema, created to force the query planner to have multiple choice. We'll build
@@ -6357,10 +7251,12 @@ describe('`debug.maxEvaluatedPlans` configuration', () => {
   const subgraphs = [
     {
       name: 'Subgraph1',
-      typeDefs
-    }, {
+      typeDefs,
+    },
+    {
       name: 'Subgraph2',
-      typeDefs  }
+      typeDefs,
+    },
   ];
 
   test('works when unset', () => {
@@ -6371,18 +7267,24 @@ describe('`debug.maxEvaluatedPlans` configuration', () => {
     // plans are considered) and we'll have to adapt the example (find a better way to force
     // choices).
 
-    const config = { debug : { maxEvaluatedPlans : undefined } };
-    const [api, queryPlanner] = composeAndCreatePlannerWithOptions(subgraphs, config);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v1
-          v2
-          v3
-          v4
+    const config = { debug: { maxEvaluatedPlans: undefined } };
+    const [api, queryPlanner] = composeAndCreatePlannerWithOptions(
+      subgraphs,
+      config,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v1
+            v2
+            v3
+            v4
+          }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -6405,18 +7307,24 @@ describe('`debug.maxEvaluatedPlans` configuration', () => {
   });
 
   test('allows setting down to 1', () => {
-    const config = { debug : { maxEvaluatedPlans : 1 } };
-    const [api, queryPlanner] = composeAndCreatePlannerWithOptions(subgraphs, config);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v1
-          v2
-          v3
-          v4
+    const config = { debug: { maxEvaluatedPlans: 1 } };
+    const [api, queryPlanner] = composeAndCreatePlannerWithOptions(
+      subgraphs,
+      config,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v1
+            v2
+            v3
+            v4
+          }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     // Note that in theory, the planner would be excused if it wasn't generated this
@@ -6447,18 +7355,24 @@ describe('`debug.maxEvaluatedPlans` configuration', () => {
   });
 
   test('can be set to an arbitrary number', () => {
-    const config = { debug : { maxEvaluatedPlans : 10 } };
-    const [api, queryPlanner] = composeAndCreatePlannerWithOptions(subgraphs, config);
-    const operation = operationFromDocument(api, gql`
-      {
-        t {
-          v1
-          v2
-          v3
-          v4
+    const config = { debug: { maxEvaluatedPlans: 10 } };
+    const [api, queryPlanner] = composeAndCreatePlannerWithOptions(
+      subgraphs,
+      config,
+    );
+    const operation = operationFromDocument(
+      api,
+      gql`
+        {
+          t {
+            v1
+            v2
+            v3
+            v4
+          }
         }
-      }
-      `);
+      `,
+    );
 
     const plan = queryPlanner.buildQueryPlan(operation);
     expect(plan).toMatchInlineSnapshot(`
@@ -6484,14 +7398,14 @@ describe('`debug.maxEvaluatedPlans` configuration', () => {
   });
 
   test('cannot be set to 0 or a negative number', () => {
-    let config = { debug : { maxEvaluatedPlans : 0 } };
+    let config = { debug: { maxEvaluatedPlans: 0 } };
     expect(() => composeAndCreatePlannerWithOptions(subgraphs, config)).toThrow(
-      'Invalid value for query planning configuration "debug.maxEvaluatedPlans"; expected a number >= 1 but got 0'
+      'Invalid value for query planning configuration "debug.maxEvaluatedPlans"; expected a number >= 1 but got 0',
     );
 
-    config = { debug : { maxEvaluatedPlans : -1 } };
+    config = { debug: { maxEvaluatedPlans: -1 } };
     expect(() => composeAndCreatePlannerWithOptions(subgraphs, config)).toThrow(
-      'Invalid value for query planning configuration "debug.maxEvaluatedPlans"; expected a number >= 1 but got -1'
+      'Invalid value for query planning configuration "debug.maxEvaluatedPlans"; expected a number >= 1 but got -1',
     );
   });
 });
@@ -6521,8 +7435,8 @@ test('correctly generate plan built from some non-individually optimal branch op
       type T {
         x: Int @shareable
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -6534,8 +7448,8 @@ test('correctly generate plan built from some non-individually optimal branch op
       type T @key(fields: "id") {
         id: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -6545,18 +7459,25 @@ test('correctly generate plan built from some non-individually optimal branch op
         x: Int @shareable
         y: Int
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        x
-        y
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlanner(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          x
+          y
+        }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -6605,8 +7526,8 @@ test('does not error on some complex fetch group dependencies', () => {
       type User {
         id: ID! @shareable
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -6623,8 +7544,8 @@ test('does not error on some complex fetch group dependencies', () => {
       type Props {
         id: ID! @shareable
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -6658,27 +7579,34 @@ test('does not error on some complex fetch group dependencies', () => {
       type V {
         x: Int
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
-  const operation = operationFromDocument(api, gql`
-    {
-      me {
-        p {
-          v0
-          t {
-            v1 {
-              x
-            }
-            v2 {
-              x
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlanner(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        me {
+          p {
+            v0
+            t {
+              v1 {
+                x
+              }
+              v2 {
+                x
+              }
             }
           }
         }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -6722,7 +7650,6 @@ test('does not error on some complex fetch group dependencies', () => {
   `);
 });
 
-
 test('does not evaluate plans relying on a key field to fetch that same field', () => {
   const subgraph1 = {
     name: 'Subgraph1',
@@ -6734,8 +7661,8 @@ test('does not evaluate plans relying on a key field to fetch that same field',
       type T @key(fields: "otherId") {
         otherId: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph2 = {
     name: 'Subgraph2',
@@ -6744,8 +7671,8 @@ test('does not evaluate plans relying on a key field to fetch that same field',
         id: ID!
         otherId: ID!
       }
-    `
-  }
+    `,
+  };
 
   const subgraph3 = {
     name: 'Subgraph3',
@@ -6753,17 +7680,24 @@ test('does not evaluate plans relying on a key field to fetch that same field',
       type T @key(fields: "id") {
         id: ID!
       }
-    `
-  }
-
-  const [api, queryPlanner] = composeAndCreatePlanner(subgraph1, subgraph2, subgraph3);
-  const operation = operationFromDocument(api, gql`
-    {
-      t {
-        id
+    `,
+  };
+
+  const [api, queryPlanner] = composeAndCreatePlanner(
+    subgraph1,
+    subgraph2,
+    subgraph3,
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          id
+        }
       }
-    }
-  `);
+    `,
+  );
 
   const plan = queryPlanner.buildQueryPlan(operation);
   expect(plan).toMatchInlineSnapshot(`
@@ -6806,5 +7740,84 @@ test('does not evaluate plans relying on a key field to fetch that same field',
   // this test ensure this is not considered anymore (considering that later plan
   // was not incorrect, but it was adding to the options to evaluate which in some
   // cases could impact query planning performance quite a bit).
-  expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(1);
+  expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(
+    1,
+  );
+});
+
+test('avoid considering indirect paths from the root when a more direct one exists', () => {
+  const subgraph1 = {
+    name: 'Subgraph1',
+    typeDefs: gql`
+      type Query {
+        t: T @shareable
+      }
+
+      type T @key(fields: "id") {
+        id: ID!
+        v0: Int @shareable
+      }
+    `,
+  };
+
+  const subgraph2 = {
+    name: 'Subgraph2',
+    typeDefs: gql`
+      type Query {
+        t: T @shareable
+      }
+
+      type T @key(fields: "id") {
+        id: ID!
+        v0: Int @shareable
+        v1: Int
+      }
+    `,
+  };
+
+  // Each of id/v0 can have 2 options each, so that's 4 combinations. If we were to consider 2 options for each
+  // v1 value however, that would multiple it by 2 each times, so it would 32 possibilities. We limit the number of
+  // evaluated plans just above our expected number of 4 so that if we exceed it, the generated plan will be sub-optimal.
+  const [api, queryPlanner] = composeAndCreatePlannerWithOptions(
+    [subgraph1, subgraph2],
+    { debug: { maxEvaluatedPlans: 6 } },
+  );
+  const operation = operationFromDocument(
+    api,
+    gql`
+      {
+        t {
+          id
+          v0
+          a0: v1
+          a1: v1
+          a2: v1
+        }
+      }
+    `,
+  );
+
+  const plan = queryPlanner.buildQueryPlan(operation);
+  expect(plan).toMatchInlineSnapshot(`
+    QueryPlan {
+      Fetch(service: "Subgraph2") {
+        {
+          t {
+            a0: v1
+            a1: v1
+            a2: v1
+            id
+            v0
+          }
+        }
+      },
+    }
+  `);
+
+  // As said above, we legit have 2 options for `id` and `v0`, and we cannot know which are best before we evaluate the
+  // plans completely. But for the multiple `v1`, we should recognize that going through the 1st subgraph (and taking a
+  // key) is never exactly a good idea.
+  expect(queryPlanner.lastGeneratedPlanStatistics()?.evaluatedPlanCount).toBe(
+    4,
+  );
 });
diff --git a/query-planner-js/src/__tests__/generateAllPlans.test.ts b/query-planner-js/src/__tests__/generateAllPlans.test.ts
index b95f50d34..5215b2f9a 100644
--- a/query-planner-js/src/__tests__/generateAllPlans.test.ts
+++ b/query-planner-js/src/__tests__/generateAllPlans.test.ts
@@ -1,7 +1,10 @@
 import { assert } from '@apollo/federation-internals';
 import { generateAllPlansAndFindBest } from '../generateAllPlans';
 
-function generateTestPlans(initial: string[], choices: string[][]): { best: string[], generated: string[][], evaluated: string[][] } {
+function generateTestPlans(
+  initial: string[],
+  choices: string[][],
+): { best: string[]; generated: string[][]; evaluated: string[][] } {
   const generated: string[][] = [];
   const evaluated: string[][] = [];
   const { best } = generateAllPlansAndFindBest({
@@ -28,38 +31,47 @@ function expectSamePlans(expected: string[][], actual: string[][]) {
   const expectedSet = new Set<string>(expected.map((e) => normalize(e)));
   for (const value of actual) {
     const normalized = normalize(value);
-    assert(expectedSet.has(normalized), `Unexpected plan [${value.join(', ')}] is not in [\n${expected.map((e) => `[ ${e.join(', ')} ]`).join('\n')}\n]`);
+    assert(
+      expectedSet.has(normalized),
+      `Unexpected plan [${value.join(', ')}] is not in [\n${expected
+        .map((e) => `[ ${e.join(', ')} ]`)
+        .join('\n')}\n]`,
+    );
   }
 
   const actualSet = new Set<string>(actual.map((e) => normalize(e)));
   for (const value of expected) {
     const normalized = normalize(value);
-    assert(actualSet.has(normalized), `Expected plan [${value.join(', ')}] not found in [\n${actual.map((e) => `[ ${e.join(', ')} ]`).join('\n')}\n]`);
+    assert(
+      actualSet.has(normalized),
+      `Expected plan [${value.join(', ')}] not found in [\n${actual
+        .map((e) => `[ ${e.join(', ')} ]`)
+        .join('\n')}\n]`,
+    );
   }
 }
 
-
 test('Pick elements at same index first', () => {
   const { best, generated } = generateTestPlans(
-    ['I'], 
+    ['I'],
     [
-      [ 'A1', 'B1'],
-      [ 'A2', 'B2'],
-      [ 'A3', 'B3'],
+      ['A1', 'B1'],
+      ['A2', 'B2'],
+      ['A3', 'B3'],
     ],
   );
   expect(best).toEqual(['I', 'A1', 'A2', 'A3']);
   expect(generated[0]).toEqual(['I', 'A1', 'A2', 'A3']);
   expect(generated[1]).toEqual(['I', 'B1', 'B2', 'B3']);
-})
+});
 
 test('Bail early for more costly elements', () => {
   const { best, generated } = generateTestPlans(
-    ['I'], 
+    ['I'],
     [
-      [ 'A1', 'B1VeryCostly'],
-      [ 'A2', 'B2Co'],
-      [ 'A3', 'B3'],
+      ['A1', 'B1VeryCostly'],
+      ['A2', 'B2Co'],
+      ['A3', 'B3'],
     ],
   );
 
@@ -68,29 +80,27 @@ test('Bail early for more costly elements', () => {
   expect(generated).toHaveLength(2);
   expect(generated[0]).toEqual(['I', 'A1', 'A2', 'A3']);
   expect(generated[1]).toEqual(['I', 'A1', 'A2', 'B3']);
-})
+});
 
 test('Handles branches of various sizes', () => {
   const { best, generated } = generateTestPlans(
-    ['I'], 
-    [
-      [ 'A1x', 'B1'],
-      [ 'A2', 'B2Costly', 'C2'],
-      [ 'A3'],
-      [ 'A4', 'B4' ],
-    ],
+    ['I'],
+    [['A1x', 'B1'], ['A2', 'B2Costly', 'C2'], ['A3'], ['A4', 'B4']],
   );
 
   expect(best).toEqual(['I', 'B1', 'A2', 'A3', 'A4']);
   // We should generate every option, except those including `B2Costly`
-  expectSamePlans([
-    [ 'I', 'A1x', 'A2', 'A3', 'A4' ],
-    [ 'I', 'A1x', 'A2', 'A3', 'B4' ],
-    [ 'I', 'A1x', 'C2', 'A3', 'A4' ],
-    [ 'I', 'A1x', 'C2', 'A3', 'B4' ],
-    [ 'I', 'B1', 'A2', 'A3', 'A4' ],
-    [ 'I', 'B1', 'A2', 'A3', 'B4' ],
-    [ 'I', 'B1', 'C2', 'A3', 'A4' ],
-    [ 'I', 'B1', 'C2', 'A3', 'B4' ],
-  ], generated);
-})
+  expectSamePlans(
+    [
+      ['I', 'A1x', 'A2', 'A3', 'A4'],
+      ['I', 'A1x', 'A2', 'A3', 'B4'],
+      ['I', 'A1x', 'C2', 'A3', 'A4'],
+      ['I', 'A1x', 'C2', 'A3', 'B4'],
+      ['I', 'B1', 'A2', 'A3', 'A4'],
+      ['I', 'B1', 'A2', 'A3', 'B4'],
+      ['I', 'B1', 'C2', 'A3', 'A4'],
+      ['I', 'B1', 'C2', 'A3', 'B4'],
+    ],
+    generated,
+  );
+});
diff --git a/query-planner-js/src/__tests__/supergraphBackwardCompatibility.test.ts b/query-planner-js/src/__tests__/supergraphBackwardCompatibility.test.ts
index 00b83a728..d9d48cf64 100644
--- a/query-planner-js/src/__tests__/supergraphBackwardCompatibility.test.ts
+++ b/query-planner-js/src/__tests__/supergraphBackwardCompatibility.test.ts
@@ -1,5 +1,12 @@
 import { composeServices } from '@apollo/composition';
-import { asFed2SubgraphDocument, assert, isDefined, operationFromDocument, Schema, Supergraph } from '@apollo/federation-internals';
+import {
+  asFed2SubgraphDocument,
+  assert,
+  isDefined,
+  operationFromDocument,
+  Schema,
+  Supergraph,
+} from '@apollo/federation-internals';
 import fs from 'fs';
 import gql from 'graphql-tag';
 import path from 'path';
@@ -16,7 +23,6 @@ import { astSerializer, queryPlanSerializer, QueryPlanner } from '..';
  *
  */
 
-
 const accounts = {
   name: 'accounts',
   typeDefs: gql`
@@ -32,8 +38,8 @@ const accounts = {
       password: String
       nickname: String @override(from: "reviews")
     }
-  `
-}
+  `,
+};
 
 const products = {
   name: 'products',
@@ -59,7 +65,7 @@ const products = {
       rating: Int @external
     }
 
-    type Movie implements Product @key(fields: "id")  {
+    type Movie implements Product @key(fields: "id") {
       id: ID!
       price: Price
       title: String
@@ -92,8 +98,8 @@ const products = {
       USD
       EUR
     }
-  `
-}
+  `,
+};
 
 const reviews = {
   name: 'reviews',
@@ -130,8 +136,8 @@ const reviews = {
       id: ID!
       reviews: [Review]
     }
-  `
-}
+  `,
+};
 
 const testSupergraphDir = path.join(__dirname, 'testSupergraphs');
 
@@ -140,33 +146,47 @@ function testSupergraphPath(v: string): fs.PathLike {
 }
 
 export function generateTestSupergraph(v: string) {
-  const services = [ accounts, products, reviews ];
-  const res =  composeServices(
-    services.map((s) => ({ ...s, typeDefs: asFed2SubgraphDocument(s.typeDefs) }))
+  const services = [accounts, products, reviews];
+  const res = composeServices(
+    services.map((s) => ({
+      ...s,
+      typeDefs: asFed2SubgraphDocument(s.typeDefs),
+    })),
+  );
+  assert(
+    !res.errors,
+    `Expected to compose but got errors:\n${res.errors?.join('\n\n')}`,
   );
-  assert(!res.errors, `Expected to compose but got errors:\n${res.errors?.join('\n\n')}`);
   fs.writeFileSync(testSupergraphPath(v), res.supergraphSdl);
 }
 
 type TestSupergraph = {
-  version: string,
-  supergraph: Supergraph,
-  api: Schema,
-}
+  version: string;
+  supergraph: Supergraph;
+  api: Schema;
+};
 
 function listTestSupergraphs(): TestSupergraph[] {
-  return fs.readdirSync(testSupergraphDir).map((file) => {
-    if (!file.startsWith('testSupergraph_')) {
-      return undefined;
-    }
-    const version = file.slice('testSupergraph_'.length, file.length - '.graphql'.length);
-    const supergraph = Supergraph.build(fs.readFileSync(path.join(testSupergraphDir, file), 'utf8'));
-    return {
-      version,
-      supergraph,
-      api: supergraph.apiSchema(),
-    };
-  }).filter(isDefined);
+  return fs
+    .readdirSync(testSupergraphDir)
+    .map((file) => {
+      if (!file.startsWith('testSupergraph_')) {
+        return undefined;
+      }
+      const version = file.slice(
+        'testSupergraph_'.length,
+        file.length - '.graphql'.length,
+      );
+      const supergraph = Supergraph.build(
+        fs.readFileSync(path.join(testSupergraphDir, file), 'utf8'),
+      );
+      return {
+        version,
+        supergraph,
+        api: supergraph.apiSchema(),
+      };
+    })
+    .filter(isDefined);
 }
 
 // This file is loaded by the `genTestSupergraph.ts` script to generate the test supergraph for the current version.
@@ -180,27 +200,32 @@ if (typeof describe !== 'undefined') {
 
     const toTest = listTestSupergraphs();
 
-    describe.each(toTest)(`handles supergraphs from $version`, ({supergraph, api}) => {
-      let qp: QueryPlanner;
-
-      test('can extract subgraphs and build the query planner', () => {
-        qp = new QueryPlanner(supergraph);
-      });
-
-      test('can execute "me" query', () => {
-        const operation = operationFromDocument(api, gql`
-          {
-            me {
-              nickname
-              reviews {
-                rating
+    describe.each(toTest)(
+      `handles supergraphs from $version`,
+      ({ supergraph, api }) => {
+        let qp: QueryPlanner;
+
+        test('can extract subgraphs and build the query planner', () => {
+          qp = new QueryPlanner(supergraph);
+        });
+
+        test('can execute "me" query', () => {
+          const operation = operationFromDocument(
+            api,
+            gql`
+              {
+                me {
+                  nickname
+                  reviews {
+                    rating
+                  }
+                }
               }
-            }
-          }
-        `);
+            `,
+          );
 
-        const plan = qp.buildQueryPlan(operation);
-        expect(plan).toMatchInlineSnapshot(`
+          const plan = qp.buildQueryPlan(operation);
+          expect(plan).toMatchInlineSnapshot(`
           QueryPlan {
             Parallel {
               Fetch(service: "accounts") {
@@ -222,34 +247,37 @@ if (typeof describe !== 'undefined') {
             },
           }
         `);
-      });
-
-      test('can execute "bestRatedProducts" query', () => {
-        const operation = operationFromDocument(api, gql`
-          {
-            bestRatedProducts(limit: 10) {
-              reviews {
-                author {
-                  nickname
+        });
+
+        test('can execute "bestRatedProducts" query', () => {
+          const operation = operationFromDocument(
+            api,
+            gql`
+              {
+                bestRatedProducts(limit: 10) {
+                  reviews {
+                    author {
+                      nickname
+                    }
+                    rating
+                  }
+                  price {
+                    value
+                    currency
+                  }
+                  ... on Movie {
+                    length_minutes
+                  }
+                  ... on Book {
+                    avg_rating
+                  }
                 }
-                rating
-              }
-              price {
-                value
-                currency
-              }
-              ... on Movie {
-                length_minutes
               }
-              ... on Book {
-                avg_rating
-              }
-            }
-          }
-        `);
+            `,
+          );
 
-        const plan = qp.buildQueryPlan(operation);
-        expect(plan).toMatchInlineSnapshot(`
+          const plan = qp.buildQueryPlan(operation);
+          expect(plan).toMatchInlineSnapshot(`
           QueryPlan {
             Sequence {
               Fetch(service: "reviews") {
@@ -330,7 +358,8 @@ if (typeof describe !== 'undefined') {
             },
           }
         `);
-      });
-    });
+        });
+      },
+    );
   });
 }
diff --git a/query-planner-js/src/buildPlan.ts b/query-planner-js/src/buildPlan.ts
index bcf9c42f5..d677c03a6 100644
--- a/query-planner-js/src/buildPlan.ts
+++ b/query-planner-js/src/buildPlan.ts
@@ -60,6 +60,7 @@ import {
   possibleRuntimeTypes,
   typesCanBeMerged,
   Supergraph,
+  sameType,
 } from "@apollo/federation-internals";
 import {
   advanceSimultaneousPathsWithOperation,
@@ -1109,6 +1110,15 @@ class FetchGroup {
       return true;
     }
 
+    // If we do have inputs, then we first look at the path to `maybeParent`, and it needs to be
+    // essentially empty, "essentially" is because path can sometimes have some leading fragment(s)
+    // and those are fine to ignore. But if the path has some field, then this implies that the inputs
+    // of `this` are based on something at a deeper level than those of `maybeParent`, and the "contains"
+    // comparison we do below would not make sense.
+    if (relation.path.some((elt) => elt.kind === 'Field')) {
+      return false;
+    }
+
     // In theory, the most general test we could have here is to check if `this.inputs` "intersects"
     // `maybeParent.selection. As if it doesn't, we know our inputs don't depend on anything the
     // parent fetches. However, selection set intersection is a bit tricky to implement (due to fragments,
@@ -1217,9 +1227,39 @@ class FetchGroup {
       return undefined;
     }
 
+    // This condition is specific to the case where we're resolving the _concrete_
+    // `__typename` field of an interface when coming from an interfaceObject type.
+    // i.e. { ... on Product { __typename id }} => { ... on Product { __typename} }
+    // This is usually useless at a glance, but in this case we need to actually
+    // keep this since this is our only path to resolving the concrete `__typename`.
+    const isInterfaceTypeConditionOnInterfaceObject = (
+      selection: Selection
+    ): boolean => {
+      if (selection.kind === "FragmentSelection") {
+        const parentType = selection.element.typeCondition;
+        if (parentType && isInterfaceType(parentType)) {
+          // Lastly, we just need to check that we're coming from a subgraph
+          // that has the type as an interface object in its schema.
+          return this.parents().some((p) => {
+            const typeInParent = this.dependencyGraph.subgraphSchemas
+              .get(p.group.subgraphName)
+              ?.type(parentType.name);
+            return typeInParent && isInterfaceObjectType(typeInParent);
+          });
+        }
+      }
+      return false;
+    };
+
     const inputSelections = this.inputs.selectionSets().flatMap((s) => s.selections());
     // Checks that every selection is contained in the input selections.
     const isUseless = this.selection.selections().every((selection) => {
+      // If we're coming from an interfaceObject _to_ an interface, we're
+      // "resolving" the concrete type of the interface and don't want to treat
+      // this as useless.
+      if (isInterfaceTypeConditionOnInterfaceObject(selection)) {
+        return false;
+      }
       const conditionInSupergraph = conditionInSupergraphIfInterfaceObject(selection);
       if (!conditionInSupergraph) {
         // We're not in the @interfaceObject case described above. We just check that an input selection contains the
@@ -1677,7 +1717,6 @@ function withFieldAliased(selectionSet: SelectionSet, aliases: FieldToAlias[]):
 }
 
 class DeferredInfo {
-
   private constructor(
     readonly label: string,
     readonly path: GroupPath,
@@ -2397,13 +2436,16 @@ class FetchDependencyGraph {
     }
 
     if (group.isUseless()) {
-      // In general, removing a group is a bit tricky because we need to deal with the fact
-      // that the group can have multiple parents and children and no break the "path in parent"
-      // in all those cases. To keep thing relatively easily, we only handle the following
-      // cases (other cases will remain non-optimal, but hopefully this handle all the cases
-      // we care about in practice):
-      //   1. if the group has no children. In which case we can just remove it with no ceremony.
-      //   2. if the group has only a single parent and we have a path to that parent.
+      // In general, removing a group is a bit tricky because we need to deal
+      // with the fact that the group can have multiple parents, and we don't
+      // have the "path in parent" in all cases. To keep thing relatively
+      // easily, we only handle the following cases (other cases will remain
+      // non-optimal, but hopefully this handle all the cases we care about in
+      // practice):
+      //   1. if the group has no children. In which case we can just remove it
+      //      with no ceremony.
+      //   2. if the group has only a single parent and we have a path to that
+      //      parent.
       if (group.children().length === 0) {
         this.remove(group);
       } else {
@@ -4042,7 +4084,7 @@ function computeGroupsForTree(
             deferContext: updatedDeferContext
           };
           if (conditions) {
-            // We have some @requires.
+            // We have @requires or some other dependency to create groups for.
             const requireResult = handleRequires(
               dependencyGraph,
               edge,
@@ -4457,7 +4499,24 @@ function handleRequires(
       subgraphName: group.subgraphName,
       mergeAt: path.inResponse(),
     });
-    newGroup.addParents(createdGroups.map((group) => ({ group })));
+    newGroup.addParents(
+      createdGroups.map((group) => ({
+        group,
+        // Usually, computing the path of our new group into the created groups
+        // is not entirely trivial, but there is at least the relatively common
+        // case where the 2 groups we look at have:
+        // 1) the same `mergeAt`, and
+        // 2) the same parentType; in that case, we can basically infer those 2
+        //    groups apply at the same "place" and so the "path in parent" is
+        //    empty. TODO: it should probably be possible to generalize this by
+        //    checking the `mergeAt` plus analyzing the selection but that
+        //    warrants some reflection...
+        path: sameMergeAt(group.mergeAt, newGroup.mergeAt)
+          && sameType(group.parentType, newGroup.parentType)
+          ? []
+          : undefined,
+      })),
+    );
     addPostRequireInputs(
       dependencyGraph,
       path,
diff --git a/renovate.json5 b/renovate.json5
index d14e3cd1b..9c06d2f1d 100644
--- a/renovate.json5
+++ b/renovate.json5
@@ -145,6 +145,41 @@
     {
       "matchPackageNames": ["@typescript-eslint/eslint-plugin"],
       "allowedVersions": "5.x",
-    }
+    },
+    // make-fetch-happen@12 drops support for node 14
+    {
+      "matchPackageNames": ["make-fetch-happen"],
+      "allowedVersions": "11.x",
+    },
+    // make-fetch-happen@11 drops support for node 12
+    {
+      "matchBaseBranches": ["version-0.x"],
+      "matchPackageNames": ["make-fetch-happen"],
+      "allowedVersions": "10.x",
+    },
+    // cspell@7 drops support for node 14
+    {
+      "matchPackageNames": ["cspell"],
+      "allowedVersions": "6.x",
+    },
+    // @apollo/cache-control-types@1.0.3 accidentally broke support for node 12
+    {
+      "matchBaseBranches": ["version-0.x"],
+      "matchPackageNames": ["@apollo/cache-control-types"],
+      "allowedVersions": "1.0.2",
+    },
+    // typescript@5.1 drops support for node 12
+    {
+      "matchBaseBranches": ["version-0.x"],
+      "matchPackageNames": ["typescript"],
+      "allowedVersions": "5.0.x",
+    },
+    // lru-cache@7.13.1 is the last patch which includes an important AbortSignal type as part of its polyfill
+    // this was a breaking change for node 12 support
+    {
+      "matchBaseBranches": ["version-0.x"],
+      "matchPackageNames": ["lru-cache"],
+      "allowedVersions": "7.13.1",
+    },
   ]
 }
diff --git a/scripts/update-next-tags.mjs b/scripts/update-next-tags.mjs
index aa50617b7..f06f8f582 100644
--- a/scripts/update-next-tags.mjs
+++ b/scripts/update-next-tags.mjs
@@ -1,10 +1,13 @@
 // @ts-check
 import { exec } from "child_process";
+import { promisify } from "util";
 import { readFileSync } from "fs";
 import fetch from "node-fetch";
 import { resolve } from "path";
 import semver from "semver";
 
+const asyncExec = promisify(exec);
+
 let statusCode = 0;
 // Collect all the packages that we publish
 const workspaces = JSON.parse(
@@ -37,14 +40,14 @@ await Promise.all(
       const command = `npm dist-tag add ${pkg}@${mostRecentVersion} next`;
       if (nextVersion !== mostRecentVersion) {
         console.log(`\`next\` tag is behind, updating...`);
-        exec(command, (e) => {
-          if (e) {
-            console.error(e);
-            throw e;
-          } else {
-            console.log("`next` tag updated successfully!");
-          }
-        });
+        try {
+          const { stdout, stderr } = await asyncExec(command)
+          if (stderr) console.error(stderr);
+          if (stdout) console.log(stdout);
+        } catch (e) {
+          console.error(e);
+          throw e;
+        }
       } else {
         console.log(
           "No action needed, `next` tag is pointed to most recent version"
diff --git a/subgraph-js/CHANGELOG.md b/subgraph-js/CHANGELOG.md
index 326f7c99c..cac11d486 100644
--- a/subgraph-js/CHANGELOG.md
+++ b/subgraph-js/CHANGELOG.md
@@ -1,5 +1,37 @@
 # CHANGELOG for `@apollo/subgraph`
 
+## 2.5.5
+### Patch Changes
+
+
+- Fix specific case for requesting __typename on interface entity type ([#2775](https://github.com/apollographql/federation/pull/2775))
+  
+  In certain cases, when resolving a __typename on an interface entity (due to it actual being requested in the operation), that fetch group could previously be trimmed / treated as useless. At a glance, it appears to be a redundant step, i.e.:
+  ```
+  { ... on Product { __typename id }} => { ... on Product { __typename} }
+  ```
+  It's actually necessary to preserve this in the case that we're coming from an interface object to an (entity) interface so that we can resolve the concrete __typename correctly.
+- Updated dependencies []:
+  - @apollo/federation-internals@2.5.5
+
+## 2.5.4
+### Patch Changes
+
+- Updated dependencies []:
+  - @apollo/federation-internals@2.5.4
+
+## 2.5.3
+### Patch Changes
+
+- Updated dependencies [[`4b9a512b`](https://github.com/apollographql/federation/commit/4b9a512b62e02544d7854fa198942aac33b93feb), [`c6e0e76d`](https://github.com/apollographql/federation/commit/c6e0e76dbc62662c2aa6ff7f657e374047b11255), [`1add932c`](https://github.com/apollographql/federation/commit/1add932c5cd1297853fb5af9a3a6aaa71243f63a)]:
+  - @apollo/federation-internals@2.5.3
+
+## 2.5.2
+### Patch Changes
+
+- Updated dependencies [[`35179f08`](https://github.com/apollographql/federation/commit/35179f086ce973e9ae7bb455f7ea7d73cdc10f69)]:
+  - @apollo/federation-internals@2.5.2
+
 ## 2.5.1
 ### Patch Changes
 
diff --git a/subgraph-js/package.json b/subgraph-js/package.json
index 46150c19d..b84e5888e 100644
--- a/subgraph-js/package.json
+++ b/subgraph-js/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@apollo/subgraph",
-  "version": "2.5.1",
+  "version": "2.5.5",
   "description": "Apollo Subgraph Utilities",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",
@@ -25,7 +25,7 @@
   },
   "dependencies": {
     "@apollo/cache-control-types": "^1.0.2",
-    "@apollo/federation-internals": "2.5.1"
+    "@apollo/federation-internals": "2.5.5"
   },
   "peerDependencies": {
     "graphql": "^16.5.0"
diff --git a/subgraph-js/src/__tests__/buildSubgraphSchema.test.ts b/subgraph-js/src/__tests__/buildSubgraphSchema.test.ts
index be3e7b732..81ee9cf3c 100644
--- a/subgraph-js/src/__tests__/buildSubgraphSchema.test.ts
+++ b/subgraph-js/src/__tests__/buildSubgraphSchema.test.ts
@@ -862,7 +862,8 @@ describe('buildSubgraphSchema', () => {
       directiveDefinitions: string,
       typeDefinitions: string,
     ) => {
-      const schema = buildSubgraphSchema(gql`${header}
+      const schema = buildSubgraphSchema(gql`
+        ${header}
         type User @key(fields: "email") @tag(name: "tagOnType") {
           email: String @tag(name: "tagOnField")
         }
@@ -881,8 +882,8 @@ describe('buildSubgraphSchema', () => {
           ? ''
           : `
         ${header.trim()}
-        `)
-        + `
+        `) +
+          `
         ${directiveDefinitions.trim()}
 
         type User
@@ -903,13 +904,15 @@ describe('buildSubgraphSchema', () => {
          = User
 
         ${typeDefinitions.trim()}
-      `);
+      `,
+      );
     };
 
-    it.each([{
-      name: 'fed1',
-      header: '',
-      directiveDefinitions: `
+    it.each([
+      {
+        name: 'fed1',
+        header: '',
+        directiveDefinitions: `
         directive @key(fields: _FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
 
         directive @requires(fields: _FieldSet!) on FIELD_DEFINITION
@@ -922,7 +925,7 @@ describe('buildSubgraphSchema', () => {
 
         directive @extends on OBJECT | INTERFACE
       `,
-      typesDefinitions: `
+        typesDefinitions: `
         scalar _FieldSet
 
         scalar _Any
@@ -938,14 +941,15 @@ describe('buildSubgraphSchema', () => {
           _service: _Service!
         }
       `,
-    }, {
-      name: 'fed2',
-      header: `
+      },
+      {
+        name: 'fed2',
+        header: `
         extend schema
           @link(url: "https://specs.apollo.dev/link/v1.0")
           @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@tag"])
       `,
-      directiveDefinitions: `
+        directiveDefinitions: `
         directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA
 
         directive @key(fields: federation__FieldSet!, resolvable: Boolean = true) repeatable on OBJECT | INTERFACE
@@ -966,7 +970,7 @@ describe('buildSubgraphSchema', () => {
 
         directive @federation__override(from: String!) on FIELD_DEFINITION
       `,
-      typesDefinitions: `
+        typesDefinitions: `
         enum link__Purpose {
           """
           \`SECURITY\` features provide metadata necessary to securely resolve fields.
@@ -996,9 +1000,13 @@ describe('buildSubgraphSchema', () => {
           _service: _Service!
         }
       `,
-    }])('adds it for $name schema', async ({header, directiveDefinitions, typesDefinitions}) => {
-      await validateTag(header, directiveDefinitions, typesDefinitions);
-    });
+      },
+    ])(
+      'adds it for $name schema',
+      async ({ header, directiveDefinitions, typesDefinitions }) => {
+        await validateTag(header, directiveDefinitions, typesDefinitions);
+      },
+    );
   });
 
   it(`fails on bad linking`, () => {
@@ -1006,8 +1014,10 @@ describe('buildSubgraphSchema', () => {
       buildSubgraphSchema(gql`
         extend schema
           @link(url: "https://specs.apollo.dev/link/v1.0")
-          @link(url: "https://specs.apollo.dev/federation/v2.0",
-            import: [ { name: "@key", as: "@primaryKey" } ])
+          @link(
+            url: "https://specs.apollo.dev/federation/v2.0"
+            import: [{ name: "@key", as: "@primaryKey" }]
+          )
 
         type Query {
           t: T
@@ -1016,10 +1026,10 @@ describe('buildSubgraphSchema', () => {
         type T @key(fields: "id") {
           id: ID!
         }
-        `);
+      `);
     } catch (e) {
       expect(errorCauses(e)?.map((e) => e.message)).toStrictEqual([
-        'Unknown directive "@key". If you meant the "@key" federation directive, you should use "@primaryKey" as it is imported under that name in the @link to the federation specification of this schema.'
+        'Unknown directive "@key". If you meant the "@key" federation directive, you should use "@primaryKey" as it is imported under that name in the @link to the federation specification of this schema.',
       ]);
     }
   });
@@ -1047,9 +1057,12 @@ describe('buildSubgraphSchema', () => {
             {
               kind: Kind.OPERATION_TYPE_DEFINITION,
               operation: OperationTypeNode.QUERY,
-              type: { kind: Kind.NAMED_TYPE, name: { kind: Kind.NAME, value: 'Query' } }
+              type: {
+                kind: Kind.NAMED_TYPE,
+                name: { kind: Kind.NAME, value: 'Query' },
+              },
             },
-          ]
+          ],
         },
         {
           kind: Kind.OBJECT_TYPE_DEFINITION,
@@ -1060,20 +1073,19 @@ describe('buildSubgraphSchema', () => {
               name: { kind: Kind.NAME, value: 'test' },
               type: {
                 kind: Kind.NAMED_TYPE,
-                name: { kind: Kind.NAME, value: 'String' }
+                name: { kind: Kind.NAME, value: 'String' },
               },
             },
-          ]
+          ],
         },
       ],
     };
 
-
     expect(() => buildSubgraphSchema(doc)).not.toThrow();
   });
 
   it('correctly attaches the provided subscribe function to the schema object', () => {
-    async function* subscribeFn () {
+    async function* subscribeFn() {
       for await (const word of ['Hello', 'Bonjour', 'Ciao']) {
         yield word;
       }
@@ -1123,18 +1135,20 @@ describe('buildSubgraphSchema', () => {
         type Query {
           x: Int
         }
-      `)
+      `);
 
       const { data, errors } = await graphql({ schema, source: query });
       expect(errors).toBeUndefined();
       expect((data?._service as any).sdl).toMatchString(expectedOutput);
-    }
+    };
 
     it('expands federation 2.0 correctly', async () => {
       // For 2.0, we expect in particular that:
       // - the @composeDirective directive is *not* present
       // - the @shareable directive is *not* repeatable
-      await testVersion('2.0', `
+      await testVersion(
+        '2.0',
+        `
         schema
           @link(url: \"https://specs.apollo.dev/link/v1.0\")
         {
@@ -1190,14 +1204,17 @@ describe('buildSubgraphSchema', () => {
         type _Service {
           sdl: String
         }
-      `);
+      `,
+      );
     });
 
     it('expands federation 2.1 correctly', async () => {
       // For 2.1, we expect in particular that:
       // - the @composeDirective directive to exists
       // - the @shareable directive is *not* repeatable
-      await testVersion('2.1', `
+      await testVersion(
+        '2.1',
+        `
         schema
           @link(url: \"https://specs.apollo.dev/link/v1.0\")
         {
@@ -1255,13 +1272,16 @@ describe('buildSubgraphSchema', () => {
         type _Service {
           sdl: String
         }
-      `);
+      `,
+      );
     });
 
     it('expands federation 2.2 correctly', async () => {
       // For 2.2, we expect everything from 2.1 plus:
       // - the @shareable directive to be repeatable
-      await testVersion('2.2', `
+      await testVersion(
+        '2.2',
+        `
         schema
           @link(url: \"https://specs.apollo.dev/link/v1.0\")
         {
@@ -1319,14 +1339,17 @@ describe('buildSubgraphSchema', () => {
         type _Service {
           sdl: String
         }
-      `);
+      `,
+      );
     });
 
     it('expands federation 2.3 correctly', async () => {
       // For 2.3, we expect in everything from 2.2 plus:
       // - the @interfaceObject directive
       // - the @tag directive to additionally have the SCHEMA location
-      await testVersion('2.3', `
+      await testVersion(
+        '2.3',
+        `
         schema
           @link(url: \"https://specs.apollo.dev/link/v1.0\")
         {
@@ -1386,7 +1409,8 @@ describe('buildSubgraphSchema', () => {
         type _Service {
           sdl: String
         }
-      `);
+      `,
+      );
     });
   });
 });
diff --git a/subgraph-js/src/types.ts b/subgraph-js/src/types.ts
index f8964680a..13333e234 100644
--- a/subgraph-js/src/types.ts
+++ b/subgraph-js/src/types.ts
@@ -57,26 +57,41 @@ function isPromise<T>(value: PromiseOrValue<T>): value is Promise<T> {
   return typeof (value as {then?: unknown})?.then === 'function';
 }
 
-function addTypeNameToPossibleReturn<T>(
-  maybeObject: null | T,
+async function maybeAddTypeNameToPossibleReturn<T extends { __typename?: string }>(
+  maybeObject: PromiseOrValue<null | T>,
   typename: string,
-): null | (T & { __typename: string }) {
-  if (maybeObject !== null && typeof maybeObject === 'object') {
-    Object.defineProperty(maybeObject, '__typename', {
+): Promise<null | T> {
+  const objectOrNull = await maybeObject;
+  if (
+    objectOrNull !== null
+    && typeof objectOrNull === 'object'
+  ) {
+    // If the object already has a __typename assigned, we're "refining" the
+    // type from an interface to an interfaceObject.
+    if ('__typename' in objectOrNull && objectOrNull.__typename !== typename) {
+      // XXX There's a really interesting nuance here in this condition. At a
+      // first glance, it looks like the code here and below could be simplified
+      // to just:
+      // ```
+      // objectOrNull.__typename = typename;
+      // return objectOrNull;
+      // ```
+      // But in this case, something internal to `graphql-js` depends on the
+      // identity of the object returned here. If we mutate in this case, we end
+      // up with errors from `graphql-js`. This might be worth investigating at
+      // some point, but for now creating a new object solves the problem and
+      // doesn't create any new ones.
+      return {
+        ...objectOrNull,
+        __typename: typename,
+      };
+    }
+
+    Object.defineProperty(objectOrNull, '__typename', {
       value: typename,
     });
   }
-  return maybeObject as null | (T & { __typename: string });
-}
-
-function maybeAddTypeNameToPossibleReturn<T>(
-  maybeObject: PromiseOrValue<null | T>,
-  typename: string,
-): PromiseOrValue<null | (T & { __typename?: string })> {
-  if (isPromise(maybeObject)) {
-    return maybeObject.then((x: any) => addTypeNameToPossibleReturn(x, typename));
-  }
-  return addTypeNameToPossibleReturn(maybeObject, typename);
+  return objectOrNull;
 }
 
 /**