@wundergraph/composition 0.0.1 → 0.3.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 (42) hide show
  1. package/LICENSE +197 -14
  2. package/README.md +140 -1
  3. package/dist/ast/ast.d.ts +2 -0
  4. package/dist/ast/ast.js.map +1 -1
  5. package/dist/ast/utils.d.ts +7 -7
  6. package/dist/ast/utils.js +30 -16
  7. package/dist/ast/utils.js.map +1 -1
  8. package/dist/errors/errors.d.ts +8 -4
  9. package/dist/errors/errors.js +67 -10
  10. package/dist/errors/errors.js.map +1 -1
  11. package/dist/federation/federation-factory.d.ts +6 -5
  12. package/dist/federation/federation-factory.js +105 -30
  13. package/dist/federation/federation-factory.js.map +1 -1
  14. package/dist/federation/federation-result.d.ts +2 -2
  15. package/dist/federation/federation-result.js +0 -7
  16. package/dist/federation/federation-result.js.map +1 -1
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +1 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/normalization/normalization-factory.d.ts +9 -3
  21. package/dist/normalization/normalization-factory.js +235 -105
  22. package/dist/normalization/normalization-factory.js.map +1 -1
  23. package/dist/normalization/utils.d.ts +7 -3
  24. package/dist/normalization/utils.js +0 -1
  25. package/dist/normalization/utils.js.map +1 -1
  26. package/dist/subgraph/field-configuration.d.ts +7 -0
  27. package/dist/subgraph/field-configuration.js +3 -0
  28. package/dist/subgraph/field-configuration.js.map +1 -0
  29. package/dist/{federation → subgraph}/subgraph.d.ts +1 -1
  30. package/dist/{federation → subgraph}/subgraph.js +8 -28
  31. package/dist/subgraph/subgraph.js.map +1 -0
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/dist/utils/constants.js +8 -1
  34. package/dist/utils/constants.js.map +1 -1
  35. package/dist/utils/string-constants.d.ts +1 -0
  36. package/dist/utils/string-constants.js +2 -1
  37. package/dist/utils/string-constants.js.map +1 -1
  38. package/dist/utils/utils.d.ts +22 -1
  39. package/dist/utils/utils.js +65 -3
  40. package/dist/utils/utils.js.map +1 -1
  41. package/package.json +11 -10
  42. package/dist/federation/subgraph.js.map +0 -1
@@ -10,6 +10,7 @@ const utils_3 = require("../utils/utils");
10
10
  const errors_1 = require("../errors/errors");
11
11
  const string_constants_1 = require("../utils/string-constants");
12
12
  const buildASTSchema_1 = require("../buildASTSchema/buildASTSchema");
13
+ const merge_1 = require("@graphql-tools/merge");
13
14
  function normalizeSubgraphFromString(subgraph) {
14
15
  let document;
15
16
  try {
@@ -53,12 +54,30 @@ class NormalizationFactory {
53
54
  operationTypes: new Map(),
54
55
  };
55
56
  }
57
+ extractArguments(node, map) {
58
+ if (!node.arguments) {
59
+ return map;
60
+ }
61
+ for (const argumentNode of node.arguments) {
62
+ const argumentName = argumentNode.name.value;
63
+ if (map.has(argumentName)) {
64
+ // TODO
65
+ this.errors.push(new Error('duplicate argument'));
66
+ continue;
67
+ }
68
+ map.set(argumentName, argumentNode);
69
+ }
70
+ return map;
71
+ }
56
72
  extractDirectives(node, map) {
57
73
  if (!node.directives) {
58
74
  return map;
59
75
  }
60
76
  for (const directive of node.directives) {
61
77
  const directiveName = directive.name.value;
78
+ if (directiveName === string_constants_1.EXTENDS) {
79
+ continue;
80
+ }
62
81
  const existingDirectives = map.get(directiveName);
63
82
  if (existingDirectives) {
64
83
  existingDirectives.push(directive);
@@ -68,20 +87,6 @@ class NormalizationFactory {
68
87
  }
69
88
  return map;
70
89
  }
71
- extractUniqueInterfaces(node, interfaces) {
72
- if (!node.interfaces) {
73
- return interfaces;
74
- }
75
- for (const face of node.interfaces) {
76
- const name = face.name.value;
77
- if (interfaces.has(name)) {
78
- this.errors.push(new Error(`Interface "${name}" can only be defined on type "${this.parentTypeName}" once.`)); // TODO
79
- continue;
80
- }
81
- interfaces.add(name);
82
- }
83
- return interfaces;
84
- }
85
90
  extractUniqueUnionMembers(members, map) {
86
91
  for (const member of members) {
87
92
  const name = member.name.value;
@@ -102,7 +107,7 @@ class NormalizationFactory {
102
107
  interfaces.add(interfaceName);
103
108
  continue;
104
109
  }
105
- this.errors.push((0, errors_1.duplicateInterfaceError)(interfaceName, typeName)); // TODO
110
+ this.errors.push((0, errors_1.duplicateInterfaceExtensionError)(interfaceName, typeName));
106
111
  }
107
112
  }
108
113
  mergeUniqueUnionMembers(baseUnion, extensionUnion) {
@@ -139,7 +144,7 @@ class NormalizationFactory {
139
144
  for (const [directiveName, directives] of parent.directives) {
140
145
  const definition = this.allDirectiveDefinitions.get(directiveName);
141
146
  if (!definition) {
142
- this.errors.push((0, errors_1.undefinedDirectiveError)(parentTypeName, directiveName));
147
+ this.errors.push((0, errors_1.undefinedDirectiveError)(directiveName, parentTypeName));
143
148
  continue;
144
149
  }
145
150
  const allArguments = new Set();
@@ -165,7 +170,7 @@ class NormalizationFactory {
165
170
  }
166
171
  if (!directive.arguments || directive.arguments.length < 1) {
167
172
  if (requiredArguments.size > 0) {
168
- errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, parentTypeName, Array.from(requiredArguments)));
173
+ errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, parentTypeName, [...requiredArguments]));
169
174
  }
170
175
  else {
171
176
  normalizedDirectives.push(directive);
@@ -173,9 +178,9 @@ class NormalizationFactory {
173
178
  continue;
174
179
  }
175
180
  const definedArguments = (0, utils_2.getDefinedArgumentsForDirective)(directive.arguments, allArguments, directiveName, parentTypeName, errorMessages);
176
- const missingRequiredArguments = (0, utils_3.getEntriesNotInSet)(requiredArguments, definedArguments);
181
+ const missingRequiredArguments = (0, utils_3.getEntriesNotInHashSet)(requiredArguments, definedArguments);
177
182
  if (missingRequiredArguments.length > 0) {
178
- errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, parentTypeName, Array.from(requiredArguments), missingRequiredArguments));
183
+ errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, parentTypeName, [...requiredArguments], missingRequiredArguments));
179
184
  }
180
185
  // Only add unique entity keys
181
186
  if (directiveName === string_constants_1.KEY) {
@@ -198,12 +203,59 @@ class NormalizationFactory {
198
203
  }
199
204
  return normalizedDirectives;
200
205
  }
206
+ convertKindForExtension(node) {
207
+ switch (node.kind) {
208
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
209
+ return graphql_1.Kind.INTERFACE_TYPE_EXTENSION;
210
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
211
+ return graphql_1.Kind.OBJECT_TYPE_EXTENSION;
212
+ default:
213
+ return node.kind;
214
+ }
215
+ }
216
+ handleObjectLikeExtension(node) {
217
+ this.isCurrentParentExtension = true;
218
+ const extension = this.extensions.get(this.parentTypeName);
219
+ const convertedKind = this.convertKindForExtension(node);
220
+ if (extension) {
221
+ if (extension.kind !== convertedKind) {
222
+ this.errors.push((0, errors_1.incompatibleExtensionKindsError)(node, extension.kind));
223
+ return false;
224
+ }
225
+ this.extractDirectives(node, extension.directives);
226
+ (0, utils_1.extractInterfaces)(node, extension.interfaces, this.errors);
227
+ return;
228
+ }
229
+ const isEntity = (0, utils_1.isObjectLikeNodeEntity)(node);
230
+ const interfaces = new Set();
231
+ this.extensions.set(this.parentTypeName, {
232
+ directives: this.extractDirectives(node, new Map()),
233
+ fields: new Map(),
234
+ interfaces: (0, utils_1.extractInterfaces)(node, interfaces, this.errors),
235
+ isEntity,
236
+ kind: convertedKind,
237
+ name: node.name,
238
+ });
239
+ if (node.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION
240
+ || node.kind === graphql_1.Kind.INTERFACE_TYPE_EXTENSION
241
+ || !isEntity) {
242
+ return;
243
+ }
244
+ const existingEntityKeyMap = this.entityMap.get(this.parentTypeName);
245
+ const { entityKeyMap, errors } = (0, utils_1.getEntityKeyExtractionResults)(node, existingEntityKeyMap || new Map());
246
+ if (errors.length > 0) {
247
+ this.errors.push(...errors);
248
+ }
249
+ if (!existingEntityKeyMap) {
250
+ this.entityMap.set(this.parentTypeName, entityKeyMap);
251
+ }
252
+ }
201
253
  validateChildDirectives(child, hostPath) {
202
254
  const childKind = child.node.kind;
203
255
  for (const [directiveName, directives] of child.directives) {
204
256
  const definition = this.allDirectiveDefinitions.get(directiveName);
205
257
  if (!definition) {
206
- this.errors.push((0, errors_1.undefinedDirectiveError)(hostPath, directiveName));
258
+ this.errors.push((0, errors_1.undefinedDirectiveError)(directiveName, hostPath));
207
259
  continue;
208
260
  }
209
261
  const allArguments = new Set();
@@ -225,14 +277,14 @@ class NormalizationFactory {
225
277
  }
226
278
  if (!directive.arguments || directive.arguments.length < 1) {
227
279
  if (requiredArguments.size > 0) {
228
- errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, hostPath, Array.from(requiredArguments)));
280
+ errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, hostPath, [...requiredArguments]));
229
281
  }
230
282
  continue;
231
283
  }
232
284
  const definedArguments = (0, utils_2.getDefinedArgumentsForDirective)(directive.arguments, allArguments, directiveName, hostPath, errorMessages);
233
- const missingRequiredArguments = (0, utils_3.getEntriesNotInSet)(requiredArguments, definedArguments);
285
+ const missingRequiredArguments = (0, utils_3.getEntriesNotInHashSet)(requiredArguments, definedArguments);
234
286
  if (missingRequiredArguments.length > 0) {
235
- errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, hostPath, Array.from(requiredArguments), missingRequiredArguments));
287
+ errorMessages.push((0, errors_1.undefinedRequiredArgumentsErrorMessage)(directiveName, hostPath, [...requiredArguments], missingRequiredArguments));
236
288
  }
237
289
  }
238
290
  if (errorMessages.length > 0) {
@@ -240,6 +292,85 @@ class NormalizationFactory {
240
292
  }
241
293
  }
242
294
  }
295
+ validateInterfaceImplementations(container) {
296
+ if (container.interfaces.size < 1) {
297
+ return;
298
+ }
299
+ const implementationErrorsMap = new Map();
300
+ for (const interfaceName of container.interfaces) {
301
+ const interfaceContainer = (0, utils_3.getOrThrowError)(this.parents, interfaceName);
302
+ if (interfaceContainer.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
303
+ throw (0, errors_1.incompatibleParentKindFatalError)(interfaceName, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, interfaceContainer.kind);
304
+ }
305
+ const implementationErrors = {
306
+ invalidFieldImplementations: new Map(),
307
+ unimplementedFields: [],
308
+ };
309
+ let hasErrors = false;
310
+ for (const [fieldName, interfaceField] of interfaceContainer.fields) {
311
+ let hasNestedErrors = false;
312
+ const containerField = container.fields.get(fieldName);
313
+ if (!containerField) {
314
+ hasErrors = true;
315
+ implementationErrors.unimplementedFields.push(fieldName);
316
+ continue;
317
+ }
318
+ const invalidFieldImplementation = {
319
+ invalidAdditionalArguments: new Set(),
320
+ invalidImplementedArguments: [],
321
+ originalResponseType: (0, merge_1.printTypeNode)(interfaceField.node.type),
322
+ unimplementedArguments: new Set(),
323
+ };
324
+ // The implemented field type must be equally or more restrictive than the original interface field type
325
+ if (!(0, utils_3.isTypeValidImplementation)(interfaceField.node.type, containerField.node.type)) {
326
+ hasErrors = true;
327
+ hasNestedErrors = true;
328
+ invalidFieldImplementation.implementedResponseType = (0, merge_1.printTypeNode)(containerField.node.type);
329
+ }
330
+ const handledArguments = new Set();
331
+ for (const [argumentName, interfaceArgument] of interfaceField.arguments) {
332
+ handledArguments.add(argumentName);
333
+ const containerArgument = containerField.arguments.get(argumentName);
334
+ // The type implementing the interface must include all arguments with no variation for that argument
335
+ if (!containerArgument) {
336
+ hasErrors = true;
337
+ hasNestedErrors = true;
338
+ invalidFieldImplementation.unimplementedArguments.add(argumentName);
339
+ continue;
340
+ }
341
+ // Implemented arguments should be the exact same type
342
+ const actualType = (0, merge_1.printTypeNode)(containerArgument.type);
343
+ const expectedType = (0, merge_1.printTypeNode)(interfaceArgument.type);
344
+ if (expectedType !== actualType) {
345
+ hasErrors = true;
346
+ hasNestedErrors = true;
347
+ invalidFieldImplementation.invalidImplementedArguments.push({ actualType, argumentName, expectedType });
348
+ }
349
+ }
350
+ // Additional arguments must be optional (nullable)
351
+ for (const [argumentName, argumentNode] of containerField.arguments) {
352
+ if (handledArguments.has(argumentName)) {
353
+ continue;
354
+ }
355
+ if (argumentNode.type.kind !== graphql_1.Kind.NON_NULL_TYPE) {
356
+ continue;
357
+ }
358
+ hasErrors = true;
359
+ hasNestedErrors = true;
360
+ invalidFieldImplementation.invalidAdditionalArguments.add(argumentName);
361
+ }
362
+ if (hasNestedErrors) {
363
+ implementationErrors.invalidFieldImplementations.set(fieldName, invalidFieldImplementation);
364
+ }
365
+ }
366
+ if (hasErrors) {
367
+ implementationErrorsMap.set(interfaceName, implementationErrors);
368
+ }
369
+ }
370
+ if (implementationErrorsMap.size) {
371
+ this.errors.push((0, errors_1.unimplementedInterfaceFieldsError)(container.name.value, (0, utils_3.kindToTypeString)(container.kind), implementationErrorsMap));
372
+ }
373
+ }
243
374
  normalize(document) {
244
375
  const factory = this;
245
376
  (0, graphql_1.visit)(document, {
@@ -282,7 +413,7 @@ class NormalizationFactory {
282
413
  enter(node) {
283
414
  const name = node.name.value;
284
415
  if (factory.parents.has(name)) {
285
- factory.errors.push((0, errors_1.duplicateTypeDefinitionError)('enum', name));
416
+ factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
286
417
  return false;
287
418
  }
288
419
  factory.parentTypeName = name;
@@ -331,7 +462,7 @@ class NormalizationFactory {
331
462
  ? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName)
332
463
  : (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName);
333
464
  if (parent.kind !== graphql_1.Kind.ENUM_TYPE_DEFINITION && parent.kind !== graphql_1.Kind.ENUM_TYPE_EXTENSION) {
334
- throw new Error(''); // TODO
465
+ throw (0, errors_1.unexpectedKindFatalError)(name);
335
466
  }
336
467
  if (parent.values.has(name)) {
337
468
  const error = factory.isCurrentParentExtension
@@ -363,7 +494,7 @@ class NormalizationFactory {
363
494
  parent.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION &&
364
495
  parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION &&
365
496
  parent.kind !== graphql_1.Kind.INTERFACE_TYPE_EXTENSION) {
366
- throw new Error(''); // TODO
497
+ throw (0, errors_1.unexpectedKindFatalError)(factory.parentTypeName);
367
498
  }
368
499
  if (parent.fields.has(name)) {
369
500
  const error = factory.isCurrentParentExtension
@@ -373,6 +504,7 @@ class NormalizationFactory {
373
504
  return;
374
505
  }
375
506
  parent.fields.set(name, {
507
+ arguments: factory.extractArguments(node, new Map),
376
508
  directives: factory.extractDirectives(node, new Map()),
377
509
  name,
378
510
  node,
@@ -386,7 +518,7 @@ class NormalizationFactory {
386
518
  enter(node) {
387
519
  const name = node.name.value;
388
520
  if (factory.parents.has(name)) {
389
- factory.errors.push((0, errors_1.duplicateTypeDefinitionError)('input object', name));
521
+ factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
390
522
  return false;
391
523
  }
392
524
  factory.parentTypeName = name;
@@ -438,7 +570,7 @@ class NormalizationFactory {
438
570
  ? (0, utils_3.getOrThrowError)(factory.extensions, factory.parentTypeName)
439
571
  : (0, utils_3.getOrThrowError)(factory.parents, factory.parentTypeName);
440
572
  if (parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION && parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_EXTENSION) {
441
- throw new Error(''); // TODO
573
+ throw (0, errors_1.unexpectedKindFatalError)(factory.parentTypeName);
442
574
  }
443
575
  if (parent.fields.has(name)) {
444
576
  factory.errors.push((0, errors_1.duplicateValueExtensionError)('input', factory.parentTypeName, name));
@@ -454,47 +586,32 @@ class NormalizationFactory {
454
586
  InterfaceTypeDefinition: {
455
587
  enter(node) {
456
588
  const name = node.name.value;
589
+ factory.parentTypeName = name;
590
+ if ((0, utils_1.isNodeExtension)(node)) {
591
+ return factory.handleObjectLikeExtension(node);
592
+ }
457
593
  if (factory.parents.has(name)) {
458
- factory.errors.push((0, errors_1.duplicateTypeDefinitionError)('interface', name));
594
+ factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
459
595
  return false;
460
596
  }
461
- factory.parentTypeName = name;
462
597
  factory.parents.set(name, {
463
598
  description: node.description,
464
599
  directives: factory.extractDirectives(node, new Map()),
465
600
  fields: new Map(),
466
- interfaces: (0, utils_1.extractInterfaces)(node, new Set()),
601
+ interfaces: (0, utils_1.extractInterfaces)(node, new Set(), factory.errors),
467
602
  kind: node.kind,
468
603
  name: node.name,
469
604
  });
470
605
  },
471
606
  leave() {
607
+ factory.isCurrentParentExtension = false;
472
608
  factory.parentTypeName = '';
473
609
  },
474
610
  },
475
611
  InterfaceTypeExtension: {
476
612
  enter(node) {
477
- const name = node.name.value;
478
- factory.parentTypeName = name;
479
- factory.isCurrentParentExtension = true;
480
- const extension = factory.extensions.get(factory.parentTypeName);
481
- if (extension) {
482
- if (extension.kind !== graphql_1.Kind.INTERFACE_TYPE_EXTENSION) {
483
- factory.errors.push((0, errors_1.incompatibleExtensionKindsError)(node, extension.kind));
484
- return false;
485
- }
486
- factory.extractDirectives(node, extension.directives);
487
- factory.extractUniqueInterfaces(node, extension.interfaces);
488
- return;
489
- }
490
- const interfaces = new Set();
491
- factory.extensions.set(name, {
492
- directives: factory.extractDirectives(node, new Map()),
493
- fields: new Map(),
494
- interfaces: (0, utils_1.extractInterfaces)(node, interfaces),
495
- kind: node.kind,
496
- name: node.name,
497
- });
613
+ factory.parentTypeName = node.name.value;
614
+ return factory.handleObjectLikeExtension(node);
498
615
  },
499
616
  leave() {
500
617
  factory.isCurrentParentExtension = false;
@@ -504,20 +621,26 @@ class NormalizationFactory {
504
621
  ObjectTypeDefinition: {
505
622
  enter(node) {
506
623
  const name = node.name.value;
624
+ factory.parentTypeName = name;
625
+ // handling for @extends directive
626
+ if ((0, utils_1.isNodeExtension)(node)) {
627
+ return factory.handleObjectLikeExtension(node);
628
+ }
507
629
  if (factory.parents.has(name)) {
508
- factory.errors.push((0, errors_1.duplicateTypeDefinitionError)('object', name));
630
+ factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
509
631
  return false;
510
632
  }
511
- factory.parentTypeName = name;
633
+ const isEntity = (0, utils_1.isObjectLikeNodeEntity)(node);
512
634
  factory.parents.set(name, {
513
635
  description: node.description,
514
636
  directives: factory.extractDirectives(node, new Map()),
515
637
  fields: new Map(),
516
- interfaces: factory.extractUniqueInterfaces(node, new Set()),
638
+ interfaces: (0, utils_1.extractInterfaces)(node, new Set(), factory.errors),
639
+ isEntity,
517
640
  kind: node.kind,
518
641
  name: node.name,
519
642
  });
520
- if (!(0, utils_1.isObjectNodeEntity)(node)) {
643
+ if (!isEntity) {
521
644
  return;
522
645
  }
523
646
  const existingEntityKeyMap = factory.entityMap.get(name);
@@ -530,43 +653,14 @@ class NormalizationFactory {
530
653
  }
531
654
  },
532
655
  leave() {
656
+ factory.isCurrentParentExtension = false;
533
657
  factory.parentTypeName = '';
534
658
  },
535
659
  },
536
660
  ObjectTypeExtension: {
537
661
  enter(node) {
538
- const name = node.name.value;
539
- factory.parentTypeName = name;
540
- factory.isCurrentParentExtension = true;
541
- const extension = factory.extensions.get(factory.parentTypeName);
542
- if (extension) {
543
- if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
544
- factory.errors.push((0, errors_1.incompatibleExtensionKindsError)(node, extension.kind));
545
- return false;
546
- }
547
- factory.extractDirectives(node, extension.directives);
548
- factory.extractUniqueInterfaces(node, extension.interfaces);
549
- return;
550
- }
551
- const interfaces = new Set();
552
- factory.extensions.set(name, {
553
- directives: factory.extractDirectives(node, new Map()),
554
- fields: new Map(),
555
- interfaces: (0, utils_1.extractInterfaces)(node, interfaces),
556
- kind: node.kind,
557
- name: node.name,
558
- });
559
- if (!(0, utils_1.isObjectNodeEntity)(node)) {
560
- return;
561
- }
562
- const existingEntityKeyMap = factory.entityMap.get(name);
563
- const { entityKeyMap, errors } = (0, utils_1.getEntityKeyExtractionResults)(node, existingEntityKeyMap || new Map());
564
- if (errors.length > 0) {
565
- factory.errors.push(...errors);
566
- }
567
- if (!existingEntityKeyMap) {
568
- factory.entityMap.set(name, entityKeyMap);
569
- }
662
+ factory.parentTypeName = node.name.value;
663
+ return factory.handleObjectLikeExtension(node);
570
664
  },
571
665
  leave() {
572
666
  factory.isCurrentParentExtension = false;
@@ -599,7 +693,7 @@ class NormalizationFactory {
599
693
  const name = node.name.value;
600
694
  const parent = factory.parents.get(name);
601
695
  if (parent) {
602
- factory.errors.push((0, errors_1.duplicateTypeDefinitionError)('scalar', name));
696
+ factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
603
697
  return false;
604
698
  }
605
699
  factory.parents.set(name, {
@@ -633,7 +727,6 @@ class NormalizationFactory {
633
727
  },
634
728
  SchemaDefinition: {
635
729
  enter(node) {
636
- // Apollo allows multiple schema definitions
637
730
  factory.extractDirectives(node, factory.schemaDefinition.directives);
638
731
  factory.schemaDefinition.description = factory.schemaDefinition.description || node.description;
639
732
  },
@@ -649,7 +742,7 @@ class NormalizationFactory {
649
742
  factory.parentTypeName = name;
650
743
  const parent = factory.parents.get(name);
651
744
  if (parent) {
652
- factory.errors.push((0, errors_1.duplicateTypeDefinitionError)('union', name));
745
+ factory.errors.push((0, errors_1.duplicateTypeDefinitionError)((0, utils_3.kindToTypeString)(node.kind), name));
653
746
  return false;
654
747
  }
655
748
  if (!node.types) {
@@ -711,9 +804,23 @@ class NormalizationFactory {
711
804
  if (this.schemaDefinition.operationTypes.size > 0) {
712
805
  definitions.push((0, utils_2.schemaContainerToNode)(this, this.schemaDefinition));
713
806
  }
807
+ // configurationDataMap is map of ConfigurationData per type name.
808
+ // It is an Intermediate configuration object that will be converted to an engine configuration in the router
809
+ const configurationDataMap = new Map();
714
810
  const validExtensionOrphans = new Set();
715
811
  const parentsToIgnore = new Set();
716
812
  for (const [typeName, extension] of this.extensions) {
813
+ const entity = this.entityMap.get(typeName);
814
+ const configurationData = {
815
+ fieldNames: new Set(),
816
+ isRootNode: !!entity,
817
+ selectionSets: entity ? [...entity.keys()] : [],
818
+ typeName,
819
+ };
820
+ if (extension.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
821
+ (0, utils_3.addIterableValuesToSet)(extension.fields.keys(), configurationData.fieldNames);
822
+ configurationDataMap.set(typeName, configurationData);
823
+ }
717
824
  const baseType = this.parents.get(typeName);
718
825
  if (!baseType) {
719
826
  if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
@@ -721,14 +828,14 @@ class NormalizationFactory {
721
828
  }
722
829
  else {
723
830
  (0, utils_2.validateEntityKeys)(this, typeName, true);
831
+ this.validateInterfaceImplementations(extension);
724
832
  validExtensionOrphans.add(typeName);
725
833
  definitions.push((0, utils_2.objectLikeContainerToNode)(this, extension));
726
834
  }
727
835
  continue;
728
836
  }
729
- if (!(0, utils_1.areBaseAndExtensionKindsCompatible)(baseType.kind, extension.kind)) {
730
- this.errors.push(new Error(`Extension error:\n Incompatible types: ` +
731
- `"${typeName}" is type "${baseType.kind}", but an extension of the same name is type "${extension.kind}.`));
837
+ if (!(0, utils_1.areBaseAndExtensionKindsCompatible)(baseType.kind, extension.kind, typeName)) {
838
+ this.errors.push((0, errors_1.incompatibleExtensionError)(typeName, baseType.kind, extension.kind));
732
839
  continue;
733
840
  }
734
841
  switch (baseType.kind) {
@@ -757,17 +864,20 @@ class NormalizationFactory {
757
864
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
758
865
  // intentional fallthrough
759
866
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
760
- const objectExtension = extension;
761
- for (const [fieldName, fieldDefinitionNode] of objectExtension.fields) {
762
- if (!baseType.fields.has(fieldName)) {
763
- baseType.fields.set(fieldName, fieldDefinitionNode);
867
+ const objectLikeExtension = extension;
868
+ for (const [fieldName, fieldContainer] of objectLikeExtension.fields) {
869
+ if (baseType.fields.has(fieldName)) {
870
+ this.errors.push((0, errors_1.duplicateFieldDefinitionError)(fieldName, typeName));
764
871
  continue;
765
872
  }
766
- this.errors.push((0, errors_1.duplicateFieldDefinitionError)(fieldName, typeName));
873
+ baseType.fields.set(fieldName, fieldContainer);
874
+ configurationData.fieldNames.add(fieldName);
767
875
  }
768
876
  (0, utils_2.validateEntityKeys)(this, typeName);
769
- this.mergeUniqueInterfaces(objectExtension.interfaces, baseType.interfaces, typeName);
770
- definitions.push((0, utils_2.objectLikeContainerToNode)(this, baseType, objectExtension));
877
+ this.mergeUniqueInterfaces(objectLikeExtension.interfaces, baseType.interfaces, typeName);
878
+ this.validateInterfaceImplementations(baseType);
879
+ configurationDataMap.set(typeName, configurationData);
880
+ definitions.push((0, utils_2.objectLikeContainerToNode)(this, baseType, objectLikeExtension));
771
881
  break;
772
882
  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
773
883
  definitions.push((0, utils_2.scalarContainerToNode)(this, baseType, extension));
@@ -777,7 +887,7 @@ class NormalizationFactory {
777
887
  definitions.push((0, utils_2.unionContainerToNode)(this, baseType, unionExtension));
778
888
  break;
779
889
  default:
780
- throw new Error('Unexpected kind'); // TODO
890
+ throw (0, errors_1.unexpectedKindFatalError)(typeName);
781
891
  }
782
892
  // At this point, the base type has been dealt with, so it doesn't need to be dealt with again
783
893
  parentsToIgnore.add(typeName);
@@ -794,9 +904,19 @@ class NormalizationFactory {
794
904
  definitions.push((0, utils_2.inputObjectContainerToNode)(this, parentContainer));
795
905
  break;
796
906
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
797
- // Intentional fallthrough
907
+ // intentional fallthrough
798
908
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
909
+ const entity = this.entityMap.get(typeName);
910
+ const configurationData = {
911
+ fieldNames: new Set(),
912
+ isRootNode: !!entity,
913
+ selectionSets: entity ? [...entity.keys()] : [],
914
+ typeName,
915
+ };
916
+ (0, utils_3.addIterableValuesToSet)(parentContainer.fields.keys(), configurationData.fieldNames);
799
917
  (0, utils_2.validateEntityKeys)(this, typeName);
918
+ this.validateInterfaceImplementations(parentContainer);
919
+ configurationDataMap.set(typeName, configurationData);
800
920
  definitions.push((0, utils_2.objectLikeContainerToNode)(this, parentContainer));
801
921
  break;
802
922
  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
@@ -823,7 +943,7 @@ class NormalizationFactory {
823
943
  }
824
944
  const object = this.parents.get(operationTypeName);
825
945
  const extension = this.extensions.get(operationTypeName);
826
- // Node is truthy of an operation type was explicitly declared
946
+ // Node is truthy if an operation type was explicitly declared
827
947
  if (node) {
828
948
  // If the type is not defined in the schema, it's always an error
829
949
  if (!object && !extension) {
@@ -833,6 +953,14 @@ class NormalizationFactory {
833
953
  // Add the explicitly defined type to the map for the federation-factory
834
954
  this.operationTypeNames.set(operationTypeName, operationType);
835
955
  }
956
+ if (!object && !extension) {
957
+ continue;
958
+ }
959
+ const rootNode = configurationDataMap.get(operationTypeName);
960
+ if (rootNode) {
961
+ rootNode.isRootNode = true;
962
+ rootNode.typeName = defaultTypeName;
963
+ }
836
964
  const containers = [object, extension];
837
965
  for (const container of containers) {
838
966
  if (!container) {
@@ -845,7 +973,8 @@ class NormalizationFactory {
845
973
  // Operations whose response type is an extension orphan could be valid through a federated graph
846
974
  // However, the field would have to be shareable to ever be valid TODO
847
975
  for (const fieldContainer of container.fields.values()) {
848
- const fieldPath = `${operationTypeName}.${fieldContainer.name}`;
976
+ const fieldName = fieldContainer.name;
977
+ const fieldPath = `${operationTypeName}.${fieldName}`;
849
978
  const fieldTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, fieldContainer.node.type);
850
979
  if (!constants_1.BASE_SCALARS.has(fieldTypeName) &&
851
980
  !this.parents.has(fieldTypeName) &&
@@ -869,6 +998,7 @@ class NormalizationFactory {
869
998
  };
870
999
  return {
871
1000
  normalizationResult: {
1001
+ configurationDataMap,
872
1002
  isVersionTwo: this.isSubgraphVersionTwo,
873
1003
  operationTypes: this.operationTypeNames,
874
1004
  subgraphAST: newAST,