@graphql-tools/stitching-directives 3.1.13-alpha-20241113142246-527bbf8c4e20af3d172439ab4746fe676b3a61c5 → 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
package/dist/index.js ADDED
@@ -0,0 +1,1193 @@
1
+ import { visit, Kind, TypeNameMetaFieldDef, parseValue, valueFromASTUntyped, getNamedType, isInterfaceType, isUnionType, isObjectType, print, getNullableType, isListType, isNamedType, isAbstractType, GraphQLDirective, GraphQLNonNull, GraphQLString, GraphQLList, parse } from 'graphql';
2
+ import { cloneSubschemaConfig } from '@graphql-tools/delegate';
3
+ import { mapSchema, MapperKind, getDirective, parseSelectionSet, getImplementingTypes, mergeDeep, isSome } from '@graphql-tools/utils';
4
+
5
+ const defaultStitchingDirectiveOptions = {
6
+ keyDirectiveName: "key",
7
+ computedDirectiveName: "computed",
8
+ canonicalDirectiveName: "canonical",
9
+ mergeDirectiveName: "merge",
10
+ pathToDirectivesInExtensions: ["directives"]
11
+ };
12
+
13
+ function extractVariables(inputValue) {
14
+ const path = [];
15
+ const variablePaths = /* @__PURE__ */ Object.create(null);
16
+ const keyPathVisitor = {
17
+ enter: (_node, key) => {
18
+ if (typeof key === "number") {
19
+ path.push(key);
20
+ }
21
+ },
22
+ leave: (_node, key) => {
23
+ if (typeof key === "number") {
24
+ path.pop();
25
+ }
26
+ }
27
+ };
28
+ const fieldPathVisitor = {
29
+ enter: (node) => {
30
+ path.push(node.name.value);
31
+ },
32
+ leave: () => {
33
+ path.pop();
34
+ }
35
+ };
36
+ const variableVisitor = {
37
+ enter: (node, key) => {
38
+ if (typeof key === "number") {
39
+ variablePaths[node.name.value] = path.concat([key]);
40
+ } else {
41
+ variablePaths[node.name.value] = path.slice();
42
+ }
43
+ return {
44
+ kind: Kind.NULL
45
+ };
46
+ }
47
+ };
48
+ const newInputValue = visit(inputValue, {
49
+ [Kind.OBJECT]: keyPathVisitor,
50
+ [Kind.LIST]: keyPathVisitor,
51
+ [Kind.OBJECT_FIELD]: fieldPathVisitor,
52
+ [Kind.VARIABLE]: variableVisitor
53
+ });
54
+ return {
55
+ inputValue: newInputValue,
56
+ variablePaths
57
+ };
58
+ }
59
+
60
+ function pathsFromSelectionSet(selectionSet, path = []) {
61
+ const paths = [];
62
+ for (const selection of selectionSet.selections) {
63
+ const additions = pathsFromSelection(selection, path) ?? [];
64
+ for (const addition of additions) {
65
+ paths.push(addition);
66
+ }
67
+ }
68
+ return paths;
69
+ }
70
+ function pathsFromSelection(selection, path) {
71
+ if (selection.kind === Kind.FIELD) {
72
+ const responseKey = selection.alias?.value ?? selection.name.value;
73
+ if (selection.selectionSet) {
74
+ return pathsFromSelectionSet(
75
+ selection.selectionSet,
76
+ path.concat([responseKey])
77
+ );
78
+ } else {
79
+ return [path.concat([responseKey])];
80
+ }
81
+ } else if (selection.kind === Kind.INLINE_FRAGMENT) {
82
+ return pathsFromSelectionSet(selection.selectionSet, path);
83
+ }
84
+ return void 0;
85
+ }
86
+
87
+ function getSourcePaths(mappingInstructions, selectionSet) {
88
+ const sourcePaths = [];
89
+ for (const mappingInstruction of mappingInstructions) {
90
+ const { sourcePath } = mappingInstruction;
91
+ if (sourcePath.length) {
92
+ sourcePaths.push(sourcePath);
93
+ continue;
94
+ }
95
+ if (selectionSet == null) {
96
+ continue;
97
+ }
98
+ const paths = pathsFromSelectionSet(selectionSet);
99
+ for (const path of paths) {
100
+ sourcePaths.push(path);
101
+ }
102
+ sourcePaths.push([TypeNameMetaFieldDef.name]);
103
+ }
104
+ return sourcePaths;
105
+ }
106
+
107
+ const KEY_DELIMITER = "__dot__";
108
+ const EXPANSION_PREFIX = "__exp";
109
+ function preparseMergeArgsExpr(mergeArgsExpr) {
110
+ const variableRegex = /\$[_A-Za-z][_A-Za-z0-9.]*/g;
111
+ const dotRegex = /\./g;
112
+ mergeArgsExpr = mergeArgsExpr.replace(
113
+ variableRegex,
114
+ (variable) => variable.replace(dotRegex, KEY_DELIMITER)
115
+ );
116
+ const segments = mergeArgsExpr.split("[[");
117
+ const expansionExpressions = /* @__PURE__ */ Object.create(null);
118
+ if (segments.length === 1) {
119
+ return { mergeArgsExpr, expansionExpressions };
120
+ }
121
+ let finalSegments = [segments[0]];
122
+ for (let i = 1; i < segments.length; i++) {
123
+ const additionalSegments = segments[i]?.split("]]");
124
+ if (additionalSegments?.length !== 2) {
125
+ throw new Error(
126
+ `Each opening "[[" must be matched by a closing "]]" without nesting.`
127
+ );
128
+ }
129
+ finalSegments = finalSegments.concat(additionalSegments);
130
+ }
131
+ let finalMergeArgsExpr = finalSegments[0];
132
+ for (let i = 1; i < finalSegments.length - 1; i += 2) {
133
+ const variableName = `${EXPANSION_PREFIX}${(i - 1) / 2 + 1}`;
134
+ expansionExpressions[variableName] = finalSegments[i];
135
+ finalMergeArgsExpr += `$${variableName}${finalSegments[i + 1]}`;
136
+ }
137
+ return { mergeArgsExpr: finalMergeArgsExpr, expansionExpressions };
138
+ }
139
+
140
+ function addProperty(object, path, value) {
141
+ const initialSegment = path[0];
142
+ if (path.length === 1) {
143
+ object[initialSegment] = value;
144
+ return;
145
+ }
146
+ let field = object[initialSegment];
147
+ if (field != null) {
148
+ addProperty(field, path.slice(1), value);
149
+ return;
150
+ }
151
+ if (typeof path[1] === "string") {
152
+ field = /* @__PURE__ */ Object.create(null);
153
+ } else {
154
+ field = [];
155
+ }
156
+ addProperty(field, path.slice(1), value);
157
+ object[initialSegment] = field;
158
+ }
159
+ function getProperty(object, path) {
160
+ if (!path.length || object == null) {
161
+ return object;
162
+ }
163
+ const newPath = path.slice();
164
+ const key = newPath.shift();
165
+ if (key == null) {
166
+ return;
167
+ }
168
+ const prop = object[key];
169
+ return getProperty(prop, newPath);
170
+ }
171
+ function getProperties(object, propertyTree) {
172
+ if (object == null) {
173
+ return object;
174
+ }
175
+ const newObject = /* @__PURE__ */ Object.create(null);
176
+ for (const key in propertyTree) {
177
+ const subKey = propertyTree[key];
178
+ if (subKey == null) {
179
+ newObject[key] = object[key];
180
+ continue;
181
+ }
182
+ const prop = object[key];
183
+ newObject[key] = deepMap(prop, function deepMapFn(item) {
184
+ return getProperties(item, subKey);
185
+ });
186
+ }
187
+ return newObject;
188
+ }
189
+ function propertyTreeFromPaths(paths) {
190
+ const propertyTree = /* @__PURE__ */ Object.create(null);
191
+ for (const path of paths) {
192
+ addProperty(propertyTree, path, null);
193
+ }
194
+ return propertyTree;
195
+ }
196
+ function deepMap(arrayOrItem, fn) {
197
+ if (Array.isArray(arrayOrItem)) {
198
+ return arrayOrItem.map(
199
+ (nestedArrayOrItem) => deepMap(nestedArrayOrItem, fn)
200
+ );
201
+ }
202
+ return fn(arrayOrItem);
203
+ }
204
+
205
+ function parseMergeArgsExpr(mergeArgsExpr, selectionSet) {
206
+ const { mergeArgsExpr: newMergeArgsExpr, expansionExpressions } = preparseMergeArgsExpr(mergeArgsExpr);
207
+ const inputValue = parseValue(`{ ${newMergeArgsExpr} }`, {
208
+ noLocation: true
209
+ });
210
+ const { inputValue: newInputValue, variablePaths } = extractVariables(inputValue);
211
+ if (!Object.keys(expansionExpressions).length) {
212
+ if (!Object.keys(variablePaths).length) {
213
+ throw new Error("Merge arguments must declare a key.");
214
+ }
215
+ const mappingInstructions = getMappingInstructions(variablePaths);
216
+ const usedProperties2 = propertyTreeFromPaths(
217
+ getSourcePaths(mappingInstructions, selectionSet)
218
+ );
219
+ return {
220
+ args: valueFromASTUntyped(newInputValue),
221
+ usedProperties: usedProperties2,
222
+ mappingInstructions
223
+ };
224
+ }
225
+ const expansionRegEx = new RegExp(`^${EXPANSION_PREFIX}[0-9]+$`);
226
+ for (const variableName in variablePaths) {
227
+ if (!variableName.match(expansionRegEx)) {
228
+ throw new Error(
229
+ "Expansions cannot be mixed with single key declarations."
230
+ );
231
+ }
232
+ }
233
+ const expansions = [];
234
+ const sourcePaths = [];
235
+ for (const variableName in expansionExpressions) {
236
+ const str = expansionExpressions[variableName];
237
+ const valuePath = variablePaths[variableName];
238
+ const {
239
+ inputValue: expansionInputValue,
240
+ variablePaths: expansionVariablePaths
241
+ } = extractVariables(parseValue(`${str}`, { noLocation: true }));
242
+ if (!Object.keys(expansionVariablePaths).length) {
243
+ throw new Error("Merge arguments must declare a key.");
244
+ }
245
+ const mappingInstructions = getMappingInstructions(expansionVariablePaths);
246
+ const value = valueFromASTUntyped(expansionInputValue);
247
+ sourcePaths.push(...getSourcePaths(mappingInstructions, selectionSet));
248
+ assertNotWithinList(valuePath);
249
+ expansions.push({
250
+ valuePath,
251
+ value,
252
+ mappingInstructions
253
+ });
254
+ }
255
+ const usedProperties = propertyTreeFromPaths(sourcePaths);
256
+ return {
257
+ args: valueFromASTUntyped(newInputValue),
258
+ usedProperties,
259
+ expansions
260
+ };
261
+ }
262
+ function getMappingInstructions(variablePaths) {
263
+ const mappingInstructions = [];
264
+ for (const keyPath in variablePaths) {
265
+ const valuePath = variablePaths[keyPath];
266
+ const splitKeyPath = keyPath.split(KEY_DELIMITER).slice(1);
267
+ assertNotWithinList(valuePath);
268
+ mappingInstructions.push({
269
+ destinationPath: valuePath,
270
+ sourcePath: splitKeyPath
271
+ });
272
+ }
273
+ return mappingInstructions;
274
+ }
275
+ function assertNotWithinList(path) {
276
+ for (const pathSegment of path) {
277
+ if (typeof pathSegment === "number") {
278
+ throw new Error("Insertions cannot be made into a list.");
279
+ }
280
+ }
281
+ }
282
+
283
+ function stitchingDirectivesTransformer(options = {}) {
284
+ const {
285
+ keyDirectiveName,
286
+ computedDirectiveName,
287
+ mergeDirectiveName,
288
+ canonicalDirectiveName,
289
+ pathToDirectivesInExtensions
290
+ } = {
291
+ ...defaultStitchingDirectiveOptions,
292
+ ...options
293
+ };
294
+ return (subschemaConfig) => {
295
+ const newSubschemaConfig = cloneSubschemaConfig(subschemaConfig);
296
+ const selectionSetsByType = /* @__PURE__ */ Object.create(null);
297
+ const computedFieldSelectionSets = /* @__PURE__ */ Object.create(null);
298
+ const mergedTypesResolversInfo = /* @__PURE__ */ Object.create(null);
299
+ const canonicalTypesInfo = /* @__PURE__ */ Object.create(null);
300
+ const selectionSetsByTypeAndEntryField = /* @__PURE__ */ Object.create(null);
301
+ const mergedTypesResolversInfoByEntryField = /* @__PURE__ */ Object.create(null);
302
+ const schema = subschemaConfig.schema;
303
+ function setCanonicalDefinition(typeName, fieldName) {
304
+ canonicalTypesInfo[typeName] = canonicalTypesInfo[typeName] || /* @__PURE__ */ Object.create(null);
305
+ if (fieldName) {
306
+ const fields = canonicalTypesInfo[typeName]?.fields ?? /* @__PURE__ */ Object.create(null);
307
+ canonicalTypesInfo[typeName].fields = fields;
308
+ fields[fieldName] = true;
309
+ } else {
310
+ canonicalTypesInfo[typeName].canonical = true;
311
+ }
312
+ }
313
+ mapSchema(schema, {
314
+ [MapperKind.OBJECT_TYPE]: (type) => {
315
+ const keyDirective = getDirective(
316
+ schema,
317
+ type,
318
+ keyDirectiveName,
319
+ pathToDirectivesInExtensions
320
+ )?.[0];
321
+ if (keyDirective != null) {
322
+ const selectionSet = parseSelectionSet(keyDirective["selectionSet"], {
323
+ noLocation: true
324
+ });
325
+ selectionSetsByType[type.name] = selectionSet;
326
+ }
327
+ const canonicalDirective = getDirective(
328
+ schema,
329
+ type,
330
+ canonicalDirectiveName,
331
+ pathToDirectivesInExtensions
332
+ )?.[0];
333
+ if (canonicalDirective != null) {
334
+ setCanonicalDefinition(type.name);
335
+ }
336
+ return void 0;
337
+ },
338
+ [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => {
339
+ const computedDirective = getDirective(
340
+ schema,
341
+ fieldConfig,
342
+ computedDirectiveName,
343
+ pathToDirectivesInExtensions
344
+ )?.[0];
345
+ if (computedDirective != null) {
346
+ const selectionSet = parseSelectionSet(
347
+ computedDirective["selectionSet"],
348
+ {
349
+ noLocation: true
350
+ }
351
+ );
352
+ if (!computedFieldSelectionSets[typeName]) {
353
+ computedFieldSelectionSets[typeName] = /* @__PURE__ */ Object.create(null);
354
+ }
355
+ computedFieldSelectionSets[typeName][fieldName] = selectionSet;
356
+ }
357
+ const mergeDirective = getDirective(
358
+ schema,
359
+ fieldConfig,
360
+ mergeDirectiveName,
361
+ pathToDirectivesInExtensions
362
+ )?.[0];
363
+ if (mergeDirective?.["keyField"] != null) {
364
+ const mergeDirectiveKeyField = mergeDirective["keyField"];
365
+ const selectionSet = parseSelectionSet(
366
+ `{ ${mergeDirectiveKeyField}}`,
367
+ {
368
+ noLocation: true
369
+ }
370
+ );
371
+ const typeNames = mergeDirective["types"];
372
+ const returnType = getNamedType(fieldConfig.type);
373
+ forEachConcreteType(schema, returnType, typeNames, (typeName2) => {
374
+ if (typeNames == null || typeNames.includes(typeName2)) {
375
+ let existingEntryFieldMap = selectionSetsByTypeAndEntryField[typeName2];
376
+ if (existingEntryFieldMap == null) {
377
+ existingEntryFieldMap = /* @__PURE__ */ Object.create(null);
378
+ selectionSetsByTypeAndEntryField[typeName2] = existingEntryFieldMap;
379
+ }
380
+ let existingSelectionSet = existingEntryFieldMap[fieldName];
381
+ if (existingSelectionSet == null) {
382
+ existingSelectionSet = selectionSetsByType[typeName2];
383
+ }
384
+ existingEntryFieldMap[fieldName] = existingSelectionSet ? mergeSelectionSets(existingSelectionSet, selectionSet) : selectionSet;
385
+ }
386
+ });
387
+ }
388
+ const canonicalDirective = getDirective(
389
+ schema,
390
+ fieldConfig,
391
+ canonicalDirectiveName,
392
+ pathToDirectivesInExtensions
393
+ )?.[0];
394
+ if (canonicalDirective != null) {
395
+ setCanonicalDefinition(typeName, fieldName);
396
+ }
397
+ return void 0;
398
+ },
399
+ [MapperKind.INTERFACE_TYPE]: (type) => {
400
+ const canonicalDirective = getDirective(
401
+ schema,
402
+ type,
403
+ canonicalDirectiveName,
404
+ pathToDirectivesInExtensions
405
+ )?.[0];
406
+ if (canonicalDirective) {
407
+ setCanonicalDefinition(type.name);
408
+ }
409
+ return void 0;
410
+ },
411
+ [MapperKind.INTERFACE_FIELD]: (fieldConfig, fieldName, typeName) => {
412
+ const canonicalDirective = getDirective(
413
+ schema,
414
+ fieldConfig,
415
+ canonicalDirectiveName,
416
+ pathToDirectivesInExtensions
417
+ )?.[0];
418
+ if (canonicalDirective) {
419
+ setCanonicalDefinition(typeName, fieldName);
420
+ }
421
+ return void 0;
422
+ },
423
+ [MapperKind.INPUT_OBJECT_TYPE]: (type) => {
424
+ const canonicalDirective = getDirective(
425
+ schema,
426
+ type,
427
+ canonicalDirectiveName,
428
+ pathToDirectivesInExtensions
429
+ )?.[0];
430
+ if (canonicalDirective) {
431
+ setCanonicalDefinition(type.name);
432
+ }
433
+ return void 0;
434
+ },
435
+ [MapperKind.INPUT_OBJECT_FIELD]: (inputFieldConfig, fieldName, typeName) => {
436
+ const canonicalDirective = getDirective(
437
+ schema,
438
+ inputFieldConfig,
439
+ canonicalDirectiveName,
440
+ pathToDirectivesInExtensions
441
+ )?.[0];
442
+ if (canonicalDirective != null) {
443
+ setCanonicalDefinition(typeName, fieldName);
444
+ }
445
+ return void 0;
446
+ },
447
+ [MapperKind.UNION_TYPE]: (type) => {
448
+ const canonicalDirective = getDirective(
449
+ schema,
450
+ type,
451
+ canonicalDirectiveName,
452
+ pathToDirectivesInExtensions
453
+ )?.[0];
454
+ if (canonicalDirective != null) {
455
+ setCanonicalDefinition(type.name);
456
+ }
457
+ return void 0;
458
+ },
459
+ [MapperKind.ENUM_TYPE]: (type) => {
460
+ const canonicalDirective = getDirective(
461
+ schema,
462
+ type,
463
+ canonicalDirectiveName,
464
+ pathToDirectivesInExtensions
465
+ )?.[0];
466
+ if (canonicalDirective != null) {
467
+ setCanonicalDefinition(type.name);
468
+ }
469
+ return void 0;
470
+ },
471
+ [MapperKind.SCALAR_TYPE]: (type) => {
472
+ const canonicalDirective = getDirective(
473
+ schema,
474
+ type,
475
+ canonicalDirectiveName,
476
+ pathToDirectivesInExtensions
477
+ )?.[0];
478
+ if (canonicalDirective != null) {
479
+ setCanonicalDefinition(type.name);
480
+ }
481
+ return void 0;
482
+ }
483
+ });
484
+ if (subschemaConfig.merge) {
485
+ for (const typeName in subschemaConfig.merge) {
486
+ const mergedTypeConfig = subschemaConfig.merge[typeName];
487
+ if (mergedTypeConfig?.selectionSet) {
488
+ const selectionSet = parseSelectionSet(
489
+ mergedTypeConfig.selectionSet,
490
+ {
491
+ noLocation: true
492
+ }
493
+ );
494
+ if (selectionSet) {
495
+ if (selectionSetsByType[typeName]) {
496
+ selectionSetsByType[typeName] = mergeSelectionSets(
497
+ selectionSetsByType[typeName],
498
+ selectionSet
499
+ );
500
+ } else {
501
+ selectionSetsByType[typeName] = selectionSet;
502
+ }
503
+ }
504
+ }
505
+ if (mergedTypeConfig?.fields) {
506
+ for (const fieldName in mergedTypeConfig.fields) {
507
+ const fieldConfig = mergedTypeConfig.fields[fieldName];
508
+ if (!fieldConfig?.selectionSet) continue;
509
+ const selectionSet = parseSelectionSet(fieldConfig.selectionSet, {
510
+ noLocation: true
511
+ });
512
+ if (selectionSet) {
513
+ if (computedFieldSelectionSets[typeName]?.[fieldName]) {
514
+ computedFieldSelectionSets[typeName][fieldName] = mergeSelectionSets(
515
+ computedFieldSelectionSets[typeName][fieldName],
516
+ selectionSet
517
+ );
518
+ } else {
519
+ if (computedFieldSelectionSets[typeName] == null) {
520
+ computedFieldSelectionSets[typeName] = /* @__PURE__ */ Object.create(null);
521
+ }
522
+ computedFieldSelectionSets[typeName][fieldName] = selectionSet;
523
+ }
524
+ }
525
+ }
526
+ }
527
+ }
528
+ }
529
+ const allSelectionSetsByType = /* @__PURE__ */ Object.create(null);
530
+ for (const typeName in selectionSetsByType) {
531
+ allSelectionSetsByType[typeName] = allSelectionSetsByType[typeName] || [];
532
+ const selectionSet = selectionSetsByType[typeName];
533
+ allSelectionSetsByType[typeName].push(selectionSet);
534
+ }
535
+ for (const typeName in computedFieldSelectionSets) {
536
+ const selectionSets = computedFieldSelectionSets[typeName];
537
+ for (const i in selectionSets) {
538
+ allSelectionSetsByType[typeName] = allSelectionSetsByType[typeName] || [];
539
+ const selectionSet = selectionSets[i];
540
+ allSelectionSetsByType[typeName].push(selectionSet);
541
+ }
542
+ }
543
+ mapSchema(schema, {
544
+ [MapperKind.OBJECT_FIELD]: function objectFieldMapper(fieldConfig, fieldName) {
545
+ const mergeDirective = getDirective(
546
+ schema,
547
+ fieldConfig,
548
+ mergeDirectiveName,
549
+ pathToDirectivesInExtensions
550
+ )?.[0];
551
+ if (mergeDirective != null) {
552
+ const returnType = getNullableType(fieldConfig.type);
553
+ const returnsList = isListType(returnType);
554
+ const namedType = getNamedType(returnType);
555
+ let mergeArgsExpr = mergeDirective["argsExpr"];
556
+ if (mergeArgsExpr == null) {
557
+ const key = mergeDirective["key"];
558
+ const keyField = mergeDirective["keyField"];
559
+ const keyExpr = key != null ? buildKeyExpr(key) : keyField != null ? `$key.${keyField}` : "$key";
560
+ const keyArg = mergeDirective["keyArg"];
561
+ const argNames = keyArg == null ? [Object.keys(fieldConfig.args ?? {})[0]] : keyArg.split(".");
562
+ const lastArgName = argNames.pop();
563
+ mergeArgsExpr = returnsList ? `${lastArgName}: [[${keyExpr}]]` : `${lastArgName}: ${keyExpr}`;
564
+ for (const argName of argNames.reverse()) {
565
+ mergeArgsExpr = `${argName}: { ${mergeArgsExpr} }`;
566
+ }
567
+ }
568
+ const typeNames = mergeDirective["types"];
569
+ forEachConcreteTypeName(
570
+ namedType,
571
+ schema,
572
+ typeNames,
573
+ function generateResolveInfo(typeName) {
574
+ const mergedSelectionSets = [];
575
+ if (allSelectionSetsByType[typeName]) {
576
+ mergedSelectionSets.push(...allSelectionSetsByType[typeName]);
577
+ }
578
+ if (selectionSetsByTypeAndEntryField[typeName]?.[fieldName]) {
579
+ mergedSelectionSets.push(
580
+ selectionSetsByTypeAndEntryField[typeName][fieldName]
581
+ );
582
+ }
583
+ const parsedMergeArgsExpr = parseMergeArgsExpr(
584
+ mergeArgsExpr,
585
+ allSelectionSetsByType[typeName] == null ? void 0 : mergeSelectionSets(...mergedSelectionSets)
586
+ );
587
+ const additionalArgs = mergeDirective["additionalArgs"];
588
+ if (additionalArgs != null) {
589
+ parsedMergeArgsExpr.args = mergeDeep([
590
+ parsedMergeArgsExpr.args,
591
+ valueFromASTUntyped(
592
+ parseValue(`{ ${additionalArgs} }`, { noLocation: true })
593
+ )
594
+ ]);
595
+ }
596
+ if (selectionSetsByTypeAndEntryField[typeName]?.[fieldName] != null) {
597
+ const typeConfigByField = mergedTypesResolversInfoByEntryField[typeName] ||= /* @__PURE__ */ Object.create(null);
598
+ typeConfigByField[fieldName] = {
599
+ fieldName,
600
+ returnsList,
601
+ ...parsedMergeArgsExpr
602
+ };
603
+ } else {
604
+ mergedTypesResolversInfo[typeName] = {
605
+ fieldName,
606
+ returnsList,
607
+ ...parsedMergeArgsExpr
608
+ };
609
+ }
610
+ }
611
+ );
612
+ }
613
+ return void 0;
614
+ }
615
+ });
616
+ for (const typeName in selectionSetsByType) {
617
+ const selectionSet = selectionSetsByType[typeName];
618
+ const mergeConfig = newSubschemaConfig.merge ?? /* @__PURE__ */ Object.create(null);
619
+ newSubschemaConfig.merge = mergeConfig;
620
+ if (mergeConfig[typeName] == null) {
621
+ newSubschemaConfig.merge[typeName] = /* @__PURE__ */ Object.create(null);
622
+ }
623
+ const mergeTypeConfig = mergeConfig[typeName];
624
+ mergeTypeConfig.selectionSet = print(selectionSet);
625
+ }
626
+ for (const typeName in computedFieldSelectionSets) {
627
+ const selectionSets = computedFieldSelectionSets[typeName];
628
+ const mergeConfig = newSubschemaConfig.merge ?? /* @__PURE__ */ Object.create(null);
629
+ newSubschemaConfig.merge = mergeConfig;
630
+ if (mergeConfig[typeName] == null) {
631
+ mergeConfig[typeName] = /* @__PURE__ */ Object.create(null);
632
+ }
633
+ const mergeTypeConfig = newSubschemaConfig.merge[typeName];
634
+ const mergeTypeConfigFields = mergeTypeConfig.fields ?? /* @__PURE__ */ Object.create(null);
635
+ mergeTypeConfig.fields = mergeTypeConfigFields;
636
+ for (const fieldName in selectionSets) {
637
+ const selectionSet = selectionSets[fieldName];
638
+ const fieldConfig = mergeTypeConfigFields[fieldName] ?? /* @__PURE__ */ Object.create(null);
639
+ mergeTypeConfigFields[fieldName] = fieldConfig;
640
+ fieldConfig.selectionSet = print(selectionSet);
641
+ fieldConfig.computed = true;
642
+ }
643
+ }
644
+ for (const typeName in mergedTypesResolversInfo) {
645
+ const mergedTypeResolverInfo = mergedTypesResolversInfo[typeName];
646
+ const mergeConfig = newSubschemaConfig.merge ?? /* @__PURE__ */ Object.create(null);
647
+ newSubschemaConfig.merge = mergeConfig;
648
+ if (newSubschemaConfig.merge[typeName] == null) {
649
+ newSubschemaConfig.merge[typeName] = /* @__PURE__ */ Object.create(null);
650
+ }
651
+ const mergeTypeConfig = newSubschemaConfig.merge[typeName];
652
+ mergeTypeConfig.fieldName = mergedTypeResolverInfo.fieldName;
653
+ if (mergedTypeResolverInfo.returnsList) {
654
+ mergeTypeConfig.key = generateKeyFn(mergedTypeResolverInfo);
655
+ mergeTypeConfig.argsFromKeys = generateArgsFromKeysFn(
656
+ mergedTypeResolverInfo
657
+ );
658
+ } else {
659
+ mergeTypeConfig.args = generateArgsFn(mergedTypeResolverInfo);
660
+ }
661
+ }
662
+ for (const typeName in canonicalTypesInfo) {
663
+ const canonicalTypeInfo = canonicalTypesInfo[typeName];
664
+ const mergeConfig = newSubschemaConfig.merge ?? /* @__PURE__ */ Object.create(null);
665
+ newSubschemaConfig.merge = mergeConfig;
666
+ if (newSubschemaConfig.merge[typeName] == null) {
667
+ newSubschemaConfig.merge[typeName] = /* @__PURE__ */ Object.create(null);
668
+ }
669
+ const mergeTypeConfig = newSubschemaConfig.merge[typeName];
670
+ if (canonicalTypeInfo.canonical) {
671
+ mergeTypeConfig.canonical = true;
672
+ }
673
+ if (canonicalTypeInfo.fields) {
674
+ const mergeTypeConfigFields = mergeTypeConfig.fields ?? /* @__PURE__ */ Object.create(null);
675
+ mergeTypeConfig.fields = mergeTypeConfigFields;
676
+ for (const fieldName in canonicalTypeInfo.fields) {
677
+ if (mergeTypeConfigFields[fieldName] == null) {
678
+ mergeTypeConfigFields[fieldName] = /* @__PURE__ */ Object.create(null);
679
+ }
680
+ mergeTypeConfigFields[fieldName].canonical = true;
681
+ }
682
+ }
683
+ }
684
+ for (const typeName in mergedTypesResolversInfoByEntryField) {
685
+ const entryPoints = [];
686
+ const existingMergeConfig = newSubschemaConfig.merge?.[typeName];
687
+ const newMergeConfig = newSubschemaConfig.merge ||= /* @__PURE__ */ Object.create(null);
688
+ if (existingMergeConfig) {
689
+ const { fields, canonical, ...baseEntryPoint } = existingMergeConfig;
690
+ newMergeConfig[typeName] = {
691
+ fields,
692
+ canonical,
693
+ entryPoints
694
+ };
695
+ entryPoints.push(baseEntryPoint);
696
+ } else {
697
+ newMergeConfig[typeName] = {
698
+ entryPoints
699
+ };
700
+ }
701
+ for (const fieldName in mergedTypesResolversInfoByEntryField[typeName]) {
702
+ const mergedTypeResolverInfo = mergedTypesResolversInfoByEntryField[typeName][fieldName];
703
+ const newEntryPoint = {
704
+ fieldName,
705
+ selectionSet: print(
706
+ selectionSetsByTypeAndEntryField[typeName][fieldName]
707
+ )
708
+ };
709
+ if (mergedTypeResolverInfo.returnsList) {
710
+ newEntryPoint.key = generateKeyFn(mergedTypeResolverInfo);
711
+ newEntryPoint.argsFromKeys = generateArgsFromKeysFn(
712
+ mergedTypeResolverInfo
713
+ );
714
+ } else {
715
+ newEntryPoint.args = generateArgsFn(mergedTypeResolverInfo);
716
+ }
717
+ entryPoints.push(newEntryPoint);
718
+ }
719
+ if (entryPoints.length === 1) {
720
+ const [entryPoint] = entryPoints;
721
+ const { fields, canonical } = newMergeConfig[typeName];
722
+ newMergeConfig[typeName] = {
723
+ ...entryPoint,
724
+ fields,
725
+ canonical
726
+ };
727
+ }
728
+ }
729
+ return newSubschemaConfig;
730
+ };
731
+ }
732
+ function forEachConcreteType(schema, type, typeNames, fn) {
733
+ if (isInterfaceType(type)) {
734
+ for (const typeName of getImplementingTypes(type.name, schema)) {
735
+ if (typeNames == null || typeNames.includes(typeName)) {
736
+ fn(typeName);
737
+ }
738
+ }
739
+ } else if (isUnionType(type)) {
740
+ for (const { name: typeName } of type.getTypes()) {
741
+ if (typeNames == null || typeNames.includes(typeName)) {
742
+ fn(typeName);
743
+ }
744
+ }
745
+ } else if (isObjectType(type)) {
746
+ fn(type.name);
747
+ }
748
+ }
749
+ function generateKeyFn(mergedTypeResolverInfo) {
750
+ return function keyFn(originalResult) {
751
+ return getProperties(originalResult, mergedTypeResolverInfo.usedProperties);
752
+ };
753
+ }
754
+ function generateArgsFromKeysFn(mergedTypeResolverInfo) {
755
+ const { expansions, args } = mergedTypeResolverInfo;
756
+ return function generateArgsFromKeys(keys) {
757
+ const newArgs = mergeDeep([{}, args]);
758
+ if (expansions) {
759
+ for (const expansion of expansions) {
760
+ const mappingInstructions = expansion.mappingInstructions;
761
+ const expanded = [];
762
+ for (const key of keys) {
763
+ let newValue = mergeDeep([{}, expansion.valuePath]);
764
+ for (const { destinationPath, sourcePath } of mappingInstructions) {
765
+ if (destinationPath.length) {
766
+ addProperty(
767
+ newValue,
768
+ destinationPath,
769
+ getProperty(key, sourcePath)
770
+ );
771
+ } else {
772
+ newValue = getProperty(key, sourcePath);
773
+ }
774
+ }
775
+ expanded.push(newValue);
776
+ }
777
+ addProperty(newArgs, expansion.valuePath, expanded);
778
+ }
779
+ }
780
+ return newArgs;
781
+ };
782
+ }
783
+ function generateArgsFn(mergedTypeResolverInfo) {
784
+ const { mappingInstructions, args, usedProperties } = mergedTypeResolverInfo;
785
+ return function generateArgs(originalResult) {
786
+ const newArgs = mergeDeep([{}, args]);
787
+ const filteredResult = getProperties(originalResult, usedProperties);
788
+ if (mappingInstructions) {
789
+ for (const mappingInstruction of mappingInstructions) {
790
+ const { destinationPath, sourcePath } = mappingInstruction;
791
+ addProperty(
792
+ newArgs,
793
+ destinationPath,
794
+ getProperty(filteredResult, sourcePath)
795
+ );
796
+ }
797
+ }
798
+ return newArgs;
799
+ };
800
+ }
801
+ function buildKeyExpr(key) {
802
+ let mergedObject = {};
803
+ for (const keyDef of key) {
804
+ let [aliasOrKeyPath, keyPath] = keyDef.split(":");
805
+ let aliasPath;
806
+ if (keyPath == null) {
807
+ keyPath = aliasPath = aliasOrKeyPath;
808
+ } else {
809
+ aliasPath = aliasOrKeyPath;
810
+ }
811
+ const aliasParts = aliasPath.split(".");
812
+ const lastAliasPart = aliasParts.pop();
813
+ if (lastAliasPart == null) {
814
+ throw new Error(`Key "${key}" is invalid, no path provided.`);
815
+ }
816
+ let object = {
817
+ [lastAliasPart]: `$key.${keyPath}`
818
+ };
819
+ for (const aliasPart of aliasParts.reverse()) {
820
+ object = { [aliasPart]: object };
821
+ }
822
+ mergedObject = mergeDeep([mergedObject, object]);
823
+ }
824
+ return JSON.stringify(mergedObject).replace(/"/g, "");
825
+ }
826
+ function mergeSelectionSets(...selectionSets) {
827
+ const normalizedSelections = /* @__PURE__ */ Object.create(null);
828
+ for (const selectionSet of selectionSets) {
829
+ for (const selection of selectionSet.selections) {
830
+ const normalizedSelection = print(selection);
831
+ normalizedSelections[normalizedSelection] = selection;
832
+ }
833
+ }
834
+ const newSelectionSet = {
835
+ kind: Kind.SELECTION_SET,
836
+ selections: Object.values(normalizedSelections)
837
+ };
838
+ return newSelectionSet;
839
+ }
840
+ function forEachConcreteTypeName(returnType, schema, typeNames, fn) {
841
+ if (isInterfaceType(returnType)) {
842
+ for (const typeName of getImplementingTypes(returnType.name, schema)) {
843
+ if (typeNames == null || typeNames.includes(typeName)) {
844
+ fn(typeName);
845
+ }
846
+ }
847
+ } else if (isUnionType(returnType)) {
848
+ for (const type of returnType.getTypes()) {
849
+ if (typeNames == null || typeNames.includes(type.name)) {
850
+ fn(type.name);
851
+ }
852
+ }
853
+ } else if (isObjectType(returnType) && (typeNames == null || typeNames.includes(returnType.name))) {
854
+ fn(returnType.name);
855
+ }
856
+ }
857
+
858
+ const dottedNameRegEx = /^[_A-Za-z][_0-9A-Za-z]*(.[_A-Za-z][_0-9A-Za-z]*)*$/;
859
+ function stitchingDirectivesValidator(options = {}) {
860
+ const {
861
+ keyDirectiveName,
862
+ computedDirectiveName,
863
+ mergeDirectiveName,
864
+ pathToDirectivesInExtensions
865
+ } = {
866
+ ...defaultStitchingDirectiveOptions,
867
+ ...options
868
+ };
869
+ return (schema) => {
870
+ const queryTypeName = schema.getQueryType()?.name;
871
+ mapSchema(schema, {
872
+ [MapperKind.OBJECT_TYPE]: (type) => {
873
+ const keyDirective = getDirective(
874
+ schema,
875
+ type,
876
+ keyDirectiveName,
877
+ pathToDirectivesInExtensions
878
+ )?.[0];
879
+ if (keyDirective != null) {
880
+ parseSelectionSet(keyDirective["selectionSet"]);
881
+ }
882
+ return void 0;
883
+ },
884
+ [MapperKind.OBJECT_FIELD]: (fieldConfig, _fieldName, typeName) => {
885
+ const computedDirective = getDirective(
886
+ schema,
887
+ fieldConfig,
888
+ computedDirectiveName,
889
+ pathToDirectivesInExtensions
890
+ )?.[0];
891
+ if (computedDirective != null) {
892
+ parseSelectionSet(computedDirective["selectionSet"]);
893
+ }
894
+ const mergeDirective = getDirective(
895
+ schema,
896
+ fieldConfig,
897
+ mergeDirectiveName,
898
+ pathToDirectivesInExtensions
899
+ )?.[0];
900
+ if (mergeDirective != null) {
901
+ if (typeName !== queryTypeName) {
902
+ throw new Error(
903
+ "@merge directive may be used only for root fields of the root Query type."
904
+ );
905
+ }
906
+ let returnType = getNullableType(fieldConfig.type);
907
+ if (isListType(returnType)) {
908
+ returnType = getNullableType(returnType.ofType);
909
+ }
910
+ if (!isNamedType(returnType)) {
911
+ throw new Error(
912
+ "@merge directive must be used on a field that returns an object or a list of objects."
913
+ );
914
+ }
915
+ const mergeArgsExpr = mergeDirective["argsExpr"];
916
+ if (mergeArgsExpr != null) {
917
+ parseMergeArgsExpr(mergeArgsExpr);
918
+ }
919
+ const args = Object.keys(fieldConfig.args ?? {});
920
+ const keyArg = mergeDirective["keyArg"];
921
+ if (keyArg == null) {
922
+ if (!mergeArgsExpr && args.length !== 1) {
923
+ throw new Error(
924
+ "Cannot use @merge directive without `keyArg` argument if resolver takes more than one argument."
925
+ );
926
+ }
927
+ } else if (!keyArg.match(dottedNameRegEx)) {
928
+ throw new Error(
929
+ "`keyArg` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods."
930
+ );
931
+ }
932
+ const keyField = mergeDirective["keyField"];
933
+ if (keyField != null && !keyField.match(dottedNameRegEx)) {
934
+ throw new Error(
935
+ "`keyField` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods."
936
+ );
937
+ }
938
+ const key = mergeDirective["key"];
939
+ if (key != null) {
940
+ if (keyField != null) {
941
+ throw new Error(
942
+ "Cannot use @merge directive with both `keyField` and `key` arguments."
943
+ );
944
+ }
945
+ for (const keyDef of key) {
946
+ let [aliasOrKeyPath, keyPath] = keyDef.split(":");
947
+ let aliasPath;
948
+ if (keyPath == null) {
949
+ keyPath = aliasPath = aliasOrKeyPath;
950
+ } else {
951
+ aliasPath = aliasOrKeyPath;
952
+ }
953
+ if (keyPath != null && !keyPath.match(dottedNameRegEx)) {
954
+ throw new Error(
955
+ "Each partial key within the `key` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods."
956
+ );
957
+ }
958
+ if (aliasPath != null && !aliasOrKeyPath.match(dottedNameRegEx)) {
959
+ throw new Error(
960
+ "Each alias within the `key` argument for @merge directive must be a set of valid GraphQL SDL names separated by periods."
961
+ );
962
+ }
963
+ }
964
+ }
965
+ const additionalArgs = mergeDirective["additionalArgs"];
966
+ if (additionalArgs != null) {
967
+ parseValue(`{ ${additionalArgs} }`, { noLocation: true });
968
+ }
969
+ if (mergeArgsExpr != null && (keyArg != null || additionalArgs != null)) {
970
+ throw new Error(
971
+ "Cannot use @merge directive with both `argsExpr` argument and any additional argument."
972
+ );
973
+ }
974
+ if (!isInterfaceType(returnType) && !isUnionType(returnType) && !isObjectType(returnType)) {
975
+ throw new Error(
976
+ "@merge directive may be used only with resolver that return an object, interface, or union."
977
+ );
978
+ }
979
+ const typeNames = mergeDirective["types"];
980
+ if (typeNames != null) {
981
+ if (!isAbstractType(returnType)) {
982
+ throw new Error(
983
+ "Types argument can only be used with a field that returns an abstract type."
984
+ );
985
+ }
986
+ const implementingTypes = isInterfaceType(returnType) ? getImplementingTypes(returnType.name, schema).map(
987
+ (typeName2) => schema.getType(typeName2)
988
+ ) : returnType.getTypes();
989
+ const implementingTypeNames = implementingTypes.map((type) => type?.name).filter(isSome);
990
+ for (const typeName2 of typeNames) {
991
+ if (!implementingTypeNames.includes(typeName2)) {
992
+ throw new Error(
993
+ `Types argument can only include only type names that implement the field return type's abstract type.`
994
+ );
995
+ }
996
+ }
997
+ }
998
+ }
999
+ return void 0;
1000
+ }
1001
+ });
1002
+ return schema;
1003
+ };
1004
+ }
1005
+
1006
+ function stitchingDirectives(options = {}) {
1007
+ const finalOptions = {
1008
+ ...defaultStitchingDirectiveOptions,
1009
+ ...options
1010
+ };
1011
+ const {
1012
+ keyDirectiveName,
1013
+ computedDirectiveName,
1014
+ mergeDirectiveName,
1015
+ canonicalDirectiveName
1016
+ } = finalOptions;
1017
+ const keyDirectiveTypeDefs = (
1018
+ /* GraphQL */
1019
+ `directive @${keyDirectiveName}(selectionSet: String!) on OBJECT`
1020
+ );
1021
+ const computedDirectiveTypeDefs = (
1022
+ /* GraphQL */
1023
+ `directive @${computedDirectiveName}(selectionSet: String!) on FIELD_DEFINITION`
1024
+ );
1025
+ const mergeDirectiveTypeDefs = (
1026
+ /* GraphQL */
1027
+ `directive @${mergeDirectiveName}(argsExpr: String, keyArg: String, keyField: String, key: [String!], additionalArgs: String) on FIELD_DEFINITION`
1028
+ );
1029
+ const canonicalDirectiveTypeDefs = (
1030
+ /* GraphQL */
1031
+ `directive @${canonicalDirectiveName} on OBJECT | INTERFACE | INPUT_OBJECT | UNION | ENUM | SCALAR | FIELD_DEFINITION | INPUT_FIELD_DEFINITION`
1032
+ );
1033
+ const keyDirective = new GraphQLDirective({
1034
+ name: keyDirectiveName,
1035
+ locations: ["OBJECT"],
1036
+ args: {
1037
+ selectionSet: { type: new GraphQLNonNull(GraphQLString) }
1038
+ }
1039
+ });
1040
+ const computedDirective = new GraphQLDirective({
1041
+ name: computedDirectiveName,
1042
+ locations: ["FIELD_DEFINITION"],
1043
+ args: {
1044
+ selectionSet: { type: new GraphQLNonNull(GraphQLString) }
1045
+ }
1046
+ });
1047
+ const mergeDirective = new GraphQLDirective({
1048
+ name: mergeDirectiveName,
1049
+ locations: ["FIELD_DEFINITION"],
1050
+ args: {
1051
+ argsExpr: { type: GraphQLString },
1052
+ keyArg: { type: GraphQLString },
1053
+ keyField: { type: GraphQLString },
1054
+ key: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
1055
+ additionalArgs: { type: GraphQLString }
1056
+ }
1057
+ });
1058
+ const canonicalDirective = new GraphQLDirective({
1059
+ name: canonicalDirectiveName,
1060
+ locations: [
1061
+ "OBJECT",
1062
+ "INTERFACE",
1063
+ "INPUT_OBJECT",
1064
+ "UNION",
1065
+ "ENUM",
1066
+ "SCALAR",
1067
+ "FIELD_DEFINITION",
1068
+ "INPUT_FIELD_DEFINITION"
1069
+ ]
1070
+ });
1071
+ const allStitchingDirectivesTypeDefs = [
1072
+ keyDirectiveTypeDefs,
1073
+ computedDirectiveTypeDefs,
1074
+ mergeDirectiveTypeDefs,
1075
+ canonicalDirectiveTypeDefs
1076
+ ].join("\n");
1077
+ return {
1078
+ keyDirectiveTypeDefs,
1079
+ computedDirectiveTypeDefs,
1080
+ mergeDirectiveTypeDefs,
1081
+ canonicalDirectiveTypeDefs,
1082
+ stitchingDirectivesTypeDefs: allStitchingDirectivesTypeDefs,
1083
+ // for backwards compatibility
1084
+ allStitchingDirectivesTypeDefs,
1085
+ keyDirective,
1086
+ computedDirective,
1087
+ mergeDirective,
1088
+ canonicalDirective,
1089
+ allStitchingDirectives: [
1090
+ keyDirective,
1091
+ computedDirective,
1092
+ mergeDirective,
1093
+ canonicalDirective
1094
+ ],
1095
+ stitchingDirectivesValidator: stitchingDirectivesValidator(finalOptions),
1096
+ stitchingDirectivesTransformer: stitchingDirectivesTransformer(finalOptions)
1097
+ };
1098
+ }
1099
+
1100
+ const extensionKind = /Extension$/;
1101
+ const entityKinds = [
1102
+ Kind.OBJECT_TYPE_DEFINITION,
1103
+ Kind.OBJECT_TYPE_EXTENSION,
1104
+ Kind.INTERFACE_TYPE_DEFINITION,
1105
+ Kind.INTERFACE_TYPE_EXTENSION
1106
+ ];
1107
+ function isEntityKind(def) {
1108
+ return entityKinds.includes(def.kind);
1109
+ }
1110
+ function getQueryTypeDef(definitions) {
1111
+ const schemaDef = definitions.find(
1112
+ (def) => def.kind === Kind.SCHEMA_DEFINITION
1113
+ );
1114
+ const typeName = schemaDef ? schemaDef.operationTypes.find(({ operation }) => operation === "query")?.type.name.value : "Query";
1115
+ return definitions.find(
1116
+ (def) => def.kind === Kind.OBJECT_TYPE_DEFINITION && def.name.value === typeName
1117
+ );
1118
+ }
1119
+ function federationToStitchingSDL(federationSDL, stitchingConfig = stitchingDirectives()) {
1120
+ const doc = parse(federationSDL);
1121
+ const entityTypes = [];
1122
+ const baseTypeNames = doc.definitions.reduce((memo, typeDef) => {
1123
+ if (!extensionKind.test(typeDef.kind) && "name" in typeDef && typeDef.name) {
1124
+ memo[typeDef.name.value] = true;
1125
+ }
1126
+ return memo;
1127
+ }, {});
1128
+ doc.definitions.forEach((typeDef) => {
1129
+ if (extensionKind.test(typeDef.kind) && "name" in typeDef && typeDef.name && !baseTypeNames[typeDef.name.value]) {
1130
+ typeDef.kind = typeDef.kind.replace(
1131
+ extensionKind,
1132
+ "Definition"
1133
+ );
1134
+ }
1135
+ if (!isEntityKind(typeDef)) return;
1136
+ const keyDirs = [];
1137
+ const otherDirs = [];
1138
+ typeDef.directives?.forEach((dir) => {
1139
+ if (dir.name.value === "key") {
1140
+ keyDirs.push(dir);
1141
+ } else {
1142
+ otherDirs.push(dir);
1143
+ }
1144
+ });
1145
+ if (!keyDirs.length) return;
1146
+ const selectionSet = `{ ${keyDirs.map((dir) => dir.arguments[0].value.value).join(" ")} }`;
1147
+ const keyFields = parse(selectionSet).definitions[0].selectionSet.selections.map((sel) => sel.name.value);
1148
+ const keyDir = keyDirs[0];
1149
+ keyDir.name.value = stitchingConfig.keyDirective.name;
1150
+ keyDir.arguments[0].name.value = "selectionSet";
1151
+ keyDir.arguments[0].value.value = selectionSet;
1152
+ typeDef.directives = [keyDir, ...otherDirs];
1153
+ typeDef.fields = typeDef.fields?.filter((fieldDef) => {
1154
+ return keyFields.includes(fieldDef.name.value) || !fieldDef.directives?.find((dir) => dir.name.value === "external");
1155
+ });
1156
+ typeDef.fields?.forEach((fieldDef) => {
1157
+ fieldDef.directives = fieldDef.directives.filter(
1158
+ (dir) => !/^(external|provides)$/.test(dir.name.value)
1159
+ );
1160
+ fieldDef.directives.forEach((dir) => {
1161
+ if (dir.name.value === "requires") {
1162
+ dir.name.value = stitchingConfig.computedDirective.name;
1163
+ dir.arguments[0].name.value = "selectionSet";
1164
+ dir.arguments[0].value.value = `{ ${dir.arguments[0].value.value} }`;
1165
+ }
1166
+ });
1167
+ });
1168
+ if (typeDef.kind === Kind.OBJECT_TYPE_DEFINITION || typeDef.kind === Kind.OBJECT_TYPE_EXTENSION) {
1169
+ entityTypes.push(typeDef.name.value);
1170
+ }
1171
+ });
1172
+ if (entityTypes.length) {
1173
+ const queryDef = getQueryTypeDef(doc.definitions);
1174
+ const entitiesSchema = parse(
1175
+ /* GraphQL */
1176
+ `
1177
+ scalar _Any
1178
+ union _Entity = ${entityTypes.filter((v, i, a) => a.indexOf(v) === i).join(" | ")}
1179
+ type Query { _entities(representations: [_Any!]!): [_Entity]! @${stitchingConfig.mergeDirective.name} }
1180
+ `
1181
+ ).definitions;
1182
+ doc.definitions.push(entitiesSchema[0]);
1183
+ doc.definitions.push(entitiesSchema[1]);
1184
+ if (queryDef) {
1185
+ queryDef.fields.push(entitiesSchema[2].fields[0]);
1186
+ } else {
1187
+ doc.definitions.push(entitiesSchema[2]);
1188
+ }
1189
+ }
1190
+ return [stitchingConfig.stitchingDirectivesTypeDefs, print(doc)].join("\n");
1191
+ }
1192
+
1193
+ export { federationToStitchingSDL, stitchingDirectives };