@theguild/federation-composition 0.5.0 → 0.5.1-rc-20231215091130-d3bef86

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.
package/README.md CHANGED
@@ -98,6 +98,21 @@ pnpm test
98
98
  - Look for `skipIf` or `skip` in the tests.
99
99
  - Refactor code (piece by piece) if you feel like it.
100
100
 
101
+ ### Compatibility
102
+
103
+ The lack of a publicly available specification for Apollo Federation, coupled with the non
104
+ open-source license of the Apollo Composition library, makes it difficult or even impossible to
105
+ assure complete compatibility of our open-source composition library.
106
+
107
+ Given that Apollo tools utilize their composition library, there is a potential for conflicting
108
+ results between our composition library and Apollo's. This may lead to variations in the supergraph,
109
+ differing composition errors, or, in some cases, conflicting composition outcomes.
110
+
111
+ We are working to ensure that our composition library is as compatible as possible with Apollo's and
112
+ will continue to do so as we learn more about the Federation specification.
113
+
114
+ Your feedback and bug reports are welcome and appreciated.
115
+
101
116
  ## Supergraph SDL Composition
102
117
 
103
118
  ✅ Done
@@ -1,8 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isDirectiveDefinition = void 0;
3
+ exports.moveSchemaAndDirectiveDefinitionsToTop = exports.isDirectiveDefinition = void 0;
4
4
  const graphql_1 = require("graphql");
5
5
  function isDirectiveDefinition(node) {
6
6
  return node.kind === graphql_1.Kind.DIRECTIVE_DEFINITION;
7
7
  }
8
8
  exports.isDirectiveDefinition = isDirectiveDefinition;
9
+ const kindOrderWeightMap = {
10
+ [graphql_1.Kind.SCHEMA_DEFINITION]: 0,
11
+ [graphql_1.Kind.SCHEMA_EXTENSION]: 1,
12
+ [graphql_1.Kind.DIRECTIVE_DEFINITION]: 2,
13
+ };
14
+ function moveSchemaAndDirectiveDefinitionsToTop(ast) {
15
+ return {
16
+ kind: graphql_1.Kind.DOCUMENT,
17
+ definitions: ast.definitions.slice().sort((a, b) => {
18
+ const aWeight = kindOrderWeightMap[a.kind] ?? 3;
19
+ const bWeight = kindOrderWeightMap[b.kind] ?? 3;
20
+ if (aWeight === bWeight) {
21
+ return 0;
22
+ }
23
+ return aWeight < bWeight ? -1 : 1;
24
+ }),
25
+ };
26
+ }
27
+ exports.moveSchemaAndDirectiveDefinitionsToTop = moveSchemaAndDirectiveDefinitionsToTop;
@@ -4,11 +4,20 @@ exports.objectTypeBuilder = exports.isRealExtension = void 0;
4
4
  const ast_js_1 = require("./ast.js");
5
5
  const common_js_1 = require("./common.js");
6
6
  function isRealExtension(meta, version) {
7
- return meta.extension
8
- ? meta.extensionType !== '@extends' && version === 'v1.0'
9
- ? false
10
- : true
11
- : false;
7
+ const hasExtendsDirective = meta.extensionType === '@extends';
8
+ if (meta.extension) {
9
+ if (version === 'v1.0' && !hasExtendsDirective) {
10
+ return false;
11
+ }
12
+ if (hasExtendsDirective) {
13
+ return true;
14
+ }
15
+ if (meta.hasDefinition) {
16
+ return false;
17
+ }
18
+ return true;
19
+ }
20
+ return false;
12
21
  }
13
22
  exports.isRealExtension = isRealExtension;
14
23
  function objectTypeBuilder() {
@@ -42,6 +51,7 @@ function objectTypeBuilder() {
42
51
  }
43
52
  type.interfaces.forEach(interfaceName => objectTypeState.interfaces.add(interfaceName));
44
53
  objectTypeState.byGraph.set(graph.id, {
54
+ hasDefinition: isDefinition,
45
55
  extension: type.extension,
46
56
  extensionType: type.extensionType,
47
57
  external: type.external,
@@ -50,6 +60,7 @@ function objectTypeBuilder() {
50
60
  shareable: type.shareable,
51
61
  interfaces: type.interfaces,
52
62
  });
63
+ const typeInGraph = objectTypeState.byGraph.get(graph.id);
53
64
  for (const field of type.fields.values()) {
54
65
  const fieldState = getOrCreateField(objectTypeState, field.name, field.type);
55
66
  field.tags.forEach(tag => fieldState.tags.add(tag));
@@ -58,7 +69,7 @@ function objectTypeBuilder() {
58
69
  fieldState.usedAsKey = true;
59
70
  }
60
71
  const isExternal = graph.version === 'v1.0'
61
- ? field.external && isRealExtension(type, graph.version)
72
+ ? field.external && isRealExtension(typeInGraph, graph.version)
62
73
  : field.external;
63
74
  const shouldForceType = !usedAsKey && !isExternal && !fieldState.internal.seenNonExternal;
64
75
  const shouldChangeType = shouldForceType ||
@@ -117,7 +128,7 @@ function objectTypeBuilder() {
117
128
  if (arg.inaccessible) {
118
129
  argState.inaccessible = true;
119
130
  }
120
- if (!field.external) {
131
+ if (arg.description && !argState.description) {
121
132
  argState.description = arg.description;
122
133
  }
123
134
  if (arg.deprecated && !argState.deprecated) {
package/cjs/validate.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validate = exports.validateSubgraph = void 0;
4
4
  const constant_case_1 = require("constant-case");
5
+ const helpers_js_1 = require("./graphql/helpers.js");
5
6
  const federation_js_1 = require("./specifications/federation.js");
6
7
  const state_js_1 = require("./subgraph/state.js");
7
8
  const validate_subgraph_js_1 = require("./subgraph/validation/validate-subgraph.js");
@@ -39,7 +40,7 @@ function buildGraphList(subgraphs) {
39
40
  name,
40
41
  id: proposedId + '_' + (count + 1),
41
42
  url,
42
- typeDefs,
43
+ typeDefs: (0, helpers_js_1.moveSchemaAndDirectiveDefinitionsToTop)(typeDefs),
43
44
  });
44
45
  idCounter.set(proposedId, count + 1);
45
46
  }
@@ -49,7 +50,7 @@ function buildGraphList(subgraphs) {
49
50
  name,
50
51
  id: proposedId,
51
52
  url,
52
- typeDefs,
53
+ typeDefs: (0, helpers_js_1.moveSchemaAndDirectiveDefinitionsToTop)(typeDefs),
53
54
  });
54
55
  }
55
56
  }
@@ -2,3 +2,21 @@ import { Kind } from 'graphql';
2
2
  export function isDirectiveDefinition(node) {
3
3
  return node.kind === Kind.DIRECTIVE_DEFINITION;
4
4
  }
5
+ const kindOrderWeightMap = {
6
+ [Kind.SCHEMA_DEFINITION]: 0,
7
+ [Kind.SCHEMA_EXTENSION]: 1,
8
+ [Kind.DIRECTIVE_DEFINITION]: 2,
9
+ };
10
+ export function moveSchemaAndDirectiveDefinitionsToTop(ast) {
11
+ return {
12
+ kind: Kind.DOCUMENT,
13
+ definitions: ast.definitions.slice().sort((a, b) => {
14
+ const aWeight = kindOrderWeightMap[a.kind] ?? 3;
15
+ const bWeight = kindOrderWeightMap[b.kind] ?? 3;
16
+ if (aWeight === bWeight) {
17
+ return 0;
18
+ }
19
+ return aWeight < bWeight ? -1 : 1;
20
+ }),
21
+ };
22
+ }
@@ -1,11 +1,20 @@
1
1
  import { createObjectTypeNode } from './ast.js';
2
2
  import { convertToConst } from './common.js';
3
3
  export function isRealExtension(meta, version) {
4
- return meta.extension
5
- ? meta.extensionType !== '@extends' && version === 'v1.0'
6
- ? false
7
- : true
8
- : false;
4
+ const hasExtendsDirective = meta.extensionType === '@extends';
5
+ if (meta.extension) {
6
+ if (version === 'v1.0' && !hasExtendsDirective) {
7
+ return false;
8
+ }
9
+ if (hasExtendsDirective) {
10
+ return true;
11
+ }
12
+ if (meta.hasDefinition) {
13
+ return false;
14
+ }
15
+ return true;
16
+ }
17
+ return false;
9
18
  }
10
19
  export function objectTypeBuilder() {
11
20
  return {
@@ -38,6 +47,7 @@ export function objectTypeBuilder() {
38
47
  }
39
48
  type.interfaces.forEach(interfaceName => objectTypeState.interfaces.add(interfaceName));
40
49
  objectTypeState.byGraph.set(graph.id, {
50
+ hasDefinition: isDefinition,
41
51
  extension: type.extension,
42
52
  extensionType: type.extensionType,
43
53
  external: type.external,
@@ -46,6 +56,7 @@ export function objectTypeBuilder() {
46
56
  shareable: type.shareable,
47
57
  interfaces: type.interfaces,
48
58
  });
59
+ const typeInGraph = objectTypeState.byGraph.get(graph.id);
49
60
  for (const field of type.fields.values()) {
50
61
  const fieldState = getOrCreateField(objectTypeState, field.name, field.type);
51
62
  field.tags.forEach(tag => fieldState.tags.add(tag));
@@ -54,7 +65,7 @@ export function objectTypeBuilder() {
54
65
  fieldState.usedAsKey = true;
55
66
  }
56
67
  const isExternal = graph.version === 'v1.0'
57
- ? field.external && isRealExtension(type, graph.version)
68
+ ? field.external && isRealExtension(typeInGraph, graph.version)
58
69
  : field.external;
59
70
  const shouldForceType = !usedAsKey && !isExternal && !fieldState.internal.seenNonExternal;
60
71
  const shouldChangeType = shouldForceType ||
@@ -113,7 +124,7 @@ export function objectTypeBuilder() {
113
124
  if (arg.inaccessible) {
114
125
  argState.inaccessible = true;
115
126
  }
116
- if (!field.external) {
127
+ if (arg.description && !argState.description) {
117
128
  argState.description = arg.description;
118
129
  }
119
130
  if (arg.deprecated && !argState.deprecated) {
package/esm/validate.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { constantCase } from 'constant-case';
2
+ import { moveSchemaAndDirectiveDefinitionsToTop } from './graphql/helpers.js';
2
3
  import { detectFederationVersion } from './specifications/federation.js';
3
4
  import { cleanSubgraphStateFromFederationSpec, cleanSubgraphStateFromLinkSpec, createSubgraphStateBuilder, } from './subgraph/state.js';
4
5
  import { validateSubgraph as internal_validateSubgraph, validateSubgraphCore, } from './subgraph/validation/validate-subgraph.js';
@@ -36,7 +37,7 @@ function buildGraphList(subgraphs) {
36
37
  name,
37
38
  id: proposedId + '_' + (count + 1),
38
39
  url,
39
- typeDefs,
40
+ typeDefs: moveSchemaAndDirectiveDefinitionsToTop(typeDefs),
40
41
  });
41
42
  idCounter.set(proposedId, count + 1);
42
43
  }
@@ -46,7 +47,7 @@ function buildGraphList(subgraphs) {
46
47
  name,
47
48
  id: proposedId,
48
49
  url,
49
- typeDefs,
50
+ typeDefs: moveSchemaAndDirectiveDefinitionsToTop(typeDefs),
50
51
  });
51
52
  }
52
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theguild/federation-composition",
3
- "version": "0.5.0",
3
+ "version": "0.5.1-rc-20231215091130-d3bef86",
4
4
  "description": "Open Source Composition library for Apollo Federation",
5
5
  "peerDependencies": {
6
6
  "graphql": "^16.0.0"
@@ -1,3 +1,4 @@
1
- import { type DefinitionNode, type DirectiveDefinitionNode } from 'graphql';
1
+ import { DocumentNode, type DefinitionNode, type DirectiveDefinitionNode } from 'graphql';
2
2
  export declare function isDirectiveDefinition(node: DefinitionNode): node is DirectiveDefinitionNode;
3
+ export declare function moveSchemaAndDirectiveDefinitionsToTop(ast: DocumentNode): DocumentNode;
3
4
  //# sourceMappingURL=helpers.d.ts.map
@@ -1,3 +1,4 @@
1
- import { type DefinitionNode, type DirectiveDefinitionNode } from 'graphql';
1
+ import { DocumentNode, type DefinitionNode, type DirectiveDefinitionNode } from 'graphql';
2
2
  export declare function isDirectiveDefinition(node: DefinitionNode): node is DirectiveDefinitionNode;
3
+ export declare function moveSchemaAndDirectiveDefinitionsToTop(ast: DocumentNode): DocumentNode;
3
4
  //# sourceMappingURL=helpers.d.ts.map
@@ -55,6 +55,7 @@ export type ObjectTypeFieldArgState = {
55
55
  };
56
56
  };
57
57
  export type ObjectTypeStateInGraph = {
58
+ hasDefinition: boolean;
58
59
  extension: boolean;
59
60
  extensionType?: '@extends' | 'extend';
60
61
  external: boolean;
@@ -55,6 +55,7 @@ export type ObjectTypeFieldArgState = {
55
55
  };
56
56
  };
57
57
  export type ObjectTypeStateInGraph = {
58
+ hasDefinition: boolean;
58
59
  extension: boolean;
59
60
  extensionType?: '@extends' | 'extend';
60
61
  external: boolean;