@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,306 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Walker = exports.WalkTracker = void 0;
4
+ const graphql_1 = require("graphql");
5
+ const edge_1 = require("./edge");
6
+ const finder_1 = require("./finder");
7
+ const operation_path_1 = require("./operation-path");
8
+ class WalkTracker {
9
+ superPath;
10
+ paths;
11
+ errors = [];
12
+ constructor(superPath, paths) {
13
+ this.superPath = superPath;
14
+ this.paths = paths;
15
+ }
16
+ move(edge) {
17
+ if ((0, edge_1.isFieldEdge)(edge) || (0, edge_1.isAbstractEdge)(edge)) {
18
+ return new WalkTracker(this.superPath.clone().move(edge), []);
19
+ }
20
+ throw new Error('Expected edge to be FieldMove or AbstractMove');
21
+ }
22
+ addPath(path) {
23
+ this.paths.push(path);
24
+ this.errors = [];
25
+ }
26
+ addError(error) {
27
+ this.errors.push(error);
28
+ }
29
+ isPossible() {
30
+ return this.paths.length > 0;
31
+ }
32
+ givesEmptyResult() {
33
+ const lastEdge = this.superPath.edge();
34
+ return (this.paths.length === 0 && this.errors.length === 0 && !!lastEdge && (0, edge_1.isAbstractEdge)(lastEdge));
35
+ }
36
+ isEdgeVisited(edge) {
37
+ return this.superPath.isVisitedEdge(edge);
38
+ }
39
+ listErrors() {
40
+ return this.errors
41
+ .filter((error, i, all) => all.findIndex(e => e.toString() === error.toString()) === i)
42
+ .filter(error => {
43
+ if (error.kind !== 'KEY') {
44
+ return true;
45
+ }
46
+ const steps = this.superPath.steps();
47
+ const lastStep = steps[steps.length - 1];
48
+ if (!lastStep || lastStep.typeName !== error.typeName) {
49
+ return true;
50
+ }
51
+ return true;
52
+ });
53
+ }
54
+ }
55
+ exports.WalkTracker = WalkTracker;
56
+ const defaultIsEdgeIgnored = () => false;
57
+ class Walker {
58
+ moveChecker;
59
+ supergraph;
60
+ mergedGraph;
61
+ logger;
62
+ pathFinder;
63
+ constructor(logger, moveChecker, supergraph, mergedGraph) {
64
+ this.moveChecker = moveChecker;
65
+ this.supergraph = supergraph;
66
+ this.mergedGraph = mergedGraph;
67
+ this.logger = logger.create('Walker');
68
+ this.pathFinder = new finder_1.PathFinder(this.logger, this.mergedGraph, this.moveChecker);
69
+ }
70
+ walkTrail(operationType, steps) {
71
+ if (steps.length === 0) {
72
+ throw new Error('Expected at least one step');
73
+ }
74
+ const rootNode = this.supergraph.nodeOf(operationType === graphql_1.OperationTypeNode.QUERY
75
+ ? 'Query'
76
+ : operationType === graphql_1.OperationTypeNode.MUTATION
77
+ ? 'Mutation'
78
+ : 'Subscription', false);
79
+ if (!rootNode) {
80
+ throw new Error(`Expected root node for operation type ${operationType}`);
81
+ }
82
+ let state = new WalkTracker(new operation_path_1.OperationPath(rootNode), this.mergedGraph.nodesOf(rootNode.typeName, false).map(n => new operation_path_1.OperationPath(n)));
83
+ for (const step of steps) {
84
+ const stepId = 'fieldName' in step && step.fieldName
85
+ ? `${step.typeName}.${step.fieldName}`
86
+ : step.typeName;
87
+ const isFieldStep = 'fieldName' in step;
88
+ const isEdgeIgnored = (edge) => {
89
+ if (isFieldStep) {
90
+ return !(0, edge_1.isFieldEdge)(edge) || edge.move.fieldName !== step.fieldName;
91
+ }
92
+ return true;
93
+ };
94
+ let called = 0;
95
+ let unreachable = false;
96
+ let emptyObjectResult = false;
97
+ this.nextStep(state, state.superPath.tail() ?? state.superPath.rootNode(), (nextState, superEdge) => {
98
+ if (called++ > 1) {
99
+ throw new Error('Expected nextStep to be called only once');
100
+ }
101
+ state = nextState;
102
+ if (nextState.isPossible()) {
103
+ if (this.logger.isEnabled) {
104
+ for (const path of nextState.paths) {
105
+ this.logger.log(() => path.toString());
106
+ }
107
+ }
108
+ this.logger.groupEnd(() => 'Advanced to ' + superEdge + ' with ' + nextState.paths.length + ' paths');
109
+ }
110
+ else if (nextState.givesEmptyResult()) {
111
+ emptyObjectResult = true;
112
+ this.logger.groupEnd(() => 'Federation will resolve an empty object for ' + superEdge);
113
+ }
114
+ else {
115
+ unreachable = true;
116
+ this.logger.log(() => 'Dead end', '🚨 ');
117
+ if (this.logger.isEnabled) {
118
+ for (const path of state.paths) {
119
+ this.logger.log(() => path.toString());
120
+ }
121
+ }
122
+ this.logger.groupEnd(() => 'Unreachable path ' + nextState.superPath.toString());
123
+ }
124
+ }, isEdgeIgnored);
125
+ if (unreachable) {
126
+ break;
127
+ }
128
+ if (emptyObjectResult) {
129
+ break;
130
+ }
131
+ }
132
+ return state;
133
+ }
134
+ walk(method = 'bfs') {
135
+ if (method === 'dfs') {
136
+ return this.dfs();
137
+ }
138
+ return this.bfs();
139
+ }
140
+ nextStep(state, superTail, next, isEdgeIgnored = defaultIsEdgeIgnored) {
141
+ const graphsLeadingToNode = Array.from(new Set(state.paths.map(p => {
142
+ const edge = p.edge();
143
+ const tail = p.tail() ?? p.rootNode();
144
+ const tailGraphName = tail.graphName;
145
+ if (edge && (0, edge_1.isFieldEdge)(edge) && edge.move.provides) {
146
+ return `${tailGraphName}#provides`;
147
+ }
148
+ return tailGraphName;
149
+ })));
150
+ if (superTail.isGraphComboVisited(graphsLeadingToNode)) {
151
+ this.logger.log(() => 'Node already visited: ' + superTail);
152
+ return;
153
+ }
154
+ superTail.setGraphComboAsVisited(graphsLeadingToNode);
155
+ const superEdges = this.supergraph.edgesOfHead(superTail);
156
+ for (const superEdge of superEdges) {
157
+ if (isEdgeIgnored(superEdge)) {
158
+ continue;
159
+ }
160
+ this.logger.group(() => 'Attempt to advance to ' + superEdge + ' (' + state.paths.length + ' paths)');
161
+ if (state.isEdgeVisited(superEdge)) {
162
+ this.logger.groupEnd(() => 'Edge already visited: ' + superEdge);
163
+ continue;
164
+ }
165
+ if (!((0, edge_1.isFieldEdge)(superEdge) || (0, edge_1.isAbstractEdge)(superEdge))) {
166
+ throw new Error('Expected edge to have a FieldMove or AbstractMove');
167
+ }
168
+ const nextState = state.move(superEdge);
169
+ const shortestPathPerTail = new Map();
170
+ const isFieldMove = (0, edge_1.isFieldEdge)(superEdge);
171
+ const id = isFieldMove
172
+ ? `${superEdge.move.typeName}.${superEdge.move.fieldName}`
173
+ : `... on ${superEdge.tail.typeName}`;
174
+ for (const path of state.paths) {
175
+ this.logger.group(() => 'Advance path: ' + path.toString());
176
+ const directPathsResult = this.pathFinder.findDirectPaths(path, isFieldMove ? superEdge.move.typeName : superEdge.tail.typeName, isFieldMove ? superEdge.move.fieldName : null, []);
177
+ if (directPathsResult.success && directPathsResult.paths.length === 0) {
178
+ this.logger.groupEnd(() => 'Abstract type');
179
+ continue;
180
+ }
181
+ if (directPathsResult.success) {
182
+ setShortestPath(shortestPathPerTail, directPathsResult.paths);
183
+ }
184
+ else {
185
+ for (const error of directPathsResult.errors) {
186
+ nextState.addError(error);
187
+ }
188
+ }
189
+ if (directPathsResult.success && superEdge.tail.isLeaf) {
190
+ this.logger.groupEnd(() => 'Reached leaf node, no need to find indirect paths');
191
+ continue;
192
+ }
193
+ const indirectPathsResult = this.pathFinder.findIndirectPaths(path, isFieldMove ? superEdge.move.typeName : superEdge.tail.typeName, isFieldMove ? superEdge.move.fieldName : null, [], [], []);
194
+ if (indirectPathsResult.success) {
195
+ setShortestPath(shortestPathPerTail, indirectPathsResult.paths);
196
+ }
197
+ else {
198
+ for (const error of indirectPathsResult.errors) {
199
+ nextState.addError(error);
200
+ }
201
+ }
202
+ this.logger.groupEnd(() => directPathsResult.success || indirectPathsResult.success
203
+ ? 'Can advance to ' + id
204
+ : 'Cannot advance to ' + id);
205
+ }
206
+ for (const shortestPathByTail of shortestPathPerTail.values()) {
207
+ nextState.addPath(shortestPathByTail);
208
+ }
209
+ next(nextState, superEdge);
210
+ }
211
+ }
212
+ dfs() {
213
+ const unreachable = [];
214
+ const rootNodes = ['Query', 'Mutation', 'Subscription']
215
+ .map(name => this.supergraph.nodeOf(name, false))
216
+ .filter((node) => !!node);
217
+ for (const rootNode of rootNodes) {
218
+ this._dfs(rootNode, new WalkTracker(new operation_path_1.OperationPath(rootNode), this.mergedGraph.nodesOf(rootNode.typeName, false).map(n => new operation_path_1.OperationPath(n))), unreachable);
219
+ }
220
+ return unreachable;
221
+ }
222
+ _dfs(superTail, state, unreachable) {
223
+ if (superTail.isLeaf) {
224
+ return;
225
+ }
226
+ this.nextStep(state, superTail, (nextState, superEdge) => {
227
+ if (nextState.isPossible()) {
228
+ if (this.logger.isEnabled) {
229
+ for (const path of nextState.paths) {
230
+ this.logger.log(() => path.toString());
231
+ }
232
+ }
233
+ this.logger.groupEnd(() => 'Advanced to ' + superEdge + ' with ' + nextState.paths.length + ' paths');
234
+ this._dfs(superEdge.tail, nextState, unreachable);
235
+ }
236
+ else if (nextState.givesEmptyResult()) {
237
+ this.logger.groupEnd(() => 'Federation will resolve an empty object for ' + superEdge);
238
+ }
239
+ else {
240
+ unreachable.push(nextState);
241
+ this.logger.log(() => 'Dead end', '🚨 ');
242
+ if (this.logger.isEnabled) {
243
+ for (const path of state.paths) {
244
+ this.logger.log(() => path.toString());
245
+ }
246
+ }
247
+ this.logger.groupEnd(() => 'Unreachable path ' + nextState.superPath.toString());
248
+ }
249
+ });
250
+ }
251
+ bfs() {
252
+ const unreachable = [];
253
+ const queue = [];
254
+ const rootNodes = ['Query', 'Mutation', 'Subscription']
255
+ .map(name => this.supergraph.nodeOf(name, false))
256
+ .filter((node) => !!node);
257
+ for (const rootNode of rootNodes) {
258
+ queue.push(new WalkTracker(new operation_path_1.OperationPath(rootNode), this.mergedGraph.nodesOf(rootNode.typeName, false).map(n => new operation_path_1.OperationPath(n))));
259
+ }
260
+ while (queue.length > 0) {
261
+ const state = queue.pop();
262
+ if (!state) {
263
+ throw new Error('Unexpected end of queue');
264
+ }
265
+ const superTail = state.superPath.tail() ?? state.superPath.rootNode();
266
+ if (superTail.isLeaf) {
267
+ continue;
268
+ }
269
+ this.nextStep(state, superTail, (nextState, superEdge) => {
270
+ if (nextState.isPossible()) {
271
+ if (this.logger.isEnabled) {
272
+ for (const path of nextState.paths) {
273
+ this.logger.log(() => path.toString());
274
+ }
275
+ }
276
+ this.logger.groupEnd(() => 'Advanced to ' + superEdge + ' with ' + nextState.paths.length + ' paths');
277
+ queue.push(nextState);
278
+ }
279
+ else if (nextState.givesEmptyResult()) {
280
+ this.logger.groupEnd(() => 'Federation will resolve an empty object for ' + superEdge);
281
+ }
282
+ else {
283
+ unreachable.push(nextState);
284
+ this.logger.log(() => 'Dead end', '🚨 ');
285
+ if (this.logger.isEnabled) {
286
+ for (const path of state.paths) {
287
+ this.logger.log(() => path.toString());
288
+ }
289
+ }
290
+ this.logger.groupEnd(() => 'Unreachable path ' + nextState.superPath.toString());
291
+ }
292
+ });
293
+ }
294
+ return unreachable;
295
+ }
296
+ }
297
+ exports.Walker = Walker;
298
+ function setShortestPath(shortestPathPerTail, paths) {
299
+ for (const path of paths) {
300
+ const tail = path.tail() ?? path.rootNode();
301
+ const shortestByTail = shortestPathPerTail.get(tail);
302
+ if (!shortestByTail || shortestByTail.depth() > path.depth()) {
303
+ shortestPathPerTail.set(tail, path);
304
+ }
305
+ }
306
+ }