@apollo/federation-internals 2.4.0 → 2.4.2

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/argumentCompositionStrategies.d.ts +34 -0
  3. package/dist/argumentCompositionStrategies.d.ts.map +1 -0
  4. package/dist/argumentCompositionStrategies.js +35 -0
  5. package/dist/argumentCompositionStrategies.js.map +1 -0
  6. package/dist/coreSpec.d.ts +12 -3
  7. package/dist/coreSpec.d.ts.map +1 -1
  8. package/dist/coreSpec.js +69 -18
  9. package/dist/coreSpec.js.map +1 -1
  10. package/dist/definitions.d.ts +1 -0
  11. package/dist/definitions.d.ts.map +1 -1
  12. package/dist/definitions.js +30 -27
  13. package/dist/definitions.js.map +1 -1
  14. package/dist/directiveAndTypeSpecification.d.ts +26 -7
  15. package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
  16. package/dist/directiveAndTypeSpecification.js +56 -4
  17. package/dist/directiveAndTypeSpecification.js.map +1 -1
  18. package/dist/federation.d.ts.map +1 -1
  19. package/dist/federation.js +24 -2
  20. package/dist/federation.js.map +1 -1
  21. package/dist/federationSpec.d.ts +2 -13
  22. package/dist/federationSpec.d.ts.map +1 -1
  23. package/dist/federationSpec.js +10 -60
  24. package/dist/federationSpec.js.map +1 -1
  25. package/dist/inaccessibleSpec.d.ts +0 -2
  26. package/dist/inaccessibleSpec.d.ts.map +1 -1
  27. package/dist/inaccessibleSpec.js +3 -6
  28. package/dist/inaccessibleSpec.js.map +1 -1
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/knownCoreFeatures.d.ts +1 -0
  34. package/dist/knownCoreFeatures.d.ts.map +1 -1
  35. package/dist/knownCoreFeatures.js +5 -1
  36. package/dist/knownCoreFeatures.js.map +1 -1
  37. package/dist/operations.d.ts +92 -1
  38. package/dist/operations.d.ts.map +1 -1
  39. package/dist/operations.js +192 -48
  40. package/dist/operations.js.map +1 -1
  41. package/dist/print.d.ts +7 -1
  42. package/dist/print.d.ts.map +1 -1
  43. package/dist/print.js +33 -5
  44. package/dist/print.js.map +1 -1
  45. package/dist/tagSpec.d.ts +0 -2
  46. package/dist/tagSpec.d.ts.map +1 -1
  47. package/dist/tagSpec.js +4 -10
  48. package/dist/tagSpec.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/__tests__/directiveAndTypeSpecifications.test.ts +41 -0
  51. package/src/__tests__/operations.test.ts +598 -45
  52. package/src/__tests__/schemaUpgrader.test.ts +1 -1
  53. package/src/argumentCompositionStrategies.ts +39 -0
  54. package/src/coreSpec.ts +94 -34
  55. package/src/definitions.ts +35 -29
  56. package/src/directiveAndTypeSpecification.ts +101 -14
  57. package/src/federation.ts +33 -4
  58. package/src/federationSpec.ts +13 -73
  59. package/src/inaccessibleSpec.ts +4 -11
  60. package/src/index.ts +3 -0
  61. package/src/knownCoreFeatures.ts +9 -0
  62. package/src/operations.ts +318 -102
  63. package/src/print.ts +39 -4
  64. package/src/tagSpec.ts +4 -12
  65. package/tsconfig.tsbuildinfo +1 -1
@@ -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;
@@ -435,7 +437,7 @@ function isUselessFollowupElement(first, followup, conditionals) {
435
437
  && followup.kind === 'FragmentElement'
436
438
  && !!followup.typeCondition
437
439
  && (followup.appliedDirectives.length === 0 || (0, definitions_1.isDirectiveApplicationsSubset)(conditionals, followup.appliedDirectives))
438
- && (0, types_1.sameType)(typeOfFirst, followup.typeCondition);
440
+ && (0, types_1.isSubtype)(followup.typeCondition, typeOfFirst);
439
441
  }
440
442
  class Operation {
441
443
  constructor(schema, rootKind, selectionSet, variableDefinitions, name) {
@@ -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
+ }
845
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);
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
  }
@@ -1032,9 +1069,17 @@ function addOneToKeyedUpdates(keyedUpdates, selection) {
1032
1069
  keyedUpdates.add(selection.key(), selection);
1033
1070
  }
1034
1071
  }
1072
+ function maybeRebaseOnSchema(toRebase, schema) {
1073
+ if (toRebase.schema() === schema) {
1074
+ return toRebase;
1075
+ }
1076
+ const rebased = schema.type(toRebase.name);
1077
+ (0, utils_1.assert)(rebased && (0, definitions_1.isCompositeType)(rebased), () => `Expected ${toRebase} to exists and be composite in the rebased schema, but got ${rebased === null || rebased === void 0 ? void 0 : rebased.kind}`);
1078
+ return rebased;
1079
+ }
1035
1080
  function isUnecessaryFragment(parentType, fragment) {
1036
1081
  return fragment.element.appliedDirectives.length === 0
1037
- && (!fragment.element.typeCondition || (0, types_1.sameType)(parentType, fragment.element.typeCondition));
1082
+ && (!fragment.element.typeCondition || (0, types_1.isSubtype)(maybeRebaseOnSchema(fragment.element.typeCondition, parentType.schema()), parentType));
1038
1083
  }
1039
1084
  function withUnecessaryFragmentsRemoved(parentType, selections) {
1040
1085
  if (selections instanceof AbstractSelection) {
@@ -1231,6 +1276,39 @@ class AbstractSelection {
1231
1276
  ? this.us()
1232
1277
  : this.withUpdatedSelectionSet(updatedSelectionSet);
1233
1278
  }
1279
+ minus(that) {
1280
+ if (this.selectionSet && that.selectionSet) {
1281
+ const updatedSubSelectionSet = this.selectionSet.minus(that.selectionSet);
1282
+ if (!updatedSubSelectionSet.isEmpty()) {
1283
+ return this.withUpdatedSelectionSet(updatedSubSelectionSet);
1284
+ }
1285
+ }
1286
+ return undefined;
1287
+ }
1288
+ tryOptimizeSubselectionOnce(_) {
1289
+ (0, utils_1.assert)(false, `UNSUPPORTED`);
1290
+ }
1291
+ tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, fragmentFilter, }) {
1292
+ let candidates = fragments.maybeApplyingAtType(parentType);
1293
+ if (fragmentFilter) {
1294
+ candidates = candidates.filter(fragmentFilter);
1295
+ }
1296
+ let shouldTryAgain;
1297
+ do {
1298
+ const { spread, optimizedSelection, hasDiff } = this.tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments });
1299
+ if (optimizedSelection) {
1300
+ subSelection = optimizedSelection;
1301
+ }
1302
+ else if (spread) {
1303
+ return spread;
1304
+ }
1305
+ shouldTryAgain = !!spread && !!hasDiff;
1306
+ if (shouldTryAgain) {
1307
+ candidates = candidates.filter((c) => c !== (spread === null || spread === void 0 ? void 0 : spread.namedFragment));
1308
+ }
1309
+ } while (shouldTryAgain);
1310
+ return subSelection;
1311
+ }
1234
1312
  }
1235
1313
  class FieldSelection extends AbstractSelection {
1236
1314
  constructor(field, _selectionSet) {
@@ -1251,20 +1329,36 @@ class FieldSelection extends AbstractSelection {
1251
1329
  return this.element.key();
1252
1330
  }
1253
1331
  optimize(fragments) {
1254
- const optimizedSelection = this.selectionSet ? this.selectionSet.optimize(fragments) : undefined;
1332
+ let optimizedSelection = this.selectionSet ? this.selectionSet.optimize(fragments) : undefined;
1255
1333
  const fieldBaseType = (0, definitions_1.baseType)(this.element.definition.type);
1256
1334
  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
- }
1335
+ const optimized = this.tryOptimizeSubselectionWithFragments({
1336
+ parentType: fieldBaseType,
1337
+ subSelection: optimizedSelection,
1338
+ fragments,
1339
+ fragmentFilter: (f) => f.appliedDirectives.length === 0,
1340
+ });
1341
+ (0, utils_1.assert)(!(optimized instanceof FragmentSpreadSelection), 'tryOptimizeSubselectionOnce should never return only a spread');
1342
+ optimizedSelection = optimized;
1263
1343
  }
1264
1344
  return this.selectionSet === optimizedSelection
1265
1345
  ? this
1266
1346
  : new FieldSelection(this.element, optimizedSelection);
1267
1347
  }
1348
+ tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1349
+ let optimizedSelection = subSelection;
1350
+ for (const candidate of candidates) {
1351
+ const { contains, diff } = optimizedSelection.diffIfContains(candidate.selectionSet);
1352
+ if (contains) {
1353
+ const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1354
+ optimizedSelection = diff
1355
+ ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1356
+ : selectionSetOf(parentType, spread);
1357
+ return { spread, optimizedSelection, hasDiff: !!diff };
1358
+ }
1359
+ }
1360
+ return {};
1361
+ }
1268
1362
  filter(predicate) {
1269
1363
  if (!this.selectionSet) {
1270
1364
  return predicate(this) ? this : undefined;
@@ -1378,6 +1472,9 @@ class FieldSelection extends AbstractSelection {
1378
1472
  }
1379
1473
  return !!this.selectionSet && this.selectionSet.contains(that.selectionSet);
1380
1474
  }
1475
+ isUnecessaryInlineFragment(_) {
1476
+ return false;
1477
+ }
1381
1478
  toString(expandFragments = true, indent) {
1382
1479
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + (this.selectionSet ? ' ' + this.selectionSet.toString(expandFragments, true, indent) : '');
1383
1480
  }
@@ -1409,18 +1506,11 @@ class FragmentSelection extends AbstractSelection {
1409
1506
  hasDefer() {
1410
1507
  return this.element.hasDefer() || this.selectionSet.hasDefer();
1411
1508
  }
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);
1509
+ isUnecessaryInlineFragment(parentType) {
1510
+ return this.element.appliedDirectives.length === 0
1511
+ && !!this.element.typeCondition
1512
+ && (this.element.typeCondition.name === parentType.name
1513
+ || ((0, definitions_1.isObjectType)(parentType) && (0, definitions_1.possibleRuntimeTypes)(this.element.typeCondition).some((t) => t.name === parentType.name)));
1424
1514
  }
1425
1515
  }
1426
1516
  exports.FragmentSelection = FragmentSelection;
@@ -1488,28 +1578,49 @@ class InlineFragmentSelection extends FragmentSelection {
1488
1578
  let optimizedSelection = this.selectionSet.optimize(fragments);
1489
1579
  const typeCondition = this.element.typeCondition;
1490
1580
  if (typeCondition) {
1491
- for (const candidate of fragments.maybeApplyingAtType(typeCondition)) {
1492
- if (optimizedSelection.equals(candidate.selectionSet)) {
1493
- let spreadDirectives = [];
1494
- if (this.element.appliedDirectives) {
1581
+ const optimized = this.tryOptimizeSubselectionWithFragments({
1582
+ parentType: typeCondition,
1583
+ subSelection: optimizedSelection,
1584
+ fragments,
1585
+ });
1586
+ if (optimized instanceof FragmentSpreadSelection) {
1587
+ return optimized;
1588
+ }
1589
+ optimizedSelection = optimized;
1590
+ }
1591
+ return this.selectionSet === optimizedSelection
1592
+ ? this
1593
+ : new InlineFragmentSelection(this.element, optimizedSelection);
1594
+ }
1595
+ tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1596
+ let optimizedSelection = subSelection;
1597
+ for (const candidate of candidates) {
1598
+ const { contains, diff } = optimizedSelection.diffIfContains(candidate.selectionSet);
1599
+ if (contains) {
1600
+ if (!diff && (0, types_1.sameType)(this.element.typeCondition, candidate.typeCondition)) {
1601
+ let spreadDirectives = this.element.appliedDirectives;
1602
+ if (candidate.appliedDirectives.length > 0) {
1495
1603
  const { isSubset, difference } = diffDirectives(this.element.appliedDirectives, candidate.appliedDirectives);
1496
1604
  if (!isSubset) {
1497
1605
  continue;
1498
1606
  }
1499
1607
  spreadDirectives = difference;
1500
1608
  }
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;
1609
+ return {
1610
+ spread: new FragmentSpreadSelection(this.parentType, fragments, candidate, spreadDirectives),
1611
+ };
1612
+ }
1613
+ if (candidate.appliedDirectives.length > 0) {
1614
+ continue;
1507
1615
  }
1616
+ const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1617
+ optimizedSelection = diff
1618
+ ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1619
+ : selectionSetOf(parentType, spread);
1620
+ return { spread, optimizedSelection, hasDiff: !!diff };
1508
1621
  }
1509
1622
  }
1510
- return this.selectionSet === optimizedSelection
1511
- ? this
1512
- : new InlineFragmentSelection(this.element, optimizedSelection);
1623
+ return {};
1513
1624
  }
1514
1625
  withoutDefer(labelsToRemove) {
1515
1626
  const newSelection = this.selectionSet.withoutDefer(labelsToRemove);
@@ -1543,7 +1654,7 @@ class InlineFragmentSelection extends FragmentSelection {
1543
1654
  return trimmed.isEmpty() ? undefined : trimmed;
1544
1655
  }
1545
1656
  if ((0, definitions_1.isObjectType)(currentType)) {
1546
- if ((0, definitions_1.isObjectType)(thisCondition)) {
1657
+ if ((0, definitions_1.isObjectType)(thisCondition) || !(0, definitions_1.possibleRuntimeTypes)(thisCondition).includes(currentType)) {
1547
1658
  return undefined;
1548
1659
  }
1549
1660
  else {
@@ -1591,6 +1702,19 @@ class InlineFragmentSelection extends FragmentSelection {
1591
1702
  expandFragments(names, updatedFragments) {
1592
1703
  return this.mapToSelectionSet((s) => s.expandFragments(names, updatedFragments));
1593
1704
  }
1705
+ equals(that) {
1706
+ if (this === that) {
1707
+ return true;
1708
+ }
1709
+ return (that instanceof FragmentSelection)
1710
+ && this.element.equals(that.element)
1711
+ && this.selectionSet.equals(that.selectionSet);
1712
+ }
1713
+ contains(that) {
1714
+ return (that instanceof FragmentSelection)
1715
+ && this.element.equals(that.element)
1716
+ && this.selectionSet.contains(that.selectionSet);
1717
+ }
1594
1718
  toString(expandFragments = true, indent) {
1595
1719
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + ' ' + this.selectionSet.toString(expandFragments, true, indent);
1596
1720
  }
@@ -1685,6 +1809,26 @@ class FragmentSpreadSelection extends FragmentSelection {
1685
1809
  withNormalizedDefer(_normalizer) {
1686
1810
  (0, utils_1.assert)(false, 'Unsupported, see `Operation.withAllDeferLabelled`');
1687
1811
  }
1812
+ minus(that) {
1813
+ (0, utils_1.assert)(this.equals(that), () => `Invalid operation for ${this.toString(false)} and ${that.toString(false)}`);
1814
+ return undefined;
1815
+ }
1816
+ equals(that) {
1817
+ if (this === that) {
1818
+ return true;
1819
+ }
1820
+ return (that instanceof FragmentSpreadSelection)
1821
+ && this.namedFragment.name === that.namedFragment.name
1822
+ && (0, definitions_1.sameDirectiveApplications)(this.spreadDirectives, that.spreadDirectives);
1823
+ }
1824
+ contains(that) {
1825
+ if (this.equals(that)) {
1826
+ return true;
1827
+ }
1828
+ return (that instanceof FragmentSelection)
1829
+ && this.element.equals(that.element)
1830
+ && this.selectionSet.contains(that.selectionSet);
1831
+ }
1688
1832
  toString(expandFragments = true, indent) {
1689
1833
  if (expandFragments) {
1690
1834
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + ' ' + this.selectionSet.toString(true, true, indent);