From e61cfe27a6dae17c0540de865a29152825ed7775 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Thu, 2 Oct 2025 16:52:43 +0200 Subject: [PATCH 01/16] bump audit --- packages/federation/package.json | 2 +- yarn.lock | 342 ++++++++++++++++--------------- 2 files changed, 175 insertions(+), 169 deletions(-) diff --git a/packages/federation/package.json b/packages/federation/package.json index f037ec529..87591102a 100644 --- a/packages/federation/package.json +++ b/packages/federation/package.json @@ -60,7 +60,7 @@ "@apollo/subgraph": "^2.11.2", "@types/lodash": "4.17.20", "graphql": "^16.9.0", - "graphql-federation-gateway-audit": "the-guild-org/graphql-federation-gateway-audit#main", + "graphql-federation-gateway-audit": "graphql-hive/federation-gateway-audit#main", "pkgroll": "2.17.0" }, "sideEffects": false diff --git a/yarn.lock b/yarn.lock index efe6a7edb..d220c516a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -165,18 +165,6 @@ __metadata: languageName: node linkType: hard -"@apollo/composition@npm:2.10.2": - version: 2.10.2 - resolution: "@apollo/composition@npm:2.10.2" - dependencies: - "@apollo/federation-internals": "npm:2.10.2" - "@apollo/query-graphs": "npm:2.10.2" - peerDependencies: - graphql: ^16.5.0 - checksum: 10c0/268d9609d5ed45c18bd30699ec6e76a83c3e6ff116d8aaacccc2d8755b1e2b00d8fd1cad6e73c28feae93cb819eded7e47efe1bbc4f41e67233f68215c27a77a - languageName: node - linkType: hard - "@apollo/composition@npm:2.11.2": version: 2.11.2 resolution: "@apollo/composition@npm:2.11.2" @@ -189,20 +177,6 @@ __metadata: languageName: node linkType: hard -"@apollo/federation-internals@npm:2.10.2": - version: 2.10.2 - resolution: "@apollo/federation-internals@npm:2.10.2" - dependencies: - "@types/uuid": "npm:^9.0.0" - chalk: "npm:^4.1.0" - js-levenshtein: "npm:^1.1.6" - uuid: "npm:^9.0.0" - peerDependencies: - graphql: ^16.5.0 - checksum: 10c0/1ba53f6cc64d820d89aba29eb0d8b8e84b98970f06f6e2fd68da2d60a5d829a276beccec4be6ee3a9952c46e0d286d171aa93f17155831a184f330f527d7971c - languageName: node - linkType: hard - "@apollo/federation-internals@npm:2.11.2": version: 2.11.2 resolution: "@apollo/federation-internals@npm:2.11.2" @@ -268,20 +242,6 @@ __metadata: languageName: node linkType: hard -"@apollo/query-graphs@npm:2.10.2": - version: 2.10.2 - resolution: "@apollo/query-graphs@npm:2.10.2" - dependencies: - "@apollo/federation-internals": "npm:2.10.2" - deep-equal: "npm:^2.0.5" - ts-graphviz: "npm:^1.5.4" - uuid: "npm:^9.0.0" - peerDependencies: - graphql: ^16.5.0 - checksum: 10c0/cd804c447f9d84db2e5de108c61c9dab79bda58ee327d8ac3de93a35d7b7136fa1bd487e43b546dfa936959a64542f66e050d5405a683020beb6d3a6e30a8d2e - languageName: node - linkType: hard - "@apollo/query-graphs@npm:2.11.2": version: 2.11.2 resolution: "@apollo/query-graphs@npm:2.11.2" @@ -385,19 +345,7 @@ __metadata: languageName: node linkType: hard -"@apollo/subgraph@npm:2.10.2": - version: 2.10.2 - resolution: "@apollo/subgraph@npm:2.10.2" - dependencies: - "@apollo/cache-control-types": "npm:^1.0.2" - "@apollo/federation-internals": "npm:2.10.2" - peerDependencies: - graphql: ^16.5.0 - checksum: 10c0/ae4f6cdff9d0fc1c9166f3ea289e2092ab8e7a5793743dc4bc8b5a61ff8fcf245f326bb3047891deb28333a0f397a0de7bff34fe2dad75cce17acda471278783 - languageName: node - linkType: hard - -"@apollo/subgraph@npm:^2.11.2": +"@apollo/subgraph@npm:2.11.2, @apollo/subgraph@npm:^2.11.2": version: 2.11.2 resolution: "@apollo/subgraph@npm:2.11.2" dependencies: @@ -5172,7 +5120,7 @@ __metadata: "@whatwg-node/fetch": "npm:^0.10.11" "@whatwg-node/promise-helpers": "npm:^1.3.2" graphql: "npm:^16.9.0" - graphql-federation-gateway-audit: "the-guild-org/graphql-federation-gateway-audit#main" + graphql-federation-gateway-audit: "graphql-hive/federation-gateway-audit#main" pkgroll: "npm:2.17.0" tslib: "npm:^2.8.1" peerDependencies: @@ -5581,19 +5529,49 @@ __metadata: languageName: node linkType: hard -"@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0": - version: 9.3.0 - resolution: "@hapi/hoek@npm:9.3.0" - checksum: 10c0/a096063805051fb8bba4c947e293c664b05a32b47e13bc654c0dd43813a1cec993bdd8f29ceb838020299e1d0f89f68dc0d62a603c13c9cc8541963f0beca055 +"@hapi/address@npm:^5.1.1": + version: 5.1.1 + resolution: "@hapi/address@npm:5.1.1" + dependencies: + "@hapi/hoek": "npm:^11.0.2" + checksum: 10c0/78138effe1e9a36fd12eb42e24adf97f3ca94b9ab1f6db65e3136cf0e5cdbd1578c14adffde18a50d776db2f326a8cb3a16d783b6705b22571a603fa47c8c3d3 languageName: node linkType: hard -"@hapi/topo@npm:^5.1.0": - version: 5.1.0 - resolution: "@hapi/topo@npm:5.1.0" +"@hapi/formula@npm:^3.0.2": + version: 3.0.2 + resolution: "@hapi/formula@npm:3.0.2" + checksum: 10c0/794d30dd13ecc070b9d93e01dacad8175c270f89bcfaa92300f843b7666d915b319d0b792694385d79270c84b52f003a4310f117202303cce3069808751f7a41 + languageName: node + linkType: hard + +"@hapi/hoek@npm:^11.0.2, @hapi/hoek@npm:^11.0.7": + version: 11.0.7 + resolution: "@hapi/hoek@npm:11.0.7" + checksum: 10c0/39a4a3ae9526ed66509f6d03c6eb43179a2590df6e98443328c966cfa5e7cbb9d340f61fdbe0afe092662d5377d5a611c3303c808fee26a9c9cfd6bd3737dc1c + languageName: node + linkType: hard + +"@hapi/pinpoint@npm:^2.0.1": + version: 2.0.1 + resolution: "@hapi/pinpoint@npm:2.0.1" + checksum: 10c0/b3072f2c57c9fa2e44d85168e253e331324158509e1c45dae2676f31555326410345fd2422f890c41201e2783c5e9bb8c7b0bdcf6abe01079742a943b0c300b9 + languageName: node + linkType: hard + +"@hapi/tlds@npm:^1.1.1": + version: 1.1.3 + resolution: "@hapi/tlds@npm:1.1.3" + checksum: 10c0/4c36635eadca2316cec7b0c8acad3ea61f4e598cdcdd8dc777f89e8be96510c4d014e1f7d43ee066dea323d5eb17828cf1ea47fb4785b13159cfeb96c4db5b04 + languageName: node + linkType: hard + +"@hapi/topo@npm:^6.0.2": + version: 6.0.2 + resolution: "@hapi/topo@npm:6.0.2" dependencies: - "@hapi/hoek": "npm:^9.0.0" - checksum: 10c0/b16b06d9357947149e032bdf10151eb71aea8057c79c4046bf32393cb89d0d0f7ca501c40c0f7534a5ceca078de0700d2257ac855c15e59fe4e00bba2f25c86f + "@hapi/hoek": "npm:^11.0.2" + checksum: 10c0/5a0079805e9a542bdb852912ce6ed3221ddd0a58569354b3900e165faabe50fb1d2d488067db97c494194835684b055828b6267be3064ac42cf2ce28db02edfc languageName: node linkType: hard @@ -8514,29 +8492,6 @@ __metadata: languageName: node linkType: hard -"@sideway/address@npm:^4.1.5": - version: 4.1.5 - resolution: "@sideway/address@npm:4.1.5" - dependencies: - "@hapi/hoek": "npm:^9.0.0" - checksum: 10c0/638eb6f7e7dba209053dd6c8da74d7cc995e2b791b97644d0303a7dd3119263bcb7225a4f6804d4db2bc4f96e5a9d262975a014f58eae4d1753c27cbc96ef959 - languageName: node - linkType: hard - -"@sideway/formula@npm:^3.0.1": - version: 3.0.1 - resolution: "@sideway/formula@npm:3.0.1" - checksum: 10c0/3fe81fa9662efc076bf41612b060eb9b02e846ea4bea5bd114f1662b7f1541e9dedcf98aff0d24400bcb92f113964a50e0290b86e284edbdf6346fa9b7e2bf2c - languageName: node - linkType: hard - -"@sideway/pinpoint@npm:^2.0.0": - version: 2.0.0 - resolution: "@sideway/pinpoint@npm:2.0.0" - checksum: 10c0/d2ca75dacaf69b8fc0bb8916a204e01def3105ee44d8be16c355e5f58189eb94039e15ce831f3d544f229889ccfa35562a0ce2516179f3a7ee1bbe0b71e55b36 - languageName: node - linkType: hard - "@sigstore/bundle@npm:^3.1.0": version: 3.1.0 resolution: "@sigstore/bundle@npm:3.1.0" @@ -9156,6 +9111,13 @@ __metadata: languageName: node linkType: hard +"@standard-schema/spec@npm:^1.0.0": + version: 1.0.0 + resolution: "@standard-schema/spec@npm:1.0.0" + checksum: 10c0/a1ab9a8bdc09b5b47aa8365d0e0ec40cc2df6437be02853696a0e377321653b0d3ac6f079a8c67d5ddbe9821025584b1fb71d9cc041a6666a96f1fadf2ece15f + languageName: node + linkType: hard + "@szmarczak/http-timer@npm:^4.0.5": version: 4.0.6 resolution: "@szmarczak/http-timer@npm:4.0.6" @@ -11324,6 +11286,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^6.2.1": + version: 6.2.3 + resolution: "ansi-styles@npm:6.2.3" + checksum: 10c0/23b8a4ce14e18fb854693b95351e286b771d23d8844057ed2e7d083cd3e708376c3323707ec6a24365f7d7eda3ca00327fe04092e29e551499ec4c8b7bfac868 + languageName: node + linkType: hard + "any-promise@npm:^1.0.0": version: 1.3.0 resolution: "any-promise@npm:1.3.0" @@ -11592,7 +11561,7 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.6.5, axios@npm:^1.8.2": +"axios@npm:^1.12.2, axios@npm:^1.6.5": version: 1.12.2 resolution: "axios@npm:1.12.2" dependencies: @@ -12445,6 +12414,17 @@ __metadata: languageName: node linkType: hard +"cliui@npm:^9.0.1": + version: 9.0.1 + resolution: "cliui@npm:9.0.1" + dependencies: + string-width: "npm:^7.2.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/13441832e9efe7c7a76bd2b8e683555c478d461a9f249dc5db9b17fe8d4b47fa9277b503914b90bd00e4a151abb6b9b02b2288972ffe2e5e3ca40bcb1c2330d3 + languageName: node + linkType: hard + "clone-deep@npm:^4.0.1": version: 4.0.1 resolution: "clone-deep@npm:4.0.1" @@ -13162,13 +13142,6 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^29.6.3": - version: 29.6.3 - resolution: "diff-sequences@npm:29.6.3" - checksum: 10c0/32e27ac7dbffdf2fb0eb5a84efd98a9ad084fbabd5ac9abb8757c6770d5320d2acd172830b28c4add29bb873d59420601dfc805ac4064330ce59b1adfd0593b2 - languageName: node - linkType: hard - "diff@npm:^4.0.1": version: 4.0.2 resolution: "diff@npm:4.0.2" @@ -13238,7 +13211,14 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:16.5.0, dotenv@npm:^16.3.1": +"dotenv@npm:17.2.3, dotenv@npm:^17.2.3": + version: 17.2.3 + resolution: "dotenv@npm:17.2.3" + checksum: 10c0/c884403209f713214a1b64d4d1defa4934c2aa5b0002f5a670ae298a51e3c3ad3ba79dfee2f8df49f01ae74290fcd9acdb1ab1d09c7bfb42b539036108bb2ba0 + languageName: node + linkType: hard + +"dotenv@npm:^16.3.1": version: 16.5.0 resolution: "dotenv@npm:16.5.0" checksum: 10c0/5bc94c919fbd955bf0ba44d33922a1e93d1078e64a1db5c30faeded1d996e7a83c55332cb8ea4fae5a9ca4d0be44cbceb95c5811e70f9f095298df09d1997dd9 @@ -13252,13 +13232,6 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:^17.2.3": - version: 17.2.3 - resolution: "dotenv@npm:17.2.3" - checksum: 10c0/c884403209f713214a1b64d4d1defa4934c2aa5b0002f5a670ae298a51e3c3ad3ba79dfee2f8df49f01ae74290fcd9acdb1ab1d09c7bfb42b539036108bb2ba0 - languageName: node - linkType: hard - "dotenv@npm:^8.1.0": version: 8.6.0 resolution: "dotenv@npm:8.6.0" @@ -13340,6 +13313,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.5.0 + resolution: "emoji-regex@npm:10.5.0" + checksum: 10c0/17cf84335a461fc23bf90575122ace2902630dc760e53299474cd3b0b5e4cfbc6c0223a389a766817538e5d20bf0f36c67b753f27c9e705056af510b8777e312 + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -14859,6 +14839,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.4.0 + resolution: "get-east-asian-width@npm:1.4.0" + checksum: 10c0/4e481d418e5a32061c36fbb90d1b225a254cc5b2df5f0b25da215dcd335a3c111f0c2023ffda43140727a9cafb62dac41d022da82c08f31083ee89f714ee3b83 + languageName: node + linkType: hard + "get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": version: 1.3.0 resolution: "get-intrinsic@npm:1.3.0" @@ -15165,26 +15152,26 @@ __metadata: languageName: node linkType: hard -"graphql-federation-gateway-audit@the-guild-org/graphql-federation-gateway-audit#main": +"graphql-federation-gateway-audit@graphql-hive/federation-gateway-audit#main": version: 1.0.0 - resolution: "graphql-federation-gateway-audit@https://github.com/the-guild-org/graphql-federation-gateway-audit.git#commit=c9b2bade89ea1bdaf5b3b5fc385c740cf764bbee" + resolution: "graphql-federation-gateway-audit@https://github.com/graphql-hive/federation-gateway-audit.git#commit=82d615dc4353a02fb39b4086adb964c6c7441b4e" dependencies: - "@apollo/composition": "npm:2.10.2" - "@apollo/subgraph": "npm:2.10.2" + "@apollo/composition": "npm:2.11.2" + "@apollo/subgraph": "npm:2.11.2" async-retry: "npm:1.3.3" detect-port: "npm:2.1.0" - dotenv: "npm:16.5.0" + dotenv: "npm:17.2.3" fets: "npm:0.8.5" get-port: "npm:7.1.0" graphql: "npm:16.11.0" - graphql-yoga: "npm:5.13.4" - jest-diff: "npm:29.7.0" + graphql-yoga: "npm:5.16.0" + jest-diff: "npm:30.2.0" kill-port-process: "npm:3.2.1" - wait-on: "npm:8.0.3" - yargs: "npm:17.7.2" + wait-on: "npm:9.0.1" + yargs: "npm:18.0.0" bin: graphql-federation-audit: ./dist/cli.js - checksum: 10c0/217d66ea0efa2ea1ec3dac854605c0ceae74a39d7b9fe4ecc7d1c6f5bd1048baf5ec827f1612e86feeeb4e967a66730e527674e84d8144bd1fb597fba9c67b71 + checksum: 10c0/d60f0bf837e3eac3e593e0675eb639c42daffce0bac6c40d89637a9caa28d62127d74d3f4eedd4bf16d6146a4aa8697bce5a43f49e7f6ea82e86433d1b67b366 languageName: node linkType: hard @@ -15273,11 +15260,11 @@ __metadata: languageName: node linkType: hard -"graphql-yoga@npm:5.13.4": - version: 5.13.4 - resolution: "graphql-yoga@npm:5.13.4" +"graphql-yoga@npm:5.16.0, graphql-yoga@npm:^5.16.0": + version: 5.16.0 + resolution: "graphql-yoga@npm:5.16.0" dependencies: - "@envelop/core": "npm:^5.2.3" + "@envelop/core": "npm:^5.3.0" "@envelop/instrumentation": "npm:^1.0.0" "@graphql-tools/executor": "npm:^1.4.0" "@graphql-tools/schema": "npm:^10.0.11" @@ -15292,7 +15279,7 @@ __metadata: tslib: "npm:^2.8.1" peerDependencies: graphql: ^15.2.0 || ^16.0.0 - checksum: 10c0/b49085b3878d392436c848b8da075d70ebfe9ec147620a0c614d3075af68ee5f388426d8ee1af875c1bd7807f643261b76cfa3b898184bdcc0ea06af54b0deba + checksum: 10c0/30015acaf6ef987d164f6c4b2c2579992dc3d8f0ddfb6e1a044a781645f9c50740e3e5bd2609885a9c0dbb6868cbb316a2bdbd7af66cfba5ae44baa4c2aa76ea languageName: node linkType: hard @@ -15319,29 +15306,6 @@ __metadata: languageName: node linkType: hard -"graphql-yoga@npm:^5.16.0": - version: 5.16.0 - resolution: "graphql-yoga@npm:5.16.0" - dependencies: - "@envelop/core": "npm:^5.3.0" - "@envelop/instrumentation": "npm:^1.0.0" - "@graphql-tools/executor": "npm:^1.4.0" - "@graphql-tools/schema": "npm:^10.0.11" - "@graphql-tools/utils": "npm:^10.6.2" - "@graphql-yoga/logger": "npm:^2.0.1" - "@graphql-yoga/subscription": "npm:^5.0.5" - "@whatwg-node/fetch": "npm:^0.10.6" - "@whatwg-node/promise-helpers": "npm:^1.2.4" - "@whatwg-node/server": "npm:^0.10.5" - dset: "npm:^3.1.4" - lru-cache: "npm:^10.0.0" - tslib: "npm:^2.8.1" - peerDependencies: - graphql: ^15.2.0 || ^16.0.0 - checksum: 10c0/30015acaf6ef987d164f6c4b2c2579992dc3d8f0ddfb6e1a044a781645f9c50740e3e5bd2609885a9c0dbb6868cbb316a2bdbd7af66cfba5ae44baa4c2aa76ea - languageName: node - linkType: hard - "graphql@npm:16.11.0": version: 16.11.0 resolution: "graphql@npm:16.11.0" @@ -16503,18 +16467,6 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:29.7.0": - version: 29.7.0 - resolution: "jest-diff@npm:29.7.0" - dependencies: - chalk: "npm:^4.0.0" - diff-sequences: "npm:^29.6.3" - jest-get-type: "npm:^29.6.3" - pretty-format: "npm:^29.7.0" - checksum: 10c0/89a4a7f182590f56f526443dde69acefb1f2f0c9e59253c61d319569856c4931eae66b8a3790c443f529267a0ddba5ba80431c585deed81827032b2b2a1fc999 - languageName: node - linkType: hard - "jest-diff@npm:30.2.0": version: 30.2.0 resolution: "jest-diff@npm:30.2.0" @@ -16872,16 +16824,18 @@ __metadata: languageName: node linkType: hard -"joi@npm:^17.13.3": - version: 17.13.3 - resolution: "joi@npm:17.13.3" +"joi@npm:^18.0.1": + version: 18.0.1 + resolution: "joi@npm:18.0.1" dependencies: - "@hapi/hoek": "npm:^9.3.0" - "@hapi/topo": "npm:^5.1.0" - "@sideway/address": "npm:^4.1.5" - "@sideway/formula": "npm:^3.0.1" - "@sideway/pinpoint": "npm:^2.0.0" - checksum: 10c0/9262aef1da3f1bec5b03caf50c46368899fe03b8ff26cbe3d53af4584dd1049079fc97230bbf1500b6149db7cc765b9ee45f0deb24bb6fc3fa06229d7148c17f + "@hapi/address": "npm:^5.1.1" + "@hapi/formula": "npm:^3.0.2" + "@hapi/hoek": "npm:^11.0.7" + "@hapi/pinpoint": "npm:^2.0.1" + "@hapi/tlds": "npm:^1.1.1" + "@hapi/topo": "npm:^6.0.2" + "@standard-schema/spec": "npm:^1.0.0" + checksum: 10c0/b803dd46e31d90d7df92fd4cd62c2129624c9ee0ab289077ec0d8ff3959ea93216a592bf315927a73fafb19bd6eb2ad2ee58d0d8d9c56599bc23cf3de9bba8b6 languageName: node linkType: hard @@ -20939,6 +20893,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.0.0, string-width@npm:^7.2.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + "string.prototype.trim@npm:^1.2.10": version: 1.2.10 resolution: "string.prototype.trim@npm:1.2.10" @@ -21004,6 +20969,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi@npm:^7.1.0": + version: 7.1.2 + resolution: "strip-ansi@npm:7.1.2" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10c0/0d6d7a023de33368fd042aab0bf48f4f4077abdfd60e5393e73c7c411e85e1b3a83507c11af2e656188511475776215df9ca589b4da2295c9455cc399ce1858b + languageName: node + linkType: hard + "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" @@ -22503,18 +22477,18 @@ __metadata: languageName: node linkType: hard -"wait-on@npm:8.0.3": - version: 8.0.3 - resolution: "wait-on@npm:8.0.3" +"wait-on@npm:9.0.1": + version: 9.0.1 + resolution: "wait-on@npm:9.0.1" dependencies: - axios: "npm:^1.8.2" - joi: "npm:^17.13.3" + axios: "npm:^1.12.2" + joi: "npm:^18.0.1" lodash: "npm:^4.17.21" minimist: "npm:^1.2.8" rxjs: "npm:^7.8.2" bin: wait-on: bin/wait-on - checksum: 10c0/7f14086c3bb6fc055207ab591d2faefc045f718aa7c959353a54af05cf08c53c25d9af80d4d5f6934a169cc0d97ebd1dcf024b13583d09b9a935c36bd745bd7b + checksum: 10c0/c90673257cedb50f5e3cb4f4a407d22a0d1c9f79295f04fee91246ed195042229fd45d2d02f331827b06728fa24706e2b8ee5c88c85de2ada87bf4bf17a73fe3 languageName: node linkType: hard @@ -22795,6 +22769,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi@npm:^9.0.0": + version: 9.0.2 + resolution: "wrap-ansi@npm:9.0.2" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/3305839b9a0d6fb930cb63a52f34d3936013d8b0682ff3ec133c9826512620f213800ffa19ea22904876d5b7e9a3c1f40682f03597d986a4ca881fa7b033688c + languageName: node + linkType: hard + "wrappy@npm:1": version: 1.0.2 resolution: "wrappy@npm:1.0.2" @@ -22906,7 +22891,28 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.7.2": +"yargs-parser@npm:^22.0.0": + version: 22.0.0 + resolution: "yargs-parser@npm:22.0.0" + checksum: 10c0/cb7ef81759c4271cb1d96b9351dbbc9a9ce35d3e1122d2b739bf6c432603824fa02c67cc12dcef6ea80283379d63495686e8f41cc7b06c6576e792aba4d33e1c + languageName: node + linkType: hard + +"yargs@npm:18.0.0": + version: 18.0.0 + resolution: "yargs@npm:18.0.0" + dependencies: + cliui: "npm:^9.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + string-width: "npm:^7.2.0" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^22.0.0" + checksum: 10c0/bf290e4723876ea9c638c786a5c42ac28e03c9ca2325e1424bf43b94e5876456292d3ed905b853ebbba6daf43ed29e772ac2a6b3c5fb1b16533245d6211778f3 + languageName: node + linkType: hard + +"yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From 9549b71ff1240c11fb8cdab608ff41b92f009f0a Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 16:06:27 +0200 Subject: [PATCH 02/16] quick fix for requires-circular test 0 --- packages/delegate/src/defaultMergedResolver.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/delegate/src/defaultMergedResolver.ts b/packages/delegate/src/defaultMergedResolver.ts index 54e29586a..cffb594df 100644 --- a/packages/delegate/src/defaultMergedResolver.ts +++ b/packages/delegate/src/defaultMergedResolver.ts @@ -304,13 +304,18 @@ function handleFlattenedParent>( nestedTypeName ]?.resolvers.get(subschema); if (resolver) { + const subschemaTypes = + stitchingInfo.mergedTypes[ + nestedTypeName + ]!.typeMaps.get(subschema)!; + const returnType = subschemaTypes[nestedTypeName]; const res = await resolver( nestedParentItem, context, info, subschema, selectionSet, - info.parentType, + returnType, // returnType info.parentType, ); if (res) { From 16436630911459b9f4d94527efeede4c396a50c2 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 16:57:30 +0200 Subject: [PATCH 03/16] local tests --- ...on-compatibility-requires-circular.test.ts | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 packages/federation/tests/federation-compatibility-requires-circular.test.ts diff --git a/packages/federation/tests/federation-compatibility-requires-circular.test.ts b/packages/federation/tests/federation-compatibility-requires-circular.test.ts new file mode 100644 index 000000000..8b8da64a1 --- /dev/null +++ b/packages/federation/tests/federation-compatibility-requires-circular.test.ts @@ -0,0 +1,211 @@ +import { buildSubgraphSchema } from '@apollo/subgraph'; +import { normalizedExecutor } from '@graphql-tools/executor'; +import { parse, print } from 'graphql'; +import { expect, it } from 'vitest'; +import { getStitchedSchemaFromLocalSchemas } from './getStitchedSchemaFromLocalSchemas'; + +const authors = [ + { + id: 'a1', + name: 'John', + yearsOfExperience: 5, + }, + { + id: 'a2', + name: 'Jane', + yearsOfExperience: 20, + }, +]; + +const posts = [ + { + id: 'p1', + body: 'p1-body', + author: authors[0], + }, + { + id: 'p2', + body: 'p2-body', + author: authors[1], + }, +]; + +const supergraph = await getStitchedSchemaFromLocalSchemas({ + localSchemas: { + a: buildSubgraphSchema([ + { + typeDefs: parse(/* GraphQL */ ` + extend schema + @link( + url: "https://specs.apollo.dev/federation/v2.3" + import: ["@key", "@external", "@requires"] + ) + + type Query { + feed: [Post] + } + + type Post @key(fields: "id") { + id: ID! + byNovice: Boolean! @external + byExpert: Boolean! @requires(fields: "byNovice") + } + + type Author @key(fields: "id") { + id: ID! + name: String! + yearsOfExperience: Int! + } + `), + resolvers: { + Query: { + feed() { + return posts.map((post) => ({ id: post.id })); + }, + }, + Post: { + __resolveReference(ref: { id: string; byNovice?: boolean }) { + const post = posts.find((post) => post.id === ref.id); + if (!post) { + return null; + } + if (ref.byNovice == null) { + return { + id: post.id, + }; + } + return { + id: post.id, + byNovice: ref.byNovice, + }; + }, + byExpert(post: { byNovice: boolean }) { + if (post.byNovice == null) { + // ensuring requires is not skipped + return null; + } + return !post.byNovice; + }, + }, + Author: { + __resolveReference(ref: { id: string }) { + const author = authors.find((author) => author.id === ref.id); + if (!author) { + return null; + } + return { + id: author.id, + name: author.name, + yearsOfExperience: author.yearsOfExperience, + }; + }, + }, + }, + }, + ]), + b: buildSubgraphSchema([ + { + typeDefs: parse(/* GraphQL */ ` + extend schema + @link( + url: "https://specs.apollo.dev/federation/v2.3" + import: ["@key", "@external", "@requires"] + ) + + type Post @key(fields: "id") { + id: ID! + author: Author! + byNovice: Boolean! @requires(fields: "author { yearsOfExperience }") + } + + type Author @key(fields: "id") { + id: ID! + yearsOfExperience: Int! @external + } + `), + resolvers: { + Post: { + __resolveReference(ref: { + id: string; + author?: { yearsOfExperience: number }; + }) { + const post = posts.find((post) => post.id === ref.id); + if (!post) { + return null; + } + return { + id: post.id, + author: { + id: post.author!.id, + ...ref.author, + }, + }; + }, + byNovice(post: { author: { yearsOfExperience: number } }) { + return post.author.yearsOfExperience < 10; + }, + }, + }, + }, + ]), + }, + onSubgraphExecute(subgraph, executionRequest, result) { + const query = print(executionRequest.document); + console.log(query); + // if (subgraph === 'a' && query.includes('_entities')) { + // // debugger; + // } + }, +}); + +it('test query 0', { timeout: 1000 }, async () => { + await expect( + normalizedExecutor({ + schema: supergraph, + document: parse(/* GraphQL */ ` + { + feed { + byNovice + } + } + `), + }), + ).resolves.toEqual({ + data: { + feed: [ + { + byNovice: true, + }, + { + byNovice: false, + }, + ], + }, + }); +}); + +it('test query 1', { timeout: 1000 }, async () => { + await expect( + normalizedExecutor({ + schema: supergraph, + document: parse(/* GraphQL */ ` + { + feed { + byExpert + } + } + `), + }), + ).resolves.toEqual({ + data: { + feed: [ + { + byExpert: false, + }, + { + byExpert: true, + }, + ], + }, + }); +}); From 3f34b5b88fa225174465c3bedbee71ead98646d2 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 17:01:02 +0200 Subject: [PATCH 04/16] no logs --- ...eration-compatibility-requires-circular.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/federation/tests/federation-compatibility-requires-circular.test.ts b/packages/federation/tests/federation-compatibility-requires-circular.test.ts index 8b8da64a1..93278bca2 100644 --- a/packages/federation/tests/federation-compatibility-requires-circular.test.ts +++ b/packages/federation/tests/federation-compatibility-requires-circular.test.ts @@ -149,13 +149,13 @@ const supergraph = await getStitchedSchemaFromLocalSchemas({ }, ]), }, - onSubgraphExecute(subgraph, executionRequest, result) { - const query = print(executionRequest.document); - console.log(query); - // if (subgraph === 'a' && query.includes('_entities')) { - // // debugger; - // } - }, + // onSubgraphExecute(subgraph, executionRequest, result) { + // const query = print(executionRequest.document); + // console.log(query); + // // if (subgraph === 'a' && query.includes('_entities')) { + // // // debugger; + // // } + // }, }); it('test query 0', { timeout: 1000 }, async () => { From ab98a67b6b271e87b945817f38f04bfdcdf4db3c Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 17:22:57 +0200 Subject: [PATCH 05/16] attempt fix --- .../delegate/src/defaultMergedResolver.ts | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/packages/delegate/src/defaultMergedResolver.ts b/packages/delegate/src/defaultMergedResolver.ts index cffb594df..a3709edb6 100644 --- a/packages/delegate/src/defaultMergedResolver.ts +++ b/packages/delegate/src/defaultMergedResolver.ts @@ -94,13 +94,14 @@ export function defaultMergedResolver( if ( fieldNodesByType?.every((fieldNode) => { const responseKey = fieldNode.alias?.value ?? fieldNode.name.value; - if (Object.prototype.hasOwnProperty.call(parent, responseKey)) { - return true; - } - return false; + return Object.prototype.hasOwnProperty.call(parent, responseKey); }) ) { handleResult(parent, responseKey, context, info); + } else { + // not all fields are present, we need to resolve more leftovers + // this can occur when there are circular @requires, see requires-circular audit test + handleLeftOver(parent, context, info, leftOver); } return deferred.promise; } @@ -388,15 +389,48 @@ function handleDeferredResolverResult>( const deferredFields = leftOver.missingFieldsParentDeferredMap.get(leftOverParent); if (deferredFields) { + const stitchingInfo = info.schema.extensions?.[ + 'stitchingInfo' + ] as StitchingInfo; + const parentTypeName = leftOverParent?.__typename || info.parentType.name; + const resolvedKeys = new Set(); for (const [responseKey, deferred] of deferredFields) { - // If the deferred field is resolved, resolve the deferred field - if (Object.prototype.hasOwnProperty.call(resolverResult, responseKey)) { + // after handleResolverResult, check if the field is now in the parent + if (Object.prototype.hasOwnProperty.call(leftOverParent, responseKey)) { + // field was added to parent by handleResolverResult deferred.resolve( handleResult(leftOverParent, responseKey, context, info), ); + resolvedKeys.add(responseKey); + } else { + // check if the required fields for this deferred field are now satisfied + const fieldNodesByType = + stitchingInfo?.fieldNodesByField?.[parentTypeName]?.[responseKey]; + if (fieldNodesByType) { + if ( + fieldNodesByType.every((fieldNode) => { + const requiredKey = + fieldNode.alias?.value ?? fieldNode.name.value; + return Object.prototype.hasOwnProperty.call( + leftOverParent, + requiredKey, + ); + }) + ) { + // requirements are satisfied, trigger handleLeftOver to fetch this field + handleLeftOver(leftOverParent, context, info, leftOver); + } + } } } - leftOver.missingFieldsParentDeferredMap.delete(leftOverParent); + // delete resolved deferred fields + for (const key of resolvedKeys) { + deferredFields.delete(key); + } + // and delete map if empty + if (deferredFields.size === 0) { + leftOver.missingFieldsParentDeferredMap.delete(leftOverParent); + } } } From 70f463a26262885c062896c49b1e84c747e031fd Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 18:32:18 +0200 Subject: [PATCH 06/16] remember quickfix --- packages/delegate/src/defaultMergedResolver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/delegate/src/defaultMergedResolver.ts b/packages/delegate/src/defaultMergedResolver.ts index a3709edb6..4b6310a9d 100644 --- a/packages/delegate/src/defaultMergedResolver.ts +++ b/packages/delegate/src/defaultMergedResolver.ts @@ -305,6 +305,7 @@ function handleFlattenedParent>( nestedTypeName ]?.resolvers.get(subschema); if (resolver) { + // TODO: quickfix, think about it const subschemaTypes = stitchingInfo.mergedTypes[ nestedTypeName From 76c6156f7fee7067c7506292aabb0f38e00368e9 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 18:58:34 +0200 Subject: [PATCH 07/16] prepare coinflict fixes --- ...bility-requires-arguments-conflict.test.ts | 219 ++++++++++++++++++ ...on-compatibility-requires-circular.test.ts | 211 ----------------- 2 files changed, 219 insertions(+), 211 deletions(-) create mode 100644 packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts delete mode 100644 packages/federation/tests/federation-compatibility-requires-circular.test.ts diff --git a/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts b/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts new file mode 100644 index 000000000..48d74b295 --- /dev/null +++ b/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts @@ -0,0 +1,219 @@ +import { buildSubgraphSchema } from '@apollo/subgraph'; +import { normalizedExecutor } from '@graphql-tools/executor'; +import { parse, print } from 'graphql'; +import { expect, it } from 'vitest'; +import { getStitchedSchemaFromLocalSchemas } from './getStitchedSchemaFromLocalSchemas'; + +const products = [ + { + upc: 'p1', + name: 'p-name-1', + price: 11, + weight: 1, + category: { + averagePrice: 11, + }, + }, + { + upc: 'p2', + name: 'p-name-2', + price: 22, + weight: 2, + category: { + averagePrice: 22, + }, + }, +]; + +const supergraph = await getStitchedSchemaFromLocalSchemas({ + localSchemas: { + a: buildSubgraphSchema([ + { + typeDefs: parse(/* GraphQL */ ` + extend schema + @link( + url: "https://specs.apollo.dev/federation/v2.3" + import: ["@key", "@external", "@requires"] + ) + + type Product @key(fields: "upc") { + upc: String! + weight: Int @external + price(currency: String!): Int @external + shippingEstimate: Int + @requires( + fields: """ + price(currency: "USD") weight + """ + ) + shippingEstimateEUR: Int + @requires( + fields: """ + price(currency: "EUR") weight + """ + ) + category: Category @external + isExpensiveCategory: Boolean + @requires( + fields: """ + category { averagePrice(currency: "USD") } + """ + ) + } + + type Category @external { + averagePrice(currency: String!): Int + } + `), + resolvers: { + Product: { + __resolveReference( + key: + | { upc: string; price: number; weight: number } + | { upc: string }, + ) { + const product = products.find((p) => p.upc === key.upc); + + if (!product) { + return null; + } + + if ('weight' in key && 'price' in key) { + return { + upc: product.upc, + weight: key.weight, + price: key.price, + category: product.category, + }; + } + + return { + upc: product.upc, + category: product.category, + }; + }, + shippingEstimate( + product: { price: number; weight: number }, + args: { currency?: string }, + ) { + const value = product.price * product.weight * 10; + + if (args.currency === 'EUR') { + return value * 1.5; + } + + return value; + }, + isExpensiveCategory(product: { + category: { averagePrice: number }; + }) { + return product.category.averagePrice > 11; + }, + }, + }, + }, + ]), + b: buildSubgraphSchema([ + { + typeDefs: parse(/* GraphQL */ ` + extend schema + @link( + url: "https://specs.apollo.dev/federation/v2.3" + import: ["@key"] + ) + + type Query { + products: [Product] + } + + type Product @key(fields: "upc") { + upc: String! + name: String + price(currency: String!): Int + weight: Int + category: Category + } + + type Category { + averagePrice(currency: String!): Int + } + `), + resolvers: { + Query: { + products() { + return products.map((p) => ({ + upc: p.upc, + name: p.name, + price: p.price, + weight: p.weight, + category: p.category, + })); + }, + }, + Product: { + __resolveReference(key: { upc: string }) { + const product = products.find((p) => p.upc === key.upc); + + if (!product) { + return null; + } + + return { + upc: product.upc, + name: product.name, + price: product.price, + weight: product.weight, + category: product.category, + }; + }, + }, + }, + }, + ]), + }, + // onSubgraphExecute(subgraph, executionRequest, result) { + // const query = print(executionRequest.document); + // console.log(query); + // // if (subgraph === 'a' && query.includes('_entities')) { + // // // debugger; + // // } + // }, +}); + +it('test-query-0', { timeout: 1000 }, async () => { + await expect( + normalizedExecutor({ + schema: supergraph, + document: parse(/* GraphQL */ ` + query { + products { + upc + name + shippingEstimate + shippingEstimateEUR + isExpensiveCategory + } + } + `), + }), + ).resolves.toEqual({ + data: { + products: [ + { + upc: 'p1', + name: 'p-name-1', + shippingEstimate: 110, + shippingEstimateEUR: 165, + isExpensiveCategory: false, + }, + { + upc: 'p2', + name: 'p-name-2', + shippingEstimate: 440, + shippingEstimateEUR: 660, + isExpensiveCategory: true, + }, + ], + }, + }); +}); diff --git a/packages/federation/tests/federation-compatibility-requires-circular.test.ts b/packages/federation/tests/federation-compatibility-requires-circular.test.ts deleted file mode 100644 index 93278bca2..000000000 --- a/packages/federation/tests/federation-compatibility-requires-circular.test.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { buildSubgraphSchema } from '@apollo/subgraph'; -import { normalizedExecutor } from '@graphql-tools/executor'; -import { parse, print } from 'graphql'; -import { expect, it } from 'vitest'; -import { getStitchedSchemaFromLocalSchemas } from './getStitchedSchemaFromLocalSchemas'; - -const authors = [ - { - id: 'a1', - name: 'John', - yearsOfExperience: 5, - }, - { - id: 'a2', - name: 'Jane', - yearsOfExperience: 20, - }, -]; - -const posts = [ - { - id: 'p1', - body: 'p1-body', - author: authors[0], - }, - { - id: 'p2', - body: 'p2-body', - author: authors[1], - }, -]; - -const supergraph = await getStitchedSchemaFromLocalSchemas({ - localSchemas: { - a: buildSubgraphSchema([ - { - typeDefs: parse(/* GraphQL */ ` - extend schema - @link( - url: "https://specs.apollo.dev/federation/v2.3" - import: ["@key", "@external", "@requires"] - ) - - type Query { - feed: [Post] - } - - type Post @key(fields: "id") { - id: ID! - byNovice: Boolean! @external - byExpert: Boolean! @requires(fields: "byNovice") - } - - type Author @key(fields: "id") { - id: ID! - name: String! - yearsOfExperience: Int! - } - `), - resolvers: { - Query: { - feed() { - return posts.map((post) => ({ id: post.id })); - }, - }, - Post: { - __resolveReference(ref: { id: string; byNovice?: boolean }) { - const post = posts.find((post) => post.id === ref.id); - if (!post) { - return null; - } - if (ref.byNovice == null) { - return { - id: post.id, - }; - } - return { - id: post.id, - byNovice: ref.byNovice, - }; - }, - byExpert(post: { byNovice: boolean }) { - if (post.byNovice == null) { - // ensuring requires is not skipped - return null; - } - return !post.byNovice; - }, - }, - Author: { - __resolveReference(ref: { id: string }) { - const author = authors.find((author) => author.id === ref.id); - if (!author) { - return null; - } - return { - id: author.id, - name: author.name, - yearsOfExperience: author.yearsOfExperience, - }; - }, - }, - }, - }, - ]), - b: buildSubgraphSchema([ - { - typeDefs: parse(/* GraphQL */ ` - extend schema - @link( - url: "https://specs.apollo.dev/federation/v2.3" - import: ["@key", "@external", "@requires"] - ) - - type Post @key(fields: "id") { - id: ID! - author: Author! - byNovice: Boolean! @requires(fields: "author { yearsOfExperience }") - } - - type Author @key(fields: "id") { - id: ID! - yearsOfExperience: Int! @external - } - `), - resolvers: { - Post: { - __resolveReference(ref: { - id: string; - author?: { yearsOfExperience: number }; - }) { - const post = posts.find((post) => post.id === ref.id); - if (!post) { - return null; - } - return { - id: post.id, - author: { - id: post.author!.id, - ...ref.author, - }, - }; - }, - byNovice(post: { author: { yearsOfExperience: number } }) { - return post.author.yearsOfExperience < 10; - }, - }, - }, - }, - ]), - }, - // onSubgraphExecute(subgraph, executionRequest, result) { - // const query = print(executionRequest.document); - // console.log(query); - // // if (subgraph === 'a' && query.includes('_entities')) { - // // // debugger; - // // } - // }, -}); - -it('test query 0', { timeout: 1000 }, async () => { - await expect( - normalizedExecutor({ - schema: supergraph, - document: parse(/* GraphQL */ ` - { - feed { - byNovice - } - } - `), - }), - ).resolves.toEqual({ - data: { - feed: [ - { - byNovice: true, - }, - { - byNovice: false, - }, - ], - }, - }); -}); - -it('test query 1', { timeout: 1000 }, async () => { - await expect( - normalizedExecutor({ - schema: supergraph, - document: parse(/* GraphQL */ ` - { - feed { - byExpert - } - } - `), - }), - ).resolves.toEqual({ - data: { - feed: [ - { - byExpert: false, - }, - { - byExpert: true, - }, - ], - }, - }); -}); From f4bdc1c3f7ec2b42e169f35dd3316fde7d63cbc6 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 18:58:38 +0200 Subject: [PATCH 08/16] not idepmpotet --- .../tests/federation-compatibility.test.ts | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/packages/federation/tests/federation-compatibility.test.ts b/packages/federation/tests/federation-compatibility.test.ts index 337d53099..1257c3bd8 100644 --- a/packages/federation/tests/federation-compatibility.test.ts +++ b/packages/federation/tests/federation-compatibility.test.ts @@ -155,45 +155,46 @@ describe('Federation Compatibility', () => { }); tests.forEach((_, i) => { describe(`test-query-${i}`, () => { - it('gives the correct result w/ core', async () => { - const test = tests[i]; - if (!test) { - throw new Error(`Test ${i} not found`); - } - const document = parse(test.query, { noLocation: true }); - const validationErrors = validate(stitchedSchema, document); - let result: ExecutionResult; - if (validationErrors.length > 0) { - result = { - errors: validationErrors, - }; - } else { - const execRes = await normalizedExecutor({ - schema: stitchedSchema, - document, - }); - assertSingleExecutionValue(execRes); - result = execRes; - } - const received = { - data: result.data ?? null, - errors: !!result.errors?.length, - }; + // TODO: audit tests are not idempotent anymore, we need to restart the subgraphs + // it('gives the correct result w/ core', async () => { + // const test = tests[i]; + // if (!test) { + // throw new Error(`Test ${i} not found`); + // } + // const document = parse(test.query, { noLocation: true }); + // const validationErrors = validate(stitchedSchema, document); + // let result: ExecutionResult; + // if (validationErrors.length > 0) { + // result = { + // errors: validationErrors, + // }; + // } else { + // const execRes = await normalizedExecutor({ + // schema: stitchedSchema, + // document, + // }); + // assertSingleExecutionValue(execRes); + // result = execRes; + // } + // const received = { + // data: result.data ?? null, + // errors: !!result.errors?.length, + // }; - const expected = { - data: test.expected.data ?? null, - errors: test.expected.errors ?? false, - }; + // const expected = { + // data: test.expected.data ?? null, + // errors: test.expected.errors ?? false, + // }; - try { - expect(received).toEqual(expected); - } catch (e) { - result.errors?.forEach((err) => { - console.error(err); - }); - throw e; - } - }); + // try { + // expect(received).toEqual(expected); + // } catch (e) { + // result.errors?.forEach((err) => { + // console.error(err); + // }); + // throw e; + // } + // }); it('gives the correct result w/ gateway', async () => { const test = tests[i]; if (!test) { From 34f56f5c49fd52204f5a3ac0dd53f4f6ada8d8d8 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 19:29:18 +0200 Subject: [PATCH 09/16] fix tests --- ...bility-requires-arguments-conflict.test.ts | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts b/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts index 48d74b295..ae125c111 100644 --- a/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts +++ b/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts @@ -92,16 +92,12 @@ const supergraph = await getStitchedSchemaFromLocalSchemas({ category: product.category, }; }, - shippingEstimate( - product: { price: number; weight: number }, - args: { currency?: string }, - ) { + shippingEstimate(product: { price: number; weight: number }) { + const value = product.price * product.weight * 10; + return value; + }, + shippingEstimateEUR(product: { price: number; weight: number }) { const value = product.price * product.weight * 10; - - if (args.currency === 'EUR') { - return value * 1.5; - } - return value; }, isExpensiveCategory(product: { @@ -166,18 +162,21 @@ const supergraph = await getStitchedSchemaFromLocalSchemas({ category: product.category, }; }, + price(parent, args) { + if (args.currency === 'EUR') return parent.price * 2; + if (args.currency === 'USD') return parent.price; + throw new Error('Unsupported currency ' + args.currency); + }, }, }, }, ]), }, - // onSubgraphExecute(subgraph, executionRequest, result) { - // const query = print(executionRequest.document); - // console.log(query); - // // if (subgraph === 'a' && query.includes('_entities')) { - // // // debugger; - // // } - // }, + onSubgraphExecute(subgraph, executionRequest, result) { + const query = print(executionRequest.document); + console.log(query); + console.dir(result, { depth: 3000 }); + }, }); it('test-query-0', { timeout: 1000 }, async () => { @@ -202,15 +201,15 @@ it('test-query-0', { timeout: 1000 }, async () => { { upc: 'p1', name: 'p-name-1', - shippingEstimate: 110, - shippingEstimateEUR: 165, + shippingEstimate: 110, // 11 * 1 * 10 + shippingEstimateEUR: 220, // (11 * 2) * 1 * 10 isExpensiveCategory: false, }, { upc: 'p2', name: 'p-name-2', - shippingEstimate: 440, - shippingEstimateEUR: 660, + shippingEstimate: 440, // 22 * 2 * 10 + shippingEstimateEUR: 880, // (22 * 2) * 2 * 10 isExpensiveCategory: true, }, ], From 286ead6a1bdb04ab0f03a0462dc08c771267d396 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 19:52:01 +0200 Subject: [PATCH 10/16] distinct aliases --- packages/federation/src/supergraph.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/federation/src/supergraph.ts b/packages/federation/src/supergraph.ts index 50a88dbfa..2723b0205 100644 --- a/packages/federation/src/supergraph.ts +++ b/packages/federation/src/supergraph.ts @@ -806,10 +806,21 @@ export function getStitchingOptionsFromSupergraphSdl( selection.kind === Kind.FIELD && selection.arguments?.length ) { + const argsHash = selection.arguments + .map( + (arg) => + arg.name.value + + // TODO: slow? faster hash? + memoizedASTPrint(arg.value).replace( + /[^a-zA-Z0-9]/g, + '', + ), + ) + .join(''); // @ts-expect-error it's ok we're mutating consciously selection.alias = { kind: Kind.NAME, - value: '_' + selection.name.value, + value: '_' + selection.name.value + '_' + argsHash, }; } if ('selectionSet' in selection && selection.selectionSet) { From b3d5315893cf69ca3686edbba52aa9f4c2c2aa1e Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Fri, 3 Oct 2025 20:13:58 +0200 Subject: [PATCH 11/16] more logs to tests --- ...federation-compatibility-requires-arguments-conflict.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts b/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts index ae125c111..448e9f0da 100644 --- a/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts +++ b/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts @@ -174,7 +174,7 @@ const supergraph = await getStitchedSchemaFromLocalSchemas({ }, onSubgraphExecute(subgraph, executionRequest, result) { const query = print(executionRequest.document); - console.log(query); + console.log(subgraph, query, executionRequest.variables); console.dir(result, { depth: 3000 }); }, }); From e06b30c282549d656adfb6418807b7a6401bcd2a Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Mon, 6 Oct 2025 17:38:16 +0200 Subject: [PATCH 12/16] no quick test --- ...bility-requires-arguments-conflict.test.ts | 218 ------------------ 1 file changed, 218 deletions(-) delete mode 100644 packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts diff --git a/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts b/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts deleted file mode 100644 index 448e9f0da..000000000 --- a/packages/federation/tests/federation-compatibility-requires-arguments-conflict.test.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { buildSubgraphSchema } from '@apollo/subgraph'; -import { normalizedExecutor } from '@graphql-tools/executor'; -import { parse, print } from 'graphql'; -import { expect, it } from 'vitest'; -import { getStitchedSchemaFromLocalSchemas } from './getStitchedSchemaFromLocalSchemas'; - -const products = [ - { - upc: 'p1', - name: 'p-name-1', - price: 11, - weight: 1, - category: { - averagePrice: 11, - }, - }, - { - upc: 'p2', - name: 'p-name-2', - price: 22, - weight: 2, - category: { - averagePrice: 22, - }, - }, -]; - -const supergraph = await getStitchedSchemaFromLocalSchemas({ - localSchemas: { - a: buildSubgraphSchema([ - { - typeDefs: parse(/* GraphQL */ ` - extend schema - @link( - url: "https://specs.apollo.dev/federation/v2.3" - import: ["@key", "@external", "@requires"] - ) - - type Product @key(fields: "upc") { - upc: String! - weight: Int @external - price(currency: String!): Int @external - shippingEstimate: Int - @requires( - fields: """ - price(currency: "USD") weight - """ - ) - shippingEstimateEUR: Int - @requires( - fields: """ - price(currency: "EUR") weight - """ - ) - category: Category @external - isExpensiveCategory: Boolean - @requires( - fields: """ - category { averagePrice(currency: "USD") } - """ - ) - } - - type Category @external { - averagePrice(currency: String!): Int - } - `), - resolvers: { - Product: { - __resolveReference( - key: - | { upc: string; price: number; weight: number } - | { upc: string }, - ) { - const product = products.find((p) => p.upc === key.upc); - - if (!product) { - return null; - } - - if ('weight' in key && 'price' in key) { - return { - upc: product.upc, - weight: key.weight, - price: key.price, - category: product.category, - }; - } - - return { - upc: product.upc, - category: product.category, - }; - }, - shippingEstimate(product: { price: number; weight: number }) { - const value = product.price * product.weight * 10; - return value; - }, - shippingEstimateEUR(product: { price: number; weight: number }) { - const value = product.price * product.weight * 10; - return value; - }, - isExpensiveCategory(product: { - category: { averagePrice: number }; - }) { - return product.category.averagePrice > 11; - }, - }, - }, - }, - ]), - b: buildSubgraphSchema([ - { - typeDefs: parse(/* GraphQL */ ` - extend schema - @link( - url: "https://specs.apollo.dev/federation/v2.3" - import: ["@key"] - ) - - type Query { - products: [Product] - } - - type Product @key(fields: "upc") { - upc: String! - name: String - price(currency: String!): Int - weight: Int - category: Category - } - - type Category { - averagePrice(currency: String!): Int - } - `), - resolvers: { - Query: { - products() { - return products.map((p) => ({ - upc: p.upc, - name: p.name, - price: p.price, - weight: p.weight, - category: p.category, - })); - }, - }, - Product: { - __resolveReference(key: { upc: string }) { - const product = products.find((p) => p.upc === key.upc); - - if (!product) { - return null; - } - - return { - upc: product.upc, - name: product.name, - price: product.price, - weight: product.weight, - category: product.category, - }; - }, - price(parent, args) { - if (args.currency === 'EUR') return parent.price * 2; - if (args.currency === 'USD') return parent.price; - throw new Error('Unsupported currency ' + args.currency); - }, - }, - }, - }, - ]), - }, - onSubgraphExecute(subgraph, executionRequest, result) { - const query = print(executionRequest.document); - console.log(subgraph, query, executionRequest.variables); - console.dir(result, { depth: 3000 }); - }, -}); - -it('test-query-0', { timeout: 1000 }, async () => { - await expect( - normalizedExecutor({ - schema: supergraph, - document: parse(/* GraphQL */ ` - query { - products { - upc - name - shippingEstimate - shippingEstimateEUR - isExpensiveCategory - } - } - `), - }), - ).resolves.toEqual({ - data: { - products: [ - { - upc: 'p1', - name: 'p-name-1', - shippingEstimate: 110, // 11 * 1 * 10 - shippingEstimateEUR: 220, // (11 * 2) * 1 * 10 - isExpensiveCategory: false, - }, - { - upc: 'p2', - name: 'p-name-2', - shippingEstimate: 440, // 22 * 2 * 10 - shippingEstimateEUR: 880, // (22 * 2) * 2 * 10 - isExpensiveCategory: true, - }, - ], - }, - }); -}); From 7d207c0c585c217a3938868892c3b06dfbeacf00 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Mon, 6 Oct 2025 17:46:08 +0200 Subject: [PATCH 13/16] drop altogether --- .../tests/federation-compatibility.test.ts | 112 ++++++------------ 1 file changed, 33 insertions(+), 79 deletions(-) diff --git a/packages/federation/tests/federation-compatibility.test.ts b/packages/federation/tests/federation-compatibility.test.ts index 1257c3bd8..00f4d07de 100644 --- a/packages/federation/tests/federation-compatibility.test.ts +++ b/packages/federation/tests/federation-compatibility.test.ts @@ -5,7 +5,6 @@ import { GatewayRuntime, useCustomFetch, } from '@graphql-hive/gateway-runtime'; -import { normalizedExecutor } from '@graphql-tools/executor'; import { ExecutionResult, filterSchema, @@ -13,16 +12,13 @@ import { MapperKind, mapSchema, } from '@graphql-tools/utils'; -import { assertSingleExecutionValue } from '@internal/testing'; import { buildSchema, getNamedType, GraphQLSchema, isEnumType, lexicographicSortSchema, - parse, printSchema, - validate, } from 'graphql'; import { createRouter } from 'graphql-federation-gateway-audit'; import { beforeAll, describe, expect, it } from 'vitest'; @@ -154,84 +150,42 @@ describe('Federation Compatibility', () => { ); }); tests.forEach((_, i) => { - describe(`test-query-${i}`, () => { - // TODO: audit tests are not idempotent anymore, we need to restart the subgraphs - // it('gives the correct result w/ core', async () => { - // const test = tests[i]; - // if (!test) { - // throw new Error(`Test ${i} not found`); - // } - // const document = parse(test.query, { noLocation: true }); - // const validationErrors = validate(stitchedSchema, document); - // let result: ExecutionResult; - // if (validationErrors.length > 0) { - // result = { - // errors: validationErrors, - // }; - // } else { - // const execRes = await normalizedExecutor({ - // schema: stitchedSchema, - // document, - // }); - // assertSingleExecutionValue(execRes); - // result = execRes; - // } - // const received = { - // data: result.data ?? null, - // errors: !!result.errors?.length, - // }; - - // const expected = { - // data: test.expected.data ?? null, - // errors: test.expected.errors ?? false, - // }; - - // try { - // expect(received).toEqual(expected); - // } catch (e) { - // result.errors?.forEach((err) => { - // console.error(err); - // }); - // throw e; - // } - // }); - it('gives the correct result w/ gateway', async () => { - const test = tests[i]; - if (!test) { - throw new Error(`Test ${i} not found`); - } - const response = await gatewayRuntime.fetch( - 'http://localhost/graphql', - { - method: 'POST', - headers: { - 'content-type': 'application/json', - }, - body: JSON.stringify({ - query: test.query, - }), + it(`test-query-${i}`, async () => { + const test = tests[i]; + if (!test) { + throw new Error(`Test ${i} not found`); + } + const response = await gatewayRuntime.fetch( + 'http://localhost/graphql', + { + method: 'POST', + headers: { + 'content-type': 'application/json', }, - ); - const result: ExecutionResult = await response.json(); - const received = { - data: result.data ?? null, - errors: !!result.errors?.length, - }; + body: JSON.stringify({ + query: test.query, + }), + }, + ); + const result: ExecutionResult = await response.json(); + const received = { + data: result.data ?? null, + errors: !!result.errors?.length, + }; - const expected = { - data: test.expected.data ?? null, - errors: test.expected.errors ?? false, - }; + const expected = { + data: test.expected.data ?? null, + errors: test.expected.errors ?? false, + }; - try { - expect(received).toEqual(expected); - } catch (e) { - result.errors?.forEach((err) => { - console.error(err); - }); - throw e; - } - }); + try { + expect(received).toEqual(expected); + } catch (e) { + result.errors?.forEach((err) => { + console.error(err); + }); + throw e; + } }); }); }); From 1692eb5b64c4710665e7742ea89c556cac56b568 Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Mon, 6 Oct 2025 17:48:20 +0200 Subject: [PATCH 14/16] bump federation audit with correct --- yarn.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index d220c516a..32a39efa7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15154,7 +15154,7 @@ __metadata: "graphql-federation-gateway-audit@graphql-hive/federation-gateway-audit#main": version: 1.0.0 - resolution: "graphql-federation-gateway-audit@https://github.com/graphql-hive/federation-gateway-audit.git#commit=82d615dc4353a02fb39b4086adb964c6c7441b4e" + resolution: "graphql-federation-gateway-audit@https://github.com/graphql-hive/federation-gateway-audit.git#commit=842c2f0eeeb571ba16c1abfe59929c161589f2d8" dependencies: "@apollo/composition": "npm:2.11.2" "@apollo/subgraph": "npm:2.11.2" @@ -15171,7 +15171,7 @@ __metadata: yargs: "npm:18.0.0" bin: graphql-federation-audit: ./dist/cli.js - checksum: 10c0/d60f0bf837e3eac3e593e0675eb639c42daffce0bac6c40d89637a9caa28d62127d74d3f4eedd4bf16d6146a4aa8697bce5a43f49e7f6ea82e86433d1b67b366 + checksum: 10c0/69e5ef728f9ed854610a40631f3f469d450f5b95b68e20f62952237bdaae4b744d4a03cf9bfacb8acc1fced885d2d578724dfb3f8ea7a05557765e09576be2a4 languageName: node linkType: hard From ebe8136784b8fd2d3b46192dd0dcfdeda71255df Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Mon, 6 Oct 2025 17:49:13 +0200 Subject: [PATCH 15/16] changeset --- .changeset/wet-bugs-double.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/wet-bugs-double.md diff --git a/.changeset/wet-bugs-double.md b/.changeset/wet-bugs-double.md new file mode 100644 index 000000000..64e8f9b98 --- /dev/null +++ b/.changeset/wet-bugs-double.md @@ -0,0 +1,6 @@ +--- +'@graphql-tools/federation': patch +'@graphql-tools/delegate': patch +--- + +Correctly resolve circular @requires in different subgraphs From 2f1daa6f6a537c7d932249b5986d11acf759821d Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Mon, 6 Oct 2025 17:59:34 +0200 Subject: [PATCH 16/16] no todo, its ok --- packages/delegate/src/defaultMergedResolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/delegate/src/defaultMergedResolver.ts b/packages/delegate/src/defaultMergedResolver.ts index 4b6310a9d..a3709edb6 100644 --- a/packages/delegate/src/defaultMergedResolver.ts +++ b/packages/delegate/src/defaultMergedResolver.ts @@ -305,7 +305,6 @@ function handleFlattenedParent>( nestedTypeName ]?.resolvers.get(subschema); if (resolver) { - // TODO: quickfix, think about it const subschemaTypes = stitchingInfo.mergedTypes[ nestedTypeName