@wundergraph/composition 0.5.2 → 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 -22
  11. package/dist/federation/federation-factory.js +611 -429
  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 +13 -1
  33. package/dist/utils/string-constants.js +15 -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,78 +1,36 @@
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();
75
32
  graphEdges = new Set();
33
+ graphPaths = new Map();
76
34
  inputFieldTypeNameSet = new Set();
77
35
  isCurrentParentEntity = false;
78
36
  isCurrentParentInterface = false;
@@ -81,10 +39,8 @@ class FederationFactory {
81
39
  isParentRootType = false;
82
40
  isParentInputObject = false;
83
41
  outputFieldTypeNameSet = new Set();
84
- parentMap = new Map();
85
- rootTypeFieldsByResponseTypeName = new Map();
42
+ parents = new Map();
86
43
  rootTypeNames = new Set([string_constants_1.DEFAULT_MUTATION, string_constants_1.DEFAULT_QUERY, string_constants_1.DEFAULT_SUBSCRIPTION]);
87
- sharedRootTypeFieldDependentResponses = new Map();
88
44
  subgraphs = [];
89
45
  shareableErrorTypeNames = new Map();
90
46
  constructor(subgraphs) {
@@ -95,28 +51,25 @@ class FederationFactory {
95
51
  }
96
52
  upsertEntity(node) {
97
53
  const typeName = node.name.value;
98
- const entity = this.entityMap.get(typeName);
54
+ const entity = this.entities.get(typeName);
99
55
  if (entity) {
100
- (0, utils_1.extractEntityKeys)(node, entity.keys);
56
+ (0, utils_1.extractEntityKeys)(node, entity.keys, this.errors);
101
57
  entity.subgraphs.add(this.currentSubgraphName);
102
58
  return;
103
59
  }
104
- this.entityMap.set(typeName, {
60
+ this.entities.set(typeName, {
105
61
  fields: new Set(),
106
- keys: (0, utils_1.extractEntityKeys)(node, new Set()),
62
+ keys: (0, utils_1.extractEntityKeys)(node, new Set(), this.errors),
107
63
  subgraphs: new Set([this.currentSubgraphName]),
108
64
  });
109
65
  }
110
66
  populateMultiGraphAndRenameOperations(subgraphs) {
111
67
  for (const subgraph of subgraphs) {
112
68
  this.currentSubgraphName = subgraph.name;
113
- (0, subgraph_1.walkSubgraphToCollectObjects)(this, subgraph);
114
- (0, subgraph_1.walkSubgraphToCollectOperationsAndFields)(this, subgraph);
69
+ (0, subgraph_1.walkSubgraphToCollectObjectLikesAndDirectiveDefinitions)(this, subgraph);
70
+ (0, subgraph_1.walkSubgraphToCollectFields)(this, subgraph);
115
71
  }
116
72
  }
117
- isParentInterface(parent) {
118
- return parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION;
119
- }
120
73
  isFieldEntityKey(parent) {
121
74
  if (parent.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || parent.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
122
75
  return parent.entityKeys.has(this.childName);
@@ -126,11 +79,11 @@ class FederationFactory {
126
79
  getEnumMergeMethod(enumName) {
127
80
  if (this.inputFieldTypeNameSet.has(enumName) || this.argumentTypeNameSet.has(enumName)) {
128
81
  if (this.outputFieldTypeNameSet.has(enumName)) {
129
- return utils_1.MergeMethod.CONSISTENT;
82
+ return utils_2.MergeMethod.CONSISTENT;
130
83
  }
131
- return utils_1.MergeMethod.INTERSECTION;
84
+ return utils_2.MergeMethod.INTERSECTION;
132
85
  }
133
- return utils_1.MergeMethod.UNION;
86
+ return utils_2.MergeMethod.UNION;
134
87
  }
135
88
  validateArgumentDefaultValues(argName, existingDefaultValue, newDefaultValue) {
136
89
  if (existingDefaultValue.kind !== newDefaultValue.kind) {
@@ -148,6 +101,7 @@ class FederationFactory {
148
101
  existingArg.includeDefaultValue = false;
149
102
  return;
150
103
  }
104
+ const argumentName = existingArg.node.name.value;
151
105
  const existingDefaultValue = existingArg.node.defaultValue;
152
106
  switch (existingDefaultValue.kind) {
153
107
  case graphql_1.Kind.LIST: // TODO
@@ -162,92 +116,54 @@ class FederationFactory {
162
116
  case graphql_1.Kind.FLOAT:
163
117
  case graphql_1.Kind.INT:
164
118
  case graphql_1.Kind.STRING:
165
- this.validateArgumentDefaultValues(existingArg.node.name.value, existingDefaultValue, newDefaultValue);
119
+ this.validateArgumentDefaultValues(argumentName, existingDefaultValue, newDefaultValue);
166
120
  break;
167
121
  default:
168
- throw new Error('Unexpected argument type'); // TODO
122
+ throw (0, errors_1.unexpectedArgumentKindFatalError)(argumentName, this.childName);
169
123
  }
170
124
  }
171
- upsertArgumentsForFieldNode(node, existingFieldNode) {
172
- if (!node.arguments) {
173
- return;
174
- }
175
- for (const arg of node.arguments) {
176
- const argName = arg.name.value;
177
- const argPath = `${node.name.value}(${argName}...)`;
178
- this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, arg.type));
179
- const existingArg = existingFieldNode.arguments.get(argName);
180
- if (existingArg) {
181
- existingArg.appearances += 1;
182
- existingArg.node.description = existingArg.node.description || arg.description;
183
- const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(existingArg.node.type, arg.type, this.childName, argName);
184
- if (typeNode) {
185
- existingArg.node.type = typeNode;
186
- }
187
- else {
188
- if (!typeErrors || typeErrors.length < 2) {
189
- throw new Error(''); // TODO this should never happen
190
- }
191
- this.errors.push((0, errors_1.incompatibleArgumentTypesError)(argName, this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
192
- }
193
- this.compareAndValidateArgumentDefaultValues(existingArg, arg);
194
- return;
195
- }
196
- const newNode = (0, ast_1.inputValueDefinitionNodeToMutable)(arg, this.childName);
197
- // TODO validation of default values
198
- existingFieldNode.arguments.set(argName, {
199
- appearances: 1,
200
- includeDefaultValue: !!arg.defaultValue,
201
- node: newNode,
202
- });
125
+ upsertRequiredSubgraph(set, isRequired) {
126
+ if (isRequired) {
127
+ set.add(this.currentSubgraphName);
203
128
  }
129
+ return set;
204
130
  }
205
- extractArgumentsFromFieldNode(node, args) {
131
+ // TODO validation of default values
132
+ upsertArguments(node, argumentMap) {
206
133
  if (!node.arguments) {
207
- return;
134
+ return argumentMap;
208
135
  }
209
- for (const arg of node.arguments) {
210
- const argName = arg.name.value;
136
+ for (const argumentNode of node.arguments) {
137
+ const argName = argumentNode.name.value;
211
138
  const argPath = `${node.name.value}(${argName}...)`;
212
- args.set(argName, {
213
- appearances: 1,
214
- includeDefaultValue: !!arg.defaultValue,
215
- node: (0, ast_1.inputValueDefinitionNodeToMutable)(arg, this.childName),
216
- });
217
- this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, arg.type));
218
- }
219
- }
220
- addConcreteTypesForInterface(node) {
221
- if (!node.interfaces || node.interfaces.length < 1) {
222
- return;
223
- }
224
- const concreteTypeName = node.name.value;
225
- for (const iFace of node.interfaces) {
226
- const interfaceName = iFace.name.value;
227
- const concreteTypes = this.abstractToConcreteTypeNames.get(interfaceName);
228
- if (concreteTypes) {
229
- concreteTypes.add(concreteTypeName);
230
- }
231
- else {
232
- 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;
233
150
  }
234
- }
235
- }
236
- addConcreteTypesForUnion(node) {
237
- if (!node.types || node.types.length < 1) {
238
- return;
239
- }
240
- const unionName = node.name.value;
241
- for (const member of node.types) {
242
- const memberName = member.name.value;
243
- const concreteTypes = this.abstractToConcreteTypeNames.get(memberName);
244
- if (concreteTypes) {
245
- 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;
246
157
  }
247
158
  else {
248
- 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]));
249
163
  }
164
+ this.compareAndValidateArgumentDefaultValues(existingArgumentContainer, argumentNode);
250
165
  }
166
+ return argumentMap;
251
167
  }
252
168
  isFieldShareable(node, parent) {
253
169
  return (!this.isCurrentSubgraphVersionTwo ||
@@ -255,112 +171,66 @@ class FederationFactory {
255
171
  (0, utils_1.isNodeShareable)(node) ||
256
172
  (this.isCurrentParentEntity && this.isFieldEntityKey(parent)));
257
173
  }
258
- addPotentiallyUnresolvableField(parent, fieldName) {
259
- const fieldContainer = (0, utils_2.getOrThrowError)(parent.fields, fieldName);
260
- for (const [responseTypeName, operation] of this.rootTypeFieldsByResponseTypeName) {
261
- // If the operation response type has no path to the parent type, continue
262
- const paths = (0, graphology_simple_path_1.allSimplePaths)(this.graph, responseTypeName, this.parentTypeName);
263
- if (responseTypeName === this.parentTypeName) {
264
- paths.push([this.parentTypeName]);
265
- }
266
- else if (paths.length < 1) {
267
- continue;
268
- }
269
- // Construct all possible paths to the unresolvable field but with the fieldName relationship between nodes
270
- const partialResolverPaths = [];
271
- for (const path of paths) {
272
- let hasEntityAncestor = false;
273
- let resolverPath = '';
274
- for (let i = 0; i < path.length - 1; i++) {
275
- const pathParent = path[i];
276
- // The field in question is resolvable if it has an entity ancestor within the same subgraph
277
- // Unresolvable fields further up the chain will be handled elsewhere
278
- const entity = this.entityMap.get(pathParent);
279
- if (entity && entity.subgraphs.has(this.currentSubgraphName)) {
280
- hasEntityAncestor = true;
281
- break;
282
- }
283
- const edges = this.graph.edges(pathParent, path[i + 1]);
284
- // If there are multiple edges, pick the first one
285
- const inlineFragment = this.graph.getEdgeAttribute(edges[0], string_constants_1.INLINE_FRAGMENT);
286
- const edgeName = this.graph.getEdgeAttribute(edges[0], string_constants_1.FIELD_NAME);
287
- // If the parent field is an abstract type, the child should be proceeded by an inline fragment
288
- resolverPath += edgeName + (inlineFragment || '.');
289
- }
290
- if (hasEntityAncestor) {
291
- continue;
292
- }
293
- // Add the unresolvable field to each path
294
- resolverPath += fieldName;
295
- // If the field could have fields itself, add ellipsis
296
- if (this.graph.hasNode(fieldContainer.rootTypeName)) {
297
- resolverPath += ' { ... }';
298
- }
299
- partialResolverPaths.push(resolverPath);
300
- }
301
- 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)) {
302
179
  return;
303
180
  }
304
- // Each of these operations returns a type that has a path to the parent
305
- for (const [operationFieldPath, operationField] of operation) {
306
- // If the operation is defined in a subgraph that the field is defined, it is resolvable
307
- if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, operationField.subgraphs)) {
308
- continue;
309
- }
310
- const fullResolverPaths = [];
311
- // The field is still resolvable if it's defined and resolved in another graph (but that isn't yet known)
312
- // Consequently, the subgraphs must be compared later to determine that the field is always resolvable
313
- for (const partialResolverPath of partialResolverPaths) {
314
- fullResolverPaths.push(`${operationFieldPath}${operationField.inlineFragment}${partialResolverPath}`);
315
- }
316
- const potentiallyUnresolvableField = {
317
- fieldContainer,
318
- fullResolverPaths,
319
- rootTypeField: operationField,
320
- };
321
- // The parent might already have unresolvable fields that have already been added
322
- const dependentResponsesByFieldName = this.sharedRootTypeFieldDependentResponses.get(this.parentTypeName);
323
- if (dependentResponsesByFieldName) {
324
- dependentResponsesByFieldName.push(potentiallyUnresolvableField);
325
- return;
326
- }
327
- this.sharedRootTypeFieldDependentResponses.set(this.parentTypeName, [potentiallyUnresolvableField]);
181
+ if ((0, utils_1.mergeExecutableDirectiveLocations)(node.locations, directiveDefinition).size < 1) {
182
+ this.executableDirectives.delete(directiveName);
183
+ return;
328
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);
329
200
  }
330
201
  }
331
202
  upsertFieldNode(node) {
332
203
  const parent = this.isCurrentParentExtensionType
333
- ? (0, utils_2.getOrThrowError)(this.extensions, this.parentTypeName)
334
- : (0, utils_2.getOrThrowError)(this.parentMap, this.parentTypeName);
335
- if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION &&
336
- parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION &&
337
- 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) {
338
209
  throw (0, errors_1.unexpectedKindFatalError)(this.parentTypeName);
339
210
  }
340
211
  const fieldMap = parent.fields;
341
212
  const isFieldShareable = this.isFieldShareable(node, parent);
342
213
  const fieldPath = `${this.parentTypeName}.${this.childName}`;
343
214
  const fieldRootTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, node.type);
344
- const existingFieldNode = fieldMap.get(this.childName);
345
- const entityParent = this.entityMap.get(this.parentTypeName);
346
- if (existingFieldNode) {
347
- existingFieldNode.appearances += 1;
348
- existingFieldNode.node.description = existingFieldNode.node.description || node.description;
349
- existingFieldNode.subgraphs.add(this.currentSubgraphName);
350
- existingFieldNode.subgraphsByShareable.set(this.currentSubgraphName, isFieldShareable);
351
- 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);
352
222
  if (typeNode) {
353
- existingFieldNode.node.type = typeNode;
223
+ existingFieldContainer.node.type = typeNode;
354
224
  }
355
225
  else {
356
226
  if (!typeErrors || typeErrors.length < 2) {
357
- throw new Error(''); // TODO this should never happen
227
+ throw (0, errors_1.fieldTypeMergeFatalError)(this.childName);
358
228
  }
359
229
  this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
360
230
  }
361
- this.upsertArgumentsForFieldNode(node, existingFieldNode);
231
+ this.upsertArguments(node, existingFieldContainer.arguments);
362
232
  // If the parent is not an interface and both fields are not shareable, is it is a shareable error
363
- if (!this.isCurrentParentInterface && (!existingFieldNode.isShareable || !isFieldShareable)) {
233
+ if (!this.isCurrentParentInterface && (!existingFieldContainer.isShareable || !isFieldShareable)) {
364
234
  const shareableErrorTypeNames = this.shareableErrorTypeNames.get(this.parentTypeName);
365
235
  if (shareableErrorTypeNames) {
366
236
  shareableErrorTypeNames.add(this.childName);
@@ -371,27 +241,22 @@ class FederationFactory {
371
241
  }
372
242
  return;
373
243
  }
374
- const args = new Map();
375
- this.extractArgumentsFromFieldNode(node, args);
376
244
  this.outputFieldTypeNameSet.add(fieldRootTypeName);
377
245
  fieldMap.set(this.childName, {
378
- appearances: 1,
379
- arguments: args,
246
+ arguments: this.upsertArguments(node, new Map()),
247
+ directives: this.extractPersistedDirectives(node.directives || [], {
248
+ directives: new Map(),
249
+ tags: new Map(),
250
+ }),
380
251
  isShareable: isFieldShareable,
381
252
  node: (0, ast_1.fieldDefinitionNodeToMutable)(node, this.parentTypeName),
382
- rootTypeName: fieldRootTypeName,
253
+ namedTypeName: fieldRootTypeName,
383
254
  subgraphs: new Set([this.currentSubgraphName]),
384
255
  subgraphsByShareable: new Map([[this.currentSubgraphName, isFieldShareable]]),
385
256
  });
386
- if (this.isParentRootType ||
387
- entityParent?.fields.has(this.childName) ||
388
- parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
389
- return;
390
- }
391
- this.addPotentiallyUnresolvableField(parent, this.childName);
392
257
  }
393
258
  upsertValueNode(node) {
394
- const parent = this.parentMap.get(this.parentTypeName);
259
+ const parent = this.parents.get(this.parentTypeName);
395
260
  switch (node.kind) {
396
261
  case graphql_1.Kind.ENUM_VALUE_DEFINITION:
397
262
  if (!parent) {
@@ -402,37 +267,43 @@ class FederationFactory {
402
267
  throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.ENUM_TYPE_DEFINITION, parent.kind);
403
268
  }
404
269
  const enumValues = parent.values;
405
- const enumValue = enumValues.get(this.childName);
406
- if (enumValue) {
407
- enumValue.node.description = enumValue.node.description || node.description;
408
- 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;
409
275
  return;
410
276
  }
411
277
  enumValues.set(this.childName, {
412
278
  appearances: 1,
279
+ directives: this.extractPersistedDirectives(node.directives || [], {
280
+ directives: new Map(),
281
+ tags: new Map(),
282
+ }),
413
283
  node: (0, ast_1.enumValueDefinitionNodeToMutable)(node),
414
284
  });
415
285
  return;
416
286
  case graphql_1.Kind.INPUT_VALUE_DEFINITION:
417
287
  if (!parent || !this.isParentInputObject) {
418
- // TODO handle directives
288
+ // these are arguments to a directive
419
289
  return;
420
290
  }
421
291
  if (parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
422
292
  throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION, parent.kind);
423
293
  }
424
294
  const inputValues = parent.fields;
425
- const inputValue = inputValues.get(this.childName);
426
- if (inputValue) {
427
- inputValue.appearances += 1;
428
- inputValue.node.description = inputValue.node.description || node.description;
429
- 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);
430
301
  if (typeNode) {
431
- inputValue.node.type = typeNode;
302
+ inputValueContainer.node.type = typeNode;
432
303
  }
433
304
  else {
434
305
  if (!typeErrors || typeErrors.length < 2) {
435
- throw new Error(''); // TODO this should never happen
306
+ throw (0, errors_1.fieldTypeMergeFatalError)(this.childName);
436
307
  }
437
308
  this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
438
309
  }
@@ -443,6 +314,10 @@ class FederationFactory {
443
314
  this.inputFieldTypeNameSet.add(inputValueNamedType);
444
315
  inputValues.set(this.childName, {
445
316
  appearances: 1,
317
+ directives: this.extractPersistedDirectives(node.directives || [], {
318
+ directives: new Map(),
319
+ tags: new Map(),
320
+ }),
446
321
  includeDefaultValue: !!node.defaultValue,
447
322
  node: (0, ast_1.inputValueDefinitionNodeToMutable)(node, this.parentTypeName),
448
323
  });
@@ -453,10 +328,10 @@ class FederationFactory {
453
328
  }
454
329
  upsertParentNode(node) {
455
330
  const parentTypeName = node.name.value;
456
- const parent = this.parentMap.get(parentTypeName);
331
+ const parent = this.parents.get(parentTypeName);
457
332
  if (parent) {
458
- parent.node.description = parent.node.description || node.description;
459
- parent.appearances += 1;
333
+ (0, utils_1.setLongestDescriptionForNode)(parent.node, node.description);
334
+ this.extractPersistedDirectives(node.directives || [], parent.directives);
460
335
  }
461
336
  switch (node.kind) {
462
337
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
@@ -464,10 +339,15 @@ class FederationFactory {
464
339
  if (parent.kind !== node.kind) {
465
340
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
466
341
  }
342
+ parent.appearances += 1;
467
343
  return;
468
344
  }
469
- this.parentMap.set(parentTypeName, {
345
+ this.parents.set(parentTypeName, {
470
346
  appearances: 1,
347
+ directives: this.extractPersistedDirectives(node.directives || [], {
348
+ directives: new Map(),
349
+ tags: new Map(),
350
+ }),
471
351
  values: new Map(),
472
352
  kind: node.kind,
473
353
  node: (0, ast_1.enumTypeDefinitionNodeToMutable)(node),
@@ -478,10 +358,15 @@ class FederationFactory {
478
358
  if (parent.kind !== node.kind) {
479
359
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
480
360
  }
361
+ parent.appearances += 1;
481
362
  return;
482
363
  }
483
- this.parentMap.set(parentTypeName, {
364
+ this.parents.set(parentTypeName, {
484
365
  appearances: 1,
366
+ directives: this.extractPersistedDirectives(node.directives || [], {
367
+ directives: new Map(),
368
+ tags: new Map(),
369
+ }),
485
370
  fields: new Map(),
486
371
  kind: node.kind,
487
372
  node: (0, ast_1.inputObjectTypeDefinitionNodeToMutable)(node),
@@ -498,8 +383,11 @@ class FederationFactory {
498
383
  }
499
384
  const nestedInterfaces = new Set();
500
385
  (0, utils_1.extractInterfaces)(node, nestedInterfaces);
501
- this.parentMap.set(parentTypeName, {
502
- appearances: 1,
386
+ this.parents.set(parentTypeName, {
387
+ directives: this.extractPersistedDirectives(node.directives || [], {
388
+ directives: new Map(),
389
+ tags: new Map(),
390
+ }),
503
391
  fields: new Map(),
504
392
  interfaces: nestedInterfaces,
505
393
  kind: node.kind,
@@ -514,8 +402,11 @@ class FederationFactory {
514
402
  }
515
403
  return;
516
404
  }
517
- this.parentMap.set(parentTypeName, {
518
- appearances: 1,
405
+ this.parents.set(parentTypeName, {
406
+ directives: this.extractPersistedDirectives(node.directives || [], {
407
+ directives: new Map(),
408
+ tags: new Map(),
409
+ }),
519
410
  kind: node.kind,
520
411
  node: (0, ast_1.scalarTypeDefinitionNodeToMutable)(node),
521
412
  });
@@ -526,16 +417,19 @@ class FederationFactory {
526
417
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
527
418
  }
528
419
  (0, utils_1.extractInterfaces)(node, parent.interfaces);
529
- (0, utils_1.extractEntityKeys)(node, parent.entityKeys);
420
+ (0, utils_1.extractEntityKeys)(node, parent.entityKeys, this.errors);
530
421
  parent.subgraphs.add(this.currentSubgraphName);
531
422
  return;
532
423
  }
533
424
  const interfaces = new Set();
534
425
  (0, utils_1.extractInterfaces)(node, interfaces);
535
426
  const entityKeys = new Set();
536
- (0, utils_1.extractEntityKeys)(node, entityKeys);
537
- this.parentMap.set(parentTypeName, {
538
- 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
+ }),
539
433
  fields: new Map(),
540
434
  entityKeys,
541
435
  interfaces,
@@ -557,8 +451,11 @@ class FederationFactory {
557
451
  node.types?.forEach((member) => parent.members.add(member.name.value));
558
452
  return;
559
453
  }
560
- this.parentMap.set(parentTypeName, {
561
- appearances: 1,
454
+ this.parents.set(parentTypeName, {
455
+ directives: this.extractPersistedDirectives(node.directives || [], {
456
+ directives: new Map(),
457
+ tags: new Map(),
458
+ }),
562
459
  kind: node.kind,
563
460
  members: new Set(node.types?.map((member) => member.name.value)),
564
461
  node: (0, ast_1.unionTypeDefinitionNodeToMutable)(node),
@@ -566,98 +463,66 @@ class FederationFactory {
566
463
  return;
567
464
  }
568
465
  }
569
- upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName = fieldTypeName, hasAbstractParent = false) {
570
- const operationFields = this.rootTypeFieldsByResponseTypeName.get(concreteTypeName);
571
- if (!operationFields) {
572
- this.rootTypeFieldsByResponseTypeName.set(concreteTypeName, new Map([
573
- [
574
- operationFieldPath,
575
- {
576
- inlineFragment: hasAbstractParent ? (0, utils_1.getInlineFragmentString)(concreteTypeName) : '.',
577
- name: fieldName,
578
- parentTypeName: this.parentTypeName,
579
- path: operationFieldPath,
580
- responseType,
581
- rootTypeName: fieldTypeName,
582
- subgraphs: new Set([this.currentSubgraphName]),
583
- },
584
- ],
585
- ]));
586
- return;
587
- }
588
- const operationField = operationFields.get(operationFieldPath);
589
- if (operationField) {
590
- operationField.subgraphs.add(this.currentSubgraphName);
591
- return;
592
- }
593
- operationFields.set(operationFieldPath, {
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
- upsertAbstractObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeNames) {
604
- for (const concreteTypeName of concreteTypeNames) {
605
- if (!this.graph.hasNode(concreteTypeName)) {
606
- throw (0, errors_1.invalidMultiGraphNodeFatalError)(concreteTypeName); // should never happen
607
- }
608
- if (!this.graphEdges.has(operationFieldPath)) {
609
- this.graph.addEdge(this.parentTypeName, concreteTypeName, { fieldName });
610
- }
611
- // Always upsert the operation field node to record subgraph appearances
612
- this.upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName, true);
613
- }
614
- // Add the path so the edges are not added again
615
- this.graphEdges.add(operationFieldPath);
616
- }
617
- validatePotentiallyUnresolvableFields() {
618
- if (this.sharedRootTypeFieldDependentResponses.size < 1) {
619
- return;
620
- }
621
- for (const [parentTypeName, potentiallyUnresolvableFields] of this.sharedRootTypeFieldDependentResponses) {
622
- for (const potentiallyUnresolvableField of potentiallyUnresolvableFields) {
623
- // There is no issue if the field is resolvable from at least one subgraph
624
- const operationField = potentiallyUnresolvableField.rootTypeField;
625
- const fieldContainer = potentiallyUnresolvableField.fieldContainer;
626
- if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, operationField.subgraphs)) {
627
- continue;
628
- }
629
- const fieldSubgraphs = [...fieldContainer.subgraphs].join('", "');
630
- this.errors.push((0, errors_1.unresolvableFieldError)(operationField, fieldContainer.node.name.value, potentiallyUnresolvableField.fullResolverPaths, fieldSubgraphs, parentTypeName));
631
- }
632
- }
633
- }
634
466
  upsertExtensionNode(node) {
635
467
  const extension = this.extensions.get(this.parentTypeName);
636
468
  if (extension) {
637
469
  if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
638
470
  throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.OBJECT_TYPE_EXTENSION, extension.kind);
639
471
  }
640
- extension.appearances += 1;
641
472
  extension.subgraphs.add(this.currentSubgraphName);
642
473
  (0, utils_1.extractInterfaces)(node, extension.interfaces);
474
+ this.extractPersistedDirectives(node.directives || [], extension.directives);
643
475
  return;
644
476
  }
645
477
  // build a new extension
646
- const interfaces = new Set();
647
- (0, utils_1.extractInterfaces)(node, interfaces);
648
- const entityKeys = new Set();
649
- (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);
650
480
  this.extensions.set(this.parentTypeName, {
651
- appearances: 1,
652
- 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,
653
488
  isRootType: this.isParentRootType,
654
489
  kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
655
490
  node: (0, ast_1.objectTypeExtensionNodeToMutable)(node),
656
- fields: new Map(),
657
- entityKeys,
658
- interfaces,
491
+ subgraphs: new Set([this.currentSubgraphName]),
659
492
  });
660
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
+ }
661
526
  getAndValidateImplementedInterfaces(container) {
662
527
  const interfaces = [];
663
528
  if (container.interfaces.size < 1) {
@@ -666,7 +531,11 @@ class FederationFactory {
666
531
  const implementationErrorsMap = new Map();
667
532
  for (const interfaceName of container.interfaces) {
668
533
  interfaces.push((0, utils_1.stringToNamedTypeNode)(interfaceName));
669
- 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
+ }
670
539
  if (interfaceContainer.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
671
540
  throw (0, errors_1.incompatibleParentKindFatalError)(interfaceName, graphql_1.Kind.INTERFACE_TYPE_DEFINITION, interfaceContainer.kind);
672
541
  }
@@ -690,7 +559,7 @@ class FederationFactory {
690
559
  unimplementedArguments: new Set(),
691
560
  };
692
561
  // The implemented field type must be equally or more restrictive than the original interface field type
693
- if (!(0, utils_2.isTypeValidImplementation)(interfaceField.node.type, containerField.node.type)) {
562
+ if (!this.isTypeValidImplementation(interfaceField.node.type, containerField.node.type)) {
694
563
  hasErrors = true;
695
564
  hasNestedErrors = true;
696
565
  invalidFieldImplementation.implementedResponseType = (0, merge_1.printTypeNode)(containerField.node.type);
@@ -738,10 +607,233 @@ class FederationFactory {
738
607
  }
739
608
  }
740
609
  if (implementationErrorsMap.size) {
741
- 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));
742
611
  }
743
612
  return interfaces;
744
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
+ }
745
837
  federate() {
746
838
  this.populateMultiGraphAndRenameOperations(this.subgraphs);
747
839
  const factory = this;
@@ -750,16 +842,20 @@ class FederationFactory {
750
842
  this.currentSubgraphName = subgraph.name;
751
843
  (0, subgraph_1.walkSubgraphToFederate)(subgraph.definitions, factory);
752
844
  }
753
- this.validatePotentiallyUnresolvableFields();
754
845
  const definitions = [];
755
- for (const definition of this.directiveDefinitions.values()) {
756
- 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);
757
853
  }
758
854
  for (const [typeName, extension] of this.extensions) {
759
- if (extension.isRootType && !this.parentMap.has(typeName)) {
855
+ if (extension.isRootType && !this.parents.has(typeName)) {
760
856
  this.upsertParentNode((0, ast_1.objectTypeExtensionNodeToMutableDefinitionNode)(extension.node));
761
857
  }
762
- const baseObject = this.parentMap.get(typeName);
858
+ const baseObject = this.parents.get(typeName);
763
859
  if (!baseObject) {
764
860
  this.errors.push((0, errors_1.noBaseTypeExtensionError)(typeName));
765
861
  continue;
@@ -767,152 +863,238 @@ class FederationFactory {
767
863
  if (baseObject.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
768
864
  throw (0, errors_1.incompatibleParentKindFatalError)(typeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, baseObject.kind);
769
865
  }
770
- // TODO check directives
771
- for (const [fieldName, field] of extension.fields) {
772
- const baseField = baseObject.fields.get(fieldName);
773
- if (!baseField) {
774
- 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);
775
870
  continue;
776
871
  }
777
- 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);
778
875
  continue;
779
876
  }
780
877
  const parent = this.shareableErrorTypeNames.get(typeName);
781
878
  if (parent) {
782
- parent.add(fieldName);
879
+ parent.add(extensionFieldName);
783
880
  continue;
784
881
  }
785
- this.shareableErrorTypeNames.set(typeName, new Set([fieldName]));
882
+ this.shareableErrorTypeNames.set(typeName, new Set([extensionFieldName]));
786
883
  }
787
884
  for (const interfaceName of extension.interfaces) {
788
885
  baseObject.interfaces.add(interfaceName);
789
886
  }
790
887
  }
791
888
  for (const [parentTypeName, children] of this.shareableErrorTypeNames) {
792
- const parent = (0, utils_2.getOrThrowError)(this.parentMap, parentTypeName);
889
+ const parent = (0, utils_3.getOrThrowError)(this.parents, parentTypeName, string_constants_1.PARENTS);
793
890
  if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
794
891
  throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, parent.kind);
795
892
  }
796
893
  this.errors.push((0, errors_1.shareableFieldDefinitionsError)(parent, children));
797
894
  }
798
- for (const parent of this.parentMap.values()) {
799
- const parentName = parent.node.name.value;
800
- 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) {
801
899
  case graphql_1.Kind.ENUM_TYPE_DEFINITION:
802
900
  const values = [];
803
- const mergeMethod = this.getEnumMergeMethod(parentName);
804
- 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);
805
904
  switch (mergeMethod) {
806
- case utils_1.MergeMethod.CONSISTENT:
807
- if (value.appearances < parent.appearances) {
808
- 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));
809
908
  }
810
- values.push(value.node);
909
+ values.push(enumValueContainer.node);
811
910
  break;
812
- case utils_1.MergeMethod.INTERSECTION:
813
- if (value.appearances === parent.appearances) {
814
- values.push(value.node);
911
+ case utils_2.MergeMethod.INTERSECTION:
912
+ if (enumValueContainer.appearances === parentContainer.appearances) {
913
+ values.push(enumValueContainer.node);
815
914
  }
816
915
  break;
817
916
  default:
818
- values.push(value.node);
917
+ values.push(enumValueContainer.node);
819
918
  break;
820
919
  }
821
920
  }
822
- parent.node.values = values;
823
- definitions.push(parent.node);
921
+ parentContainer.node.values = values;
922
+ definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
824
923
  break;
825
924
  case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
826
925
  const inputValues = [];
827
- for (const value of parent.fields.values()) {
828
- if (parent.appearances === value.appearances) {
829
- 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);
830
930
  }
831
- else if ((0, type_merging_1.isTypeRequired)(value.node.type)) {
832
- // TODO append to errors
833
- 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;
834
934
  }
835
935
  }
836
- parent.node.fields = inputValues;
837
- definitions.push(parent.node);
936
+ parentContainer.node.fields = inputValues;
937
+ definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
838
938
  break;
839
939
  case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
840
940
  const interfaceFields = [];
841
- for (const field of parent.fields.values()) {
842
- if (field.arguments) {
843
- const args = [];
844
- for (const arg of field.arguments.values()) {
845
- arg.node.defaultValue = arg.includeDefaultValue ? arg.node.defaultValue : undefined;
846
- args.push(arg.node);
847
- }
848
- field.node.arguments = args;
849
- }
850
- 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);
851
949
  }
852
- const otherInterfaces = [];
853
- for (const iFace of parent.interfaces) {
854
- otherInterfaces.push({
855
- kind: graphql_1.Kind.NAMED_TYPE,
856
- name: {
857
- kind: graphql_1.Kind.NAME,
858
- value: iFace,
859
- },
860
- });
950
+ else {
951
+ definitions.push(parentContainer.node);
861
952
  }
862
- parent.node.interfaces = otherInterfaces;
863
- parent.node.fields = interfaceFields;
864
- definitions.push(parent.node);
865
953
  break;
866
954
  case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
867
955
  const fields = [];
868
- for (const field of parent.fields.values()) {
869
- if (field.arguments) {
870
- const args = [];
871
- for (const arg of field.arguments.values()) {
872
- arg.node.defaultValue = arg.includeDefaultValue ? arg.node.defaultValue : undefined;
873
- args.push(arg.node);
874
- }
875
- field.node.arguments = args;
876
- }
877
- 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);
878
967
  }
879
- parent.node.fields = fields;
880
- parent.node.interfaces = this.getAndValidateImplementedInterfaces(parent);
881
- definitions.push(parent.node);
882
968
  break;
883
969
  case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
884
- definitions.push(parent.node);
970
+ definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
885
971
  break;
886
972
  case graphql_1.Kind.UNION_TYPE_DEFINITION:
887
973
  const types = [];
888
- for (const member of parent.members) {
889
- types.push({
890
- kind: graphql_1.Kind.NAMED_TYPE,
891
- name: {
892
- kind: graphql_1.Kind.NAME,
893
- value: member,
894
- },
895
- });
974
+ for (const memberName of parentContainer.members) {
975
+ types.push((0, utils_1.stringToNamedTypeNode)(memberName));
896
976
  }
897
- parent.node.types = types;
898
- definitions.push(parent.node);
977
+ parentContainer.node.types = types;
978
+ definitions.push((0, utils_1.getNodeWithPersistedDirectives)(parentContainer));
899
979
  break;
900
980
  }
901
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
902
990
  if (this.errors.length > 0) {
903
- return {
904
- errors: this.errors,
905
- };
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 };
906
1038
  }
907
1039
  const newAst = {
908
1040
  kind: graphql_1.Kind.DOCUMENT,
909
1041
  definitions,
910
1042
  };
911
1043
  return {
912
- federatedGraphAST: newAst,
913
- federatedGraphSchema: (0, graphql_1.buildASTSchema)(newAst),
1044
+ federationResult: {
1045
+ argumentConfigurations: this.argumentConfigurations,
1046
+ federatedGraphAST: newAst,
1047
+ federatedGraphSchema: (0, graphql_1.buildASTSchema)(newAst),
1048
+ }
914
1049
  };
915
1050
  }
916
1051
  }
917
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;
918
1100
  //# sourceMappingURL=federation-factory.js.map