@apollo/federation-internals 2.4.1 → 2.4.3

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 (64) hide show
  1. package/CHANGELOG.md +21 -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 +68 -17
  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 +18 -6
  38. package/dist/operations.d.ts.map +1 -1
  39. package/dist/operations.js +102 -37
  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 +175 -10
  52. package/src/argumentCompositionStrategies.ts +39 -0
  53. package/src/coreSpec.ts +94 -34
  54. package/src/definitions.ts +35 -29
  55. package/src/directiveAndTypeSpecification.ts +101 -14
  56. package/src/federation.ts +33 -4
  57. package/src/federationSpec.ts +13 -73
  58. package/src/inaccessibleSpec.ts +4 -11
  59. package/src/index.ts +3 -0
  60. package/src/knownCoreFeatures.ts +9 -0
  61. package/src/operations.ts +198 -40
  62. package/src/print.ts +39 -4
  63. package/src/tagSpec.ts +4 -12
  64. package/tsconfig.tsbuildinfo +1 -1
@@ -437,7 +437,7 @@ function isUselessFollowupElement(first, followup, conditionals) {
437
437
  && followup.kind === 'FragmentElement'
438
438
  && !!followup.typeCondition
439
439
  && (followup.appliedDirectives.length === 0 || (0, definitions_1.isDirectiveApplicationsSubset)(conditionals, followup.appliedDirectives))
440
- && (0, types_1.sameType)(typeOfFirst, followup.typeCondition);
440
+ && (0, types_1.isSubtype)(followup.typeCondition, typeOfFirst);
441
441
  }
442
442
  class Operation {
443
443
  constructor(schema, rootKind, selectionSet, variableDefinitions, name) {
@@ -465,8 +465,11 @@ class Operation {
465
465
  }
466
466
  }
467
467
  const toDeoptimize = (0, utils_1.mapEntries)(usages).filter(([_, count]) => count < minUsagesToOptimize).map(([name]) => name);
468
- const newFragments = (_a = optimizedSelection.fragments) === null || _a === void 0 ? void 0 : _a.without(toDeoptimize);
469
- optimizedSelection = optimizedSelection.expandFragments(toDeoptimize, newFragments);
468
+ if (toDeoptimize.length > 0) {
469
+ const newFragments = (_a = optimizedSelection.fragments) === null || _a === void 0 ? void 0 : _a.without(toDeoptimize);
470
+ optimizedSelection = optimizedSelection.expandFragments(toDeoptimize, newFragments);
471
+ optimizedSelection = optimizedSelection.trimUnsatisfiableBranches(optimizedSelection.parentType);
472
+ }
470
473
  return new Operation(this.schema, this.rootKind, optimizedSelection, this.variableDefinitions, this.name);
471
474
  }
472
475
  expandAllFragments() {
@@ -525,9 +528,11 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
525
528
  super(schema, directives);
526
529
  this.name = name;
527
530
  this.typeCondition = typeCondition;
531
+ this.selectionSetsAtTypesCache = new Map();
528
532
  }
529
533
  setSelectionSet(selectionSet) {
530
534
  (0, utils_1.assert)(!this._selectionSet, 'Attempting to set the selection set of a fragment definition already built');
535
+ (0, utils_1.assert)(selectionSet.parentType === this.typeCondition, `Fragment selection set parent is ${selectionSet.parentType} differs from the fragment condition type ${this.typeCondition}`);
531
536
  this._selectionSet = selectionSet;
532
537
  return this;
533
538
  }
@@ -559,25 +564,21 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
559
564
  };
560
565
  }
561
566
  canApplyAtType(type) {
562
- const applyAtType = (0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.runtimeTypesIntersects)(type, this.typeCondition);
563
- return applyAtType
564
- && this.validForSchema(type.schema());
567
+ return (0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.runtimeTypesIntersects)(type, this.typeCondition);
565
568
  }
566
- validForSchema(schema) {
567
- if (schema === this.schema()) {
568
- return true;
569
+ selectionSetAtType(type) {
570
+ if ((0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.isObjectType)(this.typeCondition)) {
571
+ return this.selectionSet;
569
572
  }
570
- const typeInSchema = schema.type(this.typeCondition.name);
571
- if (!typeInSchema || !(0, definitions_1.isCompositeType)(typeInSchema)) {
572
- return false;
573
+ if (!(0, definitions_1.isObjectType)(type)) {
574
+ return this.selectionSet;
573
575
  }
574
- try {
575
- this.selectionSet.rebaseOn(typeInSchema);
576
- return true;
577
- }
578
- catch (e) {
579
- return false;
576
+ let selectionSet = this.selectionSetsAtTypesCache.get(type.name);
577
+ if (!selectionSet) {
578
+ selectionSet = this.selectionSet.trimUnsatisfiableBranches(type, { recursive: false });
579
+ this.selectionSetsAtTypesCache.set(type.name, selectionSet);
580
580
  }
581
+ return selectionSet;
581
582
  }
582
583
  toString(indent) {
583
584
  return (indent !== null && indent !== void 0 ? indent : '') + `fragment ${this.name} on ${this.typeCondition}${this.appliedDirectivesToString()} ${this.selectionSet.toString(false, true, indent)}`;
@@ -640,6 +641,46 @@ class NamedFragments {
640
641
  }
641
642
  return mapped;
642
643
  }
644
+ mapToExpandedSelectionSets(mapper, recreateFct = (f, s) => f.withUpdatedSelectionSet(s)) {
645
+ const fragmentsMap = new Map();
646
+ const removedFragments = new Set();
647
+ for (const fragment of this.definitions()) {
648
+ const mappedSelectionSet = mapper(fragment.selectionSet.expandAllFragments().trimUnsatisfiableBranches(fragment.typeCondition));
649
+ if (!mappedSelectionSet) {
650
+ removedFragments.add(fragment.name);
651
+ continue;
652
+ }
653
+ const otherFragmentsUsages = new Map();
654
+ fragment.collectUsedFragmentNames(otherFragmentsUsages);
655
+ fragmentsMap.set(fragment.name, {
656
+ original: fragment,
657
+ mappedSelectionSet,
658
+ dependsOn: Array.from(otherFragmentsUsages.keys()),
659
+ });
660
+ }
661
+ const mappedFragments = new NamedFragments();
662
+ while (fragmentsMap.size > 0) {
663
+ for (const [name, info] of fragmentsMap) {
664
+ if (info.dependsOn.every((n) => mappedFragments.has(n) || removedFragments.has(n))) {
665
+ const reoptimizedSelectionSet = info.mappedSelectionSet.optimize(mappedFragments);
666
+ mappedFragments.add(recreateFct(info.original, reoptimizedSelectionSet));
667
+ fragmentsMap.delete(name);
668
+ }
669
+ }
670
+ }
671
+ return mappedFragments.isEmpty() ? undefined : mappedFragments;
672
+ }
673
+ rebaseOn(schema) {
674
+ return this.mapToExpandedSelectionSets((s) => {
675
+ const rebasedType = schema.type(s.parentType.name);
676
+ try {
677
+ return rebasedType && (0, definitions_1.isCompositeType)(rebasedType) ? s.rebaseOn(rebasedType) : undefined;
678
+ }
679
+ catch (e) {
680
+ return undefined;
681
+ }
682
+ }, (orig, newSelection) => new NamedFragmentDefinition(schema, orig.name, newSelection.parentType).setSelectionSet(newSelection));
683
+ }
643
684
  validate(variableDefinitions) {
644
685
  for (const fragment of this.fragments.values()) {
645
686
  fragment.selectionSet.validate(variableDefinitions);
@@ -755,6 +796,13 @@ class SelectionSet {
755
796
  if (!fragments || fragments.isEmpty()) {
756
797
  return this;
757
798
  }
799
+ const wrapped = new InlineFragmentSelection(new FragmentElement(this.parentType, this.parentType), this);
800
+ const optimized = wrapped.optimize(fragments);
801
+ return optimized instanceof FragmentSpreadSelection
802
+ ? selectionSetOf(this.parentType, optimized, fragments)
803
+ : optimized.selectionSet;
804
+ }
805
+ optimizeSelections(fragments) {
758
806
  (0, utils_1.assert)(!this.fragments || this.fragments.isEmpty(), `Should not be called on selection that already has named fragments, but got ${this.fragments}`);
759
807
  return this.lazyMap((selection) => selection.optimize(fragments), { fragments });
760
808
  }
@@ -767,8 +815,8 @@ class SelectionSet {
767
815
  }
768
816
  return this.lazyMap((selection) => selection.expandFragments(names, updatedFragments), { fragments: updatedFragments !== null && updatedFragments !== void 0 ? updatedFragments : null });
769
817
  }
770
- trimUnsatisfiableBranches(parentType) {
771
- return this.lazyMap((selection) => selection.trimUnsatisfiableBranches(parentType), { parentType });
818
+ trimUnsatisfiableBranches(parentType, options) {
819
+ return this.lazyMap((selection) => selection.trimUnsatisfiableBranches(parentType, options), { parentType });
772
820
  }
773
821
  lazyMap(mapper, options) {
774
822
  var _a;
@@ -881,9 +929,14 @@ class SelectionSet {
881
929
  }
882
930
  return true;
883
931
  }
884
- diffIfContains(that) {
932
+ diffWithNamedFragmentIfContained(candidate, parentType) {
933
+ const that = candidate.selectionSetAtType(parentType);
885
934
  if (this.contains(that)) {
886
- const diff = this.minus(that);
935
+ let updatedThis = this.expandFragments([candidate.name], this.fragments);
936
+ if (updatedThis !== this) {
937
+ updatedThis = updatedThis.trimUnsatisfiableBranches(parentType);
938
+ }
939
+ const diff = updatedThis.minus(that);
887
940
  return { contains: true, diff: diff.isEmpty() ? undefined : diff };
888
941
  }
889
942
  return { contains: false };
@@ -1069,9 +1122,17 @@ function addOneToKeyedUpdates(keyedUpdates, selection) {
1069
1122
  keyedUpdates.add(selection.key(), selection);
1070
1123
  }
1071
1124
  }
1125
+ function maybeRebaseOnSchema(toRebase, schema) {
1126
+ if (toRebase.schema() === schema) {
1127
+ return toRebase;
1128
+ }
1129
+ const rebased = schema.type(toRebase.name);
1130
+ (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}`);
1131
+ return rebased;
1132
+ }
1072
1133
  function isUnecessaryFragment(parentType, fragment) {
1073
1134
  return fragment.element.appliedDirectives.length === 0
1074
- && (!fragment.element.typeCondition || (0, types_1.sameType)(parentType, fragment.element.typeCondition));
1135
+ && (!fragment.element.typeCondition || (0, types_1.isSubtype)(maybeRebaseOnSchema(fragment.element.typeCondition, parentType.schema()), parentType));
1075
1136
  }
1076
1137
  function withUnecessaryFragmentsRemoved(parentType, selections) {
1077
1138
  if (selections instanceof AbstractSelection) {
@@ -1321,7 +1382,7 @@ class FieldSelection extends AbstractSelection {
1321
1382
  return this.element.key();
1322
1383
  }
1323
1384
  optimize(fragments) {
1324
- let optimizedSelection = this.selectionSet ? this.selectionSet.optimize(fragments) : undefined;
1385
+ let optimizedSelection = this.selectionSet ? this.selectionSet.optimizeSelections(fragments) : undefined;
1325
1386
  const fieldBaseType = (0, definitions_1.baseType)(this.element.definition.type);
1326
1387
  if ((0, definitions_1.isCompositeType)(fieldBaseType) && optimizedSelection) {
1327
1388
  const optimized = this.tryOptimizeSubselectionWithFragments({
@@ -1340,7 +1401,7 @@ class FieldSelection extends AbstractSelection {
1340
1401
  tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1341
1402
  let optimizedSelection = subSelection;
1342
1403
  for (const candidate of candidates) {
1343
- const { contains, diff } = optimizedSelection.diffIfContains(candidate.selectionSet);
1404
+ const { contains, diff } = optimizedSelection.diffWithNamedFragmentIfContained(candidate, parentType);
1344
1405
  if (contains) {
1345
1406
  const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1346
1407
  optimizedSelection = diff
@@ -1425,15 +1486,15 @@ class FieldSelection extends AbstractSelection {
1425
1486
  expandAllFragments() {
1426
1487
  return this.mapToSelectionSet((s) => s.expandAllFragments());
1427
1488
  }
1428
- trimUnsatisfiableBranches(_) {
1429
- var _a;
1489
+ trimUnsatisfiableBranches(_, options) {
1490
+ var _a, _b;
1430
1491
  if (!this.selectionSet) {
1431
1492
  return this;
1432
1493
  }
1433
1494
  const base = (0, definitions_1.baseType)(this.element.definition.type);
1434
1495
  (0, utils_1.assert)((0, definitions_1.isCompositeType)(base), () => `Field ${this.element} should not have a sub-selection`);
1435
- const trimmed = this.mapToSelectionSet((s) => s.trimUnsatisfiableBranches(base));
1436
- if ((_a = trimmed.selectionSet) === null || _a === void 0 ? void 0 : _a.isEmpty()) {
1496
+ const trimmed = ((_a = options === null || options === void 0 ? void 0 : options.recursive) !== null && _a !== void 0 ? _a : true) ? this.mapToSelectionSet((s) => s.trimUnsatisfiableBranches(base)) : this;
1497
+ if ((_b = trimmed.selectionSet) === null || _b === void 0 ? void 0 : _b.isEmpty()) {
1437
1498
  return trimmed.withUpdatedSelectionSet(selectionSetOfElement(new Field(base.typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1438
1499
  }
1439
1500
  else {
@@ -1567,7 +1628,7 @@ class InlineFragmentSelection extends FragmentSelection {
1567
1628
  };
1568
1629
  }
1569
1630
  optimize(fragments) {
1570
- let optimizedSelection = this.selectionSet.optimize(fragments);
1631
+ let optimizedSelection = this.selectionSet.optimizeSelections(fragments);
1571
1632
  const typeCondition = this.element.typeCondition;
1572
1633
  if (typeCondition) {
1573
1634
  const optimized = this.tryOptimizeSubselectionWithFragments({
@@ -1587,7 +1648,7 @@ class InlineFragmentSelection extends FragmentSelection {
1587
1648
  tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1588
1649
  let optimizedSelection = subSelection;
1589
1650
  for (const candidate of candidates) {
1590
- const { contains, diff } = optimizedSelection.diffIfContains(candidate.selectionSet);
1651
+ const { contains, diff } = optimizedSelection.diffWithNamedFragmentIfContained(candidate, parentType);
1591
1652
  if (contains) {
1592
1653
  if (!diff && (0, types_1.sameType)(this.element.typeCondition, candidate.typeCondition)) {
1593
1654
  let spreadDirectives = this.element.appliedDirectives;
@@ -1637,12 +1698,13 @@ class InlineFragmentSelection extends FragmentSelection {
1637
1698
  ? this
1638
1699
  : this.withUpdatedComponents(newElement, newSelection);
1639
1700
  }
1640
- trimUnsatisfiableBranches(currentType) {
1641
- var _a, _b;
1701
+ trimUnsatisfiableBranches(currentType, options) {
1702
+ var _a, _b, _c;
1703
+ const recursive = (_a = options === null || options === void 0 ? void 0 : options.recursive) !== null && _a !== void 0 ? _a : true;
1642
1704
  const thisCondition = this.element.typeCondition;
1643
1705
  if (this.element.appliedDirectives.length === 0) {
1644
1706
  if (!thisCondition || currentType === this.element.typeCondition) {
1645
- const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType);
1707
+ const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType, options);
1646
1708
  return trimmed.isEmpty() ? undefined : trimmed;
1647
1709
  }
1648
1710
  if ((0, definitions_1.isObjectType)(currentType)) {
@@ -1650,18 +1712,21 @@ class InlineFragmentSelection extends FragmentSelection {
1650
1712
  return undefined;
1651
1713
  }
1652
1714
  else {
1653
- const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType);
1715
+ const trimmed = this.selectionSet.trimUnsatisfiableBranches(currentType, options);
1654
1716
  return trimmed.isEmpty() ? undefined : trimmed;
1655
1717
  }
1656
1718
  }
1657
1719
  }
1658
- const trimmedSelectionSet = this.selectionSet.trimUnsatisfiableBranches((_a = this.element.typeCondition) !== null && _a !== void 0 ? _a : this.parentType);
1720
+ if (!recursive) {
1721
+ return this;
1722
+ }
1723
+ const trimmedSelectionSet = this.selectionSet.trimUnsatisfiableBranches((_b = this.element.typeCondition) !== null && _b !== void 0 ? _b : this.parentType);
1659
1724
  if (trimmedSelectionSet.isEmpty()) {
1660
1725
  if (this.element.appliedDirectives.length === 0) {
1661
1726
  return undefined;
1662
1727
  }
1663
1728
  else {
1664
- return this.withUpdatedSelectionSet(selectionSetOfElement(new Field(((_b = this.element.typeCondition) !== null && _b !== void 0 ? _b : this.parentType).typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1729
+ return this.withUpdatedSelectionSet(selectionSetOfElement(new Field(((_c = this.element.typeCondition) !== null && _c !== void 0 ? _c : this.parentType).typenameField(), undefined, [new definitions_1.Directive('include', { 'if': false })])));
1665
1730
  }
1666
1731
  }
1667
1732
  if (this.element.appliedDirectives.length === 0 && (0, definitions_1.isAbstractType)(thisCondition)) {