@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
@@ -30,7 +30,7 @@ const validation_context_js_1 = require("./validation-context.js");
30
30
  function validateSupergraph(subgraphStates, state, __internal) {
31
31
  const context = (0, validation_context_js_1.createSupergraphValidationContext)(subgraphStates);
32
32
  for (const subgraphState of subgraphStates.values()) {
33
- state.addGraph(subgraphState.graph);
33
+ state.addSubgraph(subgraphState);
34
34
  }
35
35
  const preSupergraphRules = [required_query_rule_js_1.RequiredQueryRule, types_of_the_same_kind_rule_js_1.TypesOfTheSameKindRule];
36
36
  const rulesToSkip = __internal?.disableValidationRules ?? [];
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Logger = exports.LoggerContext = void 0;
7
+ const debug_1 = __importDefault(require("debug"));
8
+ const originalFormatArgs = debug_1.default.formatArgs;
9
+ debug_1.default.formatArgs = function (args) {
10
+ originalFormatArgs.call(this, args);
11
+ const line = args[0];
12
+ const noColorsPrefix = ` ${this.namespace} `;
13
+ const colorsPrefix = `${this.namespace}`;
14
+ const noColorsStartsAt = line.indexOf(noColorsPrefix);
15
+ if (noColorsStartsAt > -1) {
16
+ args[0] = line.slice(noColorsStartsAt + noColorsPrefix.length);
17
+ }
18
+ else {
19
+ const colorsStartsAt = line.indexOf(colorsPrefix);
20
+ args[0] = line.slice(colorsStartsAt + colorsPrefix.length);
21
+ }
22
+ };
23
+ class LoggerContext {
24
+ indent = {
25
+ level: 0,
26
+ str: '',
27
+ times: [],
28
+ };
29
+ maxIdLength = 0;
30
+ idUpdateFns = [];
31
+ logger = (0, debug_1.default)('composition');
32
+ firstLoggerAt = 0;
33
+ down(time) {
34
+ this.updateIndent(+1, time);
35
+ }
36
+ up(time) {
37
+ if (this.indent.level > 0) {
38
+ this.updateIndent(-1, time);
39
+ return this.indent.times.pop();
40
+ }
41
+ }
42
+ getTime() {
43
+ if (!this.firstLoggerAt) {
44
+ this.firstLoggerAt = Date.now();
45
+ return 0;
46
+ }
47
+ return Date.now() - this.firstLoggerAt;
48
+ }
49
+ getIndent() {
50
+ return this.indent;
51
+ }
52
+ register(id, idUpdateFn) {
53
+ idUpdateFn(this.maxIdLength);
54
+ this.idUpdateFns.push(idUpdateFn);
55
+ const newMaxIdLength = Math.max(this.maxIdLength, id.length);
56
+ if (newMaxIdLength > this.maxIdLength) {
57
+ this.maxIdLength = newMaxIdLength;
58
+ this.idUpdateFns.forEach(fn => fn(newMaxIdLength));
59
+ }
60
+ return this.logger.extend(id);
61
+ }
62
+ updateIndent(delta, time) {
63
+ if (this.indent.level + delta < 0) {
64
+ return;
65
+ }
66
+ if (delta > 0) {
67
+ this.indent.times.push(time);
68
+ }
69
+ this.indent.level += delta;
70
+ this.indent.str = '│ '.repeat(this.indent.level);
71
+ }
72
+ }
73
+ exports.LoggerContext = LoggerContext;
74
+ class Logger {
75
+ id;
76
+ context;
77
+ isEnabled;
78
+ debug;
79
+ idPrefix;
80
+ constructor(id, context) {
81
+ this.id = id;
82
+ this.context = context;
83
+ this.id = id;
84
+ this.context = context;
85
+ this.idPrefix = `${id}`;
86
+ this.debug = this.context.register(this.id, this._updateIdPrefix.bind(this));
87
+ this.isEnabled = this.debug.enabled;
88
+ this.debug.log = console.log;
89
+ }
90
+ log(msg, prefix = '- ') {
91
+ if (this.isEnabled) {
92
+ this._log(prefix, msg);
93
+ }
94
+ }
95
+ group(msg) {
96
+ if (this.isEnabled) {
97
+ this.log(msg, '┌ ');
98
+ this.context.down(Date.now());
99
+ }
100
+ }
101
+ groupEnd(msg) {
102
+ if (this.isEnabled) {
103
+ const time = this.context.up(Date.now());
104
+ let message = msg ? (typeof msg === 'string' ? msg : msg()) : '';
105
+ if (time) {
106
+ message += ` (${Date.now() - time}ms)`;
107
+ }
108
+ this.log(message, '└ ');
109
+ }
110
+ }
111
+ create(id) {
112
+ return new Logger(id, this.context);
113
+ }
114
+ _log(prefix, msg) {
115
+ const indent = this.context.getIndent().str;
116
+ const message = typeof msg === 'string' ? msg : msg();
117
+ if (this.isEnabled) {
118
+ const sinceStart = this.context.getTime();
119
+ const text = this.idPrefix + ' ' + indent + prefix + message + ` +${sinceStart}ms`;
120
+ this.debug(text);
121
+ }
122
+ }
123
+ _updateIdPrefix(maxLength) {
124
+ this.idPrefix = this.id.padEnd(maxLength, ' ');
125
+ }
126
+ }
127
+ exports.Logger = Logger;
@@ -28,10 +28,41 @@ export function printLink(link) {
28
28
  },
29
29
  value: {
30
30
  kind: Kind.LIST,
31
- values: link.imports.map(im => ({
32
- kind: Kind.STRING,
33
- value: im.name,
34
- })),
31
+ values: link.imports.map(im => {
32
+ if (!im.alias) {
33
+ return {
34
+ kind: Kind.STRING,
35
+ value: im.name,
36
+ };
37
+ }
38
+ return {
39
+ kind: Kind.OBJECT,
40
+ fields: [
41
+ {
42
+ kind: Kind.OBJECT_FIELD,
43
+ name: {
44
+ kind: Kind.NAME,
45
+ value: 'name',
46
+ },
47
+ value: {
48
+ kind: Kind.STRING,
49
+ value: im.name,
50
+ },
51
+ },
52
+ {
53
+ kind: Kind.OBJECT_FIELD,
54
+ name: {
55
+ kind: Kind.NAME,
56
+ value: 'as',
57
+ },
58
+ value: {
59
+ kind: Kind.STRING,
60
+ value: im.alias,
61
+ },
62
+ },
63
+ ],
64
+ };
65
+ }),
35
66
  },
36
67
  },
37
68
  ],
@@ -153,7 +184,11 @@ export function mergeLinks(links) {
153
184
  return Array.from(groupByIdentity.entries()).map(([identity, link]) => ({
154
185
  identity,
155
186
  version: link.highestVersion,
156
- imports: Array.from(link.imports).map(link => ({ kind: link.kind, name: link.name })),
187
+ imports: Array.from(link.imports).map(link => ({
188
+ kind: link.kind,
189
+ name: link.name,
190
+ alias: link.alias,
191
+ })),
157
192
  name: link.name,
158
193
  }));
159
194
  }
@@ -34,10 +34,10 @@ export function validateDirectiveAgainstOriginal(providedDirectiveNode, directiv
34
34
  if (expectedType !== providedType) {
35
35
  const isNonNullableString = providedType === 'String!';
36
36
  const allowedFieldSetTypes = isFederationV2
37
- ? ['FieldSet!', 'federation__FieldSet!', '_FieldSet!']
37
+ ? ['FieldSet!', 'federation__FieldSet!', '_FieldSet!', '[String!]!']
38
38
  : ['_FieldSet!', 'String', 'String!'];
39
39
  const fieldSetTypesInSpec = isFederationV2
40
- ? ['FieldSet!', 'federation__FieldSet!', '_FieldSet!']
40
+ ? ['FieldSet!', 'federation__FieldSet!', '_FieldSet!', '[String!]!']
41
41
  : ['_FieldSet!', 'FieldSet!', 'String'];
42
42
  const expectsFieldSet = fieldSetTypesInSpec.includes(expectedType);
43
43
  if (!isNonNullableString && expectsFieldSet) {
@@ -101,6 +101,9 @@ export function createSubgraphStateBuilder(graph, typeDefs, version, links) {
101
101
  throw new Error(`Expected to find an object type`);
102
102
  }
103
103
  objectTypeBuilder.field.setType(typeDef.name.value, node.name.value, printOutputType(node.type));
104
+ if (typeDef.kind === Kind.OBJECT_TYPE_EXTENSION) {
105
+ objectTypeBuilder.field.setExtension(typeDef.name.value, node.name.value);
106
+ }
104
107
  if (version === 'v1.0' &&
105
108
  (typeDef.kind === Kind.OBJECT_TYPE_DEFINITION ||
106
109
  typeDef.name.value === expectedQueryTypeName ||
@@ -590,6 +593,9 @@ function objectTypeFactory(state, renameObject) {
590
593
  setType(typeName, fieldName, fieldType) {
591
594
  getOrCreateObjectField(state, renameObject, typeName, fieldName).type = fieldType;
592
595
  },
596
+ setExtension(typeName, fieldName) {
597
+ getOrCreateObjectField(state, renameObject, typeName, fieldName).extension = true;
598
+ },
593
599
  setDirective(typeName, fieldName, directive) {
594
600
  getOrCreateObjectField(state, renameObject, typeName, fieldName).ast.directives.push(directive);
595
601
  },
@@ -1102,6 +1108,7 @@ function getOrCreateObjectField(state, renameObject, typeName, fieldName) {
1102
1108
  external: false,
1103
1109
  inaccessible: false,
1104
1110
  authenticated: false,
1111
+ extension: false,
1105
1112
  policies: [],
1106
1113
  scopes: [],
1107
1114
  used: false,
@@ -1141,6 +1148,7 @@ function getOrCreateInterfaceField(state, typeName, fieldName) {
1141
1148
  required: false,
1142
1149
  provided: false,
1143
1150
  shareable: false,
1151
+ extension: false,
1144
1152
  tags: new Set(),
1145
1153
  args: new Map(),
1146
1154
  ast: {
@@ -103,12 +103,14 @@ export function ProvidesRules(context) {
103
103
  context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @provides(fields: ${printedFieldsValue}): field ${info.typeDefinition.name.value}.${info.fieldName} cannot be included because it has arguments (fields with argument are not allowed in @provides)`, { nodes: directiveNode, extensions: { code: 'PROVIDES_FIELDS_HAS_ARGS' } }));
104
104
  },
105
105
  interceptNonExternalField(info) {
106
- isValid = false;
107
- context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @provides(fields: ${printedFieldsValue}): field "${info.typeDefinition.name.value}.${info.fieldName}" should not be part of a @provides since it is already provided by this subgraph (it is not marked @external)`, {
108
- extensions: {
109
- code: 'PROVIDES_FIELDS_MISSING_EXTERNAL',
110
- },
111
- }));
106
+ if (context.satisfiesVersionRange('> v1.0')) {
107
+ isValid = false;
108
+ context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @provides(fields: ${printedFieldsValue}): field "${info.typeDefinition.name.value}.${info.fieldName}" should not be part of a @provides since it is already provided by this subgraph (it is not marked @external)`, {
109
+ extensions: {
110
+ code: 'PROVIDES_FIELDS_MISSING_EXTERNAL',
111
+ },
112
+ }));
113
+ }
112
114
  },
113
115
  interceptExternalField(info) {
114
116
  const keyDirectives = info.typeDefinition.directives?.filter(directive => context.isAvailableFederationDirective('key', directive));
@@ -30,7 +30,7 @@ export function RequiresRules(context) {
30
30
  return;
31
31
  }
32
32
  const printedFieldsValue = print(fieldsArg.value);
33
- if (fieldsArg.value.kind !== Kind.STRING) {
33
+ if (fieldsArg.value.kind !== Kind.STRING && fieldsArg.value.kind !== Kind.ENUM) {
34
34
  context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @requires(fields: ${printedFieldsValue}): Invalid value for argument "fields": must be a string.`, {
35
35
  nodes: directiveNode,
36
36
  extensions: {
@@ -103,12 +103,14 @@ export function RequiresRules(context) {
103
103
  }
104
104
  },
105
105
  interceptNonExternalField(info) {
106
- isValid = false;
107
- context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @requires(fields: ${printedFieldsValue}): field "${info.typeDefinition.name.value}.${info.fieldName}" should not be part of a @requires since it is already provided by this subgraph (it is not marked @external)`, {
108
- extensions: {
109
- code: 'REQUIRES_FIELDS_MISSING_EXTERNAL',
110
- },
111
- }));
106
+ if (context.satisfiesVersionRange('> v1.0')) {
107
+ isValid = false;
108
+ context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @requires(fields: ${printedFieldsValue}): field "${info.typeDefinition.name.value}.${info.fieldName}" should not be part of a @requires since it is already provided by this subgraph (it is not marked @external)`, {
109
+ extensions: {
110
+ code: 'REQUIRES_FIELDS_MISSING_EXTERNAL',
111
+ },
112
+ }));
113
+ }
112
114
  },
113
115
  });
114
116
  if (isValid) {
@@ -3,7 +3,7 @@ import { andList } from '../../utils/format.js';
3
3
  import { isList, isNonNull, stripList, stripNonNull, stripTypeModifiers, } from '../../utils/state.js';
4
4
  import { TypeKind, } from '../state.js';
5
5
  const specifiedScalars = new Set(specifiedScalarTypes.map(t => t.name));
6
- export function validateSubgraphState(state) {
6
+ export function validateSubgraphState(state, context) {
7
7
  const errors = [];
8
8
  function reportError(message) {
9
9
  errors.push(new GraphQLError(message, {
@@ -13,7 +13,7 @@ export function validateSubgraphState(state) {
13
13
  }));
14
14
  }
15
15
  validateRootTypes(state, reportError);
16
- validateDirectives(state, reportError);
16
+ validateDirectives(state, reportError, context);
17
17
  validateTypes(state, reportError);
18
18
  return errors;
19
19
  }
@@ -54,13 +54,19 @@ function validateRootTypes(state, reportError) {
54
54
  function capitalize(str) {
55
55
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
56
56
  }
57
- function validateDirectives(state, reportError) {
57
+ function validateDirectives(state, reportError, context) {
58
58
  for (const directive of state.types.values()) {
59
59
  if (isDirective(directive)) {
60
+ if (context.isLinkSpecDirective(directive.name)) {
61
+ continue;
62
+ }
60
63
  validateName(reportError, directive.name);
61
64
  for (const [argName, arg] of directive.args) {
62
65
  validateName(reportError, argName);
63
66
  const argInputTypeName = stripTypeModifiers(arg.type);
67
+ if (context.isLinkSpecType(argInputTypeName)) {
68
+ continue;
69
+ }
64
70
  if (!isInputType(state, argInputTypeName)) {
65
71
  reportError(`The type of @${directive.name}(${arg.name}:) must be Input Type ` +
66
72
  `but got: ${arg.type}.`);
@@ -157,7 +157,7 @@ export function validateSubgraph(subgraph, stateBuilder, federation, __internal)
157
157
  : null,
158
158
  subgraph.typeDefs,
159
159
  ].filter(onlyDocumentNode));
160
- const subgraphStateErrors = validateSubgraphState(stateBuilder.state);
160
+ const subgraphStateErrors = validateSubgraphState(stateBuilder.state, validationContext);
161
161
  const simpleValidationContext = createSimpleValidationContext(fullTypeDefs, typeNodeInfo);
162
162
  visit(fullTypeDefs, visitInParallel(graphqlRules.map(rule => {
163
163
  if (rulesToSkip.includes(rule.name)) {
@@ -316,7 +316,9 @@ function cleanSubgraphTypeDefsFromSubgraphSpec(typeDefs) {
316
316
  const queryTypeName = schemaDef?.operationTypes?.find(op => op.operation === OperationTypeNode.QUERY)?.type.name
317
317
  .value ?? 'Query';
318
318
  typeDefs.definitions = typeDefs.definitions.filter(def => {
319
- if (def.kind === Kind.SCALAR_TYPE_DEFINITION && def.name.value === '_Any') {
319
+ if ((def.kind === Kind.SCALAR_TYPE_DEFINITION ||
320
+ def.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION) &&
321
+ def.name.value === '_Any') {
320
322
  return false;
321
323
  }
322
324
  if (def.kind === Kind.UNION_TYPE_DEFINITION && def.name.value === '_Entity') {
@@ -1,7 +1,11 @@
1
- import { Kind, specifiedScalarTypes, } from 'graphql';
1
+ import { isTypeDefinitionNode, Kind, parse, specifiedScalarTypes, } from 'graphql';
2
2
  import { createSpecSchema } from '../../specifications/federation.js';
3
+ import { sdl as linkSpecSdl } from '../../specifications/link.js';
3
4
  import { stripTypeModifiers } from '../../utils/state.js';
4
5
  import { TypeKind } from '../state.js';
6
+ const linkSpec = parse(linkSpecSdl);
7
+ const linkSpecDirectives = linkSpec.definitions.filter((def) => def.kind === Kind.DIRECTIVE_DEFINITION);
8
+ const linkSpecTypes = linkSpec.definitions.filter(isTypeDefinitionNode);
5
9
  export function createSimpleValidationContext(typeDefs, typeNodeInfo) {
6
10
  let reportedErrors = [];
7
11
  const directiveDefinitionMap = new Map();
@@ -151,6 +155,12 @@ export function createSubgraphValidationContext(subgraph, federation, typeNodeIn
151
155
  }
152
156
  return {
153
157
  stateBuilder,
158
+ isLinkSpecDirective(name) {
159
+ return linkSpecDirectives.some(d => d.name.value === name);
160
+ },
161
+ isLinkSpecType(name) {
162
+ return linkSpecTypes.some(t => t.name.value === name);
163
+ },
154
164
  isAvailableFederationType(name) {
155
165
  const alternativeNames = typeAlternativeNamesMap.get(name);
156
166
  if (alternativeNames) {
@@ -28,11 +28,13 @@ export function directiveBuilder() {
28
28
  argState.byGraph.set(graph.id, {
29
29
  type: arg.type,
30
30
  defaultValue: arg.defaultValue,
31
+ version: graph.version,
31
32
  });
32
33
  }
33
34
  directiveState.byGraph.set(graph.id, {
34
35
  locations: directive.locations,
35
36
  repeatable: directive.repeatable,
37
+ version: graph.version,
36
38
  });
37
39
  },
38
40
  composeSupergraphNode(directive) {
@@ -60,6 +62,7 @@ function getOrCreateDirective(state, directiveName) {
60
62
  return existing;
61
63
  }
62
64
  const def = {
65
+ kind: 'directive',
63
66
  name: directiveName,
64
67
  locations: new Set(),
65
68
  byGraph: new Map(),
@@ -36,6 +36,7 @@ export function enumTypeBuilder() {
36
36
  }
37
37
  enumTypeState.byGraph.set(graph.id, {
38
38
  inaccessible: type.inaccessible,
39
+ version: graph.version,
39
40
  });
40
41
  for (const value of type.values.values()) {
41
42
  const valueState = getOrCreateEnumValue(enumTypeState, value.name);
@@ -51,6 +52,7 @@ export function enumTypeBuilder() {
51
52
  }
52
53
  valueState.byGraph.set(graph.id, {
53
54
  inaccessible: value.inaccessible,
55
+ version: graph.version,
54
56
  });
55
57
  }
56
58
  },
@@ -110,6 +112,7 @@ function getOrCreateEnumType(state, typeName) {
110
112
  return existing;
111
113
  }
112
114
  const def = {
115
+ kind: 'enum',
113
116
  name: typeName,
114
117
  values: new Map(),
115
118
  tags: new Set(),
@@ -16,6 +16,7 @@ export function inputObjectTypeBuilder() {
16
16
  }
17
17
  inputObjectTypeState.byGraph.set(graph.id, {
18
18
  inaccessible: type.inaccessible,
19
+ version: graph.version,
19
20
  });
20
21
  for (const field of type.fields.values()) {
21
22
  const fieldState = getOrCreateField(inputObjectTypeState, field.name, field.type);
@@ -39,6 +40,7 @@ export function inputObjectTypeBuilder() {
39
40
  type: field.type,
40
41
  inaccessible: field.inaccessible,
41
42
  defaultValue: field.defaultValue,
43
+ version: graph.version,
42
44
  });
43
45
  }
44
46
  },
@@ -91,6 +93,7 @@ function getOrCreateInputObjectType(state, typeName) {
91
93
  return existing;
92
94
  }
93
95
  const def = {
96
+ kind: 'input',
94
97
  name: typeName,
95
98
  tags: new Set(),
96
99
  hasDefinition: false,
@@ -33,6 +33,7 @@ export function interfaceTypeBuilder() {
33
33
  keys: type.keys,
34
34
  interfaces: type.interfaces,
35
35
  implementedBy: type.implementedBy,
36
+ version: graph.version,
36
37
  });
37
38
  for (const field of type.fields.values()) {
38
39
  const fieldState = getOrCreateInterfaceField(interfaceTypeState, field.name, field.type);
@@ -66,6 +67,7 @@ export function interfaceTypeBuilder() {
66
67
  override: field.override,
67
68
  provides: field.provides,
68
69
  requires: field.requires,
70
+ version: graph.version,
69
71
  });
70
72
  for (const arg of field.args.values()) {
71
73
  const argState = getOrCreateArg(fieldState, arg.name, arg.type);
@@ -88,6 +90,7 @@ export function interfaceTypeBuilder() {
88
90
  argState.byGraph.set(graph.id, {
89
91
  type: arg.type,
90
92
  defaultValue: arg.defaultValue,
93
+ version: graph.version,
91
94
  });
92
95
  }
93
96
  }
@@ -193,6 +196,7 @@ function getOrCreateInterfaceType(state, typeName) {
193
196
  return existing;
194
197
  }
195
198
  const def = {
199
+ kind: 'interface',
196
200
  name: typeName,
197
201
  tags: new Set(),
198
202
  inaccessible: false,
@@ -59,6 +59,7 @@ export function objectTypeBuilder() {
59
59
  inaccessible: type.inaccessible,
60
60
  shareable: type.shareable,
61
61
  interfaces: type.interfaces,
62
+ version: graph.version,
62
63
  });
63
64
  const typeInGraph = objectTypeState.byGraph.get(graph.id);
64
65
  for (const field of type.fields.values()) {
@@ -116,8 +117,10 @@ export function objectTypeBuilder() {
116
117
  provided: field.provided,
117
118
  required: field.required,
118
119
  shareable: field.shareable,
120
+ extension: field.extension,
119
121
  used: field.used,
120
122
  usedAsKey,
123
+ version: graph.version,
121
124
  });
122
125
  for (const arg of field.args.values()) {
123
126
  const argState = getOrCreateArg(fieldState, arg.name, arg.type);
@@ -144,6 +147,7 @@ export function objectTypeBuilder() {
144
147
  type: arg.type,
145
148
  inaccessible: arg.inaccessible,
146
149
  defaultValue: arg.defaultValue,
150
+ version: graph.version,
147
151
  });
148
152
  }
149
153
  }
@@ -153,7 +157,7 @@ export function objectTypeBuilder() {
153
157
  const joinTypes = isQuery
154
158
  ?
155
159
  Array.from(graphs.values()).map(graph => ({
156
- graph: graph.id,
160
+ graph: graph.graph.id,
157
161
  }))
158
162
  :
159
163
  Array.from(objectType.byGraph.entries())
@@ -216,12 +220,12 @@ export function objectTypeBuilder() {
216
220
  }
217
221
  return true;
218
222
  }
219
- function createJoinFields(fieldInGraphs, field, { hasDifferentOutputType, overridesMap, }) {
223
+ function createJoinFields(fieldInGraphs, field, { hasDifferentOutputType, }) {
220
224
  return fieldInGraphs
221
225
  .map(([graphId, meta]) => {
222
226
  const type = hasDifferentOutputType ? meta.type : undefined;
223
227
  const override = meta.override ?? undefined;
224
- const usedOverridden = provideUsedOverriddenValue(field.name, meta, overridesMap, fieldNamesOfImplementedInterfaces, graphId);
228
+ const usedOverridden = provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId);
225
229
  const external = shouldSetExternalOnJoinField(meta, graphId, field);
226
230
  const provides = meta.provides ?? undefined;
227
231
  const requires = meta.requires ?? undefined;
@@ -257,7 +261,6 @@ export function objectTypeBuilder() {
257
261
  const hasDifferentOutputType = fieldInGraphs.some(([_, meta]) => meta.type !== field.type);
258
262
  const isDefinedEverywhere = field.byGraph.size === (isQuery ? graphs.size : objectType.byGraph.size);
259
263
  let joinFields = [];
260
- const overridesMap = {};
261
264
  const differencesBetweenGraphs = {
262
265
  override: false,
263
266
  type: false,
@@ -273,10 +276,6 @@ export function objectTypeBuilder() {
273
276
  }
274
277
  if (meta.override !== null) {
275
278
  differencesBetweenGraphs.override = true;
276
- const originalGraphId = graphNameToId(meta.override);
277
- if (originalGraphId) {
278
- overridesMap[originalGraphId] = graphId;
279
- }
280
279
  }
281
280
  if (meta.provides !== null) {
282
281
  differencesBetweenGraphs.provides = true;
@@ -295,7 +294,7 @@ export function objectTypeBuilder() {
295
294
  !fieldInGraph.usedAsKey &&
296
295
  !fieldInGraph.required &&
297
296
  !fieldInGraph.provided &&
298
- !provideUsedOverriddenValue(field.name, fieldInGraph, overridesMap, fieldNamesOfImplementedInterfaces, graphId) &&
297
+ !provideUsedOverriddenValue(field, fieldInGraph, fieldNamesOfImplementedInterfaces, graphId, graphNameToId) &&
299
298
  graphs.get(graphId).version === 'v1.0') {
300
299
  return null;
301
300
  }
@@ -311,7 +310,7 @@ export function objectTypeBuilder() {
311
310
  joinFields = graphsWithOverride.map(([graphId, meta]) => ({
312
311
  graph: graphId,
313
312
  override: meta.override ?? undefined,
314
- usedOverridden: provideUsedOverriddenValue(field.name, meta, overridesMap, fieldNamesOfImplementedInterfaces, graphId),
313
+ usedOverridden: provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId),
315
314
  type: differencesBetweenGraphs.type ? meta.type : undefined,
316
315
  external: meta.external ?? undefined,
317
316
  provides: meta.provides ?? undefined,
@@ -339,7 +338,7 @@ export function objectTypeBuilder() {
339
338
  const graphsToEmit = fieldInGraphs.filter(([graphId, f]) => {
340
339
  const isExternal = f.external === true;
341
340
  const isOverridden = overriddenGraphs.includes(graphId);
342
- const needsToPrintUsedOverridden = provideUsedOverriddenValue(field.name, f, overridesMap, fieldNamesOfImplementedInterfaces, graphId);
341
+ const needsToPrintUsedOverridden = provideUsedOverriddenValue(field, f, fieldNamesOfImplementedInterfaces, graphId, graphNameToId);
343
342
  const isRequired = f.required === true;
344
343
  return (isExternal && isRequired) || needsToPrintUsedOverridden || !isOverridden;
345
344
  });
@@ -349,7 +348,7 @@ export function objectTypeBuilder() {
349
348
  joinFields = graphsToEmit.map(([graphId, meta]) => ({
350
349
  graph: graphId,
351
350
  override: meta.override ?? undefined,
352
- usedOverridden: provideUsedOverriddenValue(field.name, meta, overridesMap, fieldNamesOfImplementedInterfaces, graphId),
351
+ usedOverridden: provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId),
353
352
  type: differencesBetweenGraphs.type ? meta.type : undefined,
354
353
  external: meta.external ?? undefined,
355
354
  provides: meta.provides ?? undefined,
@@ -360,7 +359,6 @@ export function objectTypeBuilder() {
360
359
  else if (hasDifferencesBetweenGraphs) {
361
360
  joinFields = createJoinFields(fieldInGraphs, field, {
362
361
  hasDifferentOutputType,
363
- overridesMap,
364
362
  });
365
363
  }
366
364
  }
@@ -372,11 +370,11 @@ export function objectTypeBuilder() {
372
370
  const graphsToPrintJoinField = fieldInGraphs.filter(([graphId, meta]) => meta.override !== null ||
373
371
  meta.external === true ||
374
372
  (meta.shareable && !overriddenGraphs.includes(graphId)) ||
375
- provideUsedOverriddenValue(field.name, meta, overridesMap, fieldNamesOfImplementedInterfaces, graphId));
373
+ provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId));
376
374
  joinFields = graphsToPrintJoinField.map(([graphId, meta]) => ({
377
375
  graph: graphId,
378
376
  override: meta.override ?? undefined,
379
- usedOverridden: provideUsedOverriddenValue(field.name, meta, overridesMap, fieldNamesOfImplementedInterfaces, graphId),
377
+ usedOverridden: provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId),
380
378
  type: differencesBetweenGraphs.type ? meta.type : undefined,
381
379
  external: meta.external ?? undefined,
382
380
  provides: meta.provides ?? undefined,
@@ -386,7 +384,6 @@ export function objectTypeBuilder() {
386
384
  else {
387
385
  joinFields = createJoinFields(fieldInGraphs, field, {
388
386
  hasDifferentOutputType,
389
- overridesMap,
390
387
  });
391
388
  }
392
389
  }
@@ -465,12 +462,12 @@ export function objectTypeBuilder() {
465
462
  },
466
463
  };
467
464
  }
468
- function provideUsedOverriddenValue(fieldName, fieldStateInGraph, overridesMap, fieldNamesOfImplementedInterfaces, graphId) {
469
- const inGraphs = fieldNamesOfImplementedInterfaces[fieldName];
465
+ function provideUsedOverriddenValue(field, fieldStateInGraph, fieldNamesOfImplementedInterfaces, graphId, graphNameToId) {
466
+ const inGraphs = fieldNamesOfImplementedInterfaces[field.name];
470
467
  const hasMatchingInterfaceFieldInGraph = inGraphs && inGraphs.has(graphId);
471
468
  const isUsedAsNonExternalKey = fieldStateInGraph.usedAsKey && !fieldStateInGraph.external;
472
- const hasOverride = typeof overridesMap[graphId] === 'string';
473
- if (hasOverride && (isUsedAsNonExternalKey || hasMatchingInterfaceFieldInGraph)) {
469
+ const isOverridden = field.override && graphNameToId(field.override) === graphId;
470
+ if (isOverridden && (isUsedAsNonExternalKey || hasMatchingInterfaceFieldInGraph)) {
474
471
  return true;
475
472
  }
476
473
  return false;
@@ -481,6 +478,7 @@ function getOrCreateObjectType(state, typeName) {
481
478
  return existing;
482
479
  }
483
480
  const def = {
481
+ kind: 'object',
484
482
  name: typeName,
485
483
  tags: new Set(),
486
484
  hasDefinition: false,