@apollo/federation-internals 2.0.0-preview.7 → 2.0.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 (109) hide show
  1. package/CHANGELOG.md +32 -3
  2. package/dist/buildSchema.d.ts.map +1 -1
  3. package/dist/buildSchema.js +51 -41
  4. package/dist/buildSchema.js.map +1 -1
  5. package/dist/coreSpec.d.ts +16 -8
  6. package/dist/coreSpec.d.ts.map +1 -1
  7. package/dist/coreSpec.js +205 -53
  8. package/dist/coreSpec.js.map +1 -1
  9. package/dist/definitions.d.ts +28 -11
  10. package/dist/definitions.d.ts.map +1 -1
  11. package/dist/definitions.js +185 -67
  12. package/dist/definitions.js.map +1 -1
  13. package/dist/directiveAndTypeSpecification.d.ts +11 -1
  14. package/dist/directiveAndTypeSpecification.d.ts.map +1 -1
  15. package/dist/directiveAndTypeSpecification.js +77 -20
  16. package/dist/directiveAndTypeSpecification.js.map +1 -1
  17. package/dist/error.d.ts +17 -0
  18. package/dist/error.d.ts.map +1 -1
  19. package/dist/error.js +54 -2
  20. package/dist/error.js.map +1 -1
  21. package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
  22. package/dist/extractSubgraphsFromSupergraph.js +7 -1
  23. package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
  24. package/dist/federation.d.ts +22 -5
  25. package/dist/federation.d.ts.map +1 -1
  26. package/dist/federation.js +143 -86
  27. package/dist/federation.js.map +1 -1
  28. package/dist/federationSpec.d.ts +6 -2
  29. package/dist/federationSpec.d.ts.map +1 -1
  30. package/dist/federationSpec.js +47 -22
  31. package/dist/federationSpec.js.map +1 -1
  32. package/dist/inaccessibleSpec.d.ts +10 -2
  33. package/dist/inaccessibleSpec.d.ts.map +1 -1
  34. package/dist/inaccessibleSpec.js +634 -16
  35. package/dist/inaccessibleSpec.js.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/introspection.d.ts.map +1 -1
  41. package/dist/introspection.js +8 -3
  42. package/dist/introspection.js.map +1 -1
  43. package/dist/joinSpec.d.ts +5 -1
  44. package/dist/joinSpec.d.ts.map +1 -1
  45. package/dist/joinSpec.js +21 -0
  46. package/dist/joinSpec.js.map +1 -1
  47. package/dist/knownCoreFeatures.d.ts +4 -0
  48. package/dist/knownCoreFeatures.d.ts.map +1 -0
  49. package/dist/knownCoreFeatures.js +16 -0
  50. package/dist/knownCoreFeatures.js.map +1 -0
  51. package/dist/operations.d.ts +1 -0
  52. package/dist/operations.d.ts.map +1 -1
  53. package/dist/operations.js +16 -1
  54. package/dist/operations.js.map +1 -1
  55. package/dist/{sharing.d.ts → precompute.d.ts} +1 -1
  56. package/dist/precompute.d.ts.map +1 -0
  57. package/dist/{sharing.js → precompute.js} +3 -3
  58. package/dist/precompute.js.map +1 -0
  59. package/dist/schemaUpgrader.d.ts.map +1 -1
  60. package/dist/schemaUpgrader.js +17 -7
  61. package/dist/schemaUpgrader.js.map +1 -1
  62. package/dist/suggestions.d.ts +1 -1
  63. package/dist/suggestions.d.ts.map +1 -1
  64. package/dist/suggestions.js.map +1 -1
  65. package/dist/supergraphs.d.ts.map +1 -1
  66. package/dist/supergraphs.js +2 -0
  67. package/dist/supergraphs.js.map +1 -1
  68. package/dist/tagSpec.d.ts +7 -2
  69. package/dist/tagSpec.d.ts.map +1 -1
  70. package/dist/tagSpec.js +35 -14
  71. package/dist/tagSpec.js.map +1 -1
  72. package/dist/validate.js +13 -7
  73. package/dist/validate.js.map +1 -1
  74. package/dist/values.d.ts +2 -2
  75. package/dist/values.d.ts.map +1 -1
  76. package/dist/values.js +13 -11
  77. package/dist/values.js.map +1 -1
  78. package/package.json +4 -4
  79. package/src/__tests__/coreSpec.test.ts +212 -0
  80. package/src/__tests__/definitions.test.ts +75 -0
  81. package/src/__tests__/removeInaccessibleElements.test.ts +2229 -137
  82. package/src/__tests__/schemaUpgrader.test.ts +3 -2
  83. package/src/__tests__/subgraphValidation.test.ts +419 -4
  84. package/src/__tests__/values.test.ts +315 -3
  85. package/src/buildSchema.ts +98 -51
  86. package/src/coreSpec.ts +277 -65
  87. package/src/definitions.ts +317 -92
  88. package/src/directiveAndTypeSpecification.ts +98 -21
  89. package/src/error.ts +119 -1
  90. package/src/extractSubgraphsFromSupergraph.ts +7 -1
  91. package/src/federation.ts +184 -102
  92. package/src/federationSpec.ts +56 -24
  93. package/src/inaccessibleSpec.ts +985 -39
  94. package/src/index.ts +2 -0
  95. package/src/introspection.ts +8 -3
  96. package/src/joinSpec.ts +33 -3
  97. package/src/knownCoreFeatures.ts +13 -0
  98. package/src/operations.ts +15 -0
  99. package/src/{sharing.ts → precompute.ts} +3 -6
  100. package/src/schemaUpgrader.ts +29 -13
  101. package/src/suggestions.ts +1 -1
  102. package/src/supergraphs.ts +2 -0
  103. package/src/tagSpec.ts +49 -16
  104. package/src/validate.ts +20 -9
  105. package/src/values.ts +39 -12
  106. package/tsconfig.test.tsbuildinfo +1 -1
  107. package/tsconfig.tsbuildinfo +1 -1
  108. package/dist/sharing.d.ts.map +0 -1
  109. package/dist/sharing.js.map +0 -1
@@ -1,8 +1,11 @@
1
1
  import {
2
2
  Schema,
3
- } from '../../dist/definitions';
4
- import { buildSchema } from '../../dist/buildSchema';
5
- import { parseOperation } from '../../dist/operations';
3
+ } from '../definitions';
4
+ import { buildSchema } from '../buildSchema';
5
+ import { parseOperation } from '../operations';
6
+ import { buildForErrors } from './subgraphValidation.test';
7
+ import gql from 'graphql-tag';
8
+ import { printSchema } from '../print';
6
9
 
7
10
  function parseSchema(schema: string): Schema {
8
11
  try {
@@ -66,3 +69,312 @@ test('handles non-list value for list argument (as singleton)', () => {
66
69
  }
67
70
  `);
68
71
  });
72
+
73
+ describe('default value validation', () => {
74
+ it('errors on invalid default value in field argument', () => {
75
+ const doc = gql`
76
+ type Query {
77
+ f(a: Int = "foo"): Int
78
+ }
79
+ `;
80
+
81
+ expect(buildForErrors(doc)).toStrictEqual([[
82
+ 'INVALID_GRAPHQL',
83
+ '[S] Invalid default value (got: "foo") provided for argument Query.f(a:) of type Int.'
84
+ ]]);
85
+ });
86
+
87
+ it('errors on invalid default value in directive argument', () => {
88
+ const doc = gql`
89
+ type Query {
90
+ f: Int
91
+ }
92
+
93
+ directive @myDirective(a: Int = "foo") on FIELD
94
+ `;
95
+
96
+ expect(buildForErrors(doc)).toStrictEqual([[
97
+ 'INVALID_GRAPHQL',
98
+ '[S] Invalid default value (got: "foo") provided for argument @myDirective(a:) of type Int.'
99
+ ]]);
100
+ });
101
+
102
+ it('errors on invalid default value in input field', () => {
103
+ const doc = gql`
104
+ input I {
105
+ x: Int = "foo"
106
+ }
107
+ `;
108
+
109
+ expect(buildForErrors(doc)).toStrictEqual([[
110
+ 'INVALID_GRAPHQL',
111
+ '[S] Invalid default value (got: "foo") provided for input field I.x of type Int.'
112
+ ]]);
113
+ });
114
+
115
+ it('errors on invalid default value for existing input field', () => {
116
+ const doc = gql`
117
+ type Query {
118
+ f(i: I = { x: 2, y: "3" }): Int
119
+ }
120
+
121
+ input I {
122
+ x: Int
123
+ y: Int
124
+ }
125
+ `;
126
+
127
+ expect(buildForErrors(doc)).toStrictEqual([[
128
+ 'INVALID_GRAPHQL',
129
+ '[S] Invalid default value (got: {x: 2, y: "3"}) provided for argument Query.f(i:) of type I.'
130
+ ]]);
131
+ });
132
+
133
+ it('errors on default value containing unexpected input fields', () => {
134
+ const doc = gql`
135
+ type Query {
136
+ f(i: I = { x: 1, y: 2, z: 3 }): Int
137
+ }
138
+
139
+ input I {
140
+ x: Int
141
+ y: Int
142
+ }
143
+ `;
144
+
145
+ expect(buildForErrors(doc)).toStrictEqual([[
146
+ 'INVALID_GRAPHQL',
147
+ '[S] Invalid default value (got: {x: 1, y: 2, z: 3}) provided for argument Query.f(i:) of type I.'
148
+ ]]);
149
+ });
150
+
151
+ it('errors on default value being unknown enum value', () => {
152
+ const doc = gql`
153
+ type Query {
154
+ f(e: E = THREE): Int
155
+ }
156
+
157
+ enum E {
158
+ ONE
159
+ TWO
160
+ }
161
+ `;
162
+
163
+ // Note that it is slightly imperfect that the error shows the value as a "string" but is the result
164
+ // of enum values being encoded by string value internally (and while, when a value type-check correctly,
165
+ // we can use the type to display enum values properly, this is exactly a case where the value is not
166
+ // correctly type-checked, so we currently don't have a good way to figure out it's an enum when we display
167
+ // it in the error message). We could fix this someday if we change to using a specific class/object for
168
+ // enum values internally (though this might have backward compatbility constraints), but in the meantime,
169
+ // it's unlikely to trip users too much.
170
+ expect(buildForErrors(doc)).toStrictEqual([[
171
+ 'INVALID_GRAPHQL',
172
+ '[S] Invalid default value (got: "THREE") provided for argument Query.f(e:) of type E.'
173
+ ]]);
174
+ });
175
+
176
+ it('errors on default value being unknown enum value (as string)', () => {
177
+ const doc = gql`
178
+ type Query {
179
+ f(e: E = "TWOO"): Int
180
+ }
181
+
182
+ enum E {
183
+ ONE
184
+ TWO
185
+ }
186
+ `;
187
+
188
+ expect(buildForErrors(doc)).toStrictEqual([[
189
+ 'INVALID_GRAPHQL',
190
+ '[S] Invalid default value (got: "TWOO") provided for argument Query.f(e:) of type E.'
191
+ ]]);
192
+ });
193
+
194
+ it('accepts default value enum value as string, if a valid enum value', () => {
195
+ // Please note that this test show we accept strings for enum even though the GraphQL spec kind
196
+ // of say we shouldn't. But the graphQL spec also doesn't really do default value validation,
197
+ // which be believe is just wrong, and as a consequence we've seen customer schema with string-for-enum
198
+ // in default values, and it doesn't sound very harmfull to allow it (the spec even admits that some
199
+ // transport may have to deal with enums as string anyway), so we prefer having that allowance in
200
+ // federation (if this ever become a huge issue for some users, we could imagine to add a "strict"
201
+ // more that start refusing this).
202
+ const doc = gql`
203
+ type Query {
204
+ f(e: E = "TWO"): Int
205
+ }
206
+
207
+ enum E {
208
+ ONE
209
+ TWO
210
+ }
211
+ `;
212
+
213
+ expect(buildForErrors(doc)).toBeUndefined();
214
+ });
215
+
216
+ it('accepts any value for a custom scalar in field agument', () => {
217
+ const doc = gql`
218
+ type Query {
219
+ f(i: Scalar = { x: 2, y: "3" }): Int
220
+ }
221
+
222
+ scalar Scalar
223
+ `;
224
+
225
+ expect(buildForErrors(doc)).toBeUndefined();
226
+ });
227
+
228
+ it('accepts any value for a custom scalar in directive agument', () => {
229
+ const doc = gql`
230
+ type Query {
231
+ f: Int
232
+ }
233
+
234
+ directive @myDirective(i: Scalar = { x: 2, y: "3" }) on FIELD
235
+ scalar Scalar
236
+ `;
237
+
238
+ expect(buildForErrors(doc)).toBeUndefined();
239
+ });
240
+
241
+ it('accepts any value for a custom scalar in an input field', () => {
242
+ const doc = gql`
243
+ input I {
244
+ x: Scalar = { z: { a: 4} }
245
+ }
246
+
247
+ scalar Scalar
248
+ `;
249
+
250
+ expect(buildForErrors(doc)).toBeUndefined();
251
+ });
252
+
253
+ it('accepts default value coercible to list for a list type', () => {
254
+ const doc = gql`
255
+ type Query {
256
+ f(x: [String] = "foo"): Int
257
+ }
258
+ `;
259
+
260
+ expect(buildForErrors(doc)).toBeUndefined();
261
+ });
262
+
263
+ it('accepts default value coercible to list for a list type through multiple coercions', () => {
264
+ const doc = gql`
265
+ type Query {
266
+ f(x: [[[String]!]]! = "foo"): Int
267
+ }
268
+ `;
269
+
270
+ expect(buildForErrors(doc)).toBeUndefined();
271
+ });
272
+
273
+ it('errors on default value no coercible to list for a list type through multiple coercions', () => {
274
+ const doc = gql`
275
+ type Query {
276
+ f(x: [[[String]!]]! = 2): Int
277
+ }
278
+ `;
279
+
280
+ expect(buildForErrors(doc)).toStrictEqual([[
281
+ 'INVALID_GRAPHQL',
282
+ '[S] Invalid default value (got: 2) provided for argument Query.f(x:) of type [[[String]!]]!.'
283
+ ]]);
284
+ });
285
+
286
+ it('accepts default value coercible to its type but needing multiple/nested coercions', () => {
287
+ const doc = gql`
288
+ type Query {
289
+ f(x: I = { j: {x: 1, z: "Foo"} }): Int
290
+ }
291
+
292
+ input I {
293
+ j: [J]
294
+ }
295
+
296
+ input J {
297
+ x: ID
298
+ y: ID
299
+ z: ID
300
+ }
301
+ `;
302
+
303
+ expect(buildForErrors(doc)).toBeUndefined();
304
+ });
305
+
306
+ it('accepts default values that, if actually coerced, woudl result in infinite loops', () => {
307
+ // This example is stolen from this comment: https://github.com/graphql/graphql-spec/pull/793#issuecomment-738736539
308
+ // It essentially show that while, as the other tests of this file show, we 1) validate default value against
309
+ // their type and 2) ensures default values coercible to said type don't fail such validation, we also do
310
+ // _not_ do the actual coercion of those values, which in this example would lead to an infinite loop.
311
+ const doc = gql`
312
+ input A {
313
+ b: B = {}
314
+ }
315
+
316
+ input B {
317
+ a: A = {}
318
+ }
319
+
320
+ type Query {
321
+ q(a: A = {}): Int
322
+ }
323
+ `;
324
+
325
+ expect(buildForErrors(doc)).toBeUndefined();
326
+ });
327
+
328
+ it('errors on null default value for non-nullable input', () => {
329
+ const doc = gql`
330
+ type Query {
331
+ f(i: Int! = null): Int
332
+ }
333
+ `;
334
+
335
+ expect(buildForErrors(doc)).toStrictEqual([[
336
+ 'INVALID_GRAPHQL',
337
+ '[S] Invalid default value (got: null) provided for argument Query.f(i:) of type Int!.'
338
+ ]]);
339
+ });
340
+
341
+ it('Accepts null default value for nullable input', () => {
342
+ const doc = gql`
343
+ type Query {
344
+ f(i: Int = null): Int
345
+ }
346
+ `;
347
+
348
+ expect(buildForErrors(doc)).toBeUndefined();
349
+ });
350
+ });
351
+
352
+ describe('values printing', () => {
353
+ it('prints enums value correctly within multiple lists', () => {
354
+ const sdl = `
355
+ type Query {
356
+ f(a: [[[E]!]!] = [[[FOO], [BAR]]]): Int
357
+ }
358
+
359
+ enum E {
360
+ FOO
361
+ BAR
362
+ }
363
+ `
364
+ expect(printSchema(parseSchema(sdl))).toMatchString(sdl);
365
+ })
366
+
367
+ it('prints enums value when its coercible to list through multiple coercions', () => {
368
+ const sdl = `
369
+ type Query {
370
+ f(a: [[[E]!]!] = FOO): Int
371
+ }
372
+
373
+ enum E {
374
+ FOO
375
+ BAR
376
+ }
377
+ `
378
+ expect(printSchema(parseSchema(sdl))).toMatchString(sdl);
379
+ })
380
+ });
@@ -46,7 +46,9 @@ import {
46
46
  UnionType,
47
47
  InputObjectType,
48
48
  EnumType,
49
- Extension
49
+ Extension,
50
+ ErrGraphQLValidationFailed,
51
+ errorCauses,
50
52
  } from "./definitions";
51
53
 
52
54
  function buildValue(value?: ValueNode): any {
@@ -66,6 +68,7 @@ export function buildSchemaFromAST(
66
68
  documentNode: DocumentNode,
67
69
  options?: BuildSchemaOptions,
68
70
  ): Schema {
71
+ const errors: GraphQLError[] = [];
69
72
  const schema = new Schema(options?.blueprint);
70
73
  // We do a first pass to add all empty types and directives definition. This ensure any reference on one of
71
74
  // those can be resolved in the 2nd pass, regardless of the order of the definitions in the AST.
@@ -77,29 +80,30 @@ export function buildSchemaFromAST(
77
80
  // populated (and at this point, we don't really know the name of the `@core` directive since it can be renamed, so
78
81
  // we just handle all directives).
79
82
  for (const directiveDefinitionNode of directiveDefinitions) {
80
- buildDirectiveDefinitionInner(directiveDefinitionNode, schema.directive(directiveDefinitionNode.name.value)!);
83
+ buildDirectiveDefinitionInner(directiveDefinitionNode, schema.directive(directiveDefinitionNode.name.value)!, errors);
81
84
  }
82
85
  for (const schemaDefinition of schemaDefinitions) {
83
- buildSchemaDefinitionInner(schemaDefinition, schema.schemaDefinition);
86
+ buildSchemaDefinitionInner(schemaDefinition, schema.schemaDefinition, errors);
84
87
  }
85
88
  for (const schemaExtension of schemaExtensions) {
86
- buildSchemaDefinitionInner(schemaExtension, schema.schemaDefinition, schema.schemaDefinition.newExtension());
89
+ buildSchemaDefinitionInner(schemaExtension, schema.schemaDefinition, errors, schema.schemaDefinition.newExtension());
87
90
  }
88
91
 
89
- schema.blueprint.onDirectiveDefinitionAndSchemaParsed(schema);
92
+ errors.push(...schema.blueprint.onDirectiveDefinitionAndSchemaParsed(schema));
90
93
 
91
94
  for (const definitionNode of documentNode.definitions) {
92
95
  switch (definitionNode.kind) {
93
96
  case 'OperationDefinition':
94
97
  case 'FragmentDefinition':
95
- throw new GraphQLError("Invalid executable definition found while building schema", definitionNode);
98
+ errors.push(new GraphQLError("Invalid executable definition found while building schema", definitionNode));
99
+ continue;
96
100
  case 'ScalarTypeDefinition':
97
101
  case 'ObjectTypeDefinition':
98
102
  case 'InterfaceTypeDefinition':
99
103
  case 'UnionTypeDefinition':
100
104
  case 'EnumTypeDefinition':
101
105
  case 'InputObjectTypeDefinition':
102
- buildNamedTypeInner(definitionNode, schema.type(definitionNode.name.value)!, schema.blueprint);
106
+ buildNamedTypeInner(definitionNode, schema.type(definitionNode.name.value)!, schema.blueprint, errors);
103
107
  break;
104
108
  case 'ScalarTypeExtension':
105
109
  case 'ObjectTypeExtension':
@@ -110,11 +114,20 @@ export function buildSchemaFromAST(
110
114
  const toExtend = schema.type(definitionNode.name.value)!;
111
115
  const extension = toExtend.newExtension();
112
116
  extension.sourceAST = definitionNode;
113
- buildNamedTypeInner(definitionNode, toExtend, schema.blueprint, extension);
117
+ buildNamedTypeInner(definitionNode, toExtend, schema.blueprint, errors, extension);
114
118
  break;
115
119
  }
116
120
  }
117
121
 
122
+ // Note: we could try calling `schema.validate()` regardless of errors building the schema and merge the resulting
123
+ // errors, and there is some subset of cases where this be a tad more convenient (as the user would get all the errors
124
+ // at once), but in most cases a bunch of the errors thrown by `schema.validate()` would actually be consequences of
125
+ // the schema not be properly built in the first place and those errors would be confusing to the user. And avoiding
126
+ // confusing users probably trumps a rare minor convenience.
127
+ if (errors.length > 0) {
128
+ throw ErrGraphQLValidationFailed(errors);
129
+ }
130
+
118
131
  if (options?.validate ?? true) {
119
132
  schema.validate();
120
133
  }
@@ -187,21 +200,24 @@ function getReferencedType(node: NamedTypeNode, schema: Schema): NamedType {
187
200
  return type;
188
201
  }
189
202
 
190
- function withNodeAttachedToError(operation: () => void, node: ASTNode) {
203
+ function withNodeAttachedToError(operation: () => void, node: ASTNode, errors: GraphQLError[]) {
191
204
  try {
192
205
  operation();
193
206
  } catch (e) {
194
- if (e instanceof GraphQLError) {
195
- const allNodes: ASTNode | ASTNode[] = e.nodes ? [node, ...e.nodes] : node;
196
- throw new GraphQLError(
197
- e.message,
198
- allNodes,
199
- e.source,
200
- e.positions,
201
- e.path,
202
- e,
203
- e.extensions
204
- );
207
+ const causes = errorCauses(e);
208
+ if (causes) {
209
+ for (const cause of causes) {
210
+ const allNodes: ASTNode | ASTNode[] = cause.nodes ? [node, ...cause.nodes] : node;
211
+ errors.push(new GraphQLError(
212
+ cause.message,
213
+ allNodes,
214
+ cause.source,
215
+ cause.positions,
216
+ cause.path,
217
+ cause,
218
+ cause.extensions
219
+ ));
220
+ }
205
221
  } else {
206
222
  throw e;
207
223
  }
@@ -211,31 +227,39 @@ function withNodeAttachedToError(operation: () => void, node: ASTNode) {
211
227
  function buildSchemaDefinitionInner(
212
228
  schemaNode: SchemaDefinitionNode | SchemaExtensionNode,
213
229
  schemaDefinition: SchemaDefinition,
230
+ errors: GraphQLError[],
214
231
  extension?: Extension<SchemaDefinition>
215
232
  ) {
216
233
  for (const opTypeNode of schemaNode.operationTypes ?? []) {
217
234
  withNodeAttachedToError(
218
235
  () => schemaDefinition.setRoot(opTypeNode.operation, opTypeNode.type.name.value).setOfExtension(extension),
219
- opTypeNode);
236
+ opTypeNode,
237
+ errors,
238
+ );
220
239
  }
221
240
  schemaDefinition.sourceAST = schemaNode;
222
241
  if ('description' in schemaNode) {
223
242
  schemaDefinition.description = schemaNode.description?.value;
224
243
  }
225
- buildAppliedDirectives(schemaNode, schemaDefinition, extension);
244
+ buildAppliedDirectives(schemaNode, schemaDefinition, errors, extension);
226
245
  }
227
246
 
228
247
  function buildAppliedDirectives(
229
248
  elementNode: NodeWithDirectives,
230
249
  element: SchemaElement<any, any>,
250
+ errors: GraphQLError[],
231
251
  extension?: Extension<any>
232
252
  ) {
233
253
  for (const directive of elementNode.directives ?? []) {
234
- withNodeAttachedToError(() => {
235
- const d = element.applyDirective(directive.name.value, buildArgs(directive));
236
- d.setOfExtension(extension);
237
- d.sourceAST = directive;
238
- }, directive);
254
+ withNodeAttachedToError(
255
+ () => {
256
+ const d = element.applyDirective(directive.name.value, buildArgs(directive));
257
+ d.setOfExtension(extension);
258
+ d.sourceAST = directive;
259
+ },
260
+ directive,
261
+ errors,
262
+ );
239
263
  }
240
264
  }
241
265
 
@@ -251,6 +275,7 @@ function buildNamedTypeInner(
251
275
  definitionNode: DefinitionNode & NodeWithDirectives & NodeWithDescription,
252
276
  type: NamedType,
253
277
  blueprint: SchemaBlueprint,
278
+ errors: GraphQLError[],
254
279
  extension?: Extension<any>,
255
280
  ) {
256
281
  switch (definitionNode.kind) {
@@ -265,18 +290,20 @@ function buildNamedTypeInner(
265
290
  }
266
291
  const field = fieldBasedType.addField(fieldNode.name.value);
267
292
  field.setOfExtension(extension);
268
- buildFieldDefinitionInner(fieldNode, field);
293
+ buildFieldDefinitionInner(fieldNode, field, errors);
269
294
  }
270
295
  for (const itfNode of definitionNode.interfaces ?? []) {
271
296
  withNodeAttachedToError(
272
297
  () => {
273
298
  const itfName = itfNode.name.value;
274
299
  if (fieldBasedType.implementsInterface(itfName)) {
275
- throw new GraphQLError(`Type ${type} can only implement ${itfName} once.`);
300
+ throw new GraphQLError(`Type "${type}" can only implement "${itfName}" once.`);
276
301
  }
277
302
  fieldBasedType.addImplementedInterface(itfName).setOfExtension(extension);
278
303
  },
279
- itfNode);
304
+ itfNode,
305
+ errors,
306
+ );
280
307
  }
281
308
  break;
282
309
  case 'UnionTypeDefinition':
@@ -287,11 +314,13 @@ function buildNamedTypeInner(
287
314
  () => {
288
315
  const name = namedType.name.value;
289
316
  if (unionType.hasTypeMember(name)) {
290
- throw new GraphQLError(`Union type ${unionType} can only include type ${name} once.`);
317
+ throw new GraphQLError(`Union type "${unionType}" can only include type "${name}" once.`);
291
318
  }
292
319
  unionType.addType(name).setOfExtension(extension);
293
320
  },
294
- namedType);
321
+ namedType,
322
+ errors,
323
+ );
295
324
  }
296
325
  break;
297
326
  case 'EnumTypeDefinition':
@@ -303,7 +332,7 @@ function buildNamedTypeInner(
303
332
  v.description = enumVal.description.value;
304
333
  }
305
334
  v.setOfExtension(extension);
306
- buildAppliedDirectives(enumVal, v);
335
+ buildAppliedDirectives(enumVal, v, errors);
307
336
  }
308
337
  break;
309
338
  case 'InputObjectTypeDefinition':
@@ -312,41 +341,47 @@ function buildNamedTypeInner(
312
341
  for (const fieldNode of definitionNode.fields ?? []) {
313
342
  const field = inputObjectType.addField(fieldNode.name.value);
314
343
  field.setOfExtension(extension);
315
- buildInputFieldDefinitionInner(fieldNode, field);
344
+ buildInputFieldDefinitionInner(fieldNode, field, errors);
316
345
  }
317
346
  break;
318
347
  }
319
- buildAppliedDirectives(definitionNode, type, extension);
348
+ buildAppliedDirectives(definitionNode, type, errors, extension);
320
349
  if (definitionNode.description) {
321
350
  type.description = definitionNode.description.value;
322
351
  }
323
352
  type.sourceAST = definitionNode;
324
353
  }
325
354
 
326
- function buildFieldDefinitionInner(fieldNode: FieldDefinitionNode, field: FieldDefinition<any>) {
355
+ function buildFieldDefinitionInner(
356
+ fieldNode: FieldDefinitionNode,
357
+ field: FieldDefinition<any>,
358
+ errors: GraphQLError[],
359
+ ) {
327
360
  const type = buildTypeReferenceFromAST(fieldNode.type, field.schema());
328
- field.type = ensureOutputType(type, field.coordinate, fieldNode);
361
+ field.type = validateOutputType(type, field.coordinate, fieldNode, errors);
329
362
  for (const inputValueDef of fieldNode.arguments ?? []) {
330
- buildArgumentDefinitionInner(inputValueDef, field.addArgument(inputValueDef.name.value));
363
+ buildArgumentDefinitionInner(inputValueDef, field.addArgument(inputValueDef.name.value), errors);
331
364
  }
332
- buildAppliedDirectives(fieldNode, field);
365
+ buildAppliedDirectives(fieldNode, field, errors);
333
366
  field.description = fieldNode.description?.value;
334
367
  field.sourceAST = fieldNode;
335
368
  }
336
369
 
337
- function ensureOutputType(type: Type, what: string, node: ASTNode): OutputType {
370
+ function validateOutputType(type: Type, what: string, node: ASTNode, errors: GraphQLError[]): OutputType | undefined {
338
371
  if (isOutputType(type)) {
339
372
  return type;
340
373
  } else {
341
- throw new GraphQLError(`The type of ${what} must be Output Type but got: ${type}, a ${type.kind}.`, node);
374
+ errors.push(new GraphQLError(`The type of "${what}" must be Output Type but got "${type}", a ${type.kind}.`, node));
375
+ return undefined;
342
376
  }
343
377
  }
344
378
 
345
- function ensureInputType(type: Type, what: string, node: ASTNode): InputType {
379
+ function validateInputType(type: Type, what: string, node: ASTNode, errors: GraphQLError[]): InputType | undefined {
346
380
  if (isInputType(type)) {
347
381
  return type;
348
382
  } else {
349
- throw new GraphQLError(`The type of ${what} must be Input Type but got: ${type}, a ${type.kind}.`, node);
383
+ errors.push(new GraphQLError(`The type of "${what}" must be Input Type but got "${type}", a ${type.kind}.`, node));
384
+ return undefined;
350
385
  }
351
386
  }
352
387
 
@@ -369,27 +404,39 @@ function buildTypeReferenceFromAST(typeNode: TypeNode, schema: Schema): Type {
369
404
  }
370
405
  }
371
406
 
372
- function buildArgumentDefinitionInner(inputNode: InputValueDefinitionNode, arg: ArgumentDefinition<any>) {
407
+ function buildArgumentDefinitionInner(
408
+ inputNode: InputValueDefinitionNode,
409
+ arg: ArgumentDefinition<any>,
410
+ errors: GraphQLError[],
411
+ ) {
373
412
  const type = buildTypeReferenceFromAST(inputNode.type, arg.schema());
374
- arg.type = ensureInputType(type, arg.coordinate, inputNode);
413
+ arg.type = validateInputType(type, arg.coordinate, inputNode, errors);
375
414
  arg.defaultValue = buildValue(inputNode.defaultValue);
376
- buildAppliedDirectives(inputNode, arg);
415
+ buildAppliedDirectives(inputNode, arg, errors);
377
416
  arg.description = inputNode.description?.value;
378
417
  arg.sourceAST = inputNode;
379
418
  }
380
419
 
381
- function buildInputFieldDefinitionInner(fieldNode: InputValueDefinitionNode, field: InputFieldDefinition) {
420
+ function buildInputFieldDefinitionInner(
421
+ fieldNode: InputValueDefinitionNode,
422
+ field: InputFieldDefinition,
423
+ errors: GraphQLError[],
424
+ ) {
382
425
  const type = buildTypeReferenceFromAST(fieldNode.type, field.schema());
383
- field.type = ensureInputType(type, field.coordinate, fieldNode);
426
+ field.type = validateInputType(type, field.coordinate, fieldNode, errors);
384
427
  field.defaultValue = buildValue(fieldNode.defaultValue);
385
- buildAppliedDirectives(fieldNode, field);
428
+ buildAppliedDirectives(fieldNode, field, errors);
386
429
  field.description = fieldNode.description?.value;
387
430
  field.sourceAST = fieldNode;
388
431
  }
389
432
 
390
- function buildDirectiveDefinitionInner(directiveNode: DirectiveDefinitionNode, directive: DirectiveDefinition) {
433
+ function buildDirectiveDefinitionInner(
434
+ directiveNode: DirectiveDefinitionNode,
435
+ directive: DirectiveDefinition,
436
+ errors: GraphQLError[],
437
+ ) {
391
438
  for (const inputValueDef of directiveNode.arguments ?? []) {
392
- buildArgumentDefinitionInner(inputValueDef, directive.addArgument(inputValueDef.name.value));
439
+ buildArgumentDefinitionInner(inputValueDef, directive.addArgument(inputValueDef.name.value), errors);
393
440
  }
394
441
  directive.repeatable = directiveNode.repeatable;
395
442
  const locations = directiveNode.locations.map(({ value }) => value as DirectiveLocation);