@wundergraph/composition 0.0.1

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 (49) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +3 -0
  3. package/dist/ast/ast.d.ts +84 -0
  4. package/dist/ast/ast.js +183 -0
  5. package/dist/ast/ast.js.map +1 -0
  6. package/dist/ast/utils.d.ts +130 -0
  7. package/dist/ast/utils.js +298 -0
  8. package/dist/ast/utils.js.map +1 -0
  9. package/dist/buildASTSchema/buildASTSchema.d.ts +22 -0
  10. package/dist/buildASTSchema/buildASTSchema.js +59 -0
  11. package/dist/buildASTSchema/buildASTSchema.js.map +1 -0
  12. package/dist/buildASTSchema/extendSchema.d.ts +21 -0
  13. package/dist/buildASTSchema/extendSchema.js +555 -0
  14. package/dist/buildASTSchema/extendSchema.js.map +1 -0
  15. package/dist/errors/errors.d.ts +60 -0
  16. package/dist/errors/errors.js +302 -0
  17. package/dist/errors/errors.js.map +1 -0
  18. package/dist/federation/federation-factory.d.ts +58 -0
  19. package/dist/federation/federation-factory.js +843 -0
  20. package/dist/federation/federation-factory.js.map +1 -0
  21. package/dist/federation/federation-result.d.ts +6 -0
  22. package/dist/federation/federation-result.js +10 -0
  23. package/dist/federation/federation-result.js.map +1 -0
  24. package/dist/federation/subgraph.d.ts +18 -0
  25. package/dist/federation/subgraph.js +305 -0
  26. package/dist/federation/subgraph.js.map +1 -0
  27. package/dist/index.d.ts +9 -0
  28. package/dist/index.js +26 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/normalization/normalization-factory.d.ts +54 -0
  31. package/dist/normalization/normalization-factory.js +882 -0
  32. package/dist/normalization/normalization-factory.js.map +1 -0
  33. package/dist/normalization/utils.d.ts +121 -0
  34. package/dist/normalization/utils.js +278 -0
  35. package/dist/normalization/utils.js.map +1 -0
  36. package/dist/tsconfig.tsbuildinfo +1 -0
  37. package/dist/type-merging/type-merging.d.ts +9 -0
  38. package/dist/type-merging/type-merging.js +112 -0
  39. package/dist/type-merging/type-merging.js.map +1 -0
  40. package/dist/utils/constants.d.ts +6 -0
  41. package/dist/utils/constants.js +157 -0
  42. package/dist/utils/constants.js.map +1 -0
  43. package/dist/utils/string-constants.d.ts +39 -0
  44. package/dist/utils/string-constants.js +43 -0
  45. package/dist/utils/string-constants.js.map +1 -0
  46. package/dist/utils/utils.d.ts +7 -0
  47. package/dist/utils/utils.js +80 -0
  48. package/dist/utils/utils.js.map +1 -0
  49. package/package.json +35 -0
@@ -0,0 +1,843 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FederationFactory = exports.federateSubgraphs = void 0;
4
+ const graphology_1 = require("graphology");
5
+ const graphology_simple_path_1 = require("graphology-simple-path");
6
+ const graphql_1 = require("graphql");
7
+ const ast_1 = require("../ast/ast");
8
+ const utils_1 = require("../ast/utils");
9
+ const errors_1 = require("../errors/errors");
10
+ const type_merging_1 = require("../type-merging/type-merging");
11
+ const subgraph_1 = require("./subgraph");
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");
15
+ function federateSubgraphs(subgraphs) {
16
+ if (subgraphs.length < 1) {
17
+ throw errors_1.minimumSubgraphRequirementError;
18
+ }
19
+ const normalizedSubgraphs = [];
20
+ const validationErrors = [];
21
+ const subgraphNames = new Set();
22
+ const nonUniqueSubgraphNames = new Set();
23
+ const invalidNameErrorMessages = [];
24
+ for (let i = 0; i < subgraphs.length; i++) {
25
+ const subgraph = subgraphs[i];
26
+ const name = subgraph.name || `subgraph-${i}-${Date.now()}`;
27
+ if (!subgraph.name) {
28
+ invalidNameErrorMessages.push((0, errors_1.invalidSubgraphNameErrorMessage)(i, name));
29
+ }
30
+ else {
31
+ (0, subgraph_1.validateSubgraphName)(subgraph.name, subgraphNames, nonUniqueSubgraphNames);
32
+ }
33
+ const { errors, normalizationResult } = (0, normalization_factory_1.normalizeSubgraph)(subgraph.definitions);
34
+ if (errors) {
35
+ validationErrors.push((0, errors_1.subgraphValidationError)(name, errors));
36
+ continue;
37
+ }
38
+ if (!normalizationResult) {
39
+ validationErrors.push((0, errors_1.subgraphValidationError)(name, [errors_1.subgraphValidationFailureErrorMessage]));
40
+ continue;
41
+ }
42
+ normalizedSubgraphs.push({
43
+ definitions: normalizationResult.subgraphAST,
44
+ isVersionTwo: normalizationResult.isVersionTwo,
45
+ name,
46
+ operationTypes: normalizationResult.operationTypes,
47
+ url: subgraph.url,
48
+ });
49
+ }
50
+ const allErrors = [];
51
+ if (invalidNameErrorMessages.length > 0 || nonUniqueSubgraphNames.size > 0) {
52
+ allErrors.push((0, errors_1.invalidSubgraphNamesError)(Array.from(nonUniqueSubgraphNames), invalidNameErrorMessages));
53
+ }
54
+ allErrors.push(...validationErrors);
55
+ if (allErrors.length > 0) {
56
+ return { errors: allErrors };
57
+ }
58
+ const federationFactory = new FederationFactory(normalizedSubgraphs);
59
+ return federationFactory.federate();
60
+ }
61
+ exports.federateSubgraphs = federateSubgraphs;
62
+ class FederationFactory {
63
+ abstractToConcreteTypeNames = new Map();
64
+ areFieldsShareable = false;
65
+ argumentTypeNameSet = new Set();
66
+ parentTypeName = '';
67
+ currentSubgraphName = '';
68
+ childName = '';
69
+ directiveDefinitions = new Map();
70
+ entityMap = new Map();
71
+ errors = [];
72
+ extensions = new Map();
73
+ graph = new graphology_1.MultiGraph();
74
+ graphEdges = new Set();
75
+ inputFieldTypeNameSet = new Set();
76
+ isCurrentParentEntity = false;
77
+ isCurrentParentInterface = false;
78
+ isCurrentSubgraphVersionTwo = false;
79
+ isCurrentParentExtensionType = false;
80
+ isParentRootType = false;
81
+ isParentInputObject = false;
82
+ operationFieldsByResponseTypeName = new Map();
83
+ outputFieldTypeNameSet = new Set();
84
+ parentMap = new Map();
85
+ rootTypeNames = new Set([string_constants_1.DEFAULT_MUTATION, string_constants_1.DEFAULT_QUERY, string_constants_1.DEFAULT_SUBSCRIPTION]);
86
+ sharedOperationDependentResponses = new Map();
87
+ subgraphs = [];
88
+ shareableErrorTypeNames = new Map();
89
+ constructor(subgraphs) {
90
+ this.subgraphs = subgraphs;
91
+ }
92
+ isObjectRootType(node) {
93
+ return this.rootTypeNames.has(node.name.value);
94
+ }
95
+ upsertEntity(node) {
96
+ const typeName = node.name.value;
97
+ const entity = this.entityMap.get(typeName);
98
+ if (entity) {
99
+ (0, utils_1.extractEntityKeys)(node, entity.keys);
100
+ entity.subgraphs.add(this.currentSubgraphName);
101
+ return;
102
+ }
103
+ this.entityMap.set(typeName, {
104
+ fields: new Set(),
105
+ keys: (0, utils_1.extractEntityKeys)(node, new Set()),
106
+ subgraphs: new Set([this.currentSubgraphName]),
107
+ });
108
+ }
109
+ populateMultiGraphAndRenameOperations(subgraphs) {
110
+ for (const subgraph of subgraphs) {
111
+ this.currentSubgraphName = subgraph.name;
112
+ (0, subgraph_1.walkSubgraphToCollectObjects)(this, subgraph);
113
+ (0, subgraph_1.walkSubgraphToCollectOperationsAndFields)(this, subgraph);
114
+ }
115
+ }
116
+ isParentInterface(parent) {
117
+ return parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION;
118
+ }
119
+ isFieldEntityKey(parent) {
120
+ if (parent.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || parent.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
121
+ return parent.entityKeys.has(this.childName);
122
+ }
123
+ return false;
124
+ }
125
+ getEnumMergeMethod(enumName) {
126
+ if (this.inputFieldTypeNameSet.has(enumName) || this.argumentTypeNameSet.has(enumName)) {
127
+ if (this.outputFieldTypeNameSet.has(enumName)) {
128
+ return utils_1.MergeMethod.CONSISTENT;
129
+ }
130
+ return utils_1.MergeMethod.INTERSECTION;
131
+ }
132
+ return utils_1.MergeMethod.UNION;
133
+ }
134
+ validateArgumentDefaultValues(argName, existingDefaultValue, newDefaultValue) {
135
+ if (existingDefaultValue.kind !== newDefaultValue.kind) {
136
+ // This should be caught by subgraph validation
137
+ this.errors.push((0, errors_1.incompatibleArgumentDefaultValueTypeError)(argName, this.parentTypeName, this.childName, existingDefaultValue.kind, newDefaultValue.kind));
138
+ }
139
+ if ('value' in newDefaultValue && existingDefaultValue.value !== newDefaultValue.value) {
140
+ this.errors.push((0, errors_1.incompatibleArgumentDefaultValueError)(argName, this.parentTypeName, this.childName, existingDefaultValue.value, newDefaultValue.value));
141
+ }
142
+ }
143
+ compareAndValidateArgumentDefaultValues(existingArg, newArg) {
144
+ const newDefaultValue = newArg.defaultValue;
145
+ existingArg.node.defaultValue = existingArg.node.defaultValue || newDefaultValue;
146
+ if (!existingArg.node.defaultValue || !newDefaultValue) {
147
+ existingArg.includeDefaultValue = false;
148
+ return;
149
+ }
150
+ const existingDefaultValue = existingArg.node.defaultValue;
151
+ switch (existingDefaultValue.kind) {
152
+ case graphql_1.Kind.LIST: // TODO
153
+ break;
154
+ case graphql_1.Kind.NULL:
155
+ break;
156
+ case graphql_1.Kind.OBJECT:
157
+ break;
158
+ // BOOLEAN, ENUM, FLOAT, INT, and STRING purposely fall through
159
+ case graphql_1.Kind.BOOLEAN:
160
+ case graphql_1.Kind.ENUM:
161
+ case graphql_1.Kind.FLOAT:
162
+ case graphql_1.Kind.INT:
163
+ case graphql_1.Kind.STRING:
164
+ this.validateArgumentDefaultValues(existingArg.node.name.value, existingDefaultValue, newDefaultValue);
165
+ break;
166
+ default:
167
+ throw new Error('Unexpected argument type'); // TODO
168
+ }
169
+ }
170
+ upsertArgumentsForFieldNode(node, existingFieldNode) {
171
+ if (!node.arguments) {
172
+ return;
173
+ }
174
+ for (const arg of node.arguments) {
175
+ const argName = arg.name.value;
176
+ const argPath = `${node.name.value}(${argName}...)`;
177
+ this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, arg.type));
178
+ const existingArg = existingFieldNode.arguments.get(argName);
179
+ if (existingArg) {
180
+ existingArg.appearances += 1;
181
+ existingArg.node.description = existingArg.node.description || arg.description;
182
+ const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(existingArg.node.type, arg.type, this.childName, argName);
183
+ if (typeNode) {
184
+ existingArg.node.type = typeNode;
185
+ }
186
+ else {
187
+ if (!typeErrors || typeErrors.length < 2) {
188
+ throw new Error(''); // TODO this should never happen
189
+ }
190
+ this.errors.push((0, errors_1.incompatibleArgumentTypesError)(argName, this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
191
+ }
192
+ this.compareAndValidateArgumentDefaultValues(existingArg, arg);
193
+ return;
194
+ }
195
+ const newNode = (0, ast_1.inputValueDefinitionNodeToMutable)(arg, this.childName);
196
+ // TODO validation of default values
197
+ existingFieldNode.arguments.set(argName, {
198
+ appearances: 1,
199
+ includeDefaultValue: !!arg.defaultValue,
200
+ node: newNode,
201
+ });
202
+ }
203
+ }
204
+ extractArgumentsFromFieldNode(node, args) {
205
+ if (!node.arguments) {
206
+ return;
207
+ }
208
+ for (const arg of node.arguments) {
209
+ const argName = arg.name.value;
210
+ const argPath = `${node.name.value}(${argName}...)`;
211
+ args.set(argName, {
212
+ appearances: 1,
213
+ includeDefaultValue: !!arg.defaultValue,
214
+ node: (0, ast_1.inputValueDefinitionNodeToMutable)(arg, this.childName),
215
+ });
216
+ this.argumentTypeNameSet.add((0, type_merging_1.getNamedTypeForChild)(argPath, arg.type));
217
+ }
218
+ }
219
+ addConcreteTypesForInterface(node) {
220
+ if (!node.interfaces || node.interfaces.length < 1) {
221
+ return;
222
+ }
223
+ const concreteTypeName = node.name.value;
224
+ for (const iFace of node.interfaces) {
225
+ const interfaceName = iFace.name.value;
226
+ const concreteTypes = this.abstractToConcreteTypeNames.get(interfaceName);
227
+ if (concreteTypes) {
228
+ concreteTypes.add(concreteTypeName);
229
+ }
230
+ else {
231
+ this.abstractToConcreteTypeNames.set(interfaceName, new Set([concreteTypeName]));
232
+ }
233
+ }
234
+ }
235
+ addConcreteTypesForUnion(node) {
236
+ if (!node.types || node.types.length < 1) {
237
+ return;
238
+ }
239
+ const unionName = node.name.value;
240
+ for (const member of node.types) {
241
+ const memberName = member.name.value;
242
+ const concreteTypes = this.abstractToConcreteTypeNames.get(memberName);
243
+ if (concreteTypes) {
244
+ concreteTypes.add(memberName);
245
+ }
246
+ else {
247
+ this.abstractToConcreteTypeNames.set(unionName, new Set([memberName]));
248
+ }
249
+ }
250
+ }
251
+ isFieldShareable(node, parent) {
252
+ return (!this.isCurrentSubgraphVersionTwo ||
253
+ this.areFieldsShareable ||
254
+ (0, utils_1.isNodeShareable)(node) ||
255
+ (this.isCurrentParentEntity && this.isFieldEntityKey(parent)));
256
+ }
257
+ addPotentiallyUnresolvableField(parent, fieldName) {
258
+ const fieldContainer = (0, utils_2.getOrThrowError)(parent.fields, fieldName);
259
+ for (const [responseTypeName, operation] of this.operationFieldsByResponseTypeName) {
260
+ // If the operation response type has no path to the parent type, continue
261
+ const paths = (0, graphology_simple_path_1.allSimplePaths)(this.graph, responseTypeName, this.parentTypeName);
262
+ if (responseTypeName === this.parentTypeName) {
263
+ paths.push([this.parentTypeName]);
264
+ }
265
+ else if (paths.length < 1) {
266
+ continue;
267
+ }
268
+ // Construct all possible paths to the unresolvable field but with the fieldName relationship between nodes
269
+ const partialResolverPaths = [];
270
+ for (const path of paths) {
271
+ let hasEntityAncestor = false;
272
+ let resolverPath = '';
273
+ for (let i = 0; i < path.length - 1; i++) {
274
+ const pathParent = path[i];
275
+ // The field in question is resolvable if it has an entity ancestor within the same subgraph
276
+ // Unresolvable fields further up the chain will be handled elsewhere
277
+ const entity = this.entityMap.get(pathParent);
278
+ if (entity && entity.subgraphs.has(this.currentSubgraphName)) {
279
+ hasEntityAncestor = true;
280
+ break;
281
+ }
282
+ const edges = this.graph.edges(pathParent, path[i + 1]);
283
+ // If there are multiple edges, pick the first one
284
+ const inlineFragment = this.graph.getEdgeAttribute(edges[0], string_constants_1.INLINE_FRAGMENT);
285
+ const edgeName = this.graph.getEdgeAttribute(edges[0], string_constants_1.FIELD_NAME);
286
+ // If the parent field is an abstract type, the child should be proceeded by an inline fragment
287
+ resolverPath += edgeName + (inlineFragment || '.');
288
+ }
289
+ if (hasEntityAncestor) {
290
+ continue;
291
+ }
292
+ // Add the unresolvable field to each path
293
+ resolverPath += fieldName;
294
+ // If the field could have fields itself, add ellipsis
295
+ if (this.graph.hasNode(fieldContainer.rootTypeName)) {
296
+ resolverPath += ' { ... }';
297
+ }
298
+ partialResolverPaths.push(resolverPath);
299
+ }
300
+ if (partialResolverPaths.length < 1) {
301
+ return;
302
+ }
303
+ // Each of these operations returns a type that has a path to the parent
304
+ for (const [operationFieldPath, operationField] of operation) {
305
+ // If the operation is defined in a subgraph that the field is defined, it is resolvable
306
+ if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, operationField.subgraphs)) {
307
+ continue;
308
+ }
309
+ const fullResolverPaths = [];
310
+ // The field is still resolvable if it's defined and resolved in another graph (but that isn't yet known)
311
+ // Consequently, the subgraphs must be compared later to determine that the field is always resolvable
312
+ for (const partialResolverPath of partialResolverPaths) {
313
+ fullResolverPaths.push(`${operationFieldPath}${operationField.inlineFragment}${partialResolverPath}`);
314
+ }
315
+ const potentiallyUnresolvableField = {
316
+ fieldContainer,
317
+ fullResolverPaths,
318
+ operationField,
319
+ };
320
+ // The parent might already have unresolvable fields that have already been added
321
+ const dependentResponsesByFieldName = this.sharedOperationDependentResponses.get(this.parentTypeName);
322
+ if (dependentResponsesByFieldName) {
323
+ dependentResponsesByFieldName.push(potentiallyUnresolvableField);
324
+ return;
325
+ }
326
+ this.sharedOperationDependentResponses.set(this.parentTypeName, [potentiallyUnresolvableField]);
327
+ }
328
+ }
329
+ }
330
+ upsertFieldNode(node) {
331
+ const parent = this.isCurrentParentExtensionType
332
+ ? (0, utils_2.getOrThrowError)(this.extensions, this.parentTypeName)
333
+ : (0, utils_2.getOrThrowError)(this.parentMap, this.parentTypeName);
334
+ if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION &&
335
+ parent.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION &&
336
+ parent.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
337
+ throw new Error(''); // TODO
338
+ }
339
+ const fieldMap = parent.fields;
340
+ const isFieldShareable = this.isFieldShareable(node, parent);
341
+ const fieldPath = `${this.parentTypeName}.${this.childName}`;
342
+ const fieldRootTypeName = (0, type_merging_1.getNamedTypeForChild)(fieldPath, node.type);
343
+ const existingFieldNode = fieldMap.get(this.childName);
344
+ const entityParent = this.entityMap.get(this.parentTypeName);
345
+ if (existingFieldNode) {
346
+ existingFieldNode.appearances += 1;
347
+ existingFieldNode.node.description = existingFieldNode.node.description || node.description;
348
+ existingFieldNode.subgraphs.add(this.currentSubgraphName);
349
+ existingFieldNode.subgraphsByShareable.set(this.currentSubgraphName, isFieldShareable);
350
+ const { typeErrors, typeNode } = (0, type_merging_1.getLeastRestrictiveMergedTypeNode)(existingFieldNode.node.type, node.type, this.parentTypeName, this.childName);
351
+ if (typeNode) {
352
+ existingFieldNode.node.type = typeNode;
353
+ }
354
+ else {
355
+ if (!typeErrors || typeErrors.length < 2) {
356
+ throw new Error(''); // TODO this should never happen
357
+ }
358
+ this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
359
+ }
360
+ this.upsertArgumentsForFieldNode(node, existingFieldNode);
361
+ if (!this.isCurrentParentInterface && !(existingFieldNode.isShareable || isFieldShareable)) {
362
+ // TODO
363
+ const shareableErrorTypeNames = this.shareableErrorTypeNames.get(this.parentTypeName);
364
+ if (shareableErrorTypeNames) {
365
+ shareableErrorTypeNames.add(this.childName);
366
+ }
367
+ else {
368
+ this.shareableErrorTypeNames.set(this.parentTypeName, new Set([this.childName]));
369
+ }
370
+ }
371
+ return;
372
+ }
373
+ const args = new Map();
374
+ this.extractArgumentsFromFieldNode(node, args);
375
+ this.outputFieldTypeNameSet.add(fieldRootTypeName);
376
+ fieldMap.set(this.childName, {
377
+ appearances: 1,
378
+ arguments: args,
379
+ isShareable: isFieldShareable,
380
+ node: (0, ast_1.fieldDefinitionNodeToMutable)(node, this.parentTypeName),
381
+ rootTypeName: fieldRootTypeName,
382
+ subgraphs: new Set([this.currentSubgraphName]),
383
+ subgraphsByShareable: new Map([[this.currentSubgraphName, isFieldShareable]]),
384
+ });
385
+ if (this.isParentRootType ||
386
+ entityParent?.fields.has(this.childName) ||
387
+ parent.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION) {
388
+ return;
389
+ }
390
+ this.addPotentiallyUnresolvableField(parent, this.childName);
391
+ }
392
+ upsertValueNode(node) {
393
+ const parent = this.parentMap.get(this.parentTypeName);
394
+ switch (node.kind) {
395
+ case graphql_1.Kind.ENUM_VALUE_DEFINITION:
396
+ if (!parent) {
397
+ // This should never happen
398
+ throw (0, errors_1.federationInvalidParentTypeError)(this.parentTypeName, this.childName);
399
+ }
400
+ if (parent.kind !== graphql_1.Kind.ENUM_TYPE_DEFINITION) {
401
+ throw new Error(''); // TODO
402
+ }
403
+ const enumValues = parent.values;
404
+ const enumValue = enumValues.get(this.childName);
405
+ if (enumValue) {
406
+ enumValue.node.description = enumValue.node.description || node.description;
407
+ enumValue.appearances += 1;
408
+ return;
409
+ }
410
+ enumValues.set(this.childName, {
411
+ appearances: 1,
412
+ node: (0, ast_1.enumValueDefinitionNodeToMutable)(node),
413
+ });
414
+ return;
415
+ case graphql_1.Kind.INPUT_VALUE_DEFINITION:
416
+ if (!parent || !this.isParentInputObject) {
417
+ // TODO handle directives
418
+ return;
419
+ }
420
+ if (parent.kind !== graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
421
+ throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION, parent.kind);
422
+ }
423
+ const inputValues = parent.fields;
424
+ const inputValue = inputValues.get(this.childName);
425
+ if (inputValue) {
426
+ inputValue.appearances += 1;
427
+ inputValue.node.description = inputValue.node.description || node.description;
428
+ const { typeErrors, typeNode } = (0, type_merging_1.getMostRestrictiveMergedTypeNode)(inputValue.node.type, node.type, this.parentTypeName, this.childName);
429
+ if (typeNode) {
430
+ inputValue.node.type = typeNode;
431
+ }
432
+ else {
433
+ if (!typeErrors || typeErrors.length < 2) {
434
+ throw new Error(''); // TODO this should never happen
435
+ }
436
+ this.errors.push((0, errors_1.incompatibleChildTypesError)(this.parentTypeName, this.childName, typeErrors[0], typeErrors[1]));
437
+ }
438
+ return;
439
+ }
440
+ const valuePath = `${this.parentTypeName}.${this.childName}`;
441
+ const inputValueNamedType = (0, type_merging_1.getNamedTypeForChild)(valuePath, node.type);
442
+ this.inputFieldTypeNameSet.add(inputValueNamedType);
443
+ inputValues.set(this.childName, {
444
+ appearances: 1,
445
+ includeDefaultValue: !!node.defaultValue,
446
+ node: (0, ast_1.inputValueDefinitionNodeToMutable)(node, this.parentTypeName),
447
+ });
448
+ return;
449
+ default:
450
+ throw (0, errors_1.unexpectedKindFatalError)(this.childName);
451
+ }
452
+ }
453
+ upsertParentNode(node) {
454
+ const parentTypeName = node.name.value;
455
+ const parent = this.parentMap.get(parentTypeName);
456
+ if (parent) {
457
+ parent.node.description = parent.node.description || node.description;
458
+ parent.appearances += 1;
459
+ }
460
+ switch (node.kind) {
461
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION:
462
+ if (parent) {
463
+ if (parent.kind !== node.kind) {
464
+ throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
465
+ }
466
+ return;
467
+ }
468
+ this.parentMap.set(parentTypeName, {
469
+ appearances: 1,
470
+ values: new Map(),
471
+ kind: node.kind,
472
+ node: (0, ast_1.enumTypeDefinitionNodeToMutable)(node),
473
+ });
474
+ return;
475
+ case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
476
+ if (parent) {
477
+ if (parent.kind !== node.kind) {
478
+ throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
479
+ }
480
+ return;
481
+ }
482
+ this.parentMap.set(parentTypeName, {
483
+ appearances: 1,
484
+ fields: new Map(),
485
+ kind: node.kind,
486
+ node: (0, ast_1.inputObjectTypeDefinitionNodeToMutable)(node),
487
+ });
488
+ return;
489
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
490
+ if (parent) {
491
+ if (parent.kind !== node.kind) {
492
+ throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
493
+ }
494
+ (0, utils_1.extractInterfaces)(node, parent.interfaces);
495
+ parent.subgraphs.add(this.currentSubgraphName);
496
+ return;
497
+ }
498
+ const nestedInterfaces = new Set();
499
+ (0, utils_1.extractInterfaces)(node, nestedInterfaces);
500
+ this.parentMap.set(parentTypeName, {
501
+ appearances: 1,
502
+ fields: new Map(),
503
+ interfaces: nestedInterfaces,
504
+ kind: node.kind,
505
+ node: (0, ast_1.interfaceTypeDefinitionNodeToMutable)(node),
506
+ subgraphs: new Set([this.currentSubgraphName]),
507
+ });
508
+ return;
509
+ case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
510
+ if (parent) {
511
+ if (parent.kind !== node.kind) {
512
+ throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
513
+ }
514
+ return;
515
+ }
516
+ this.parentMap.set(parentTypeName, {
517
+ appearances: 1,
518
+ kind: node.kind,
519
+ node: (0, ast_1.scalarTypeDefinitionNodeToMutable)(node),
520
+ });
521
+ return;
522
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
523
+ if (parent) {
524
+ if (parent.kind !== node.kind) {
525
+ throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
526
+ }
527
+ (0, utils_1.extractInterfaces)(node, parent.interfaces);
528
+ (0, utils_1.extractEntityKeys)(node, parent.entityKeys);
529
+ parent.subgraphs.add(this.currentSubgraphName);
530
+ return;
531
+ }
532
+ const interfaces = new Set();
533
+ (0, utils_1.extractInterfaces)(node, interfaces);
534
+ const entityKeys = new Set();
535
+ (0, utils_1.extractEntityKeys)(node, entityKeys);
536
+ this.parentMap.set(parentTypeName, {
537
+ appearances: 1,
538
+ fields: new Map(),
539
+ entityKeys,
540
+ interfaces,
541
+ isRootType: this.isParentRootType,
542
+ kind: node.kind,
543
+ node: (0, ast_1.objectTypeDefinitionNodeToMutable)(node),
544
+ subgraphs: new Set([this.currentSubgraphName]),
545
+ });
546
+ return;
547
+ case graphql_1.Kind.UNION_TYPE_DEFINITION:
548
+ if (parent) {
549
+ if (parent.kind !== node.kind) {
550
+ throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, node.kind, parent.kind);
551
+ }
552
+ if (!node.types || node.types.length < 1) {
553
+ this.errors.push((0, errors_1.invalidUnionError)(parent.node.name.value));
554
+ return;
555
+ }
556
+ node.types?.forEach((member) => parent.members.add(member.name.value));
557
+ return;
558
+ }
559
+ this.parentMap.set(parentTypeName, {
560
+ appearances: 1,
561
+ kind: node.kind,
562
+ members: new Set(node.types?.map((member) => member.name.value)),
563
+ node: (0, ast_1.unionTypeDefinitionNodeToMutable)(node),
564
+ });
565
+ return;
566
+ }
567
+ }
568
+ upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName = fieldTypeName, hasAbstractParent = false) {
569
+ const operationFields = this.operationFieldsByResponseTypeName.get(concreteTypeName);
570
+ if (!operationFields) {
571
+ this.operationFieldsByResponseTypeName.set(concreteTypeName, new Map([
572
+ [
573
+ operationFieldPath,
574
+ {
575
+ inlineFragment: hasAbstractParent ? (0, utils_1.getInlineFragmentString)(concreteTypeName) : '.',
576
+ name: fieldName,
577
+ parentTypeName: this.parentTypeName,
578
+ path: operationFieldPath,
579
+ responseType,
580
+ rootTypeName: fieldTypeName,
581
+ subgraphs: new Set([this.currentSubgraphName]),
582
+ },
583
+ ],
584
+ ]));
585
+ return;
586
+ }
587
+ const operationField = operationFields.get(operationFieldPath);
588
+ if (operationField) {
589
+ operationField.subgraphs.add(this.currentSubgraphName);
590
+ return;
591
+ }
592
+ operationFields.set(operationFieldPath, {
593
+ inlineFragment: hasAbstractParent ? (0, utils_1.getInlineFragmentString)(concreteTypeName) : '.',
594
+ name: fieldName,
595
+ parentTypeName: this.parentTypeName,
596
+ path: operationFieldPath,
597
+ responseType,
598
+ rootTypeName: fieldTypeName,
599
+ subgraphs: new Set([this.currentSubgraphName]),
600
+ });
601
+ }
602
+ upsertAbstractObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeNames) {
603
+ for (const concreteTypeName of concreteTypeNames) {
604
+ if (!this.graph.hasNode(concreteTypeName)) {
605
+ throw (0, errors_1.invalidMultiGraphNodeFatalError)(concreteTypeName); // should never happen
606
+ }
607
+ if (!this.graphEdges.has(operationFieldPath)) {
608
+ this.graph.addEdge(this.parentTypeName, concreteTypeName, { fieldName });
609
+ }
610
+ // Always upsert the operation field node to record subgraph appearances
611
+ this.upsertConcreteObjectLikeOperationFieldNode(fieldName, fieldTypeName, operationFieldPath, responseType, concreteTypeName, true);
612
+ }
613
+ // Add the path so the edges are not added again
614
+ this.graphEdges.add(operationFieldPath);
615
+ }
616
+ validatePotentiallyUnresolvableFields() {
617
+ if (this.sharedOperationDependentResponses.size < 1) {
618
+ return;
619
+ }
620
+ for (const [parentTypeName, potentiallyUnresolvableFields] of this.sharedOperationDependentResponses) {
621
+ for (const potentiallyUnresolvableField of potentiallyUnresolvableFields) {
622
+ // There is no issue if the field is resolvable from at least one subgraph
623
+ const operationField = potentiallyUnresolvableField.operationField;
624
+ const fieldContainer = potentiallyUnresolvableField.fieldContainer;
625
+ if ((0, utils_2.doSetsHaveAnyOverlap)(fieldContainer.subgraphs, operationField.subgraphs)) {
626
+ continue;
627
+ }
628
+ const fieldSubgraphs = Array.from(fieldContainer.subgraphs).join('", "');
629
+ this.errors.push((0, errors_1.unresolvableFieldError)(operationField, fieldContainer.node.name.value, potentiallyUnresolvableField.fullResolverPaths, fieldSubgraphs, parentTypeName));
630
+ }
631
+ }
632
+ }
633
+ upsertExtensionNode(node) {
634
+ const extension = this.extensions.get(this.parentTypeName);
635
+ if (extension) {
636
+ if (extension.kind !== graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
637
+ throw (0, errors_1.incompatibleParentKindFatalError)(this.parentTypeName, graphql_1.Kind.OBJECT_TYPE_EXTENSION, extension.kind);
638
+ }
639
+ extension.appearances += 1;
640
+ extension.subgraphs.add(this.currentSubgraphName);
641
+ (0, utils_1.extractInterfaces)(node, extension.interfaces);
642
+ return;
643
+ }
644
+ // build a new extension
645
+ const interfaces = new Set();
646
+ (0, utils_1.extractInterfaces)(node, interfaces);
647
+ const entityKeys = new Set();
648
+ (0, utils_1.extractEntityKeys)(node, entityKeys);
649
+ this.extensions.set(this.parentTypeName, {
650
+ appearances: 1,
651
+ subgraphs: new Set([this.currentSubgraphName]),
652
+ isRootType: this.isParentRootType,
653
+ kind: graphql_1.Kind.OBJECT_TYPE_EXTENSION,
654
+ node: (0, ast_1.objectTypeExtensionNodeToMutable)(node),
655
+ fields: new Map(),
656
+ entityKeys,
657
+ interfaces,
658
+ });
659
+ }
660
+ federate() {
661
+ this.populateMultiGraphAndRenameOperations(this.subgraphs);
662
+ const factory = this;
663
+ for (const subgraph of this.subgraphs) {
664
+ this.isCurrentSubgraphVersionTwo = subgraph.isVersionTwo;
665
+ this.currentSubgraphName = subgraph.name;
666
+ (0, subgraph_1.walkSubgraphToFederate)(subgraph.definitions, factory);
667
+ }
668
+ this.validatePotentiallyUnresolvableFields();
669
+ const definitions = [];
670
+ for (const definition of this.directiveDefinitions.values()) {
671
+ definitions.push(definition);
672
+ }
673
+ for (const [typeName, extension] of this.extensions) {
674
+ if (extension.isRootType && !this.parentMap.has(typeName)) {
675
+ this.upsertParentNode((0, ast_1.objectTypeExtensionNodeToMutableDefinitionNode)(extension.node));
676
+ }
677
+ const baseObject = this.parentMap.get(typeName);
678
+ if (!baseObject) {
679
+ this.errors.push((0, errors_1.noBaseTypeExtensionError)(typeName));
680
+ continue;
681
+ }
682
+ if (baseObject.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
683
+ throw (0, errors_1.incompatibleParentKindFatalError)(typeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, baseObject.kind);
684
+ }
685
+ // TODO check directives
686
+ for (const [fieldName, field] of extension.fields) {
687
+ const baseField = baseObject.fields.get(fieldName);
688
+ if (!baseField) {
689
+ baseObject.fields.set(fieldName, field);
690
+ continue;
691
+ }
692
+ if (baseField.isShareable && field.isShareable) {
693
+ continue;
694
+ }
695
+ const parent = this.shareableErrorTypeNames.get(typeName);
696
+ if (parent) {
697
+ parent.add(fieldName);
698
+ continue;
699
+ }
700
+ this.shareableErrorTypeNames.set(typeName, new Set([fieldName]));
701
+ }
702
+ for (const interfaceName of extension.interfaces) {
703
+ baseObject.interfaces.add(interfaceName);
704
+ }
705
+ }
706
+ for (const [parentTypeName, children] of this.shareableErrorTypeNames) {
707
+ const parent = (0, utils_2.getOrThrowError)(this.parentMap, parentTypeName);
708
+ if (parent.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
709
+ throw (0, errors_1.incompatibleParentKindFatalError)(parentTypeName, graphql_1.Kind.OBJECT_TYPE_DEFINITION, parent.kind);
710
+ }
711
+ this.errors.push((0, errors_1.shareableFieldDefinitionsError)(parent, children));
712
+ }
713
+ for (const parent of this.parentMap.values()) {
714
+ const parentName = parent.node.name.value;
715
+ switch (parent.kind) {
716
+ case graphql_1.Kind.ENUM_TYPE_DEFINITION:
717
+ const values = [];
718
+ const mergeMethod = this.getEnumMergeMethod(parentName);
719
+ for (const value of parent.values.values()) {
720
+ switch (mergeMethod) {
721
+ case utils_1.MergeMethod.CONSISTENT:
722
+ if (value.appearances < parent.appearances) {
723
+ this.errors.push((0, errors_1.incompatibleSharedEnumError)(parentName));
724
+ }
725
+ values.push(value.node);
726
+ break;
727
+ case utils_1.MergeMethod.INTERSECTION:
728
+ if (value.appearances === parent.appearances) {
729
+ values.push(value.node);
730
+ }
731
+ break;
732
+ case utils_1.MergeMethod.UNION:
733
+ values.push(value.node);
734
+ break;
735
+ }
736
+ }
737
+ parent.node.values = values;
738
+ definitions.push(parent.node);
739
+ break;
740
+ case graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION:
741
+ const inputValues = [];
742
+ for (const value of parent.fields.values()) {
743
+ if (parent.appearances === value.appearances) {
744
+ inputValues.push(value.node);
745
+ }
746
+ else if ((0, type_merging_1.isTypeRequired)(value.node.type)) {
747
+ // TODO append to errors
748
+ throw (0, errors_1.federationRequiredInputFieldError)(parentName, value.node.name.value);
749
+ }
750
+ }
751
+ parent.node.fields = inputValues;
752
+ definitions.push(parent.node);
753
+ break;
754
+ case graphql_1.Kind.INTERFACE_TYPE_DEFINITION:
755
+ const interfaceFields = [];
756
+ for (const field of parent.fields.values()) {
757
+ if (field.arguments) {
758
+ const args = [];
759
+ for (const arg of field.arguments.values()) {
760
+ arg.node.defaultValue = arg.includeDefaultValue ? arg.node.defaultValue : undefined;
761
+ args.push(arg.node);
762
+ }
763
+ field.node.arguments = args;
764
+ }
765
+ interfaceFields.push(field.node);
766
+ }
767
+ const otherInterfaces = [];
768
+ for (const iFace of parent.interfaces) {
769
+ otherInterfaces.push({
770
+ kind: graphql_1.Kind.NAMED_TYPE,
771
+ name: {
772
+ kind: graphql_1.Kind.NAME,
773
+ value: iFace,
774
+ },
775
+ });
776
+ }
777
+ parent.node.interfaces = otherInterfaces;
778
+ parent.node.fields = interfaceFields;
779
+ definitions.push(parent.node);
780
+ break;
781
+ case graphql_1.Kind.OBJECT_TYPE_DEFINITION:
782
+ const fields = [];
783
+ for (const field of parent.fields.values()) {
784
+ if (field.arguments) {
785
+ const args = [];
786
+ for (const arg of field.arguments.values()) {
787
+ arg.node.defaultValue = arg.includeDefaultValue ? arg.node.defaultValue : undefined;
788
+ args.push(arg.node);
789
+ }
790
+ field.node.arguments = args;
791
+ }
792
+ fields.push(field.node);
793
+ }
794
+ const interfaces = [];
795
+ for (const iFace of parent.interfaces) {
796
+ interfaces.push({
797
+ kind: graphql_1.Kind.NAMED_TYPE,
798
+ name: {
799
+ kind: graphql_1.Kind.NAME,
800
+ value: iFace,
801
+ },
802
+ });
803
+ }
804
+ parent.node.fields = fields;
805
+ parent.node.interfaces = interfaces;
806
+ definitions.push(parent.node);
807
+ break;
808
+ case graphql_1.Kind.SCALAR_TYPE_DEFINITION:
809
+ definitions.push(parent.node);
810
+ break;
811
+ case graphql_1.Kind.UNION_TYPE_DEFINITION:
812
+ const types = [];
813
+ for (const member of parent.members) {
814
+ types.push({
815
+ kind: graphql_1.Kind.NAMED_TYPE,
816
+ name: {
817
+ kind: graphql_1.Kind.NAME,
818
+ value: member,
819
+ },
820
+ });
821
+ }
822
+ parent.node.types = types;
823
+ definitions.push(parent.node);
824
+ break;
825
+ }
826
+ }
827
+ if (this.errors.length > 0) {
828
+ return {
829
+ errors: this.errors,
830
+ };
831
+ }
832
+ const newAst = {
833
+ kind: graphql_1.Kind.DOCUMENT,
834
+ definitions,
835
+ };
836
+ return {
837
+ federatedGraphAST: newAst,
838
+ federatedGraphSchema: (0, graphql_1.buildASTSchema)(newAst),
839
+ };
840
+ }
841
+ }
842
+ exports.FederationFactory = FederationFactory;
843
+ //# sourceMappingURL=federation-factory.js.map