@apollo/federation-internals 2.4.1 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/argumentCompositionStrategies.d.ts +34 -0
  3. package/dist/argumentCompositionStrategies.d.ts.map +1 -0
  4. package/dist/argumentCompositionStrategies.js +35 -0
  5. package/dist/argumentCompositionStrategies.js.map +1 -0
  6. package/dist/coreSpec.d.ts +12 -3
  7. package/dist/coreSpec.d.ts.map +1 -1
  8. package/dist/coreSpec.js +68 -17
  9. package/dist/coreSpec.js.map +1 -1
  10. package/dist/definitions.d.ts +1 -0
  11. package/dist/definitions.d.ts.map +1 -1
  12. package/dist/definitions.js +30 -27
  13. package/dist/definitions.js.map +1 -1
  14. package/dist/directiveAndTypeSpecification.d.ts +26 -7
  15. package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
  16. package/dist/directiveAndTypeSpecification.js +56 -4
  17. package/dist/directiveAndTypeSpecification.js.map +1 -1
  18. package/dist/federation.d.ts.map +1 -1
  19. package/dist/federation.js +24 -2
  20. package/dist/federation.js.map +1 -1
  21. package/dist/federationSpec.d.ts +2 -13
  22. package/dist/federationSpec.d.ts.map +1 -1
  23. package/dist/federationSpec.js +10 -60
  24. package/dist/federationSpec.js.map +1 -1
  25. package/dist/inaccessibleSpec.d.ts +0 -2
  26. package/dist/inaccessibleSpec.d.ts.map +1 -1
  27. package/dist/inaccessibleSpec.js +3 -6
  28. package/dist/inaccessibleSpec.js.map +1 -1
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -0
  32. package/dist/index.js.map +1 -1
  33. package/dist/knownCoreFeatures.d.ts +1 -0
  34. package/dist/knownCoreFeatures.d.ts.map +1 -1
  35. package/dist/knownCoreFeatures.js +5 -1
  36. package/dist/knownCoreFeatures.js.map +1 -1
  37. package/dist/operations.d.ts.map +1 -1
  38. package/dist/operations.js +10 -2
  39. package/dist/operations.js.map +1 -1
  40. package/dist/print.d.ts +7 -1
  41. package/dist/print.d.ts.map +1 -1
  42. package/dist/print.js +33 -5
  43. package/dist/print.js.map +1 -1
  44. package/dist/tagSpec.d.ts +0 -2
  45. package/dist/tagSpec.d.ts.map +1 -1
  46. package/dist/tagSpec.js +4 -10
  47. package/dist/tagSpec.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/__tests__/directiveAndTypeSpecifications.test.ts +41 -0
  50. package/src/argumentCompositionStrategies.ts +39 -0
  51. package/src/coreSpec.ts +94 -34
  52. package/src/definitions.ts +35 -29
  53. package/src/directiveAndTypeSpecification.ts +101 -14
  54. package/src/federation.ts +33 -4
  55. package/src/federationSpec.ts +13 -73
  56. package/src/inaccessibleSpec.ts +4 -11
  57. package/src/index.ts +3 -0
  58. package/src/knownCoreFeatures.ts +9 -0
  59. package/src/operations.ts +14 -4
  60. package/src/print.ts +39 -4
  61. package/src/tagSpec.ts +4 -12
  62. package/tsconfig.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tagSpec.js","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tagSpec.js","sourceRoot":"","sources":["../src/tagSpec.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apollo/federation-internals",
3
- "version": "2.4.1",
3
+ "version": "2.4.2",
4
4
  "description": "Apollo Federation internal utilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,41 @@
1
+ import { DirectiveLocation } from "graphql";
2
+ import "../definitions";
3
+ import { createDirectiveSpecification } from "../directiveAndTypeSpecification";
4
+ import { ARGUMENT_COMPOSITION_STRATEGIES } from "../argumentCompositionStrategies";
5
+ import { TAG_VERSIONS } from "../tagSpec";
6
+
7
+ const supergraphSpecification = () => TAG_VERSIONS.latest();
8
+
9
+ test('must have supergraph link if composed', () => {
10
+ expect(() => createDirectiveSpecification({
11
+ name: 'foo',
12
+ locations: [DirectiveLocation.OBJECT],
13
+ composes: true,
14
+ })).toThrow('Should provide a @link specification to use in supergraph for directive @foo if it composes');
15
+ });
16
+
17
+ test('must have a merge strategy on all arguments if any', () => {
18
+ expect(() => createDirectiveSpecification({
19
+ name: 'foo',
20
+ locations: [DirectiveLocation.OBJECT],
21
+ composes: true,
22
+ supergraphSpecification,
23
+ args: [
24
+ { name: "v1", type: (schema) => schema.intType(), compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX },
25
+ { name: "v2", type: (schema) => schema.intType() }
26
+ ],
27
+ })).toThrow('Invalid directive specification for @foo: not all arguments define a composition strategy');
28
+ });
29
+
30
+ test('must be not be repeatable if it has a merge strategy', () => {
31
+ expect(() => createDirectiveSpecification({
32
+ name: 'foo',
33
+ locations: [DirectiveLocation.OBJECT],
34
+ composes: true,
35
+ repeatable: true,
36
+ supergraphSpecification,
37
+ args: [
38
+ { name: "v", type: (schema) => schema.intType(), compositionStrategy: ARGUMENT_COMPOSITION_STRATEGIES.MAX },
39
+ ],
40
+ })).toThrow('Invalid directive specification for @foo: @foo is repeatable and should not define composition strategy for its arguments');
41
+ });
@@ -0,0 +1,39 @@
1
+ import { InputType, ListType, NonNullType, Schema } from "./definitions"
2
+
3
+ export type ArgumentCompositionStrategy = {
4
+ name: string,
5
+ supportedTypes: (schema: Schema) => InputType[],
6
+ mergeValues: (values: any[]) => any,
7
+ }
8
+
9
+ export const ARGUMENT_COMPOSITION_STRATEGIES = {
10
+ MAX: {
11
+ name: 'MAX',
12
+ supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
13
+ mergeValues: (values: any[]) => Math.max(...values),
14
+ },
15
+ MIN: {
16
+ name: 'MIN',
17
+ supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
18
+ mergeValues: (values: any[]) => Math.min(...values),
19
+ },
20
+ SUM: {
21
+ name: 'SUM',
22
+ supportedTypes: (schema: Schema) => [new NonNullType(schema.intType())],
23
+ mergeValues: (values: any[]) => values.reduce((acc, val) => acc + val, 0),
24
+ },
25
+ INTERSECTION: {
26
+ name: 'INTERSECTION',
27
+ supportedTypes: (schema: Schema) => schema.builtInScalarTypes().map((t) => new NonNullType(new ListType(new NonNullType(t)))),
28
+ mergeValues: (values: any[]) => values.reduce((acc, val) => acc.filter((v: any) => val.includes(v)), values[0]),
29
+ },
30
+ UNION: {
31
+ name: 'UNION',
32
+ supportedTypes: (schema: Schema) => schema.builtInScalarTypes().map((t) => new NonNullType(new ListType(new NonNullType(t)))),
33
+ mergeValues: (values: any[]) =>
34
+ values.reduce((acc, val) => {
35
+ const newValues = val.filter((v: any) => !acc.includes(v));
36
+ return acc.concat(newValues);
37
+ }, []),
38
+ },
39
+ }
package/src/coreSpec.ts CHANGED
@@ -2,12 +2,12 @@ import { ASTNode, DirectiveLocation, GraphQLError, StringValueNode } from "graph
2
2
  import { URL } from "url";
3
3
  import { CoreFeature, Directive, DirectiveDefinition, EnumType, ErrGraphQLAPISchemaValidationFailed, ErrGraphQLValidationFailed, InputType, ListType, NamedType, NonNullType, ScalarType, Schema, SchemaDefinition, SchemaElement, sourceASTs } from "./definitions";
4
4
  import { sameType } from "./types";
5
- import { assert, firstOf } from './utils';
5
+ import { assert, firstOf, MapWithCachedArrays } from './utils';
6
6
  import { aggregateError, ERRORS } from "./error";
7
7
  import { valueToString } from "./values";
8
8
  import { coreFeatureDefinitionIfKnown, registerKnownFeature } from "./knownCoreFeatures";
9
9
  import { didYouMean, suggestionList } from "./suggestions";
10
- import { ArgumentSpecification, createDirectiveSpecification, createEnumTypeSpecification, createScalarTypeSpecification, DirectiveSpecification, TypeSpecification } from "./directiveAndTypeSpecification";
10
+ import { ArgumentSpecification, createDirectiveSpecification, createEnumTypeSpecification, createScalarTypeSpecification, DirectiveCompositionSpecification, DirectiveSpecification, TypeSpecification } from "./directiveAndTypeSpecification";
11
11
 
12
12
  export const coreIdentity = 'https://specs.apollo.dev/core';
13
13
  export const linkIdentity = 'https://specs.apollo.dev/link';
@@ -38,10 +38,37 @@ function purposesDescription(purpose: CorePurpose) {
38
38
  export abstract class FeatureDefinition {
39
39
  readonly url: FeatureUrl;
40
40
 
41
+ private readonly _directiveSpecs = new MapWithCachedArrays<string, DirectiveSpecification>();
42
+ private readonly _typeSpecs = new MapWithCachedArrays<string, TypeSpecification>();
43
+
41
44
  constructor(url: FeatureUrl | string) {
42
45
  this.url = typeof url === 'string' ? FeatureUrl.parse(url) : url;
43
46
  }
44
47
 
48
+ protected registerDirective(spec: DirectiveSpecification) {
49
+ this._directiveSpecs.set(spec.name, spec);
50
+ }
51
+
52
+ protected registerType(spec: TypeSpecification) {
53
+ this._typeSpecs.set(spec.name, spec);
54
+ }
55
+
56
+ directiveSpecs(): readonly DirectiveSpecification[] {
57
+ return this._directiveSpecs.values();
58
+ }
59
+
60
+ directiveSpec(name: string): DirectiveSpecification | undefined {
61
+ return this._directiveSpecs.get(name);
62
+ }
63
+
64
+ typeSpecs(): readonly TypeSpecification[] {
65
+ return this._typeSpecs.values();
66
+ }
67
+
68
+ typeSpec(name: string): TypeSpecification | undefined {
69
+ return this._typeSpecs.get(name);
70
+ }
71
+
45
72
  get identity(): string {
46
73
  return this.url.identity;
47
74
  }
@@ -60,9 +87,25 @@ export abstract class FeatureDefinition {
60
87
  return nameInSchema != undefined && (directive.name === nameInSchema || directive.name.startsWith(`${nameInSchema}__`));
61
88
  }
62
89
 
63
- abstract addElementsToSchema(schema: Schema): GraphQLError[];
90
+ addElementsToSchema(schema: Schema): GraphQLError[] {
91
+ const feature = this.featureInSchema(schema);
92
+ assert(feature, 'The federation specification should have been added to the schema before this is called');
64
93
 
65
- abstract allElementNames(): string[];
94
+ let errors: GraphQLError[] = [];
95
+ for (const type of this.typeSpecs()) {
96
+ errors = errors.concat(this.addTypeSpec(schema, type));
97
+ }
98
+
99
+ for (const directive of this.directiveSpecs()) {
100
+ errors = errors.concat(this.addDirectiveSpec(schema, directive));
101
+ }
102
+ return errors;
103
+ }
104
+
105
+ allElementNames(): string[] {
106
+ return this.directiveSpecs().map((spec) => `@${spec.name}`)
107
+ .concat(this.typeSpecs().map((spec) => spec.name));
108
+ }
66
109
 
67
110
  protected nameInSchema(schema: Schema): string | undefined {
68
111
  const feature = this.featureInSchema(schema);
@@ -79,12 +122,12 @@ export abstract class FeatureDefinition {
79
122
  return feature ? feature.typeNameInSchema(typeName) : undefined;
80
123
  }
81
124
 
82
- protected rootDirective<TApplicationArgs extends {[key: string]: any}>(schema: Schema): DirectiveDefinition<TApplicationArgs> | undefined {
125
+ protected rootDirective<TApplicationArgs extends { [key: string]: any }>(schema: Schema): DirectiveDefinition<TApplicationArgs> | undefined {
83
126
  const name = this.nameInSchema(schema);
84
127
  return name ? schema.directive(name) as DirectiveDefinition<TApplicationArgs> | undefined : undefined;
85
128
  }
86
129
 
87
- protected directive<TApplicationArgs extends {[key: string]: any}>(schema: Schema, elementName: string): DirectiveDefinition<TApplicationArgs> | undefined {
130
+ protected directive<TApplicationArgs extends { [key: string]: any }>(schema: Schema, elementName: string): DirectiveDefinition<TApplicationArgs> | undefined {
88
131
  const name = this.directiveNameInSchema(schema, elementName);
89
132
  return name ? schema.directive(name) as DirectiveDefinition<TApplicationArgs> | undefined : undefined;
90
133
  }
@@ -130,11 +173,17 @@ export abstract class FeatureDefinition {
130
173
  return undefined;
131
174
  }
132
175
 
176
+ compositionSpecification(directiveNameInFeature: string): DirectiveCompositionSpecification | undefined {
177
+ const spec = this._directiveSpecs.get(directiveNameInFeature);
178
+ return spec?.composition;
179
+ }
180
+
133
181
  toString(): string {
134
182
  return `${this.identity}/${this.version}`
135
183
  }
136
184
  }
137
185
 
186
+
138
187
  export type CoreDirectiveArgs = {
139
188
  url: undefined,
140
189
  feature: string,
@@ -289,7 +338,7 @@ export function isCoreSpecDirectiveApplication(directive: Directive<SchemaDefini
289
338
  if (url.identity === coreIdentity) {
290
339
  return directive.name === (args.as ?? 'core');
291
340
  } else {
292
- return url.identity === linkIdentity && directive.name === (args.as ?? linkDirectiveDefaultName);
341
+ return url.identity === linkIdentity && directive.name === (args.as ?? linkDirectiveDefaultName);
293
342
  }
294
343
  } catch (err) {
295
344
  return false;
@@ -307,7 +356,7 @@ function isValidUrlArgumentType(type: InputType, schema: Schema): boolean {
307
356
 
308
357
  const linkPurposeTypeSpec = createEnumTypeSpecification({
309
358
  name: 'Purpose',
310
- values: corePurposes.map((name) => ({ name, description: purposesDescription(name)}))
359
+ values: corePurposes.map((name) => ({ name, description: purposesDescription(name) }))
311
360
  });
312
361
 
313
362
  const linkImportTypeSpec = createScalarTypeSpecification({ name: 'Import' });
@@ -321,32 +370,43 @@ export class CoreSpecDefinition extends FeatureDefinition {
321
370
  name,
322
371
  locations: [DirectiveLocation.SCHEMA],
323
372
  repeatable: true,
324
- argumentFct: (schema, nameInSchema) => this.createDefinitionArgumentSpecifications(schema, nameInSchema),
373
+ args: this.createDefinitionArgumentSpecifications(),
325
374
  });
375
+ this.registerDirective(this.directiveDefinitionSpec);
326
376
  }
327
377
 
328
- private createDefinitionArgumentSpecifications(schema: Schema, nameInSchema?: string): { args: ArgumentSpecification[], errors: GraphQLError[] } {
378
+ private createDefinitionArgumentSpecifications(): ArgumentSpecification[] {
329
379
  const args: ArgumentSpecification[] = [
330
- { name: this.urlArgName(), type: schema.stringType() },
331
- { name: 'as', type: schema.stringType() },
380
+ { name: this.urlArgName(), type: (schema) => schema.stringType() },
381
+ { name: 'as', type: (schema) => schema.stringType() },
332
382
  ];
333
383
  if (this.supportPurposes()) {
334
- const purposeName = `${nameInSchema ?? this.url.name}__${linkPurposeTypeSpec.name}`;
335
- const errors = linkPurposeTypeSpec.checkOrAdd(schema, purposeName);
336
- if (errors.length > 0) {
337
- return { args, errors }
338
- }
339
- args.push({ name: 'for', type: schema.type(purposeName) as InputType });
384
+ args.push({
385
+ name: 'for',
386
+ type: (schema, nameInSchema) => {
387
+ const purposeName = `${nameInSchema ?? this.url.name}__${linkPurposeTypeSpec.name}`;
388
+ const errors = linkPurposeTypeSpec.checkOrAdd(schema, purposeName);
389
+ if (errors.length > 0) {
390
+ return errors;
391
+ }
392
+ return schema.type(purposeName) as InputType;
393
+ },
394
+ });
340
395
  }
341
396
  if (this.supportImport()) {
342
- const importName = `${nameInSchema ?? this.url.name}__${linkImportTypeSpec.name}`;
343
- const errors = linkImportTypeSpec.checkOrAdd(schema, importName);
344
- if (errors.length > 0) {
345
- return { args, errors }
346
- }
347
- args.push({ name: 'import', type: new ListType(schema.type(importName)!) });
397
+ args.push({
398
+ name: 'import',
399
+ type: (schema, nameInSchema) => {
400
+ const importName = `${nameInSchema ?? this.url.name}__${linkImportTypeSpec.name}`;
401
+ const errors = linkImportTypeSpec.checkOrAdd(schema, importName);
402
+ if (errors.length > 0) {
403
+ return errors;
404
+ }
405
+ return new ListType(schema.type(importName)!);
406
+ }
407
+ });
348
408
  }
349
- return { args, errors: [] };
409
+ return args;
350
410
  }
351
411
 
352
412
  addElementsToSchema(_: Schema): GraphQLError[] {
@@ -394,7 +454,7 @@ export class CoreSpecDefinition extends FeatureDefinition {
394
454
  //
395
455
  // So instead, we put the directive on the schema definition unless some extensions exists but no
396
456
  // definition does (that is, no non-extension elements are populated).
397
- const schemaDef = schema.schemaDefinition;
457
+ const schemaDef = schema.schemaDefinition;
398
458
  // Side-note: this test must be done _before_ we call `applyDirective`, otherwise it would take it into
399
459
  // account.
400
460
  const hasDefinition = schemaDef.hasNonExtensionElements();
@@ -429,7 +489,7 @@ export class CoreSpecDefinition extends FeatureDefinition {
429
489
  * must start with a `@`.
430
490
  */
431
491
  allElementNames(): string[] {
432
- const names = [ `@${this.url.name}` ];
492
+ const names = [`@${this.url.name}`];
433
493
  if (this.supportPurposes()) {
434
494
  names.push('Purpose');
435
495
  }
@@ -533,7 +593,7 @@ export class FeatureDefinitions<T extends FeatureDefinition = FeatureDefinition>
533
593
  * Versions are a (major, minor) number pair.
534
594
  */
535
595
  export class FeatureVersion {
536
- constructor(public readonly major: number, public readonly minor: number) {}
596
+ constructor(public readonly major: number, public readonly minor: number) { }
537
597
 
538
598
  /**
539
599
  * Parse a version specifier of the form "v(major).(minor)" or throw
@@ -565,8 +625,8 @@ export class FeatureVersion {
565
625
  * ```
566
626
  **/
567
627
  public satisfies(required: FeatureVersion): boolean {
568
- const {major, minor} = this
569
- const {major: rMajor, minor: rMinor} = required
628
+ const { major, minor } = this
629
+ const { major: rMajor, minor: rMinor } = required
570
630
  return rMajor == major && (
571
631
  major == 0
572
632
  ? rMinor == minor
@@ -580,7 +640,7 @@ export class FeatureVersion {
580
640
  * of compatibility, so those will just return the same thing as `this.toString()`.
581
641
  */
582
642
  public get series() {
583
- const {major} = this
643
+ const { major } = this
584
644
  return major > 0 ? `${major}.x` : String(this)
585
645
  }
586
646
 
@@ -650,7 +710,7 @@ export class FeatureUrl {
650
710
  public readonly name: string,
651
711
  public readonly version: FeatureVersion,
652
712
  public readonly element?: string,
653
- ) {}
713
+ ) { }
654
714
 
655
715
  /// Parse a spec URL or throw
656
716
  public static parse(input: string, node?: ASTNode): FeatureUrl {
@@ -668,7 +728,7 @@ export class FeatureUrl {
668
728
  if (!name) {
669
729
  throw ERRORS.INVALID_LINK_IDENTIFIER.err(`Missing feature name component in feature url '${url}'`, { nodes: node })
670
730
  }
671
- const element = url.hash ? url.hash.slice(1): undefined
731
+ const element = url.hash ? url.hash.slice(1) : undefined
672
732
  url.hash = ''
673
733
  url.search = ''
674
734
  url.password = ''
@@ -690,7 +750,7 @@ export class FeatureUrl {
690
750
  */
691
751
  public satisfies(requested: FeatureUrl): boolean {
692
752
  return requested.identity === this.identity &&
693
- this.version.satisfies(requested.version)
753
+ this.version.satisfies(requested.version)
694
754
  }
695
755
 
696
756
  public equals(other: FeatureUrl) {
@@ -1078,16 +1078,7 @@ export class CoreFeatures {
1078
1078
  isImported: false,
1079
1079
  } : undefined;
1080
1080
  } else {
1081
- const directFeature = this.byAlias.get(element.name);
1082
- if (directFeature && isDirective) {
1083
- return {
1084
- feature: directFeature,
1085
- nameInFeature: directFeature.imports.find(imp => imp.as === `@${element.name}`)?.name.slice(1) ?? element.name,
1086
- isImported: true,
1087
- };
1088
- }
1089
-
1090
- // Let's see if it's an import. If not, it's not associated to a declared feature.
1081
+ // Let's first see if it's an import, as this would take precedence over directive implicitely named like their feature.
1091
1082
  const importName = isDirective ? '@' + element.name : element.name;
1092
1083
  const allFeatures = [this.coreItself, ...this.byIdentity.values()];
1093
1084
  for (const feature of allFeatures) {
@@ -1101,6 +1092,17 @@ export class CoreFeatures {
1101
1092
  }
1102
1093
  }
1103
1094
  }
1095
+
1096
+ // Otherwise, this may be the special directive having the same name as its feature.
1097
+ const directFeature = this.byAlias.get(element.name);
1098
+ if (directFeature && isDirective) {
1099
+ return {
1100
+ feature: directFeature,
1101
+ nameInFeature: directFeature.imports.find(imp => imp.as === `@${element.name}`)?.name.slice(1) ?? element.name,
1102
+ isImported: true,
1103
+ };
1104
+ }
1105
+
1104
1106
  return undefined;
1105
1107
  }
1106
1108
  }
@@ -1113,22 +1115,22 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
1113
1115
  createDirectiveSpecification({
1114
1116
  name: 'include',
1115
1117
  locations: [DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
1116
- argumentFct: (schema) => ({ args: [{ name: 'if', type: new NonNullType(schema.booleanType()) }], errors: [] })
1118
+ args: [{ name: 'if', type: (schema) => new NonNullType(schema.booleanType()) }],
1117
1119
  }),
1118
1120
  createDirectiveSpecification({
1119
1121
  name: 'skip',
1120
1122
  locations: [DirectiveLocation.FIELD, DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
1121
- argumentFct: (schema) => ({ args: [{ name: 'if', type: new NonNullType(schema.booleanType()) }], errors: [] })
1123
+ args: [{ name: 'if', type: (schema) => new NonNullType(schema.booleanType()) }],
1122
1124
  }),
1123
1125
  createDirectiveSpecification({
1124
1126
  name: 'deprecated',
1125
1127
  locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE, DirectiveLocation.ARGUMENT_DEFINITION, DirectiveLocation.INPUT_FIELD_DEFINITION],
1126
- argumentFct: (schema) => ({ args: [{ name: 'reason', type: schema.stringType(), defaultValue: 'No longer supported' }], errors: [] })
1128
+ args: [{ name: 'reason', type: (schema) => schema.stringType(), defaultValue: 'No longer supported' }],
1127
1129
  }),
1128
1130
  createDirectiveSpecification({
1129
1131
  name: 'specifiedBy',
1130
1132
  locations: [DirectiveLocation.SCALAR],
1131
- argumentFct: (schema) => ({ args: [{ name: 'url', type: new NonNullType(schema.stringType()) }], errors: [] })
1133
+ args: [{ name: 'url', type: (schema) => new NonNullType(schema.stringType()) }],
1132
1134
  }),
1133
1135
  // Note that @defer and @stream are unconditionally added to `Schema` even if they are technically "optional" built-in. _But_,
1134
1136
  // the `Schema#toGraphQLJSSchema` method has an option to decide if @defer/@stream should be included or not in the resulting
@@ -1136,13 +1138,10 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
1136
1138
  createDirectiveSpecification({
1137
1139
  name: 'defer',
1138
1140
  locations: [DirectiveLocation.FRAGMENT_SPREAD, DirectiveLocation.INLINE_FRAGMENT],
1139
- argumentFct: (schema) => ({
1140
- args: [
1141
- { name: 'label', type: schema.stringType() },
1142
- { name: 'if', type: new NonNullType(schema.booleanType()), defaultValue: true },
1143
- ],
1144
- errors: [],
1145
- })
1141
+ args: [
1142
+ { name: 'label', type: (schema) => schema.stringType() },
1143
+ { name: 'if', type: (schema) => new NonNullType(schema.booleanType()), defaultValue: true },
1144
+ ],
1146
1145
  }),
1147
1146
  // Adding @stream too so that it's know and we don't error out if it is queries. It feels like it would be weird to do so for @stream but not
1148
1147
  // @defer when both are defined in the same spec. That said, that does *not* mean we currently _implement_ @stream, we don't, and so putting
@@ -1150,14 +1149,11 @@ const graphQLBuiltInDirectivesSpecifications: readonly DirectiveSpecification[]
1150
1149
  createDirectiveSpecification({
1151
1150
  name: 'stream',
1152
1151
  locations: [DirectiveLocation.FIELD],
1153
- argumentFct: (schema) => ({
1154
- args: [
1155
- { name: 'label', type: schema.stringType() },
1156
- { name: 'initialCount', type: schema.intType(), defaultValue: 0 },
1157
- { name: 'if', type: new NonNullType(schema.booleanType()), defaultValue: true },
1158
- ],
1159
- errors: [],
1160
- })
1152
+ args: [
1153
+ { name: 'label', type: (schema) => schema.stringType() },
1154
+ { name: 'initialCount', type: (schema) => schema.intType(), defaultValue: 0 },
1155
+ { name: 'if', type: (schema) => new NonNullType(schema.booleanType()), defaultValue: true },
1156
+ ],
1161
1157
  }),
1162
1158
  ];
1163
1159
 
@@ -1457,6 +1453,16 @@ export class Schema {
1457
1453
  return this._builtInTypes.get('ID')! as ScalarType;
1458
1454
  }
1459
1455
 
1456
+ builtInScalarTypes(): ScalarType[] {
1457
+ return [
1458
+ this.intType(),
1459
+ this.floatType(),
1460
+ this.stringType(),
1461
+ this.booleanType(),
1462
+ this.idType(),
1463
+ ];
1464
+ }
1465
+
1460
1466
  addType<T extends NamedType>(type: T): T {
1461
1467
  const existing = this.type(type.name);
1462
1468
  if (existing) {