@graphql-tools/stitching-directives 3.0.0-alpha-f6c67111.0 → 3.0.0-alpha-20230517123108-df347e95

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