@graphql-tools/federation 4.4.2 → 4.4.3-alpha-12654e24df4f034d8c7112f5052f1b37674985ae

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @graphql-tools/federation
2
2
 
3
+ ## 4.4.3-alpha-12654e24df4f034d8c7112f5052f1b37674985ae
4
+ ### Patch Changes
5
+
6
+
7
+
8
+ - [#2346](https://github.com/graphql-hive/gateway/pull/2346) [`16fd346`](https://github.com/graphql-hive/gateway/commit/16fd346a18fd77e1094fe4ac2f7a6c2d8923bd68) Thanks [@copilot-swe-agent](https://github.com/apps/copilot-swe-agent)! - Fix `@requires` argument conflict when multiple computed fields in a federation subgraph require the same external field with different argument values (e.g. `price(currency: "USD")` vs `price(currency: "EUR")`).
9
+
10
+ Previously, all `@requires` selections for computed fields in a subgraph were merged into a single entity representation. When two computed fields needed the same external field but with different argument values, the stitching engine would alias each field differently — but both aliases ended up in the same representation object. The second field would overwrite the first, so one of the computed fields would always receive the wrong value.
11
+
12
+ **What changed:**
13
+
14
+ - **Computed field conflict detection** — when building subschema merge configs from the supergraph SDL, the federation stitching layer now detects conflicts: two computed fields conflict when they produce the same top-level aliased field name in their `@requires` selections but with different aliases (i.e. the same external field required with different argument values).
15
+
16
+ - **Conflict group isolation** — conflicting computed fields are separated into independent "conflict groups". Each group gets its own `SubschemaConfig` (sharing the same underlying executor/endpoint) with only its own computed fields and a deduplicated entity representation that carries only the aliased fields it actually needs. This ensures each group sends the correct representation to the subgraph and receives the right data back.
17
+
18
+ - **Schema scoping per group** — the main `SubschemaConfig`'s schema is filtered (via `mapSchema`) to remove computed fields that were moved to a conflict group, preventing the stitching engine from incorrectly routing those fields back to the main subschema. Each conflict group's `SubschemaConfig` is likewise scoped to expose only its own computed fields.
19
+
20
+ - **`getMergedTypeConfigFromKey` extended** — accepts an optional `keysExtraKeys` parameter so each conflict group can inject its own `extraKeys` set when building entity key functions, keeping key generation independent per group.
21
+
22
+ - **Federation audit test enabled** — the `requires-with-argument-conflict` case in the federation compatibility test suite was previously skipped for stitching (marked `it.todo`). It now passes and the skip has been removed.
23
+
3
24
  ## 4.4.2
4
25
  ### Patch Changes
5
26
 
package/dist/index.cjs CHANGED
@@ -1272,14 +1272,15 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1272
1272
  const mergeConfig = {};
1273
1273
  const typeNameKeyMap = typeNameKeysBySubgraphMap.get(subgraphName);
1274
1274
  const unionTypeNodes = [];
1275
+ const pendingConflictSubschemaConfigs = [];
1275
1276
  if (typeNameKeyMap) {
1276
1277
  const typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(subgraphName);
1277
1278
  for (const [typeName, keys] of typeNameKeyMap) {
1278
- let getMergedTypeConfigFromKey2 = function(key) {
1279
+ let getMergedTypeConfigFromKey2 = function(key, keysExtraKeys = extraKeys) {
1279
1280
  return {
1280
1281
  selectionSet: `{ ${key} }`,
1281
1282
  argsFromKeys: getArgsFromKeysForFederation,
1282
- key: getKeyFnForFederation(typeName, [key, ...extraKeys]),
1283
+ key: getKeyFnForFederation(typeName, [key, ...keysExtraKeys]),
1283
1284
  fieldName: `_entities`,
1284
1285
  dataLoaderOptions: {
1285
1286
  cacheKeyFn: getCacheKeyFnFromKey(key),
@@ -1291,38 +1292,93 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1291
1292
  const fieldsKeyMap = typeNameFieldsKeyMap?.get(typeName);
1292
1293
  const extraKeys = /* @__PURE__ */ new Set();
1293
1294
  if (fieldsKeyMap) {
1294
- const fieldsConfig = mergedTypeConfig.fields = {};
1295
+ let aliasFieldsWithArgs2 = function(selectionSetNode) {
1296
+ for (const selection of selectionSetNode.selections) {
1297
+ if (selection.kind === graphql.Kind.FIELD && selection.arguments?.length) {
1298
+ const argsHash = selection.arguments.map(
1299
+ (arg) => arg.name.value + // TODO: slow? faster hash?
1300
+ memoizedASTPrint(arg.value).replace(/[^a-zA-Z0-9]/g, "")
1301
+ ).join("");
1302
+ selection.alias = {
1303
+ kind: graphql.Kind.NAME,
1304
+ value: "_" + selection.name.value + "_" + argsHash
1305
+ };
1306
+ }
1307
+ if ("selectionSet" in selection && selection.selectionSet) {
1308
+ aliasFieldsWithArgs2(selection.selectionSet);
1309
+ }
1310
+ }
1311
+ };
1312
+ const mainFieldsConfig = mergedTypeConfig.fields = {};
1313
+ const groups = [
1314
+ {
1315
+ extraKeys,
1316
+ fieldsConfig: mainFieldsConfig,
1317
+ aliasedFields: /* @__PURE__ */ new Map()
1318
+ }
1319
+ ];
1295
1320
  for (const [fieldName, fieldNameKey] of fieldsKeyMap) {
1296
1321
  const selectionSetNode = utils.parseSelectionSet(`{${fieldNameKey}}`);
1297
- (function aliasFieldsWithArgs(selectionSetNode2) {
1298
- for (const selection of selectionSetNode2.selections) {
1299
- if (selection.kind === graphql.Kind.FIELD && selection.arguments?.length) {
1300
- const argsHash = selection.arguments.map(
1301
- (arg) => arg.name.value + // TODO: slow? faster hash?
1302
- memoizedASTPrint(arg.value).replace(
1303
- /[^a-zA-Z0-9]/g,
1304
- ""
1305
- )
1306
- ).join("");
1307
- selection.alias = {
1308
- kind: graphql.Kind.NAME,
1309
- value: "_" + selection.name.value + "_" + argsHash
1310
- };
1311
- }
1312
- if ("selectionSet" in selection && selection.selectionSet) {
1313
- aliasFieldsWithArgs(selection.selectionSet);
1314
- }
1315
- }
1316
- })(selectionSetNode);
1322
+ aliasFieldsWithArgs2(selectionSetNode);
1317
1323
  const selectionSet = graphql.print(selectionSetNode).replaceAll(/\n/g, " ").replaceAll(/\s+/g, " ");
1318
- extraKeys.add(
1324
+ const extraKey = (
1319
1325
  // remove first and last characters (curly braces)
1320
1326
  selectionSet.slice(1, -1)
1321
1327
  );
1322
- fieldsConfig[fieldName] = {
1328
+ const fieldAliasedNames = /* @__PURE__ */ new Map();
1329
+ for (const sel of selectionSetNode.selections) {
1330
+ if (sel.kind === graphql.Kind.FIELD && sel.alias) {
1331
+ fieldAliasedNames.set(sel.name.value, sel.alias.value);
1332
+ }
1333
+ }
1334
+ let targetGroup;
1335
+ for (const group of groups) {
1336
+ let hasConflict = false;
1337
+ for (const [origName, aliasName] of fieldAliasedNames) {
1338
+ const existingAlias = group.aliasedFields.get(origName);
1339
+ if (existingAlias !== void 0 && existingAlias !== aliasName) {
1340
+ hasConflict = true;
1341
+ break;
1342
+ }
1343
+ }
1344
+ if (!hasConflict) {
1345
+ targetGroup = group;
1346
+ break;
1347
+ }
1348
+ }
1349
+ if (!targetGroup) {
1350
+ const newExtraKeys = /* @__PURE__ */ new Set();
1351
+ const newFieldsConfig = {};
1352
+ targetGroup = {
1353
+ extraKeys: newExtraKeys,
1354
+ fieldsConfig: newFieldsConfig,
1355
+ aliasedFields: /* @__PURE__ */ new Map()
1356
+ };
1357
+ groups.push(targetGroup);
1358
+ }
1359
+ targetGroup.extraKeys.add(extraKey);
1360
+ targetGroup.fieldsConfig[fieldName] = {
1323
1361
  selectionSet,
1324
1362
  computed: true
1325
1363
  };
1364
+ for (const [origName, aliasName] of fieldAliasedNames) {
1365
+ targetGroup.aliasedFields.set(origName, aliasName);
1366
+ }
1367
+ }
1368
+ for (const group of groups.slice(1)) {
1369
+ const additionalMergeTypeConfig = {
1370
+ fields: group.fieldsConfig
1371
+ };
1372
+ if (typeNameCanonicalMap.get(typeName) === subgraphName) {
1373
+ additionalMergeTypeConfig.canonical = true;
1374
+ }
1375
+ pendingConflictSubschemaConfigs.push({
1376
+ typeName,
1377
+ // Store closure-captured values for later SubschemaConfig creation
1378
+ // by borrowing getMergedTypeConfigFromKey with the group's extraKeys.
1379
+ mergeTypeConfig: additionalMergeTypeConfig,
1380
+ groupExtraKeys: group.extraKeys
1381
+ });
1326
1382
  }
1327
1383
  }
1328
1384
  if (typeNameCanonicalMap.get(typeName) === subgraphName) {
@@ -1341,6 +1397,21 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1341
1397
  );
1342
1398
  mergedTypeConfig.entryPoints = entryPoints;
1343
1399
  }
1400
+ for (const pending of pendingConflictSubschemaConfigs) {
1401
+ if (pending.typeName === typeName && !("selectionSet" in pending.mergeTypeConfig) && !("entryPoints" in pending.mergeTypeConfig)) {
1402
+ const groupExtraKeys = pending.groupExtraKeys;
1403
+ if (keysArr.length === 1 && keysArr[0]) {
1404
+ Object.assign(
1405
+ pending.mergeTypeConfig,
1406
+ getMergedTypeConfigFromKey2(keysArr[0], groupExtraKeys)
1407
+ );
1408
+ } else if (keysArr.length > 1) {
1409
+ pending.mergeTypeConfig.entryPoints = keysArr.map(
1410
+ (key) => getMergedTypeConfigFromKey2(key, groupExtraKeys)
1411
+ );
1412
+ }
1413
+ }
1414
+ }
1344
1415
  unionTypeNodes.push({
1345
1416
  kind: graphql.Kind.NAMED_TYPE,
1346
1417
  name: {
@@ -1731,10 +1802,55 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1731
1802
  }
1732
1803
  }
1733
1804
  }
1805
+ let mainSchema = schema;
1806
+ const additionalGroupFieldsByType = /* @__PURE__ */ new Map();
1807
+ for (const {
1808
+ typeName,
1809
+ mergeTypeConfig
1810
+ } of pendingConflictSubschemaConfigs) {
1811
+ for (const [fieldName, fieldConfig] of Object.entries(
1812
+ mergeTypeConfig.fields ?? {}
1813
+ )) {
1814
+ if (!fieldConfig?.computed) continue;
1815
+ let set = additionalGroupFieldsByType.get(typeName);
1816
+ if (!set) {
1817
+ set = /* @__PURE__ */ new Set();
1818
+ additionalGroupFieldsByType.set(typeName, set);
1819
+ }
1820
+ set.add(fieldName);
1821
+ }
1822
+ }
1823
+ if (additionalGroupFieldsByType.size > 0) {
1824
+ mainSchema = utils.mapSchema(schema, {
1825
+ [utils.MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
1826
+ if (additionalGroupFieldsByType.get(typeName)?.has(fieldName)) {
1827
+ return null;
1828
+ }
1829
+ return fieldConfig;
1830
+ }
1831
+ });
1832
+ }
1833
+ const allComputedFieldsByType = new Map(
1834
+ Array.from(additionalGroupFieldsByType, ([t, s]) => [t, new Set(s)])
1835
+ );
1836
+ for (const [typeName, typeConfig] of Object.entries(mergeConfig)) {
1837
+ if (!typeConfig?.fields) continue;
1838
+ for (const [fieldName, fieldConfig] of Object.entries(
1839
+ typeConfig.fields
1840
+ )) {
1841
+ if (!fieldConfig?.computed) continue;
1842
+ let set = allComputedFieldsByType.get(typeName);
1843
+ if (!set) {
1844
+ set = /* @__PURE__ */ new Set();
1845
+ allComputedFieldsByType.set(typeName, set);
1846
+ }
1847
+ set.add(fieldName);
1848
+ }
1849
+ }
1734
1850
  const subschemaConfig = {
1735
1851
  name: subgraphName,
1736
1852
  endpoint,
1737
- schema,
1853
+ schema: mainSchema,
1738
1854
  executor: executor$1,
1739
1855
  merge: mergeConfig,
1740
1856
  transforms,
@@ -1742,6 +1858,36 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1742
1858
  batchingOptions: opts.batchingOptions
1743
1859
  };
1744
1860
  subschemas.push(subschemaConfig);
1861
+ for (const {
1862
+ typeName,
1863
+ mergeTypeConfig
1864
+ } of pendingConflictSubschemaConfigs) {
1865
+ const groupOwnFields = new Set(
1866
+ Object.entries(mergeTypeConfig.fields ?? {}).filter(([, fc]) => fc?.computed).map(([fn]) => fn)
1867
+ );
1868
+ const allComputedForType = allComputedFieldsByType.get(typeName) ?? /* @__PURE__ */ new Set();
1869
+ const fieldsToRemove = new Set(
1870
+ [...allComputedForType].filter((f) => !groupOwnFields.has(f))
1871
+ );
1872
+ const groupSchema = fieldsToRemove.size > 0 ? utils.mapSchema(schema, {
1873
+ [utils.MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, type) => {
1874
+ if (type === typeName && fieldsToRemove.has(fieldName)) {
1875
+ return null;
1876
+ }
1877
+ return fieldConfig;
1878
+ }
1879
+ }) : schema;
1880
+ subschemas.push({
1881
+ name: subgraphName,
1882
+ endpoint,
1883
+ schema: groupSchema,
1884
+ executor: executor$1,
1885
+ merge: { [typeName]: mergeTypeConfig },
1886
+ transforms,
1887
+ batch: opts.batch,
1888
+ batchingOptions: opts.batchingOptions
1889
+ });
1890
+ }
1745
1891
  }
1746
1892
  const defaultMerger = stitch.getDefaultFieldConfigMerger(true);
1747
1893
  const fieldConfigMerger = function(candidates) {
package/dist/index.js CHANGED
@@ -1270,14 +1270,15 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1270
1270
  const mergeConfig = {};
1271
1271
  const typeNameKeyMap = typeNameKeysBySubgraphMap.get(subgraphName);
1272
1272
  const unionTypeNodes = [];
1273
+ const pendingConflictSubschemaConfigs = [];
1273
1274
  if (typeNameKeyMap) {
1274
1275
  const typeNameFieldsKeyMap = typeNameFieldsKeyBySubgraphMap.get(subgraphName);
1275
1276
  for (const [typeName, keys] of typeNameKeyMap) {
1276
- let getMergedTypeConfigFromKey2 = function(key) {
1277
+ let getMergedTypeConfigFromKey2 = function(key, keysExtraKeys = extraKeys) {
1277
1278
  return {
1278
1279
  selectionSet: `{ ${key} }`,
1279
1280
  argsFromKeys: getArgsFromKeysForFederation,
1280
- key: getKeyFnForFederation(typeName, [key, ...extraKeys]),
1281
+ key: getKeyFnForFederation(typeName, [key, ...keysExtraKeys]),
1281
1282
  fieldName: `_entities`,
1282
1283
  dataLoaderOptions: {
1283
1284
  cacheKeyFn: getCacheKeyFnFromKey(key),
@@ -1289,38 +1290,93 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1289
1290
  const fieldsKeyMap = typeNameFieldsKeyMap?.get(typeName);
1290
1291
  const extraKeys = /* @__PURE__ */ new Set();
1291
1292
  if (fieldsKeyMap) {
1292
- const fieldsConfig = mergedTypeConfig.fields = {};
1293
+ let aliasFieldsWithArgs2 = function(selectionSetNode) {
1294
+ for (const selection of selectionSetNode.selections) {
1295
+ if (selection.kind === Kind.FIELD && selection.arguments?.length) {
1296
+ const argsHash = selection.arguments.map(
1297
+ (arg) => arg.name.value + // TODO: slow? faster hash?
1298
+ memoizedASTPrint(arg.value).replace(/[^a-zA-Z0-9]/g, "")
1299
+ ).join("");
1300
+ selection.alias = {
1301
+ kind: Kind.NAME,
1302
+ value: "_" + selection.name.value + "_" + argsHash
1303
+ };
1304
+ }
1305
+ if ("selectionSet" in selection && selection.selectionSet) {
1306
+ aliasFieldsWithArgs2(selection.selectionSet);
1307
+ }
1308
+ }
1309
+ };
1310
+ const mainFieldsConfig = mergedTypeConfig.fields = {};
1311
+ const groups = [
1312
+ {
1313
+ extraKeys,
1314
+ fieldsConfig: mainFieldsConfig,
1315
+ aliasedFields: /* @__PURE__ */ new Map()
1316
+ }
1317
+ ];
1293
1318
  for (const [fieldName, fieldNameKey] of fieldsKeyMap) {
1294
1319
  const selectionSetNode = parseSelectionSet(`{${fieldNameKey}}`);
1295
- (function aliasFieldsWithArgs(selectionSetNode2) {
1296
- for (const selection of selectionSetNode2.selections) {
1297
- if (selection.kind === Kind.FIELD && selection.arguments?.length) {
1298
- const argsHash = selection.arguments.map(
1299
- (arg) => arg.name.value + // TODO: slow? faster hash?
1300
- memoizedASTPrint(arg.value).replace(
1301
- /[^a-zA-Z0-9]/g,
1302
- ""
1303
- )
1304
- ).join("");
1305
- selection.alias = {
1306
- kind: Kind.NAME,
1307
- value: "_" + selection.name.value + "_" + argsHash
1308
- };
1309
- }
1310
- if ("selectionSet" in selection && selection.selectionSet) {
1311
- aliasFieldsWithArgs(selection.selectionSet);
1312
- }
1313
- }
1314
- })(selectionSetNode);
1320
+ aliasFieldsWithArgs2(selectionSetNode);
1315
1321
  const selectionSet = print(selectionSetNode).replaceAll(/\n/g, " ").replaceAll(/\s+/g, " ");
1316
- extraKeys.add(
1322
+ const extraKey = (
1317
1323
  // remove first and last characters (curly braces)
1318
1324
  selectionSet.slice(1, -1)
1319
1325
  );
1320
- fieldsConfig[fieldName] = {
1326
+ const fieldAliasedNames = /* @__PURE__ */ new Map();
1327
+ for (const sel of selectionSetNode.selections) {
1328
+ if (sel.kind === Kind.FIELD && sel.alias) {
1329
+ fieldAliasedNames.set(sel.name.value, sel.alias.value);
1330
+ }
1331
+ }
1332
+ let targetGroup;
1333
+ for (const group of groups) {
1334
+ let hasConflict = false;
1335
+ for (const [origName, aliasName] of fieldAliasedNames) {
1336
+ const existingAlias = group.aliasedFields.get(origName);
1337
+ if (existingAlias !== void 0 && existingAlias !== aliasName) {
1338
+ hasConflict = true;
1339
+ break;
1340
+ }
1341
+ }
1342
+ if (!hasConflict) {
1343
+ targetGroup = group;
1344
+ break;
1345
+ }
1346
+ }
1347
+ if (!targetGroup) {
1348
+ const newExtraKeys = /* @__PURE__ */ new Set();
1349
+ const newFieldsConfig = {};
1350
+ targetGroup = {
1351
+ extraKeys: newExtraKeys,
1352
+ fieldsConfig: newFieldsConfig,
1353
+ aliasedFields: /* @__PURE__ */ new Map()
1354
+ };
1355
+ groups.push(targetGroup);
1356
+ }
1357
+ targetGroup.extraKeys.add(extraKey);
1358
+ targetGroup.fieldsConfig[fieldName] = {
1321
1359
  selectionSet,
1322
1360
  computed: true
1323
1361
  };
1362
+ for (const [origName, aliasName] of fieldAliasedNames) {
1363
+ targetGroup.aliasedFields.set(origName, aliasName);
1364
+ }
1365
+ }
1366
+ for (const group of groups.slice(1)) {
1367
+ const additionalMergeTypeConfig = {
1368
+ fields: group.fieldsConfig
1369
+ };
1370
+ if (typeNameCanonicalMap.get(typeName) === subgraphName) {
1371
+ additionalMergeTypeConfig.canonical = true;
1372
+ }
1373
+ pendingConflictSubschemaConfigs.push({
1374
+ typeName,
1375
+ // Store closure-captured values for later SubschemaConfig creation
1376
+ // by borrowing getMergedTypeConfigFromKey with the group's extraKeys.
1377
+ mergeTypeConfig: additionalMergeTypeConfig,
1378
+ groupExtraKeys: group.extraKeys
1379
+ });
1324
1380
  }
1325
1381
  }
1326
1382
  if (typeNameCanonicalMap.get(typeName) === subgraphName) {
@@ -1339,6 +1395,21 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1339
1395
  );
1340
1396
  mergedTypeConfig.entryPoints = entryPoints;
1341
1397
  }
1398
+ for (const pending of pendingConflictSubschemaConfigs) {
1399
+ if (pending.typeName === typeName && !("selectionSet" in pending.mergeTypeConfig) && !("entryPoints" in pending.mergeTypeConfig)) {
1400
+ const groupExtraKeys = pending.groupExtraKeys;
1401
+ if (keysArr.length === 1 && keysArr[0]) {
1402
+ Object.assign(
1403
+ pending.mergeTypeConfig,
1404
+ getMergedTypeConfigFromKey2(keysArr[0], groupExtraKeys)
1405
+ );
1406
+ } else if (keysArr.length > 1) {
1407
+ pending.mergeTypeConfig.entryPoints = keysArr.map(
1408
+ (key) => getMergedTypeConfigFromKey2(key, groupExtraKeys)
1409
+ );
1410
+ }
1411
+ }
1412
+ }
1342
1413
  unionTypeNodes.push({
1343
1414
  kind: Kind.NAMED_TYPE,
1344
1415
  name: {
@@ -1729,10 +1800,55 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1729
1800
  }
1730
1801
  }
1731
1802
  }
1803
+ let mainSchema = schema;
1804
+ const additionalGroupFieldsByType = /* @__PURE__ */ new Map();
1805
+ for (const {
1806
+ typeName,
1807
+ mergeTypeConfig
1808
+ } of pendingConflictSubschemaConfigs) {
1809
+ for (const [fieldName, fieldConfig] of Object.entries(
1810
+ mergeTypeConfig.fields ?? {}
1811
+ )) {
1812
+ if (!fieldConfig?.computed) continue;
1813
+ let set = additionalGroupFieldsByType.get(typeName);
1814
+ if (!set) {
1815
+ set = /* @__PURE__ */ new Set();
1816
+ additionalGroupFieldsByType.set(typeName, set);
1817
+ }
1818
+ set.add(fieldName);
1819
+ }
1820
+ }
1821
+ if (additionalGroupFieldsByType.size > 0) {
1822
+ mainSchema = mapSchema(schema, {
1823
+ [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
1824
+ if (additionalGroupFieldsByType.get(typeName)?.has(fieldName)) {
1825
+ return null;
1826
+ }
1827
+ return fieldConfig;
1828
+ }
1829
+ });
1830
+ }
1831
+ const allComputedFieldsByType = new Map(
1832
+ Array.from(additionalGroupFieldsByType, ([t, s]) => [t, new Set(s)])
1833
+ );
1834
+ for (const [typeName, typeConfig] of Object.entries(mergeConfig)) {
1835
+ if (!typeConfig?.fields) continue;
1836
+ for (const [fieldName, fieldConfig] of Object.entries(
1837
+ typeConfig.fields
1838
+ )) {
1839
+ if (!fieldConfig?.computed) continue;
1840
+ let set = allComputedFieldsByType.get(typeName);
1841
+ if (!set) {
1842
+ set = /* @__PURE__ */ new Set();
1843
+ allComputedFieldsByType.set(typeName, set);
1844
+ }
1845
+ set.add(fieldName);
1846
+ }
1847
+ }
1732
1848
  const subschemaConfig = {
1733
1849
  name: subgraphName,
1734
1850
  endpoint,
1735
- schema,
1851
+ schema: mainSchema,
1736
1852
  executor,
1737
1853
  merge: mergeConfig,
1738
1854
  transforms,
@@ -1740,6 +1856,36 @@ function getStitchingOptionsFromSupergraphSdl(opts) {
1740
1856
  batchingOptions: opts.batchingOptions
1741
1857
  };
1742
1858
  subschemas.push(subschemaConfig);
1859
+ for (const {
1860
+ typeName,
1861
+ mergeTypeConfig
1862
+ } of pendingConflictSubschemaConfigs) {
1863
+ const groupOwnFields = new Set(
1864
+ Object.entries(mergeTypeConfig.fields ?? {}).filter(([, fc]) => fc?.computed).map(([fn]) => fn)
1865
+ );
1866
+ const allComputedForType = allComputedFieldsByType.get(typeName) ?? /* @__PURE__ */ new Set();
1867
+ const fieldsToRemove = new Set(
1868
+ [...allComputedForType].filter((f) => !groupOwnFields.has(f))
1869
+ );
1870
+ const groupSchema = fieldsToRemove.size > 0 ? mapSchema(schema, {
1871
+ [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, type) => {
1872
+ if (type === typeName && fieldsToRemove.has(fieldName)) {
1873
+ return null;
1874
+ }
1875
+ return fieldConfig;
1876
+ }
1877
+ }) : schema;
1878
+ subschemas.push({
1879
+ name: subgraphName,
1880
+ endpoint,
1881
+ schema: groupSchema,
1882
+ executor,
1883
+ merge: { [typeName]: mergeTypeConfig },
1884
+ transforms,
1885
+ batch: opts.batch,
1886
+ batchingOptions: opts.batchingOptions
1887
+ });
1888
+ }
1743
1889
  }
1744
1890
  const defaultMerger = getDefaultFieldConfigMerger(true);
1745
1891
  const fieldConfigMerger = function(candidates) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-tools/federation",
3
- "version": "4.4.2",
3
+ "version": "4.4.3-alpha-12654e24df4f034d8c7112f5052f1b37674985ae",
4
4
  "type": "module",
5
5
  "description": "Useful tools to create and manipulate GraphQL schemas.",
6
6
  "repository": {