@graphql-tools/stitching-directives 3.0.0-alpha-be1b2ebd.0 → 3.0.0-alpha-20230517124048-55208565

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. package/README.md +0 -2
  2. package/cjs/defaultStitchingDirectiveOptions.js +10 -0
  3. package/cjs/extractVariables.js +52 -0
  4. package/cjs/federationToStitchingSDL.js +114 -0
  5. package/cjs/getSourcePaths.js +25 -0
  6. package/cjs/index.js +6 -0
  7. package/cjs/package.json +1 -0
  8. package/cjs/parseMergeArgsExpr.js +69 -0
  9. package/cjs/pathsFromSelectionSet.js +31 -0
  10. package/cjs/preparseMergeArgsExpr.js +31 -0
  11. package/cjs/properties.js +70 -0
  12. package/cjs/stitchingDirectives.js +78 -0
  13. package/cjs/stitchingDirectivesTransformer.js +423 -0
  14. package/cjs/stitchingDirectivesValidator.js +120 -0
  15. package/cjs/types.js +0 -0
  16. package/esm/defaultStitchingDirectiveOptions.js +7 -0
  17. package/esm/extractVariables.js +48 -0
  18. package/esm/federationToStitchingSDL.js +110 -0
  19. package/esm/getSourcePaths.js +21 -0
  20. package/esm/index.js +3 -0
  21. package/esm/parseMergeArgsExpr.js +65 -0
  22. package/esm/pathsFromSelectionSet.js +27 -0
  23. package/esm/preparseMergeArgsExpr.js +27 -0
  24. package/esm/properties.js +63 -0
  25. package/esm/stitchingDirectives.js +74 -0
  26. package/esm/stitchingDirectivesTransformer.js +419 -0
  27. package/esm/stitchingDirectivesValidator.js +116 -0
  28. package/esm/types.js +0 -0
  29. package/package.json +40 -15
  30. package/typings/defaultStitchingDirectiveOptions.d.cts +2 -0
  31. package/{defaultStitchingDirectiveOptions.d.ts → typings/defaultStitchingDirectiveOptions.d.ts} +1 -1
  32. package/typings/extractVariables.d.cts +7 -0
  33. package/{extractVariables.d.ts → typings/extractVariables.d.ts} +1 -1
  34. package/typings/federationToStitchingSDL.d.cts +2 -0
  35. package/{federationToStitchingSDL.d.ts → typings/federationToStitchingSDL.d.ts} +1 -1
  36. package/typings/getSourcePaths.d.cts +3 -0
  37. package/{getSourcePaths.d.ts → typings/getSourcePaths.d.ts} +1 -1
  38. package/typings/index.d.cts +3 -0
  39. package/typings/index.d.ts +3 -0
  40. package/typings/parseMergeArgsExpr.d.cts +3 -0
  41. package/{parseMergeArgsExpr.d.ts → typings/parseMergeArgsExpr.d.ts} +1 -1
  42. package/typings/pathsFromSelectionSet.d.ts +2 -0
  43. package/typings/preparseMergeArgsExpr.d.ts +6 -0
  44. package/typings/properties.d.cts +5 -0
  45. package/{properties.d.ts → typings/properties.d.ts} +1 -1
  46. package/typings/stitchingDirectives.d.cts +19 -0
  47. package/{stitchingDirectives.d.ts → typings/stitchingDirectives.d.ts} +1 -1
  48. package/typings/stitchingDirectivesTransformer.d.cts +3 -0
  49. package/{stitchingDirectivesTransformer.d.ts → typings/stitchingDirectivesTransformer.d.ts} +1 -1
  50. package/typings/stitchingDirectivesValidator.d.cts +3 -0
  51. package/{stitchingDirectivesValidator.d.ts → typings/stitchingDirectivesValidator.d.ts} +1 -1
  52. package/typings/types.d.cts +35 -0
  53. package/{types.d.ts → typings/types.d.ts} +3 -3
  54. package/index.d.ts +0 -3
  55. package/index.js +0 -972
  56. package/index.mjs +0 -967
  57. /package/{pathsFromSelectionSet.d.ts → typings/pathsFromSelectionSet.d.cts} +0 -0
  58. /package/{preparseMergeArgsExpr.d.ts → typings/preparseMergeArgsExpr.d.cts} +0 -0
@@ -0,0 +1,419 @@
1
+ import { getNamedType, getNullableType, isInterfaceType, isListType, isObjectType, isUnionType, Kind, parseValue, print, valueFromASTUntyped, } from 'graphql';
2
+ import { cloneSubschemaConfig } from '@graphql-tools/delegate';
3
+ import { getDirective, getImplementingTypes, MapperKind, mapSchema, mergeDeep, parseSelectionSet, } from '@graphql-tools/utils';
4
+ import { defaultStitchingDirectiveOptions } from './defaultStitchingDirectiveOptions.js';
5
+ import { parseMergeArgsExpr } from './parseMergeArgsExpr.js';
6
+ import { addProperty, getProperty, getProperties } from './properties.js';
7
+ import { stitchingDirectivesValidator } from './stitchingDirectivesValidator.js';
8
+ export function stitchingDirectivesTransformer(options = {}) {
9
+ const { keyDirectiveName, computedDirectiveName, mergeDirectiveName, canonicalDirectiveName, pathToDirectivesInExtensions, } = {
10
+ ...defaultStitchingDirectiveOptions,
11
+ ...options,
12
+ };
13
+ return (subschemaConfig) => {
14
+ var _a, _b, _c, _d, _e, _f, _g, _h;
15
+ const newSubschemaConfig = cloneSubschemaConfig(subschemaConfig);
16
+ const selectionSetsByType = Object.create(null);
17
+ const computedFieldSelectionSets = Object.create(null);
18
+ const mergedTypesResolversInfo = Object.create(null);
19
+ const canonicalTypesInfo = Object.create(null);
20
+ const schema = subschemaConfig.schema;
21
+ // gateway should also run validation
22
+ stitchingDirectivesValidator(options)(schema);
23
+ function setCanonicalDefinition(typeName, fieldName) {
24
+ var _a;
25
+ canonicalTypesInfo[typeName] = canonicalTypesInfo[typeName] || Object.create(null);
26
+ if (fieldName) {
27
+ const fields = (_a = canonicalTypesInfo[typeName].fields) !== null && _a !== void 0 ? _a : Object.create(null);
28
+ canonicalTypesInfo[typeName].fields = fields;
29
+ fields[fieldName] = true;
30
+ }
31
+ else {
32
+ canonicalTypesInfo[typeName].canonical = true;
33
+ }
34
+ }
35
+ mapSchema(schema, {
36
+ [MapperKind.OBJECT_TYPE]: type => {
37
+ var _a, _b;
38
+ const keyDirective = (_a = getDirective(schema, type, keyDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
39
+ if (keyDirective != null) {
40
+ const selectionSet = parseSelectionSet(keyDirective['selectionSet'], { noLocation: true });
41
+ selectionSetsByType[type.name] = selectionSet;
42
+ }
43
+ const canonicalDirective = (_b = getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _b === void 0 ? void 0 : _b[0];
44
+ if (canonicalDirective != null) {
45
+ setCanonicalDefinition(type.name);
46
+ }
47
+ return undefined;
48
+ },
49
+ [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
50
+ var _a, _b, _c;
51
+ const computedDirective = (_a = getDirective(schema, fieldConfig, computedDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
52
+ if (computedDirective != null) {
53
+ const selectionSet = parseSelectionSet(computedDirective['selectionSet'], { noLocation: true });
54
+ if (!computedFieldSelectionSets[typeName]) {
55
+ computedFieldSelectionSets[typeName] = Object.create(null);
56
+ }
57
+ computedFieldSelectionSets[typeName][fieldName] = selectionSet;
58
+ }
59
+ const mergeDirective = (_b = getDirective(schema, fieldConfig, mergeDirectiveName, pathToDirectivesInExtensions)) === null || _b === void 0 ? void 0 : _b[0];
60
+ if ((mergeDirective === null || mergeDirective === void 0 ? void 0 : mergeDirective['keyField']) != null) {
61
+ const mergeDirectiveKeyField = mergeDirective['keyField'];
62
+ const selectionSet = parseSelectionSet(`{ ${mergeDirectiveKeyField}}`, { noLocation: true });
63
+ const typeNames = mergeDirective['types'];
64
+ const returnType = getNamedType(fieldConfig.type);
65
+ forEachConcreteType(schema, returnType, typeNames, typeName => {
66
+ if (typeNames == null || typeNames.includes(typeName)) {
67
+ const existingSelectionSet = selectionSetsByType[typeName];
68
+ selectionSetsByType[typeName] = existingSelectionSet
69
+ ? mergeSelectionSets(existingSelectionSet, selectionSet)
70
+ : selectionSet;
71
+ }
72
+ });
73
+ }
74
+ const canonicalDirective = (_c = getDirective(schema, fieldConfig, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _c === void 0 ? void 0 : _c[0];
75
+ if (canonicalDirective != null) {
76
+ setCanonicalDefinition(typeName, fieldName);
77
+ }
78
+ return undefined;
79
+ },
80
+ [MapperKind.INTERFACE_TYPE]: type => {
81
+ var _a;
82
+ const canonicalDirective = (_a = getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
83
+ if (canonicalDirective) {
84
+ setCanonicalDefinition(type.name);
85
+ }
86
+ return undefined;
87
+ },
88
+ [MapperKind.INTERFACE_FIELD]: (fieldConfig, fieldName, typeName) => {
89
+ var _a;
90
+ const canonicalDirective = (_a = getDirective(schema, fieldConfig, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
91
+ if (canonicalDirective) {
92
+ setCanonicalDefinition(typeName, fieldName);
93
+ }
94
+ return undefined;
95
+ },
96
+ [MapperKind.INPUT_OBJECT_TYPE]: type => {
97
+ var _a;
98
+ const canonicalDirective = (_a = getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
99
+ if (canonicalDirective) {
100
+ setCanonicalDefinition(type.name);
101
+ }
102
+ return undefined;
103
+ },
104
+ [MapperKind.INPUT_OBJECT_FIELD]: (inputFieldConfig, fieldName, typeName) => {
105
+ var _a;
106
+ const canonicalDirective = (_a = getDirective(schema, inputFieldConfig, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
107
+ if (canonicalDirective != null) {
108
+ setCanonicalDefinition(typeName, fieldName);
109
+ }
110
+ return undefined;
111
+ },
112
+ [MapperKind.UNION_TYPE]: type => {
113
+ var _a;
114
+ const canonicalDirective = (_a = getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
115
+ if (canonicalDirective != null) {
116
+ setCanonicalDefinition(type.name);
117
+ }
118
+ return undefined;
119
+ },
120
+ [MapperKind.ENUM_TYPE]: type => {
121
+ var _a;
122
+ const canonicalDirective = (_a = getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
123
+ if (canonicalDirective != null) {
124
+ setCanonicalDefinition(type.name);
125
+ }
126
+ return undefined;
127
+ },
128
+ [MapperKind.SCALAR_TYPE]: type => {
129
+ var _a;
130
+ const canonicalDirective = (_a = getDirective(schema, type, canonicalDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
131
+ if (canonicalDirective != null) {
132
+ setCanonicalDefinition(type.name);
133
+ }
134
+ return undefined;
135
+ },
136
+ });
137
+ if (subschemaConfig.merge) {
138
+ for (const typeName in subschemaConfig.merge) {
139
+ const mergedTypeConfig = subschemaConfig.merge[typeName];
140
+ if (mergedTypeConfig.selectionSet) {
141
+ const selectionSet = parseSelectionSet(mergedTypeConfig.selectionSet, { noLocation: true });
142
+ if (selectionSet) {
143
+ if (selectionSetsByType[typeName]) {
144
+ selectionSetsByType[typeName] = mergeSelectionSets(selectionSetsByType[typeName], selectionSet);
145
+ }
146
+ else {
147
+ selectionSetsByType[typeName] = selectionSet;
148
+ }
149
+ }
150
+ }
151
+ if (mergedTypeConfig.fields) {
152
+ for (const fieldName in mergedTypeConfig.fields) {
153
+ const fieldConfig = mergedTypeConfig.fields[fieldName];
154
+ if (!fieldConfig.selectionSet)
155
+ continue;
156
+ const selectionSet = parseSelectionSet(fieldConfig.selectionSet, { noLocation: true });
157
+ if (selectionSet) {
158
+ if ((_a = computedFieldSelectionSets[typeName]) === null || _a === void 0 ? void 0 : _a[fieldName]) {
159
+ computedFieldSelectionSets[typeName][fieldName] = mergeSelectionSets(computedFieldSelectionSets[typeName][fieldName], selectionSet);
160
+ }
161
+ else {
162
+ if (computedFieldSelectionSets[typeName] == null) {
163
+ computedFieldSelectionSets[typeName] = Object.create(null);
164
+ }
165
+ computedFieldSelectionSets[typeName][fieldName] = selectionSet;
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ const allSelectionSetsByType = Object.create(null);
173
+ for (const typeName in selectionSetsByType) {
174
+ allSelectionSetsByType[typeName] = allSelectionSetsByType[typeName] || [];
175
+ const selectionSet = selectionSetsByType[typeName];
176
+ allSelectionSetsByType[typeName].push(selectionSet);
177
+ }
178
+ for (const typeName in computedFieldSelectionSets) {
179
+ const selectionSets = computedFieldSelectionSets[typeName];
180
+ for (const i in selectionSets) {
181
+ allSelectionSetsByType[typeName] = allSelectionSetsByType[typeName] || [];
182
+ const selectionSet = selectionSets[i];
183
+ allSelectionSetsByType[typeName].push(selectionSet);
184
+ }
185
+ }
186
+ mapSchema(schema, {
187
+ [MapperKind.OBJECT_FIELD]: function objectFieldMapper(fieldConfig, fieldName) {
188
+ var _a, _b;
189
+ const mergeDirective = (_a = getDirective(schema, fieldConfig, mergeDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
190
+ if (mergeDirective != null) {
191
+ const returnType = getNullableType(fieldConfig.type);
192
+ const returnsList = isListType(returnType);
193
+ const namedType = getNamedType(returnType);
194
+ let mergeArgsExpr = mergeDirective['argsExpr'];
195
+ if (mergeArgsExpr == null) {
196
+ const key = mergeDirective['key'];
197
+ const keyField = mergeDirective['keyField'];
198
+ const keyExpr = key != null ? buildKeyExpr(key) : keyField != null ? `$key.${keyField}` : '$key';
199
+ const keyArg = mergeDirective['keyArg'];
200
+ const argNames = keyArg == null ? [Object.keys((_b = fieldConfig.args) !== null && _b !== void 0 ? _b : {})[0]] : keyArg.split('.');
201
+ const lastArgName = argNames.pop();
202
+ mergeArgsExpr = returnsList ? `${lastArgName}: [[${keyExpr}]]` : `${lastArgName}: ${keyExpr}`;
203
+ for (const argName of argNames.reverse()) {
204
+ mergeArgsExpr = `${argName}: { ${mergeArgsExpr} }`;
205
+ }
206
+ }
207
+ const typeNames = mergeDirective['types'];
208
+ forEachConcreteTypeName(namedType, schema, typeNames, function generateResolveInfo(typeName) {
209
+ const parsedMergeArgsExpr = parseMergeArgsExpr(mergeArgsExpr, allSelectionSetsByType[typeName] == null
210
+ ? undefined
211
+ : mergeSelectionSets(...allSelectionSetsByType[typeName]));
212
+ const additionalArgs = mergeDirective['additionalArgs'];
213
+ if (additionalArgs != null) {
214
+ parsedMergeArgsExpr.args = mergeDeep([
215
+ parsedMergeArgsExpr.args,
216
+ valueFromASTUntyped(parseValue(`{ ${additionalArgs} }`, { noLocation: true })),
217
+ ]);
218
+ }
219
+ mergedTypesResolversInfo[typeName] = {
220
+ fieldName,
221
+ returnsList,
222
+ ...parsedMergeArgsExpr,
223
+ };
224
+ });
225
+ }
226
+ return undefined;
227
+ },
228
+ });
229
+ for (const typeName in selectionSetsByType) {
230
+ const selectionSet = selectionSetsByType[typeName];
231
+ const mergeConfig = (_b = newSubschemaConfig.merge) !== null && _b !== void 0 ? _b : Object.create(null);
232
+ newSubschemaConfig.merge = mergeConfig;
233
+ if (mergeConfig[typeName] == null) {
234
+ newSubschemaConfig.merge[typeName] = Object.create(null);
235
+ }
236
+ const mergeTypeConfig = mergeConfig[typeName];
237
+ mergeTypeConfig.selectionSet = print(selectionSet);
238
+ }
239
+ for (const typeName in computedFieldSelectionSets) {
240
+ const selectionSets = computedFieldSelectionSets[typeName];
241
+ const mergeConfig = (_c = newSubschemaConfig.merge) !== null && _c !== void 0 ? _c : Object.create(null);
242
+ newSubschemaConfig.merge = mergeConfig;
243
+ if (mergeConfig[typeName] == null) {
244
+ mergeConfig[typeName] = Object.create(null);
245
+ }
246
+ const mergeTypeConfig = newSubschemaConfig.merge[typeName];
247
+ const mergeTypeConfigFields = (_d = mergeTypeConfig.fields) !== null && _d !== void 0 ? _d : Object.create(null);
248
+ mergeTypeConfig.fields = mergeTypeConfigFields;
249
+ for (const fieldName in selectionSets) {
250
+ const selectionSet = selectionSets[fieldName];
251
+ const fieldConfig = (_e = mergeTypeConfigFields[fieldName]) !== null && _e !== void 0 ? _e : Object.create(null);
252
+ mergeTypeConfigFields[fieldName] = fieldConfig;
253
+ fieldConfig.selectionSet = print(selectionSet);
254
+ fieldConfig.computed = true;
255
+ }
256
+ }
257
+ for (const typeName in mergedTypesResolversInfo) {
258
+ const mergedTypeResolverInfo = mergedTypesResolversInfo[typeName];
259
+ const mergeConfig = (_f = newSubschemaConfig.merge) !== null && _f !== void 0 ? _f : Object.create(null);
260
+ newSubschemaConfig.merge = mergeConfig;
261
+ if (newSubschemaConfig.merge[typeName] == null) {
262
+ newSubschemaConfig.merge[typeName] = Object.create(null);
263
+ }
264
+ const mergeTypeConfig = newSubschemaConfig.merge[typeName];
265
+ mergeTypeConfig.fieldName = mergedTypeResolverInfo.fieldName;
266
+ if (mergedTypeResolverInfo.returnsList) {
267
+ mergeTypeConfig.key = generateKeyFn(mergedTypeResolverInfo);
268
+ mergeTypeConfig.argsFromKeys = generateArgsFromKeysFn(mergedTypeResolverInfo);
269
+ }
270
+ else {
271
+ mergeTypeConfig.args = generateArgsFn(mergedTypeResolverInfo);
272
+ }
273
+ }
274
+ for (const typeName in canonicalTypesInfo) {
275
+ const canonicalTypeInfo = canonicalTypesInfo[typeName];
276
+ const mergeConfig = (_g = newSubschemaConfig.merge) !== null && _g !== void 0 ? _g : Object.create(null);
277
+ newSubschemaConfig.merge = mergeConfig;
278
+ if (newSubschemaConfig.merge[typeName] == null) {
279
+ newSubschemaConfig.merge[typeName] = Object.create(null);
280
+ }
281
+ const mergeTypeConfig = newSubschemaConfig.merge[typeName];
282
+ if (canonicalTypeInfo.canonical) {
283
+ mergeTypeConfig.canonical = true;
284
+ }
285
+ if (canonicalTypeInfo.fields) {
286
+ const mergeTypeConfigFields = (_h = mergeTypeConfig.fields) !== null && _h !== void 0 ? _h : Object.create(null);
287
+ mergeTypeConfig.fields = mergeTypeConfigFields;
288
+ for (const fieldName in canonicalTypeInfo.fields) {
289
+ if (mergeTypeConfigFields[fieldName] == null) {
290
+ mergeTypeConfigFields[fieldName] = Object.create(null);
291
+ }
292
+ mergeTypeConfigFields[fieldName].canonical = true;
293
+ }
294
+ }
295
+ }
296
+ return newSubschemaConfig;
297
+ };
298
+ }
299
+ function forEachConcreteType(schema, type, typeNames, fn) {
300
+ if (isInterfaceType(type)) {
301
+ for (const typeName of getImplementingTypes(type.name, schema)) {
302
+ if (typeNames == null || typeNames.includes(typeName)) {
303
+ fn(typeName);
304
+ }
305
+ }
306
+ }
307
+ else if (isUnionType(type)) {
308
+ for (const { name: typeName } of type.getTypes()) {
309
+ if (typeNames == null || typeNames.includes(typeName)) {
310
+ fn(typeName);
311
+ }
312
+ }
313
+ }
314
+ else if (isObjectType(type)) {
315
+ fn(type.name);
316
+ }
317
+ }
318
+ function generateKeyFn(mergedTypeResolverInfo) {
319
+ return function keyFn(originalResult) {
320
+ return getProperties(originalResult, mergedTypeResolverInfo.usedProperties);
321
+ };
322
+ }
323
+ function generateArgsFromKeysFn(mergedTypeResolverInfo) {
324
+ const { expansions, args } = mergedTypeResolverInfo;
325
+ return function generateArgsFromKeys(keys) {
326
+ const newArgs = mergeDeep([{}, args]);
327
+ if (expansions) {
328
+ for (const expansion of expansions) {
329
+ const mappingInstructions = expansion.mappingInstructions;
330
+ const expanded = [];
331
+ for (const key of keys) {
332
+ let newValue = mergeDeep([{}, expansion.valuePath]);
333
+ for (const { destinationPath, sourcePath } of mappingInstructions) {
334
+ if (destinationPath.length) {
335
+ addProperty(newValue, destinationPath, getProperty(key, sourcePath));
336
+ }
337
+ else {
338
+ newValue = getProperty(key, sourcePath);
339
+ }
340
+ }
341
+ expanded.push(newValue);
342
+ }
343
+ addProperty(newArgs, expansion.valuePath, expanded);
344
+ }
345
+ }
346
+ return newArgs;
347
+ };
348
+ }
349
+ function generateArgsFn(mergedTypeResolverInfo) {
350
+ const { mappingInstructions, args, usedProperties } = mergedTypeResolverInfo;
351
+ return function generateArgs(originalResult) {
352
+ const newArgs = mergeDeep([{}, args]);
353
+ const filteredResult = getProperties(originalResult, usedProperties);
354
+ if (mappingInstructions) {
355
+ for (const mappingInstruction of mappingInstructions) {
356
+ const { destinationPath, sourcePath } = mappingInstruction;
357
+ addProperty(newArgs, destinationPath, getProperty(filteredResult, sourcePath));
358
+ }
359
+ }
360
+ return newArgs;
361
+ };
362
+ }
363
+ function buildKeyExpr(key) {
364
+ let mergedObject = {};
365
+ for (const keyDef of key) {
366
+ let [aliasOrKeyPath, keyPath] = keyDef.split(':');
367
+ let aliasPath;
368
+ if (keyPath == null) {
369
+ keyPath = aliasPath = aliasOrKeyPath;
370
+ }
371
+ else {
372
+ aliasPath = aliasOrKeyPath;
373
+ }
374
+ const aliasParts = aliasPath.split('.');
375
+ const lastAliasPart = aliasParts.pop();
376
+ if (lastAliasPart == null) {
377
+ throw new Error(`Key "${key}" is invalid, no path provided.`);
378
+ }
379
+ let object = { [lastAliasPart]: `$key.${keyPath}` };
380
+ for (const aliasPart of aliasParts.reverse()) {
381
+ object = { [aliasPart]: object };
382
+ }
383
+ mergedObject = mergeDeep([mergedObject, object]);
384
+ }
385
+ return JSON.stringify(mergedObject).replace(/"/g, '');
386
+ }
387
+ function mergeSelectionSets(...selectionSets) {
388
+ const normalizedSelections = Object.create(null);
389
+ for (const selectionSet of selectionSets) {
390
+ for (const selection of selectionSet.selections) {
391
+ const normalizedSelection = print(selection);
392
+ normalizedSelections[normalizedSelection] = selection;
393
+ }
394
+ }
395
+ const newSelectionSet = {
396
+ kind: Kind.SELECTION_SET,
397
+ selections: Object.values(normalizedSelections),
398
+ };
399
+ return newSelectionSet;
400
+ }
401
+ function forEachConcreteTypeName(returnType, schema, typeNames, fn) {
402
+ if (isInterfaceType(returnType)) {
403
+ for (const typeName of getImplementingTypes(returnType.name, schema)) {
404
+ if (typeNames == null || typeNames.includes(typeName)) {
405
+ fn(typeName);
406
+ }
407
+ }
408
+ }
409
+ else if (isUnionType(returnType)) {
410
+ for (const type of returnType.getTypes()) {
411
+ if (typeNames == null || typeNames.includes(type.name)) {
412
+ fn(type.name);
413
+ }
414
+ }
415
+ }
416
+ else if (isObjectType(returnType) && (typeNames == null || typeNames.includes(returnType.name))) {
417
+ fn(returnType.name);
418
+ }
419
+ }
@@ -0,0 +1,116 @@
1
+ import { getNullableType, isAbstractType, isInterfaceType, isListType, isNamedType, isObjectType, isUnionType, parseValue, } from 'graphql';
2
+ import { getDirective, getImplementingTypes, isSome, MapperKind, mapSchema, parseSelectionSet, } from '@graphql-tools/utils';
3
+ import { defaultStitchingDirectiveOptions } from './defaultStitchingDirectiveOptions.js';
4
+ import { parseMergeArgsExpr } from './parseMergeArgsExpr.js';
5
+ const dottedNameRegEx = /^[_A-Za-z][_0-9A-Za-z]*(.[_A-Za-z][_0-9A-Za-z]*)*$/;
6
+ export function stitchingDirectivesValidator(options = {}) {
7
+ const { keyDirectiveName, computedDirectiveName, mergeDirectiveName, pathToDirectivesInExtensions } = {
8
+ ...defaultStitchingDirectiveOptions,
9
+ ...options,
10
+ };
11
+ return (schema) => {
12
+ var _a;
13
+ const queryTypeName = (_a = schema.getQueryType()) === null || _a === void 0 ? void 0 : _a.name;
14
+ mapSchema(schema, {
15
+ [MapperKind.OBJECT_TYPE]: type => {
16
+ var _a;
17
+ const keyDirective = (_a = getDirective(schema, type, keyDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
18
+ if (keyDirective != null) {
19
+ parseSelectionSet(keyDirective['selectionSet']);
20
+ }
21
+ return undefined;
22
+ },
23
+ [MapperKind.OBJECT_FIELD]: (fieldConfig, _fieldName, typeName) => {
24
+ var _a, _b, _c;
25
+ const computedDirective = (_a = getDirective(schema, fieldConfig, computedDirectiveName, pathToDirectivesInExtensions)) === null || _a === void 0 ? void 0 : _a[0];
26
+ if (computedDirective != null) {
27
+ parseSelectionSet(computedDirective['selectionSet']);
28
+ }
29
+ const mergeDirective = (_b = getDirective(schema, fieldConfig, mergeDirectiveName, pathToDirectivesInExtensions)) === null || _b === void 0 ? void 0 : _b[0];
30
+ if (mergeDirective != null) {
31
+ if (typeName !== queryTypeName) {
32
+ throw new Error('@merge directive may be used only for root fields of the root Query type.');
33
+ }
34
+ let returnType = getNullableType(fieldConfig.type);
35
+ if (isListType(returnType)) {
36
+ returnType = getNullableType(returnType.ofType);
37
+ }
38
+ if (!isNamedType(returnType)) {
39
+ throw new Error('@merge directive must be used on a field that returns an object or a list of objects.');
40
+ }
41
+ const mergeArgsExpr = mergeDirective['argsExpr'];
42
+ if (mergeArgsExpr != null) {
43
+ parseMergeArgsExpr(mergeArgsExpr);
44
+ }
45
+ const args = Object.keys((_c = fieldConfig.args) !== null && _c !== void 0 ? _c : {});
46
+ const keyArg = mergeDirective['keyArg'];
47
+ if (keyArg == null) {
48
+ if (!mergeArgsExpr && args.length !== 1) {
49
+ throw new Error('Cannot use @merge directive without `keyArg` argument if resolver takes more than one argument.');
50
+ }
51
+ }
52
+ else if (!keyArg.match(dottedNameRegEx)) {
53
+ throw new Error('`keyArg` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods.');
54
+ // TODO: ideally we should check that the arg exists for the resolver
55
+ }
56
+ const keyField = mergeDirective['keyField'];
57
+ if (keyField != null && !keyField.match(dottedNameRegEx)) {
58
+ throw new Error('`keyField` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods.');
59
+ // TODO: ideally we should check that it is part of the key
60
+ }
61
+ const key = mergeDirective['key'];
62
+ if (key != null) {
63
+ if (keyField != null) {
64
+ throw new Error('Cannot use @merge directive with both `keyField` and `key` arguments.');
65
+ }
66
+ for (const keyDef of key) {
67
+ let [aliasOrKeyPath, keyPath] = keyDef.split(':');
68
+ let aliasPath;
69
+ if (keyPath == null) {
70
+ keyPath = aliasPath = aliasOrKeyPath;
71
+ }
72
+ else {
73
+ aliasPath = aliasOrKeyPath;
74
+ }
75
+ if (keyPath != null && !keyPath.match(dottedNameRegEx)) {
76
+ throw new Error('Each partial key within the `key` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods.');
77
+ // TODO: ideally we should check that it is part of the key
78
+ }
79
+ if (aliasPath != null && !aliasOrKeyPath.match(dottedNameRegEx)) {
80
+ throw new Error('Each alias within the `key` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods.');
81
+ // TODO: ideally we should check that the arg exists within the resolver
82
+ }
83
+ }
84
+ }
85
+ const additionalArgs = mergeDirective['additionalArgs'];
86
+ if (additionalArgs != null) {
87
+ parseValue(`{ ${additionalArgs} }`, { noLocation: true });
88
+ }
89
+ if (mergeArgsExpr != null && (keyArg != null || additionalArgs != null)) {
90
+ throw new Error('Cannot use @merge directive with both `argsExpr` argument and any additional argument.');
91
+ }
92
+ if (!isInterfaceType(returnType) && !isUnionType(returnType) && !isObjectType(returnType)) {
93
+ throw new Error('@merge directive may be used only with resolver that return an object, interface, or union.');
94
+ }
95
+ const typeNames = mergeDirective['types'];
96
+ if (typeNames != null) {
97
+ if (!isAbstractType(returnType)) {
98
+ throw new Error('Types argument can only be used with a field that returns an abstract type.');
99
+ }
100
+ const implementingTypes = isInterfaceType(returnType)
101
+ ? getImplementingTypes(returnType.name, schema).map(typeName => schema.getType(typeName))
102
+ : returnType.getTypes();
103
+ const implementingTypeNames = implementingTypes.map(type => type === null || type === void 0 ? void 0 : type.name).filter(isSome);
104
+ for (const typeName of typeNames) {
105
+ if (!implementingTypeNames.includes(typeName)) {
106
+ throw new Error(`Types argument can only include only type names that implement the field return type's abstract type.`);
107
+ }
108
+ }
109
+ }
110
+ }
111
+ return undefined;
112
+ },
113
+ });
114
+ return schema;
115
+ };
116
+ }
package/esm/types.js ADDED
File without changes
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@graphql-tools/stitching-directives",
3
- "version": "3.0.0-alpha-be1b2ebd.0",
3
+ "version": "3.0.0-alpha-20230517124048-55208565",
4
4
  "description": "A set of utils for faster development of GraphQL tools",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
7
- "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
7
+ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
8
8
  },
9
9
  "dependencies": {
10
- "@graphql-tools/delegate": "9.0.0-alpha-be1b2ebd.0",
11
- "@graphql-tools/utils": "9.0.0-alpha-be1b2ebd.0",
12
- "tslib": "~2.3.0"
10
+ "@graphql-tools/delegate": "10.0.0-alpha-20230517124048-55208565",
11
+ "@graphql-tools/utils": "10.0.0-alpha-20230517124048-55208565",
12
+ "tslib": "^2.4.0"
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",
@@ -17,20 +17,45 @@
17
17
  "directory": "packages/stitching-directives"
18
18
  },
19
19
  "license": "MIT",
20
- "main": "index.js",
21
- "module": "index.mjs",
22
- "typings": "index.d.ts",
20
+ "engines": {
21
+ "node": ">=16.0.0"
22
+ },
23
+ "main": "cjs/index.js",
24
+ "module": "esm/index.js",
25
+ "typings": "typings/index.d.ts",
23
26
  "typescript": {
24
- "definition": "index.d.ts"
27
+ "definition": "typings/index.d.ts"
25
28
  },
29
+ "type": "module",
26
30
  "exports": {
27
31
  ".": {
28
- "require": "./index.js",
29
- "import": "./index.mjs"
32
+ "require": {
33
+ "types": "./typings/index.d.cts",
34
+ "default": "./cjs/index.js"
35
+ },
36
+ "import": {
37
+ "types": "./typings/index.d.ts",
38
+ "default": "./esm/index.js"
39
+ },
40
+ "default": {
41
+ "types": "./typings/index.d.ts",
42
+ "default": "./esm/index.js"
43
+ }
30
44
  },
31
45
  "./*": {
32
- "require": "./*.js",
33
- "import": "./*.mjs"
34
- }
46
+ "require": {
47
+ "types": "./typings/*.d.cts",
48
+ "default": "./cjs/*.js"
49
+ },
50
+ "import": {
51
+ "types": "./typings/*.d.ts",
52
+ "default": "./esm/*.js"
53
+ },
54
+ "default": {
55
+ "types": "./typings/*.d.ts",
56
+ "default": "./esm/*.js"
57
+ }
58
+ },
59
+ "./package.json": "./package.json"
35
60
  }
36
- }
61
+ }
@@ -0,0 +1,2 @@
1
+ import { StitchingDirectivesFinalOptions } from './types.cjs';
2
+ export declare const defaultStitchingDirectiveOptions: StitchingDirectivesFinalOptions;
@@ -1,2 +1,2 @@
1
- import { StitchingDirectivesFinalOptions } from './types';
1
+ import { StitchingDirectivesFinalOptions } from './types.js';
2
2
  export declare const defaultStitchingDirectiveOptions: StitchingDirectivesFinalOptions;
@@ -0,0 +1,7 @@
1
+ import { ValueNode } from 'graphql';
2
+ type VariablePaths = Record<string, Array<string | number>>;
3
+ export declare function extractVariables(inputValue: ValueNode): {
4
+ inputValue: ValueNode;
5
+ variablePaths: VariablePaths;
6
+ };
7
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { ValueNode } from 'graphql';
2
- declare type VariablePaths = Record<string, Array<string | number>>;
2
+ type VariablePaths = Record<string, Array<string | number>>;
3
3
  export declare function extractVariables(inputValue: ValueNode): {
4
4
  inputValue: ValueNode;
5
5
  variablePaths: VariablePaths;
@@ -0,0 +1,2 @@
1
+ import { StitchingDirectivesResult } from './stitchingDirectives.cjs';
2
+ export declare function federationToStitchingSDL(federationSDL: string, stitchingConfig?: StitchingDirectivesResult): string;
@@ -1,2 +1,2 @@
1
- import { StitchingDirectivesResult } from './stitchingDirectives';
1
+ import { StitchingDirectivesResult } from './stitchingDirectives.js';
2
2
  export declare function federationToStitchingSDL(federationSDL: string, stitchingConfig?: StitchingDirectivesResult): string;
@@ -0,0 +1,3 @@
1
+ import { SelectionSetNode } from 'graphql';
2
+ import { MappingInstruction } from './types.cjs';
3
+ export declare function getSourcePaths(mappingInstructions: Array<MappingInstruction>, selectionSet?: SelectionSetNode): Array<Array<string>>;
@@ -1,3 +1,3 @@
1
1
  import { SelectionSetNode } from 'graphql';
2
- import { MappingInstruction } from './types';
2
+ import { MappingInstruction } from './types.js';
3
3
  export declare function getSourcePaths(mappingInstructions: Array<MappingInstruction>, selectionSet?: SelectionSetNode): Array<Array<string>>;