@apollo/federation-internals 2.4.0 → 2.4.1

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.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.operationToDocument = exports.parseSelectionSet = exports.parseOperation = exports.operationFromDocument = exports.FragmentSelection = exports.FieldSelection = exports.selectionOfElement = exports.selectionSetOfElement = exports.selectionSetOf = exports.allFieldDefinitionsInSelectionSet = exports.MutableSelectionSet = exports.SelectionSetUpdates = exports.SelectionSet = exports.NamedFragments = exports.NamedFragmentDefinition = exports.Operation = exports.concatOperationPaths = exports.conditionalDirectivesInOperationPath = exports.sameOperationPaths = exports.operationPathToStringPath = exports.FragmentElement = exports.Field = void 0;
4
4
  const graphql_1 = require("graphql");
5
5
  const definitions_1 = require("./definitions");
6
+ const federation_1 = require("./federation");
6
7
  const error_1 = require("./error");
7
8
  const types_1 = require("./types");
8
9
  const utils_1 = require("./utils");
@@ -174,7 +175,8 @@ class Field extends AbstractOperationElement {
174
175
  canRebaseOn(parentType) {
175
176
  const fieldParentType = this.definition.parent;
176
177
  return parentType.name === fieldParentType.name
177
- || ((0, definitions_1.isInterfaceType)(fieldParentType) && fieldParentType.allImplementations().some(i => i.name === parentType.name));
178
+ || (0, definitions_1.isInterfaceType)(fieldParentType)
179
+ || (0, federation_1.isInterfaceObjectType)(fieldParentType);
178
180
  }
179
181
  typeIfAddedTo(parentType) {
180
182
  var _a, _b;
@@ -557,8 +559,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
557
559
  };
558
560
  }
559
561
  canApplyAtType(type) {
560
- const applyAtType = (0, types_1.sameType)(this.typeCondition, type)
561
- || ((0, definitions_1.isAbstractType)(this.typeCondition) && !(0, definitions_1.isUnionType)(type) && (0, types_1.isDirectSubtype)(this.typeCondition, type));
562
+ const applyAtType = (0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.runtimeTypesIntersects)(type, this.typeCondition);
562
563
  return applyAtType
563
564
  && this.validForSchema(type.schema());
564
565
  }
@@ -839,31 +840,67 @@ class SelectionSet {
839
840
  }
840
841
  return true;
841
842
  }
842
- contains(that) {
843
- if (this._selections.length < that._selections.length) {
844
- return false;
843
+ triviallyNestedSelectionsForKey(parentType, key) {
844
+ const found = [];
845
+ for (const selection of this.selections()) {
846
+ if (selection.isUnecessaryInlineFragment(parentType)) {
847
+ const selectionForKey = selection.selectionSet._keyedSelections.get(key);
848
+ if (selectionForKey) {
849
+ found.push(selectionForKey);
850
+ }
851
+ for (const nestedSelection of selection.selectionSet.triviallyNestedSelectionsForKey(parentType, key)) {
852
+ found.push(nestedSelection);
853
+ }
854
+ }
855
+ }
856
+ return found;
857
+ }
858
+ mergeSameKeySelections(selections) {
859
+ if (selections.length === 0) {
860
+ return undefined;
861
+ }
862
+ const first = selections[0];
863
+ if (!first.selectionSet || (first instanceof FragmentSpreadSelection) || selections.length === 1) {
864
+ return first;
865
+ }
866
+ const mergedSubselections = new SelectionSetUpdates();
867
+ for (const selection of selections) {
868
+ mergedSubselections.add(selection.selectionSet);
845
869
  }
870
+ return first.withUpdatedSelectionSet(mergedSubselections.toSelectionSet(first.selectionSet.parentType));
871
+ }
872
+ contains(that) {
846
873
  for (const [key, thatSelection] of that._keyedSelections) {
847
874
  const thisSelection = this._keyedSelections.get(key);
848
- if (!thisSelection || !thisSelection.contains(thatSelection)) {
875
+ const otherSelections = this.triviallyNestedSelectionsForKey(this.parentType, key);
876
+ const mergedSelection = this.mergeSameKeySelections([thisSelection].concat(otherSelections).filter(utils_1.isDefined));
877
+ if (!(mergedSelection && mergedSelection.contains(thatSelection))
878
+ && !(thatSelection.isUnecessaryInlineFragment(this.parentType) && this.contains(thatSelection.selectionSet))) {
849
879
  return false;
850
880
  }
851
881
  }
852
882
  return true;
853
883
  }
884
+ diffIfContains(that) {
885
+ if (this.contains(that)) {
886
+ const diff = this.minus(that);
887
+ return { contains: true, diff: diff.isEmpty() ? undefined : diff };
888
+ }
889
+ return { contains: false };
890
+ }
854
891
  minus(that) {
855
892
  const updated = new SelectionSetUpdates();
856
893
  for (const [key, thisSelection] of this._keyedSelections) {
857
894
  const thatSelection = that._keyedSelections.get(key);
858
- if (!thatSelection) {
895
+ const otherSelections = that.triviallyNestedSelectionsForKey(this.parentType, key);
896
+ const allSelections = thatSelection ? [thatSelection].concat(otherSelections) : otherSelections;
897
+ if (allSelections.length === 0) {
859
898
  updated.add(thisSelection);
860
899
  }
861
900
  else {
862
- if (thisSelection.selectionSet && thatSelection.selectionSet) {
863
- const updatedSubSelectionSet = thisSelection.selectionSet.minus(thatSelection.selectionSet);
864
- if (!updatedSubSelectionSet.isEmpty()) {
865
- updated.add(thisSelection.withUpdatedSelectionSet(updatedSubSelectionSet));
866
- }
901
+ const selectionDiff = allSelections.reduce((prev, val) => prev === null || prev === void 0 ? void 0 : prev.minus(val), thisSelection);
902
+ if (selectionDiff) {
903
+ updated.add(selectionDiff);
867
904
  }
868
905
  }
869
906
  }
@@ -1231,6 +1268,39 @@ class AbstractSelection {
1231
1268
  ? this.us()
1232
1269
  : this.withUpdatedSelectionSet(updatedSelectionSet);
1233
1270
  }
1271
+ minus(that) {
1272
+ if (this.selectionSet && that.selectionSet) {
1273
+ const updatedSubSelectionSet = this.selectionSet.minus(that.selectionSet);
1274
+ if (!updatedSubSelectionSet.isEmpty()) {
1275
+ return this.withUpdatedSelectionSet(updatedSubSelectionSet);
1276
+ }
1277
+ }
1278
+ return undefined;
1279
+ }
1280
+ tryOptimizeSubselectionOnce(_) {
1281
+ (0, utils_1.assert)(false, `UNSUPPORTED`);
1282
+ }
1283
+ tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, fragmentFilter, }) {
1284
+ let candidates = fragments.maybeApplyingAtType(parentType);
1285
+ if (fragmentFilter) {
1286
+ candidates = candidates.filter(fragmentFilter);
1287
+ }
1288
+ let shouldTryAgain;
1289
+ do {
1290
+ const { spread, optimizedSelection, hasDiff } = this.tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments });
1291
+ if (optimizedSelection) {
1292
+ subSelection = optimizedSelection;
1293
+ }
1294
+ else if (spread) {
1295
+ return spread;
1296
+ }
1297
+ shouldTryAgain = !!spread && !!hasDiff;
1298
+ if (shouldTryAgain) {
1299
+ candidates = candidates.filter((c) => c !== (spread === null || spread === void 0 ? void 0 : spread.namedFragment));
1300
+ }
1301
+ } while (shouldTryAgain);
1302
+ return subSelection;
1303
+ }
1234
1304
  }
1235
1305
  class FieldSelection extends AbstractSelection {
1236
1306
  constructor(field, _selectionSet) {
@@ -1251,20 +1321,36 @@ class FieldSelection extends AbstractSelection {
1251
1321
  return this.element.key();
1252
1322
  }
1253
1323
  optimize(fragments) {
1254
- const optimizedSelection = this.selectionSet ? this.selectionSet.optimize(fragments) : undefined;
1324
+ let optimizedSelection = this.selectionSet ? this.selectionSet.optimize(fragments) : undefined;
1255
1325
  const fieldBaseType = (0, definitions_1.baseType)(this.element.definition.type);
1256
1326
  if ((0, definitions_1.isCompositeType)(fieldBaseType) && optimizedSelection) {
1257
- for (const candidate of fragments.maybeApplyingAtType(fieldBaseType)) {
1258
- if (optimizedSelection.equals(candidate.selectionSet)) {
1259
- const fragmentSelection = new FragmentSpreadSelection(fieldBaseType, fragments, candidate, []);
1260
- return new FieldSelection(this.element, selectionSetOf(fieldBaseType, fragmentSelection));
1261
- }
1262
- }
1327
+ const optimized = this.tryOptimizeSubselectionWithFragments({
1328
+ parentType: fieldBaseType,
1329
+ subSelection: optimizedSelection,
1330
+ fragments,
1331
+ fragmentFilter: (f) => f.appliedDirectives.length === 0,
1332
+ });
1333
+ (0, utils_1.assert)(!(optimized instanceof FragmentSpreadSelection), 'tryOptimizeSubselectionOnce should never return only a spread');
1334
+ optimizedSelection = optimized;
1263
1335
  }
1264
1336
  return this.selectionSet === optimizedSelection
1265
1337
  ? this
1266
1338
  : new FieldSelection(this.element, optimizedSelection);
1267
1339
  }
1340
+ tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1341
+ let optimizedSelection = subSelection;
1342
+ for (const candidate of candidates) {
1343
+ const { contains, diff } = optimizedSelection.diffIfContains(candidate.selectionSet);
1344
+ if (contains) {
1345
+ const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1346
+ optimizedSelection = diff
1347
+ ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1348
+ : selectionSetOf(parentType, spread);
1349
+ return { spread, optimizedSelection, hasDiff: !!diff };
1350
+ }
1351
+ }
1352
+ return {};
1353
+ }
1268
1354
  filter(predicate) {
1269
1355
  if (!this.selectionSet) {
1270
1356
  return predicate(this) ? this : undefined;
@@ -1378,6 +1464,9 @@ class FieldSelection extends AbstractSelection {
1378
1464
  }
1379
1465
  return !!this.selectionSet && this.selectionSet.contains(that.selectionSet);
1380
1466
  }
1467
+ isUnecessaryInlineFragment(_) {
1468
+ return false;
1469
+ }
1381
1470
  toString(expandFragments = true, indent) {
1382
1471
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + (this.selectionSet ? ' ' + this.selectionSet.toString(expandFragments, true, indent) : '');
1383
1472
  }
@@ -1409,18 +1498,11 @@ class FragmentSelection extends AbstractSelection {
1409
1498
  hasDefer() {
1410
1499
  return this.element.hasDefer() || this.selectionSet.hasDefer();
1411
1500
  }
1412
- equals(that) {
1413
- if (this === that) {
1414
- return true;
1415
- }
1416
- return (that instanceof FragmentSelection)
1417
- && this.element.equals(that.element)
1418
- && this.selectionSet.equals(that.selectionSet);
1419
- }
1420
- contains(that) {
1421
- return (that instanceof FragmentSelection)
1422
- && this.element.equals(that.element)
1423
- && this.selectionSet.contains(that.selectionSet);
1501
+ isUnecessaryInlineFragment(parentType) {
1502
+ return this.element.appliedDirectives.length === 0
1503
+ && !!this.element.typeCondition
1504
+ && (this.element.typeCondition.name === parentType.name
1505
+ || ((0, definitions_1.isObjectType)(parentType) && (0, definitions_1.possibleRuntimeTypes)(this.element.typeCondition).some((t) => t.name === parentType.name)));
1424
1506
  }
1425
1507
  }
1426
1508
  exports.FragmentSelection = FragmentSelection;
@@ -1488,28 +1570,49 @@ class InlineFragmentSelection extends FragmentSelection {
1488
1570
  let optimizedSelection = this.selectionSet.optimize(fragments);
1489
1571
  const typeCondition = this.element.typeCondition;
1490
1572
  if (typeCondition) {
1491
- for (const candidate of fragments.maybeApplyingAtType(typeCondition)) {
1492
- if (optimizedSelection.equals(candidate.selectionSet)) {
1493
- let spreadDirectives = [];
1494
- if (this.element.appliedDirectives) {
1573
+ const optimized = this.tryOptimizeSubselectionWithFragments({
1574
+ parentType: typeCondition,
1575
+ subSelection: optimizedSelection,
1576
+ fragments,
1577
+ });
1578
+ if (optimized instanceof FragmentSpreadSelection) {
1579
+ return optimized;
1580
+ }
1581
+ optimizedSelection = optimized;
1582
+ }
1583
+ return this.selectionSet === optimizedSelection
1584
+ ? this
1585
+ : new InlineFragmentSelection(this.element, optimizedSelection);
1586
+ }
1587
+ tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1588
+ let optimizedSelection = subSelection;
1589
+ for (const candidate of candidates) {
1590
+ const { contains, diff } = optimizedSelection.diffIfContains(candidate.selectionSet);
1591
+ if (contains) {
1592
+ if (!diff && (0, types_1.sameType)(this.element.typeCondition, candidate.typeCondition)) {
1593
+ let spreadDirectives = this.element.appliedDirectives;
1594
+ if (candidate.appliedDirectives.length > 0) {
1495
1595
  const { isSubset, difference } = diffDirectives(this.element.appliedDirectives, candidate.appliedDirectives);
1496
1596
  if (!isSubset) {
1497
1597
  continue;
1498
1598
  }
1499
1599
  spreadDirectives = difference;
1500
1600
  }
1501
- const newSelection = new FragmentSpreadSelection(this.parentType, fragments, candidate, spreadDirectives);
1502
- if ((0, types_1.sameType)(typeCondition, candidate.typeCondition)) {
1503
- return newSelection;
1504
- }
1505
- optimizedSelection = selectionSetOf(this.parentType, newSelection);
1506
- break;
1601
+ return {
1602
+ spread: new FragmentSpreadSelection(this.parentType, fragments, candidate, spreadDirectives),
1603
+ };
1604
+ }
1605
+ if (candidate.appliedDirectives.length > 0) {
1606
+ continue;
1507
1607
  }
1608
+ const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1609
+ optimizedSelection = diff
1610
+ ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1611
+ : selectionSetOf(parentType, spread);
1612
+ return { spread, optimizedSelection, hasDiff: !!diff };
1508
1613
  }
1509
1614
  }
1510
- return this.selectionSet === optimizedSelection
1511
- ? this
1512
- : new InlineFragmentSelection(this.element, optimizedSelection);
1615
+ return {};
1513
1616
  }
1514
1617
  withoutDefer(labelsToRemove) {
1515
1618
  const newSelection = this.selectionSet.withoutDefer(labelsToRemove);
@@ -1543,7 +1646,7 @@ class InlineFragmentSelection extends FragmentSelection {
1543
1646
  return trimmed.isEmpty() ? undefined : trimmed;
1544
1647
  }
1545
1648
  if ((0, definitions_1.isObjectType)(currentType)) {
1546
- if ((0, definitions_1.isObjectType)(thisCondition)) {
1649
+ if ((0, definitions_1.isObjectType)(thisCondition) || !(0, definitions_1.possibleRuntimeTypes)(thisCondition).includes(currentType)) {
1547
1650
  return undefined;
1548
1651
  }
1549
1652
  else {
@@ -1591,6 +1694,19 @@ class InlineFragmentSelection extends FragmentSelection {
1591
1694
  expandFragments(names, updatedFragments) {
1592
1695
  return this.mapToSelectionSet((s) => s.expandFragments(names, updatedFragments));
1593
1696
  }
1697
+ equals(that) {
1698
+ if (this === that) {
1699
+ return true;
1700
+ }
1701
+ return (that instanceof FragmentSelection)
1702
+ && this.element.equals(that.element)
1703
+ && this.selectionSet.equals(that.selectionSet);
1704
+ }
1705
+ contains(that) {
1706
+ return (that instanceof FragmentSelection)
1707
+ && this.element.equals(that.element)
1708
+ && this.selectionSet.contains(that.selectionSet);
1709
+ }
1594
1710
  toString(expandFragments = true, indent) {
1595
1711
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + ' ' + this.selectionSet.toString(expandFragments, true, indent);
1596
1712
  }
@@ -1685,6 +1801,26 @@ class FragmentSpreadSelection extends FragmentSelection {
1685
1801
  withNormalizedDefer(_normalizer) {
1686
1802
  (0, utils_1.assert)(false, 'Unsupported, see `Operation.withAllDeferLabelled`');
1687
1803
  }
1804
+ minus(that) {
1805
+ (0, utils_1.assert)(this.equals(that), () => `Invalid operation for ${this.toString(false)} and ${that.toString(false)}`);
1806
+ return undefined;
1807
+ }
1808
+ equals(that) {
1809
+ if (this === that) {
1810
+ return true;
1811
+ }
1812
+ return (that instanceof FragmentSpreadSelection)
1813
+ && this.namedFragment.name === that.namedFragment.name
1814
+ && (0, definitions_1.sameDirectiveApplications)(this.spreadDirectives, that.spreadDirectives);
1815
+ }
1816
+ contains(that) {
1817
+ if (this.equals(that)) {
1818
+ return true;
1819
+ }
1820
+ return (that instanceof FragmentSelection)
1821
+ && this.element.equals(that.element)
1822
+ && this.selectionSet.contains(that.selectionSet);
1823
+ }
1688
1824
  toString(expandFragments = true, indent) {
1689
1825
  if (expandFragments) {
1690
1826
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + ' ' + this.selectionSet.toString(true, true, indent);