@apollo/federation-internals 2.4.6 → 2.4.7

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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
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;
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.ContainsResult = 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
6
  const federation_1 = require("./federation");
@@ -442,16 +442,92 @@ function isUselessFollowupElement(first, followup, conditionals) {
442
442
  && (followup.appliedDirectives.length === 0 || (0, definitions_1.isDirectiveApplicationsSubset)(conditionals, followup.appliedDirectives))
443
443
  && (0, types_1.isSubtype)(followup.typeCondition, typeOfFirst);
444
444
  }
445
+ function computeFragmentsDependents(fragments) {
446
+ const reverseDeps = new utils_1.SetMultiMap();
447
+ for (const fragment of fragments.definitions()) {
448
+ for (const dependency of fragment.fragmentUsages().keys()) {
449
+ reverseDeps.add(dependency, fragment.name);
450
+ }
451
+ }
452
+ return reverseDeps;
453
+ }
454
+ function clearKeptFragments(usages, fragments, minUsagesToOptimize) {
455
+ let toCheck = Array.from(usages.entries()).filter(([_, count]) => count >= minUsagesToOptimize).map(([name, _]) => name);
456
+ while (toCheck.length > 0) {
457
+ const newToCheck = [];
458
+ for (const name of toCheck) {
459
+ usages.delete(name);
460
+ const ownUsages = fragments.get(name).fragmentUsages();
461
+ for (const [otherName, otherCount] of ownUsages.entries()) {
462
+ const prevCount = usages.get(otherName);
463
+ if (prevCount !== undefined) {
464
+ const newCount = prevCount + otherCount;
465
+ usages.set(otherName, newCount);
466
+ if (prevCount < minUsagesToOptimize && newCount >= minUsagesToOptimize) {
467
+ newToCheck.push(otherName);
468
+ }
469
+ }
470
+ }
471
+ }
472
+ toCheck = newToCheck;
473
+ }
474
+ }
475
+ function computeFragmentsToKeep(selectionSet, fragments, minUsagesToOptimize) {
476
+ const usages = new Map();
477
+ selectionSet.collectUsedFragmentNames(usages);
478
+ if (usages.size === 0) {
479
+ return null;
480
+ }
481
+ for (const fragment of fragments.definitions()) {
482
+ if (usages.get(fragment.name) === undefined) {
483
+ usages.set(fragment.name, 0);
484
+ }
485
+ }
486
+ const reverseDependencies = computeFragmentsDependents(fragments);
487
+ const toExpand = new Set;
488
+ let shouldContinue = true;
489
+ while (shouldContinue) {
490
+ shouldContinue = false;
491
+ clearKeptFragments(usages, fragments, minUsagesToOptimize);
492
+ for (const name of (0, utils_1.mapKeys)(usages)) {
493
+ const count = usages.get(name);
494
+ if (count === 0) {
495
+ continue;
496
+ }
497
+ if (count >= minUsagesToOptimize) {
498
+ shouldContinue = true;
499
+ break;
500
+ }
501
+ const fragmentsUsingName = reverseDependencies.get(name);
502
+ if (!fragmentsUsingName || [...fragmentsUsingName].every((fragName) => toExpand.has(fragName) || !usages.get(fragName))) {
503
+ toExpand.add(name);
504
+ usages.delete(name);
505
+ shouldContinue = true;
506
+ const nameUsages = fragments.get(name).fragmentUsages();
507
+ for (const [otherName, otherCount] of nameUsages.entries()) {
508
+ const prev = usages.get(otherName);
509
+ if (prev !== undefined) {
510
+ usages.set(otherName, prev + count * otherCount);
511
+ }
512
+ }
513
+ }
514
+ }
515
+ }
516
+ for (const name of usages.keys()) {
517
+ toExpand.add(name);
518
+ }
519
+ return toExpand.size === 0 ? fragments : fragments.filter((f) => !toExpand.has(f.name));
520
+ }
445
521
  class Operation {
446
- constructor(schema, rootKind, selectionSet, variableDefinitions, name) {
522
+ constructor(schema, rootKind, selectionSet, variableDefinitions, fragments, name) {
447
523
  this.schema = schema;
448
524
  this.rootKind = rootKind;
449
525
  this.selectionSet = selectionSet;
450
526
  this.variableDefinitions = variableDefinitions;
527
+ this.fragments = fragments;
451
528
  this.name = name;
452
529
  }
453
530
  optimize(fragments, minUsagesToOptimize = 2) {
454
- var _a;
455
531
  (0, utils_1.assert)(minUsagesToOptimize >= 1, `Expected 'minUsagesToOptimize' to be at least 1, but got ${minUsagesToOptimize}`);
456
532
  if (!fragments || fragments.isEmpty()) {
457
533
  return this;
@@ -460,50 +536,41 @@ class Operation {
460
536
  if (optimizedSelection === this.selectionSet) {
461
537
  return this;
462
538
  }
463
- const usages = new Map();
464
- optimizedSelection.collectUsedFragmentNames(usages);
465
- for (const fragment of fragments.names()) {
466
- if (!usages.has(fragment)) {
467
- usages.set(fragment, 0);
468
- }
469
- }
470
- const toDeoptimize = (0, utils_1.mapEntries)(usages).filter(([_, count]) => count < minUsagesToOptimize).map(([name]) => name);
471
- if (toDeoptimize.length > 0) {
472
- const newFragments = (_a = optimizedSelection.fragments) === null || _a === void 0 ? void 0 : _a.without(toDeoptimize);
473
- optimizedSelection = optimizedSelection.expandFragments(toDeoptimize, newFragments);
474
- optimizedSelection = optimizedSelection.trimUnsatisfiableBranches(optimizedSelection.parentType);
539
+ const finalFragments = computeFragmentsToKeep(optimizedSelection, fragments, minUsagesToOptimize);
540
+ if (finalFragments === null || (finalFragments === null || finalFragments === void 0 ? void 0 : finalFragments.size) === fragments.size) {
541
+ return new Operation(this.schema, this.rootKind, optimizedSelection, this.variableDefinitions, finalFragments !== null && finalFragments !== void 0 ? finalFragments : undefined, this.name);
475
542
  }
476
- return new Operation(this.schema, this.rootKind, optimizedSelection, this.variableDefinitions, this.name);
543
+ optimizedSelection = optimizedSelection.expandFragments(finalFragments);
544
+ optimizedSelection = optimizedSelection.trimUnsatisfiableBranches(optimizedSelection.parentType);
545
+ return new Operation(this.schema, this.rootKind, optimizedSelection, this.variableDefinitions, finalFragments, this.name);
477
546
  }
478
547
  expandAllFragments() {
479
- const expandedSelections = this.selectionSet.expandAllFragments();
548
+ const expandedSelections = this.selectionSet.expandFragments();
480
549
  if (expandedSelections === this.selectionSet) {
481
550
  return this;
482
551
  }
483
- return new Operation(this.schema, this.rootKind, expandedSelections, this.variableDefinitions, this.name);
552
+ return new Operation(this.schema, this.rootKind, expandedSelections, this.variableDefinitions, undefined, this.name);
484
553
  }
485
554
  trimUnsatisfiableBranches() {
486
555
  const trimmedSelections = this.selectionSet.trimUnsatisfiableBranches(this.selectionSet.parentType);
487
556
  if (trimmedSelections === this.selectionSet) {
488
557
  return this;
489
558
  }
490
- return new Operation(this.schema, this.rootKind, trimmedSelections, this.variableDefinitions, this.name);
559
+ return new Operation(this.schema, this.rootKind, trimmedSelections, this.variableDefinitions, this.fragments, this.name);
491
560
  }
492
561
  withoutDefer(labelsToRemove) {
493
- (0, utils_1.assert)(!this.selectionSet.fragments || this.selectionSet.fragments.isEmpty(), 'Removing @defer currently only work on "expanded" selections (no named fragments)');
494
562
  const updated = this.selectionSet.withoutDefer(labelsToRemove);
495
563
  return updated == this.selectionSet
496
564
  ? this
497
- : new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.name);
565
+ : new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.fragments, this.name);
498
566
  }
499
567
  withNormalizedDefer() {
500
- (0, utils_1.assert)(!this.selectionSet.fragments || this.selectionSet.fragments.isEmpty(), 'Assigning @defer lables currently only work on "expanded" selections (no named fragments)');
501
568
  const normalizer = new DeferNormalizer();
502
569
  const { hasDefers, hasNonLabelledOrConditionalDefers } = normalizer.init(this.selectionSet);
503
570
  let updatedOperation = this;
504
571
  if (hasNonLabelledOrConditionalDefers) {
505
572
  const updated = this.selectionSet.withNormalizedDefer(normalizer);
506
- updatedOperation = new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.name);
573
+ updatedOperation = new Operation(this.schema, this.rootKind, updated, this.variableDefinitions, this.fragments, this.name);
507
574
  }
508
575
  return {
509
576
  operation: updatedOperation,
@@ -522,7 +589,7 @@ class Operation {
522
589
  return defaultedVariableValues;
523
590
  }
524
591
  toString(expandFragments = false, prettyPrint = true) {
525
- return this.selectionSet.toOperationString(this.rootKind, this.variableDefinitions, this.name, expandFragments, prettyPrint);
592
+ return this.selectionSet.toOperationString(this.rootKind, this.variableDefinitions, this.fragments, this.name, expandFragments, prettyPrint);
526
593
  }
527
594
  }
528
595
  exports.Operation = Operation;
@@ -531,7 +598,7 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
531
598
  super(schema, directives);
532
599
  this.name = name;
533
600
  this.typeCondition = typeCondition;
534
- this.selectionSetsAtTypesCache = new Map();
601
+ this.expandedSelectionSetsAtTypesCache = new Map();
535
602
  }
536
603
  setSelectionSet(selectionSet) {
537
604
  (0, utils_1.assert)(!this._selectionSet, 'Attempting to set the selection set of a fragment definition already built');
@@ -543,11 +610,28 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
543
610
  (0, utils_1.assert)(this._selectionSet, () => `Trying to access fragment definition ${this.name} before it is fully built`);
544
611
  return this._selectionSet;
545
612
  }
613
+ expandedSelectionSet() {
614
+ if (!this._expandedSelectionSet) {
615
+ this._expandedSelectionSet = this.selectionSet.expandFragments().trimUnsatisfiableBranches(this.typeCondition);
616
+ }
617
+ return this._expandedSelectionSet;
618
+ }
546
619
  withUpdatedSelectionSet(newSelectionSet) {
547
620
  return new NamedFragmentDefinition(this.schema(), this.name, this.typeCondition).setSelectionSet(newSelectionSet);
548
621
  }
622
+ fragmentUsages() {
623
+ if (!this._fragmentUsages) {
624
+ this._fragmentUsages = new Map();
625
+ this.selectionSet.collectUsedFragmentNames(this._fragmentUsages);
626
+ }
627
+ return this._fragmentUsages;
628
+ }
549
629
  collectUsedFragmentNames(collector) {
550
- this.selectionSet.collectUsedFragmentNames(collector);
630
+ const usages = this.fragmentUsages();
631
+ for (const [name, count] of usages.entries()) {
632
+ const prevCount = collector.get(name);
633
+ collector.set(name, prevCount ? prevCount + count : count);
634
+ }
551
635
  }
552
636
  toFragmentDefinitionNode() {
553
637
  return {
@@ -567,21 +651,49 @@ class NamedFragmentDefinition extends definitions_1.DirectiveTargetElement {
567
651
  };
568
652
  }
569
653
  canApplyAtType(type) {
570
- return (0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.runtimeTypesIntersects)(type, this.typeCondition);
654
+ if ((0, types_1.sameType)(type, this.typeCondition)) {
655
+ return true;
656
+ }
657
+ if (!(0, definitions_1.isAbstractType)(this.typeCondition)) {
658
+ return false;
659
+ }
660
+ const conditionRuntimes = (0, definitions_1.possibleRuntimeTypes)(this.typeCondition);
661
+ const typeRuntimes = (0, definitions_1.possibleRuntimeTypes)(type);
662
+ return conditionRuntimes.length >= typeRuntimes.length
663
+ && typeRuntimes.every((t1) => conditionRuntimes.some((t2) => (0, types_1.sameType)(t1, t2)));
571
664
  }
572
- selectionSetAtType(type) {
665
+ expandedSelectionSetAtType(type) {
666
+ const expandedSelectionSet = this.expandedSelectionSet();
573
667
  if ((0, types_1.sameType)(type, this.typeCondition) || (0, definitions_1.isObjectType)(this.typeCondition)) {
574
- return this.selectionSet;
668
+ return expandedSelectionSet;
575
669
  }
576
670
  if (!(0, definitions_1.isObjectType)(type)) {
577
- return this.selectionSet;
671
+ return expandedSelectionSet;
578
672
  }
579
- let selectionSet = this.selectionSetsAtTypesCache.get(type.name);
580
- if (!selectionSet) {
581
- selectionSet = this.selectionSet.trimUnsatisfiableBranches(type, { recursive: false });
582
- this.selectionSetsAtTypesCache.set(type.name, selectionSet);
673
+ let selectionAtType = this.expandedSelectionSetsAtTypesCache.get(type.name);
674
+ if (!selectionAtType) {
675
+ selectionAtType = expandedSelectionSet.trimUnsatisfiableBranches(type, { recursive: false });
676
+ this.expandedSelectionSetsAtTypesCache.set(type.name, selectionAtType);
583
677
  }
584
- return selectionSet;
678
+ return selectionAtType;
679
+ }
680
+ includes(otherFragment) {
681
+ if (this.name === otherFragment) {
682
+ return false;
683
+ }
684
+ if (!this._includedFragmentNames) {
685
+ this._includedFragmentNames = this.computeIncludedFragmentNames();
686
+ }
687
+ return this._includedFragmentNames.has(otherFragment);
688
+ }
689
+ computeIncludedFragmentNames() {
690
+ const included = new Set();
691
+ for (const selection of this.selectionSet.selections()) {
692
+ if (selection instanceof FragmentSpreadSelection) {
693
+ included.add(selection.namedFragment.name);
694
+ }
695
+ }
696
+ return included;
585
697
  }
586
698
  toString(indent) {
587
699
  return (indent !== null && indent !== void 0 ? indent : '') + `fragment ${this.name} on ${this.typeCondition}${this.appliedDirectivesToString()} ${this.selectionSet.toString(false, true, indent)}`;
@@ -593,7 +705,10 @@ class NamedFragments {
593
705
  this.fragments = new utils_1.MapWithCachedArrays();
594
706
  }
595
707
  isEmpty() {
596
- return this.fragments.size === 0;
708
+ return this.size === 0;
709
+ }
710
+ get size() {
711
+ return this.fragments.size;
597
712
  }
598
713
  names() {
599
714
  return this.fragments.keys();
@@ -612,22 +727,6 @@ class NamedFragments {
612
727
  maybeApplyingAtType(type) {
613
728
  return this.fragments.values().filter(f => f.canApplyAtType(type));
614
729
  }
615
- without(names) {
616
- if (!names.some(n => this.fragments.has(n))) {
617
- return this;
618
- }
619
- const newFragments = new NamedFragments();
620
- for (const fragment of this.fragments.values()) {
621
- if (!names.includes(fragment.name)) {
622
- const updatedSelectionSet = fragment.selectionSet.expandFragments(names, newFragments);
623
- const newFragment = updatedSelectionSet === fragment.selectionSet
624
- ? fragment
625
- : fragment.withUpdatedSelectionSet(updatedSelectionSet);
626
- newFragments.add(newFragment);
627
- }
628
- }
629
- return newFragments.isEmpty() ? undefined : newFragments;
630
- }
631
730
  get(name) {
632
731
  return this.fragments.get(name);
633
732
  }
@@ -644,45 +743,70 @@ class NamedFragments {
644
743
  }
645
744
  return mapped;
646
745
  }
647
- mapToExpandedSelectionSets(mapper, recreateFct = (f, s) => f.withUpdatedSelectionSet(s)) {
746
+ mapInDependencyOrder(mapper) {
648
747
  const fragmentsMap = new Map();
649
- const removedFragments = new Set();
650
748
  for (const fragment of this.definitions()) {
651
- const mappedSelectionSet = mapper(fragment.selectionSet.expandAllFragments().trimUnsatisfiableBranches(fragment.typeCondition));
652
- if (!mappedSelectionSet) {
653
- removedFragments.add(fragment.name);
654
- continue;
655
- }
656
- const otherFragmentsUsages = new Map();
657
- fragment.collectUsedFragmentNames(otherFragmentsUsages);
658
749
  fragmentsMap.set(fragment.name, {
659
- original: fragment,
660
- mappedSelectionSet,
661
- dependsOn: Array.from(otherFragmentsUsages.keys()),
750
+ fragment,
751
+ dependsOn: Array.from(fragment.fragmentUsages().keys()),
662
752
  });
663
753
  }
754
+ const removedFragments = new Set();
664
755
  const mappedFragments = new NamedFragments();
665
756
  while (fragmentsMap.size > 0) {
666
757
  for (const [name, info] of fragmentsMap) {
667
758
  if (info.dependsOn.every((n) => mappedFragments.has(n) || removedFragments.has(n))) {
668
- const reoptimizedSelectionSet = info.mappedSelectionSet.optimize(mappedFragments);
669
- mappedFragments.add(recreateFct(info.original, reoptimizedSelectionSet));
759
+ const mapped = mapper(info.fragment, mappedFragments);
670
760
  fragmentsMap.delete(name);
761
+ if (!mapped) {
762
+ removedFragments.add(name);
763
+ }
764
+ else {
765
+ mappedFragments.add(mapped);
766
+ }
767
+ break;
671
768
  }
672
769
  }
673
770
  }
674
771
  return mappedFragments.isEmpty() ? undefined : mappedFragments;
675
772
  }
773
+ mapToExpandedSelectionSets(mapper) {
774
+ return this.mapInDependencyOrder((fragment, newFragments) => {
775
+ const mappedSelectionSet = mapper(fragment.selectionSet.expandFragments().trimUnsatisfiableBranches(fragment.typeCondition));
776
+ if (!mappedSelectionSet) {
777
+ return undefined;
778
+ }
779
+ const reoptimizedSelectionSet = mappedSelectionSet.optimize(newFragments);
780
+ return fragment.withUpdatedSelectionSet(reoptimizedSelectionSet);
781
+ });
782
+ }
676
783
  rebaseOn(schema) {
677
- return this.mapToExpandedSelectionSets((s) => {
678
- const rebasedType = schema.type(s.parentType.name);
784
+ return this.mapInDependencyOrder((fragment, newFragments) => {
785
+ const rebasedType = schema.type(fragment.selectionSet.parentType.name);
679
786
  try {
680
- return rebasedType && (0, definitions_1.isCompositeType)(rebasedType) ? s.rebaseOn(rebasedType) : undefined;
787
+ if (!rebasedType || !(0, definitions_1.isCompositeType)(rebasedType)) {
788
+ return undefined;
789
+ }
790
+ const rebasedSelection = fragment.selectionSet.rebaseOn(rebasedType, newFragments);
791
+ return new NamedFragmentDefinition(schema, fragment.name, rebasedType).setSelectionSet(rebasedSelection);
681
792
  }
682
793
  catch (e) {
683
794
  return undefined;
684
795
  }
685
- }, (orig, newSelection) => new NamedFragmentDefinition(schema, orig.name, newSelection.parentType).setSelectionSet(newSelection));
796
+ });
797
+ }
798
+ filter(predicate) {
799
+ return this.mapInDependencyOrder((fragment, newFragments) => {
800
+ if (predicate(fragment)) {
801
+ const updatedSelectionSet = fragment.selectionSet.expandFragments(newFragments);
802
+ return updatedSelectionSet === fragment.selectionSet
803
+ ? fragment
804
+ : fragment.withUpdatedSelectionSet(updatedSelectionSet);
805
+ }
806
+ else {
807
+ return undefined;
808
+ }
809
+ });
686
810
  }
687
811
  validate(variableDefinitions) {
688
812
  for (const fragment of this.fragments.values()) {
@@ -743,10 +867,15 @@ class DeferNormalizer {
743
867
  this.deferConditions.add(condition.name, label);
744
868
  }
745
869
  }
870
+ var ContainsResult;
871
+ (function (ContainsResult) {
872
+ ContainsResult[ContainsResult["NOT_CONTAINED"] = 0] = "NOT_CONTAINED";
873
+ ContainsResult[ContainsResult["STRICTLY_CONTAINED"] = 1] = "STRICTLY_CONTAINED";
874
+ ContainsResult[ContainsResult["EQUAL"] = 2] = "EQUAL";
875
+ })(ContainsResult = exports.ContainsResult || (exports.ContainsResult = {}));
746
876
  class SelectionSet {
747
- constructor(parentType, keyedSelections = new Map(), fragments) {
877
+ constructor(parentType, keyedSelections = new Map()) {
748
878
  this.parentType = parentType;
749
- this.fragments = fragments;
750
879
  this._keyedSelections = keyedSelections;
751
880
  this._selections = (0, utils_1.mapValues)(keyedSelections);
752
881
  }
@@ -802,21 +931,14 @@ class SelectionSet {
802
931
  const wrapped = new InlineFragmentSelection(new FragmentElement(this.parentType, this.parentType), this);
803
932
  const optimized = wrapped.optimize(fragments);
804
933
  return optimized instanceof FragmentSpreadSelection
805
- ? selectionSetOf(this.parentType, optimized, fragments)
934
+ ? selectionSetOf(this.parentType, optimized)
806
935
  : optimized.selectionSet;
807
936
  }
808
937
  optimizeSelections(fragments) {
809
- (0, utils_1.assert)(!this.fragments || this.fragments.isEmpty(), `Should not be called on selection that already has named fragments, but got ${this.fragments}`);
810
- return this.lazyMap((selection) => selection.optimize(fragments), { fragments });
938
+ return this.lazyMap((selection) => selection.optimize(fragments));
811
939
  }
812
- expandAllFragments() {
813
- return this.lazyMap((selection) => selection.expandAllFragments(), { fragments: null });
814
- }
815
- expandFragments(names, updatedFragments) {
816
- if (names.length === 0) {
817
- return this;
818
- }
819
- return this.lazyMap((selection) => selection.expandFragments(names, updatedFragments), { fragments: updatedFragments !== null && updatedFragments !== void 0 ? updatedFragments : null });
940
+ expandFragments(updatedFragments) {
941
+ return this.lazyMap((selection) => selection.expandFragments(updatedFragments));
820
942
  }
821
943
  trimUnsatisfiableBranches(parentType, options) {
822
944
  return this.lazyMap((selection) => selection.trimUnsatisfiableBranches(parentType, options), { parentType });
@@ -824,8 +946,6 @@ class SelectionSet {
824
946
  lazyMap(mapper, options) {
825
947
  var _a;
826
948
  const selections = this.selections();
827
- const updatedFragments = options === null || options === void 0 ? void 0 : options.fragments;
828
- const newFragments = updatedFragments === undefined ? this.fragments : (updatedFragments !== null && updatedFragments !== void 0 ? updatedFragments : undefined);
829
949
  let updatedSelections = undefined;
830
950
  for (let i = 0; i < selections.length; i++) {
831
951
  const selection = selections[i];
@@ -841,19 +961,14 @@ class SelectionSet {
841
961
  }
842
962
  }
843
963
  if (!updatedSelections) {
844
- return this.withUpdatedFragments(newFragments);
964
+ return this;
845
965
  }
846
- return updatedSelections.toSelectionSet((_a = options === null || options === void 0 ? void 0 : options.parentType) !== null && _a !== void 0 ? _a : this.parentType, newFragments);
847
- }
848
- withUpdatedFragments(newFragments) {
849
- return this.fragments === newFragments ? this : new SelectionSet(this.parentType, this._keyedSelections, newFragments);
966
+ return updatedSelections.toSelectionSet((_a = options === null || options === void 0 ? void 0 : options.parentType) !== null && _a !== void 0 ? _a : this.parentType);
850
967
  }
851
968
  withoutDefer(labelsToRemove) {
852
- (0, utils_1.assert)(!this.fragments, 'Not yet supported');
853
969
  return this.lazyMap((selection) => selection.withoutDefer(labelsToRemove));
854
970
  }
855
971
  withNormalizedDefer(normalizer) {
856
- (0, utils_1.assert)(!this.fragments, 'Not yet supported');
857
972
  return this.lazyMap((selection) => selection.withNormalizedDefer(normalizer));
858
973
  }
859
974
  hasDefer() {
@@ -866,15 +981,15 @@ class SelectionSet {
866
981
  const updated = this.filter((selection) => { var _a; return ((_a = selection.selectionSet) === null || _a === void 0 ? void 0 : _a.isEmpty()) !== true; });
867
982
  return updated.isEmpty() ? undefined : updated;
868
983
  }
869
- rebaseOn(parentType) {
984
+ rebaseOn(parentType, fragments) {
870
985
  if (this.parentType === parentType) {
871
986
  return this;
872
987
  }
873
988
  const newSelections = new Map();
874
989
  for (const selection of this.selections()) {
875
- newSelections.set(selection.key(), selection.rebaseOn(parentType));
990
+ newSelections.set(selection.key(), selection.rebaseOn(parentType, fragments));
876
991
  }
877
- return new SelectionSet(parentType, newSelections, this.fragments);
992
+ return new SelectionSet(parentType, newSelections);
878
993
  }
879
994
  equals(that) {
880
995
  if (this === that) {
@@ -891,61 +1006,30 @@ class SelectionSet {
891
1006
  }
892
1007
  return true;
893
1008
  }
894
- triviallyNestedSelectionsForKey(parentType, key) {
895
- const found = [];
896
- for (const selection of this.selections()) {
897
- if (selection.isUnecessaryInlineFragment(parentType)) {
898
- const selectionForKey = selection.selectionSet._keyedSelections.get(key);
899
- if (selectionForKey) {
900
- found.push(selectionForKey);
901
- }
902
- for (const nestedSelection of selection.selectionSet.triviallyNestedSelectionsForKey(parentType, key)) {
903
- found.push(nestedSelection);
904
- }
905
- }
906
- }
907
- return found;
908
- }
909
- mergeSameKeySelections(selections) {
910
- if (selections.length === 0) {
911
- return undefined;
912
- }
913
- const first = selections[0];
914
- if (!first.selectionSet || (first instanceof FragmentSpreadSelection) || selections.length === 1) {
915
- return first;
916
- }
917
- const mergedSubselections = new SelectionSetUpdates();
918
- for (const selection of selections) {
919
- mergedSubselections.add(selection.selectionSet);
920
- }
921
- if (first.kind === 'FieldSelection') {
922
- const rebasedField = first.element.rebaseOn(this.parentType);
923
- return new FieldSelection(rebasedField, mergedSubselections.toSelectionSet(rebasedField.baseType()));
924
- }
925
- else {
926
- const rebasedFragment = first.element.rebaseOn(this.parentType);
927
- return new InlineFragmentSelection(rebasedFragment, mergedSubselections.toSelectionSet(rebasedFragment.castedType()));
928
- }
929
- }
930
1009
  contains(that) {
1010
+ if (that._selections.length > this._selections.length) {
1011
+ return ContainsResult.NOT_CONTAINED;
1012
+ }
1013
+ let isEqual = true;
931
1014
  for (const [key, thatSelection] of that._keyedSelections) {
932
1015
  const thisSelection = this._keyedSelections.get(key);
933
- const otherSelections = this.triviallyNestedSelectionsForKey(this.parentType, key);
934
- const mergedSelection = this.mergeSameKeySelections([thisSelection].concat(otherSelections).filter(utils_1.isDefined));
935
- if (!(mergedSelection && mergedSelection.contains(thatSelection))
936
- && !(thatSelection.isUnecessaryInlineFragment(this.parentType) && this.contains(thatSelection.selectionSet))) {
937
- return false;
1016
+ const selectionResult = thisSelection === null || thisSelection === void 0 ? void 0 : thisSelection.contains(thatSelection);
1017
+ if (selectionResult === undefined || selectionResult === ContainsResult.NOT_CONTAINED) {
1018
+ return ContainsResult.NOT_CONTAINED;
938
1019
  }
1020
+ isEqual && (isEqual = selectionResult === ContainsResult.EQUAL);
939
1021
  }
940
- return true;
1022
+ return isEqual && that._selections.length === this._selections.length
1023
+ ? ContainsResult.EQUAL
1024
+ : ContainsResult.STRICTLY_CONTAINED;
941
1025
  }
942
- diffWithNamedFragmentIfContained(candidate, parentType) {
943
- const that = candidate.selectionSetAtType(parentType);
1026
+ diffWithNamedFragmentIfContained(candidate, parentType, fragments) {
1027
+ const that = candidate.expandedSelectionSetAtType(parentType);
944
1028
  if (that.isEmpty() || (that.selections().length === 1 && that.selections()[0].isTypenameField())) {
945
1029
  return { contains: false };
946
1030
  }
947
1031
  if (this.contains(that)) {
948
- let updatedThis = this.expandFragments([candidate.name], this.fragments);
1032
+ let updatedThis = this.expandFragments(fragments.filter((f) => f.name !== candidate.name));
949
1033
  if (updatedThis !== this) {
950
1034
  updatedThis = updatedThis.trimUnsatisfiableBranches(parentType);
951
1035
  }
@@ -958,25 +1042,17 @@ class SelectionSet {
958
1042
  const updated = new SelectionSetUpdates();
959
1043
  for (const [key, thisSelection] of this._keyedSelections) {
960
1044
  const thatSelection = that._keyedSelections.get(key);
961
- const otherSelections = that.triviallyNestedSelectionsForKey(this.parentType, key);
962
- const allSelections = thatSelection ? [thatSelection].concat(otherSelections) : otherSelections;
963
- if (allSelections.length === 0) {
964
- if (thisSelection instanceof FragmentSpreadSelection) {
965
- const expanded = thisSelection.selectionSet.expandAllFragments().trimUnsatisfiableBranches(this.parentType);
966
- if (expanded.minus(that).isEmpty()) {
967
- continue;
968
- }
1045
+ if (thatSelection) {
1046
+ const remainder = thisSelection.minus(thatSelection);
1047
+ if (remainder) {
1048
+ updated.add(remainder);
969
1049
  }
970
- updated.add(thisSelection);
971
1050
  }
972
1051
  else {
973
- const selectionDiff = allSelections.reduce((prev, val) => prev === null || prev === void 0 ? void 0 : prev.minus(val), thisSelection);
974
- if (selectionDiff) {
975
- updated.add(selectionDiff);
976
- }
1052
+ updated.add(thisSelection);
977
1053
  }
978
1054
  }
979
- return updated.toSelectionSet(this.parentType, this.fragments);
1055
+ return updated.toSelectionSet(this.parentType);
980
1056
  }
981
1057
  canRebaseOn(parentTypeToTest) {
982
1058
  return this.selections().every((selection) => selection.canAddTo(parentTypeToTest));
@@ -1046,10 +1122,10 @@ class SelectionSet {
1046
1122
  }
1047
1123
  return false;
1048
1124
  }
1049
- toOperationString(rootKind, variableDefinitions, operationName, expandFragments = false, prettyPrint = true) {
1125
+ toOperationString(rootKind, variableDefinitions, fragments, operationName, expandFragments = false, prettyPrint = true) {
1050
1126
  const indent = prettyPrint ? '' : undefined;
1051
- const fragmentsDefinitions = !expandFragments && this.fragments && !this.fragments.isEmpty()
1052
- ? this.fragments.toString(indent) + "\n\n"
1127
+ const fragmentsDefinitions = !expandFragments && fragments && !fragments.isEmpty()
1128
+ ? fragments.toString(indent) + "\n\n"
1053
1129
  : "";
1054
1130
  if (rootKind == "query" && !operationName && variableDefinitions.isEmpty()) {
1055
1131
  return fragmentsDefinitions + this.toString(expandFragments, true, indent);
@@ -1184,7 +1260,7 @@ function makeSelection(parentType, updates, fragments) {
1184
1260
  (0, utils_1.assert)(updates.length > 0, 'Should not be called without any updates');
1185
1261
  const first = updates[0];
1186
1262
  if (updates.length === 1 && first instanceof AbstractSelection) {
1187
- return first.rebaseOn(parentType);
1263
+ return first.rebaseOn(parentType, fragments);
1188
1264
  }
1189
1265
  const element = updateElement(first).rebaseOn(parentType);
1190
1266
  const subSelectionParentType = element.kind === 'Field' ? element.baseType() : element.castedType();
@@ -1223,7 +1299,7 @@ function makeSelectionSet(parentType, keyedUpdates, fragments) {
1223
1299
  for (const [key, updates] of keyedUpdates.entries()) {
1224
1300
  selections.set(key, makeSelection(parentType, updates, fragments));
1225
1301
  }
1226
- return new SelectionSet(parentType, selections, fragments);
1302
+ return new SelectionSet(parentType, selections);
1227
1303
  }
1228
1304
  class MutableSelectionSet {
1229
1305
  constructor(parentType, _updates, memoizer) {
@@ -1299,14 +1375,14 @@ function allFieldDefinitionsInSelectionSet(selection) {
1299
1375
  return allFields;
1300
1376
  }
1301
1377
  exports.allFieldDefinitionsInSelectionSet = allFieldDefinitionsInSelectionSet;
1302
- function selectionSetOf(parentType, selection, fragments) {
1378
+ function selectionSetOf(parentType, selection) {
1303
1379
  const map = new Map();
1304
1380
  map.set(selection.key(), selection);
1305
- return new SelectionSet(parentType, map, fragments);
1381
+ return new SelectionSet(parentType, map);
1306
1382
  }
1307
1383
  exports.selectionSetOf = selectionSetOf;
1308
- function selectionSetOfElement(element, subSelection, fragments) {
1309
- return selectionSetOf(element.parentType, selectionOfElement(element, subSelection), fragments);
1384
+ function selectionSetOfElement(element, subSelection) {
1385
+ return selectionSetOf(element.parentType, selectionOfElement(element, subSelection));
1310
1386
  }
1311
1387
  exports.selectionSetOfElement = selectionSetOfElement;
1312
1388
  function selectionOfElement(element, subSelection) {
@@ -1332,10 +1408,6 @@ class AbstractSelection {
1332
1408
  var _a;
1333
1409
  (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.collectUsedFragmentNames(collector);
1334
1410
  }
1335
- namedFragments() {
1336
- var _a;
1337
- return (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.fragments;
1338
- }
1339
1411
  withUpdatedSelectionSet(selectionSet) {
1340
1412
  return this.withUpdatedComponents(this.element, selectionSet);
1341
1413
  }
@@ -1351,6 +1423,9 @@ class AbstractSelection {
1351
1423
  ? this.us()
1352
1424
  : this.withUpdatedSelectionSet(updatedSelectionSet);
1353
1425
  }
1426
+ isFragmentSpread() {
1427
+ return false;
1428
+ }
1354
1429
  minus(that) {
1355
1430
  if (this.selectionSet && that.selectionSet) {
1356
1431
  const updatedSubSelectionSet = this.selectionSet.minus(that.selectionSet);
@@ -1360,29 +1435,38 @@ class AbstractSelection {
1360
1435
  }
1361
1436
  return undefined;
1362
1437
  }
1363
- tryOptimizeSubselectionOnce(_) {
1364
- (0, utils_1.assert)(false, `UNSUPPORTED`);
1365
- }
1366
- tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, fragmentFilter, }) {
1438
+ tryOptimizeSubselectionWithFragments({ parentType, subSelection, fragments, canUseFullMatchingFragment, }) {
1367
1439
  let candidates = fragments.maybeApplyingAtType(parentType);
1368
- if (fragmentFilter) {
1369
- candidates = candidates.filter(fragmentFilter);
1370
- }
1371
- let shouldTryAgain;
1372
- do {
1373
- const { spread, optimizedSelection, hasDiff } = this.tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments });
1374
- if (optimizedSelection) {
1375
- subSelection = optimizedSelection;
1440
+ const applyingFragments = [];
1441
+ for (const candidate of candidates) {
1442
+ const fragmentSSet = candidate.expandedSelectionSetAtType(parentType);
1443
+ if (fragmentSSet.isEmpty() || (fragmentSSet.selections().length === 1 && fragmentSSet.selections()[0].isTypenameField())) {
1444
+ continue;
1376
1445
  }
1377
- else if (spread) {
1378
- return spread;
1446
+ const res = subSelection.contains(fragmentSSet);
1447
+ if (res === ContainsResult.EQUAL) {
1448
+ if (canUseFullMatchingFragment(candidate)) {
1449
+ return candidate;
1450
+ }
1451
+ if (candidate.appliedDirectives.length === 0) {
1452
+ applyingFragments.push(candidate);
1453
+ }
1379
1454
  }
1380
- shouldTryAgain = !!spread && !!hasDiff;
1381
- if (shouldTryAgain) {
1382
- candidates = candidates.filter((c) => c !== (spread === null || spread === void 0 ? void 0 : spread.namedFragment));
1455
+ else if (res === ContainsResult.STRICTLY_CONTAINED && candidate.appliedDirectives.length === 0) {
1456
+ applyingFragments.push(candidate);
1383
1457
  }
1384
- } while (shouldTryAgain);
1385
- return subSelection;
1458
+ }
1459
+ if (applyingFragments.length === 0) {
1460
+ return subSelection;
1461
+ }
1462
+ const filteredApplyingFragments = applyingFragments.filter((f) => !applyingFragments.some((o) => o.includes(f.name)));
1463
+ let notCoveredByFragments = subSelection;
1464
+ const optimized = new SelectionSetUpdates();
1465
+ for (const fragment of filteredApplyingFragments) {
1466
+ notCoveredByFragments = notCoveredByFragments.minus(fragment.expandedSelectionSetAtType(parentType));
1467
+ optimized.add(new FragmentSpreadSelection(parentType, fragments, fragment, []));
1468
+ }
1469
+ return optimized.add(notCoveredByFragments).toSelectionSet(parentType, fragments);
1386
1470
  }
1387
1471
  }
1388
1472
  class FieldSelection extends AbstractSelection {
@@ -1407,35 +1491,29 @@ class FieldSelection extends AbstractSelection {
1407
1491
  return this.element.key();
1408
1492
  }
1409
1493
  optimize(fragments) {
1410
- let optimizedSelection = this.selectionSet ? this.selectionSet.optimizeSelections(fragments) : undefined;
1411
- const fieldBaseType = this.element.baseType();
1412
- if ((0, definitions_1.isCompositeType)(fieldBaseType) && optimizedSelection) {
1494
+ const fieldBaseType = (0, definitions_1.baseType)(this.element.definition.type);
1495
+ if (!(0, definitions_1.isCompositeType)(fieldBaseType) || !this.selectionSet) {
1496
+ return this;
1497
+ }
1498
+ let optimizedSelection = this.selectionSet;
1499
+ if ((0, definitions_1.isCompositeType)(fieldBaseType) && this.selectionSet) {
1413
1500
  const optimized = this.tryOptimizeSubselectionWithFragments({
1414
1501
  parentType: fieldBaseType,
1415
- subSelection: optimizedSelection,
1502
+ subSelection: this.selectionSet,
1416
1503
  fragments,
1417
- fragmentFilter: (f) => f.appliedDirectives.length === 0,
1504
+ canUseFullMatchingFragment: (fragment) => fragment.appliedDirectives.length === 0,
1418
1505
  });
1419
- (0, utils_1.assert)(!(optimized instanceof FragmentSpreadSelection), 'tryOptimizeSubselectionOnce should never return only a spread');
1420
- optimizedSelection = optimized;
1506
+ if (optimized instanceof NamedFragmentDefinition) {
1507
+ optimizedSelection = selectionSetOf(fieldBaseType, new FragmentSpreadSelection(fieldBaseType, fragments, optimized, []));
1508
+ }
1509
+ else {
1510
+ optimizedSelection = optimized;
1511
+ }
1421
1512
  }
1513
+ optimizedSelection = optimizedSelection.optimize(fragments);
1422
1514
  return this.selectionSet === optimizedSelection
1423
1515
  ? this
1424
- : new FieldSelection(this.element, optimizedSelection);
1425
- }
1426
- tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1427
- let optimizedSelection = subSelection;
1428
- for (const candidate of candidates) {
1429
- const { contains, diff } = optimizedSelection.diffWithNamedFragmentIfContained(candidate, parentType);
1430
- if (contains) {
1431
- const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1432
- optimizedSelection = diff
1433
- ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1434
- : selectionSetOf(parentType, spread);
1435
- return { spread, optimizedSelection, hasDiff: !!diff };
1436
- }
1437
- }
1438
- return {};
1516
+ : this.withUpdatedSelectionSet(optimizedSelection);
1439
1517
  }
1440
1518
  filter(predicate) {
1441
1519
  if (!this.selectionSet) {
@@ -1453,7 +1531,7 @@ class FieldSelection extends AbstractSelection {
1453
1531
  validate(this.element.isLeafField() || (this.selectionSet && !this.selectionSet.isEmpty()), () => `Invalid empty selection set for field "${this.element.definition.coordinate}" of non-leaf type ${this.element.definition.type}`, this.element.definition.sourceAST);
1454
1532
  (_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.validate(variableDefinitions);
1455
1533
  }
1456
- rebaseOn(parentType) {
1534
+ rebaseOn(parentType, fragments) {
1457
1535
  if (this.element.parentType === parentType) {
1458
1536
  return this;
1459
1537
  }
@@ -1466,7 +1544,7 @@ class FieldSelection extends AbstractSelection {
1466
1544
  return this.withUpdatedElement(rebasedElement);
1467
1545
  }
1468
1546
  validate((0, definitions_1.isCompositeType)(rebasedBase), () => `Cannot rebase field selection ${this} on ${parentType}: rebased field base return type ${rebasedBase} is not composite`);
1469
- return this.withUpdatedComponents(rebasedElement, this.selectionSet.rebaseOn(rebasedBase));
1547
+ return this.withUpdatedComponents(rebasedElement, this.selectionSet.rebaseOn(rebasedBase, fragments));
1470
1548
  }
1471
1549
  canAddTo(parentType) {
1472
1550
  if (this.element.parentType === parentType) {
@@ -1508,9 +1586,6 @@ class FieldSelection extends AbstractSelection {
1508
1586
  var _a;
1509
1587
  return !!((_a = this.selectionSet) === null || _a === void 0 ? void 0 : _a.hasDefer());
1510
1588
  }
1511
- expandAllFragments() {
1512
- return this.mapToSelectionSet((s) => s.expandAllFragments());
1513
- }
1514
1589
  trimUnsatisfiableBranches(_, options) {
1515
1590
  var _a, _b;
1516
1591
  if (!this.selectionSet) {
@@ -1526,8 +1601,8 @@ class FieldSelection extends AbstractSelection {
1526
1601
  return trimmed;
1527
1602
  }
1528
1603
  }
1529
- expandFragments(names, updatedFragments) {
1530
- return this.mapToSelectionSet((s) => s.expandFragments(names, updatedFragments));
1604
+ expandFragments(updatedFragments) {
1605
+ return this.mapToSelectionSet((s) => s.expandFragments(updatedFragments));
1531
1606
  }
1532
1607
  equals(that) {
1533
1608
  if (this === that) {
@@ -1543,15 +1618,14 @@ class FieldSelection extends AbstractSelection {
1543
1618
  }
1544
1619
  contains(that) {
1545
1620
  if (!(that instanceof FieldSelection) || !this.element.equals(that.element)) {
1546
- return false;
1621
+ return ContainsResult.NOT_CONTAINED;
1547
1622
  }
1548
- if (!that.selectionSet) {
1549
- return true;
1623
+ if (!this.selectionSet) {
1624
+ (0, utils_1.assert)(!that.selectionSet, '`this` and `that` have the same element, so if one does not have a sub-selection, neither should the other one');
1625
+ return ContainsResult.EQUAL;
1550
1626
  }
1551
- return !!this.selectionSet && this.selectionSet.contains(that.selectionSet);
1552
- }
1553
- isUnecessaryInlineFragment(_) {
1554
- return false;
1627
+ (0, utils_1.assert)(that.selectionSet, '`this` and `that` have the same element, so if one has sub-selection, the other one should too');
1628
+ return this.selectionSet.contains(that.selectionSet);
1555
1629
  }
1556
1630
  toString(expandFragments = true, indent) {
1557
1631
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + (this.selectionSet ? ' ' + this.selectionSet.toString(expandFragments, true, indent) : '');
@@ -1584,12 +1658,6 @@ class FragmentSelection extends AbstractSelection {
1584
1658
  hasDefer() {
1585
1659
  return this.element.hasDefer() || this.selectionSet.hasDefer();
1586
1660
  }
1587
- isUnecessaryInlineFragment(parentType) {
1588
- return this.element.appliedDirectives.length === 0
1589
- && !!this.element.typeCondition
1590
- && (this.element.typeCondition.name === parentType.name
1591
- || ((0, definitions_1.isObjectType)(parentType) && (0, definitions_1.possibleRuntimeTypes)(this.element.typeCondition).some((t) => t.name === parentType.name)));
1592
- }
1593
1661
  }
1594
1662
  exports.FragmentSelection = FragmentSelection;
1595
1663
  class InlineFragmentSelection extends FragmentSelection {
@@ -1611,7 +1679,7 @@ class InlineFragmentSelection extends FragmentSelection {
1611
1679
  validate(!this.selectionSet.isEmpty(), () => `Invalid empty selection set for fragment "${this.element}"`);
1612
1680
  this.selectionSet.validate(variableDefinitions);
1613
1681
  }
1614
- rebaseOn(parentType) {
1682
+ rebaseOn(parentType, fragments) {
1615
1683
  if (this.parentType === parentType) {
1616
1684
  return this;
1617
1685
  }
@@ -1620,7 +1688,7 @@ class InlineFragmentSelection extends FragmentSelection {
1620
1688
  if (rebasedCastedType === this.selectionSet.parentType) {
1621
1689
  return this.withUpdatedElement(rebasedFragment);
1622
1690
  }
1623
- return this.withUpdatedComponents(rebasedFragment, this.selectionSet.rebaseOn(rebasedCastedType));
1691
+ return this.withUpdatedComponents(rebasedFragment, this.selectionSet.rebaseOn(rebasedCastedType, fragments));
1624
1692
  }
1625
1693
  canAddTo(parentType) {
1626
1694
  if (this.element.parentType === parentType) {
@@ -1653,52 +1721,39 @@ class InlineFragmentSelection extends FragmentSelection {
1653
1721
  };
1654
1722
  }
1655
1723
  optimize(fragments) {
1656
- let optimizedSelection = this.selectionSet.optimizeSelections(fragments);
1724
+ let optimizedSelection = this.selectionSet;
1657
1725
  const typeCondition = this.element.typeCondition;
1658
1726
  if (typeCondition) {
1659
1727
  const optimized = this.tryOptimizeSubselectionWithFragments({
1660
1728
  parentType: typeCondition,
1661
1729
  subSelection: optimizedSelection,
1662
1730
  fragments,
1731
+ canUseFullMatchingFragment: (fragment) => {
1732
+ return fragment.appliedDirectives.length === 0
1733
+ || ((0, types_1.sameType)(typeCondition, fragment.typeCondition)
1734
+ && fragment.appliedDirectives.every((d) => this.element.appliedDirectives.some((s) => (0, definitions_1.sameDirectiveApplication)(d, s))));
1735
+ },
1663
1736
  });
1664
- if (optimized instanceof FragmentSpreadSelection) {
1665
- return optimized;
1666
- }
1667
- optimizedSelection = optimized;
1668
- }
1669
- return this.selectionSet === optimizedSelection
1670
- ? this
1671
- : new InlineFragmentSelection(this.element, optimizedSelection);
1672
- }
1673
- tryOptimizeSubselectionOnce({ parentType, subSelection, candidates, fragments, }) {
1674
- let optimizedSelection = subSelection;
1675
- for (const candidate of candidates) {
1676
- const { contains, diff } = optimizedSelection.diffWithNamedFragmentIfContained(candidate, parentType);
1677
- if (contains) {
1678
- if (!diff && (0, types_1.sameType)(this.element.typeCondition, candidate.typeCondition)) {
1737
+ if (optimized instanceof NamedFragmentDefinition) {
1738
+ if ((0, types_1.sameType)(typeCondition, optimized.typeCondition)) {
1679
1739
  let spreadDirectives = this.element.appliedDirectives;
1680
- if (candidate.appliedDirectives.length > 0) {
1681
- const { isSubset, difference } = diffDirectives(this.element.appliedDirectives, candidate.appliedDirectives);
1682
- if (!isSubset) {
1683
- continue;
1684
- }
1685
- spreadDirectives = difference;
1740
+ if (optimized.appliedDirectives) {
1741
+ spreadDirectives = spreadDirectives.filter((s) => !optimized.appliedDirectives.some((d) => (0, definitions_1.sameDirectiveApplication)(d, s)));
1686
1742
  }
1687
- return {
1688
- spread: new FragmentSpreadSelection(this.parentType, fragments, candidate, spreadDirectives),
1689
- };
1743
+ return new FragmentSpreadSelection(this.parentType, fragments, optimized, spreadDirectives);
1690
1744
  }
1691
- if (candidate.appliedDirectives.length > 0) {
1692
- continue;
1745
+ else {
1746
+ optimizedSelection = selectionSetOf(typeCondition, new FragmentSpreadSelection(typeCondition, fragments, optimized, []));
1693
1747
  }
1694
- const spread = new FragmentSpreadSelection(parentType, fragments, candidate, []);
1695
- optimizedSelection = diff
1696
- ? new SelectionSetUpdates().add(spread).add(diff).toSelectionSet(parentType, fragments)
1697
- : selectionSetOf(parentType, spread);
1698
- return { spread, optimizedSelection, hasDiff: !!diff };
1748
+ }
1749
+ else {
1750
+ optimizedSelection = optimized;
1699
1751
  }
1700
1752
  }
1701
- return {};
1753
+ optimizedSelection = optimizedSelection.optimizeSelections(fragments);
1754
+ return this.selectionSet === optimizedSelection
1755
+ ? this
1756
+ : new InlineFragmentSelection(this.element, optimizedSelection);
1702
1757
  }
1703
1758
  withoutDefer(labelsToRemove) {
1704
1759
  const newSelection = this.selectionSet.withoutDefer(labelsToRemove);
@@ -1778,11 +1833,8 @@ class InlineFragmentSelection extends FragmentSelection {
1778
1833
  }
1779
1834
  return this.selectionSet === trimmedSelectionSet ? this : this.withUpdatedSelectionSet(trimmedSelectionSet);
1780
1835
  }
1781
- expandAllFragments() {
1782
- return this.mapToSelectionSet((s) => s.expandAllFragments());
1783
- }
1784
- expandFragments(names, updatedFragments) {
1785
- return this.mapToSelectionSet((s) => s.expandFragments(names, updatedFragments));
1836
+ expandFragments(updatedFragments) {
1837
+ return this.mapToSelectionSet((s) => s.expandFragments(updatedFragments));
1786
1838
  }
1787
1839
  equals(that) {
1788
1840
  if (this === that) {
@@ -1793,22 +1845,15 @@ class InlineFragmentSelection extends FragmentSelection {
1793
1845
  && this.selectionSet.equals(that.selectionSet);
1794
1846
  }
1795
1847
  contains(that) {
1796
- return (that instanceof FragmentSelection)
1797
- && this.element.equals(that.element)
1798
- && this.selectionSet.contains(that.selectionSet);
1848
+ if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
1849
+ return ContainsResult.NOT_CONTAINED;
1850
+ }
1851
+ return this.selectionSet.contains(that.selectionSet);
1799
1852
  }
1800
1853
  toString(expandFragments = true, indent) {
1801
1854
  return (indent !== null && indent !== void 0 ? indent : '') + this.element + ' ' + this.selectionSet.toString(expandFragments, true, indent);
1802
1855
  }
1803
1856
  }
1804
- function diffDirectives(superset, maybeSubset) {
1805
- if (maybeSubset.every((d) => superset.some((s) => (0, definitions_1.sameDirectiveApplication)(d, s)))) {
1806
- return { isSubset: true, difference: superset.filter((s) => !maybeSubset.some((d) => (0, definitions_1.sameDirectiveApplication)(d, s))) };
1807
- }
1808
- else {
1809
- return { isSubset: false, difference: [] };
1810
- }
1811
- }
1812
1857
  class FragmentSpreadSelection extends FragmentSelection {
1813
1858
  constructor(sourceType, fragments, namedFragment, spreadDirectives) {
1814
1859
  super(new FragmentElement(sourceType, namedFragment.typeCondition, namedFragment.appliedDirectives.concat(spreadDirectives)));
@@ -1816,6 +1861,9 @@ class FragmentSpreadSelection extends FragmentSelection {
1816
1861
  this.namedFragment = namedFragment;
1817
1862
  this.spreadDirectives = spreadDirectives;
1818
1863
  }
1864
+ isFragmentSpread() {
1865
+ return true;
1866
+ }
1819
1867
  get selectionSet() {
1820
1868
  return this.namedFragment.selectionSet;
1821
1869
  }
@@ -1829,10 +1877,8 @@ class FragmentSpreadSelection extends FragmentSelection {
1829
1877
  (0, utils_1.assert)(false, `Unsupported`);
1830
1878
  }
1831
1879
  trimUnsatisfiableBranches(parentType) {
1832
- return this.rebaseOn(parentType);
1833
- }
1834
- namedFragments() {
1835
- return this.fragments;
1880
+ (0, utils_1.assert)(parentType.schema() === this.parentType.schema(), 'Should not try to trim using a type from another schema');
1881
+ return this.rebaseOn(parentType, this.fragments);
1836
1882
  }
1837
1883
  validate() {
1838
1884
  this.validateDeferAndStream();
@@ -1859,32 +1905,29 @@ class FragmentSpreadSelection extends FragmentSelection {
1859
1905
  optimize(_) {
1860
1906
  return this;
1861
1907
  }
1862
- rebaseOn(parentType) {
1908
+ rebaseOn(parentType, fragments) {
1863
1909
  if (this.parentType === parentType) {
1864
1910
  return this;
1865
1911
  }
1866
- return new FragmentSpreadSelection(parentType, this.fragments, this.namedFragment, this.spreadDirectives);
1912
+ (0, utils_1.assert)(fragments || this.parentType.schema() === parentType.schema(), `Must provide fragments is rebasing on other schema`);
1913
+ const newFragments = fragments !== null && fragments !== void 0 ? fragments : this.fragments;
1914
+ const namedFragment = newFragments.get(this.namedFragment.name);
1915
+ (0, utils_1.assert)(namedFragment, () => `Cannot rebase ${this} if it isn't part of the provided fragments`);
1916
+ return new FragmentSpreadSelection(parentType, newFragments, namedFragment, this.spreadDirectives);
1867
1917
  }
1868
1918
  canAddTo(_) {
1869
1919
  return true;
1870
1920
  }
1871
- expandAllFragments() {
1872
- const expandedSubSelections = this.selectionSet.expandAllFragments();
1873
- return (0, types_1.sameType)(this.parentType, this.namedFragment.typeCondition) && this.element.appliedDirectives.length === 0
1874
- ? expandedSubSelections.selections()
1875
- : new InlineFragmentSelection(this.element, expandedSubSelections);
1876
- }
1877
- expandFragments(names, updatedFragments) {
1878
- if (!names.includes(this.namedFragment.name)) {
1921
+ expandFragments(updatedFragments) {
1922
+ if (updatedFragments === null || updatedFragments === void 0 ? void 0 : updatedFragments.has(this.namedFragment.name)) {
1879
1923
  return this;
1880
1924
  }
1881
- const expandedSubSelections = this.selectionSet.expandFragments(names, updatedFragments);
1925
+ const expandedSubSelections = this.selectionSet.expandFragments(updatedFragments);
1882
1926
  return (0, types_1.sameType)(this.parentType, this.namedFragment.typeCondition) && this.element.appliedDirectives.length === 0
1883
1927
  ? expandedSubSelections.selections()
1884
1928
  : new InlineFragmentSelection(this.element, expandedSubSelections);
1885
1929
  }
1886
1930
  collectUsedFragmentNames(collector) {
1887
- this.selectionSet.collectUsedFragmentNames(collector);
1888
1931
  const usageCount = collector.get(this.namedFragment.name);
1889
1932
  collector.set(this.namedFragment.name, usageCount === undefined ? 1 : usageCount + 1);
1890
1933
  }
@@ -1908,11 +1951,12 @@ class FragmentSpreadSelection extends FragmentSelection {
1908
1951
  }
1909
1952
  contains(that) {
1910
1953
  if (this.equals(that)) {
1911
- return true;
1954
+ return ContainsResult.EQUAL;
1912
1955
  }
1913
- return (that instanceof FragmentSelection)
1914
- && this.element.equals(that.element)
1915
- && this.selectionSet.contains(that.selectionSet);
1956
+ if (!(that instanceof FragmentSelection) || !this.element.equals(that.element)) {
1957
+ return ContainsResult.NOT_CONTAINED;
1958
+ }
1959
+ return this.selectionSet.contains(that.selectionSet);
1916
1960
  }
1917
1961
  toString(expandFragments = true, indent) {
1918
1962
  if (expandFragments) {
@@ -1927,7 +1971,7 @@ class FragmentSpreadSelection extends FragmentSelection {
1927
1971
  }
1928
1972
  function selectionSetOfNode(parentType, node, variableDefinitions, fragments, fieldAccessor = (type, name) => type.field(name)) {
1929
1973
  if (node.selections.length === 1) {
1930
- return selectionSetOf(parentType, selectionOfNode(parentType, node.selections[0], variableDefinitions, fragments, fieldAccessor), fragments);
1974
+ return selectionSetOf(parentType, selectionOfNode(parentType, node.selections[0], variableDefinitions, fragments, fieldAccessor));
1931
1975
  }
1932
1976
  const selections = new SelectionSetUpdates();
1933
1977
  for (const selectionNode of node.selections) {
@@ -2018,13 +2062,14 @@ function operationFromAST({ schema, operation, variableDefinitions, fragments, v
2018
2062
  var _a;
2019
2063
  const rootType = schema.schemaDefinition.root(operation.operation);
2020
2064
  validate(rootType, () => `The schema has no "${operation.operation}" root type defined`);
2065
+ const fragmentsIfAny = fragments.isEmpty() ? undefined : fragments;
2021
2066
  return new Operation(schema, operation.operation, parseSelectionSet({
2022
2067
  parentType: rootType.type,
2023
2068
  source: operation.selectionSet,
2024
2069
  variableDefinitions,
2025
- fragments: fragments.isEmpty() ? undefined : fragments,
2070
+ fragments: fragmentsIfAny,
2026
2071
  validate: validateInput,
2027
- }), variableDefinitions, (_a = operation.name) === null || _a === void 0 ? void 0 : _a.value);
2072
+ }), variableDefinitions, fragmentsIfAny, (_a = operation.name) === null || _a === void 0 ? void 0 : _a.value);
2028
2073
  }
2029
2074
  function parseOperation(schema, operation, options) {
2030
2075
  return operationFromDocument(schema, (0, graphql_1.parse)(operation), options);
@@ -2056,8 +2101,8 @@ function operationToDocument(operation) {
2056
2101
  selectionSet: operation.selectionSet.toSelectionSetNode(),
2057
2102
  variableDefinitions: operation.variableDefinitions.toVariableDefinitionNodes(),
2058
2103
  };
2059
- const fragmentASTs = operation.selectionSet.fragments
2060
- ? (_a = operation.selectionSet.fragments) === null || _a === void 0 ? void 0 : _a.toFragmentDefinitionNodes()
2104
+ const fragmentASTs = operation.fragments
2105
+ ? (_a = operation.fragments) === null || _a === void 0 ? void 0 : _a.toFragmentDefinitionNodes()
2061
2106
  : [];
2062
2107
  return {
2063
2108
  kind: graphql_1.Kind.DOCUMENT,