@graphql-tools/stitching-directives 3.1.13 → 3.1.14-alpha-c1f969d9556b274c8e1313a3d2a4742f231bc03a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. package/CHANGELOG.md +929 -0
  2. package/dist/index.cjs +1196 -0
  3. package/dist/index.d.cts +58 -0
  4. package/dist/index.d.ts +58 -0
  5. package/dist/index.js +1193 -0
  6. package/package.json +32 -42
  7. package/cjs/defaultStitchingDirectiveOptions.js +0 -10
  8. package/cjs/extractVariables.js +0 -52
  9. package/cjs/federationToStitchingSDL.js +0 -116
  10. package/cjs/getSourcePaths.js +0 -25
  11. package/cjs/index.js +0 -6
  12. package/cjs/package.json +0 -1
  13. package/cjs/parseMergeArgsExpr.js +0 -77
  14. package/cjs/pathsFromSelectionSet.js +0 -29
  15. package/cjs/preparseMergeArgsExpr.js +0 -31
  16. package/cjs/properties.js +0 -70
  17. package/cjs/stitchingDirectives.js +0 -78
  18. package/cjs/stitchingDirectivesTransformer.js +0 -490
  19. package/cjs/stitchingDirectivesValidator.js +0 -119
  20. package/cjs/types.js +0 -0
  21. package/esm/defaultStitchingDirectiveOptions.js +0 -7
  22. package/esm/extractVariables.js +0 -48
  23. package/esm/federationToStitchingSDL.js +0 -112
  24. package/esm/getSourcePaths.js +0 -21
  25. package/esm/index.js +0 -3
  26. package/esm/parseMergeArgsExpr.js +0 -73
  27. package/esm/pathsFromSelectionSet.js +0 -25
  28. package/esm/preparseMergeArgsExpr.js +0 -27
  29. package/esm/properties.js +0 -63
  30. package/esm/stitchingDirectives.js +0 -74
  31. package/esm/stitchingDirectivesTransformer.js +0 -486
  32. package/esm/stitchingDirectivesValidator.js +0 -115
  33. package/esm/types.js +0 -0
  34. package/typings/defaultStitchingDirectiveOptions.d.cts +0 -2
  35. package/typings/defaultStitchingDirectiveOptions.d.ts +0 -2
  36. package/typings/extractVariables.d.cts +0 -7
  37. package/typings/extractVariables.d.ts +0 -7
  38. package/typings/federationToStitchingSDL.d.cts +0 -2
  39. package/typings/federationToStitchingSDL.d.ts +0 -2
  40. package/typings/getSourcePaths.d.cts +0 -3
  41. package/typings/getSourcePaths.d.ts +0 -3
  42. package/typings/index.d.cts +0 -3
  43. package/typings/index.d.ts +0 -3
  44. package/typings/parseMergeArgsExpr.d.cts +0 -3
  45. package/typings/parseMergeArgsExpr.d.ts +0 -3
  46. package/typings/pathsFromSelectionSet.d.cts +0 -2
  47. package/typings/pathsFromSelectionSet.d.ts +0 -2
  48. package/typings/preparseMergeArgsExpr.d.cts +0 -6
  49. package/typings/preparseMergeArgsExpr.d.ts +0 -6
  50. package/typings/properties.d.cts +0 -5
  51. package/typings/properties.d.ts +0 -5
  52. package/typings/stitchingDirectives.d.cts +0 -19
  53. package/typings/stitchingDirectives.d.ts +0 -19
  54. package/typings/stitchingDirectivesTransformer.d.cts +0 -3
  55. package/typings/stitchingDirectivesTransformer.d.ts +0 -3
  56. package/typings/stitchingDirectivesValidator.d.cts +0 -3
  57. package/typings/stitchingDirectivesValidator.d.ts +0 -3
  58. package/typings/types.d.cts +0 -35
  59. package/typings/types.d.ts +0 -35
@@ -1,112 +0,0 @@
1
- // Taken from https://github.com/gmac/federation-to-stitching-sdl/blob/main/index.js
2
- import { Kind, parse, print, } from 'graphql';
3
- import { stitchingDirectives } from './stitchingDirectives.js';
4
- const extensionKind = /Extension$/;
5
- const entityKinds = [
6
- Kind.OBJECT_TYPE_DEFINITION,
7
- Kind.OBJECT_TYPE_EXTENSION,
8
- Kind.INTERFACE_TYPE_DEFINITION,
9
- Kind.INTERFACE_TYPE_EXTENSION,
10
- ];
11
- function isEntityKind(def) {
12
- return entityKinds.includes(def.kind);
13
- }
14
- function getQueryTypeDef(definitions) {
15
- const schemaDef = definitions.find(def => def.kind === Kind.SCHEMA_DEFINITION);
16
- const typeName = schemaDef
17
- ? schemaDef.operationTypes.find(({ operation }) => operation === 'query')?.type.name.value
18
- : 'Query';
19
- return definitions.find(def => def.kind === Kind.OBJECT_TYPE_DEFINITION && def.name.value === typeName);
20
- }
21
- // Federation services are actually fairly complex,
22
- // as the `buildFederatedSchema` helper does a fair amount
23
- // of hidden work to setup the Federation schema specification:
24
- // https://www.apollographql.com/docs/federation/federation-spec/#federation-schema-specification
25
- export function federationToStitchingSDL(federationSDL, stitchingConfig = stitchingDirectives()) {
26
- const doc = parse(federationSDL);
27
- const entityTypes = [];
28
- const baseTypeNames = doc.definitions.reduce((memo, typeDef) => {
29
- if (!extensionKind.test(typeDef.kind) && 'name' in typeDef && typeDef.name) {
30
- memo[typeDef.name.value] = true;
31
- }
32
- return memo;
33
- }, {});
34
- doc.definitions.forEach(typeDef => {
35
- // Un-extend all types (remove "extends" keywords)...
36
- // extended types are invalid GraphQL without a local base type to extend from.
37
- // Stitching merges flat types in lieu of hierarchical extensions.
38
- if (extensionKind.test(typeDef.kind) &&
39
- 'name' in typeDef &&
40
- typeDef.name &&
41
- !baseTypeNames[typeDef.name.value]) {
42
- typeDef.kind = typeDef.kind.replace(extensionKind, 'Definition');
43
- }
44
- if (!isEntityKind(typeDef))
45
- return;
46
- // Find object definitions with "@key" directives;
47
- // these are federated entities that get turned into merged types.
48
- const keyDirs = [];
49
- const otherDirs = [];
50
- typeDef.directives?.forEach(dir => {
51
- if (dir.name.value === 'key') {
52
- keyDirs.push(dir);
53
- }
54
- else {
55
- otherDirs.push(dir);
56
- }
57
- });
58
- if (!keyDirs.length)
59
- return;
60
- // Setup stitching MergedTypeConfig for all federated entities:
61
- const selectionSet = `{ ${keyDirs.map((dir) => dir.arguments[0].value.value).join(' ')} }`;
62
- const keyFields = parse(selectionSet).definitions[0].selectionSet.selections.map((sel) => sel.name.value);
63
- const keyDir = keyDirs[0];
64
- keyDir.name.value = stitchingConfig.keyDirective.name;
65
- keyDir.arguments[0].name.value = 'selectionSet';
66
- keyDir.arguments[0].value.value = selectionSet;
67
- typeDef.directives = [keyDir, ...otherDirs];
68
- // Remove non-key "@external" fields from the type...
69
- // the stitching query planner expects services to only publish their own fields.
70
- // This makes "@provides" moot because the query planner can automate the logic.
71
- typeDef.fields = typeDef.fields?.filter(fieldDef => {
72
- return (keyFields.includes(fieldDef.name.value) ||
73
- !fieldDef.directives?.find(dir => dir.name.value === 'external'));
74
- });
75
- // Discard remaining "@external" directives and any "@provides" directives
76
- typeDef.fields?.forEach((fieldDef) => {
77
- fieldDef.directives = fieldDef.directives.filter((dir) => !/^(external|provides)$/.test(dir.name.value));
78
- fieldDef.directives.forEach((dir) => {
79
- if (dir.name.value === 'requires') {
80
- dir.name.value = stitchingConfig.computedDirective.name;
81
- dir.arguments[0].name.value = 'selectionSet';
82
- dir.arguments[0].value.value = `{ ${dir.arguments[0].value.value} }`;
83
- }
84
- });
85
- });
86
- if (typeDef.kind === Kind.OBJECT_TYPE_DEFINITION ||
87
- typeDef.kind === Kind.OBJECT_TYPE_EXTENSION) {
88
- entityTypes.push(typeDef.name.value);
89
- }
90
- });
91
- // Federation service SDLs are incomplete because they omit the federation spec itself...
92
- // (https://www.apollographql.com/docs/federation/federation-spec/#federation-schema-specification)
93
- // To make federation SDLs into valid and parsable GraphQL schemas,
94
- // we must fill in the missing details from the specification.
95
- if (entityTypes.length) {
96
- const queryDef = getQueryTypeDef(doc.definitions);
97
- const entitiesSchema = parse(/* GraphQL */ `
98
- scalar _Any
99
- union _Entity = ${entityTypes.filter((v, i, a) => a.indexOf(v) === i).join(' | ')}
100
- type Query { _entities(representations: [_Any!]!): [_Entity]! @${stitchingConfig.mergeDirective.name} }
101
- `).definitions;
102
- doc.definitions.push(entitiesSchema[0]);
103
- doc.definitions.push(entitiesSchema[1]);
104
- if (queryDef) {
105
- queryDef.fields.push(entitiesSchema[2].fields[0]);
106
- }
107
- else {
108
- doc.definitions.push(entitiesSchema[2]);
109
- }
110
- }
111
- return [stitchingConfig.stitchingDirectivesTypeDefs, print(doc)].join('\n');
112
- }
@@ -1,21 +0,0 @@
1
- import { TypeNameMetaFieldDef } from 'graphql';
2
- import { pathsFromSelectionSet } from './pathsFromSelectionSet.js';
3
- export function getSourcePaths(mappingInstructions, selectionSet) {
4
- const sourcePaths = [];
5
- for (const mappingInstruction of mappingInstructions) {
6
- const { sourcePath } = mappingInstruction;
7
- if (sourcePath.length) {
8
- sourcePaths.push(sourcePath);
9
- continue;
10
- }
11
- if (selectionSet == null) {
12
- continue;
13
- }
14
- const paths = pathsFromSelectionSet(selectionSet);
15
- for (const path of paths) {
16
- sourcePaths.push(path);
17
- }
18
- sourcePaths.push([TypeNameMetaFieldDef.name]);
19
- }
20
- return sourcePaths;
21
- }
package/esm/index.js DELETED
@@ -1,3 +0,0 @@
1
- export * from './stitchingDirectives.js';
2
- export * from './types.js';
3
- export * from './federationToStitchingSDL.js';
@@ -1,73 +0,0 @@
1
- import { parseValue, valueFromASTUntyped } from 'graphql';
2
- import { extractVariables } from './extractVariables.js';
3
- import { getSourcePaths } from './getSourcePaths.js';
4
- import { EXPANSION_PREFIX, KEY_DELIMITER, preparseMergeArgsExpr } from './preparseMergeArgsExpr.js';
5
- import { propertyTreeFromPaths } from './properties.js';
6
- export function parseMergeArgsExpr(mergeArgsExpr, selectionSet) {
7
- const { mergeArgsExpr: newMergeArgsExpr, expansionExpressions } = preparseMergeArgsExpr(mergeArgsExpr);
8
- const inputValue = parseValue(`{ ${newMergeArgsExpr} }`, { noLocation: true });
9
- const { inputValue: newInputValue, variablePaths } = extractVariables(inputValue);
10
- if (!Object.keys(expansionExpressions).length) {
11
- if (!Object.keys(variablePaths).length) {
12
- throw new Error('Merge arguments must declare a key.');
13
- }
14
- const mappingInstructions = getMappingInstructions(variablePaths);
15
- const usedProperties = propertyTreeFromPaths(getSourcePaths(mappingInstructions, selectionSet));
16
- return {
17
- args: valueFromASTUntyped(newInputValue),
18
- usedProperties,
19
- mappingInstructions,
20
- };
21
- }
22
- const expansionRegEx = new RegExp(`^${EXPANSION_PREFIX}[0-9]+$`);
23
- for (const variableName in variablePaths) {
24
- if (!variableName.match(expansionRegEx)) {
25
- throw new Error('Expansions cannot be mixed with single key declarations.');
26
- }
27
- }
28
- const expansions = [];
29
- const sourcePaths = [];
30
- for (const variableName in expansionExpressions) {
31
- const str = expansionExpressions[variableName];
32
- const valuePath = variablePaths[variableName];
33
- const { inputValue: expansionInputValue, variablePaths: expansionVariablePaths } = extractVariables(parseValue(`${str}`, { noLocation: true }));
34
- if (!Object.keys(expansionVariablePaths).length) {
35
- throw new Error('Merge arguments must declare a key.');
36
- }
37
- const mappingInstructions = getMappingInstructions(expansionVariablePaths);
38
- const value = valueFromASTUntyped(expansionInputValue);
39
- sourcePaths.push(...getSourcePaths(mappingInstructions, selectionSet));
40
- assertNotWithinList(valuePath);
41
- expansions.push({
42
- valuePath,
43
- value,
44
- mappingInstructions,
45
- });
46
- }
47
- const usedProperties = propertyTreeFromPaths(sourcePaths);
48
- return {
49
- args: valueFromASTUntyped(newInputValue),
50
- usedProperties,
51
- expansions,
52
- };
53
- }
54
- function getMappingInstructions(variablePaths) {
55
- const mappingInstructions = [];
56
- for (const keyPath in variablePaths) {
57
- const valuePath = variablePaths[keyPath];
58
- const splitKeyPath = keyPath.split(KEY_DELIMITER).slice(1);
59
- assertNotWithinList(valuePath);
60
- mappingInstructions.push({
61
- destinationPath: valuePath,
62
- sourcePath: splitKeyPath,
63
- });
64
- }
65
- return mappingInstructions;
66
- }
67
- function assertNotWithinList(path) {
68
- for (const pathSegment of path) {
69
- if (typeof pathSegment === 'number') {
70
- throw new Error('Insertions cannot be made into a list.');
71
- }
72
- }
73
- }
@@ -1,25 +0,0 @@
1
- import { Kind } from 'graphql';
2
- export function pathsFromSelectionSet(selectionSet, path = []) {
3
- const paths = [];
4
- for (const selection of selectionSet.selections) {
5
- const additions = pathsFromSelection(selection, path) ?? [];
6
- for (const addition of additions) {
7
- paths.push(addition);
8
- }
9
- }
10
- return paths;
11
- }
12
- function pathsFromSelection(selection, path) {
13
- if (selection.kind === Kind.FIELD) {
14
- const responseKey = selection.alias?.value ?? selection.name.value;
15
- if (selection.selectionSet) {
16
- return pathsFromSelectionSet(selection.selectionSet, path.concat([responseKey]));
17
- }
18
- else {
19
- return [path.concat([responseKey])];
20
- }
21
- }
22
- else if (selection.kind === Kind.INLINE_FRAGMENT) {
23
- return pathsFromSelectionSet(selection.selectionSet, path);
24
- }
25
- }
@@ -1,27 +0,0 @@
1
- export const KEY_DELIMITER = '__dot__';
2
- export const EXPANSION_PREFIX = '__exp';
3
- export function preparseMergeArgsExpr(mergeArgsExpr) {
4
- const variableRegex = /\$[_A-Za-z][_A-Za-z0-9.]*/g;
5
- const dotRegex = /\./g;
6
- mergeArgsExpr = mergeArgsExpr.replace(variableRegex, variable => variable.replace(dotRegex, KEY_DELIMITER));
7
- const segments = mergeArgsExpr.split('[[');
8
- const expansionExpressions = Object.create(null);
9
- if (segments.length === 1) {
10
- return { mergeArgsExpr, expansionExpressions };
11
- }
12
- let finalSegments = [segments[0]];
13
- for (let i = 1; i < segments.length; i++) {
14
- const additionalSegments = segments[i].split(']]');
15
- if (additionalSegments.length !== 2) {
16
- throw new Error(`Each opening "[[" must be matched by a closing "]]" without nesting.`);
17
- }
18
- finalSegments = finalSegments.concat(additionalSegments);
19
- }
20
- let finalMergeArgsExpr = finalSegments[0];
21
- for (let i = 1; i < finalSegments.length - 1; i += 2) {
22
- const variableName = `${EXPANSION_PREFIX}${(i - 1) / 2 + 1}`;
23
- expansionExpressions[variableName] = finalSegments[i];
24
- finalMergeArgsExpr += `\$${variableName}${finalSegments[i + 1]}`;
25
- }
26
- return { mergeArgsExpr: finalMergeArgsExpr, expansionExpressions };
27
- }
package/esm/properties.js DELETED
@@ -1,63 +0,0 @@
1
- export function addProperty(object, path, value) {
2
- const initialSegment = path[0];
3
- if (path.length === 1) {
4
- object[initialSegment] = value;
5
- return;
6
- }
7
- let field = object[initialSegment];
8
- if (field != null) {
9
- addProperty(field, path.slice(1), value);
10
- return;
11
- }
12
- if (typeof path[1] === 'string') {
13
- field = Object.create(null);
14
- }
15
- else {
16
- field = [];
17
- }
18
- addProperty(field, path.slice(1), value);
19
- object[initialSegment] = field;
20
- }
21
- export function getProperty(object, path) {
22
- if (!path.length || object == null) {
23
- return object;
24
- }
25
- const newPath = path.slice();
26
- const key = newPath.shift();
27
- if (key == null) {
28
- return;
29
- }
30
- const prop = object[key];
31
- return getProperty(prop, newPath);
32
- }
33
- export function getProperties(object, propertyTree) {
34
- if (object == null) {
35
- return object;
36
- }
37
- const newObject = Object.create(null);
38
- for (const key in propertyTree) {
39
- const subKey = propertyTree[key];
40
- if (subKey == null) {
41
- newObject[key] = object[key];
42
- continue;
43
- }
44
- const prop = object[key];
45
- newObject[key] = deepMap(prop, function deepMapFn(item) {
46
- return getProperties(item, subKey);
47
- });
48
- }
49
- return newObject;
50
- }
51
- export function propertyTreeFromPaths(paths) {
52
- const propertyTree = Object.create(null);
53
- for (const path of paths) {
54
- addProperty(propertyTree, path, null);
55
- }
56
- return propertyTree;
57
- }
58
- function deepMap(arrayOrItem, fn) {
59
- if (Array.isArray(arrayOrItem)) {
60
- return arrayOrItem.map(nestedArrayOrItem => deepMap(nestedArrayOrItem, fn));
61
- }
62
- return fn(arrayOrItem);
63
- }
@@ -1,74 +0,0 @@
1
- import { GraphQLDirective, GraphQLList, GraphQLNonNull, GraphQLString, } from 'graphql';
2
- import { defaultStitchingDirectiveOptions } from './defaultStitchingDirectiveOptions.js';
3
- import { stitchingDirectivesTransformer } from './stitchingDirectivesTransformer.js';
4
- import { stitchingDirectivesValidator } from './stitchingDirectivesValidator.js';
5
- export function stitchingDirectives(options = {}) {
6
- const finalOptions = {
7
- ...defaultStitchingDirectiveOptions,
8
- ...options,
9
- };
10
- const { keyDirectiveName, computedDirectiveName, mergeDirectiveName, canonicalDirectiveName } = finalOptions;
11
- const keyDirectiveTypeDefs = /* GraphQL */ `directive @${keyDirectiveName}(selectionSet: String!) on OBJECT`;
12
- const computedDirectiveTypeDefs = /* GraphQL */ `directive @${computedDirectiveName}(selectionSet: String!) on FIELD_DEFINITION`;
13
- const mergeDirectiveTypeDefs = /* GraphQL */ `directive @${mergeDirectiveName}(argsExpr: String, keyArg: String, keyField: String, key: [String!], additionalArgs: String) on FIELD_DEFINITION`;
14
- const canonicalDirectiveTypeDefs = /* GraphQL */ `directive @${canonicalDirectiveName} on OBJECT | INTERFACE | INPUT_OBJECT | UNION | ENUM | SCALAR | FIELD_DEFINITION | INPUT_FIELD_DEFINITION`;
15
- const keyDirective = new GraphQLDirective({
16
- name: keyDirectiveName,
17
- locations: ['OBJECT'],
18
- args: {
19
- selectionSet: { type: new GraphQLNonNull(GraphQLString) },
20
- },
21
- });
22
- const computedDirective = new GraphQLDirective({
23
- name: computedDirectiveName,
24
- locations: ['FIELD_DEFINITION'],
25
- args: {
26
- selectionSet: { type: new GraphQLNonNull(GraphQLString) },
27
- },
28
- });
29
- const mergeDirective = new GraphQLDirective({
30
- name: mergeDirectiveName,
31
- locations: ['FIELD_DEFINITION'],
32
- args: {
33
- argsExpr: { type: GraphQLString },
34
- keyArg: { type: GraphQLString },
35
- keyField: { type: GraphQLString },
36
- key: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
37
- additionalArgs: { type: GraphQLString },
38
- },
39
- });
40
- const canonicalDirective = new GraphQLDirective({
41
- name: canonicalDirectiveName,
42
- locations: [
43
- 'OBJECT',
44
- 'INTERFACE',
45
- 'INPUT_OBJECT',
46
- 'UNION',
47
- 'ENUM',
48
- 'SCALAR',
49
- 'FIELD_DEFINITION',
50
- 'INPUT_FIELD_DEFINITION',
51
- ],
52
- });
53
- const allStitchingDirectivesTypeDefs = [
54
- keyDirectiveTypeDefs,
55
- computedDirectiveTypeDefs,
56
- mergeDirectiveTypeDefs,
57
- canonicalDirectiveTypeDefs,
58
- ].join('\n');
59
- return {
60
- keyDirectiveTypeDefs,
61
- computedDirectiveTypeDefs,
62
- mergeDirectiveTypeDefs,
63
- canonicalDirectiveTypeDefs,
64
- stitchingDirectivesTypeDefs: allStitchingDirectivesTypeDefs, // for backwards compatibility
65
- allStitchingDirectivesTypeDefs,
66
- keyDirective,
67
- computedDirective,
68
- mergeDirective,
69
- canonicalDirective,
70
- allStitchingDirectives: [keyDirective, computedDirective, mergeDirective, canonicalDirective],
71
- stitchingDirectivesValidator: stitchingDirectivesValidator(finalOptions),
72
- stitchingDirectivesTransformer: stitchingDirectivesTransformer(finalOptions),
73
- };
74
- }