@wundergraph/composition 0.5.3 → 0.6.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 (41) hide show
  1. package/dist/ast/ast.d.ts +21 -3
  2. package/dist/ast/ast.js +23 -11
  3. package/dist/ast/ast.js.map +1 -1
  4. package/dist/ast/utils.d.ts +16 -103
  5. package/dist/ast/utils.js +132 -16
  6. package/dist/ast/utils.js.map +1 -1
  7. package/dist/errors/errors.d.ts +16 -4
  8. package/dist/errors/errors.js +97 -21
  9. package/dist/errors/errors.js.map +1 -1
  10. package/dist/federation/federation-factory.d.ts +28 -24
  11. package/dist/federation/federation-factory.js +610 -446
  12. package/dist/federation/federation-factory.js.map +1 -1
  13. package/dist/federation/utils.d.ts +130 -0
  14. package/dist/federation/utils.js +15 -0
  15. package/dist/federation/utils.js.map +1 -0
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.js +2 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/normalization/normalization-factory.d.ts +9 -3
  20. package/dist/normalization/normalization-factory.js +221 -75
  21. package/dist/normalization/normalization-factory.js.map +1 -1
  22. package/dist/normalization/utils.d.ts +4 -0
  23. package/dist/normalization/utils.js +12 -5
  24. package/dist/normalization/utils.js.map +1 -1
  25. package/dist/subgraph/field-configuration.d.ts +12 -1
  26. package/dist/subgraph/subgraph.d.ts +2 -2
  27. package/dist/subgraph/subgraph.js +45 -82
  28. package/dist/subgraph/subgraph.js.map +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/dist/utils/constants.js +26 -8
  31. package/dist/utils/constants.js.map +1 -1
  32. package/dist/utils/string-constants.d.ts +12 -1
  33. package/dist/utils/string-constants.js +14 -2
  34. package/dist/utils/string-constants.js.map +1 -1
  35. package/dist/utils/utils.d.ts +15 -3
  36. package/dist/utils/utils.js +58 -23
  37. package/dist/utils/utils.js.map +1 -1
  38. package/package.json +4 -3
  39. package/dist/federation/federation-result.d.ts +0 -6
  40. package/dist/federation/federation-result.js +0 -3
  41. package/dist/federation/federation-result.js.map +0 -1
@@ -1,74 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FederationFactory = exports.federateSubgraphs = void 0;
3
+ exports.federateSubgraphs = exports.FederationFactory = void 0;
4
4
  const graphology_1 = require("graphology");
5
- const graphology_simple_path_1 = require("graphology-simple-path");
6
5
  const graphql_1 = require("graphql");
7
6
  const ast_1 = require("../ast/ast");
8
7
  const utils_1 = require("../ast/utils");
9
8
  const errors_1 = require("../errors/errors");
10
9
  const type_merging_1 = require("../type-merging/type-merging");
10
+ const utils_2 = require("./utils");
11
11
  const subgraph_1 = require("../subgraph/subgraph");
12
12
  const string_constants_1 = require("../utils/string-constants");
13
- const utils_2 = require("../utils/utils");
14
- const normalization_factory_1 = require("../normalization/normalization-factory");
13
+ const utils_3 = require("../utils/utils");
15
14
  const merge_1 = require("@graphql-tools/merge");
16
- function federateSubgraphs(subgraphs) {
17
- if (subgraphs.length < 1) {
18
- throw errors_1.minimumSubgraphRequirementError;
19
- }
20
- const normalizedSubgraphs = [];
21
- const validationErrors = [];
22
- const subgraphNames = new Set();
23
- const nonUniqueSubgraphNames = new Set();
24
- const invalidNameErrorMessages = [];
25
- for (let i = 0; i < subgraphs.length; i++) {
26
- const subgraph = subgraphs[i];
27
- const name = subgraph.name || `subgraph-${i}-${Date.now()}`;
28
- if (!subgraph.name) {
29
- invalidNameErrorMessages.push((0, errors_1.invalidSubgraphNameErrorMessage)(i, name));
30
- }
31
- else {
32
- (0, subgraph_1.validateSubgraphName)(subgraph.name, subgraphNames, nonUniqueSubgraphNames);
33
- }
34
- const { errors, normalizationResult } = (0, normalization_factory_1.normalizeSubgraph)(subgraph.definitions);
35
- if (errors) {
36
- validationErrors.push((0, errors_1.subgraphValidationError)(name, errors));
37
- continue;
38
- }
39
- if (!normalizationResult) {
40
- validationErrors.push((0, errors_1.subgraphValidationError)(name, [errors_1.subgraphValidationFailureErrorMessage]));
41
- continue;
42
- }
43
- normalizedSubgraphs.push({
44
- definitions: normalizationResult.subgraphAST,
45
- isVersionTwo: normalizationResult.isVersionTwo,
46
- name,
47
- operationTypes: normalizationResult.operationTypes,
48
- url: subgraph.url,
49
- });
50
- }
51
- const allErrors = [];
52
- if (invalidNameErrorMessages.length > 0 || nonUniqueSubgraphNames.size > 0) {
53
- allErrors.push((0, errors_1.invalidSubgraphNamesError)([...nonUniqueSubgraphNames], invalidNameErrorMessages));
54
- }
55
- allErrors.push(...validationErrors);
56
- if (allErrors.length > 0) {
57
- return { errors: allErrors };
58
- }
59
- const federationFactory = new FederationFactory(normalizedSubgraphs);
60
- return federationFactory.federate();
61
- }
62
- exports.federateSubgraphs = federateSubgraphs;
15
+ const constants_1 = require("../utils/constants");
16
+ const normalization_factory_1 = require("../normalization/normalization-factory");
63
17
  class FederationFactory {
64
18
  abstractToConcreteTypeNames = new Map();
65
19
  areFieldsShareable = false;
66
20
  argumentTypeNameSet = new Set();
21
+ argumentConfigurations = [];
22
+ executableDirectives = new Set();
67
23
  parentTypeName = '';
24
+ persistedDirectives = new Set([string_constants_1.DEPRECATED, string_constants_1.INACCESSIBLE, string_constants_1.TAG]);
68
25
  currentSubgraphName = '';
69
26
  childName = '';
70
27
  directiveDefinitions = new Map();
71
- entityMap = new Map();
28
+ entities = new Map();
72
29
  errors = [];
73
30
  extensions = new Map();
74
31
  graph = new graphology_1.MultiGraph();
@@ -82,10 +39,8 @@ class FederationFactory {
82
39
  isParentRootType = false;
83
40
  isParentInputObject = false;
84
41
  outputFieldTypeNameSet = new Set();
85
- parentMap = new Map();
86
- rootTypeFieldsByResponseTypeName = new Map();
42
+ parents = new Map();
87
43
  rootTypeNames = new Set([string_constants_1.DEFAULT_MUTATION, string_constants_1.DEFAULT_QUERY, string_constants_1.DEFAULT_SUBSCRIPTION]);
88
- sharedRootTypeFieldDependentResponses = new Map();
89
44
  subgraphs = [];
90
45
  shareableErrorTypeNames = new Map();
91
46
  constructor(subgraphs) {
@@ -96,28 +51,25 @@ class FederationFactory {
96
51
  }
97
52
  upsertEntity(node) {
98
53
  const typeName = node.name.value;
99
- const entity = this.entityMap.get(typeName);
54
+ const entity = this.entities.get(typeName);
100
55
  if (entity) {
101
- (0, utils_1.extractEntityKeys)(node, entity.keys);
56
+ (0, utils_1.extractEntityKeys)(node, entity.keys, this.errors);
102
57
  entity.subgraphs.add(this.currentSubgraphName);
103
58
  return;
104
59
  }
105
- this.entityMap.set(typeName, {
60
+ this.entities.set(typeName, {
106
61
  fields: new Set(),
107
- keys: (0, utils_1.extractEntityKeys)(node, new Set()),
62
+ keys: (0, utils_1.extractEntityKeys)(node, new Set(), this.errors),
108
63
  subgraphs: new Set([this.currentSubgraphName]),
109
64
  });
110
65
  }
111
66
  populateMultiGraphAndRenameOperations(subgraphs) {
112
67
  for (const subgraph of subgraphs) {
113
68
  this.currentSubgraphName = subgraph.name;
114
- (0, subgraph_1.walkSubgraphToCollectObjects)(this, subgraph);
115
- (0, subgraph_1.walkSubgraphToCollectOperationsAndFields)(this, subgraph);
69
+ (0, subgraph_1.walkSubgraphToCollectObjectLikesAndDirectiveDefinitions)(this, subgraph);
70
+ (0, subgraph_1.walkSubgraphToCollectFields)(this, subgraph);
116
71
  }
117
72
  }
118
- isParentInterface(parent) {
119
- return parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION;
120
- }
121
73
  isFieldEntityKey(parent) {
122
74
  if (parent.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || parent.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
123
75
  return parent.entityKeys.has(this.childName);
@@ -127,11 +79,11 @@ class FederationFactory {
127
79
  getEnumMergeMethod(enumName) {
128
80
  if (this.inputFieldTypeNameSet.has(enumName) || this.argumentTypeNameSet.has(enumName)) {
129
81
  if (this.outputFieldTypeNameSet.has(enumName)) {
130
- return utils_1.MergeMethod.CONSISTENT;
82
+ return utils_2.MergeMethod.CONSISTENT;
131
83
  }
132
- return utils_1.MergeMethod.INTERSECTION;
84
+ return utils_2.MergeMethod.INTERSECTION;
133
85
  }
134
- return utils_1.MergeMethod.UNION;
86
+ return utils_2.MergeMethod.UNION;
135
87
  }
136
88
  validateArgumentDefaultValues(argName, existingDefaultValue, newDefaultValue) {
137
89
  if (existingDefaultValue.kind !== newDefaultValue.kind) {
@@ -149,6 +101,7 @@ class FederationFactory {
149
101
  existingArg.includeDefaultValue = false;
150
102
  return;
151
103
  }
104
+ const argumentName = existingArg.node.name.value;
152
105
  const existingDefaultValue = existingArg.node.defaultValue;
153
106
  switch (existingDefaultValue.kind) {
154
107
  case graphql_1.Kind.LIST: // TODO
@@ -163,92 +116,54 @@ class FederationFactory {
163
116
  case graphql_1.Kind.FLOAT:
164
117
  case graphql_1.Kind.INT:
165
118
  case graphql_1.Kind.STRING:
166
- this.validateArgumentDefaultValues(existingArg.node.name.value, existingDefaultValue, newDefaultValue);
119
+ this.validateArgumentDefaultValues(argumentName, existingDefaultValue, newDefaultValue);
167
120
  break;
168
121
  default:
169
- throw new Error('Unexpected argument type'); // TODO
122
+ throw (0, errors_1.unexpectedArgumentKindFatalError)(argumentName, this.childName);
170
123
  }
171
124
  }
172
- upsertArgumentsForFieldNode(node, existingFieldNode) {
173
- if (!node.arguments) {
174
- return;
175
- }
176
- for (const arg of node.arguments) {
177
- const argName = arg.name.value;
178
- const argPath = `${node.name.value}(${argName}...)`;
179
- this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, arg.type));
180
- const existingArg = existingFieldNode.arguments.get(argName);
181
- if (existingArg) {
182
- existingArg.appearances += 1;
183
- existingArg.node.description = existingArg.node.description || arg.description;
184
- const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(existingArg.node.type, arg.type, this.childName, argName);
185
- if (typeNode) {
186
- existingArg.node.type = typeNode;
187
- }
188
- else {
189
- if (!typeErrors || typeErrors.length < 2) {
190
- throw new Error(''); // TODO this should never happen
191
- }
192
- this.errors.push((0, errors_1.incompatibleArgumentTypesError)(argName, this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
193
- }
194
- this.compareAndValidateArgumentDefaultValues(existingArg, arg);
195
- return;
196
- }
197
- const newNode = (0, ast_1.inputValueDefinitionNodeToMutable)(arg, this.childName);
198
- // TODO validation of default values
199
- existingFieldNode.arguments.set(argName, {
200
- appearances: 1,
201
- includeDefaultValue: !!arg.defaultValue,
202
- node: newNode,
203
- });
125
+ upsertRequiredSubgraph(set, isRequired) {
126
+ if (isRequired) {
127
+ set.add(this.currentSubgraphName);
204
128
  }
129
+ return set;
205
130
  }
206
- extractArgumentsFromFieldNode(node, args) {
131
+ // TODO validation of default values
132
+ upsertArguments(node, argumentMap) {
207
133
  if (!node.arguments) {
208
- return;
134
+ return argumentMap;
209
135
  }
210
- for (const arg of node.arguments) {
211
- const argName = arg.name.value;
136
+ for (const argumentNode of node.arguments) {
137
+ const argName = argumentNode.name.value;
212
138
  const argPath = `${node.name.value}(${argName}...)`;
213
- args.set(argName, {
214
- appearances: 1,
215
- includeDefaultValue: !!arg.defaultValue,
216
- node: (0, ast_1.inputValueDefinitionNodeToMutable)(arg, this.childName),
217
- });
218
- this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, arg.type));
219
- }
220
- }
221
- addConcreteTypesForInterface(node) {
222
- if (!node.interfaces || node.interfaces.length < 1) {
223
- return;
224
- }
225
- const concreteTypeName = node.name.value;
226
- for (const iFace of node.interfaces) {
227
- const interfaceName = iFace.name.value;
228
- const concreteTypes = this.abstractToConcreteTypeNames.get(interfaceName);
229
- if (concreteTypes) {
230
- concreteTypes.add(concreteTypeName);
231
- }
232
- else {
233
- this.abstractToConcreteTypeNames.set(interfaceName, new Set([concreteTypeName]));
139
+ this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, argumentNode.type));
140
+ const isRequired = (0, type_merging_1.isTypeRequired)(argumentNode.type);
141
+ const existingArgumentContainer = argumentMap.get(argName);
142
+ if (!existingArgumentContainer) {
143
+ argumentMap.set(argName, {
144
+ includeDefaultValue: !!argumentNode.defaultValue,
145
+ node: (0, ast_1.inputValueDefinitionNodeToMutable)(argumentNode, this.childName),
146
+ requiredSubgraphs: this.upsertRequiredSubgraph(new Set(), isRequired),
147
+ subgraphs: new Set([this.currentSubgraphName]),
148
+ });
149
+ continue;
234
150
  }
235
- }
236
- }
237
- addConcreteTypesForUnion(node) {
238
- if (!node.types || node.types.length < 1) {
239
- return;
240
- }
241
- const unionName = node.name.value;
242
- for (const member of node.types) {
243
- const memberName = member.name.value;
244
- const concreteTypes = this.abstractToConcreteTypeNames.get(memberName);
245
- if (concreteTypes) {
246
- concreteTypes.add(memberName);
151
+ (0, utils_1.setLongestDescriptionForNode)(existingArgumentContainer.node, argumentNode.description);
152
+ this.upsertRequiredSubgraph(existingArgumentContainer.requiredSubgraphs, isRequired);
153
+ existingArgumentContainer.subgraphs.add(this.currentSubgraphName);
154
+ const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(existingArgumentContainer.node.type, argumentNode.type, this.childName, argName);
155
+ if (typeNode) {
156
+ existingArgumentContainer.node.type = typeNode;
247
157
  }
248
158
  else {
249
- this.abstractToConcreteTypeNames.set(unionName, new Set([memberName]));
159
+ if (!typeErrors || typeErrors.length < 2) {
160
+ throw (0, errors_1.argumentTypeMergeFatalError)(argName, this.childName);
161
+ }
162
+ this.errors.push((0, errors_1.incompatibleArgumentTypesError)(argName, this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
250
163
  }
164
+ this.compareAndValidateArgumentDefaultValues(existingArgumentContainer, argumentNode);
251
165
  }
166
+ return argumentMap;
252
167
  }
253
168
  isFieldShareable(node, parent) {
254
169
  return (!this.isCurrentSubgraphVersionTwo ||
@@ -256,129 +171,66 @@ class FederationFactory {
256
171
  (0, utils_1.isNodeShareable)(node) ||
257
172
  (this.isCurrentParentEntity && this.isFieldEntityKey(parent)));
258
173
  }
259
- getAllSimplePaths(responseTypeName) {
260
- if (responseTypeName === this.parentTypeName) {
261
- return [[this.parentTypeName]];
262
- }
263
- const responsePaths = this.graphPaths.get(responseTypeName);
264
- if (!responsePaths) {
265
- const allPaths = (0, graphology_simple_path_1.allSimplePaths)(this.graph, responseTypeName, this.parentTypeName);
266
- this.graphPaths.set(responseTypeName, new Map([
267
- [this.parentTypeName, allPaths]
268
- ]));
269
- return allPaths;
270
- }
271
- const pathsToParent = responsePaths.get(this.parentTypeName);
272
- if (pathsToParent) {
273
- return pathsToParent;
274
- }
275
- const allParentPaths = (0, graphology_simple_path_1.allSimplePaths)(this.graph, responseTypeName, this.parentTypeName);
276
- responsePaths.set(this.parentTypeName, allParentPaths);
277
- return allParentPaths;
278
- }
279
- addPotentiallyUnresolvableField(parent, fieldName) {
280
- const fieldContainer = (0, utils_2.getOrThrowError)(parent.fields, fieldName);
281
- for (const [responseTypeName, rootTypeFields] of this.rootTypeFieldsByResponseTypeName) {
282
- const paths = this.getAllSimplePaths(responseTypeName);
283
- // If the rootTypeFields response type has no path to the parent type, continue
284
- if (paths.length < 1) {
285
- continue;
286
- }
287
- // Construct all possible paths to the unresolvable field but with the fieldName relationship between nodes
288
- const partialResolverPaths = [];
289
- for (const path of paths) {
290
- let hasEntityAncestor = false;
291
- let resolverPath = '';
292
- for (let i = 0; i < path.length - 1; i++) {
293
- const pathParent = path[i];
294
- // The field in question is resolvable if it has an entity ancestor within the same subgraph
295
- // Unresolvable fields further up the chain will be handled elsewhere
296
- const entity = this.entityMap.get(pathParent);
297
- if (entity && entity.subgraphs.has(this.currentSubgraphName)) {
298
- hasEntityAncestor = true;
299
- break;
300
- }
301
- const edges = this.graph.edges(pathParent, path[i + 1]);
302
- // If there are multiple edges, pick the first one
303
- const inlineFragment = this.graph.getEdgeAttribute(edges[0], string_constants_1.INLINE_FRAGMENT);
304
- const edgeName = this.graph.getEdgeAttribute(edges[0], string_constants_1.FIELD_NAME);
305
- // If the parent field is an abstract type, the child should be proceeded by an inline fragment
306
- resolverPath += edgeName + (inlineFragment || '.');
307
- }
308
- if (hasEntityAncestor) {
309
- continue;
310
- }
311
- // Add the unresolvable field to each path
312
- resolverPath += fieldName;
313
- // If the field could have fields itself, add ellipsis
314
- if (this.graph.hasNode(fieldContainer.rootTypeName)) {
315
- resolverPath += string_constants_1.FRAGMENT_REPRESENTATION;
316
- }
317
- partialResolverPaths.push(resolverPath);
318
- }
319
- if (partialResolverPaths.length < 1) {
174
+ upsertDirectiveNode(node) {
175
+ const directiveName = node.name.value;
176
+ const directiveDefinition = this.directiveDefinitions.get(directiveName);
177
+ if (directiveDefinition) {
178
+ if (!this.executableDirectives.has(directiveName)) {
320
179
  return;
321
180
  }
322
- // Each of these rootTypeFields returns a type that has a path to the parent
323
- for (const [rootTypeFieldPath, rootTypeField] of rootTypeFields) {
324
- // If the rootTypeField is defined in a subgraph that the field is defined, it is resolvable
325
- if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, rootTypeField.subgraphs)) {
326
- continue;
327
- }
328
- const fullResolverPaths = [];
329
- // The field is still resolvable if it's defined and resolved in another graph (but that isn't yet known)
330
- // Consequently, the subgraphs must be compared later to determine that the field is always resolvable
331
- for (const partialResolverPath of partialResolverPaths) {
332
- fullResolverPaths.push(`${rootTypeFieldPath}${rootTypeField.inlineFragment}${partialResolverPath}`);
333
- }
334
- const potentiallyUnresolvableField = {
335
- fieldContainer,
336
- fullResolverPaths,
337
- rootTypeField: rootTypeField,
338
- };
339
- // The parent might already have unresolvable fields that have already been added
340
- const dependentResponsesByFieldName = this.sharedRootTypeFieldDependentResponses.get(this.parentTypeName);
341
- if (dependentResponsesByFieldName) {
342
- dependentResponsesByFieldName.push(potentiallyUnresolvableField);
343
- return;
344
- }
345
- this.sharedRootTypeFieldDependentResponses.set(this.parentTypeName, [potentiallyUnresolvableField]);
181
+ if ((0, utils_1.mergeExecutableDirectiveLocations)(node.locations, directiveDefinition).size < 1) {
182
+ this.executableDirectives.delete(directiveName);
183
+ return;
346
184
  }
185
+ this.upsertArguments(node, directiveDefinition.arguments);
186
+ (0, utils_1.setLongestDescriptionForNode)(directiveDefinition.node, node.description);
187
+ directiveDefinition.node.repeatable = directiveDefinition.node.repeatable && node.repeatable;
188
+ directiveDefinition.subgraphs.add(this.currentSubgraphName);
189
+ return;
190
+ }
191
+ const executableLocations = (0, utils_1.extractExecutableDirectiveLocations)(node.locations, new Set());
192
+ this.directiveDefinitions.set(directiveName, {
193
+ arguments: this.upsertArguments(node, new Map()),
194
+ executableLocations,
195
+ node: (0, ast_1.directiveDefinitionNodeToMutable)(node),
196
+ subgraphs: new Set([this.currentSubgraphName]),
197
+ });
198
+ if (executableLocations.size > 0) {
199
+ this.executableDirectives.add(directiveName);
347
200
  }
348
201
  }
349
202
  upsertFieldNode(node) {
350
203
  const parent = this.isCurrentParentExtensionType
351
- ? (0, utils_2.getOrThrowError)(this.extensions, this.parentTypeName)
352
- : (0, utils_2.getOrThrowError)(this.parentMap, this.parentTypeName);
353
- if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION &&
354
- parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION &&
355
- parent.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
204
+ ? (0, utils_3.getOrThrowError)(this.extensions, this.parentTypeName, string_constants_1.EXTENSIONS)
205
+ : (0, utils_3.getOrThrowError)(this.parents, this.parentTypeName, string_constants_1.PARENTS);
206
+ if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION
207
+ && parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION
208
+ && parent.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
356
209
  throw (0, errors_1.unexpectedKindFatalError)(this.parentTypeName);
357
210
  }
358
211
  const fieldMap = parent.fields;
359
212
  const isFieldShareable = this.isFieldShareable(node, parent);
360
213
  const fieldPath = `${this.parentTypeName}.${this.childName}`;
361
214
  const fieldRootTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, node.type);
362
- const existingFieldNode = fieldMap.get(this.childName);
363
- const entityParent = this.entityMap.get(this.parentTypeName);
364
- if (existingFieldNode) {
365
- existingFieldNode.appearances += 1;
366
- existingFieldNode.node.description = existingFieldNode.node.description || node.description;
367
- existingFieldNode.subgraphs.add(this.currentSubgraphName);
368
- existingFieldNode.subgraphsByShareable.set(this.currentSubgraphName, isFieldShareable);
369
- const { typeErrors, typeNode } = (0, type_merging_1.getLeastRestrictiveMergedTypeNode)(existingFieldNode.node.type, node.type, this.parentTypeName, this.childName);
215
+ const existingFieldContainer = fieldMap.get(this.childName);
216
+ if (existingFieldContainer) {
217
+ this.extractPersistedDirectives(node.directives || [], existingFieldContainer.directives);
218
+ (0, utils_1.setLongestDescriptionForNode)(existingFieldContainer.node, node.description);
219
+ existingFieldContainer.subgraphs.add(this.currentSubgraphName);
220
+ existingFieldContainer.subgraphsByShareable.set(this.currentSubgraphName, isFieldShareable);
221
+ const { typeErrors, typeNode } = (0, type_merging_1.getLeastRestrictiveMergedTypeNode)(existingFieldContainer.node.type, node.type, this.parentTypeName, this.childName);
370
222
  if (typeNode) {
371
- existingFieldNode.node.type = typeNode;
223
+ existingFieldContainer.node.type = typeNode;
372
224
  }
373
225
  else {
374
226
  if (!typeErrors || typeErrors.length < 2) {
375
- throw new Error(''); // TODO this should never happen
227
+ throw (0, errors_1.fieldTypeMergeFatalError)(this.childName);
376
228
  }
377
229
  this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
378
230
  }
379
- this.upsertArgumentsForFieldNode(node, existingFieldNode);
231
+ this.upsertArguments(node, existingFieldContainer.arguments);
380
232
  // If the parent is not an interface and both fields are not shareable, is it is a shareable error
381
- if (!this.isCurrentParentInterface && (!existingFieldNode.isShareable || !isFieldShareable)) {
233
+ if (!this.isCurrentParentInterface && (!existingFieldContainer.isShareable || !isFieldShareable)) {
382
234
  const shareableErrorTypeNames = this.shareableErrorTypeNames.get(this.parentTypeName);
383
235
  if (shareableErrorTypeNames) {
384
236
  shareableErrorTypeNames.add(this.childName);
@@ -389,27 +241,22 @@ class FederationFactory {
389
241
  }
390
242
  return;
391
243
  }
392
- const args = new Map();
393
- this.extractArgumentsFromFieldNode(node, args);
394
244
  this.outputFieldTypeNameSet.add(fieldRootTypeName);
395
245
  fieldMap.set(this.childName, {
396
- appearances: 1,
397
- arguments: args,
246
+ arguments: this.upsertArguments(node, new Map()),
247
+ directives: this.extractPersistedDirectives(node.directives || [], {
248
+ directives: new Map(),
249
+ tags: new Map(),
250
+ }),
398
251
  isShareable: isFieldShareable,
399
252
  node: (0, ast_1.fieldDefinitionNodeToMutable)(node, this.parentTypeName),
400
- rootTypeName: fieldRootTypeName,
253
+ namedTypeName: fieldRootTypeName,
401
254
  subgraphs: new Set([this.currentSubgraphName]),
402
255
  subgraphsByShareable: new Map([[this.currentSubgraphName, isFieldShareable]]),
403
256
  });
404
- if (this.isParentRootType ||
405
- entityParent?.fields.has(this.childName) ||
406
- parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
407
- return;
408
- }
409
- this.addPotentiallyUnresolvableField(parent, this.childName);
410
257
  }
411
258
  upsertValueNode(node) {
412
- const parent = this.parentMap.get(this.parentTypeName);
259
+ const parent = this.parents.get(this.parentTypeName);
413
260
  switch (node.kind) {
414
261
  case graphql_1.Kind.ENUM_VALUE_DEFINITION:
415
262
  if (!parent) {
@@ -420,37 +267,43 @@ class FederationFactory {
420
267
  throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.ENUM_TYPE_DEFINITION, parent.kind);
421
268
  }
422
269
  const enumValues = parent.values;
423
- const enumValue = enumValues.get(this.childName);
424
- if (enumValue) {
425
- enumValue.node.description = enumValue.node.description || node.description;
426
- enumValue.appearances += 1;
270
+ const enumValueContainer = enumValues.get(this.childName);
271
+ if (enumValueContainer) {
272
+ this.extractPersistedDirectives(node.directives || [], enumValueContainer.directives);
273
+ (0, utils_1.setLongestDescriptionForNode)(enumValueContainer.node, node.description);
274
+ enumValueContainer.appearances += 1;
427
275
  return;
428
276
  }
429
277
  enumValues.set(this.childName, {
430
278
  appearances: 1,
279
+ directives: this.extractPersistedDirectives(node.directives || [], {
280
+ directives: new Map(),
281
+ tags: new Map(),
282
+ }),
431
283
  node: (0, ast_1.enumValueDefinitionNodeToMutable)(node),
432
284
  });
433
285
  return;
434
286
  case graphql_1.Kind.INPUT_VALUE_DEFINITION:
435
287
  if (!parent || !this.isParentInputObject) {
436
- // TODO handle directives
288
+ // these are arguments to a directive
437
289
  return;
438
290
  }
439
291
  if (parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
440
292
  throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION, parent.kind);
441
293
  }
442
294
  const inputValues = parent.fields;
443
- const inputValue = inputValues.get(this.childName);
444
- if (inputValue) {
445
- inputValue.appearances += 1;
446
- inputValue.node.description = inputValue.node.description || node.description;
447
- const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(inputValue.node.type, node.type, this.parentTypeName, this.childName);
295
+ const inputValueContainer = inputValues.get(this.childName);
296
+ if (inputValueContainer) {
297
+ this.extractPersistedDirectives(node.directives || [], inputValueContainer.directives);
298
+ inputValueContainer.appearances += 1;
299
+ (0, utils_1.setLongestDescriptionForNode)(inputValueContainer.node, node.description);
300
+ const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(inputValueContainer.node.type, node.type, this.parentTypeName, this.childName);
448
301
  if (typeNode) {
449
- inputValue.node.type = typeNode;
302
+ inputValueContainer.node.type = typeNode;
450
303
  }
451
304
  else {
452
305
  if (!typeErrors || typeErrors.length < 2) {
453
- throw new Error(''); // TODO this should never happen
306
+ throw (0, errors_1.fieldTypeMergeFatalError)(this.childName);
454
307
  }
455
308
  this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
456
309
  }
@@ -461,6 +314,10 @@ class FederationFactory {
461
314
  this.inputFieldTypeNameSet.add(inputValueNamedType);
462
315
  inputValues.set(this.childName, {
463
316
  appearances: 1,
317
+ directives: this.extractPersistedDirectives(node.directives || [], {
318
+ directives: new Map(),
319
+ tags: new Map(),
320
+ }),
464
321
  includeDefaultValue: !!node.defaultValue,
465
322
  node: (0, ast_1.inputValueDefinitionNodeToMutable)(node, this.parentTypeName),
466
323
  });
@@ -471,10 +328,10 @@ class FederationFactory {
471
328
  }
472
329
  upsertParentNode(node) {
473
330
  const parentTypeName = node.name.value;
474
- const parent = this.parentMap.get(parentTypeName);
331
+ const parent = this.parents.get(parentTypeName);
475
332
  if (parent) {
476
- parent.node.description = parent.node.description || node.description;
477
- parent.appearances += 1;
333
+ (0, utils_1.setLongestDescriptionForNode)(parent.node, node.description);
334
+ this.extractPersistedDirectives(node.directives || [], parent.directives);
478
335
  }
479
336
  switch (node.kind) {
480
337
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
@@ -482,10 +339,15 @@ class FederationFactory {
482
339
  if (parent.kind !== node.kind) {
483
340
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
484
341
  }
342
+ parent.appearances += 1;
485
343
  return;
486
344
  }
487
- this.parentMap.set(parentTypeName, {
345
+ this.parents.set(parentTypeName, {
488
346
  appearances: 1,
347
+ directives: this.extractPersistedDirectives(node.directives || [], {
348
+ directives: new Map(),
349
+ tags: new Map(),
350
+ }),
489
351
  values: new Map(),
490
352
  kind: node.kind,
491
353
  node: (0, ast_1.enumTypeDefinitionNodeToMutable)(node),
@@ -496,10 +358,15 @@ class FederationFactory {
496
358
  if (parent.kind !== node.kind) {
497
359
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
498
360
  }
361
+ parent.appearances += 1;
499
362
  return;
500
363
  }
501
- this.parentMap.set(parentTypeName, {
364
+ this.parents.set(parentTypeName, {
502
365
  appearances: 1,
366
+ directives: this.extractPersistedDirectives(node.directives || [], {
367
+ directives: new Map(),
368
+ tags: new Map(),
369
+ }),
503
370
  fields: new Map(),
504
371
  kind: node.kind,
505
372
  node: (0, ast_1.inputObjectTypeDefinitionNodeToMutable)(node),
@@ -516,8 +383,11 @@ class FederationFactory {
516
383
  }
517
384
  const nestedInterfaces = new Set();
518
385
  (0, utils_1.extractInterfaces)(node, nestedInterfaces);
519
- this.parentMap.set(parentTypeName, {
520
- appearances: 1,
386
+ this.parents.set(parentTypeName, {
387
+ directives: this.extractPersistedDirectives(node.directives || [], {
388
+ directives: new Map(),
389
+ tags: new Map(),
390
+ }),
521
391
  fields: new Map(),
522
392
  interfaces: nestedInterfaces,
523
393
  kind: node.kind,
@@ -532,8 +402,11 @@ class FederationFactory {
532
402
  }
533
403
  return;
534
404
  }
535
- this.parentMap.set(parentTypeName, {
536
- appearances: 1,
405
+ this.parents.set(parentTypeName, {
406
+ directives: this.extractPersistedDirectives(node.directives || [], {
407
+ directives: new Map(),
408
+ tags: new Map(),
409
+ }),
537
410
  kind: node.kind,
538
411
  node: (0, ast_1.scalarTypeDefinitionNodeToMutable)(node),
539
412
  });
@@ -544,16 +417,19 @@ class FederationFactory {
544
417
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
545
418
  }
546
419
  (0, utils_1.extractInterfaces)(node, parent.interfaces);
547
- (0, utils_1.extractEntityKeys)(node, parent.entityKeys);
420
+ (0, utils_1.extractEntityKeys)(node, parent.entityKeys, this.errors);
548
421
  parent.subgraphs.add(this.currentSubgraphName);
549
422
  return;
550
423
  }
551
424
  const interfaces = new Set();
552
425
  (0, utils_1.extractInterfaces)(node, interfaces);
553
426
  const entityKeys = new Set();
554
- (0, utils_1.extractEntityKeys)(node, entityKeys);
555
- this.parentMap.set(parentTypeName, {
556
- appearances: 1,
427
+ (0, utils_1.extractEntityKeys)(node, entityKeys, this.errors);
428
+ this.parents.set(parentTypeName, {
429
+ directives: this.extractPersistedDirectives(node.directives || [], {
430
+ directives: new Map(),
431
+ tags: new Map(),
432
+ }),
557
433
  fields: new Map(),
558
434
  entityKeys,
559
435
  interfaces,
@@ -575,8 +451,11 @@ class FederationFactory {
575
451
  node.types?.forEach((member) => parent.members.add(member.name.value));
576
452
  return;
577
453
  }
578
- this.parentMap.set(parentTypeName, {
579
- appearances: 1,
454
+ this.parents.set(parentTypeName, {
455
+ directives: this.extractPersistedDirectives(node.directives || [], {
456
+ directives: new Map(),
457
+ tags: new Map(),
458
+ }),
580
459
  kind: node.kind,
581
460
  members: new Set(node.types?.map((member) => member.name.value)),
582
461
  node: (0, ast_1.unionTypeDefinitionNodeToMutable)(node),
@@ -584,98 +463,66 @@ class FederationFactory {
584
463
  return;
585
464
  }
586
465
  }
587
- upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName = fieldTypeName, hasAbstractParent = false) {
588
- const operationFields = this.rootTypeFieldsByResponseTypeName.get(concreteTypeName);
589
- if (!operationFields) {
590
- this.rootTypeFieldsByResponseTypeName.set(concreteTypeName, new Map([
591
- [
592
- operationFieldPath,
593
- {
594
- inlineFragment: hasAbstractParent ? (0, utils_1.getInlineFragmentString)(concreteTypeName) : '.',
595
- name: fieldName,
596
- parentTypeName: this.parentTypeName,
597
- path: operationFieldPath,
598
- responseType,
599
- rootTypeName: fieldTypeName,
600
- subgraphs: new Set([this.currentSubgraphName]),
601
- },
602
- ],
603
- ]));
604
- return;
605
- }
606
- const operationField = operationFields.get(operationFieldPath);
607
- if (operationField) {
608
- operationField.subgraphs.add(this.currentSubgraphName);
609
- return;
610
- }
611
- operationFields.set(operationFieldPath, {
612
- inlineFragment: hasAbstractParent ? (0, utils_1.getInlineFragmentString)(concreteTypeName) : '.',
613
- name: fieldName,
614
- parentTypeName: this.parentTypeName,
615
- path: operationFieldPath,
616
- responseType,
617
- rootTypeName: fieldTypeName,
618
- subgraphs: new Set([this.currentSubgraphName]),
619
- });
620
- }
621
- upsertAbstractObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeNames) {
622
- for (const concreteTypeName of concreteTypeNames) {
623
- if (!this.graph.hasNode(concreteTypeName)) {
624
- throw (0, errors_1.invalidMultiGraphNodeFatalError)(concreteTypeName); // should never happen
625
- }
626
- if (!this.graphEdges.has(operationFieldPath)) {
627
- this.graph.addEdge(this.parentTypeName, concreteTypeName, { fieldName });
628
- }
629
- // Always upsert the operation field node to record subgraph appearances
630
- this.upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName, true);
631
- }
632
- // Add the path so the edges are not added again
633
- this.graphEdges.add(operationFieldPath);
634
- }
635
- validatePotentiallyUnresolvableFields() {
636
- if (this.sharedRootTypeFieldDependentResponses.size < 1) {
637
- return;
638
- }
639
- for (const [parentTypeName, potentiallyUnresolvableFields] of this.sharedRootTypeFieldDependentResponses) {
640
- for (const potentiallyUnresolvableField of potentiallyUnresolvableFields) {
641
- // There is no issue if the field is resolvable from at least one subgraph
642
- const operationField = potentiallyUnresolvableField.rootTypeField;
643
- const fieldContainer = potentiallyUnresolvableField.fieldContainer;
644
- if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, operationField.subgraphs)) {
645
- continue;
646
- }
647
- const fieldSubgraphs = [...fieldContainer.subgraphs].join('", "');
648
- this.errors.push((0, errors_1.unresolvableFieldError)(operationField, fieldContainer.node.name.value, potentiallyUnresolvableField.fullResolverPaths, fieldSubgraphs, parentTypeName));
649
- }
650
- }
651
- }
652
466
  upsertExtensionNode(node) {
653
467
  const extension = this.extensions.get(this.parentTypeName);
654
468
  if (extension) {
655
469
  if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
656
470
  throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.OBJECT_TYPE_EXTENSION, extension.kind);
657
471
  }
658
- extension.appearances += 1;
659
472
  extension.subgraphs.add(this.currentSubgraphName);
660
473
  (0, utils_1.extractInterfaces)(node, extension.interfaces);
474
+ this.extractPersistedDirectives(node.directives || [], extension.directives);
661
475
  return;
662
476
  }
663
477
  // build a new extension
664
- const interfaces = new Set();
665
- (0, utils_1.extractInterfaces)(node, interfaces);
666
- const entityKeys = new Set();
667
- (0, utils_1.extractEntityKeys)(node, entityKeys);
478
+ const interfaces = (0, utils_1.extractInterfaces)(node, new Set());
479
+ const entityKeys = (0, utils_1.extractEntityKeys)(node, new Set(), this.errors);
668
480
  this.extensions.set(this.parentTypeName, {
669
- appearances: 1,
670
- subgraphs: new Set([this.currentSubgraphName]),
481
+ directives: this.extractPersistedDirectives(node.directives || [], {
482
+ directives: new Map(),
483
+ tags: new Map(),
484
+ }),
485
+ entityKeys,
486
+ fields: new Map(),
487
+ interfaces,
671
488
  isRootType: this.isParentRootType,
672
489
  kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
673
490
  node: (0, ast_1.objectTypeExtensionNodeToMutable)(node),
674
- fields: new Map(),
675
- entityKeys,
676
- interfaces,
491
+ subgraphs: new Set([this.currentSubgraphName]),
677
492
  });
678
493
  }
494
+ isTypeValidImplementation(originalType, implementationType) {
495
+ if (originalType.kind === graphql_1.Kind.NON_NULL_TYPE) {
496
+ if (implementationType.kind !== graphql_1.Kind.NON_NULL_TYPE) {
497
+ return false;
498
+ }
499
+ return this.isTypeValidImplementation(originalType.type, implementationType.type);
500
+ }
501
+ if (implementationType.kind === graphql_1.Kind.NON_NULL_TYPE) {
502
+ return this.isTypeValidImplementation(originalType, implementationType.type);
503
+ }
504
+ switch (originalType.kind) {
505
+ case graphql_1.Kind.NAMED_TYPE:
506
+ if (implementationType.kind === graphql_1.Kind.NAMED_TYPE) {
507
+ const originalTypeName = originalType.name.value;
508
+ const implementationTypeName = implementationType.name.value;
509
+ if (originalTypeName === implementationTypeName) {
510
+ return true;
511
+ }
512
+ const concreteTypes = this.abstractToConcreteTypeNames.get(originalTypeName);
513
+ if (!concreteTypes) {
514
+ return false;
515
+ }
516
+ return concreteTypes.has(implementationTypeName);
517
+ }
518
+ return false;
519
+ default:
520
+ if (implementationType.kind === graphql_1.Kind.LIST_TYPE) {
521
+ return this.isTypeValidImplementation(originalType.type, implementationType.type);
522
+ }
523
+ return false;
524
+ }
525
+ }
679
526
  getAndValidateImplementedInterfaces(container) {
680
527
  const interfaces = [];
681
528
  if (container.interfaces.size < 1) {
@@ -684,7 +531,11 @@ class FederationFactory {
684
531
  const implementationErrorsMap = new Map();
685
532
  for (const interfaceName of container.interfaces) {
686
533
  interfaces.push((0, utils_1.stringToNamedTypeNode)(interfaceName));
687
- const interfaceContainer = (0, utils_2.getOrThrowError)(this.parentMap, interfaceName);
534
+ const interfaceContainer = this.parents.get(interfaceName);
535
+ if (!interfaceContainer) {
536
+ this.errors.push((0, errors_1.undefinedTypeError)(interfaceName));
537
+ continue;
538
+ }
688
539
  if (interfaceContainer.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
689
540
  throw (0, errors_1.incompatibleParentKindFatalError)(interfaceName, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, interfaceContainer.kind);
690
541
  }
@@ -708,7 +559,7 @@ class FederationFactory {
708
559
  unimplementedArguments: new Set(),
709
560
  };
710
561
  // The implemented field type must be equally or more restrictive than the original interface field type
711
- if (!(0, utils_2.isTypeValidImplementation)(interfaceField.node.type, containerField.node.type)) {
562
+ if (!this.isTypeValidImplementation(interfaceField.node.type, containerField.node.type)) {
712
563
  hasErrors = true;
713
564
  hasNestedErrors = true;
714
565
  invalidFieldImplementation.implementedResponseType = (0, merge_1.printTypeNode)(containerField.node.type);
@@ -756,10 +607,233 @@ class FederationFactory {
756
607
  }
757
608
  }
758
609
  if (implementationErrorsMap.size) {
759
- this.errors.push((0, errors_1.unimplementedInterfaceFieldsError)(container.node.name.value, (0, utils_2.kindToTypeString)(container.kind), implementationErrorsMap));
610
+ this.errors.push((0, errors_1.unimplementedInterfaceFieldsError)(container.node.name.value, (0, utils_3.kindToTypeString)(container.kind), implementationErrorsMap));
760
611
  }
761
612
  return interfaces;
762
613
  }
614
+ mergeArguments(container, args, errors, argumentNames) {
615
+ for (const argumentContainer of container.arguments.values()) {
616
+ const missingSubgraphs = (0, utils_3.getEntriesNotInHashSet)(container.subgraphs, argumentContainer.subgraphs);
617
+ const argumentName = argumentContainer.node.name.value;
618
+ if (missingSubgraphs.length > 0) {
619
+ // Required arguments must be defined in all subgraphs that define the field
620
+ if (argumentContainer.requiredSubgraphs.size > 0) {
621
+ errors.push({
622
+ argumentName,
623
+ missingSubgraphs,
624
+ requiredSubgraphs: [...argumentContainer.requiredSubgraphs]
625
+ });
626
+ }
627
+ // If the argument is always optional, but it's not defined in all subgraphs that define the field,
628
+ // the argument should not be included in the federated graph
629
+ continue;
630
+ }
631
+ argumentContainer.node.defaultValue = argumentContainer.includeDefaultValue
632
+ ? argumentContainer.node.defaultValue : undefined;
633
+ args.push(argumentContainer.node);
634
+ if (argumentNames) {
635
+ argumentNames.push(argumentName);
636
+ }
637
+ }
638
+ }
639
+ addValidExecutableDirectiveDefinition(directiveName, directiveContainer, definitions) {
640
+ if (!this.executableDirectives.has(directiveName)) {
641
+ return;
642
+ }
643
+ if (this.subgraphs.length !== directiveContainer.subgraphs.size) {
644
+ return;
645
+ }
646
+ directiveContainer.node.locations = (0, utils_1.setToNameNodeArray)(directiveContainer.executableLocations);
647
+ if (!directiveContainer.arguments) {
648
+ definitions.push(directiveContainer.node);
649
+ return;
650
+ }
651
+ const args = [];
652
+ const errors = [];
653
+ this.mergeArguments(directiveContainer, args, errors);
654
+ if (errors.length > 0) {
655
+ this.errors.push((0, errors_1.invalidRequiredArgumentsError)(string_constants_1.DIRECTIVE_DEFINITION, directiveName, errors));
656
+ return;
657
+ }
658
+ directiveContainer.node.arguments = args;
659
+ definitions.push(directiveContainer.node);
660
+ }
661
+ getMergedFieldDefinitionNode(fieldContainer, parentTypeName) {
662
+ if (!fieldContainer.arguments) {
663
+ return fieldContainer.node;
664
+ }
665
+ (0, utils_1.pushPersistedDirectivesToNode)(fieldContainer);
666
+ const fieldName = fieldContainer.node.name.value;
667
+ const fieldPath = `${parentTypeName}.${fieldName}`;
668
+ const args = [];
669
+ const errors = [];
670
+ const argumentNames = [];
671
+ this.mergeArguments(fieldContainer, args, errors, argumentNames);
672
+ if (errors.length > 0) {
673
+ this.errors.push((0, errors_1.invalidRequiredArgumentsError)(string_constants_1.FIELD, fieldPath, errors));
674
+ }
675
+ else if (argumentNames.length > 0) {
676
+ this.argumentConfigurations.push({
677
+ argumentNames,
678
+ fieldName,
679
+ typeName: parentTypeName
680
+ });
681
+ }
682
+ fieldContainer.node.arguments = args;
683
+ return fieldContainer.node;
684
+ }
685
+ // tags with the same name string are merged
686
+ mergeTagDirectives(directive, map) {
687
+ // the directive has been validated in the normalizer
688
+ if (!directive.arguments || directive.arguments.length !== 1) {
689
+ this.errors.push(errors_1.invalidTagDirectiveError); // should never happen
690
+ return;
691
+ }
692
+ const nameArgument = directive.arguments[0].value;
693
+ if (nameArgument.kind !== graphql_1.Kind.STRING) {
694
+ this.errors.push(errors_1.invalidTagDirectiveError); // should never happen
695
+ return;
696
+ }
697
+ map.set(nameArgument.value, directive);
698
+ }
699
+ extractPersistedDirectives(directives, container) {
700
+ if (directives.length < 1) {
701
+ return container;
702
+ }
703
+ for (const directive of directives) {
704
+ const directiveName = directive.name.value;
705
+ if (!this.persistedDirectives.has(directiveName)) {
706
+ continue;
707
+ }
708
+ if (directiveName === string_constants_1.TAG) {
709
+ this.mergeTagDirectives(directive, container.tags);
710
+ continue;
711
+ }
712
+ const existingDirectives = container.directives.get(directiveName);
713
+ if (!existingDirectives) {
714
+ container.directives.set(directiveName, [directive]);
715
+ continue;
716
+ }
717
+ // Naïvely ignore non-repeatable directives
718
+ const definition = (0, utils_3.getOrThrowError)(this.directiveDefinitions, directiveName, 'directiveDefinitions');
719
+ if (!definition.node.repeatable) {
720
+ continue;
721
+ }
722
+ existingDirectives.push(directive);
723
+ }
724
+ return container;
725
+ }
726
+ entityAncestor(entityAncestors, fieldSubgraphs, parentTypeName) {
727
+ if (!this.graph.hasNode(parentTypeName)) {
728
+ return false;
729
+ }
730
+ for (const entityAncestorName of entityAncestors) {
731
+ const path = `${entityAncestorName}.${parentTypeName}`;
732
+ if (this.graphPaths.get(path)) {
733
+ return true;
734
+ }
735
+ if (entityAncestorName === parentTypeName) {
736
+ const hasOverlap = (0, utils_3.doSetsHaveAnyOverlap)(fieldSubgraphs, (0, utils_3.getOrThrowError)(this.entities, entityAncestorName, string_constants_1.ENTITIES).subgraphs);
737
+ this.graphPaths.set(path, hasOverlap);
738
+ return hasOverlap;
739
+ }
740
+ if ((0, utils_3.hasSimplePath)(this.graph, entityAncestorName, parentTypeName)) {
741
+ this.graphPaths.set(path, true);
742
+ return true;
743
+ }
744
+ this.graphPaths.set(path, false);
745
+ }
746
+ return false;
747
+ }
748
+ evaluateResolvabilityOfObject(parentContainer, rootTypeFieldData, currentFieldPath, evaluatedObjectLikes, entityAncestors, isParentAbstract = false) {
749
+ const parentTypeName = parentContainer.node.name.value;
750
+ if (evaluatedObjectLikes.has(parentTypeName)) {
751
+ return;
752
+ }
753
+ for (const [fieldName, fieldContainer] of parentContainer.fields) {
754
+ const fieldNamedTypeName = fieldContainer.namedTypeName;
755
+ if (string_constants_1.ROOT_TYPES.has(fieldNamedTypeName)) {
756
+ continue;
757
+ }
758
+ // Avoid an infinite loop with self-referential objects
759
+ if (evaluatedObjectLikes.has(fieldNamedTypeName)) {
760
+ continue;
761
+ }
762
+ const isFieldResolvable = (0, utils_3.doSetsHaveAnyOverlap)(rootTypeFieldData.subgraphs, fieldContainer.subgraphs);
763
+ const newCurrentFieldPath = currentFieldPath + (isParentAbstract ? ' ' : '.') + fieldName;
764
+ const entity = this.entities.get(fieldNamedTypeName);
765
+ if (isFieldResolvable || this.entityAncestor(entityAncestors, fieldContainer.subgraphs, parentTypeName)) {
766
+ // The base scalars are not in this.parentMap
767
+ if (constants_1.BASE_SCALARS.has(fieldNamedTypeName)) {
768
+ continue;
769
+ }
770
+ const childContainer = (0, utils_3.getOrThrowError)(this.parents, fieldNamedTypeName, string_constants_1.PARENTS);
771
+ switch (childContainer.kind) {
772
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION:
773
+ // intentional fallthrough
774
+ case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
775
+ continue;
776
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
777
+ this.evaluateResolvabilityOfObject(childContainer, rootTypeFieldData, newCurrentFieldPath, new Set([...evaluatedObjectLikes, parentTypeName]), entity ? [...entityAncestors, fieldNamedTypeName] : [...entityAncestors]);
778
+ continue;
779
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
780
+ // intentional fallthrough
781
+ case graphql_1.Kind.UNION_TYPE_DEFINITION:
782
+ this.evaluateResolvabilityOfAbstractType(fieldNamedTypeName, childContainer.kind, rootTypeFieldData, newCurrentFieldPath, new Set([...evaluatedObjectLikes, parentTypeName]), entity ? [...entityAncestors, fieldNamedTypeName] : [...entityAncestors], fieldContainer.subgraphs);
783
+ continue;
784
+ default:
785
+ this.errors.push((0, errors_1.unexpectedObjectResponseType)(newCurrentFieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
786
+ continue;
787
+ }
788
+ }
789
+ if (constants_1.BASE_SCALARS.has(fieldNamedTypeName)) {
790
+ this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath, parentTypeName));
791
+ continue;
792
+ }
793
+ const childContainer = (0, utils_3.getOrThrowError)(this.parents, fieldNamedTypeName, string_constants_1.PARENTS);
794
+ switch (childContainer.kind) {
795
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION:
796
+ // intentional fallthrough
797
+ case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
798
+ this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath, parentTypeName));
799
+ continue;
800
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
801
+ // intentional fallthrough
802
+ case graphql_1.Kind.UNION_TYPE_DEFINITION:
803
+ // intentional fallthrough
804
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
805
+ this.errors.push((0, errors_1.unresolvableFieldError)(rootTypeFieldData, fieldName, [...fieldContainer.subgraphs], newCurrentFieldPath + string_constants_1.SELECTION_REPRESENTATION, parentTypeName));
806
+ continue;
807
+ default:
808
+ this.errors.push((0, errors_1.unexpectedObjectResponseType)(newCurrentFieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
809
+ }
810
+ }
811
+ }
812
+ evaluateResolvabilityOfAbstractType(fieldNamedTypeName, fieldNamedTypeKind, rootTypeFieldData, currentFieldPath, evaluatedObjectLikes, entityAncestors, parentSubgraphs) {
813
+ if (evaluatedObjectLikes.has(fieldNamedTypeName)) {
814
+ return;
815
+ }
816
+ const concreteTypeNames = this.abstractToConcreteTypeNames.get(fieldNamedTypeName);
817
+ if (!concreteTypeNames) {
818
+ (0, errors_1.noConcreteTypesForAbstractTypeError)((0, utils_3.kindToTypeString)(fieldNamedTypeKind), fieldNamedTypeName);
819
+ return;
820
+ }
821
+ for (const concreteTypeName of concreteTypeNames) {
822
+ if (evaluatedObjectLikes.has(concreteTypeName)) {
823
+ continue;
824
+ }
825
+ const concreteParentContainer = (0, utils_3.getOrThrowError)(this.parents, concreteTypeName, string_constants_1.PARENTS);
826
+ if (concreteParentContainer.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
827
+ // throw
828
+ continue;
829
+ }
830
+ if (!(0, utils_3.doSetsHaveAnyOverlap)(concreteParentContainer.subgraphs, parentSubgraphs)) {
831
+ continue;
832
+ }
833
+ const entity = this.entities.get(concreteTypeName);
834
+ this.evaluateResolvabilityOfObject(concreteParentContainer, rootTypeFieldData, currentFieldPath + ` ... on ` + concreteTypeName, new Set([...evaluatedObjectLikes, fieldNamedTypeName]), entity ? [...entityAncestors, concreteTypeName] : [...entityAncestors], true);
835
+ }
836
+ }
763
837
  federate() {
764
838
  this.populateMultiGraphAndRenameOperations(this.subgraphs);
765
839
  const factory = this;
@@ -768,16 +842,20 @@ class FederationFactory {
768
842
  this.currentSubgraphName = subgraph.name;
769
843
  (0, subgraph_1.walkSubgraphToFederate)(subgraph.definitions, factory);
770
844
  }
771
- this.validatePotentiallyUnresolvableFields();
772
845
  const definitions = [];
773
- for (const definition of this.directiveDefinitions.values()) {
774
- definitions.push(definition);
846
+ for (const [directiveName, directiveContainer] of this.directiveDefinitions) {
847
+ if (this.persistedDirectives.has(directiveName)) {
848
+ definitions.push(directiveContainer.node);
849
+ continue;
850
+ }
851
+ // The definitions must be present in all subgraphs to kept in the federated graph
852
+ this.addValidExecutableDirectiveDefinition(directiveName, directiveContainer, definitions);
775
853
  }
776
854
  for (const [typeName, extension] of this.extensions) {
777
- if (extension.isRootType && !this.parentMap.has(typeName)) {
855
+ if (extension.isRootType && !this.parents.has(typeName)) {
778
856
  this.upsertParentNode((0, ast_1.objectTypeExtensionNodeToMutableDefinitionNode)(extension.node));
779
857
  }
780
- const baseObject = this.parentMap.get(typeName);
858
+ const baseObject = this.parents.get(typeName);
781
859
  if (!baseObject) {
782
860
  this.errors.push((0, errors_1.noBaseTypeExtensionError)(typeName));
783
861
  continue;
@@ -785,152 +863,238 @@ class FederationFactory {
785
863
  if (baseObject.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
786
864
  throw (0, errors_1.incompatibleParentKindFatalError)(typeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, baseObject.kind);
787
865
  }
788
- // TODO check directives
789
- for (const [fieldName, field] of extension.fields) {
790
- const baseField = baseObject.fields.get(fieldName);
791
- if (!baseField) {
792
- baseObject.fields.set(fieldName, field);
866
+ for (const [extensionFieldName, extensionFieldContainer] of extension.fields) {
867
+ const baseFieldContainer = baseObject.fields.get(extensionFieldName);
868
+ if (!baseFieldContainer) {
869
+ baseObject.fields.set(extensionFieldName, extensionFieldContainer);
793
870
  continue;
794
871
  }
795
- if (baseField.isShareable && field.isShareable) {
872
+ if (baseFieldContainer.isShareable && extensionFieldContainer.isShareable) {
873
+ (0, utils_1.setLongestDescriptionForNode)(baseFieldContainer.node, extensionFieldContainer.node.description);
874
+ (0, utils_3.addIterableValuesToSet)(extensionFieldContainer.subgraphs, baseFieldContainer.subgraphs);
796
875
  continue;
797
876
  }
798
877
  const parent = this.shareableErrorTypeNames.get(typeName);
799
878
  if (parent) {
800
- parent.add(fieldName);
879
+ parent.add(extensionFieldName);
801
880
  continue;
802
881
  }
803
- this.shareableErrorTypeNames.set(typeName, new Set([fieldName]));
882
+ this.shareableErrorTypeNames.set(typeName, new Set([extensionFieldName]));
804
883
  }
805
884
  for (const interfaceName of extension.interfaces) {
806
885
  baseObject.interfaces.add(interfaceName);
807
886
  }
808
887
  }
809
888
  for (const [parentTypeName, children] of this.shareableErrorTypeNames) {
810
- const parent = (0, utils_2.getOrThrowError)(this.parentMap, parentTypeName);
889
+ const parent = (0, utils_3.getOrThrowError)(this.parents, parentTypeName, string_constants_1.PARENTS);
811
890
  if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
812
891
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, parent.kind);
813
892
  }
814
893
  this.errors.push((0, errors_1.shareableFieldDefinitionsError)(parent, children));
815
894
  }
816
- for (const parent of this.parentMap.values()) {
817
- const parentName = parent.node.name.value;
818
- switch (parent.kind) {
895
+ const objectLikeContainersWithInterfaces = [];
896
+ for (const parentContainer of this.parents.values()) {
897
+ const parentTypeName = parentContainer.node.name.value;
898
+ switch (parentContainer.kind) {
819
899
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
820
900
  const values = [];
821
- const mergeMethod = this.getEnumMergeMethod(parentName);
822
- for (const value of parent.values.values()) {
901
+ const mergeMethod = this.getEnumMergeMethod(parentTypeName);
902
+ for (const enumValueContainer of parentContainer.values.values()) {
903
+ (0, utils_1.pushPersistedDirectivesToNode)(enumValueContainer);
823
904
  switch (mergeMethod) {
824
- case utils_1.MergeMethod.CONSISTENT:
825
- if (value.appearances < parent.appearances) {
826
- this.errors.push((0, errors_1.incompatibleSharedEnumError)(parentName));
905
+ case utils_2.MergeMethod.CONSISTENT:
906
+ if (enumValueContainer.appearances < parentContainer.appearances) {
907
+ this.errors.push((0, errors_1.incompatibleSharedEnumError)(parentTypeName));
827
908
  }
828
- values.push(value.node);
909
+ values.push(enumValueContainer.node);
829
910
  break;
830
- case utils_1.MergeMethod.INTERSECTION:
831
- if (value.appearances === parent.appearances) {
832
- values.push(value.node);
911
+ case utils_2.MergeMethod.INTERSECTION:
912
+ if (enumValueContainer.appearances === parentContainer.appearances) {
913
+ values.push(enumValueContainer.node);
833
914
  }
834
915
  break;
835
916
  default:
836
- values.push(value.node);
917
+ values.push(enumValueContainer.node);
837
918
  break;
838
919
  }
839
920
  }
840
- parent.node.values = values;
841
- definitions.push(parent.node);
921
+ parentContainer.node.values = values;
922
+ definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
842
923
  break;
843
924
  case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
844
925
  const inputValues = [];
845
- for (const value of parent.fields.values()) {
846
- if (parent.appearances === value.appearances) {
847
- inputValues.push(value.node);
926
+ for (const inputValueContainer of parentContainer.fields.values()) {
927
+ (0, utils_1.pushPersistedDirectivesToNode)(inputValueContainer);
928
+ if (parentContainer.appearances === inputValueContainer.appearances) {
929
+ inputValues.push(inputValueContainer.node);
848
930
  }
849
- else if ((0, type_merging_1.isTypeRequired)(value.node.type)) {
850
- // TODO append to errors
851
- throw (0, errors_1.federationRequiredInputFieldError)(parentName, value.node.name.value);
931
+ else if ((0, type_merging_1.isTypeRequired)(inputValueContainer.node.type)) {
932
+ this.errors.push((0, errors_1.federationRequiredInputFieldError)(parentTypeName, inputValueContainer.node.name.value));
933
+ break;
852
934
  }
853
935
  }
854
- parent.node.fields = inputValues;
855
- definitions.push(parent.node);
936
+ parentContainer.node.fields = inputValues;
937
+ definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
856
938
  break;
857
939
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
858
940
  const interfaceFields = [];
859
- for (const field of parent.fields.values()) {
860
- if (field.arguments) {
861
- const args = [];
862
- for (const arg of field.arguments.values()) {
863
- arg.node.defaultValue = arg.includeDefaultValue ? arg.node.defaultValue : undefined;
864
- args.push(arg.node);
865
- }
866
- field.node.arguments = args;
867
- }
868
- interfaceFields.push(field.node);
941
+ for (const fieldContainer of parentContainer.fields.values()) {
942
+ interfaceFields.push(this.getMergedFieldDefinitionNode(fieldContainer, parentTypeName));
943
+ }
944
+ parentContainer.node.fields = interfaceFields;
945
+ (0, utils_1.pushPersistedDirectivesToNode)(parentContainer);
946
+ // Interface implementations can only be evaluated after they've been fully merged
947
+ if (parentContainer.interfaces.size > 0) {
948
+ objectLikeContainersWithInterfaces.push(parentContainer);
869
949
  }
870
- const otherInterfaces = [];
871
- for (const iFace of parent.interfaces) {
872
- otherInterfaces.push({
873
- kind: graphql_1.Kind.NAMED_TYPE,
874
- name: {
875
- kind: graphql_1.Kind.NAME,
876
- value: iFace,
877
- },
878
- });
950
+ else {
951
+ definitions.push(parentContainer.node);
879
952
  }
880
- parent.node.interfaces = otherInterfaces;
881
- parent.node.fields = interfaceFields;
882
- definitions.push(parent.node);
883
953
  break;
884
954
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
885
955
  const fields = [];
886
- for (const field of parent.fields.values()) {
887
- if (field.arguments) {
888
- const args = [];
889
- for (const arg of field.arguments.values()) {
890
- arg.node.defaultValue = arg.includeDefaultValue ? arg.node.defaultValue : undefined;
891
- args.push(arg.node);
892
- }
893
- field.node.arguments = args;
894
- }
895
- fields.push(field.node);
956
+ for (const fieldContainer of parentContainer.fields.values()) {
957
+ fields.push(this.getMergedFieldDefinitionNode(fieldContainer, parentTypeName));
958
+ }
959
+ parentContainer.node.fields = fields;
960
+ (0, utils_1.pushPersistedDirectivesToNode)(parentContainer);
961
+ // Interface implementations can only be evaluated after they've been fully merged
962
+ if (parentContainer.interfaces.size > 0) {
963
+ objectLikeContainersWithInterfaces.push(parentContainer);
964
+ }
965
+ else {
966
+ definitions.push(parentContainer.node);
896
967
  }
897
- parent.node.fields = fields;
898
- parent.node.interfaces = this.getAndValidateImplementedInterfaces(parent);
899
- definitions.push(parent.node);
900
968
  break;
901
969
  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
902
- definitions.push(parent.node);
970
+ definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
903
971
  break;
904
972
  case graphql_1.Kind.UNION_TYPE_DEFINITION:
905
973
  const types = [];
906
- for (const member of parent.members) {
907
- types.push({
908
- kind: graphql_1.Kind.NAMED_TYPE,
909
- name: {
910
- kind: graphql_1.Kind.NAME,
911
- value: member,
912
- },
913
- });
974
+ for (const memberName of parentContainer.members) {
975
+ types.push((0, utils_1.stringToNamedTypeNode)(memberName));
914
976
  }
915
- parent.node.types = types;
916
- definitions.push(parent.node);
977
+ parentContainer.node.types = types;
978
+ definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
917
979
  break;
918
980
  }
919
981
  }
982
+ for (const container of objectLikeContainersWithInterfaces) {
983
+ container.node.interfaces = this.getAndValidateImplementedInterfaces(container);
984
+ definitions.push(container.node);
985
+ }
986
+ if (!this.parents.has(string_constants_1.QUERY)) {
987
+ this.errors.push(errors_1.noQueryRootTypeError);
988
+ }
989
+ // return any composition errors before checking whether all fields are resolvable
920
990
  if (this.errors.length > 0) {
921
- return {
922
- errors: this.errors,
923
- };
991
+ return { errors: this.errors };
992
+ }
993
+ for (const rootTypeName of string_constants_1.ROOT_TYPES) {
994
+ const rootTypeContainer = this.parents.get(rootTypeName);
995
+ if (!rootTypeContainer || rootTypeContainer.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
996
+ continue;
997
+ }
998
+ // If a root type field returns a Scalar or Enum, track it so that it is not evaluated it again
999
+ const evaluatedNamedTypes = new Set(constants_1.BASE_SCALARS);
1000
+ for (const [rootTypeFieldName, rootTypeFieldContainer] of rootTypeContainer.fields) {
1001
+ const rootTypeFieldNamedTypeName = rootTypeFieldContainer.namedTypeName;
1002
+ if (evaluatedNamedTypes.has(rootTypeFieldNamedTypeName)) {
1003
+ continue;
1004
+ }
1005
+ const childContainer = (0, utils_3.getOrThrowError)(this.parents, rootTypeFieldNamedTypeName, string_constants_1.PARENTS);
1006
+ const fieldPath = `${rootTypeName}.${rootTypeFieldName}`;
1007
+ const rootTypeFieldData = {
1008
+ fieldName: rootTypeFieldName,
1009
+ fieldTypeNodeString: (0, merge_1.printTypeNode)(rootTypeFieldContainer.node.type),
1010
+ path: fieldPath,
1011
+ typeName: rootTypeName,
1012
+ subgraphs: rootTypeFieldContainer.subgraphs,
1013
+ };
1014
+ const evaluatedObjectLikes = new Set();
1015
+ switch (childContainer.kind) {
1016
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION:
1017
+ // intentional fallthrough
1018
+ case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
1019
+ // Root type fields whose response type is an Enums and Scalars will always be resolvable
1020
+ // Consequently, subsequent checks can be skipped
1021
+ evaluatedNamedTypes.add(rootTypeFieldNamedTypeName);
1022
+ continue;
1023
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
1024
+ this.evaluateResolvabilityOfObject(childContainer, rootTypeFieldData, fieldPath, evaluatedObjectLikes, this.entities.has(rootTypeFieldNamedTypeName) ? [rootTypeFieldNamedTypeName] : []);
1025
+ continue;
1026
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
1027
+ // intentional fallthrough
1028
+ case graphql_1.Kind.UNION_TYPE_DEFINITION:
1029
+ this.evaluateResolvabilityOfAbstractType(rootTypeFieldNamedTypeName, childContainer.kind, rootTypeFieldData, fieldPath, evaluatedObjectLikes, this.entities.has(rootTypeFieldNamedTypeName) ? [rootTypeFieldNamedTypeName] : [], rootTypeFieldContainer.subgraphs);
1030
+ continue;
1031
+ default:
1032
+ this.errors.push((0, errors_1.unexpectedObjectResponseType)(fieldPath, (0, utils_3.kindToTypeString)(childContainer.kind)));
1033
+ }
1034
+ }
1035
+ }
1036
+ if (this.errors.length > 0) {
1037
+ return { errors: this.errors };
924
1038
  }
925
1039
  const newAst = {
926
1040
  kind: graphql_1.Kind.DOCUMENT,
927
1041
  definitions,
928
1042
  };
929
1043
  return {
930
- federatedGraphAST: newAst,
931
- federatedGraphSchema: (0, graphql_1.buildASTSchema)(newAst),
1044
+ federationResult: {
1045
+ argumentConfigurations: this.argumentConfigurations,
1046
+ federatedGraphAST: newAst,
1047
+ federatedGraphSchema: (0, graphql_1.buildASTSchema)(newAst),
1048
+ }
932
1049
  };
933
1050
  }
934
1051
  }
935
1052
  exports.FederationFactory = FederationFactory;
1053
+ function federateSubgraphs(subgraphs) {
1054
+ if (subgraphs.length < 1) {
1055
+ throw errors_1.minimumSubgraphRequirementError;
1056
+ }
1057
+ const normalizedSubgraphs = [];
1058
+ const validationErrors = [];
1059
+ const subgraphNames = new Set();
1060
+ const nonUniqueSubgraphNames = new Set();
1061
+ const invalidNameErrorMessages = [];
1062
+ for (let i = 0; i < subgraphs.length; i++) {
1063
+ const subgraph = subgraphs[i];
1064
+ const name = subgraph.name || `subgraph-${i}-${Date.now()}`;
1065
+ if (!subgraph.name) {
1066
+ invalidNameErrorMessages.push((0, errors_1.invalidSubgraphNameErrorMessage)(i, name));
1067
+ }
1068
+ else {
1069
+ (0, subgraph_1.validateSubgraphName)(subgraph.name, subgraphNames, nonUniqueSubgraphNames);
1070
+ }
1071
+ const { errors, normalizationResult } = (0, normalization_factory_1.normalizeSubgraph)(subgraph.definitions);
1072
+ if (errors) {
1073
+ validationErrors.push((0, errors_1.subgraphValidationError)(name, errors));
1074
+ continue;
1075
+ }
1076
+ if (!normalizationResult) {
1077
+ validationErrors.push((0, errors_1.subgraphValidationError)(name, [errors_1.subgraphValidationFailureErrorMessage]));
1078
+ continue;
1079
+ }
1080
+ normalizedSubgraphs.push({
1081
+ definitions: normalizationResult.subgraphAST,
1082
+ isVersionTwo: normalizationResult.isVersionTwo,
1083
+ name,
1084
+ operationTypes: normalizationResult.operationTypes,
1085
+ url: subgraph.url,
1086
+ });
1087
+ }
1088
+ const allErrors = [];
1089
+ if (invalidNameErrorMessages.length > 0 || nonUniqueSubgraphNames.size > 0) {
1090
+ allErrors.push((0, errors_1.invalidSubgraphNamesError)([...nonUniqueSubgraphNames], invalidNameErrorMessages));
1091
+ }
1092
+ allErrors.push(...validationErrors);
1093
+ if (allErrors.length > 0) {
1094
+ return { errors: allErrors };
1095
+ }
1096
+ const federationFactory = new FederationFactory(normalizedSubgraphs);
1097
+ return federationFactory.federate();
1098
+ }
1099
+ exports.federateSubgraphs = federateSubgraphs;
936
1100
  //# sourceMappingURL=federation-factory.js.map