@apollo/federation-internals 2.1.0-alpha.4 → 2.1.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +1 -10
  2. package/dist/buildSchema.d.ts.map +1 -1
  3. package/dist/buildSchema.js +1 -1
  4. package/dist/buildSchema.js.map +1 -1
  5. package/dist/coreSpec.d.ts +1 -4
  6. package/dist/coreSpec.d.ts.map +1 -1
  7. package/dist/coreSpec.js +1 -5
  8. package/dist/coreSpec.js.map +1 -1
  9. package/dist/definitions.d.ts +26 -29
  10. package/dist/definitions.d.ts.map +1 -1
  11. package/dist/definitions.js +202 -158
  12. package/dist/definitions.js.map +1 -1
  13. package/dist/error.d.ts +5 -0
  14. package/dist/error.d.ts.map +1 -1
  15. package/dist/error.js +47 -1
  16. package/dist/error.js.map +1 -1
  17. package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
  18. package/dist/extractSubgraphsFromSupergraph.js +126 -5
  19. package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
  20. package/dist/federation.d.ts +2 -2
  21. package/dist/federation.d.ts.map +1 -1
  22. package/dist/federation.js +6 -6
  23. package/dist/federation.js.map +1 -1
  24. package/dist/operations.d.ts +2 -2
  25. package/dist/operations.d.ts.map +1 -1
  26. package/dist/operations.js +23 -20
  27. package/dist/operations.js.map +1 -1
  28. package/dist/print.js.map +1 -1
  29. package/dist/schemaUpgrader.d.ts.map +1 -1
  30. package/dist/schemaUpgrader.js +4 -4
  31. package/dist/schemaUpgrader.js.map +1 -1
  32. package/dist/supergraphs.d.ts +1 -3
  33. package/dist/supergraphs.d.ts.map +1 -1
  34. package/dist/supergraphs.js +9 -22
  35. package/dist/supergraphs.js.map +1 -1
  36. package/dist/utils.d.ts +2 -1
  37. package/dist/utils.d.ts.map +1 -1
  38. package/dist/utils.js +12 -1
  39. package/dist/utils.js.map +1 -1
  40. package/package.json +2 -3
  41. package/src/__tests__/coreSpec.test.ts +1 -1
  42. package/src/__tests__/definitions.test.ts +13 -4
  43. package/src/__tests__/extractSubgraphsFromSupergraph.test.ts +9 -5
  44. package/src/__tests__/removeInaccessibleElements.test.ts +1 -3
  45. package/src/__tests__/schemaUpgrader.test.ts +0 -1
  46. package/src/__tests__/subgraphValidation.test.ts +1 -2
  47. package/src/buildSchema.ts +1 -2
  48. package/src/coreSpec.ts +2 -7
  49. package/src/definitions.ts +173 -148
  50. package/src/error.ts +62 -0
  51. package/src/extractSubgraphsFromSupergraph.ts +178 -7
  52. package/src/federation.ts +4 -3
  53. package/src/operations.ts +39 -28
  54. package/src/print.ts +2 -2
  55. package/src/schemaUpgrader.ts +5 -4
  56. package/src/supergraphs.ts +16 -25
  57. package/src/utils.ts +15 -0
  58. package/tsconfig.test.tsbuildinfo +1 -1
  59. package/tsconfig.tsbuildinfo +1 -1
@@ -31,67 +31,30 @@ import {
31
31
  isCoreSpecDirectiveApplication,
32
32
  removeAllCoreFeatures,
33
33
  } from "./coreSpec";
34
- import { assert, mapValues, MapWithCachedArrays, setValues } from "./utils";
34
+ import { assert, mapValues, MapWithCachedArrays, removeArrayElement } from "./utils";
35
35
  import { withDefaultValues, valueEquals, valueToString, valueToAST, variablesInValue, valueFromAST, valueNodeToConstValueNode, argumentsEquals } from "./values";
36
36
  import { removeInaccessibleElements } from "./inaccessibleSpec";
37
37
  import { printDirectiveDefinition, printSchema } from './print';
38
38
  import { sameType } from './types';
39
39
  import { addIntrospectionFields, introspectionFieldNames, isIntrospectionName } from "./introspection";
40
- import { err } from '@apollo/core-schema';
41
- import { GraphQLErrorExt } from "@apollo/core-schema/dist/error";
42
40
  import { validateSDL } from "graphql/validation/validate";
43
41
  import { SDLValidationRule } from "graphql/validation/ValidationContext";
44
42
  import { specifiedSDLRules } from "graphql/validation/specifiedRules";
45
43
  import { validateSchema } from "./validate";
46
44
  import { createDirectiveSpecification, createScalarTypeSpecification, DirectiveSpecification, TypeSpecification } from "./directiveAndTypeSpecification";
47
45
  import { didYouMean, suggestionList } from "./suggestions";
48
- import { ERRORS, withModifiedErrorMessage } from "./error";
46
+ import { aggregateError, ERRORS, withModifiedErrorMessage } from "./error";
49
47
 
50
48
  const validationErrorCode = 'GraphQLValidationFailed';
51
49
  const DEFAULT_VALIDATION_ERROR_MESSAGE = 'The schema is not a valid GraphQL schema.';
52
50
 
53
51
  export const ErrGraphQLValidationFailed = (causes: GraphQLError[], message: string = DEFAULT_VALIDATION_ERROR_MESSAGE) =>
54
- err(validationErrorCode, {
55
- message: message + '. Caused by:\n' + causes.map((c) => c.toString()).join('\n\n'),
56
- causes
57
- });
52
+ aggregateError(validationErrorCode, message, causes);
58
53
 
59
54
  const apiSchemaValidationErrorCode = 'GraphQLAPISchemaValidationFailed';
60
55
 
61
56
  export const ErrGraphQLAPISchemaValidationFailed = (causes: GraphQLError[]) =>
62
- err(apiSchemaValidationErrorCode, {
63
- message: 'The supergraph schema failed to produce a valid API schema',
64
- causes
65
- });
66
-
67
- /**
68
- * Given an error that may have been thrown during schema validation, extract the causes of validation failure.
69
- * If the error is not a graphQL error, undefined is returned.
70
- */
71
- export function errorCauses(e: Error): GraphQLError[] | undefined {
72
- if (e instanceof GraphQLErrorExt) {
73
- if (e.code === validationErrorCode || e.code === apiSchemaValidationErrorCode) {
74
- return ((e as any).causes) as GraphQLError[];
75
- }
76
- return [e];
77
- }
78
- if (e instanceof GraphQLError) {
79
- return [e];
80
- }
81
- return undefined;
82
- }
83
-
84
- export function printGraphQLErrorsOrRethrow(e: Error): string {
85
- const causes = errorCauses(e);
86
- if (!causes) {
87
- throw e;
88
- }
89
- return causes.map(e => e.toString()).join('\n\n');
90
- }
91
-
92
- export function printErrors(errors: GraphQLError[]): string {
93
- return errors.map(e => e.toString()).join('\n\n');
94
- }
57
+ aggregateError(apiSchemaValidationErrorCode, 'The supergraph schema failed to produce a valid API schema', causes);
95
58
 
96
59
  export const typenameFieldName = '__typename';
97
60
 
@@ -372,7 +335,7 @@ export interface Named {
372
335
  export type ExtendableElement = SchemaDefinition | NamedType;
373
336
 
374
337
  export class DirectiveTargetElement<T extends DirectiveTargetElement<T>> {
375
- public readonly appliedDirectives: Directive<T>[] = [];
338
+ private _appliedDirectives: Directive<T>[] | undefined;
376
339
 
377
340
  constructor(private readonly _schema: Schema) {}
378
341
 
@@ -387,6 +350,10 @@ export class DirectiveTargetElement<T extends DirectiveTargetElement<T>> {
387
350
  return this.appliedDirectives.filter(d => d.name == directiveName);
388
351
  }
389
352
 
353
+ get appliedDirectives(): readonly Directive<T>[] {
354
+ return this._appliedDirectives ?? [];
355
+ }
356
+
390
357
  hasAppliedDirective(nameOrDefinition: string | DirectiveDefinition): boolean {
391
358
  const directiveName = typeof nameOrDefinition === 'string' ? nameOrDefinition : nameOrDefinition.name;
392
359
  return this.appliedDirectives.some(d => d.name == directiveName);
@@ -410,7 +377,11 @@ export class DirectiveTargetElement<T extends DirectiveTargetElement<T>> {
410
377
  }
411
378
  Element.prototype['setParent'].call(toAdd, this);
412
379
  // TODO: we should typecheck arguments or our TApplicationArgs business is just a lie.
413
- this.appliedDirectives.push(toAdd);
380
+ if (this._appliedDirectives) {
381
+ this._appliedDirectives.push(toAdd);
382
+ } else {
383
+ this._appliedDirectives = [ toAdd ];
384
+ }
414
385
  return toAdd;
415
386
  }
416
387
 
@@ -532,35 +503,40 @@ type UnappliedDirective = {
532
503
 
533
504
  // TODO: ideally, we should hide the ctor of this class as we rely in places on the fact the no-one external defines new implementations.
534
505
  export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>, TParent extends SchemaElement<any, any> | Schema> extends Element<TParent> {
535
- protected readonly _appliedDirectives: Directive<TOwnType>[] = [];
536
- protected _unappliedDirectives: UnappliedDirective[] = [];
506
+ protected _appliedDirectives: Directive<TOwnType>[] | undefined;
507
+ protected _unappliedDirectives: UnappliedDirective[] | undefined;
537
508
  description?: string;
538
509
 
539
510
  addUnappliedDirective({ nameOrDef, args, extension, directive }: UnappliedDirective) {
540
- this._unappliedDirectives.push({
511
+ const toAdd = {
541
512
  nameOrDef,
542
513
  args: args ?? {},
543
514
  extension,
544
515
  directive,
545
- });
516
+ };
517
+ if (this._unappliedDirectives) {
518
+ this._unappliedDirectives.push(toAdd);
519
+ } else {
520
+ this._unappliedDirectives = [toAdd];
521
+ }
546
522
  }
547
523
 
548
524
  processUnappliedDirectives() {
549
- for (const { nameOrDef, args, extension, directive } of this._unappliedDirectives) {
525
+ for (const { nameOrDef, args, extension, directive } of this._unappliedDirectives ?? []) {
550
526
  const d = this.applyDirective(nameOrDef, args);
551
527
  d.setOfExtension(extension);
552
528
  d.sourceAST = directive;
553
529
  }
554
- this._unappliedDirectives = [];
530
+ this._unappliedDirectives = undefined;
555
531
  }
556
532
 
557
533
  get appliedDirectives(): readonly Directive<TOwnType>[] {
558
- return this._appliedDirectives;
534
+ return this._appliedDirectives ?? [];
559
535
  }
560
536
 
561
537
  appliedDirectivesOf<TApplicationArgs extends {[key: string]: any} = {[key: string]: any}>(nameOrDefinition: string | DirectiveDefinition<TApplicationArgs>): Directive<TOwnType, TApplicationArgs>[] {
562
538
  const directiveName = typeof nameOrDefinition === 'string' ? nameOrDefinition : nameOrDefinition.name;
563
- return this._appliedDirectives.filter(d => d.name == directiveName) as Directive<TOwnType, TApplicationArgs>[];
539
+ return this.appliedDirectives.filter(d => d.name == directiveName) as Directive<TOwnType, TApplicationArgs>[];
564
540
  }
565
541
 
566
542
  hasAppliedDirective(nameOrDefinition: string | DirectiveDefinition<any>): boolean {
@@ -600,10 +576,14 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
600
576
  const toAdd = new Directive<TOwnType, TApplicationArgs>(name, args ?? Object.create(null));
601
577
  Element.prototype['setParent'].call(toAdd, this);
602
578
  // TODO: we should typecheck arguments or our TApplicationArgs business is just a lie.
603
- if (asFirstDirective) {
604
- this._appliedDirectives.unshift(toAdd);
579
+ if (this._appliedDirectives) {
580
+ if (asFirstDirective) {
581
+ this._appliedDirectives.unshift(toAdd);
582
+ } else {
583
+ this._appliedDirectives.push(toAdd);
584
+ }
605
585
  } else {
606
- this._appliedDirectives.push(toAdd);
586
+ this._appliedDirectives = [toAdd];
607
587
  }
608
588
  DirectiveDefinition.prototype['addReferencer'].call(toAdd.definition!, toAdd);
609
589
  this.onModification();
@@ -612,6 +592,9 @@ export abstract class SchemaElement<TOwnType extends SchemaElement<any, TParent>
612
592
 
613
593
  protected removeAppliedDirectives() {
614
594
  // We copy the array because this._appliedDirectives is modified in-place by `directive.remove()`
595
+ if (!this._appliedDirectives) {
596
+ return;
597
+ }
615
598
  const applied = this._appliedDirectives.concat();
616
599
  applied.forEach(d => d.remove());
617
600
  }
@@ -685,8 +668,8 @@ export abstract class NamedSchemaElement<TOwnType extends NamedSchemaElement<TOw
685
668
  }
686
669
 
687
670
  abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSchemaElement<TOwnType, Schema, TReferencer>> extends NamedSchemaElement<TOwnType, Schema, TReferencer> {
688
- protected readonly _referencers: Set<TReferencer> = new Set();
689
- protected readonly _extensions: Set<Extension<TOwnType>> = new Set();
671
+ protected _referencers?: TReferencer[];
672
+ protected _extensions?: Extension<TOwnType>[];
690
673
  public preserveEmptyDefinition: boolean = false;
691
674
 
692
675
  constructor(name: string, readonly isBuiltIn: boolean = false) {
@@ -694,11 +677,19 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
694
677
  }
695
678
 
696
679
  private addReferencer(referencer: TReferencer) {
697
- this._referencers.add(referencer);
680
+ if (this._referencers) {
681
+ if (!this._referencers.includes(referencer)) {
682
+ this._referencers.push(referencer);
683
+ }
684
+ } else {
685
+ this._referencers = [ referencer ];
686
+ }
698
687
  }
699
688
 
700
689
  private removeReferencer(referencer: TReferencer) {
701
- this._referencers.delete(referencer);
690
+ if (this._referencers) {
691
+ removeArrayElement(referencer, this._referencers);
692
+ }
702
693
  }
703
694
 
704
695
  get coordinate(): string {
@@ -709,8 +700,12 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
709
700
  // Overriden by those types that do have children
710
701
  }
711
702
 
712
- extensions(): ReadonlySet<Extension<TOwnType>> {
713
- return this._extensions;
703
+ extensions(): readonly Extension<TOwnType>[] {
704
+ return this._extensions ?? [];
705
+ }
706
+
707
+ hasExtension(extension: Extension<any>): boolean {
708
+ return this._extensions?.includes(extension) ?? false;
714
709
  }
715
710
 
716
711
  newExtension(): Extension<TOwnType> {
@@ -720,23 +715,27 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
720
715
  addExtension(extension: Extension<TOwnType>): Extension<TOwnType> {
721
716
  this.checkUpdate();
722
717
  // Let's be nice and not complaint if we add an extension already added.
723
- if (this._extensions.has(extension)) {
718
+ if (this.hasExtension(extension)) {
724
719
  return extension;
725
720
  }
726
721
  assert(!extension.extendedElement, () => `Cannot add extension to type ${this}: it is already added to another type`);
727
- this._extensions.add(extension);
722
+ if (this._extensions) {
723
+ this._extensions.push(extension);
724
+ } else {
725
+ this._extensions = [ extension ];
726
+ }
728
727
  Extension.prototype['setExtendedElement'].call(extension, this);
729
728
  this.onModification();
730
729
  return extension;
731
730
  }
732
731
 
733
732
  removeExtensions() {
734
- if (this._extensions.size === 0) {
733
+ if (!this._extensions) {
735
734
  return;
736
735
  }
737
736
 
738
- this._extensions.clear();
739
- for (const directive of this._appliedDirectives) {
737
+ this._extensions = undefined;
738
+ for (const directive of this.appliedDirectives) {
740
739
  directive.removeOfExtension();
741
740
  }
742
741
  this.removeInnerElementsExtensions();
@@ -747,12 +746,12 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
747
746
  }
748
747
 
749
748
  hasExtensionElements(): boolean {
750
- return this._extensions.size > 0;
749
+ return !!this._extensions;
751
750
  }
752
751
 
753
752
  hasNonExtensionElements(): boolean {
754
753
  return this.preserveEmptyDefinition
755
- || this._appliedDirectives.some(d => d.ofExtension() === undefined)
754
+ || this.appliedDirectives.some(d => d.ofExtension() === undefined)
756
755
  || this.hasNonExtensionInnerElements();
757
756
  }
758
757
 
@@ -798,11 +797,11 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
798
797
  this.removeAppliedDirectives();
799
798
  this.removeInnerElements();
800
799
  // Remove this type's references.
801
- const toReturn = setValues(this._referencers).map(r => {
800
+ const toReturn = this._referencers?.map(r => {
802
801
  SchemaElement.prototype['removeTypeReferenceInternal'].call(r, this);
803
802
  return r;
804
- });
805
- this._referencers.clear();
803
+ }) ?? [];
804
+ this._referencers = undefined;
806
805
  // Remove this type from its parent schema.
807
806
  Schema.prototype['removeTypeInternal'].call(this._parent, this);
808
807
  this._parent = undefined;
@@ -830,11 +829,11 @@ abstract class BaseNamedType<TReferencer, TOwnType extends NamedType & NamedSche
830
829
  protected abstract removeReferenceRecursive(ref: TReferencer): void;
831
830
 
832
831
  referencers(): readonly TReferencer[] {
833
- return setValues(this._referencers);
832
+ return this._referencers ?? [];
834
833
  }
835
834
 
836
835
  isReferenced(): boolean {
837
- return this._referencers.size > 0;
836
+ return !!this._referencers;
838
837
  }
839
838
 
840
839
  protected abstract removeInnerElements(): void;
@@ -887,8 +886,7 @@ abstract class BaseExtensionMember<TExtended extends ExtendableElement> extends
887
886
 
888
887
  setOfExtension(extension: Extension<TExtended> | undefined) {
889
888
  this.checkUpdate();
890
- // See similar comment on FieldDefinition.setOfExtension for why we have to cast.
891
- assert(!extension || this._parent?.extensions().has(extension as any), () => `Cannot set object as part of the provided extension: it is not an extension of parent ${this.parent}`);
889
+ assert(!extension || this._parent?.hasExtension(extension), () => `Cannot set object as part of the provided extension: it is not an extension of parent ${this.parent}`);
892
890
  this._extension = extension;
893
891
  }
894
892
 
@@ -1130,15 +1128,16 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
1130
1128
  locations: [DirectiveLocation.SCALAR],
1131
1129
  argumentFct: (schema) => ({ args: [{ name: 'url', type: new NonNullType(schema.stringType()) }], errors: [] })
1132
1130
  }),
1133
- // TODO: currently inconditionally adding @defer as the list of built-in. It's probably fine, but double check if we want to not do so when @defer-support is
1134
- // not enabled or something (it would probably be hard to handle it at that point anyway but well...).
1131
+ // Note that @defer and @stream are unconditionally added to `Schema` even if they are technically "optional" built-in. _But_,
1132
+ // the `Schema#toGraphQLJSSchema` method has an option to decide if @defer/@stream should be included or not in the resulting
1133
+ // schema, which is how the gateway and router can, at runtime, decide to include or not include them based on actual support.
1135
1134
  createDirectiveSpecification({
1136
1135
  name: 'defer',
1137
1136
  locations: [DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
1138
1137
  argumentFct: (schema) => ({
1139
1138
  args: [
1140
1139
  { name: 'label', type: schema.stringType() },
1141
- { name: 'if', type: schema.booleanType() },
1140
+ { name: 'if', type: new NonNullType(schema.booleanType()), defaultValue: true },
1142
1141
  ],
1143
1142
  errors: [],
1144
1143
  })
@@ -1153,7 +1152,7 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
1153
1152
  args: [
1154
1153
  { name: 'label', type: schema.stringType() },
1155
1154
  { name: 'initialCount', type: schema.intType(), defaultValue: 0 },
1156
- { name: 'if', type: schema.booleanType() },
1155
+ { name: 'if', type: new NonNullType(schema.booleanType()), defaultValue: true },
1157
1156
  ],
1158
1157
  errors: [],
1159
1158
  })
@@ -1161,9 +1160,6 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
1161
1160
  ];
1162
1161
 
1163
1162
  export type DeferDirectiveArgs = {
1164
- // TODO: we currently do not support variables for the defer label. Passing a label in a variable
1165
- // feels like a weird use case in the first place, but we should probably fix this nonetheless (or
1166
- // if we decide to have it be a known limitations, we should at least reject it cleanly).
1167
1163
  label?: string,
1168
1164
  if?: boolean | Variable,
1169
1165
  }
@@ -1178,6 +1174,10 @@ export type StreamDirectiveArgs = {
1178
1174
  // A coordinate is up to 3 "graphQL name" ([_A-Za-z][_0-9A-Za-z]*).
1179
1175
  const coordinateRegexp = /^@?[_A-Za-z][_0-9A-Za-z]*(\.[_A-Za-z][_0-9A-Za-z]*)?(\([_A-Za-z][_0-9A-Za-z]*:\))?$/;
1180
1176
 
1177
+ export type SchemaConfig = {
1178
+ cacheAST?: boolean,
1179
+ }
1180
+
1181
1181
  export class Schema {
1182
1182
  private _schemaDefinition: SchemaDefinition;
1183
1183
  private readonly _builtInTypes = new MapWithCachedArrays<string, NamedType>();
@@ -1191,7 +1191,10 @@ export class Schema {
1191
1191
  private cachedDocument?: DocumentNode;
1192
1192
  private apiSchema?: Schema;
1193
1193
 
1194
- constructor(readonly blueprint: SchemaBlueprint = defaultSchemaBlueprint) {
1194
+ constructor(
1195
+ readonly blueprint: SchemaBlueprint = defaultSchemaBlueprint,
1196
+ readonly config: SchemaConfig = {},
1197
+ ) {
1195
1198
  this._schemaDefinition = new SchemaDefinition();
1196
1199
  Element.prototype['setParent'].call(this._schemaDefinition, this);
1197
1200
  graphQLBuiltInTypesSpecifications.forEach((spec) => spec.checkOrAdd(this, undefined, true));
@@ -1241,10 +1244,6 @@ export class Schema {
1241
1244
  }
1242
1245
  }
1243
1246
 
1244
- private forceSetCachedDocument(document: DocumentNode) {
1245
- this.cachedDocument = document;
1246
- }
1247
-
1248
1247
  isCoreSchema(): boolean {
1249
1248
  return this.coreFeatures !== undefined;
1250
1249
  }
@@ -1256,7 +1255,12 @@ export class Schema {
1256
1255
  toAST(): DocumentNode {
1257
1256
  if (!this.cachedDocument) {
1258
1257
  // As we're not building the document from a file, having locations info might be more confusing that not.
1259
- this.forceSetCachedDocument(parse(printSchema(this), { noLocation: true }));
1258
+ const ast = parse(printSchema(this), { noLocation: true });
1259
+ const shouldCache = this.config.cacheAST ?? false;
1260
+ if (!shouldCache) {
1261
+ return ast;
1262
+ }
1263
+ this.cachedDocument = ast;
1260
1264
  }
1261
1265
  return this.cachedDocument!;
1262
1266
  }
@@ -1293,8 +1297,9 @@ export class Schema {
1293
1297
  return nodes;
1294
1298
  }
1295
1299
 
1296
- toGraphQLJSSchema(config?: { includeDefer?: boolean }): GraphQLSchema {
1300
+ toGraphQLJSSchema(config?: { includeDefer?: boolean, includeStream?: boolean }): GraphQLSchema {
1297
1301
  const includeDefer = config?.includeDefer ?? false;
1302
+ const includeStream = config?.includeStream ?? false;
1298
1303
 
1299
1304
  let ast = this.toAST();
1300
1305
 
@@ -1306,6 +1311,9 @@ export class Schema {
1306
1311
  if (includeDefer) {
1307
1312
  additionalNodes.push(this.deferDirective().toAST());
1308
1313
  }
1314
+ if (includeStream) {
1315
+ additionalNodes.push(this.streamDirective().toAST());
1316
+ }
1309
1317
  if (additionalNodes.length > 0) {
1310
1318
  ast = {
1311
1319
  kind: Kind.DOCUMENT,
@@ -1537,8 +1545,10 @@ export class Schema {
1537
1545
  }
1538
1546
 
1539
1547
  invalidate() {
1548
+ if (this.isValidated) {
1549
+ this.blueprint.onInvalidation(this);
1550
+ }
1540
1551
  this.isValidated = false;
1541
- this.blueprint.onInvalidation(this);
1542
1552
  }
1543
1553
 
1544
1554
  validate() {
@@ -1682,7 +1692,7 @@ export class RootType extends BaseExtensionMember<SchemaDefinition> {
1682
1692
  export class SchemaDefinition extends SchemaElement<SchemaDefinition, Schema> {
1683
1693
  readonly kind = 'SchemaDefinition' as const;
1684
1694
  protected readonly _roots = new MapWithCachedArrays<SchemaRootKind, RootType>();
1685
- protected readonly _extensions = new Set<Extension<SchemaDefinition>>();
1695
+ protected _extensions: Extension<SchemaDefinition>[] | undefined;
1686
1696
  public preserveEmptyDefinition: boolean = false;
1687
1697
 
1688
1698
  roots(): readonly RootType[] {
@@ -1752,8 +1762,12 @@ export class SchemaDefinition extends SchemaElement<SchemaDefinition, Schema> {
1752
1762
  return toSet;
1753
1763
  }
1754
1764
 
1755
- extensions(): ReadonlySet<Extension<SchemaDefinition>> {
1756
- return this._extensions;
1765
+ extensions(): Extension<SchemaDefinition>[] {
1766
+ return this._extensions ?? [];
1767
+ }
1768
+
1769
+ hasExtension(extension: Extension<any>): boolean {
1770
+ return this._extensions?.includes(extension) ?? false;
1757
1771
  }
1758
1772
 
1759
1773
  newExtension(): Extension<SchemaDefinition> {
@@ -1763,23 +1777,27 @@ export class SchemaDefinition extends SchemaElement<SchemaDefinition, Schema> {
1763
1777
  addExtension(extension: Extension<SchemaDefinition>): Extension<SchemaDefinition> {
1764
1778
  this.checkUpdate();
1765
1779
  // Let's be nice and not complaint if we add an extension already added.
1766
- if (this._extensions.has(extension)) {
1780
+ if (this.hasExtension(extension)) {
1767
1781
  return extension;
1768
1782
  }
1769
1783
  assert(!extension.extendedElement, 'Cannot add extension to this schema: extension is already added to another schema');
1770
- this._extensions.add(extension);
1784
+ if (this._extensions) {
1785
+ this._extensions.push(extension);
1786
+ } else {
1787
+ this._extensions = [extension];
1788
+ }
1771
1789
  Extension.prototype['setExtendedElement'].call(extension, this);
1772
1790
  this.onModification();
1773
1791
  return extension;
1774
1792
  }
1775
1793
 
1776
1794
  hasExtensionElements(): boolean {
1777
- return this._extensions.size > 0;
1795
+ return !!this._extensions;
1778
1796
  }
1779
1797
 
1780
1798
  hasNonExtensionElements(): boolean {
1781
1799
  return this.preserveEmptyDefinition
1782
- || this._appliedDirectives.some((d) => d.ofExtension() === undefined)
1800
+ || this.appliedDirectives.some((d) => d.ofExtension() === undefined)
1783
1801
  || this.roots().some((r) => r.ofExtension() === undefined);
1784
1802
  }
1785
1803
 
@@ -1853,7 +1871,7 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
1853
1871
  // either to the main type definition _or_ to a single extension. In theory, a document could have `implements X`
1854
1872
  // in both of those places (or on 2 distinct extensions). We don't preserve that level of detail, but this
1855
1873
  // feels like a very minor limitation with little practical impact, and it avoids additional complexity.
1856
- private readonly _interfaceImplementations: MapWithCachedArrays<string, InterfaceImplementation<T>> = new MapWithCachedArrays();
1874
+ private _interfaceImplementations: MapWithCachedArrays<string, InterfaceImplementation<T>> | undefined;
1857
1875
  private readonly _fields: MapWithCachedArrays<string, FieldDefinition<T>> = new MapWithCachedArrays();
1858
1876
  private _cachedNonBuiltInFields?: readonly FieldDefinition<T>[];
1859
1877
 
@@ -1872,11 +1890,11 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
1872
1890
  }
1873
1891
 
1874
1892
  interfaceImplementations(): readonly InterfaceImplementation<T>[] {
1875
- return this._interfaceImplementations.values();
1893
+ return this._interfaceImplementations?.values() ?? [];
1876
1894
  }
1877
1895
 
1878
1896
  interfaceImplementation(type: string | InterfaceType): InterfaceImplementation<T> | undefined {
1879
- return this._interfaceImplementations.get(typeof type === 'string' ? type : type.name);
1897
+ return this._interfaceImplementations ? this._interfaceImplementations.get(typeof type === 'string' ? type : type.name) : undefined;
1880
1898
  }
1881
1899
 
1882
1900
  interfaces(): readonly InterfaceType[] {
@@ -1884,7 +1902,7 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
1884
1902
  }
1885
1903
 
1886
1904
  implementsInterface(type: string | InterfaceType): boolean {
1887
- return this._interfaceImplementations.has(typeof type === 'string' ? type : type.name);
1905
+ return this._interfaceImplementations?.has(typeof type === 'string' ? type : type.name) ?? false;
1888
1906
  }
1889
1907
 
1890
1908
  addImplementedInterface(nameOrItfOrItfImpl: InterfaceImplementation<T> | InterfaceType | string): InterfaceImplementation<T> {
@@ -1908,8 +1926,11 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
1908
1926
  }
1909
1927
  toAdd = new InterfaceImplementation<T>(itf);
1910
1928
  }
1911
- const existing = this._interfaceImplementations.get(toAdd.interface.name);
1929
+ const existing = this._interfaceImplementations?.get(toAdd.interface.name);
1912
1930
  if (!existing) {
1931
+ if (!this._interfaceImplementations) {
1932
+ this._interfaceImplementations = new MapWithCachedArrays();
1933
+ }
1913
1934
  this._interfaceImplementations.set(toAdd.interface.name, toAdd);
1914
1935
  addReferenceToType(this, toAdd.interface);
1915
1936
  Element.prototype['setParent'].call(toAdd, this);
@@ -1996,12 +2017,12 @@ abstract class FieldBasedType<T extends (ObjectType | InterfaceType) & NamedSche
1996
2017
  }
1997
2018
 
1998
2019
  private removeInterfaceImplementation(itf: InterfaceType) {
1999
- this._interfaceImplementations.delete(itf.name);
2020
+ this._interfaceImplementations?.delete(itf.name);
2000
2021
  removeReferenceToType(this, itf);
2001
2022
  }
2002
2023
 
2003
2024
  protected removeTypeReference(type: NamedType) {
2004
- this._interfaceImplementations.delete(type.name);
2025
+ this._interfaceImplementations?.delete(type.name);
2005
2026
  }
2006
2027
 
2007
2028
  protected removeInnerElements(): void {
@@ -2071,7 +2092,7 @@ export class InterfaceType extends FieldBasedType<InterfaceType, InterfaceTypeRe
2071
2092
  readonly astDefinitionKind = Kind.INTERFACE_TYPE_DEFINITION;
2072
2093
 
2073
2094
  allImplementations(): (ObjectType | InterfaceType)[] {
2074
- return setValues(this._referencers).filter(ref => ref.kind === 'ObjectType' || ref.kind === 'InterfaceType') as (ObjectType | InterfaceType)[];
2095
+ return this.referencers().filter(ref => ref.kind === 'ObjectType' || ref.kind === 'InterfaceType') as (ObjectType | InterfaceType)[];
2075
2096
  }
2076
2097
 
2077
2098
  possibleRuntimeTypes(): readonly ObjectType[] {
@@ -2271,15 +2292,12 @@ export class EnumType extends BaseNamedType<OutputTypeReferencer, EnumType> {
2271
2292
  }
2272
2293
 
2273
2294
  private removeValueInternal(value: EnumValue) {
2274
- const index = this._values.indexOf(value);
2275
- if (index >= 0) {
2276
- this._values.splice(index, 1);
2277
- }
2295
+ removeArrayElement(value, this._values);
2278
2296
  }
2279
2297
 
2280
2298
  protected removeInnerElements(): void {
2281
- // Make a copy, since EnumValue.remove() will modify this._values.
2282
- const values = Array.from(this._values);
2299
+ // Make a copy (indirectly), since EnumValue.remove() will modify this._values.
2300
+ const values = this.values;
2283
2301
  for (const value of values) {
2284
2302
  value.remove();
2285
2303
  }
@@ -2434,7 +2452,7 @@ export class NonNullType<T extends NullableType> extends BaseWrapperType<T> {
2434
2452
 
2435
2453
  export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaElementWithType<OutputType, FieldDefinition<TParent>, TParent, never> {
2436
2454
  readonly kind = 'FieldDefinition' as const;
2437
- private readonly _args: MapWithCachedArrays<string, ArgumentDefinition<FieldDefinition<TParent>>> = new MapWithCachedArrays();
2455
+ private _args: MapWithCachedArrays<string, ArgumentDefinition<FieldDefinition<TParent>>> | undefined;
2438
2456
  private _extension?: Extension<TParent>;
2439
2457
 
2440
2458
  constructor(name: string, readonly isBuiltIn: boolean = false) {
@@ -2451,15 +2469,15 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
2451
2469
  }
2452
2470
 
2453
2471
  hasArguments(): boolean {
2454
- return this._args.size > 0;
2472
+ return !!this._args && this._args.size > 0;
2455
2473
  }
2456
2474
 
2457
2475
  arguments(): readonly ArgumentDefinition<FieldDefinition<TParent>>[] {
2458
- return this._args.values();
2476
+ return this._args?.values() ?? [];
2459
2477
  }
2460
2478
 
2461
2479
  argument(name: string): ArgumentDefinition<FieldDefinition<TParent>> | undefined {
2462
- return this._args.get(name);
2480
+ return this._args?.get(name);
2463
2481
  }
2464
2482
 
2465
2483
  addArgument(arg: ArgumentDefinition<FieldDefinition<TParent>>): ArgumentDefinition<FieldDefinition<TParent>>;
@@ -2489,6 +2507,9 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
2489
2507
  if (type && !isInputType(type)) {
2490
2508
  throw ERRORS.INVALID_GRAPHQL.err(`Invalid output type ${type} for argument ${toAdd.name} of ${this}: arguments should be input types.`);
2491
2509
  }
2510
+ if (!this._args) {
2511
+ this._args = new MapWithCachedArrays();
2512
+ }
2492
2513
  this._args.set(toAdd.name, toAdd);
2493
2514
  Element.prototype['setParent'].call(toAdd, this);
2494
2515
  if (typeof nameOrArg === 'string') {
@@ -2508,10 +2529,8 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
2508
2529
 
2509
2530
  setOfExtension(extension: Extension<TParent> | undefined) {
2510
2531
  this.checkUpdate();
2511
- // It seems typescript "expand" `TParent` below into `ObjectType | Interface`, so it essentially lose the context that
2512
- // the `TParent` in `Extension<TParent>` will always match. Hence the `as any`.
2513
2532
  assert(
2514
- !extension || this._parent?.extensions().has(extension as any),
2533
+ !extension || this._parent?.hasExtension(extension),
2515
2534
  () => `Cannot mark field ${this.name} as part of the provided extension: it is not an extension of field parent type ${this.parent}`
2516
2535
  );
2517
2536
  this._extension = extension;
@@ -2527,7 +2546,9 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
2527
2546
  }
2528
2547
 
2529
2548
  private removeArgumentInternal(name: string) {
2530
- this._args.delete(name);
2549
+ if (this._args) {
2550
+ this._args.delete(name);
2551
+ }
2531
2552
  }
2532
2553
 
2533
2554
  // Only called through the prototype from FieldBasedType.removeInnerElements because we don't want to expose it.
@@ -2589,9 +2610,9 @@ export class FieldDefinition<TParent extends CompositeType> extends NamedSchemaE
2589
2610
  }
2590
2611
 
2591
2612
  toString(): string {
2592
- const args = this._args.size == 0
2593
- ? ""
2594
- : '(' + this.arguments().map(arg => arg.toString()).join(', ') + ')';
2613
+ const args = this.hasArguments()
2614
+ ? '(' + this.arguments().map(arg => arg.toString()).join(', ') + ')'
2615
+ : "";
2595
2616
  return `${this.name}${args}: ${this.type}`;
2596
2617
  }
2597
2618
  }
@@ -2620,10 +2641,8 @@ export class InputFieldDefinition extends NamedSchemaElementWithType<InputType,
2620
2641
 
2621
2642
  setOfExtension(extension: Extension<InputObjectType> | undefined) {
2622
2643
  this.checkUpdate();
2623
- // It seems typescript "expand" `TParent` below into `ObjectType | Interface`, so it essentially lose the context that
2624
- // the `TParent` in `Extension<TParent>` will always match. Hence the `as any`.
2625
2644
  assert(
2626
- !extension || this._parent?.extensions().has(extension as any),
2645
+ !extension || this._parent?.hasExtension(extension),
2627
2646
  () => `Cannot mark field ${this.name} as part of the provided extension: it is not an extension of field parent type ${this.parent}`,
2628
2647
  );
2629
2648
  this._extension = extension;
@@ -2773,7 +2792,7 @@ export class EnumValue extends NamedSchemaElement<EnumValue, EnumType, never> {
2773
2792
  setOfExtension(extension: Extension<EnumType> | undefined) {
2774
2793
  this.checkUpdate();
2775
2794
  assert(
2776
- !extension || this._parent?.extensions().has(extension as any),
2795
+ !extension || this._parent?.hasExtension(extension),
2777
2796
  () => `Cannot mark field ${this.name} as part of the provided extension: it is not an extension of enum value parent type ${this.parent}`,
2778
2797
  );
2779
2798
  this._extension = extension;
@@ -2830,10 +2849,10 @@ export class EnumValue extends NamedSchemaElement<EnumValue, EnumType, never> {
2830
2849
  export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} = {[key: string]: any}> extends NamedSchemaElement<DirectiveDefinition<TApplicationArgs>, Schema, Directive> {
2831
2850
  readonly kind = 'DirectiveDefinition' as const;
2832
2851
 
2833
- private readonly _args: MapWithCachedArrays<string, ArgumentDefinition<DirectiveDefinition>> = new MapWithCachedArrays();
2852
+ private _args?: MapWithCachedArrays<string, ArgumentDefinition<DirectiveDefinition>>;
2834
2853
  repeatable: boolean = false;
2835
2854
  private readonly _locations: DirectiveLocation[] = [];
2836
- private readonly _referencers: Set<Directive<SchemaElement<any, any>, TApplicationArgs>> = new Set();
2855
+ private _referencers?: Directive<SchemaElement<any, any>, TApplicationArgs>[];
2837
2856
 
2838
2857
  constructor(name: string, readonly isBuiltIn: boolean = false) {
2839
2858
  super(name);
@@ -2844,11 +2863,11 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2844
2863
  }
2845
2864
 
2846
2865
  arguments(): readonly ArgumentDefinition<DirectiveDefinition>[] {
2847
- return this._args.values();
2866
+ return this._args?.values() ?? [];
2848
2867
  }
2849
2868
 
2850
2869
  argument(name: string): ArgumentDefinition<DirectiveDefinition> | undefined {
2851
- return this._args.get(name);
2870
+ return this._args?.get(name);
2852
2871
  }
2853
2872
 
2854
2873
  addArgument(arg: ArgumentDefinition<DirectiveDefinition>): ArgumentDefinition<DirectiveDefinition>;
@@ -2866,6 +2885,9 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2866
2885
  if (this.argument(toAdd.name)) {
2867
2886
  throw ERRORS.INVALID_GRAPHQL.err(`Argument ${toAdd.name} already exists on field ${this.name}`);
2868
2887
  }
2888
+ if (!this._args) {
2889
+ this._args = new MapWithCachedArrays();
2890
+ }
2869
2891
  this._args.set(toAdd.name, toAdd);
2870
2892
  Element.prototype['setParent'].call(toAdd, this);
2871
2893
  if (typeof nameOrArg === 'string') {
@@ -2876,7 +2898,7 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2876
2898
  }
2877
2899
 
2878
2900
  private removeArgumentInternal(name: string) {
2879
- this._args.delete(name);
2901
+ this._args?.delete(name);
2880
2902
  }
2881
2903
 
2882
2904
  get locations(): readonly DirectiveLocation[] {
@@ -2918,11 +2940,7 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2918
2940
  removeLocations(...locations: DirectiveLocation[]): DirectiveDefinition {
2919
2941
  let modified = false;
2920
2942
  for (const location of locations) {
2921
- const index = this._locations.indexOf(location);
2922
- if (index >= 0) {
2923
- this._locations.splice(index, 1);
2924
- modified = true;
2925
- }
2943
+ modified ||= removeArrayElement(location, this._locations);
2926
2944
  }
2927
2945
  if (modified) {
2928
2946
  this.onModification();
@@ -2939,16 +2957,24 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2939
2957
  }
2940
2958
 
2941
2959
  applications(): readonly Directive<SchemaElement<any, any>, TApplicationArgs>[] {
2942
- return setValues(this._referencers);
2960
+ return this._referencers ?? [];
2943
2961
  }
2944
2962
 
2945
2963
  private addReferencer(referencer: Directive<SchemaElement<any, any>, TApplicationArgs>) {
2946
2964
  assert(referencer, 'Referencer should exists');
2947
- this._referencers.add(referencer);
2965
+ if (this._referencers) {
2966
+ if (!this._referencers.includes(referencer)) {
2967
+ this._referencers.push(referencer);
2968
+ }
2969
+ } else {
2970
+ this._referencers = [ referencer ];
2971
+ }
2948
2972
  }
2949
2973
 
2950
2974
  private removeReferencer(referencer: Directive<SchemaElement<any, any>, TApplicationArgs>) {
2951
- this._referencers.delete(referencer);
2975
+ if (this._referencers) {
2976
+ removeArrayElement(referencer, this._referencers);
2977
+ }
2952
2978
  }
2953
2979
 
2954
2980
  protected removeTypeReference(type: NamedType) {
@@ -2969,7 +2995,7 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2969
2995
  this.onModification();
2970
2996
  // Remove this directive definition's children.
2971
2997
  this.sourceAST = undefined;
2972
- assert(this._appliedDirectives.length === 0, "Directive definition should not have directive applied to it");
2998
+ assert(!this._appliedDirectives || this._appliedDirectives.length === 0, "Directive definition should not have directive applied to it");
2973
2999
  for (const arg of this.arguments()) {
2974
3000
  arg.remove();
2975
3001
  }
@@ -2979,8 +3005,8 @@ export class DirectiveDefinition<TApplicationArgs extends {[key: string]: any} =
2979
3005
  // doesn't store a link to that definition. Instead, we fetch the definition
2980
3006
  // from the schema when requested. So we don't have to do anything on the
2981
3007
  // referencers other than clear them (and return the pre-cleared set).
2982
- const toReturn = setValues(this._referencers);
2983
- this._referencers.clear();
3008
+ const toReturn = this._referencers ?? [];
3009
+ this._referencers = undefined;
2984
3010
  // Remove this directive definition from its parent schema.
2985
3011
  Schema.prototype['removeDirectiveInternal'].call(this._parent, this);
2986
3012
  this._parent = undefined;
@@ -3093,7 +3119,7 @@ export class Directive<
3093
3119
  parent instanceof SchemaDefinition || parent instanceof BaseNamedType,
3094
3120
  'Can only mark directive parts of extensions when directly apply to type or schema definition.'
3095
3121
  );
3096
- assert(parent.extensions().has(extension), () => `Cannot mark directive ${this.name} as part of the provided extension: it is not an extension of parent ${parent}`);
3122
+ assert(parent.hasExtension(extension), () => `Cannot mark directive ${this.name} as part of the provided extension: it is not an extension of parent ${parent}`);
3097
3123
  }
3098
3124
  this._extension = extension;
3099
3125
  this.onModification();
@@ -3157,9 +3183,8 @@ export class Directive<
3157
3183
  }
3158
3184
  // Remove this directive application from its parent schema element.
3159
3185
  const parentDirectives = this._parent.appliedDirectives as Directive<TParent>[];
3160
- const index = parentDirectives.indexOf(this);
3161
- assert(index >= 0, () => `Directive ${this} lists ${this._parent} as parent, but that parent doesn't list it as applied directive`);
3162
- parentDirectives.splice(index, 1);
3186
+ const removed = removeArrayElement(this, parentDirectives);
3187
+ assert(removed, () => `Directive ${this} lists ${this._parent} as parent, but that parent doesn't list it as applied directive`);
3163
3188
  this._parent = undefined;
3164
3189
  this._extension = undefined;
3165
3190
  return true;
@@ -3179,7 +3204,7 @@ export function sameDirectiveApplication(application1: Directive<any, any>, appl
3179
3204
  /**
3180
3205
  * Checks whether the 2 provided "set" of directive applications are the same (same applications, regardless or order).
3181
3206
  */
3182
- export function sameDirectiveApplications(applications1: Directive<any, any>[], applications2: Directive<any, any>[]): boolean {
3207
+ export function sameDirectiveApplications(applications1: readonly Directive<any, any>[], applications2: readonly Directive<any, any>[]): boolean {
3183
3208
  if (applications1.length !== applications2.length) {
3184
3209
  return false;
3185
3210
  }
@@ -3197,7 +3222,7 @@ export function sameDirectiveApplications(applications1: Directive<any, any>[],
3197
3222
  *
3198
3223
  * Sub-set here means that all of the applications in `maybeSubset` appears in `applications`.
3199
3224
  */
3200
- export function isDirectiveApplicationsSubset(applications: Directive<any, any>[], maybeSubset: Directive<any, any>[]): boolean {
3225
+ export function isDirectiveApplicationsSubset(applications: readonly Directive<any, any>[], maybeSubset: readonly Directive<any, any>[]): boolean {
3201
3226
  if (maybeSubset.length > applications.length) {
3202
3227
  return false;
3203
3228
  }
@@ -3213,7 +3238,7 @@ export function isDirectiveApplicationsSubset(applications: Directive<any, any>[
3213
3238
  /**
3214
3239
  * Computes the difference between the set of directives applications `baseApplications` and the `toRemove` one.
3215
3240
  */
3216
- export function directiveApplicationsSubstraction(baseApplications: Directive<any, any>[], toRemove: Directive<any, any>[]): Directive<any, any>[] {
3241
+ export function directiveApplicationsSubstraction(baseApplications: readonly Directive<any, any>[], toRemove: readonly Directive<any, any>[]): Directive<any, any>[] {
3217
3242
  return baseApplications.filter((application) => !toRemove.some((other) => sameDirectiveApplication(application, other)));
3218
3243
  }
3219
3244