@theguild/federation-composition 0.6.2 → 0.7.0-alpha-20240104152841-c814a1f

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 (40) hide show
  1. package/README.md +1 -0
  2. package/cjs/graphql/sort-sdl.js +1 -0
  3. package/cjs/subgraph/helpers.js +14 -6
  4. package/cjs/subgraph/validation/rules/elements/key.js +31 -11
  5. package/cjs/subgraph/validation/rules/elements/provides.js +5 -5
  6. package/cjs/subgraph/validation/rules/elements/requires.js +3 -1
  7. package/cjs/subgraph/validation/rules/known-directives-rule.js +3 -1
  8. package/cjs/subgraph/validation/rules/provided-arguments-on-directives-rule.js +6 -0
  9. package/cjs/subgraph/validation/validate-state.js +24 -1
  10. package/cjs/subgraph/validation/validate-subgraph.js +44 -28
  11. package/cjs/supergraph/composition/ast.js +24 -1
  12. package/cjs/supergraph/composition/object-type.js +150 -34
  13. package/cjs/supergraph/state.js +1 -0
  14. package/cjs/supergraph/validation/rules/enum-values-rule.js +3 -1
  15. package/cjs/supergraph/validation/rules/fields-of-the-same-type-rule.js +39 -16
  16. package/cjs/supergraph/validation/rules/satisfiablity-rule.js +318 -5
  17. package/esm/graphql/sort-sdl.js +1 -0
  18. package/esm/subgraph/helpers.js +14 -6
  19. package/esm/subgraph/validation/rules/elements/key.js +32 -12
  20. package/esm/subgraph/validation/rules/elements/provides.js +5 -5
  21. package/esm/subgraph/validation/rules/elements/requires.js +3 -1
  22. package/esm/subgraph/validation/rules/known-directives-rule.js +3 -1
  23. package/esm/subgraph/validation/rules/provided-arguments-on-directives-rule.js +6 -0
  24. package/esm/subgraph/validation/validate-state.js +22 -0
  25. package/esm/subgraph/validation/validate-subgraph.js +45 -29
  26. package/esm/supergraph/composition/ast.js +24 -1
  27. package/esm/supergraph/composition/object-type.js +150 -34
  28. package/esm/supergraph/state.js +1 -0
  29. package/esm/supergraph/validation/rules/enum-values-rule.js +3 -1
  30. package/esm/supergraph/validation/rules/fields-of-the-same-type-rule.js +39 -16
  31. package/esm/supergraph/validation/rules/satisfiablity-rule.js +318 -5
  32. package/package.json +1 -1
  33. package/typings/subgraph/validation/validate-state.d.cts +2 -1
  34. package/typings/subgraph/validation/validate-state.d.ts +2 -1
  35. package/typings/supergraph/composition/ast.d.cts +1 -0
  36. package/typings/supergraph/composition/ast.d.ts +1 -0
  37. package/typings/supergraph/composition/common.d.cts +2 -0
  38. package/typings/supergraph/composition/common.d.ts +2 -0
  39. package/typings/supergraph/composition/object-type.d.cts +2 -0
  40. package/typings/supergraph/composition/object-type.d.ts +2 -0
package/README.md CHANGED
@@ -196,6 +196,7 @@ Your feedback and bug reports are welcome and appreciated.
196
196
  - [ ] `SHAREABLE_HAS_MISMATCHED_RUNTIME_TYPES`
197
197
  - [ ] `UNSUPPORTED_FEATURE`
198
198
  - [ ] `UNSUPPORTED_LINKED_FEATURE`
199
+ - [ ] `TYPE_WITH_ONLY_UNUSED_EXTERNAL`
199
200
  - [ ] `SATISFIABILITY_ERROR` - deeply nested key fields
200
201
  - [ ] `SATISFIABILITY_ERROR` - fragments in keys
201
202
  - [ ] `SATISFIABILITY_ERROR` - support interfaces... (kill me)
@@ -33,6 +33,7 @@ function sortSDL(doc) {
33
33
  ...node,
34
34
  directives: sortNodes(node.directives),
35
35
  fields: sortNodes(node.fields),
36
+ interfaces: sortNodes(node.interfaces),
36
37
  };
37
38
  },
38
39
  InterfaceTypeDefinition(node) {
@@ -138,7 +138,8 @@ function visitFields({ context, selectionSet, typeDefinition, interceptField, in
138
138
  }
139
139
  break;
140
140
  }
141
- if (interceptDirective && selection.directives?.length) {
141
+ const isTypename = selection.name.value === '__typename';
142
+ if (!isTypename && interceptDirective && selection.directives?.length) {
142
143
  for (const directive of selection.directives) {
143
144
  interceptDirective({
144
145
  directiveName: directive.name.value,
@@ -146,21 +147,23 @@ function visitFields({ context, selectionSet, typeDefinition, interceptField, in
146
147
  });
147
148
  }
148
149
  }
149
- context.markAsUsed('fields', typeDefinition.kind, typeDefinition.name.value, selectionFieldDef.name.value);
150
- if (interceptField) {
150
+ if (!isTypename) {
151
+ context.markAsUsed('fields', typeDefinition.kind, typeDefinition.name.value, selectionFieldDef.name.value);
152
+ }
153
+ if (!isTypename && interceptField) {
151
154
  interceptField({
152
155
  typeDefinition,
153
156
  fieldName: selection.name.value,
154
157
  });
155
158
  }
156
- if (selectionFieldDef.arguments?.length && interceptArguments) {
159
+ if (!isTypename && selectionFieldDef.arguments?.length && interceptArguments) {
157
160
  interceptArguments({
158
161
  typeDefinition,
159
162
  fieldName: selection.name.value,
160
163
  });
161
164
  continue;
162
165
  }
163
- if (interceptNonExternalField || interceptExternalField) {
166
+ if (!isTypename && (interceptNonExternalField || interceptExternalField)) {
164
167
  const isExternal = selectionFieldDef.directives?.some(d => context.isAvailableFederationDirective('external', d));
165
168
  const fieldName = selection.name.value;
166
169
  const fieldDef = typeDefinition.fields?.find(field => field.name.value === fieldName);
@@ -189,7 +192,8 @@ function visitFields({ context, selectionSet, typeDefinition, interceptField, in
189
192
  if (!innerTypeDef) {
190
193
  continue;
191
194
  }
192
- if (interceptInterfaceType &&
195
+ if (!isTypename &&
196
+ interceptInterfaceType &&
193
197
  (innerTypeDef.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION ||
194
198
  innerTypeDef.kind === graphql_1.Kind.INTERFACE_TYPE_EXTENSION)) {
195
199
  interceptInterfaceType({
@@ -212,9 +216,13 @@ function visitFields({ context, selectionSet, typeDefinition, interceptField, in
212
216
  context,
213
217
  selectionSet: innerSelection,
214
218
  typeDefinition: innerTypeDef,
219
+ interceptField,
215
220
  interceptArguments,
216
221
  interceptUnknownField,
217
222
  interceptInterfaceType,
223
+ interceptDirective,
224
+ interceptExternalField,
225
+ interceptFieldWithMissingSelectionSet,
218
226
  });
219
227
  }
220
228
  }
@@ -39,17 +39,37 @@ function KeyRules(context) {
39
39
  }
40
40
  const printedFieldsValue = (0, printer_js_1.print)(fieldsArg.value);
41
41
  if (fieldsArg.value.kind !== graphql_1.Kind.STRING) {
42
- context.reportError(new graphql_1.GraphQLError(`On type "${typeCoordinate}", for @key(fields: ${printedFieldsValue}): Invalid value for argument "fields": must be a string.`, {
43
- nodes: directiveNode,
44
- extensions: {
45
- code: 'KEY_INVALID_FIELDS_TYPE',
46
- },
47
- }));
48
- return;
42
+ const isListWithStrings = fieldsArg.value.kind === graphql_1.Kind.LIST &&
43
+ fieldsArg.value.values.every(value => value.kind === graphql_1.Kind.STRING);
44
+ if (context.satisfiesVersionRange('> v1.0') || !isListWithStrings) {
45
+ context.reportError(new graphql_1.GraphQLError(`On type "${typeCoordinate}", for @key(fields: ${printedFieldsValue}): Invalid value for argument "fields": must be a string.`, {
46
+ nodes: directiveNode,
47
+ extensions: {
48
+ code: 'KEY_INVALID_FIELDS_TYPE',
49
+ },
50
+ }));
51
+ return;
52
+ }
49
53
  }
50
54
  let selectionSet;
55
+ let normalizedFieldsArgValue = fieldsArg.value.kind === graphql_1.Kind.STRING
56
+ ? fieldsArg.value
57
+ : ({
58
+ kind: graphql_1.Kind.STRING,
59
+ value: fieldsArg.value.values
60
+ .map(v => {
61
+ if (v.kind !== graphql_1.Kind.STRING) {
62
+ throw new Error('Expected fields argument value to be a string');
63
+ }
64
+ return v.value;
65
+ })
66
+ .join(' '),
67
+ });
68
+ if (normalizedFieldsArgValue.kind !== graphql_1.Kind.STRING) {
69
+ throw new Error('Expected fields argument value to be a string');
70
+ }
51
71
  try {
52
- selectionSet = (0, helpers_js_1.parseFields)(fieldsArg.value.value);
72
+ selectionSet = (0, helpers_js_1.parseFields)(normalizedFieldsArgValue.value);
53
73
  }
54
74
  catch (error) {
55
75
  if (error instanceof graphql_1.GraphQLError) {
@@ -112,7 +132,7 @@ function KeyRules(context) {
112
132
  },
113
133
  });
114
134
  if (usedOnInterface) {
115
- const expectedFieldsValue = fieldsArg.value.value;
135
+ const expectedFieldsValue = normalizedFieldsArgValue.value;
116
136
  knownObjectsAndInterfaces.forEach(def => {
117
137
  if (def.interfaces?.some(i => i.name.value === typeDef.name.value)) {
118
138
  let shouldError = true;
@@ -141,10 +161,10 @@ function KeyRules(context) {
141
161
  const resolvableArgValue = directiveNode.arguments?.find(arg => arg.name.value === 'resolvable' && arg.value.kind === graphql_1.Kind.BOOLEAN)?.value;
142
162
  const resolvable = resolvableArgValue?.value ?? true;
143
163
  if (usedOnInterface) {
144
- context.stateBuilder.interfaceType.setKey(typeDef.name.value, fieldsArg.value.value, fieldsUsedInKey, resolvable);
164
+ context.stateBuilder.interfaceType.setKey(typeDef.name.value, normalizedFieldsArgValue.value, fieldsUsedInKey, resolvable);
145
165
  return;
146
166
  }
147
- context.stateBuilder.objectType.setKey(typeDef.name.value, fieldsArg.value.value, fieldsUsedInKey, resolvable);
167
+ context.stateBuilder.objectType.setKey(typeDef.name.value, normalizedFieldsArgValue.value, fieldsUsedInKey, resolvable);
148
168
  }
149
169
  },
150
170
  };
@@ -114,9 +114,6 @@ function ProvidesRules(context) {
114
114
  }));
115
115
  },
116
116
  interceptExternalField(info) {
117
- if (context.satisfiesVersionRange('< v2.0')) {
118
- return;
119
- }
120
117
  const keyDirectives = info.typeDefinition.directives?.filter(directive => context.isAvailableFederationDirective('key', directive));
121
118
  if (!keyDirectives?.length) {
122
119
  return;
@@ -157,14 +154,17 @@ function ProvidesRules(context) {
157
154
  }
158
155
  if (info.typeDefinition.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
159
156
  info.typeDefinition.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
160
- context.stateBuilder.objectType.field.markAsProvided(info.typeDefinition.name.value, info.fieldName);
157
+ if (info.fieldName !== '__typename') {
158
+ context.stateBuilder.objectType.field.markAsProvided(info.typeDefinition.name.value, info.fieldName);
159
+ }
161
160
  }
162
161
  },
163
162
  });
164
163
  }
165
164
  }
166
165
  }
167
- if (interceptedFieldIsPrimaryKeyFromExtension) {
166
+ if (context.satisfiesVersionRange('>= v2.0') &&
167
+ interceptedFieldIsPrimaryKeyFromExtension) {
168
168
  isValid = false;
169
169
  context.reportError(new graphql_1.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 "effectively" provided by this subgraph (while it is marked @external, it is a @key field of an extension type, which are not internally considered external for historical/backward compatibility reasons)`, {
170
170
  extensions: {
@@ -81,7 +81,9 @@ function RequiresRules(context) {
81
81
  interceptField(info) {
82
82
  if (info.typeDefinition.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
83
83
  info.typeDefinition.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) {
84
- context.stateBuilder.objectType.field.markedAsRequired(info.typeDefinition.name.value, info.fieldName);
84
+ if (info.fieldName !== '__typename') {
85
+ context.stateBuilder.objectType.field.markedAsRequired(info.typeDefinition.name.value, info.fieldName);
86
+ }
85
87
  }
86
88
  },
87
89
  interceptUnknownField(info) {
@@ -11,7 +11,9 @@ function KnownDirectivesRule(context) {
11
11
  }
12
12
  }
13
13
  for (const specifiedDirective of graphql_1.specifiedDirectives) {
14
- locationsMap.set(specifiedDirective.name, new Set(specifiedDirective.locations.map(loc => String(loc))));
14
+ if (!locationsMap.has(specifiedDirective.name)) {
15
+ locationsMap.set(specifiedDirective.name, new Set(specifiedDirective.locations.map(loc => String(loc))));
16
+ }
15
17
  }
16
18
  return {
17
19
  Directive(node, _key, _parent, _path, ancestors) {
@@ -28,6 +28,12 @@ function ProvidedArgumentsOnDirectivesRule(context) {
28
28
  continue;
29
29
  }
30
30
  if (printedType !== 'Any' && printedType !== printedValue) {
31
+ if (printedValue === '[]') {
32
+ if (argDefinition.type.kind === graphql_1.Kind.LIST_TYPE &&
33
+ argDefinition.type.type.kind !== graphql_1.Kind.NON_NULL_TYPE) {
34
+ continue;
35
+ }
36
+ }
31
37
  const namedType = (0, helpers_js_1.namedTypeFromTypeNode)(argDefinition.type);
32
38
  const typeName = namedType.name.value;
33
39
  if (printedValue === 'Int' && typeName === 'Float') {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isUnionType = exports.isInterfaceType = exports.isObjectType = exports.isEnumType = exports.isScalarType = exports.isInputObjectType = exports.typeExists = exports.isInputType = exports.isTypeSubTypeOf = exports.validateSubgraphState = void 0;
3
+ exports.isDirective = exports.isUnionType = exports.isInterfaceType = exports.isObjectType = exports.isEnumType = exports.isScalarType = exports.isInputObjectType = exports.typeExists = exports.isInputType = exports.isTypeSubTypeOf = exports.validateSubgraphState = void 0;
4
4
  const graphql_1 = require("graphql");
5
5
  const format_js_1 = require("../../utils/format.js");
6
6
  const state_js_1 = require("../../utils/state.js");
@@ -16,6 +16,7 @@ function validateSubgraphState(state) {
16
16
  }));
17
17
  }
18
18
  validateRootTypes(state, reportError);
19
+ validateDirectives(state, reportError);
19
20
  validateTypes(state, reportError);
20
21
  return errors;
21
22
  }
@@ -57,6 +58,24 @@ function validateRootTypes(state, reportError) {
57
58
  function capitalize(str) {
58
59
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
59
60
  }
61
+ function validateDirectives(state, reportError) {
62
+ for (const directive of state.types.values()) {
63
+ if (isDirective(directive)) {
64
+ validateName(reportError, directive.name);
65
+ for (const [argName, arg] of directive.args) {
66
+ validateName(reportError, argName);
67
+ const argInputTypeName = (0, state_js_1.stripTypeModifiers)(arg.type);
68
+ if (!isInputType(state, argInputTypeName)) {
69
+ reportError(`The type of @${directive.name}(${arg.name}:) must be Input Type ` +
70
+ `but got: ${arg.type}.`);
71
+ }
72
+ if (isRequiredArgument(arg) && arg.deprecated?.deprecated === true) {
73
+ reportError(`Required argument @${directive.name}(${arg.name}:) cannot be deprecated.`);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ }
60
79
  function validateTypes(state, reportError) {
61
80
  const validateInputObjectCircularRefs = createInputObjectCircularRefsValidator(state, reportError);
62
81
  const implementationsMap = new Map();
@@ -474,3 +493,7 @@ function isUnionType(type) {
474
493
  return type.kind === state_js_2.TypeKind.UNION;
475
494
  }
476
495
  exports.isUnionType = isUnionType;
496
+ function isDirective(type) {
497
+ return type.kind === state_js_2.TypeKind.DIRECTIVE;
498
+ }
499
+ exports.isDirective = isDirective;
@@ -62,6 +62,33 @@ function validateSubgraphCore(subgraph) {
62
62
  exports.validateSubgraphCore = validateSubgraphCore;
63
63
  function validateSubgraph(subgraph, stateBuilder, federation, __internal) {
64
64
  subgraph.typeDefs = cleanSubgraphTypeDefsFromSubgraphSpec(subgraph.typeDefs);
65
+ const linkSpecDefinitions = (0, graphql_1.parse)(`
66
+ enum Purpose {
67
+ EXECUTION
68
+ SECURITY
69
+ }
70
+
71
+ directive @link(
72
+ url: String
73
+ as: String
74
+ for: link__Purpose
75
+ import: [link__Import]
76
+ ) repeatable on SCHEMA
77
+
78
+ scalar link__Import
79
+
80
+ enum link__Purpose {
81
+ """
82
+ \`SECURITY\` features provide metadata necessary to securely resolve fields.
83
+ """
84
+ SECURITY
85
+
86
+ """
87
+ \`EXECUTION\` features provide metadata necessary for operation execution.
88
+ """
89
+ EXECUTION
90
+ }
91
+ `).definitions;
65
92
  const rulesToSkip = __internal?.disableValidationRules ?? [];
66
93
  const typeNodeInfo = new type_node_info_js_1.TypeNodeInfo();
67
94
  const validationContext = (0, validation_context_js_1.createSubgraphValidationContext)(subgraph, federation, typeNodeInfo, stateBuilder);
@@ -111,6 +138,12 @@ function validateSubgraph(subgraph, stateBuilder, federation, __internal) {
111
138
  return rule(validationContext);
112
139
  })))));
113
140
  const federationDefinitionReplacements = validationContext.collectFederationDefinitionReplacements();
141
+ const linkSpecDefinitionsToInclude = linkSpecDefinitions.filter(def => {
142
+ if ('name' in def && typeof def.name?.value === 'string') {
143
+ return !stateBuilder.state.types.has(def.name.value);
144
+ }
145
+ return true;
146
+ });
114
147
  const fullTypeDefs = (0, graphql_1.concatAST)([
115
148
  {
116
149
  kind: graphql_1.Kind.DOCUMENT,
@@ -120,33 +153,12 @@ function validateSubgraph(subgraph, stateBuilder, federation, __internal) {
120
153
  },
121
154
  validationContext.satisfiesVersionRange('> v1.0') && !stateBuilder.state.specs.link
122
155
  ?
123
- (0, graphql_1.parse)(`
124
- enum Purpose {
125
- EXECUTION
126
- SECURITY
127
- }
128
-
129
- directive @link(
130
- url: String
131
- as: String
132
- for: link__Purpose
133
- import: [link__Import]
134
- ) repeatable on SCHEMA
135
-
136
- scalar link__Import
137
-
138
- enum link__Purpose {
139
- """
140
- \`SECURITY\` features provide metadata necessary to securely resolve fields.
141
- """
142
- SECURITY
143
-
144
- """
145
- \`EXECUTION\` features provide metadata necessary for operation execution.
146
- """
147
- EXECUTION
148
- }
149
- `)
156
+ linkSpecDefinitionsToInclude.length > 0
157
+ ? {
158
+ kind: graphql_1.Kind.DOCUMENT,
159
+ definitions: linkSpecDefinitionsToInclude,
160
+ }
161
+ : null
150
162
  : null,
151
163
  subgraph.typeDefs,
152
164
  ].filter(onlyDocumentNode));
@@ -305,6 +317,10 @@ function onlyDocumentNode(item) {
305
317
  }
306
318
  function cleanSubgraphTypeDefsFromSubgraphSpec(typeDefs) {
307
319
  let queryTypes = [];
320
+ const schemaDef = typeDefs.definitions.find(node => (node.kind === graphql_1.Kind.SCHEMA_DEFINITION || node.kind === graphql_1.Kind.SCHEMA_EXTENSION) &&
321
+ node.operationTypes?.some(op => op.operation === graphql_1.OperationTypeNode.QUERY));
322
+ const queryTypeName = schemaDef?.operationTypes?.find(op => op.operation === graphql_1.OperationTypeNode.QUERY)?.type.name
323
+ .value ?? 'Query';
308
324
  typeDefs.definitions = typeDefs.definitions.filter(def => {
309
325
  if (def.kind === graphql_1.Kind.SCALAR_TYPE_DEFINITION && def.name.value === '_Any') {
310
326
  return false;
@@ -316,7 +332,7 @@ function cleanSubgraphTypeDefsFromSubgraphSpec(typeDefs) {
316
332
  return false;
317
333
  }
318
334
  if ((def.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION || def.kind === graphql_1.Kind.OBJECT_TYPE_EXTENSION) &&
319
- def.name.value === 'Query') {
335
+ def.name.value === queryTypeName) {
320
336
  queryTypes.push(def);
321
337
  }
322
338
  return true;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.stripFederation = exports.schemaCoordinate = exports.createJoinGraphEnumTypeNode = exports.createScalarTypeNode = exports.createEnumTypeNode = exports.createUnionTypeNode = exports.createInputObjectTypeNode = exports.createInterfaceTypeNode = exports.createObjectTypeNode = exports.createDirectiveNode = exports.createSchemaNode = void 0;
4
4
  const graphql_1 = require("graphql");
5
+ const printer_js_1 = require("../../graphql/printer.js");
5
6
  function createSchemaNode(schema) {
6
7
  return {
7
8
  kind: graphql_1.Kind.SCHEMA_DEFINITION,
@@ -353,6 +354,19 @@ function createJoinFieldDirectiveNode(join) {
353
354
  },
354
355
  }
355
356
  : null,
357
+ join.usedOverridden
358
+ ? {
359
+ kind: graphql_1.Kind.ARGUMENT,
360
+ name: {
361
+ kind: graphql_1.Kind.NAME,
362
+ value: 'usedOverridden',
363
+ },
364
+ value: {
365
+ kind: graphql_1.Kind.BOOLEAN,
366
+ value: true,
367
+ },
368
+ }
369
+ : null,
356
370
  join.external
357
371
  ? {
358
372
  kind: graphql_1.Kind.ARGUMENT,
@@ -727,7 +741,16 @@ function createNamedTypeNode(name) {
727
741
  };
728
742
  }
729
743
  function applyDirectives(common) {
730
- return [].concat(common.ast?.directives ?? [], common.join?.type?.map(createJoinTypeDirectiveNode) ?? [], common.join?.implements?.map(createJoinImplementsDirectiveNode) ?? [], common.join?.field?.map(createJoinFieldDirectiveNode) ?? [], common.join?.unionMember?.map(createJoinUnionMemberDirectiveNode) ?? [], common.join?.enumValue?.map(createJoinEnumValueDirectiveNode) ?? [], common.tags?.map(createTagDirectiveNode) ?? [], common.inaccessible ? [createInaccessibleDirectiveNode()] : [], common.authenticated ? [createAuthenticatedDirectiveNode()] : [], common.policies?.length ? [createPolicyDirectiveNode(common.policies)] : [], common.scopes?.length ? [createRequiresScopesDirectiveNode(common.scopes)] : [], common.deprecated ? [createDeprecatedDirectiveNode(common.deprecated)] : [], common.specifiedBy ? [createSpecifiedByDirectiveNode(common.specifiedBy)] : []);
744
+ const deduplicatedDirectives = (common.ast?.directives ?? [])
745
+ .map(directive => {
746
+ return {
747
+ ast: directive,
748
+ string: (0, printer_js_1.print)(directive),
749
+ };
750
+ })
751
+ .filter((directive, index, all) => all.findIndex(d => d.string === directive.string) === index)
752
+ .map(d => d.ast);
753
+ return [].concat(deduplicatedDirectives, common.join?.type?.map(createJoinTypeDirectiveNode) ?? [], common.join?.implements?.map(createJoinImplementsDirectiveNode) ?? [], common.join?.field?.map(createJoinFieldDirectiveNode) ?? [], common.join?.unionMember?.map(createJoinUnionMemberDirectiveNode) ?? [], common.join?.enumValue?.map(createJoinEnumValueDirectiveNode) ?? [], common.tags?.map(createTagDirectiveNode) ?? [], common.inaccessible ? [createInaccessibleDirectiveNode()] : [], common.authenticated ? [createAuthenticatedDirectiveNode()] : [], common.policies?.length ? [createPolicyDirectiveNode(common.policies)] : [], common.scopes?.length ? [createRequiresScopesDirectiveNode(common.scopes)] : [], common.deprecated ? [createDeprecatedDirectiveNode(common.deprecated)] : [], common.specifiedBy ? [createSpecifiedByDirectiveNode(common.specifiedBy)] : []);
731
754
  }
732
755
  function nonEmpty(value) {
733
756
  return value != null;