@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
@@ -0,0 +1,671 @@
1
+ import { specifiedScalarTypes } from 'graphql';
2
+ import { stripTypeModifiers } from '../../../../utils/state';
3
+ import { SUPERGRAPH_ID } from './constants';
4
+ import { assertAbstractEdge, assertFieldEdge, Edge, isAbstractEdge, isEntityEdge, isFieldEdge, } from './edge';
5
+ import { scoreKeyFields } from './helpers';
6
+ import { AbstractMove, EntityMove, FieldMove } from './moves';
7
+ import { Node } from './node';
8
+ export class Graph {
9
+ name;
10
+ supergraphState;
11
+ fieldsResolver;
12
+ ignoreInaccessible;
13
+ _warnedAboutIncorrectEdge = false;
14
+ nodesByTypeIndex = [];
15
+ edgesByHeadTypeIndex = [];
16
+ edgesByTailTypeIndex = [];
17
+ typeNameToNodeIndexes = new Map();
18
+ typeChildren = [];
19
+ typeChildrenCache = new Map();
20
+ isSubgraph;
21
+ logger;
22
+ id;
23
+ idSymbol;
24
+ constructor(logger, id, name, supergraphState, fieldsResolver, ignoreInaccessible = false) {
25
+ this.name = name;
26
+ this.supergraphState = supergraphState;
27
+ this.fieldsResolver = fieldsResolver;
28
+ this.ignoreInaccessible = ignoreInaccessible;
29
+ this.logger = logger.create('Graph');
30
+ if (typeof id === 'string') {
31
+ this.idSymbol = Symbol.for(id);
32
+ this.id = id;
33
+ this.isSubgraph = true;
34
+ }
35
+ else {
36
+ this.idSymbol = id;
37
+ this.id = id.toString();
38
+ this.isSubgraph = this.idSymbol !== SUPERGRAPH_ID;
39
+ }
40
+ }
41
+ addUnreachableTypes() {
42
+ if (!this.isSupergraph()) {
43
+ for (const [typeName, state] of this.supergraphState.objectTypes) {
44
+ if (state.byGraph.has(this.id)) {
45
+ this.createNodesAndEdgesForType(typeName);
46
+ }
47
+ }
48
+ for (const [typeName, state] of this.supergraphState.scalarTypes) {
49
+ if (state.byGraph.has(this.id)) {
50
+ this.createNodeForScalarType(typeName);
51
+ }
52
+ }
53
+ for (const [_, state] of this.supergraphState.enumTypes) {
54
+ if (state.byGraph.has(this.id)) {
55
+ this.createNodeForEnumType(state);
56
+ }
57
+ }
58
+ for (const [_, state] of this.supergraphState.unionTypes) {
59
+ if (state.byGraph.has(this.id)) {
60
+ this.createNodeForUnionType(state);
61
+ }
62
+ }
63
+ for (const [_, state] of this.supergraphState.interfaceTypes) {
64
+ if (state.byGraph.has(this.id)) {
65
+ this.createNodeForInterfaceType(state);
66
+ }
67
+ }
68
+ }
69
+ return this;
70
+ }
71
+ addFromRoots() {
72
+ for (const typeName of ['Query', 'Mutation', 'Subscription']) {
73
+ const typeState = this.supergraphState.objectTypes.get(typeName);
74
+ if (typeState && this.trueOrIfSubgraphThen(() => typeState.byGraph.has(this.id))) {
75
+ this.createNodesAndEdgesForType(typeState.name);
76
+ }
77
+ }
78
+ return this;
79
+ }
80
+ addFromEntities() {
81
+ for (const typeState of this.supergraphState.objectTypes.values()) {
82
+ if (typeState?.isEntity && this.trueOrIfSubgraphThen(() => typeState.byGraph.has(this.id))) {
83
+ this.createNodesAndEdgesForType(typeState.name);
84
+ }
85
+ }
86
+ return this;
87
+ }
88
+ addSubgraph(graph) {
89
+ for (const node of graph.nodesByTypeIndex.flat()) {
90
+ this.addNode(node.withoutState());
91
+ }
92
+ for (const edges of graph.edgesByHeadTypeIndex) {
93
+ for (const edge of edges) {
94
+ this.addEdge(edge);
95
+ }
96
+ }
97
+ }
98
+ connectUnionOrInterface(nodeIndex, sameTypeNameNodeIndexes, edgesToAdd) {
99
+ for (const headNode of this.nodesByTypeIndex[nodeIndex]) {
100
+ const edges = this.edgesOfTail(headNode);
101
+ if (edges.length === 0) {
102
+ continue;
103
+ }
104
+ for (const otherNodeIndex of sameTypeNameNodeIndexes) {
105
+ if (nodeIndex === otherNodeIndex) {
106
+ continue;
107
+ }
108
+ for (const tailNode of this.nodesByTypeIndex[otherNodeIndex]) {
109
+ if (headNode === tailNode) {
110
+ continue;
111
+ }
112
+ for (const edge of edges) {
113
+ edgesToAdd.push(new Edge(edge.head, edge.move, tailNode));
114
+ }
115
+ }
116
+ }
117
+ }
118
+ }
119
+ connectEntities(nodeIndex, sameTypeNameNodeIndexes, edgesToAdd) {
120
+ for (const headNode of this.nodesByTypeIndex[nodeIndex]) {
121
+ for (const otherNodeIndex of sameTypeNameNodeIndexes) {
122
+ if (nodeIndex === otherNodeIndex) {
123
+ continue;
124
+ }
125
+ for (const tailNode of this.nodesByTypeIndex[otherNodeIndex]) {
126
+ if (!(tailNode.typeState && 'isEntity' in tailNode.typeState && tailNode.typeState.isEntity)) {
127
+ continue;
128
+ }
129
+ const typeStateInGraph = tailNode.typeState.byGraph.get(tailNode.graphId);
130
+ const keys = (typeStateInGraph?.keys ?? [])
131
+ .slice()
132
+ .sort((a, b) => scoreKeyFields(a.fields) - scoreKeyFields(b.fields));
133
+ for (const key of keys) {
134
+ if (key.resolvable) {
135
+ edgesToAdd.push(new Edge(headNode, new EntityMove(this.fieldsResolver.resolve(headNode.typeName, key.fields)), tailNode));
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ addProvidedInterfaceFields(head, providedFields, queue) {
143
+ const abstractIndexes = head.getAbstractEdgeIndexes(head.typeName);
144
+ if (!abstractIndexes || abstractIndexes.length === 0) {
145
+ throw new Error('Expected abstract indexes to be defined');
146
+ }
147
+ const interfaceFields = [];
148
+ const fieldsByType = new Map();
149
+ for (const providedField of providedFields) {
150
+ if (providedField.typeName === head.typeName) {
151
+ interfaceFields.push(providedField);
152
+ continue;
153
+ }
154
+ const existing = fieldsByType.get(providedField.typeName);
155
+ if (existing) {
156
+ existing.push(providedField);
157
+ }
158
+ else {
159
+ fieldsByType.set(providedField.typeName, [providedField]);
160
+ }
161
+ }
162
+ for (const [typeName, providedFields] of fieldsByType) {
163
+ let edgeIndex;
164
+ let edge;
165
+ for (let i = 0; i < abstractIndexes.length; i++) {
166
+ const index = abstractIndexes[i];
167
+ const potentialEdge = this.edgesByHeadTypeIndex[head.index][index];
168
+ if (!potentialEdge) {
169
+ throw new Error('Expected edge to be defined');
170
+ }
171
+ if (potentialEdge.tail.typeName === typeName) {
172
+ edgeIndex = index;
173
+ edge = potentialEdge;
174
+ break;
175
+ }
176
+ }
177
+ if (typeof edgeIndex === 'undefined' || !edge) {
178
+ throw new Error(`Expected an abstract edge matching "${typeName}" to be defined`);
179
+ }
180
+ const newTail = this.duplicateNode(edge.tail);
181
+ const newEdge = new Edge(edge.head, edge.move, newTail);
182
+ this.replaceEdgeAt(edge.head.index, edge.tail.index, newEdge, edgeIndex);
183
+ queue.push({
184
+ head: newTail,
185
+ providedFields,
186
+ });
187
+ }
188
+ if (!interfaceFields.length) {
189
+ return;
190
+ }
191
+ for (const index of abstractIndexes) {
192
+ const edge = this.edgesByHeadTypeIndex[head.index][index];
193
+ if (!edge) {
194
+ throw new Error('Expected edge to be defined');
195
+ }
196
+ assertAbstractEdge(edge);
197
+ if (edge.isCrossGraphEdge()) {
198
+ continue;
199
+ }
200
+ const newTail = this.duplicateNode(edge.tail);
201
+ const newEdge = new Edge(edge.head, new AbstractMove(), newTail);
202
+ this.replaceEdgeAt(edge.head.index, edge.tail.index, newEdge, index);
203
+ queue.push({
204
+ head: newTail,
205
+ providedFields: interfaceFields.map(f => ({
206
+ ...f,
207
+ typeName: newTail.typeName,
208
+ })),
209
+ });
210
+ }
211
+ }
212
+ addProvidedField(head, providedField, queue) {
213
+ if (providedField.fieldName === '__typename') {
214
+ return;
215
+ }
216
+ const indexes = head.getFieldEdgeIndexes(providedField.fieldName);
217
+ if (!indexes || indexes.length === 0) {
218
+ if (head.typeState?.kind === 'object') {
219
+ throw new Error('Expected indexes to be defined: ' +
220
+ providedField.typeName +
221
+ '.' +
222
+ providedField.fieldName);
223
+ }
224
+ else {
225
+ throw new Error(`Expected ${providedField.typeName}.${providedField.fieldName} to be point to an object type, other kinds are not supported (received: ${head.typeState?.kind})`);
226
+ }
227
+ }
228
+ for (const index of indexes) {
229
+ const edge = this.edgesByHeadTypeIndex[head.index][index];
230
+ if (!edge) {
231
+ throw new Error('Expected edge to be defined');
232
+ }
233
+ assertFieldEdge(edge);
234
+ if (edge.isCrossGraphEdge()) {
235
+ continue;
236
+ }
237
+ const newTail = this.duplicateNode(edge.tail);
238
+ const newEdge = new Edge(edge.head, new FieldMove(edge.move.typeName, edge.move.fieldName, edge.move.requires, edge.move.provides, true), newTail);
239
+ this.replaceEdgeAt(edge.head.index, edge.tail.index, newEdge, index);
240
+ if (providedField.selectionSet) {
241
+ queue.push({
242
+ head: newTail,
243
+ providedFields: providedField.selectionSet,
244
+ });
245
+ }
246
+ }
247
+ }
248
+ joinSubgraphs() {
249
+ const edgesToAdd = [];
250
+ for (let i = 0; i < this.nodesByTypeIndex.length; i++) {
251
+ const typeNode = this.nodesByTypeIndex[i][0];
252
+ if (!typeNode.typeState) {
253
+ continue;
254
+ }
255
+ const otherNodesIndexes = this.getIndexesOfType(typeNode.typeName);
256
+ if (!Array.isArray(otherNodesIndexes)) {
257
+ continue;
258
+ }
259
+ if (typeNode.typeState?.kind === 'object' && typeNode.typeState?.isEntity) {
260
+ this.connectEntities(i, otherNodesIndexes, edgesToAdd);
261
+ }
262
+ else if (typeNode.typeState.kind === 'union' || typeNode.typeState.kind === 'interface') {
263
+ this.connectUnionOrInterface(i, otherNodesIndexes, edgesToAdd);
264
+ }
265
+ }
266
+ while (edgesToAdd.length > 0) {
267
+ const edge = edgesToAdd.pop();
268
+ if (!edge) {
269
+ throw new Error('Expected edge to be defined');
270
+ }
271
+ this.addEdge(edge);
272
+ }
273
+ for (let headIndex = 0; headIndex < this.edgesByHeadTypeIndex.length; headIndex++) {
274
+ const edges = this.edgesByHeadTypeIndex[headIndex];
275
+ for (let edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
276
+ const edge = edges[edgeIndex];
277
+ if (edge.isCrossGraphEdge()) {
278
+ continue;
279
+ }
280
+ if (!(isFieldEdge(edge) && edge.move.provides)) {
281
+ continue;
282
+ }
283
+ const newTail = this.duplicateNode(edge.tail);
284
+ const newEdge = new Edge(edge.head, edge.move, newTail);
285
+ this.replaceEdgeAt(headIndex, edge.tail.index, newEdge, edgeIndex);
286
+ const queue = [
287
+ {
288
+ head: newTail,
289
+ providedFields: edge.move.provides.fields,
290
+ },
291
+ ];
292
+ while (queue.length > 0) {
293
+ const item = queue.pop();
294
+ if (!item) {
295
+ throw new Error('Expected item to be defined');
296
+ }
297
+ const { head, providedFields } = item;
298
+ if (head.typeState?.kind === 'interface') {
299
+ this.addProvidedInterfaceFields(head, providedFields, queue);
300
+ continue;
301
+ }
302
+ for (const providedField of providedFields) {
303
+ this.addProvidedField(head, providedField, queue);
304
+ }
305
+ }
306
+ }
307
+ }
308
+ return this;
309
+ }
310
+ duplicateNode(originalNode) {
311
+ const newNode = this.createNode(originalNode.typeName, originalNode.typeState, originalNode.graphId, originalNode.graphName);
312
+ for (const edge of this.edgesOfHead(originalNode)) {
313
+ this.addEdge(new Edge(newNode, edge.move, edge.tail));
314
+ }
315
+ return newNode;
316
+ }
317
+ replaceEdgeAt(headIndex, tailIndex, newEdge, edgeIndex) {
318
+ this.edgesByHeadTypeIndex[headIndex][edgeIndex] = newEdge;
319
+ const newEdgesByTail = [];
320
+ for (const edge of this.edgesByTailTypeIndex[tailIndex]) {
321
+ if (edge !== newEdge) {
322
+ newEdgesByTail.push(edge);
323
+ }
324
+ }
325
+ newEdgesByTail.push(newEdge);
326
+ this.edgesByTailTypeIndex[tailIndex] = newEdgesByTail;
327
+ }
328
+ print(asLink = false) {
329
+ let str = 'digraph G {';
330
+ if (this.supergraphState.objectTypes.has('Query')) {
331
+ str += '\n root -> Query';
332
+ }
333
+ if (this.supergraphState.objectTypes.has('Mutation')) {
334
+ str += '\n root -> Mutation';
335
+ }
336
+ if (this.supergraphState.objectTypes.has('Subscription')) {
337
+ str += '\n root -> Subscription';
338
+ }
339
+ for (const edge of this.edgesByHeadTypeIndex.flat()) {
340
+ if (edge.head.typeName === 'Query') {
341
+ str += `\n "Query" -> "${edge.head}";`;
342
+ }
343
+ else if (edge.head.typeName === 'Mutation') {
344
+ str += `\n "Mutation" -> "${edge.head}";`;
345
+ }
346
+ else if (edge.head.typeName === 'Subscription') {
347
+ str += `\n "Subscription" -> "${edge.head}";`;
348
+ }
349
+ str += `\n "${edge.head}" -> "${edge.tail}" [label="${edge.move}"];`;
350
+ }
351
+ str += '\n}';
352
+ if (asLink) {
353
+ return `https://dreampuf.github.io/GraphvizOnline/#${encodeURIComponent(str)}`;
354
+ }
355
+ return str;
356
+ }
357
+ graphNameToId(graphName) {
358
+ for (const [id, { graph }] of this.supergraphState.subgraphs) {
359
+ if (graph.name === graphName) {
360
+ return id;
361
+ }
362
+ }
363
+ }
364
+ nodeOf(typeName, failIfMissing = true) {
365
+ const indexes = this.getIndexesOfType(typeName);
366
+ if (!Array.isArray(indexes)) {
367
+ if (failIfMissing) {
368
+ throw new Error(`Expected TypeNode(${typeName}) to be inserted first in graph ${this.id}`);
369
+ }
370
+ return undefined;
371
+ }
372
+ if (indexes.length > 1) {
373
+ throw new Error(`Expected only one node for ${typeName} in graph ${this.id}`);
374
+ }
375
+ return this.nodesByTypeIndex[indexes[0]][0];
376
+ }
377
+ nodesOf(typeName, failIfMissing = true) {
378
+ const indexes = this.getIndexesOfType(typeName);
379
+ if (!Array.isArray(indexes)) {
380
+ if (failIfMissing) {
381
+ throw new Error(`Expected TypeNode(${typeName}) to be inserted first in graph ${this.id}`);
382
+ }
383
+ return [];
384
+ }
385
+ const nodes = [];
386
+ for (const i of indexes) {
387
+ for (const node of this.nodesByTypeIndex[i]) {
388
+ nodes.push(node);
389
+ }
390
+ }
391
+ return nodes;
392
+ }
393
+ getSameGraphEdgesOfIndex(head, indexes, kind) {
394
+ const edges = [];
395
+ if (!indexes) {
396
+ return [];
397
+ }
398
+ for (const i of indexes) {
399
+ const edge = this.edgesByHeadTypeIndex[head.index][i];
400
+ if (!edge) {
401
+ throw new Error(`Expected edge to be defined at index ${i}`);
402
+ }
403
+ if (edge.head.graphName === head.graphName) {
404
+ edges.push(edge);
405
+ continue;
406
+ }
407
+ if (!this._warnedAboutIncorrectEdge) {
408
+ console.error(`Expected edge to be in the same graph as head (${kind})` + edge.toString());
409
+ this._warnedAboutIncorrectEdge = true;
410
+ }
411
+ }
412
+ return edges;
413
+ }
414
+ fieldEdgesOfHead(head, fieldName) {
415
+ return this.getSameGraphEdgesOfIndex(head, head.getFieldEdgeIndexes(fieldName), 'field');
416
+ }
417
+ abstractEdgesOfHead(head) {
418
+ return this.getSameGraphEdgesOfIndex(head, head.getAbstractEdgeIndexes(head.typeName), 'abstract');
419
+ }
420
+ entityEdgesOfHead(head) {
421
+ return this.getSameGraphEdgesOfIndex(head, head.getEntityEdgeIndexes(head.typeName), 'entity');
422
+ }
423
+ crossGraphEdgesOfHead(head) {
424
+ return this.getSameGraphEdgesOfIndex(head, head.getCrossGraphEdgeIndexes(head.typeName), 'cross-graph');
425
+ }
426
+ edgesOfHead(head) {
427
+ return this.edgesByHeadTypeIndex[head.index]?.filter(e => e.head === head) ?? [];
428
+ }
429
+ edgesOfTail(tail) {
430
+ return this.edgesByTailTypeIndex[tail.index]?.filter(e => e.tail === tail) ?? [];
431
+ }
432
+ possibleTypesOf(typeName) {
433
+ if (this.supergraphState.interfaceTypes.has(typeName)) {
434
+ return Array.from(this.supergraphState.interfaceTypes.get(typeName).implementedBy);
435
+ }
436
+ if (this.supergraphState.unionTypes.has(typeName)) {
437
+ return Array.from(this.supergraphState.unionTypes.get(typeName).members);
438
+ }
439
+ return [typeName];
440
+ }
441
+ canReachTypeFromType(fromTypeName, toTypeName) {
442
+ if (fromTypeName === toTypeName) {
443
+ return true;
444
+ }
445
+ const fromTypeIndexes = this.getIndexesOfType(fromTypeName);
446
+ if (!fromTypeIndexes) {
447
+ return false;
448
+ }
449
+ const visited = new Array(this.typeChildren.length).fill(false);
450
+ const queue = [];
451
+ for (const i of fromTypeIndexes) {
452
+ visited[i] = true;
453
+ }
454
+ queue.push(fromTypeName);
455
+ while (queue.length > 0) {
456
+ const typeName = queue.shift();
457
+ if (!typeName) {
458
+ throw new Error('Unexpected end of queue');
459
+ }
460
+ const typeIndexes = this.getIndexesOfType(typeName);
461
+ if (typeof typeIndexes === 'undefined') {
462
+ throw new Error(`Could not find an index of type: ${typeName}`);
463
+ }
464
+ this.typeChildrenCache.set(`${fromTypeName} -> ${typeName}`, true);
465
+ if (typeName === toTypeName) {
466
+ return true;
467
+ }
468
+ for (const typeIndex of typeIndexes) {
469
+ const children = this.typeChildren[typeIndex];
470
+ for (const childTypeName of children) {
471
+ const childTypeIndexes = this.getIndexesOfType(childTypeName);
472
+ if (typeof childTypeIndexes === 'undefined') {
473
+ throw new Error(`Could not find an index of type: ${typeName}`);
474
+ }
475
+ for (const childTypeIndex of childTypeIndexes) {
476
+ if (!visited[childTypeIndex]) {
477
+ visited[childTypeIndex] = true;
478
+ this.typeChildrenCache.set(`${fromTypeName} -> ${childTypeName}`, true);
479
+ this.typeChildrenCache.set(`${typeName} -> ${childTypeName}`, true);
480
+ queue.push(childTypeName);
481
+ }
482
+ }
483
+ }
484
+ }
485
+ }
486
+ this.typeChildrenCache.set(`${fromTypeName} -> ${toTypeName}`, false);
487
+ return false;
488
+ }
489
+ createNodesAndEdgesForType(typeName) {
490
+ if (this.supergraphState.objectTypes.has(typeName)) {
491
+ return this.createNodesAndEdgesForObjectType(this.supergraphState.objectTypes.get(typeName));
492
+ }
493
+ if (specifiedScalarTypes.some(t => t.name === typeName) ||
494
+ this.supergraphState.scalarTypes.has(typeName)) {
495
+ return this.createNodeForScalarType(typeName);
496
+ }
497
+ if (this.supergraphState.enumTypes.has(typeName)) {
498
+ return this.createNodeForEnumType(this.supergraphState.enumTypes.get(typeName));
499
+ }
500
+ if (this.supergraphState.unionTypes.has(typeName)) {
501
+ return this.createNodeForUnionType(this.supergraphState.unionTypes.get(typeName));
502
+ }
503
+ if (this.supergraphState.interfaceTypes.has(typeName)) {
504
+ return this.createNodeForInterfaceType(this.supergraphState.interfaceTypes.get(typeName));
505
+ }
506
+ throw new Error(`Not implemented path: createNodesAndEdgesForType(${typeName})`);
507
+ }
508
+ ensureNonOrSingleNode(typeName) {
509
+ const indexes = this.typeNameToNodeIndexes.get(typeName);
510
+ if (!Array.isArray(indexes)) {
511
+ return;
512
+ }
513
+ if (indexes.length > 1) {
514
+ throw new Error(`Expected only one node for ${typeName} in graph ${this.id}`);
515
+ }
516
+ return this.nodesByTypeIndex[indexes[0]][0];
517
+ }
518
+ createNodesAndEdgesForObjectType(typeState) {
519
+ const existing = this.ensureNonOrSingleNode(typeState.name);
520
+ if (existing) {
521
+ return existing;
522
+ }
523
+ const head = this.createTypeNode(typeState.name, typeState);
524
+ for (const field of typeState.fields.values()) {
525
+ if (this.trueOrIfSubgraphThen(() => field.byGraph.has(this.id))) {
526
+ this.createEdgeForObjectTypeField(head, field);
527
+ }
528
+ }
529
+ return head;
530
+ }
531
+ createNodeForScalarType(typeName) {
532
+ const existing = this.ensureNonOrSingleNode(typeName);
533
+ if (existing) {
534
+ return existing;
535
+ }
536
+ return this.createTypeNode(typeName, this.supergraphState.scalarTypes.get(typeName) ?? null);
537
+ }
538
+ createNodeForEnumType(typeState) {
539
+ const existing = this.ensureNonOrSingleNode(typeState.name);
540
+ if (existing) {
541
+ return existing;
542
+ }
543
+ return this.createTypeNode(typeState.name, typeState);
544
+ }
545
+ createNodeForUnionType(typeState) {
546
+ const existing = this.ensureNonOrSingleNode(typeState.name);
547
+ if (existing) {
548
+ return existing;
549
+ }
550
+ const head = this.createTypeNode(typeState.name, typeState);
551
+ const members = this.isSupergraph()
552
+ ? typeState.members
553
+ : typeState.byGraph.get(this.id)?.members;
554
+ if (members) {
555
+ for (const memberTypeName of members) {
556
+ const tail = this.createNodesAndEdgesForType(memberTypeName);
557
+ this.addEdge(new Edge(head, new AbstractMove(), tail));
558
+ }
559
+ }
560
+ return head;
561
+ }
562
+ createNodeForInterfaceType(typeState) {
563
+ const existing = this.ensureNonOrSingleNode(typeState.name);
564
+ if (existing) {
565
+ return existing;
566
+ }
567
+ const head = this.createTypeNode(typeState.name, typeState);
568
+ const implementedBy = this.isSupergraph()
569
+ ? typeState.implementedBy
570
+ : typeState.byGraph.get(this.id)?.implementedBy;
571
+ if (implementedBy) {
572
+ for (const memberTypeName of implementedBy) {
573
+ const tail = this.createNodesAndEdgesForType(memberTypeName);
574
+ this.addEdge(new Edge(head, new AbstractMove(), tail));
575
+ }
576
+ }
577
+ return head;
578
+ }
579
+ createEdgeForObjectTypeField(head, field) {
580
+ if (this.ignoreInaccessible && field.inaccessible) {
581
+ return;
582
+ }
583
+ if (this.isSupergraph() && field.byGraph.size === 1) {
584
+ const graphId = Array.from(field.byGraph.keys())[0];
585
+ const isExternal = field.byGraph.get(graphId)?.external === true;
586
+ const isFederationV1 = this.supergraphState.subgraphs.get(graphId)?.version === 'v1.0';
587
+ if (isExternal && isFederationV1) {
588
+ return;
589
+ }
590
+ }
591
+ const outputTypeName = stripTypeModifiers(field.type);
592
+ const tail = this.createNodesAndEdgesForType(outputTypeName);
593
+ if (!tail) {
594
+ throw new Error(`Failed to create Node for ${outputTypeName} in subgraph ${this.id}`);
595
+ }
596
+ if (this.isSupergraph()) {
597
+ return this.addEdge(new Edge(head, new FieldMove(head.typeName, field.name), tail));
598
+ }
599
+ const requires = field.byGraph.get(head.graphId)?.requires;
600
+ const provides = field.byGraph.get(head.graphId)?.provides;
601
+ return this.addEdge(new Edge(head, new FieldMove(head.typeName, field.name, requires ? this.fieldsResolver.resolve(head.typeName, requires) : null, provides ? this.fieldsResolver.resolve(outputTypeName, provides) : null), tail));
602
+ }
603
+ createTypeNode(typeName, typeState) {
604
+ if (this.typeNameToNodeIndexes.has(typeName)) {
605
+ throw new Error(`Node for ${typeName} already exists in subgraph ${this.id}`);
606
+ }
607
+ return this.createNode(typeName, typeState, this.id, this.name);
608
+ }
609
+ createNode(typeName, typeState, graphId, graphName) {
610
+ const index = this.nodesByTypeIndex.push([]) - 1;
611
+ const node = new Node(index, typeName, typeState, graphId, graphName);
612
+ this.nodesByTypeIndex[node.index].push(node);
613
+ this.edgesByHeadTypeIndex.push([]);
614
+ this.edgesByTailTypeIndex.push([]);
615
+ this.typeChildren.push(new Set());
616
+ const existing = this.typeNameToNodeIndexes.get(typeName);
617
+ if (Array.isArray(existing)) {
618
+ existing.push(index);
619
+ }
620
+ else {
621
+ this.typeNameToNodeIndexes.set(typeName, [index]);
622
+ }
623
+ return node;
624
+ }
625
+ addNode(node) {
626
+ const newIndex = this.nodesByTypeIndex.push([]) - 1;
627
+ node.index = newIndex;
628
+ this.nodesByTypeIndex[node.index].push(node);
629
+ this.edgesByHeadTypeIndex.push([]);
630
+ this.edgesByTailTypeIndex.push([]);
631
+ this.typeChildren.push(new Set());
632
+ const existing = this.typeNameToNodeIndexes.get(node.typeName);
633
+ if (Array.isArray(existing)) {
634
+ existing.push(newIndex);
635
+ }
636
+ else {
637
+ this.typeNameToNodeIndexes.set(node.typeName, [newIndex]);
638
+ }
639
+ return node;
640
+ }
641
+ addEdge(edge) {
642
+ const edgeIndex = this.edgesByHeadTypeIndex[edge.head.index].push(edge) - 1;
643
+ this.edgesByTailTypeIndex[edge.tail.index].push(edge);
644
+ this.typeChildren[edge.head.index].add(edge.tail.typeName);
645
+ if (isFieldEdge(edge)) {
646
+ edge.head.addFieldEdge(edge.move.fieldName, edgeIndex);
647
+ }
648
+ else if (isEntityEdge(edge)) {
649
+ edge.head.addEntityEdge(edge.head.typeName, edgeIndex);
650
+ }
651
+ else if (isAbstractEdge(edge)) {
652
+ edge.head.addAbstractEdge(edge.head.typeName, edgeIndex);
653
+ }
654
+ if (edge.isCrossGraphEdge()) {
655
+ edge.head.addCrossGraphEdge(edge.head.typeName, edgeIndex);
656
+ }
657
+ return edge;
658
+ }
659
+ getIndexesOfType(typeName) {
660
+ return this.typeNameToNodeIndexes.get(typeName);
661
+ }
662
+ trueOrIfSubgraphThen(conditionFn) {
663
+ if (this.isSubgraph) {
664
+ return conditionFn();
665
+ }
666
+ return true;
667
+ }
668
+ isSupergraph() {
669
+ return this.isSubgraph === false;
670
+ }
671
+ }