@graphql-tools/stitching-directives 3.1.13 → 3.1.14-alpha-09de1d3da51074c5901decc7efd24f43af24378f

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