@apollo/federation-internals 2.8.1 → 2.8.3-beta.0

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.
@@ -59,6 +59,8 @@ import { coreFeatureDefinitionIfKnown } from "./knownCoreFeatures";
59
59
  const validationErrorCode = 'GraphQLValidationFailed';
60
60
  const DEFAULT_VALIDATION_ERROR_MESSAGE = 'The schema is not a valid GraphQL schema.';
61
61
 
62
+ const EMPTY_SET = new Set<never>();
63
+
62
64
  export const ErrGraphQLValidationFailed = (causes: GraphQLError[], message: string = DEFAULT_VALIDATION_ERROR_MESSAGE) =>
63
65
  aggregateError(validationErrorCode, message, causes);
64
66
 
@@ -660,7 +662,7 @@ export abstract class NamedSchemaElement<TOwnType extends NamedSchemaElement<TOw
660
662
  }
661
663
 
662
664
  abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSchemaElement<TOwnType, Schema, TReferencer>> extends NamedSchemaElement<TOwnType, Schema, TReferencer> {
663
- protected _referencers?: TReferencer[];
665
+ protected _referencers?: Set<TReferencer>;
664
666
  protected _extensions?: Extension<TOwnType>[];
665
667
  public preserveEmptyDefinition: boolean = false;
666
668
 
@@ -669,19 +671,12 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
669
671
  }
670
672
 
671
673
  private addReferencer(referencer: TReferencer) {
672
- if (this._referencers) {
673
- if (!this._referencers.includes(referencer)) {
674
- this._referencers.push(referencer);
675
- }
676
- } else {
677
- this._referencers = [ referencer ];
678
- }
674
+ this._referencers ??= new Set();
675
+ this._referencers.add(referencer);
679
676
  }
680
677
 
681
678
  private removeReferencer(referencer: TReferencer) {
682
- if (this._referencers) {
683
- removeArrayElement(referencer, this._referencers);
684
- }
679
+ this._referencers?.delete(referencer)
685
680
  }
686
681
 
687
682
  get coordinate(): string {
@@ -789,10 +784,11 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
789
784
  this.removeAppliedDirectives();
790
785
  this.removeInnerElements();
791
786
  // Remove this type's references.
792
- const toReturn = this._referencers?.map(r => {
787
+ const toReturn: TReferencer[] = [];
788
+ this._referencers?.forEach(r => {
793
789
  SchemaElement.prototype['removeTypeReferenceInternal'].call(r, this);
794
- return r;
795
- }) ?? [];
790
+ toReturn.push(r);
791
+ });
796
792
  this._referencers = undefined;
797
793
  // Remove this type from its parent schema.
798
794
  Schema.prototype['removeTypeInternal'].call(this._parent, this);
@@ -820,8 +816,8 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
820
816
 
821
817
  protected abstract removeReferenceRecursive(ref: TReferencer): void;
822
818
 
823
- referencers(): readonly TReferencer[] {
824
- return this._referencers ?? [];
819
+ referencers(): ReadonlySet<TReferencer> {
820
+ return this._referencers ?? EMPTY_SET;
825
821
  }
826
822
 
827
823
  isReferenced(): boolean {
@@ -1261,7 +1257,7 @@ export class Schema {
1261
1257
  if (!this.apiSchema) {
1262
1258
  this.validate();
1263
1259
 
1264
- const apiSchema = this.clone();
1260
+ const apiSchema = this.clone(undefined, false);
1265
1261
 
1266
1262
  // As we compute the API schema of a supergraph, we want to ignore explicit definitions of `@defer` and `@stream` because
1267
1263
  // those correspond to the merging of potential definitions from the subgraphs, but whether the supergraph API schema
@@ -1611,9 +1607,9 @@ export class Schema {
1611
1607
  this.isValidated = true;
1612
1608
  }
1613
1609
 
1614
- clone(builtIns?: SchemaBlueprint): Schema {
1610
+ clone(builtIns?: SchemaBlueprint, cloneJoinDirectives: boolean = true): Schema {
1615
1611
  const cloned = new Schema(builtIns ?? this.blueprint);
1616
- copy(this, cloned);
1612
+ copy(this, cloned, cloneJoinDirectives);
1617
1613
  if (this.isValidated) {
1618
1614
  cloned.assumeValid();
1619
1615
  }
@@ -2124,7 +2120,13 @@ export class ObjectType extends FieldBasedType<ObjectType, ObjectTypeReferencer>
2124
2120
  }
2125
2121
 
2126
2122
  unionsWhereMember(): readonly UnionType[] {
2127
- return this._referencers?.filter<UnionType>((r): r is UnionType => r instanceof BaseNamedType && isUnionType(r)) ?? [];
2123
+ const unions: UnionType[] = [];
2124
+ this._referencers?.forEach((r) => {
2125
+ if (r instanceof BaseNamedType && isUnionType(r)) {
2126
+ unions.push(r);
2127
+ }
2128
+ });
2129
+ return unions;
2128
2130
  }
2129
2131
  }
2130
2132
 
@@ -2133,7 +2135,13 @@ export class InterfaceType extends FieldBasedType<InterfaceType, InterfaceTypeRe
2133
2135
  readonly astDefinitionKind = Kind.INTERFACE_TYPE_DEFINITION;
2134
2136
 
2135
2137
  allImplementations(): (ObjectType | InterfaceType)[] {
2136
- return this.referencers().filter(ref => ref.kind === 'ObjectType' || ref.kind === 'InterfaceType') as (ObjectType | InterfaceType)[];
2138
+ const implementations: (ObjectType | InterfaceType)[] = [];
2139
+ this.referencers().forEach(ref => {
2140
+ if (ref.kind === 'ObjectType' || ref.kind === 'InterfaceType') {
2141
+ implementations.push(ref);
2142
+ }
2143
+ });
2144
+ return implementations;
2137
2145
  }
2138
2146
 
2139
2147
  possibleRuntimeTypes(): readonly ObjectType[] {
@@ -2291,7 +2299,7 @@ export class UnionType extends BaseNamedType<OutputTypeReferencer, UnionType> {
2291
2299
  export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
2292
2300
  readonly kind = 'EnumType' as const;
2293
2301
  readonly astDefinitionKind = Kind.ENUM_TYPE_DEFINITION;
2294
- protected readonly _values: EnumValue[] = [];
2302
+ private _values = new Map<string, EnumValue>();
2295
2303
 
2296
2304
  get values(): readonly EnumValue[] {
2297
2305
  // Because our abstractions are mutable, and removal is done by calling
@@ -2299,11 +2307,11 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
2299
2307
  // try to iterate on the result of this method and call `remove()` on
2300
2308
  // some of the return value based on some condition. But this will break
2301
2309
  // in an error-prone way if we don't copy, so we do.
2302
- return Array.from(this._values);
2310
+ return Array.from(this._values.values());
2303
2311
  }
2304
-
2312
+
2305
2313
  value(name: string): EnumValue | undefined {
2306
- return this._values.find(v => v.name === name);
2314
+ return this._values.get(name);
2307
2315
  }
2308
2316
 
2309
2317
  addValue(value: EnumValue): EnumValue;
@@ -2319,7 +2327,7 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
2319
2327
  }
2320
2328
  const existing = this.value(toAdd.name);
2321
2329
  if (!existing) {
2322
- this._values.push(toAdd);
2330
+ this._values.set(toAdd.name, toAdd);
2323
2331
  Element.prototype['setParent'].call(toAdd, this);
2324
2332
  this.onModification();
2325
2333
  return toAdd;
@@ -2333,7 +2341,7 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
2333
2341
  }
2334
2342
 
2335
2343
  private removeValueInternal(value: EnumValue) {
2336
- removeArrayElement(value, this._values);
2344
+ this._values.delete(value.name);
2337
2345
  }
2338
2346
 
2339
2347
  protected removeInnerElements(): void {
@@ -2345,7 +2353,7 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
2345
2353
  }
2346
2354
 
2347
2355
  protected hasNonExtensionInnerElements(): boolean {
2348
- return this._values.some(v => v.ofExtension() === undefined);
2356
+ return Array.from(this._values.values()).some(v => v.ofExtension() === undefined);
2349
2357
  }
2350
2358
 
2351
2359
  protected removeReferenceRecursive(ref: OutputTypeReferencer): void {
@@ -2353,7 +2361,9 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
2353
2361
  }
2354
2362
 
2355
2363
  protected removeInnerElementsExtensions(): void {
2356
- this._values.forEach(v => v.removeOfExtension());
2364
+ for (const v of this._values.values()) {
2365
+ v.removeOfExtension();
2366
+ }
2357
2367
  }
2358
2368
  }
2359
2369
 
@@ -2893,7 +2903,7 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2893
2903
  private _args?: MapWithCachedArrays<string, ArgumentDefinition<DirectiveDefinition>>;
2894
2904
  repeatable: boolean = false;
2895
2905
  private readonly _locations: DirectiveLocation[] = [];
2896
- private _referencers?: Directive<SchemaElement<any, any>, TApplicationArgs>[];
2906
+ private _referencers?: Set<Directive<SchemaElement<any, any>, TApplicationArgs>>;
2897
2907
 
2898
2908
  constructor(name: string, readonly isBuiltIn: boolean = false) {
2899
2909
  super(name);
@@ -2997,25 +3007,19 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2997
3007
  return this.locations.some((loc) => isTypeSystemDirectiveLocation(loc));
2998
3008
  }
2999
3009
 
3000
- applications(): readonly Directive<SchemaElement<any, any>, TApplicationArgs>[] {
3001
- return this._referencers ?? [];
3010
+ applications(): ReadonlySet<Directive<SchemaElement<any, any>, TApplicationArgs>> {
3011
+ this._referencers ??= new Set();
3012
+ return this._referencers;
3002
3013
  }
3003
3014
 
3004
3015
  private addReferencer(referencer: Directive<SchemaElement<any, any>, TApplicationArgs>) {
3005
3016
  assert(referencer, 'Referencer should exists');
3006
- if (this._referencers) {
3007
- if (!this._referencers.includes(referencer)) {
3008
- this._referencers.push(referencer);
3009
- }
3010
- } else {
3011
- this._referencers = [ referencer ];
3012
- }
3017
+ this._referencers ??= new Set();
3018
+ this._referencers.add(referencer);
3013
3019
  }
3014
3020
 
3015
3021
  private removeReferencer(referencer: Directive<SchemaElement<any, any>, TApplicationArgs>) {
3016
- if (this._referencers) {
3017
- removeArrayElement(referencer, this._referencers);
3018
- }
3022
+ this._referencers?.delete(referencer);
3019
3023
  }
3020
3024
 
3021
3025
  protected removeTypeReference(type: NamedType) {
@@ -3046,7 +3050,7 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
3046
3050
  // doesn't store a link to that definition. Instead, we fetch the definition
3047
3051
  // from the schema when requested. So we don't have to do anything on the
3048
3052
  // referencers other than clear them (and return the pre-cleared set).
3049
- const toReturn = this._referencers ?? [];
3053
+ const toReturn = Array.from(this._referencers ?? []);
3050
3054
  this._referencers = undefined;
3051
3055
  // Remove this directive definition from its parent schema.
3052
3056
  Schema.prototype['removeDirectiveInternal'].call(this._parent, this);
@@ -3596,7 +3600,7 @@ export function copyDirectiveDefinitionToSchema({
3596
3600
  );
3597
3601
  }
3598
3602
 
3599
- function copy(source: Schema, dest: Schema) {
3603
+ function copy(source: Schema, dest: Schema, cloneJoinDirectives: boolean) {
3600
3604
  // We shallow copy types first so any future reference to any of them can be dereferenced.
3601
3605
  for (const type of typesToCopy(source, dest)) {
3602
3606
  dest.addType(newNamedType(type.kind, type.name));
@@ -3613,7 +3617,7 @@ function copy(source: Schema, dest: Schema) {
3613
3617
 
3614
3618
  copySchemaDefinitionInner(source.schemaDefinition, dest.schemaDefinition);
3615
3619
  for (const type of typesToCopy(source, dest)) {
3616
- copyNamedTypeInner(type, dest.type(type.name)!);
3620
+ copyNamedTypeInner(type, dest.type(type.name)!, cloneJoinDirectives);
3617
3621
  }
3618
3622
  }
3619
3623
 
@@ -3653,7 +3657,7 @@ function copySchemaDefinitionInner(source: SchemaDefinition, dest: SchemaDefinit
3653
3657
  dest.sourceAST = source.sourceAST;
3654
3658
  }
3655
3659
 
3656
- function copyNamedTypeInner(source: NamedType, dest: NamedType) {
3660
+ function copyNamedTypeInner(source: NamedType, dest: NamedType, cloneJoinDirectives: boolean) {
3657
3661
  dest.preserveEmptyDefinition = source.preserveEmptyDefinition;
3658
3662
  const extensionsMap = copyExtensions(source, dest);
3659
3663
  // Same as copyAppliedDirectives, but as the directive applies to the type, we need to remember if the application
@@ -3670,7 +3674,7 @@ function copyNamedTypeInner(source: NamedType, dest: NamedType) {
3670
3674
  for (const sourceField of source.fields()) {
3671
3675
  const destField = destFieldBasedType.addField(new FieldDefinition(sourceField.name));
3672
3676
  copyOfExtension(extensionsMap, sourceField, destField);
3673
- copyFieldDefinitionInner(sourceField, destField);
3677
+ copyFieldDefinitionInner(sourceField, destField, cloneJoinDirectives);
3674
3678
  }
3675
3679
  for (const sourceImpl of source.interfaceImplementations()) {
3676
3680
  const destImpl = destFieldBasedType.addImplementedInterface(sourceImpl.interface.name);
@@ -3690,7 +3694,7 @@ function copyNamedTypeInner(source: NamedType, dest: NamedType) {
3690
3694
  const destValue = destEnumType.addValue(sourceValue.name);
3691
3695
  destValue.description = sourceValue.description;
3692
3696
  copyOfExtension(extensionsMap, sourceValue, destValue);
3693
- copyAppliedDirectives(sourceValue, destValue);
3697
+ copyAppliedDirectives(sourceValue, destValue, cloneJoinDirectives);
3694
3698
  }
3695
3699
  break
3696
3700
  case 'InputObjectType':
@@ -3698,13 +3702,13 @@ function copyNamedTypeInner(source: NamedType, dest: NamedType) {
3698
3702
  for (const sourceField of source.fields()) {
3699
3703
  const destField = destInputType.addField(new InputFieldDefinition(sourceField.name));
3700
3704
  copyOfExtension(extensionsMap, sourceField, destField);
3701
- copyInputFieldDefinitionInner(sourceField, destField);
3705
+ copyInputFieldDefinitionInner(sourceField, destField, cloneJoinDirectives);
3702
3706
  }
3703
3707
  }
3704
3708
  }
3705
3709
 
3706
- function copyAppliedDirectives(source: SchemaElement<any, any>, dest: SchemaElement<any, any>) {
3707
- source.appliedDirectives.forEach((d) => copyAppliedDirective(d, dest));
3710
+ function copyAppliedDirectives(source: SchemaElement<any, any>, dest: SchemaElement<any, any>, cloneJoinDirectives: boolean) {
3711
+ source.appliedDirectives.filter(d => cloneJoinDirectives || !d.name.startsWith('join__')).forEach((d) => copyAppliedDirective(d, dest));
3708
3712
  }
3709
3713
 
3710
3714
  function copyAppliedDirective(source: Directive<any, any>, dest: SchemaElement<any, any>): Directive<any, any> {
@@ -3713,23 +3717,27 @@ function copyAppliedDirective(source: Directive<any, any>, dest: SchemaElement<a
3713
3717
  return res;
3714
3718
  }
3715
3719
 
3716
- function copyFieldDefinitionInner<P extends ObjectType | InterfaceType>(source: FieldDefinition<P>, dest: FieldDefinition<P>) {
3720
+ function copyFieldDefinitionInner<P extends ObjectType | InterfaceType>(source: FieldDefinition<P>, dest: FieldDefinition<P>, cloneJoinDirectives: boolean) {
3717
3721
  const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()) as OutputType;
3718
3722
  dest.type = type;
3719
3723
  for (const arg of source.arguments()) {
3720
3724
  const argType = copyWrapperTypeOrTypeRef(arg.type, dest.schema());
3721
- copyArgumentDefinitionInner(arg, dest.addArgument(arg.name, argType as InputType));
3725
+ copyArgumentDefinitionInner({
3726
+ source: arg,
3727
+ dest: dest.addArgument(arg.name, argType as InputType),
3728
+ cloneJoinDirectives,
3729
+ });
3722
3730
  }
3723
- copyAppliedDirectives(source, dest);
3731
+ copyAppliedDirectives(source, dest, cloneJoinDirectives);
3724
3732
  dest.description = source.description;
3725
3733
  dest.sourceAST = source.sourceAST;
3726
3734
  }
3727
3735
 
3728
- function copyInputFieldDefinitionInner(source: InputFieldDefinition, dest: InputFieldDefinition) {
3736
+ function copyInputFieldDefinitionInner(source: InputFieldDefinition, dest: InputFieldDefinition, cloneJoinDirectives: boolean) {
3729
3737
  const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()) as InputType;
3730
3738
  dest.type = type;
3731
3739
  dest.defaultValue = source.defaultValue;
3732
- copyAppliedDirectives(source, dest);
3740
+ copyAppliedDirectives(source, dest, cloneJoinDirectives);
3733
3741
  dest.description = source.description;
3734
3742
  dest.sourceAST = source.sourceAST;
3735
3743
  }
@@ -3748,16 +3756,22 @@ function copyWrapperTypeOrTypeRef(source: Type | undefined, destParent: Schema):
3748
3756
  }
3749
3757
  }
3750
3758
 
3751
- function copyArgumentDefinitionInner<P extends FieldDefinition<any> | DirectiveDefinition>(
3759
+ function copyArgumentDefinitionInner<P extends FieldDefinition<any> | DirectiveDefinition>({
3760
+ source,
3761
+ dest,
3762
+ copyDirectiveApplications = true,
3763
+ cloneJoinDirectives,
3764
+ }: {
3752
3765
  source: ArgumentDefinition<P>,
3753
3766
  dest: ArgumentDefinition<P>,
3754
- copyDirectiveApplications: boolean = true,
3755
- ) {
3767
+ copyDirectiveApplications?: boolean,
3768
+ cloneJoinDirectives: boolean,
3769
+ }) {
3756
3770
  const type = copyWrapperTypeOrTypeRef(source.type, dest.schema()) as InputType;
3757
3771
  dest.type = type;
3758
3772
  dest.defaultValue = source.defaultValue;
3759
3773
  if (copyDirectiveApplications) {
3760
- copyAppliedDirectives(source, dest);
3774
+ copyAppliedDirectives(source, dest, cloneJoinDirectives);
3761
3775
  }
3762
3776
  dest.description = source.description;
3763
3777
  dest.sourceAST = source.sourceAST;
@@ -3779,7 +3793,12 @@ function copyDirectiveDefinitionInner(
3779
3793
 
3780
3794
  for (const arg of source.arguments()) {
3781
3795
  const type = copyWrapperTypeOrTypeRef(arg.type, dest.schema());
3782
- copyArgumentDefinitionInner(arg, dest.addArgument(arg.name, type as InputType), copyDirectiveApplicationsInArguments);
3796
+ copyArgumentDefinitionInner({
3797
+ source: arg,
3798
+ dest: dest.addArgument(arg.name, type as InputType),
3799
+ copyDirectiveApplications: copyDirectiveApplicationsInArguments,
3800
+ cloneJoinDirectives: true,
3801
+ });
3783
3802
  }
3784
3803
  dest.repeatable = source.repeatable;
3785
3804
  dest.addLocations(...locations);
package/src/federation.ts CHANGED
@@ -1195,7 +1195,7 @@ export class FederationMetadata {
1195
1195
  ): Post20FederationDirectiveDefinition<TApplicationArgs> {
1196
1196
  return this.getFederationDirective<TApplicationArgs>(name) ?? {
1197
1197
  name,
1198
- applications: () => new Array<Directive<any, TApplicationArgs>>(),
1198
+ applications: () => new Set<Directive<any, TApplicationArgs>>(),
1199
1199
  };
1200
1200
  }
1201
1201
 
@@ -1391,7 +1391,7 @@ export class FederationMetadata {
1391
1391
 
1392
1392
  export type FederationDirectiveNotDefinedInSchema<TApplicationArgs extends {[key: string]: any}> = {
1393
1393
  name: string,
1394
- applications: () => readonly Directive<any, TApplicationArgs>[],
1394
+ applications: () => ReadonlySet<Directive<any, TApplicationArgs>>,
1395
1395
  }
1396
1396
 
1397
1397
  export type Post20FederationDirectiveDefinition<TApplicationArgs extends {[key: string]: any}> =
@@ -1577,12 +1577,19 @@ export class FederationBlueprint extends SchemaBlueprint {
1577
1577
  const parent = application.parent;
1578
1578
  const name = application.arguments().name as string;
1579
1579
 
1580
+ const match = name.match(/^([A-Za-z]\w*)$/);
1580
1581
  if (name.includes('_')) {
1581
1582
  errorCollector.push(ERRORS.CONTEXT_NAME_INVALID.err(
1582
1583
  `Context name "${name}" may not contain an underscore.`,
1583
1584
  { nodes: sourceASTs(application) }
1584
1585
  ));
1585
1586
  }
1587
+ else if (!match) {
1588
+ errorCollector.push(ERRORS.CONTEXT_NAME_INVALID.err(
1589
+ `Context name "${name}" is invalid. It should have only alphanumeric characters.`,
1590
+ { nodes: sourceASTs(application) }
1591
+ ));
1592
+ }
1586
1593
  const types = contextToTypeMap.get(name);
1587
1594
  if (types) {
1588
1595
  types.push(parent);
@@ -2033,7 +2040,7 @@ function completeFed1SubgraphSchema(schema: Schema): GraphQLError[] {
2033
2040
  // definition to re-add the "correct" version, we'd have to re-attach existing applications (doable but not
2034
2041
  // done). This assert is so we notice it quickly if that ever happens (again, unlikely, because fed1 schema
2035
2042
  // is a backward compatibility thing and there is no reason to expand that too much in the future).
2036
- assert(directive.applications().length === 0, `${directive} shouldn't have had validation at that places`);
2043
+ assert(directive.applications().size === 0, `${directive} shouldn't have had validation at that places`);
2037
2044
 
2038
2045
  // The patterns we recognize and "correct" (by essentially ignoring the definition)
2039
2046
  // are:
package/src/operations.ts CHANGED
@@ -1594,16 +1594,17 @@ export class SelectionSet {
1594
1594
  // No match, so we need to create a new fragment. First, we minimize the
1595
1595
  // selection set before creating the fragment with it.
1596
1596
  const [minimizedSelectionSet] = selection.selectionSet.minimizeSelectionSet(namedFragments, seenSelections);
1597
+ const updatedEquivalentSelectionSetCandidates = seenSelections.get(mockHashCode); // may have changed after previous statement
1597
1598
  const fragmentDefinition = new NamedFragmentDefinition(
1598
1599
  this.parentType.schema(),
1599
- `_generated_${mockHashCode}_${equivalentSelectionSetCandidates?.length ?? 0}`,
1600
+ `_generated_${mockHashCode}_${updatedEquivalentSelectionSetCandidates?.length ?? 0}`,
1600
1601
  selection.element.typeCondition
1601
1602
  ).setSelectionSet(minimizedSelectionSet);
1602
1603
  namedFragments.add(fragmentDefinition);
1603
1604
 
1604
1605
  // Create a new "hash code" bucket or add to the existing one.
1605
- if (equivalentSelectionSetCandidates) {
1606
- equivalentSelectionSetCandidates.push([selection.selectionSet, fragmentDefinition]);
1606
+ if (updatedEquivalentSelectionSetCandidates) {
1607
+ updatedEquivalentSelectionSetCandidates.push([selection.selectionSet, fragmentDefinition]);
1607
1608
  } else {
1608
1609
  seenSelections.set(mockHashCode, [[selection.selectionSet, fragmentDefinition]]);
1609
1610
  }
@@ -233,7 +233,7 @@ export function upgradeSubgraphsIfNecessary(inputs: Subgraphs): UpgradeResult {
233
233
  for (const subgraph of inputs.values()) {
234
234
  if (subgraph.isFed2Subgraph()) {
235
235
  subgraphs.add(subgraph);
236
- if (subgraph.metadata().interfaceObjectDirective().applications().length > 0) {
236
+ if (subgraph.metadata().interfaceObjectDirective().applications().size > 0) {
237
237
  subgraphsUsingInterfaceObject.push(subgraph.name);
238
238
  }
239
239
  } else {