@theguild/federation-composition 0.9.0 → 0.10.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 (126) hide show
  1. package/README.md +2 -1
  2. package/cjs/specifications/link.js +40 -5
  3. package/cjs/subgraph/helpers.js +2 -2
  4. package/cjs/subgraph/state.js +8 -0
  5. package/cjs/subgraph/validation/rules/elements/provides.js +8 -6
  6. package/cjs/subgraph/validation/rules/elements/requires.js +9 -7
  7. package/cjs/subgraph/validation/validate-state.js +9 -3
  8. package/cjs/subgraph/validation/validate-subgraph.js +4 -2
  9. package/cjs/subgraph/validation/validation-context.js +10 -0
  10. package/cjs/supergraph/composition/directive.js +3 -0
  11. package/cjs/supergraph/composition/enum-type.js +3 -0
  12. package/cjs/supergraph/composition/input-object-type.js +3 -0
  13. package/cjs/supergraph/composition/interface-type.js +4 -0
  14. package/cjs/supergraph/composition/object-type.js +18 -20
  15. package/cjs/supergraph/composition/scalar-type.js +2 -0
  16. package/cjs/supergraph/composition/union-type.js +2 -0
  17. package/cjs/supergraph/state.js +24 -26
  18. package/cjs/supergraph/validation/rules/fields-of-the-same-type-rule.js +35 -0
  19. package/cjs/supergraph/validation/rules/invalid-field-sharing-rule.js +4 -1
  20. package/cjs/supergraph/validation/rules/satisfiablity/constants.js +4 -0
  21. package/cjs/supergraph/validation/rules/satisfiablity/edge.js +64 -0
  22. package/cjs/supergraph/validation/rules/satisfiablity/errors.js +44 -0
  23. package/cjs/supergraph/validation/rules/satisfiablity/fields.js +147 -0
  24. package/cjs/supergraph/validation/rules/satisfiablity/finder.js +267 -0
  25. package/cjs/supergraph/validation/rules/satisfiablity/graph.js +675 -0
  26. package/cjs/supergraph/validation/rules/satisfiablity/helpers.js +41 -0
  27. package/cjs/supergraph/validation/rules/satisfiablity/move-validator.js +337 -0
  28. package/cjs/supergraph/validation/rules/satisfiablity/moves.js +52 -0
  29. package/cjs/supergraph/validation/rules/satisfiablity/node.js +89 -0
  30. package/cjs/supergraph/validation/rules/satisfiablity/operation-path.js +70 -0
  31. package/cjs/supergraph/validation/rules/satisfiablity/supergraph.js +37 -0
  32. package/cjs/supergraph/validation/rules/satisfiablity/walker.js +306 -0
  33. package/cjs/supergraph/validation/rules/satisfiablity-rule.js +45 -1081
  34. package/cjs/supergraph/validation/validate-supergraph.js +1 -1
  35. package/cjs/utils/logger.js +127 -0
  36. package/esm/specifications/link.js +40 -5
  37. package/esm/subgraph/helpers.js +2 -2
  38. package/esm/subgraph/state.js +8 -0
  39. package/esm/subgraph/validation/rules/elements/provides.js +8 -6
  40. package/esm/subgraph/validation/rules/elements/requires.js +9 -7
  41. package/esm/subgraph/validation/validate-state.js +9 -3
  42. package/esm/subgraph/validation/validate-subgraph.js +4 -2
  43. package/esm/subgraph/validation/validation-context.js +11 -1
  44. package/esm/supergraph/composition/directive.js +3 -0
  45. package/esm/supergraph/composition/enum-type.js +3 -0
  46. package/esm/supergraph/composition/input-object-type.js +3 -0
  47. package/esm/supergraph/composition/interface-type.js +4 -0
  48. package/esm/supergraph/composition/object-type.js +18 -20
  49. package/esm/supergraph/composition/scalar-type.js +2 -0
  50. package/esm/supergraph/composition/union-type.js +2 -0
  51. package/esm/supergraph/state.js +24 -26
  52. package/esm/supergraph/validation/rules/fields-of-the-same-type-rule.js +35 -0
  53. package/esm/supergraph/validation/rules/invalid-field-sharing-rule.js +4 -1
  54. package/esm/supergraph/validation/rules/satisfiablity/constants.js +1 -0
  55. package/esm/supergraph/validation/rules/satisfiablity/edge.js +54 -0
  56. package/esm/supergraph/validation/rules/satisfiablity/errors.js +40 -0
  57. package/esm/supergraph/validation/rules/satisfiablity/fields.js +142 -0
  58. package/esm/supergraph/validation/rules/satisfiablity/finder.js +261 -0
  59. package/esm/supergraph/validation/rules/satisfiablity/graph.js +671 -0
  60. package/esm/supergraph/validation/rules/satisfiablity/helpers.js +35 -0
  61. package/esm/supergraph/validation/rules/satisfiablity/move-validator.js +333 -0
  62. package/esm/supergraph/validation/rules/satisfiablity/moves.js +46 -0
  63. package/esm/supergraph/validation/rules/satisfiablity/node.js +85 -0
  64. package/esm/supergraph/validation/rules/satisfiablity/operation-path.js +66 -0
  65. package/esm/supergraph/validation/rules/satisfiablity/supergraph.js +33 -0
  66. package/esm/supergraph/validation/rules/satisfiablity/walker.js +301 -0
  67. package/esm/supergraph/validation/rules/satisfiablity-rule.js +40 -1076
  68. package/esm/supergraph/validation/validate-supergraph.js +1 -1
  69. package/esm/utils/logger.js +119 -0
  70. package/package.json +2 -1
  71. package/typings/subgraph/state.d.cts +2 -0
  72. package/typings/subgraph/state.d.ts +2 -0
  73. package/typings/subgraph/validation/validate-state.d.cts +2 -1
  74. package/typings/subgraph/validation/validate-state.d.ts +2 -1
  75. package/typings/subgraph/validation/validation-context.d.cts +3 -0
  76. package/typings/subgraph/validation/validation-context.d.ts +3 -0
  77. package/typings/supergraph/composition/common.d.cts +2 -1
  78. package/typings/supergraph/composition/common.d.ts +2 -1
  79. package/typings/supergraph/composition/directive.d.cts +4 -0
  80. package/typings/supergraph/composition/directive.d.ts +4 -0
  81. package/typings/supergraph/composition/enum-type.d.cts +4 -0
  82. package/typings/supergraph/composition/enum-type.d.ts +4 -0
  83. package/typings/supergraph/composition/input-object-type.d.cts +4 -0
  84. package/typings/supergraph/composition/input-object-type.d.ts +4 -0
  85. package/typings/supergraph/composition/interface-type.d.cts +5 -0
  86. package/typings/supergraph/composition/interface-type.d.ts +5 -0
  87. package/typings/supergraph/composition/object-type.d.cts +5 -0
  88. package/typings/supergraph/composition/object-type.d.ts +5 -0
  89. package/typings/supergraph/composition/scalar-type.d.cts +3 -0
  90. package/typings/supergraph/composition/scalar-type.d.ts +3 -0
  91. package/typings/supergraph/composition/union-type.d.cts +3 -0
  92. package/typings/supergraph/composition/union-type.d.ts +3 -0
  93. package/typings/supergraph/state.d.cts +4 -4
  94. package/typings/supergraph/state.d.ts +4 -4
  95. package/typings/supergraph/validation/rules/satisfiablity/constants.d.cts +2 -0
  96. package/typings/supergraph/validation/rules/satisfiablity/constants.d.ts +2 -0
  97. package/typings/supergraph/validation/rules/satisfiablity/edge.d.cts +31 -0
  98. package/typings/supergraph/validation/rules/satisfiablity/edge.d.ts +31 -0
  99. package/typings/supergraph/validation/rules/satisfiablity/errors.d.cts +17 -0
  100. package/typings/supergraph/validation/rules/satisfiablity/errors.d.ts +17 -0
  101. package/typings/supergraph/validation/rules/satisfiablity/fields.d.cts +33 -0
  102. package/typings/supergraph/validation/rules/satisfiablity/fields.d.ts +33 -0
  103. package/typings/supergraph/validation/rules/satisfiablity/finder.d.cts +28 -0
  104. package/typings/supergraph/validation/rules/satisfiablity/finder.d.ts +28 -0
  105. package/typings/supergraph/validation/rules/satisfiablity/graph.d.cts +63 -0
  106. package/typings/supergraph/validation/rules/satisfiablity/graph.d.ts +63 -0
  107. package/typings/supergraph/validation/rules/satisfiablity/helpers.d.cts +7 -0
  108. package/typings/supergraph/validation/rules/satisfiablity/helpers.d.ts +7 -0
  109. package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.cts +25 -0
  110. package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.ts +25 -0
  111. package/typings/supergraph/validation/rules/satisfiablity/moves.d.cts +24 -0
  112. package/typings/supergraph/validation/rules/satisfiablity/moves.d.ts +24 -0
  113. package/typings/supergraph/validation/rules/satisfiablity/node.d.cts +31 -0
  114. package/typings/supergraph/validation/rules/satisfiablity/node.d.ts +31 -0
  115. package/typings/supergraph/validation/rules/satisfiablity/operation-path.d.cts +29 -0
  116. package/typings/supergraph/validation/rules/satisfiablity/operation-path.d.ts +29 -0
  117. package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.cts +14 -0
  118. package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.ts +14 -0
  119. package/typings/supergraph/validation/rules/satisfiablity/walker.d.cts +35 -0
  120. package/typings/supergraph/validation/rules/satisfiablity/walker.d.ts +35 -0
  121. package/typings/utils/logger.d.cts +33 -0
  122. package/typings/utils/logger.d.ts +33 -0
  123. package/cjs/utils/dependency-graph.js +0 -227
  124. package/esm/utils/dependency-graph.js +0 -222
  125. package/typings/utils/dependency-graph.d.cts +0 -31
  126. package/typings/utils/dependency-graph.d.ts +0 -31
@@ -28,6 +28,7 @@ export function scalarTypeBuilder() {
28
28
  });
29
29
  scalarTypeState.byGraph.set(graph.id, {
30
30
  inaccessible: type.inaccessible,
31
+ version: graph.version,
31
32
  });
32
33
  },
33
34
  composeSupergraphNode(scalarType) {
@@ -58,6 +59,7 @@ function getOrCreateScalarType(state, typeName) {
58
59
  return existing;
59
60
  }
60
61
  const def = {
62
+ kind: 'scalar',
61
63
  name: typeName,
62
64
  tags: new Set(),
63
65
  inaccessible: false,
@@ -15,6 +15,7 @@ export function unionTypeBuilder() {
15
15
  }
16
16
  unionTypeState.byGraph.set(graph.id, {
17
17
  members: type.members,
18
+ version: graph.version,
18
19
  });
19
20
  for (const member of type.members) {
20
21
  unionTypeState.members.add(member);
@@ -48,6 +49,7 @@ function getOrCreateUnionType(state, typeName) {
48
49
  return existing;
49
50
  }
50
51
  const def = {
52
+ kind: 'union',
51
53
  name: typeName,
52
54
  members: new Set(),
53
55
  tags: new Set(),
@@ -12,7 +12,7 @@ import { scalarTypeBuilder } from './composition/scalar-type.js';
12
12
  import { unionTypeBuilder } from './composition/union-type.js';
13
13
  export function createSupergraphStateBuilder() {
14
14
  const state = {
15
- graphs: new Map(),
15
+ subgraphs: new Map(),
16
16
  scalarTypes: new Map(),
17
17
  objectTypes: new Map(),
18
18
  interfaceTypes: new Map(),
@@ -36,22 +36,21 @@ export function createSupergraphStateBuilder() {
36
36
  const interfaceType = interfaceTypeBuilder();
37
37
  const objectType = objectTypeBuilder();
38
38
  const unionType = unionTypeBuilder();
39
+ const subgraphStates = new Map();
40
+ const graphNameToIdMap = {};
39
41
  return {
40
- addGraph(graph) {
41
- if (state.graphs.has(graph.id)) {
42
- throw new Error(`Graph with ID "${graph.id}" already exists`);
42
+ addSubgraph(subgraph) {
43
+ if (state.subgraphs.has(subgraph.graph.id)) {
44
+ throw new Error(`Graph with ID "${subgraph.graph.id}" already exists`);
43
45
  }
44
- state.graphs.set(graph.id, {
45
- id: graph.id,
46
- name: graph.name,
47
- url: graph.url,
48
- version: graph.version,
49
- });
46
+ state.subgraphs.set(subgraph.graph.id, subgraph);
47
+ graphNameToIdMap[subgraph.graph.name] = subgraph.graph.id;
50
48
  },
51
49
  getGraph(id) {
52
- return state.graphs.get(id);
50
+ return state.subgraphs.get(id);
53
51
  },
54
52
  visitSubgraphState(subgraphState) {
53
+ subgraphStates.set(subgraphState.graph.id, subgraphState);
55
54
  for (const link of subgraphState.links) {
56
55
  state.links.push(linkWithGraph(link, subgraphState.graph.id));
57
56
  }
@@ -106,15 +105,14 @@ export function createSupergraphStateBuilder() {
106
105
  getSupergraphState() {
107
106
  return state;
108
107
  },
108
+ getSubgraphState(graphId) {
109
+ return subgraphStates.get(graphId);
110
+ },
109
111
  links() {
110
112
  return mergeLinks(state.links);
111
113
  },
112
114
  build() {
113
115
  const transformFields = createFieldsTransformer(state);
114
- const graphNameToIdMap = {};
115
- for (const [id, graph] of state.graphs) {
116
- graphNameToIdMap[graph.name] = id;
117
- }
118
116
  const helpers = {
119
117
  graphNameToId(graphName) {
120
118
  return graphNameToIdMap[graphName] ?? null;
@@ -131,31 +129,31 @@ export function createSupergraphStateBuilder() {
131
129
  1;
132
130
  const nodes = new Array(numberOfExpectedNodes);
133
131
  let i = 0;
134
- nodes[i++] = createJoinGraphEnumTypeNode(Array.from(state.graphs.values()).map(graph => ({
135
- name: graph.name,
136
- enumValue: graph.id,
137
- url: graph.url ?? '',
132
+ nodes[i++] = createJoinGraphEnumTypeNode(Array.from(state.subgraphs.values()).map(subgraph => ({
133
+ name: subgraph.graph.name,
134
+ enumValue: subgraph.graph.id,
135
+ url: subgraph.graph.url ?? '',
138
136
  })));
139
137
  for (const directiveState of state.directives.values()) {
140
- nodes[i++] = directive.composeSupergraphNode(directiveState, state.graphs, helpers);
138
+ nodes[i++] = directive.composeSupergraphNode(directiveState, state.subgraphs, helpers);
141
139
  }
142
140
  for (const scalarTypeState of state.scalarTypes.values()) {
143
- nodes[i++] = scalarType.composeSupergraphNode(scalarTypeState, state.graphs, helpers);
141
+ nodes[i++] = scalarType.composeSupergraphNode(scalarTypeState, state.subgraphs, helpers);
144
142
  }
145
143
  for (const objectTypeState of state.objectTypes.values()) {
146
- nodes[i++] = objectType.composeSupergraphNode(transformFields(objectTypeState), state.graphs, helpers);
144
+ nodes[i++] = objectType.composeSupergraphNode(transformFields(objectTypeState), state.subgraphs, helpers);
147
145
  }
148
146
  for (const interfaceTypeState of state.interfaceTypes.values()) {
149
- nodes[i++] = interfaceType.composeSupergraphNode(interfaceTypeState, state.graphs, helpers);
147
+ nodes[i++] = interfaceType.composeSupergraphNode(interfaceTypeState, state.subgraphs, helpers);
150
148
  }
151
149
  for (const unionTypeState of state.unionTypes.values()) {
152
- nodes[i++] = unionType.composeSupergraphNode(unionTypeState, state.graphs, helpers);
150
+ nodes[i++] = unionType.composeSupergraphNode(unionTypeState, state.subgraphs, helpers);
153
151
  }
154
152
  for (const enumTypeState of state.enumTypes.values()) {
155
- nodes[i++] = enumType.composeSupergraphNode(enumTypeState, state.graphs, helpers);
153
+ nodes[i++] = enumType.composeSupergraphNode(enumTypeState, state.subgraphs, helpers);
156
154
  }
157
155
  for (const inputObjectTypeState of state.inputObjectTypes.values()) {
158
- nodes[i++] = inputObjectType.composeSupergraphNode(inputObjectTypeState, state.graphs, helpers);
156
+ nodes[i++] = inputObjectType.composeSupergraphNode(inputObjectTypeState, state.subgraphs, helpers);
159
157
  }
160
158
  return nodes;
161
159
  },
@@ -1,4 +1,5 @@
1
1
  import { GraphQLError } from 'graphql';
2
+ import { TypeKind } from '../../../subgraph/state.js';
2
3
  function stripNonNull(type) {
3
4
  return type.replace(/!$/, '');
4
5
  }
@@ -33,7 +34,28 @@ export function FieldsOfTheSameTypeRule(context) {
33
34
  return {
34
35
  ObjectTypeField(objectTypeState, fieldState) {
35
36
  const typeToGraphs = new Map();
37
+ const typeNameToPossibleTypeNames = new Map();
36
38
  fieldState.byGraph.forEach((field, graphName) => {
39
+ const typeName = field.type.replaceAll('!', '').replaceAll('[', '').replaceAll(']', '');
40
+ const typeState = context.subgraphStates.get(graphName)?.types.get(typeName);
41
+ if (typeState?.kind === TypeKind.UNION) {
42
+ if (!typeNameToPossibleTypeNames.has(typeName)) {
43
+ typeNameToPossibleTypeNames.set(typeName, new Set());
44
+ }
45
+ const list = typeNameToPossibleTypeNames.get(typeName);
46
+ typeState.members.forEach(member => {
47
+ list.add(member);
48
+ });
49
+ }
50
+ else if (typeState?.kind === TypeKind.INTERFACE) {
51
+ if (!typeNameToPossibleTypeNames.has(typeName)) {
52
+ typeNameToPossibleTypeNames.set(typeName, new Set());
53
+ }
54
+ const list = typeNameToPossibleTypeNames.get(typeName);
55
+ typeState.implementedBy.forEach(member => {
56
+ list.add(member);
57
+ });
58
+ }
37
59
  const normalizedOutputTypes = normalizeOutputTypeStrings({
38
60
  superType: fieldState.type,
39
61
  localType: field.type,
@@ -50,6 +72,19 @@ export function FieldsOfTheSameTypeRule(context) {
50
72
  }
51
73
  });
52
74
  if (typeToGraphs.size > 1) {
75
+ if (typeNameToPossibleTypeNames.size === 1) {
76
+ const possibleTypeNames = [];
77
+ typeNameToPossibleTypeNames.forEach((list, unionOrInterfaceName) => {
78
+ possibleTypeNames.push(unionOrInterfaceName);
79
+ for (const typeName of list) {
80
+ possibleTypeNames.push(typeName);
81
+ }
82
+ });
83
+ const outputTypeNames = Array.from(typeToGraphs.keys()).map(t => t.replaceAll('!', '').replaceAll('[', '').replaceAll(']', ''));
84
+ if (outputTypeNames.every(t => possibleTypeNames.includes(t))) {
85
+ return;
86
+ }
87
+ }
53
88
  const groups = Array.from(typeToGraphs.entries()).map(([outputType, graphs]) => {
54
89
  const plural = graphs.length > 1 ? 's' : '';
55
90
  return `type "${outputType}" in subgraph${plural} "${graphs
@@ -33,7 +33,10 @@ export function InvalidFieldSharingRule(context) {
33
33
  continue;
34
34
  }
35
35
  }
36
- if (fieldIsShareable || fieldIsUsedAsKey) {
36
+ const fedV1FieldInExtension = field.version === 'v1.0' && field.extension;
37
+ if (fieldIsShareable ||
38
+ fieldIsUsedAsKey ||
39
+ (objectTypeState.isEntity && fedV1FieldInExtension)) {
37
40
  resolvableIn.push(graphId);
38
41
  continue;
39
42
  }
@@ -0,0 +1 @@
1
+ export const SUPERGRAPH_ID = Symbol('__supergraph__');
@@ -0,0 +1,54 @@
1
+ import { lazy } from './helpers';
2
+ import { AbstractMove, EntityMove, FieldMove } from './moves';
3
+ export function isEntityEdge(edge) {
4
+ return edge.move instanceof EntityMove;
5
+ }
6
+ export function assertEntityEdge(edge) {
7
+ if (!isEntityEdge(edge)) {
8
+ throw new Error(`Expected edge to be Edge<EntityMove>, but got ${edge}`);
9
+ }
10
+ }
11
+ export function isAbstractEdge(edge) {
12
+ return edge.move instanceof AbstractMove;
13
+ }
14
+ export function assertAbstractEdge(edge) {
15
+ if (!isAbstractEdge(edge)) {
16
+ throw new Error(`Expected edge to be Edge<AbstractMove>, but got ${edge}`);
17
+ }
18
+ }
19
+ export function isFieldEdge(edge) {
20
+ return edge.move instanceof FieldMove;
21
+ }
22
+ export function assertFieldEdge(edge) {
23
+ if (!isFieldEdge(edge)) {
24
+ throw new Error(`Expected edge to be Edge<FieldMove>, but got ${edge}`);
25
+ }
26
+ }
27
+ export class Edge {
28
+ head;
29
+ move;
30
+ tail;
31
+ resolvable = [];
32
+ _toString = lazy(() => `${this.head} -(${this.move})-> ${this.tail}`);
33
+ constructor(head, move, tail) {
34
+ this.head = head;
35
+ this.move = move;
36
+ this.tail = tail;
37
+ }
38
+ isCrossGraphEdge() {
39
+ return this.head.graphId !== this.tail.graphId;
40
+ }
41
+ toString() {
42
+ return this._toString.get();
43
+ }
44
+ getResolvability(graphNames) {
45
+ return this.resolvable.find(([checkedGraphNames]) => {
46
+ return checkedGraphNames.every(name => graphNames.includes(name));
47
+ })?.[1];
48
+ }
49
+ setResolvable(success, graphNames, error) {
50
+ const result = success ? { success, error: undefined } : { success, error: error };
51
+ this.resolvable.push([graphNames, result]);
52
+ return result;
53
+ }
54
+ }
@@ -0,0 +1,40 @@
1
+ export class SatisfiabilityError extends Error {
2
+ kind;
3
+ sourceGraphName;
4
+ typeName;
5
+ fieldName;
6
+ static forKey(sourceGraphName, targetGraphName, typeName, keyFields) {
7
+ return new SatisfiabilityError('KEY', sourceGraphName, typeName, null, `cannot move to subgraph "${targetGraphName}" using @key(fields: "${keyFields}") of "${typeName}", the key field(s) cannot be resolved from subgraph "${sourceGraphName}".`);
8
+ }
9
+ static forRequire(sourceGraphName, typeName, fieldName) {
10
+ return new SatisfiabilityError('REQUIRE', sourceGraphName, typeName, fieldName, `cannot satisfy @require conditions on field "${typeName}.${fieldName}".`);
11
+ }
12
+ static forExternal(sourceGraphName, typeName, fieldName) {
13
+ return new SatisfiabilityError('EXTERNAL', sourceGraphName, typeName, fieldName, `field "${typeName}.${fieldName}" is not resolvable because marked @external.`);
14
+ }
15
+ static forMissingField(sourceGraphName, typeName, fieldName) {
16
+ return new SatisfiabilityError('MISSING_FIELD', sourceGraphName, typeName, fieldName, `cannot find field "${typeName}.${fieldName}".`);
17
+ }
18
+ static forNoKey(sourceGraphName, targetGraphName, typeName, fieldName) {
19
+ return new SatisfiabilityError('NO_KEY', sourceGraphName, typeName, fieldName, `cannot move to subgraph "${targetGraphName}", which has field "${typeName}.${fieldName}", because type "${typeName}" has no @key defined in subgraph "${targetGraphName}".`);
20
+ }
21
+ constructor(kind, sourceGraphName, typeName, fieldName, message) {
22
+ super(message);
23
+ this.kind = kind;
24
+ this.sourceGraphName = sourceGraphName;
25
+ this.typeName = typeName;
26
+ this.fieldName = fieldName;
27
+ }
28
+ isMatchingField(typeName, fieldName) {
29
+ if (this.typeName !== typeName) {
30
+ return false;
31
+ }
32
+ if (this.fieldName) {
33
+ return this.fieldName === fieldName;
34
+ }
35
+ return true;
36
+ }
37
+ toString() {
38
+ return this.message;
39
+ }
40
+ }
@@ -0,0 +1,142 @@
1
+ import { Kind } from 'graphql';
2
+ import { parseFields } from '../../../../subgraph/helpers';
3
+ import { stripTypeModifiers } from '../../../../utils/state';
4
+ export class Fields {
5
+ typeName;
6
+ source;
7
+ fields;
8
+ constructor(typeName, source, fields) {
9
+ this.typeName = typeName;
10
+ this.source = source;
11
+ this.fields = fields;
12
+ }
13
+ contains(typeName, fieldName) {
14
+ return this._contains(typeName, fieldName, this.fields);
15
+ }
16
+ equals(other) {
17
+ if (this.typeName !== other.typeName) {
18
+ return false;
19
+ }
20
+ if (this.source === other.source) {
21
+ return true;
22
+ }
23
+ return this._fieldsEqual(this.fields, other.fields);
24
+ }
25
+ _fieldsEqual(fields, otherFields) {
26
+ if (fields.length !== otherFields.length) {
27
+ return false;
28
+ }
29
+ for (let i = 0; i < fields.length; i++) {
30
+ const field = fields[i];
31
+ const otherField = otherFields[i];
32
+ if (field.typeName !== otherField.typeName || field.fieldName !== otherField.fieldName) {
33
+ return false;
34
+ }
35
+ const areEqual = Array.isArray(field.selectionSet) && Array.isArray(otherField.selectionSet)
36
+ ? this._fieldsEqual(field.selectionSet, otherField.selectionSet)
37
+ : field.selectionSet === otherField.selectionSet;
38
+ if (!areEqual) {
39
+ return false;
40
+ }
41
+ }
42
+ return true;
43
+ }
44
+ _contains(typeName, fieldName, fields) {
45
+ return fields.some(f => (f.typeName === typeName && f.fieldName === fieldName) ||
46
+ (f.selectionSet ? this._contains(typeName, fieldName, f.selectionSet) : false));
47
+ }
48
+ toString() {
49
+ return this.source;
50
+ }
51
+ }
52
+ export class FieldsResolver {
53
+ supergraphState;
54
+ cache = new Map();
55
+ constructor(supergraphState) {
56
+ this.supergraphState = supergraphState;
57
+ }
58
+ resolve(typeName, keyFields) {
59
+ const key = this.keyFactory(typeName, keyFields);
60
+ if (this.cache.has(key)) {
61
+ return this.cache.get(key);
62
+ }
63
+ const typeState = this.supergraphState.objectTypes.get(typeName);
64
+ if (!typeState) {
65
+ throw new Error(`Expected an object type when resolving keyFields of ${typeName}`);
66
+ }
67
+ const selectionSetNode = parseFields(keyFields);
68
+ if (!selectionSetNode) {
69
+ throw new Error(`Expected a selection set when resolving keyFields of ${typeName}`);
70
+ }
71
+ const fields = new Fields(typeName, keyFields, this.resolveSelectionSetNode(typeName, selectionSetNode));
72
+ this.cache.set(key, fields);
73
+ return fields;
74
+ }
75
+ keyFactory(typeName, keyFields) {
76
+ return `${typeName}/${keyFields}`;
77
+ }
78
+ resolveFieldNode(typeName, fieldNode, fields) {
79
+ const typeState = this.supergraphState.objectTypes.get(typeName) ??
80
+ this.supergraphState.interfaceTypes.get(typeName);
81
+ if (!typeState) {
82
+ throw new Error(`Type "${typeName}" is not defined.`);
83
+ }
84
+ if (fieldNode.name.value === '__typename') {
85
+ return;
86
+ }
87
+ if (!typeState.fields.has(fieldNode.name.value)) {
88
+ throw new Error(`Type "${typeName.toString()}" does not have field "${fieldNode.name.value}".`);
89
+ }
90
+ if (fieldNode.selectionSet) {
91
+ const outputType = stripTypeModifiers(typeState.fields.get(fieldNode.name.value).type);
92
+ fields.push({
93
+ fieldName: fieldNode.name.value,
94
+ typeName,
95
+ selectionSet: this.resolveSelectionSetNode(outputType, fieldNode.selectionSet),
96
+ });
97
+ }
98
+ else {
99
+ fields.push({
100
+ typeName,
101
+ fieldName: fieldNode.name.value,
102
+ selectionSet: null,
103
+ });
104
+ }
105
+ }
106
+ resolveInlineFragmentNode(fragmentNode, fields) {
107
+ if (!fragmentNode.typeCondition?.name.value) {
108
+ throw new Error(`Inline fragment without type condition is not supported.`);
109
+ }
110
+ const typeName = fragmentNode.typeCondition.name.value;
111
+ const typeState = this.supergraphState.objectTypes.get(typeName) ??
112
+ this.supergraphState.interfaceTypes.get(typeName);
113
+ if (!typeState) {
114
+ throw new Error(`Type "${typeName}" is not defined.`);
115
+ }
116
+ for (const selection of fragmentNode.selectionSet.selections) {
117
+ if (selection.kind === Kind.FIELD) {
118
+ this.resolveFieldNode(typeName, selection, fields);
119
+ }
120
+ else {
121
+ throw new Error(`Inline fragment within an inline fragment is not supported.`);
122
+ }
123
+ }
124
+ }
125
+ resolveSelectionSetNode(typeName, selectionSetNode, fields = []) {
126
+ for (const selection of selectionSetNode.selections) {
127
+ if (selection.kind === Kind.FIELD) {
128
+ this.resolveFieldNode(typeName, selection, fields);
129
+ }
130
+ else if (selection.kind === Kind.INLINE_FRAGMENT) {
131
+ this.resolveInlineFragmentNode(selection, fields);
132
+ }
133
+ else {
134
+ throw new Error(`Fragment spread is not supported.`);
135
+ }
136
+ }
137
+ return this.sortFields(fields);
138
+ }
139
+ sortFields(fields) {
140
+ return fields.sort((a, b) => `${a.typeName}.${a.fieldName}`.localeCompare(`${b.typeName}.${b.fieldName}`));
141
+ }
142
+ }