@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,35 @@
1
+ export function occurrences(str, subString) {
2
+ if (subString.length <= 0) {
3
+ return str.length + 1;
4
+ }
5
+ let n = 0, pos = 0, step = subString.length;
6
+ while (true) {
7
+ pos = str.indexOf(subString, pos);
8
+ if (pos >= 0) {
9
+ ++n;
10
+ pos += step;
11
+ }
12
+ else
13
+ break;
14
+ }
15
+ return n;
16
+ }
17
+ export function scoreKeyFields(keyFields) {
18
+ const fields = occurrences(keyFields, ' ') + 1;
19
+ const innerSelectionSets = occurrences(keyFields, '{') * 3;
20
+ return fields + innerSelectionSets;
21
+ }
22
+ export function lazy(factory) {
23
+ let value;
24
+ return {
25
+ get() {
26
+ if (value === undefined) {
27
+ value = factory();
28
+ }
29
+ return value;
30
+ },
31
+ invalidate() {
32
+ value = undefined;
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,333 @@
1
+ import { isEntityEdge, isFieldEdge } from './edge';
2
+ import { SatisfiabilityError } from './errors';
3
+ import { concatIfNotExistsFields, concatIfNotExistsString, PathFinder } from './finder';
4
+ export class MoveValidator {
5
+ supergraph;
6
+ cache = new Map();
7
+ logger;
8
+ pathFinder;
9
+ constructor(logger, supergraph) {
10
+ this.supergraph = supergraph;
11
+ this.logger = logger.create('MoveValidator');
12
+ this.pathFinder = new PathFinder(this.logger, supergraph, this);
13
+ }
14
+ canResolveFields(fields, path, visitedEdges, visitedGraphs, visitedFields) {
15
+ const cacheKey = JSON.stringify(fields) +
16
+ ' | ' +
17
+ visitedGraphs.join(',') +
18
+ visitedFields.join(',') +
19
+ ' | ' +
20
+ visitedEdges
21
+ .map(e => e.toString())
22
+ .sort()
23
+ .join(',');
24
+ const cached = this.cache.get(cacheKey);
25
+ if (cached) {
26
+ return cached;
27
+ }
28
+ const requirements = [];
29
+ for (const field of fields) {
30
+ requirements.unshift({
31
+ field,
32
+ paths: [path.clone()],
33
+ });
34
+ }
35
+ while (requirements.length > 0) {
36
+ const requirement = requirements.pop();
37
+ if (!requirement) {
38
+ break;
39
+ }
40
+ const result = this.validateRequirement(requirement, visitedEdges, visitedGraphs, visitedFields);
41
+ if (result.success === false) {
42
+ this.cache.set(cacheKey, result);
43
+ return result;
44
+ }
45
+ for (const innerRequirement of result.requirements) {
46
+ requirements.unshift(innerRequirement);
47
+ }
48
+ }
49
+ this.cache.set(cacheKey, {
50
+ success: true,
51
+ errors: undefined,
52
+ });
53
+ return {
54
+ success: true,
55
+ errors: undefined,
56
+ };
57
+ }
58
+ validateRequirement(requirement, visitedEdges, visitedGraphs, visitedFields) {
59
+ const nextPaths = [];
60
+ const errors = [];
61
+ if ('type' in requirement) {
62
+ for (const path of requirement.paths) {
63
+ const directPathsResult = this.pathFinder.findDirectPaths(path, requirement.type.childTypeName, null, visitedEdges);
64
+ if (directPathsResult.success) {
65
+ if (this.logger.isEnabled) {
66
+ this.logger.log(() => 'Possible direct paths:');
67
+ for (const path of directPathsResult.paths) {
68
+ this.logger.log(() => ' ' + path.toString());
69
+ }
70
+ }
71
+ nextPaths.push(...directPathsResult.paths);
72
+ }
73
+ else {
74
+ errors.push(...directPathsResult.errors);
75
+ }
76
+ }
77
+ for (const path of requirement.paths) {
78
+ const indirectPathsResult = this.pathFinder.findIndirectPaths(path, requirement.type.childTypeName, null, visitedEdges, visitedGraphs, visitedFields);
79
+ if (indirectPathsResult.success) {
80
+ if (this.logger.isEnabled) {
81
+ this.logger.log(() => 'Possible indirect paths:');
82
+ for (const path of indirectPathsResult.paths) {
83
+ this.logger.log(() => ' ' + path.toString());
84
+ }
85
+ }
86
+ nextPaths.push(...indirectPathsResult.paths);
87
+ }
88
+ else {
89
+ errors.push(...indirectPathsResult.errors);
90
+ }
91
+ }
92
+ if (nextPaths.length === 0) {
93
+ if (this.logger.isEnabled) {
94
+ this.logger.log(() => 'Could not resolve from:');
95
+ for (const path of requirement.paths) {
96
+ this.logger.log(() => ' ' + path.toString());
97
+ }
98
+ }
99
+ return {
100
+ success: false,
101
+ errors,
102
+ };
103
+ }
104
+ if (!requirement.type.field) {
105
+ return {
106
+ success: true,
107
+ requirements: [],
108
+ };
109
+ }
110
+ return {
111
+ success: true,
112
+ requirements: [
113
+ {
114
+ field: requirement.type.field,
115
+ paths: nextPaths.slice(),
116
+ },
117
+ ],
118
+ };
119
+ }
120
+ const possibleTypes = this.supergraph.possibleTypesOf(requirement.field.typeName);
121
+ const needsAbstractMove = !possibleTypes.includes(requirement.field.typeName);
122
+ if (needsAbstractMove) {
123
+ const requirements = [];
124
+ for (const possibleType of possibleTypes) {
125
+ const abstractMoveRequirement = {
126
+ type: {
127
+ parentTypeName: requirement.field.typeName,
128
+ childTypeName: possibleType,
129
+ field: {
130
+ ...requirement.field,
131
+ typeName: possibleType,
132
+ },
133
+ },
134
+ paths: requirement.paths,
135
+ };
136
+ requirements.push(abstractMoveRequirement);
137
+ }
138
+ this.logger.log(() => 'Abstract move');
139
+ return {
140
+ success: true,
141
+ requirements,
142
+ };
143
+ }
144
+ for (const path of requirement.paths) {
145
+ const directPathsResult = this.pathFinder.findDirectPaths(path, requirement.field.typeName, requirement.field.fieldName, visitedEdges);
146
+ if (directPathsResult.success) {
147
+ if (this.logger.isEnabled) {
148
+ this.logger.log(() => 'Possible direct paths:');
149
+ for (const path of directPathsResult.paths) {
150
+ this.logger.log(() => ' ' + path.toString());
151
+ }
152
+ }
153
+ nextPaths.push(...directPathsResult.paths);
154
+ }
155
+ else {
156
+ errors.push(...directPathsResult.errors);
157
+ }
158
+ }
159
+ for (const path of requirement.paths) {
160
+ const indirectPathsResult = this.pathFinder.findIndirectPaths(path, requirement.field.typeName, requirement.field.fieldName, visitedEdges, visitedGraphs, visitedFields);
161
+ if (indirectPathsResult.success) {
162
+ if (this.logger.isEnabled) {
163
+ this.logger.log(() => 'Possible indirect paths:');
164
+ for (const path of indirectPathsResult.paths) {
165
+ this.logger.log(() => ' ' + path.toString());
166
+ }
167
+ }
168
+ nextPaths.push(...indirectPathsResult.paths);
169
+ }
170
+ else {
171
+ errors.push(...indirectPathsResult.errors);
172
+ }
173
+ }
174
+ if (nextPaths.length === 0) {
175
+ this.logger.log(() => `Failed to resolve field ${requirement.field.typeName}.${requirement.field.fieldName} from:`);
176
+ if (this.logger.isEnabled) {
177
+ for (const path of requirement.paths) {
178
+ this.logger.log(() => ` ` + path);
179
+ }
180
+ }
181
+ return {
182
+ success: false,
183
+ errors: errors.filter(e => e.isMatchingField(requirement.field.typeName, requirement.field.fieldName)),
184
+ };
185
+ }
186
+ if (!requirement.field.selectionSet || requirement.field.selectionSet.length === 0) {
187
+ return {
188
+ success: true,
189
+ requirements: [],
190
+ };
191
+ }
192
+ return {
193
+ success: true,
194
+ requirements: requirement.field.selectionSet.map(field => ({
195
+ field,
196
+ paths: nextPaths.slice(),
197
+ })),
198
+ };
199
+ }
200
+ isExternal(edge) {
201
+ if (!isFieldEdge(edge)) {
202
+ return false;
203
+ }
204
+ if (edge.move.provided) {
205
+ return false;
206
+ }
207
+ if (!edge.head.typeState) {
208
+ return false;
209
+ }
210
+ if (edge.head.typeState.kind !== 'object') {
211
+ return false;
212
+ }
213
+ const fieldState = edge.head.typeState.fields.get(edge.move.fieldName);
214
+ if (!fieldState) {
215
+ return false;
216
+ }
217
+ const objectTypeStateInGraph = edge.head.typeState.byGraph.get(edge.head.graphId);
218
+ if (!objectTypeStateInGraph) {
219
+ return false;
220
+ }
221
+ const fieldStateInGraph = fieldState.byGraph.get(edge.head.graphId);
222
+ if (!fieldStateInGraph) {
223
+ return false;
224
+ }
225
+ const external = fieldState.byGraph.get(edge.head.graphId)?.external ?? false;
226
+ if (!external) {
227
+ return false;
228
+ }
229
+ const isFedV1 = fieldStateInGraph.version === 'v1.0';
230
+ if (isFedV1 && objectTypeStateInGraph.extension && fieldState.usedAsKey) {
231
+ return false;
232
+ }
233
+ if (!fieldStateInGraph.usedAsKey) {
234
+ return true;
235
+ }
236
+ let hasNonExternalFields = false;
237
+ if (isFedV1) {
238
+ for (const [fieldName, fieldState] of edge.head.typeState.fields) {
239
+ if (fieldName === edge.move.fieldName) {
240
+ continue;
241
+ }
242
+ const fieldStateInGraph = fieldState.byGraph.get(edge.head.graphId);
243
+ if (!fieldStateInGraph) {
244
+ continue;
245
+ }
246
+ if (!fieldStateInGraph.external) {
247
+ hasNonExternalFields = true;
248
+ break;
249
+ }
250
+ }
251
+ }
252
+ if (hasNonExternalFields) {
253
+ return false;
254
+ }
255
+ if (objectTypeStateInGraph.extension) {
256
+ return false;
257
+ }
258
+ return true;
259
+ }
260
+ isOverridden(edge) {
261
+ if (!isFieldEdge(edge)) {
262
+ return false;
263
+ }
264
+ if (!edge.head.typeState) {
265
+ return false;
266
+ }
267
+ if (!edge.head.typeState || edge.head.typeState.kind !== 'object') {
268
+ return false;
269
+ }
270
+ const fieldState = edge.head.typeState.fields.get(edge.move.fieldName);
271
+ if (!fieldState) {
272
+ return false;
273
+ }
274
+ if (!fieldState.override) {
275
+ return false;
276
+ }
277
+ const overriddenGraphId = this.supergraph.graphNameToId(fieldState.override);
278
+ if (!overriddenGraphId) {
279
+ return false;
280
+ }
281
+ return edge.head.graphId === overriddenGraphId;
282
+ }
283
+ isEdgeResolvable(edge, path, visitedEdges, visitedGraphs, visitedFields) {
284
+ this.logger.group(() => 'Checking resolvability of ' + edge);
285
+ this.logger.log(() => 'Visited graphs: ' + visitedGraphs.join(','));
286
+ const resolvability = edge.getResolvability(concatIfNotExistsString(visitedGraphs, edge.tail.graphName));
287
+ if (resolvability) {
288
+ this.logger.groupEnd(() => resolvability.success
289
+ ? `Can move to ${edge}`
290
+ : `Cannot move to ${edge} (already visited: ${resolvability.error.kind})`);
291
+ return resolvability;
292
+ }
293
+ if (isFieldEdge(edge)) {
294
+ if (this.isOverridden(edge)) {
295
+ this.logger.groupEnd(() => 'Cannot move to ' + edge + ' because it is overridden');
296
+ return edge.setResolvable(false, visitedGraphs, SatisfiabilityError.forMissingField(edge.tail.graphName, edge.move.typeName, edge.move.fieldName));
297
+ }
298
+ if (edge.move.requires) {
299
+ this.logger.log(() => 'Detected @requires');
300
+ const newVisitedGraphs = concatIfNotExistsString(visitedGraphs, edge.tail.graphName);
301
+ const newVisitedFields = concatIfNotExistsFields(visitedFields, edge.move.requires);
302
+ this.logger.log(() => 'Visited graphs: ' + newVisitedGraphs.join(','));
303
+ if (this.canResolveFields(edge.move.requires.fields, path, visitedEdges.concat(edge), newVisitedGraphs, newVisitedFields).success) {
304
+ this.logger.groupEnd(() => 'Can move to ' + edge);
305
+ return edge.setResolvable(true, newVisitedGraphs);
306
+ }
307
+ this.logger.groupEnd(() => 'Cannot move to ' + edge + ' because @require is not resolvable');
308
+ return {
309
+ success: false,
310
+ error: SatisfiabilityError.forRequire(edge.head.graphName, edge.move.typeName, edge.move.fieldName),
311
+ };
312
+ }
313
+ else if (this.isExternal(edge)) {
314
+ this.logger.groupEnd(() => 'Cannot move to ' + edge + ' because it is external and cross-graph');
315
+ return edge.setResolvable(false, visitedGraphs, SatisfiabilityError.forExternal(edge.head.graphName, edge.move.typeName, edge.move.fieldName));
316
+ }
317
+ }
318
+ else if (isEntityEdge(edge)) {
319
+ this.logger.log(() => 'Detected @key');
320
+ const newVisitedGraphs = concatIfNotExistsString(visitedGraphs, edge.tail.graphName);
321
+ const newVisitedFields = concatIfNotExistsFields(visitedFields, edge.move.keyFields);
322
+ this.logger.log(() => 'Visited graphs: ' + newVisitedGraphs.join(','));
323
+ if (this.canResolveFields(edge.move.keyFields.fields, path, visitedEdges.concat(edge), newVisitedGraphs, newVisitedFields).success) {
324
+ this.logger.groupEnd(() => 'Can move to ' + edge);
325
+ return edge.setResolvable(true, newVisitedGraphs);
326
+ }
327
+ this.logger.groupEnd(() => 'Cannot move to ' + edge + ' because key fields are not resolvable');
328
+ return edge.setResolvable(false, newVisitedGraphs, SatisfiabilityError.forKey(edge.head.graphName, edge.tail.graphName, edge.head.typeName, edge.move.keyFields.toString()));
329
+ }
330
+ this.logger.groupEnd(() => 'Can move to ' + edge);
331
+ return edge.setResolvable(true, visitedGraphs);
332
+ }
333
+ }
@@ -0,0 +1,46 @@
1
+ import { lazy } from './helpers';
2
+ export class FieldMove {
3
+ typeName;
4
+ fieldName;
5
+ requires;
6
+ provides;
7
+ provided;
8
+ _toString = lazy(() => {
9
+ let str = this.fieldName;
10
+ if (this.requires) {
11
+ str += ` @require(${this.requires})`;
12
+ }
13
+ if (this.provides) {
14
+ str += ` @provides(${this.provides})`;
15
+ }
16
+ if (this.provided) {
17
+ str += ' @provided';
18
+ }
19
+ return str;
20
+ });
21
+ constructor(typeName, fieldName, requires = null, provides = null, provided = false) {
22
+ this.typeName = typeName;
23
+ this.fieldName = fieldName;
24
+ this.requires = requires;
25
+ this.provides = provides;
26
+ this.provided = provided;
27
+ }
28
+ toString() {
29
+ return this._toString.get();
30
+ }
31
+ }
32
+ export class AbstractMove {
33
+ toString() {
34
+ return `🔮`;
35
+ }
36
+ }
37
+ export class EntityMove {
38
+ keyFields;
39
+ _toString = lazy(() => `🔑 ${this.keyFields}`);
40
+ constructor(keyFields) {
41
+ this.keyFields = keyFields;
42
+ }
43
+ toString() {
44
+ return this._toString.get();
45
+ }
46
+ }
@@ -0,0 +1,85 @@
1
+ import { lazy } from './helpers';
2
+ export class Node {
3
+ index;
4
+ typeName;
5
+ typeState;
6
+ graphId;
7
+ graphName;
8
+ _toString = lazy(() => `${this.typeName}/${this.graphName}`);
9
+ isLeaf = false;
10
+ childrenIndex = new Map();
11
+ visitedGraphCombos = [];
12
+ constructor(index, typeName, typeState, graphId, graphName) {
13
+ this.index = index;
14
+ this.typeName = typeName;
15
+ this.typeState = typeState;
16
+ this.graphId = graphId;
17
+ this.graphName = graphName;
18
+ if (this.typeState === undefined) {
19
+ throw new Error(`Expected typeState to be defined for ${typeName} in subgraph ${graphName}`);
20
+ }
21
+ if (this.typeState === null ||
22
+ this.typeState.kind === 'scalar' ||
23
+ this.typeState.kind === 'enum') {
24
+ this.isLeaf = true;
25
+ }
26
+ }
27
+ withoutState() {
28
+ this.childrenIndex = new Map();
29
+ this.visitedGraphCombos = [];
30
+ return this;
31
+ }
32
+ addFieldEdge(fieldName, edgeAt) {
33
+ const id = `field__${fieldName}`;
34
+ const indexes = this.childrenIndex.get(id);
35
+ if (indexes) {
36
+ if (!indexes.includes(edgeAt)) {
37
+ indexes.push(edgeAt);
38
+ }
39
+ }
40
+ else {
41
+ this.childrenIndex.set(id, [edgeAt]);
42
+ }
43
+ }
44
+ getFieldEdgeIndexes(fieldName) {
45
+ return this.childrenIndex.get(`field__${fieldName}`);
46
+ }
47
+ addEntityEdge(typeName, edgeAt) {
48
+ this.pushToChildrenIndex(`entity__${typeName}`, edgeAt);
49
+ }
50
+ getEntityEdgeIndexes(typeName) {
51
+ return this.childrenIndex.get(`entity__${typeName}`);
52
+ }
53
+ addAbstractEdge(typeName, edgeAt) {
54
+ this.pushToChildrenIndex(`abstract__${typeName}`, edgeAt);
55
+ }
56
+ getAbstractEdgeIndexes(typeName) {
57
+ return this.childrenIndex.get(`abstract__${typeName}`);
58
+ }
59
+ addCrossGraphEdge(typeName, edgeAt) {
60
+ this.pushToChildrenIndex(`cross-graph__${typeName}`, edgeAt);
61
+ }
62
+ getCrossGraphEdgeIndexes(typeName) {
63
+ return this.childrenIndex.get(`cross-graph__${typeName}`);
64
+ }
65
+ pushToChildrenIndex(id, edgeAt) {
66
+ const indexes = this.childrenIndex.get(id);
67
+ if (indexes) {
68
+ if (!indexes.includes(edgeAt)) {
69
+ indexes.push(edgeAt);
70
+ }
71
+ }
72
+ else {
73
+ this.childrenIndex.set(id, [edgeAt]);
74
+ }
75
+ }
76
+ isGraphComboVisited(graphNameProvidesCombos) {
77
+ return this.visitedGraphCombos.some(visitedGraphs => visitedGraphs.every(g => graphNameProvidesCombos.includes(g)));
78
+ }
79
+ setGraphComboAsVisited(graphNames) {
80
+ this.visitedGraphCombos.push(graphNames);
81
+ }
82
+ toString() {
83
+ return this._toString.get();
84
+ }
85
+ }
@@ -0,0 +1,66 @@
1
+ import { isFieldEdge } from './edge';
2
+ import { lazy } from './helpers';
3
+ export class OperationPath {
4
+ _rootNode;
5
+ _toString = lazy(() => {
6
+ let str = this._rootNode.toString();
7
+ for (let i = 0; i < this.previousEdges.length; i++) {
8
+ const edge = this.previousEdges[i];
9
+ if (edge) {
10
+ str += ` -(${edge.move})-> ${edge.tail}`;
11
+ }
12
+ }
13
+ return str;
14
+ });
15
+ previousNodes = [];
16
+ previousEdges = [];
17
+ previousSteps = [];
18
+ constructor(_rootNode) {
19
+ this._rootNode = _rootNode;
20
+ }
21
+ move(edge) {
22
+ this._toString.invalidate();
23
+ this.advance(edge);
24
+ return this;
25
+ }
26
+ clone() {
27
+ const newPath = new OperationPath(this._rootNode);
28
+ newPath.previousNodes = this.previousNodes.slice();
29
+ newPath.previousEdges = this.previousEdges.slice();
30
+ newPath.previousSteps = this.previousSteps.slice();
31
+ return newPath;
32
+ }
33
+ depth() {
34
+ return this.previousEdges.length;
35
+ }
36
+ edge() {
37
+ return this.previousEdges[this.previousEdges.length - 1];
38
+ }
39
+ steps() {
40
+ return this.previousSteps;
41
+ }
42
+ tail() {
43
+ return this.edge()?.tail;
44
+ }
45
+ rootNode() {
46
+ return this._rootNode;
47
+ }
48
+ isVisitedEdge(edge) {
49
+ return this.previousEdges.includes(edge);
50
+ }
51
+ toString() {
52
+ return this._toString.get();
53
+ }
54
+ advance(edge) {
55
+ this.previousEdges.push(edge);
56
+ this.previousNodes.push(edge.head);
57
+ this.previousSteps.push(isFieldEdge(edge)
58
+ ? {
59
+ typeName: edge.move.typeName,
60
+ fieldName: edge.move.fieldName,
61
+ }
62
+ : {
63
+ typeName: edge.tail.typeName,
64
+ });
65
+ }
66
+ }
@@ -0,0 +1,33 @@
1
+ import { Logger, LoggerContext } from '../../../../utils/logger';
2
+ import { SUPERGRAPH_ID } from './constants';
3
+ import { FieldsResolver } from './fields';
4
+ import { Graph } from './graph';
5
+ import { MoveValidator } from './move-validator';
6
+ import { Walker } from './walker';
7
+ export class Supergraph {
8
+ supergraph;
9
+ mergedGraph;
10
+ fieldsResolver;
11
+ moveRequirementChecker;
12
+ logger = new Logger('Supergraph', new LoggerContext());
13
+ constructor(supergraphState) {
14
+ this.fieldsResolver = new FieldsResolver(supergraphState);
15
+ this.supergraph = new Graph(this.logger, SUPERGRAPH_ID, 'supergraph', supergraphState, this.fieldsResolver, true);
16
+ this.mergedGraph = new Graph(this.logger, SUPERGRAPH_ID, 'merged', supergraphState, this.fieldsResolver);
17
+ for (const [id, subgraphState] of supergraphState.subgraphs) {
18
+ this.mergedGraph.addSubgraph(new Graph(this.logger, id, subgraphState.graph.name, supergraphState, this.fieldsResolver, false)
19
+ .addFromRoots()
20
+ .addFromEntities()
21
+ .addUnreachableTypes());
22
+ }
23
+ this.mergedGraph.joinSubgraphs();
24
+ this.supergraph.addFromRoots();
25
+ this.moveRequirementChecker = new MoveValidator(this.logger, this.mergedGraph);
26
+ }
27
+ validate() {
28
+ return new Walker(this.logger, this.moveRequirementChecker, this.supergraph, this.mergedGraph).walk('dfs');
29
+ }
30
+ validateOperation(operation, steps) {
31
+ return new Walker(this.logger, this.moveRequirementChecker, this.supergraph, this.mergedGraph).walkTrail(operation, steps);
32
+ }
33
+ }