@graphql-inspector/core 4.0.2 → 4.0.3

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 (193) hide show
  1. package/cjs/ast/document.js +40 -0
  2. package/cjs/coverage/index.js +128 -0
  3. package/cjs/coverage/output/json.js +7 -0
  4. package/cjs/diff/argument.js +25 -0
  5. package/cjs/diff/changes/argument.js +48 -0
  6. package/cjs/diff/changes/change.js +75 -0
  7. package/cjs/diff/changes/directive.js +124 -0
  8. package/cjs/diff/changes/enum.js +75 -0
  9. package/cjs/diff/changes/field.js +166 -0
  10. package/cjs/diff/changes/input.js +98 -0
  11. package/cjs/diff/changes/object.js +28 -0
  12. package/cjs/diff/changes/schema.js +40 -0
  13. package/cjs/diff/changes/type.js +72 -0
  14. package/cjs/diff/changes/union.js +28 -0
  15. package/cjs/diff/directive.js +41 -0
  16. package/cjs/diff/enum.js +34 -0
  17. package/cjs/diff/field.js +54 -0
  18. package/cjs/diff/index.js +22 -0
  19. package/cjs/diff/input.js +47 -0
  20. package/cjs/diff/interface.js +20 -0
  21. package/cjs/diff/object.js +33 -0
  22. package/cjs/diff/onComplete/types.js +0 -0
  23. package/cjs/diff/rules/config.js +0 -0
  24. package/cjs/diff/rules/consider-usage.js +39 -0
  25. package/cjs/diff/rules/dangerous-breaking.js +13 -0
  26. package/cjs/diff/rules/ignore-description-changes.js +21 -0
  27. package/cjs/diff/rules/index.js +8 -0
  28. package/cjs/diff/rules/safe-unreachable.js +19 -0
  29. package/cjs/diff/rules/suppress-removal-of-deprecated-field.js +58 -0
  30. package/cjs/diff/rules/types.js +0 -0
  31. package/cjs/diff/schema.js +107 -0
  32. package/cjs/diff/union.js +18 -0
  33. package/cjs/index.js +27 -0
  34. package/cjs/package.json +1 -0
  35. package/cjs/similar/index.js +57 -0
  36. package/cjs/utils/apollo.js +21 -0
  37. package/cjs/utils/compare.js +91 -0
  38. package/cjs/utils/graphql.js +194 -0
  39. package/cjs/utils/is-deprecated.js +17 -0
  40. package/cjs/utils/path.js +7 -0
  41. package/cjs/utils/string.js +70 -0
  42. package/cjs/validate/alias-count.js +37 -0
  43. package/cjs/validate/complexity.js +39 -0
  44. package/cjs/validate/directive-count.js +37 -0
  45. package/cjs/validate/index.js +143 -0
  46. package/cjs/validate/query-depth.js +103 -0
  47. package/cjs/validate/token-count.js +55 -0
  48. package/esm/ast/document.js +36 -0
  49. package/esm/coverage/index.js +124 -0
  50. package/esm/coverage/output/json.js +3 -0
  51. package/esm/diff/argument.js +21 -0
  52. package/esm/diff/changes/argument.js +42 -0
  53. package/esm/diff/changes/change.js +72 -0
  54. package/esm/diff/changes/directive.js +111 -0
  55. package/esm/diff/changes/enum.js +66 -0
  56. package/esm/diff/changes/field.js +150 -0
  57. package/esm/diff/changes/input.js +88 -0
  58. package/esm/diff/changes/object.js +23 -0
  59. package/esm/diff/changes/schema.js +34 -0
  60. package/esm/diff/changes/type.js +63 -0
  61. package/esm/diff/changes/union.js +23 -0
  62. package/esm/diff/directive.js +37 -0
  63. package/esm/diff/enum.js +30 -0
  64. package/esm/diff/field.js +50 -0
  65. package/esm/diff/index.js +18 -0
  66. package/esm/diff/input.js +43 -0
  67. package/esm/diff/interface.js +16 -0
  68. package/esm/diff/object.js +29 -0
  69. package/esm/diff/onComplete/types.js +0 -0
  70. package/esm/diff/rules/config.js +0 -0
  71. package/esm/diff/rules/consider-usage.js +35 -0
  72. package/esm/diff/rules/dangerous-breaking.js +9 -0
  73. package/esm/diff/rules/ignore-description-changes.js +17 -0
  74. package/esm/diff/rules/index.js +5 -0
  75. package/esm/diff/rules/safe-unreachable.js +15 -0
  76. package/esm/diff/rules/suppress-removal-of-deprecated-field.js +54 -0
  77. package/esm/diff/rules/types.js +0 -0
  78. package/esm/diff/schema.js +103 -0
  79. package/esm/diff/union.js +14 -0
  80. package/esm/index.js +11 -0
  81. package/esm/similar/index.js +53 -0
  82. package/esm/utils/apollo.js +16 -0
  83. package/esm/utils/compare.js +82 -0
  84. package/esm/utils/graphql.js +181 -0
  85. package/esm/utils/is-deprecated.js +13 -0
  86. package/esm/utils/path.js +3 -0
  87. package/esm/utils/string.js +64 -0
  88. package/esm/validate/alias-count.js +32 -0
  89. package/esm/validate/complexity.js +34 -0
  90. package/esm/validate/directive-count.js +32 -0
  91. package/esm/validate/index.js +139 -0
  92. package/esm/validate/query-depth.js +97 -0
  93. package/esm/validate/token-count.js +50 -0
  94. package/package.json +30 -9
  95. package/typings/ast/document.d.ts +15 -0
  96. package/typings/coverage/index.d.ts +51 -0
  97. package/typings/coverage/output/json.d.cts +2 -0
  98. package/{coverage → typings/coverage}/output/json.d.ts +1 -1
  99. package/typings/diff/argument.d.cts +3 -0
  100. package/{diff → typings/diff}/argument.d.ts +1 -1
  101. package/typings/diff/changes/argument.d.cts +5 -0
  102. package/{diff → typings/diff}/changes/argument.d.ts +1 -1
  103. package/typings/diff/changes/change.d.ts +69 -0
  104. package/typings/diff/changes/directive.d.cts +12 -0
  105. package/{diff → typings/diff}/changes/directive.d.ts +1 -1
  106. package/typings/diff/changes/enum.d.cts +8 -0
  107. package/{diff → typings/diff}/changes/enum.d.ts +1 -1
  108. package/typings/diff/changes/field.d.cts +15 -0
  109. package/{diff → typings/diff}/changes/field.d.ts +1 -1
  110. package/typings/diff/changes/input.d.cts +9 -0
  111. package/{diff → typings/diff}/changes/input.d.ts +1 -1
  112. package/typings/diff/changes/object.d.cts +4 -0
  113. package/{diff → typings/diff}/changes/object.d.ts +1 -1
  114. package/typings/diff/changes/schema.d.cts +5 -0
  115. package/{diff → typings/diff}/changes/schema.d.ts +1 -1
  116. package/typings/diff/changes/type.d.cts +8 -0
  117. package/{diff → typings/diff}/changes/type.d.ts +1 -1
  118. package/typings/diff/changes/union.d.cts +4 -0
  119. package/{diff → typings/diff}/changes/union.d.ts +1 -1
  120. package/typings/diff/directive.d.cts +3 -0
  121. package/{diff → typings/diff}/directive.d.ts +1 -1
  122. package/typings/diff/enum.d.cts +3 -0
  123. package/{diff → typings/diff}/enum.d.ts +1 -1
  124. package/typings/diff/field.d.cts +3 -0
  125. package/{diff → typings/diff}/field.d.ts +1 -1
  126. package/typings/diff/index.d.cts +9 -0
  127. package/typings/diff/index.d.ts +9 -0
  128. package/typings/diff/input.d.cts +3 -0
  129. package/{diff → typings/diff}/input.d.ts +1 -1
  130. package/typings/diff/interface.d.cts +3 -0
  131. package/{diff → typings/diff}/interface.d.ts +1 -1
  132. package/typings/diff/object.d.cts +3 -0
  133. package/{diff → typings/diff}/object.d.ts +1 -1
  134. package/typings/diff/onComplete/types.d.cts +7 -0
  135. package/typings/diff/onComplete/types.d.ts +7 -0
  136. package/typings/diff/rules/config.d.cts +2 -0
  137. package/typings/diff/rules/config.d.ts +2 -0
  138. package/typings/diff/rules/consider-usage.d.cts +29 -0
  139. package/{diff → typings/diff}/rules/consider-usage.d.ts +2 -2
  140. package/typings/diff/rules/dangerous-breaking.d.cts +2 -0
  141. package/{diff → typings/diff}/rules/dangerous-breaking.d.ts +1 -1
  142. package/typings/diff/rules/ignore-description-changes.d.cts +2 -0
  143. package/{diff → typings/diff}/rules/ignore-description-changes.d.ts +1 -1
  144. package/typings/diff/rules/index.d.cts +5 -0
  145. package/typings/diff/rules/index.d.ts +5 -0
  146. package/typings/diff/rules/safe-unreachable.d.cts +2 -0
  147. package/{diff → typings/diff}/rules/safe-unreachable.d.ts +1 -1
  148. package/typings/diff/rules/suppress-removal-of-deprecated-field.d.cts +2 -0
  149. package/{diff → typings/diff}/rules/suppress-removal-of-deprecated-field.d.ts +1 -1
  150. package/typings/diff/rules/types.d.cts +8 -0
  151. package/{diff → typings/diff}/rules/types.d.ts +2 -2
  152. package/typings/diff/schema.d.cts +4 -0
  153. package/{diff → typings/diff}/schema.d.ts +2 -2
  154. package/typings/diff/union.d.cts +3 -0
  155. package/{diff → typings/diff}/union.d.ts +1 -1
  156. package/typings/index.d.cts +12 -0
  157. package/typings/index.d.ts +12 -0
  158. package/typings/similar/index.d.cts +6 -0
  159. package/{similar → typings/similar}/index.d.ts +1 -1
  160. package/typings/utils/apollo.d.ts +5 -0
  161. package/typings/utils/compare.d.ts +22 -0
  162. package/typings/utils/graphql.d.ts +11 -0
  163. package/typings/utils/is-deprecated.d.cts +2 -0
  164. package/{utils → typings/utils}/is-deprecated.d.ts +1 -1
  165. package/typings/utils/path.d.ts +1 -0
  166. package/typings/utils/string.d.ts +14 -0
  167. package/typings/validate/alias-count.d.ts +10 -0
  168. package/typings/validate/complexity.d.cts +16 -0
  169. package/{validate → typings/validate}/complexity.d.ts +1 -1
  170. package/typings/validate/directive-count.d.ts +10 -0
  171. package/typings/validate/index.d.ts +49 -0
  172. package/typings/validate/query-depth.d.ts +15 -0
  173. package/typings/validate/token-count.d.ts +12 -0
  174. package/diff/index.d.ts +0 -9
  175. package/diff/onComplete/types.d.ts +0 -7
  176. package/diff/rules/config.d.ts +0 -2
  177. package/diff/rules/index.d.ts +0 -5
  178. package/index.d.ts +0 -12
  179. package/index.js +0 -2075
  180. package/index.mjs +0 -2061
  181. /package/{ast/document.d.ts → typings/ast/document.d.cts} +0 -0
  182. /package/{coverage/index.d.ts → typings/coverage/index.d.cts} +0 -0
  183. /package/{diff/changes/change.d.ts → typings/diff/changes/change.d.cts} +0 -0
  184. /package/{utils/apollo.d.ts → typings/utils/apollo.d.cts} +0 -0
  185. /package/{utils/compare.d.ts → typings/utils/compare.d.cts} +0 -0
  186. /package/{utils/graphql.d.ts → typings/utils/graphql.d.cts} +0 -0
  187. /package/{utils/path.d.ts → typings/utils/path.d.cts} +0 -0
  188. /package/{utils/string.d.ts → typings/utils/string.d.cts} +0 -0
  189. /package/{validate/alias-count.d.ts → typings/validate/alias-count.d.cts} +0 -0
  190. /package/{validate/directive-count.d.ts → typings/validate/directive-count.d.cts} +0 -0
  191. /package/{validate/index.d.ts → typings/validate/index.d.cts} +0 -0
  192. /package/{validate/query-depth.d.ts → typings/validate/query-depth.d.cts} +0 -0
  193. /package/{validate/token-count.d.ts → typings/validate/token-count.d.cts} +0 -0
package/index.js DELETED
@@ -1,2075 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
-
7
- const graphql = require('graphql');
8
- const tslib = require('tslib');
9
- const inspect = _interopDefault(require('object-inspect'));
10
- const dependencyGraph = require('dependency-graph');
11
- const parser = require('graphql/language/parser');
12
-
13
- function readDocument(source) {
14
- const result = {
15
- source,
16
- fragments: [],
17
- operations: [],
18
- hasFragments: false,
19
- hasOperations: false,
20
- };
21
- const documentNode = graphql.parse(source.body);
22
- const filepath = source.name;
23
- const definitions = documentNode.definitions || [];
24
- definitions.forEach((node) => {
25
- if (isOperation(node)) {
26
- result.operations.push({
27
- node,
28
- source: filepath,
29
- });
30
- }
31
- else if (isFragment(node)) {
32
- result.fragments.push({
33
- node,
34
- source: filepath,
35
- });
36
- }
37
- });
38
- result.hasFragments = result.fragments.length > 0;
39
- result.hasOperations = result.operations.length > 0;
40
- return result;
41
- }
42
- function isOperation(node) {
43
- return node.kind === graphql.Kind.OPERATION_DEFINITION;
44
- }
45
- function isFragment(node) {
46
- return node.kind === graphql.Kind.FRAGMENT_DEFINITION;
47
- }
48
-
49
- function isDeprecated(fieldOrEnumValue) {
50
- var _a, _b;
51
- if ('isDeprecated' in fieldOrEnumValue) {
52
- return fieldOrEnumValue['isDeprecated'];
53
- }
54
- if (fieldOrEnumValue.deprecationReason != null) {
55
- return true;
56
- }
57
- if ((_b = (_a = fieldOrEnumValue.astNode) === null || _a === void 0 ? void 0 : _a.directives) === null || _b === void 0 ? void 0 : _b.some(directive => directive.name.value === 'deprecated')) {
58
- return true;
59
- }
60
- return false;
61
- }
62
-
63
- function safeChangeForField(oldType, newType) {
64
- if (!graphql.isWrappingType(oldType) && !graphql.isWrappingType(newType)) {
65
- return oldType.toString() === newType.toString();
66
- }
67
- if (graphql.isNonNullType(newType)) {
68
- const ofType = graphql.isNonNullType(oldType) ? oldType.ofType : oldType;
69
- return safeChangeForField(ofType, newType.ofType);
70
- }
71
- if (graphql.isListType(oldType)) {
72
- return ((graphql.isListType(newType) && safeChangeForField(oldType.ofType, newType.ofType)) ||
73
- (graphql.isNonNullType(newType) && safeChangeForField(oldType, newType.ofType)));
74
- }
75
- return false;
76
- }
77
- function safeChangeForInputValue(oldType, newType) {
78
- if (!graphql.isWrappingType(oldType) && !graphql.isWrappingType(newType)) {
79
- return oldType.toString() === newType.toString();
80
- }
81
- if (graphql.isListType(oldType) && graphql.isListType(newType)) {
82
- return safeChangeForInputValue(oldType.ofType, newType.ofType);
83
- }
84
- if (graphql.isNonNullType(oldType)) {
85
- const ofType = graphql.isNonNullType(newType) ? newType.ofType : newType;
86
- return safeChangeForInputValue(oldType.ofType, ofType);
87
- }
88
- return false;
89
- }
90
- function getKind(type) {
91
- const node = type.astNode;
92
- return (node === null || node === void 0 ? void 0 : node.kind) || '';
93
- }
94
- function getTypePrefix(type) {
95
- const kind = getKind(type);
96
- const kindsMap = {
97
- [graphql.Kind.SCALAR_TYPE_DEFINITION]: 'scalar',
98
- [graphql.Kind.OBJECT_TYPE_DEFINITION]: 'type',
99
- [graphql.Kind.INTERFACE_TYPE_DEFINITION]: 'interface',
100
- [graphql.Kind.UNION_TYPE_DEFINITION]: 'union',
101
- [graphql.Kind.ENUM_TYPE_DEFINITION]: 'enum',
102
- [graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
103
- };
104
- return kindsMap[kind.toString()];
105
- }
106
- function isPrimitive(type) {
107
- return ['String', 'Int', 'Float', 'Boolean', 'ID'].includes(typeof type === 'string' ? type : type.name);
108
- }
109
- function isForIntrospection(type) {
110
- return [
111
- '__Schema',
112
- '__Type',
113
- '__TypeKind',
114
- '__Field',
115
- '__InputValue',
116
- '__EnumValue',
117
- '__Directive',
118
- '__DirectiveLocation',
119
- ].includes(typeof type === 'string' ? type : type.name);
120
- }
121
- function findDeprecatedUsages(schema, ast) {
122
- const errors = [];
123
- const typeInfo = new graphql.TypeInfo(schema);
124
- graphql.visit(ast, graphql.visitWithTypeInfo(typeInfo, {
125
- Argument(node) {
126
- const argument = typeInfo.getArgument();
127
- if (argument) {
128
- const reason = argument.deprecationReason;
129
- if (reason) {
130
- const fieldDef = typeInfo.getFieldDef();
131
- if (fieldDef) {
132
- errors.push(new graphql.GraphQLError(`The argument '${argument === null || argument === void 0 ? void 0 : argument.name}' of '${fieldDef.name}' is deprecated. ${reason}`, [node]));
133
- }
134
- }
135
- }
136
- },
137
- Field(node) {
138
- const fieldDef = typeInfo.getFieldDef();
139
- if (fieldDef && isDeprecated(fieldDef)) {
140
- const parentType = typeInfo.getParentType();
141
- if (parentType) {
142
- const reason = fieldDef.deprecationReason;
143
- errors.push(new graphql.GraphQLError(`The field '${parentType.name}.${fieldDef.name}' is deprecated.${reason ? ' ' + reason : ''}`, [node]));
144
- }
145
- }
146
- },
147
- EnumValue(node) {
148
- const enumVal = typeInfo.getEnumValue();
149
- if (enumVal && isDeprecated(enumVal)) {
150
- const type = graphql.getNamedType(typeInfo.getInputType());
151
- if (type) {
152
- const reason = enumVal.deprecationReason;
153
- errors.push(new graphql.GraphQLError(`The enum value '${type.name}.${enumVal.name}' is deprecated.${reason ? ' ' + reason : ''}`, [node]));
154
- }
155
- }
156
- },
157
- }));
158
- return errors;
159
- }
160
- function removeFieldIfDirectives(node, directiveNames) {
161
- var _a;
162
- if ((_a = node.directives) === null || _a === void 0 ? void 0 : _a.some(d => directiveNames.includes(d.name.value))) {
163
- return null;
164
- }
165
- return node;
166
- }
167
- function removeDirectives(node, directiveNames) {
168
- if (node.directives) {
169
- return Object.assign(Object.assign({}, node), { directives: node.directives.filter(d => !directiveNames.includes(d.name.value)) });
170
- }
171
- return node;
172
- }
173
- function getReachableTypes(schema) {
174
- const reachableTypes = new Set();
175
- const collect = (type) => {
176
- const typeName = type.name;
177
- if (reachableTypes.has(typeName)) {
178
- return;
179
- }
180
- reachableTypes.add(typeName);
181
- if (graphql.isScalarType(type)) {
182
- return;
183
- }
184
- if (graphql.isInterfaceType(type) || graphql.isObjectType(type)) {
185
- if (graphql.isInterfaceType(type)) {
186
- const { objects, interfaces } = schema.getImplementations(type);
187
- for (const child of objects) {
188
- collect(child);
189
- }
190
- for (const child of interfaces) {
191
- collect(child);
192
- }
193
- }
194
- const fields = type.getFields();
195
- for (const fieldName in fields) {
196
- const field = fields[fieldName];
197
- collect(resolveOutputType(field.type));
198
- const args = field.args;
199
- for (const argName in args) {
200
- const arg = args[argName];
201
- collect(resolveInputType(arg.type));
202
- }
203
- }
204
- }
205
- else if (graphql.isUnionType(type)) {
206
- const types = type.getTypes();
207
- for (const child of types) {
208
- collect(child);
209
- }
210
- }
211
- else if (graphql.isInputObjectType(type)) {
212
- const fields = type.getFields();
213
- for (const fieldName in fields) {
214
- const field = fields[fieldName];
215
- collect(resolveInputType(field.type));
216
- }
217
- }
218
- };
219
- for (const type of [
220
- schema.getQueryType(),
221
- schema.getMutationType(),
222
- schema.getSubscriptionType(),
223
- ]) {
224
- if (type) {
225
- collect(type);
226
- }
227
- }
228
- return reachableTypes;
229
- }
230
- function resolveOutputType(output) {
231
- if (graphql.isListType(output) || graphql.isNonNullType(output)) {
232
- return resolveOutputType(output.ofType);
233
- }
234
- return output;
235
- }
236
- function resolveInputType(input) {
237
- if (graphql.isListType(input) || graphql.isNonNullType(input)) {
238
- return resolveInputType(input.ofType);
239
- }
240
- return input;
241
- }
242
-
243
- function coverage(schema, sources) {
244
- const coverage = {
245
- sources,
246
- types: {},
247
- stats: {
248
- numTypes: 0,
249
- numTypesCoveredFully: 0,
250
- numTypesCovered: 0,
251
- numFields: 0,
252
- numFiledsCovered: 0,
253
- },
254
- };
255
- const typeMap = schema.getTypeMap();
256
- const typeInfo = new graphql.TypeInfo(schema);
257
- const visitor = source => ({
258
- Field(node) {
259
- const fieldDef = typeInfo.getFieldDef();
260
- const parent = typeInfo.getParentType();
261
- if ((parent === null || parent === void 0 ? void 0 : parent.name) &&
262
- !isForIntrospection(parent.name) &&
263
- (fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.name) &&
264
- fieldDef.name !== '__typename' &&
265
- fieldDef.name !== '__schema') {
266
- const sourceName = source.name;
267
- const typeCoverage = coverage.types[parent.name];
268
- const fieldCoverage = typeCoverage.children[fieldDef.name];
269
- const locations = fieldCoverage.locations[sourceName];
270
- typeCoverage.hits++;
271
- fieldCoverage.hits++;
272
- if (node.loc) {
273
- fieldCoverage.locations[sourceName] = [node.loc, ...(locations || [])];
274
- }
275
- if (node.arguments) {
276
- for (const argNode of node.arguments) {
277
- const argCoverage = fieldCoverage.children[argNode.name.value];
278
- argCoverage.hits++;
279
- if (argNode.loc) {
280
- argCoverage.locations[sourceName] = [
281
- argNode.loc,
282
- ...(argCoverage.locations[sourceName] || []),
283
- ];
284
- }
285
- }
286
- }
287
- }
288
- },
289
- });
290
- for (const typename in typeMap) {
291
- if (!isForIntrospection(typename) && !isPrimitive(typename)) {
292
- const type = typeMap[typename];
293
- if (graphql.isObjectType(type) || graphql.isInterfaceType(type)) {
294
- const typeCoverage = {
295
- hits: 0,
296
- fieldsCount: 0,
297
- fieldsCountCovered: 0,
298
- type,
299
- children: {},
300
- };
301
- const fieldMap = type.getFields();
302
- for (const fieldname in fieldMap) {
303
- const field = fieldMap[fieldname];
304
- typeCoverage.children[field.name] = {
305
- hits: 0,
306
- fieldsCount: 0,
307
- fieldsCountCovered: 0,
308
- locations: {},
309
- children: {},
310
- };
311
- for (const arg of field.args) {
312
- typeCoverage.children[field.name].children[arg.name] = {
313
- hits: 0,
314
- fieldsCount: 0,
315
- fieldsCountCovered: 0,
316
- locations: {},
317
- };
318
- }
319
- }
320
- coverage.types[type.name] = typeCoverage;
321
- }
322
- }
323
- }
324
- const documents = coverage.sources.map(readDocument);
325
- documents.forEach((doc, i) => {
326
- const source = coverage.sources[i];
327
- doc.operations.forEach(op => {
328
- graphql.visit(op.node, graphql.visitWithTypeInfo(typeInfo, visitor(source)));
329
- });
330
- doc.fragments.forEach(fr => {
331
- graphql.visit(fr.node, graphql.visitWithTypeInfo(typeInfo, visitor(source)));
332
- });
333
- });
334
- for (const key in coverage.types) {
335
- const me = coverage.types[key];
336
- processStats(me);
337
- coverage.stats.numTypes++;
338
- if (me.fieldsCountCovered > 0)
339
- coverage.stats.numTypesCovered++;
340
- if (me.fieldsCount == me.fieldsCountCovered)
341
- coverage.stats.numTypesCoveredFully++;
342
- coverage.stats.numFields += me.fieldsCount;
343
- coverage.stats.numFiledsCovered += me.fieldsCountCovered;
344
- }
345
- return coverage;
346
- }
347
- function processStats(me) {
348
- const children = me.children;
349
- if (children) {
350
- for (const k in children) {
351
- const ch = children[k];
352
- if (ch.children !== undefined) {
353
- processStats(ch);
354
- me.fieldsCount += ch.fieldsCount;
355
- me.fieldsCountCovered += ch.fieldsCountCovered;
356
- }
357
- me.fieldsCount++;
358
- if (ch.hits > 0) {
359
- me.fieldsCountCovered++;
360
- }
361
- }
362
- }
363
- }
364
-
365
- function parsePath(path) {
366
- return path.split('.');
367
- }
368
-
369
- (function (ChangeType) {
370
- // Argument
371
- ChangeType["FieldArgumentDescriptionChanged"] = "FIELD_ARGUMENT_DESCRIPTION_CHANGED";
372
- ChangeType["FieldArgumentDefaultChanged"] = "FIELD_ARGUMENT_DEFAULT_CHANGED";
373
- ChangeType["FieldArgumentTypeChanged"] = "FIELD_ARGUMENT_TYPE_CHANGED";
374
- // Directive
375
- ChangeType["DirectiveRemoved"] = "DIRECTIVE_REMOVED";
376
- ChangeType["DirectiveAdded"] = "DIRECTIVE_ADDED";
377
- ChangeType["DirectiveDescriptionChanged"] = "DIRECTIVE_DESCRIPTION_CHANGED";
378
- ChangeType["DirectiveLocationAdded"] = "DIRECTIVE_LOCATION_ADDED";
379
- ChangeType["DirectiveLocationRemoved"] = "DIRECTIVE_LOCATION_REMOVED";
380
- ChangeType["DirectiveArgumentAdded"] = "DIRECTIVE_ARGUMENT_ADDED";
381
- ChangeType["DirectiveArgumentRemoved"] = "DIRECTIVE_ARGUMENT_REMOVED";
382
- ChangeType["DirectiveArgumentDescriptionChanged"] = "DIRECTIVE_ARGUMENT_DESCRIPTION_CHANGED";
383
- ChangeType["DirectiveArgumentDefaultValueChanged"] = "DIRECTIVE_ARGUMENT_DEFAULT_VALUE_CHANGED";
384
- ChangeType["DirectiveArgumentTypeChanged"] = "DIRECTIVE_ARGUMENT_TYPE_CHANGED";
385
- // Enum
386
- ChangeType["EnumValueRemoved"] = "ENUM_VALUE_REMOVED";
387
- ChangeType["EnumValueAdded"] = "ENUM_VALUE_ADDED";
388
- ChangeType["EnumValueDescriptionChanged"] = "ENUM_VALUE_DESCRIPTION_CHANGED";
389
- ChangeType["EnumValueDeprecationReasonChanged"] = "ENUM_VALUE_DEPRECATION_REASON_CHANGED";
390
- ChangeType["EnumValueDeprecationReasonAdded"] = "ENUM_VALUE_DEPRECATION_REASON_ADDED";
391
- ChangeType["EnumValueDeprecationReasonRemoved"] = "ENUM_VALUE_DEPRECATION_REASON_REMOVED";
392
- // Field
393
- ChangeType["FieldRemoved"] = "FIELD_REMOVED";
394
- ChangeType["FieldAdded"] = "FIELD_ADDED";
395
- ChangeType["FieldDescriptionChanged"] = "FIELD_DESCRIPTION_CHANGED";
396
- ChangeType["FieldDescriptionAdded"] = "FIELD_DESCRIPTION_ADDED";
397
- ChangeType["FieldDescriptionRemoved"] = "FIELD_DESCRIPTION_REMOVED";
398
- ChangeType["FieldDeprecationAdded"] = "FIELD_DEPRECATION_ADDED";
399
- ChangeType["FieldDeprecationRemoved"] = "FIELD_DEPRECATION_REMOVED";
400
- ChangeType["FieldDeprecationReasonChanged"] = "FIELD_DEPRECATION_REASON_CHANGED";
401
- ChangeType["FieldDeprecationReasonAdded"] = "FIELD_DEPRECATION_REASON_ADDED";
402
- ChangeType["FieldDeprecationReasonRemoved"] = "FIELD_DEPRECATION_REASON_REMOVED";
403
- ChangeType["FieldTypeChanged"] = "FIELD_TYPE_CHANGED";
404
- ChangeType["FieldArgumentAdded"] = "FIELD_ARGUMENT_ADDED";
405
- ChangeType["FieldArgumentRemoved"] = "FIELD_ARGUMENT_REMOVED";
406
- // Input
407
- ChangeType["InputFieldRemoved"] = "INPUT_FIELD_REMOVED";
408
- ChangeType["InputFieldAdded"] = "INPUT_FIELD_ADDED";
409
- ChangeType["InputFieldDescriptionAdded"] = "INPUT_FIELD_DESCRIPTION_ADDED";
410
- ChangeType["InputFieldDescriptionRemoved"] = "INPUT_FIELD_DESCRIPTION_REMOVED";
411
- ChangeType["InputFieldDescriptionChanged"] = "INPUT_FIELD_DESCRIPTION_CHANGED";
412
- ChangeType["InputFieldDefaultValueChanged"] = "INPUT_FIELD_DEFAULT_VALUE_CHANGED";
413
- ChangeType["InputFieldTypeChanged"] = "INPUT_FIELD_TYPE_CHANGED";
414
- // Type
415
- ChangeType["ObjectTypeInterfaceAdded"] = "OBJECT_TYPE_INTERFACE_ADDED";
416
- ChangeType["ObjectTypeInterfaceRemoved"] = "OBJECT_TYPE_INTERFACE_REMOVED";
417
- // Schema
418
- ChangeType["SchemaQueryTypeChanged"] = "SCHEMA_QUERY_TYPE_CHANGED";
419
- ChangeType["SchemaMutationTypeChanged"] = "SCHEMA_MUTATION_TYPE_CHANGED";
420
- ChangeType["SchemaSubscriptionTypeChanged"] = "SCHEMA_SUBSCRIPTION_TYPE_CHANGED";
421
- // Type
422
- ChangeType["TypeRemoved"] = "TYPE_REMOVED";
423
- ChangeType["TypeAdded"] = "TYPE_ADDED";
424
- ChangeType["TypeKindChanged"] = "TYPE_KIND_CHANGED";
425
- ChangeType["TypeDescriptionChanged"] = "TYPE_DESCRIPTION_CHANGED";
426
- // TODO
427
- ChangeType["TypeDescriptionRemoved"] = "TYPE_DESCRIPTION_REMOVED";
428
- // TODO
429
- ChangeType["TypeDescriptionAdded"] = "TYPE_DESCRIPTION_ADDED";
430
- // Union
431
- ChangeType["UnionMemberRemoved"] = "UNION_MEMBER_REMOVED";
432
- ChangeType["UnionMemberAdded"] = "UNION_MEMBER_ADDED";
433
- })(exports.ChangeType || (exports.ChangeType = {}));
434
- (function (CriticalityLevel) {
435
- CriticalityLevel["Breaking"] = "BREAKING";
436
- CriticalityLevel["NonBreaking"] = "NON_BREAKING";
437
- CriticalityLevel["Dangerous"] = "DANGEROUS";
438
- })(exports.CriticalityLevel || (exports.CriticalityLevel = {}));
439
-
440
- const considerUsage = ({ changes, config }) => tslib.__awaiter(void 0, void 0, void 0, function* () {
441
- if (!config) {
442
- throw new Error(`considerUsage rule is missing config`);
443
- }
444
- const collectedBreakingField = [];
445
- changes.forEach(change => {
446
- if (change.criticality.level === exports.CriticalityLevel.Breaking && change.path) {
447
- const [typeName, fieldName, argumentName] = parsePath(change.path);
448
- collectedBreakingField.push({
449
- type: typeName,
450
- field: fieldName,
451
- argument: argumentName,
452
- });
453
- }
454
- });
455
- // True if safe to break, false otherwise
456
- const usageList = yield config.checkUsage(collectedBreakingField);
457
- // turns an array of booleans into an array of `Type.Field` strings
458
- // includes only those that are safe to break the api
459
- const suppressedPaths = collectedBreakingField
460
- .filter((_, i) => usageList[i] === true)
461
- .map(({ type, field, argument }) => [type, field, argument].filter(Boolean).join('.'));
462
- return changes.map(change => {
463
- // Turns those "safe to break" changes into "dangerous"
464
- if (change.criticality.level === exports.CriticalityLevel.Breaking &&
465
- change.path &&
466
- suppressedPaths.some(p => change.path.startsWith(p))) {
467
- return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }), message: `${change.message} (non-breaking based on usage)` });
468
- }
469
- return change;
470
- });
471
- });
472
-
473
- const dangerousBreaking = ({ changes }) => {
474
- return changes.map(change => {
475
- if (change.criticality.level === exports.CriticalityLevel.Dangerous) {
476
- return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Breaking }) });
477
- }
478
- return change;
479
- });
480
- };
481
-
482
- const descriptionChangeTypes = [
483
- exports.ChangeType.FieldArgumentDescriptionChanged,
484
- exports.ChangeType.DirectiveDescriptionChanged,
485
- exports.ChangeType.DirectiveArgumentDescriptionChanged,
486
- exports.ChangeType.EnumValueDescriptionChanged,
487
- exports.ChangeType.FieldDescriptionChanged,
488
- exports.ChangeType.FieldDescriptionAdded,
489
- exports.ChangeType.FieldDescriptionRemoved,
490
- exports.ChangeType.InputFieldDescriptionAdded,
491
- exports.ChangeType.InputFieldDescriptionRemoved,
492
- exports.ChangeType.InputFieldDescriptionChanged,
493
- exports.ChangeType.TypeDescriptionChanged,
494
- ];
495
- const ignoreDescriptionChanges = ({ changes }) => {
496
- return changes.filter(change => !descriptionChangeTypes.includes(change.type));
497
- };
498
-
499
- const safeUnreachable = ({ changes, oldSchema }) => {
500
- const reachable = getReachableTypes(oldSchema);
501
- return changes.map(change => {
502
- if (change.criticality.level === exports.CriticalityLevel.Breaking && change.path) {
503
- const [typeName] = parsePath(change.path);
504
- if (!reachable.has(typeName)) {
505
- return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.NonBreaking }), message: 'Unreachable from root' });
506
- }
507
- }
508
- return change;
509
- });
510
- };
511
-
512
- const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, newSchema }) => {
513
- return changes.map(change => {
514
- if (change.type === exports.ChangeType.FieldRemoved &&
515
- change.criticality.level === exports.CriticalityLevel.Breaking &&
516
- change.path) {
517
- const [typeName, fieldName] = parsePath(change.path);
518
- const type = oldSchema.getType(typeName);
519
- if (graphql.isObjectType(type) || graphql.isInterfaceType(type)) {
520
- const field = type.getFields()[fieldName];
521
- if (isDeprecated(field)) {
522
- return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
523
- }
524
- }
525
- }
526
- if (change.type === exports.ChangeType.EnumValueRemoved &&
527
- change.criticality.level === exports.CriticalityLevel.Breaking &&
528
- change.path) {
529
- const [enumName, enumItem] = parsePath(change.path);
530
- const type = oldSchema.getType(enumName);
531
- if (graphql.isEnumType(type)) {
532
- const item = type.getValue(enumItem);
533
- if (item && isDeprecated(item)) {
534
- return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
535
- }
536
- }
537
- }
538
- if (change.type === exports.ChangeType.InputFieldRemoved &&
539
- change.criticality.level === exports.CriticalityLevel.Breaking &&
540
- change.path) {
541
- const [inputName, inputItem] = parsePath(change.path);
542
- const type = oldSchema.getType(inputName);
543
- if (graphql.isInputObjectType(type)) {
544
- const item = type.getFields()[inputItem];
545
- if (item && isDeprecated(item)) {
546
- return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
547
- }
548
- }
549
- }
550
- if (change.type === exports.ChangeType.TypeRemoved &&
551
- change.criticality.level === exports.CriticalityLevel.Breaking &&
552
- change.path) {
553
- const [typeName] = parsePath(change.path);
554
- const type = newSchema.getType(typeName);
555
- if (!type) {
556
- return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
557
- }
558
- }
559
- return change;
560
- });
561
- };
562
-
563
- const rules = /*#__PURE__*/Object.freeze({
564
- __proto__: null,
565
- considerUsage: considerUsage,
566
- dangerousBreaking: dangerousBreaking,
567
- ignoreDescriptionChanges: ignoreDescriptionChanges,
568
- safeUnreachable: safeUnreachable,
569
- suppressRemovalOfDeprecatedField: suppressRemovalOfDeprecatedField
570
- });
571
-
572
- function keyMap(list, keyFn) {
573
- return list.reduce((map, item) => {
574
- map[keyFn(item)] = item;
575
- return map;
576
- }, Object.create(null));
577
- }
578
- function isEqual(a, b) {
579
- if (Array.isArray(a) && Array.isArray(b)) {
580
- if (a.length !== b.length)
581
- return false;
582
- for (let index = 0; index < a.length; index++) {
583
- if (!isEqual(a[index], b[index])) {
584
- return false;
585
- }
586
- }
587
- return true;
588
- }
589
- if (a && b && typeof a === 'object' && typeof b === 'object') {
590
- const aRecord = a;
591
- const bRecord = b;
592
- const aKeys = Object.keys(aRecord);
593
- const bKeys = Object.keys(bRecord);
594
- if (aKeys.length !== bKeys.length)
595
- return false;
596
- for (const key of aKeys) {
597
- if (!isEqual(aRecord[key], bRecord[key])) {
598
- return false;
599
- }
600
- }
601
- return true;
602
- }
603
- return a === b || (!a && !b);
604
- }
605
- function isNotEqual(a, b) {
606
- return !isEqual(a, b);
607
- }
608
- function isVoid(a) {
609
- return typeof a === 'undefined' || a === null;
610
- }
611
- function diffArrays(a, b) {
612
- return a.filter(c => !b.some(d => isEqual(d, c)));
613
- }
614
- function compareLists(oldList, newList, callbacks) {
615
- const oldMap = keyMap(oldList, ({ name }) => name);
616
- const newMap = keyMap(newList, ({ name }) => name);
617
- const added = [];
618
- const removed = [];
619
- const mutual = [];
620
- for (const oldItem of oldList) {
621
- const newItem = newMap[oldItem.name];
622
- if (newItem === undefined) {
623
- removed.push(oldItem);
624
- }
625
- else {
626
- mutual.push({
627
- newVersion: newItem,
628
- oldVersion: oldItem,
629
- });
630
- }
631
- }
632
- for (const newItem of newList) {
633
- if (oldMap[newItem.name] === undefined) {
634
- added.push(newItem);
635
- }
636
- }
637
- if (callbacks) {
638
- if (callbacks.onAdded) {
639
- added.forEach(callbacks.onAdded);
640
- }
641
- if (callbacks.onRemoved) {
642
- removed.forEach(callbacks.onRemoved);
643
- }
644
- if (callbacks.onMutual) {
645
- mutual.forEach(callbacks.onMutual);
646
- }
647
- }
648
- return {
649
- added,
650
- removed,
651
- mutual,
652
- };
653
- }
654
-
655
- function directiveRemoved(directive) {
656
- return {
657
- criticality: {
658
- level: exports.CriticalityLevel.Breaking,
659
- },
660
- type: exports.ChangeType.DirectiveRemoved,
661
- message: `Directive '${directive.name}' was removed`,
662
- path: `@${directive.name}`,
663
- };
664
- }
665
- function directiveAdded(directive) {
666
- return {
667
- criticality: {
668
- level: exports.CriticalityLevel.NonBreaking,
669
- },
670
- type: exports.ChangeType.DirectiveAdded,
671
- message: `Directive '${directive.name}' was added`,
672
- path: `@${directive.name}`,
673
- };
674
- }
675
- function directiveDescriptionChanged(oldDirective, newDirective) {
676
- return {
677
- criticality: {
678
- level: exports.CriticalityLevel.NonBreaking,
679
- },
680
- type: exports.ChangeType.DirectiveDescriptionChanged,
681
- message: `Directive '${oldDirective.name}' description changed from '${oldDirective.description}' to '${newDirective.description}'`,
682
- path: `@${oldDirective.name}`,
683
- };
684
- }
685
- function directiveLocationAdded(directive, location) {
686
- return {
687
- criticality: {
688
- level: exports.CriticalityLevel.NonBreaking,
689
- },
690
- type: exports.ChangeType.DirectiveLocationAdded,
691
- message: `Location '${location}' was added to directive '${directive.name}'`,
692
- path: `@${directive.name}`,
693
- };
694
- }
695
- function directiveLocationRemoved(directive, location) {
696
- return {
697
- criticality: {
698
- level: exports.CriticalityLevel.Breaking,
699
- },
700
- type: exports.ChangeType.DirectiveLocationRemoved,
701
- message: `Location '${location}' was removed from directive '${directive.name}'`,
702
- path: `@${directive.name}`,
703
- };
704
- }
705
- function directiveArgumentAdded(directive, arg) {
706
- return {
707
- criticality: {
708
- level: graphql.isNonNullType(arg.type) ? exports.CriticalityLevel.Breaking : exports.CriticalityLevel.NonBreaking,
709
- },
710
- type: exports.ChangeType.DirectiveArgumentAdded,
711
- message: `Argument '${arg.name}' was added to directive '${directive.name}'`,
712
- path: `@${directive.name}`,
713
- };
714
- }
715
- function directiveArgumentRemoved(directive, arg) {
716
- return {
717
- criticality: {
718
- level: exports.CriticalityLevel.Breaking,
719
- },
720
- type: exports.ChangeType.DirectiveArgumentRemoved,
721
- message: `Argument '${arg.name}' was removed from directive '${directive.name}'`,
722
- path: `@${directive.name}.${arg.name}`,
723
- };
724
- }
725
- function directiveArgumentDescriptionChanged(directive, oldArg, newArg) {
726
- return {
727
- criticality: {
728
- level: exports.CriticalityLevel.NonBreaking,
729
- },
730
- type: exports.ChangeType.DirectiveArgumentDescriptionChanged,
731
- message: `Description for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.description}' to '${newArg.description}'`,
732
- path: `@${directive.name}.${oldArg.name}`,
733
- };
734
- }
735
- function directiveArgumentDefaultValueChanged(directive, oldArg, newArg) {
736
- return {
737
- criticality: {
738
- level: exports.CriticalityLevel.Dangerous,
739
- reason: 'Changing the default value for an argument may change the runtime behaviour of a field if it was never provided.',
740
- },
741
- type: exports.ChangeType.DirectiveArgumentDefaultValueChanged,
742
- message: typeof oldArg.defaultValue === 'undefined'
743
- ? `Default value '${newArg.defaultValue}' was added to argument '${newArg.name}' on directive '${directive.name}'`
744
- : `Default value for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.defaultValue}' to '${newArg.defaultValue}'`,
745
- path: `@${directive.name}.${oldArg.name}`,
746
- };
747
- }
748
- function directiveArgumentTypeChanged(directive, oldArg, newArg) {
749
- return {
750
- criticality: safeChangeForInputValue(oldArg.type, newArg.type)
751
- ? {
752
- level: exports.CriticalityLevel.NonBreaking,
753
- reason: 'Changing an input field from non-null to null is considered non-breaking.',
754
- }
755
- : {
756
- level: exports.CriticalityLevel.Breaking,
757
- },
758
- type: exports.ChangeType.DirectiveArgumentTypeChanged,
759
- message: `Type for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.type}' to '${newArg.type}'`,
760
- path: `@${directive.name}.${oldArg.name}`,
761
- };
762
- }
763
-
764
- function schemaQueryTypeChanged(oldSchema, newSchema) {
765
- const oldName = (oldSchema.getQueryType() || {}).name || 'unknown';
766
- const newName = (newSchema.getQueryType() || {}).name || 'unknown';
767
- return {
768
- criticality: {
769
- level: exports.CriticalityLevel.Breaking,
770
- },
771
- type: exports.ChangeType.SchemaQueryTypeChanged,
772
- message: `Schema query root has changed from '${oldName}' to '${newName}'`,
773
- };
774
- }
775
- function schemaMutationTypeChanged(oldSchema, newSchema) {
776
- const oldName = (oldSchema.getMutationType() || {}).name || 'unknown';
777
- const newName = (newSchema.getMutationType() || {}).name || 'unknown';
778
- return {
779
- criticality: {
780
- level: exports.CriticalityLevel.Breaking,
781
- },
782
- type: exports.ChangeType.SchemaMutationTypeChanged,
783
- message: `Schema mutation root has changed from '${oldName}' to '${newName}'`,
784
- };
785
- }
786
- function schemaSubscriptionTypeChanged(oldSchema, newSchema) {
787
- const oldName = (oldSchema.getSubscriptionType() || {}).name || 'unknown';
788
- const newName = (newSchema.getSubscriptionType() || {}).name || 'unknown';
789
- return {
790
- criticality: {
791
- level: exports.CriticalityLevel.Breaking,
792
- },
793
- type: exports.ChangeType.SchemaSubscriptionTypeChanged,
794
- message: `Schema subscription root has changed from '${oldName}' to '${newName}'`,
795
- };
796
- }
797
-
798
- function typeRemoved(type) {
799
- return {
800
- criticality: {
801
- level: exports.CriticalityLevel.Breaking,
802
- },
803
- type: exports.ChangeType.TypeRemoved,
804
- message: `Type '${type.name}' was removed`,
805
- path: type.name,
806
- };
807
- }
808
- function typeAdded(type) {
809
- return {
810
- criticality: {
811
- level: exports.CriticalityLevel.NonBreaking,
812
- },
813
- type: exports.ChangeType.TypeAdded,
814
- message: `Type '${type.name}' was added`,
815
- path: type.name,
816
- };
817
- }
818
- function typeKindChanged(oldType, newType) {
819
- return {
820
- criticality: {
821
- level: exports.CriticalityLevel.Breaking,
822
- reason: `Changing the kind of a type is a breaking change because it can cause existing queries to error. For example, turning an object type to a scalar type would break queries that define a selection set for this type.`,
823
- },
824
- type: exports.ChangeType.TypeKindChanged,
825
- message: `'${oldType.name}' kind changed from '${getKind(oldType)}' to '${getKind(newType)}'`,
826
- path: oldType.name,
827
- };
828
- }
829
- function typeDescriptionChanged(oldType, newType) {
830
- return {
831
- criticality: {
832
- level: exports.CriticalityLevel.NonBreaking,
833
- },
834
- type: exports.ChangeType.TypeDescriptionChanged,
835
- message: `Description '${oldType.description}' on type '${oldType.name}' has changed to '${newType.description}'`,
836
- path: oldType.name,
837
- };
838
- }
839
- function typeDescriptionRemoved(type) {
840
- return {
841
- criticality: {
842
- level: exports.CriticalityLevel.NonBreaking,
843
- },
844
- type: exports.ChangeType.TypeDescriptionRemoved,
845
- message: `Description '${type.description}' was removed from object type '${type.name}'`,
846
- path: type.name,
847
- };
848
- }
849
- function typeDescriptionAdded(type) {
850
- return {
851
- criticality: {
852
- level: exports.CriticalityLevel.NonBreaking,
853
- },
854
- type: exports.ChangeType.TypeDescriptionAdded,
855
- message: `Object type '${type.name}' has description '${type.description}'`,
856
- path: type.name,
857
- };
858
- }
859
-
860
- function changesInDirective(oldDirective, newDirective, addChange) {
861
- if (isNotEqual(oldDirective.description, newDirective.description)) {
862
- addChange(directiveDescriptionChanged(oldDirective, newDirective));
863
- }
864
- const locations = {
865
- added: diffArrays(newDirective.locations, oldDirective.locations),
866
- removed: diffArrays(oldDirective.locations, newDirective.locations),
867
- };
868
- // locations added
869
- locations.added.forEach(location => addChange(directiveLocationAdded(newDirective, location)));
870
- // locations removed
871
- locations.removed.forEach(location => addChange(directiveLocationRemoved(oldDirective, location)));
872
- compareLists(oldDirective.args, newDirective.args, {
873
- onAdded(arg) {
874
- addChange(directiveArgumentAdded(newDirective, arg));
875
- },
876
- onRemoved(arg) {
877
- addChange(directiveArgumentRemoved(oldDirective, arg));
878
- },
879
- onMutual(arg) {
880
- changesInDirectiveArgument(oldDirective, arg.oldVersion, arg.newVersion, addChange);
881
- },
882
- });
883
- }
884
- function changesInDirectiveArgument(directive, oldArg, newArg, addChange) {
885
- if (isNotEqual(oldArg.description, newArg.description)) {
886
- addChange(directiveArgumentDescriptionChanged(directive, oldArg, newArg));
887
- }
888
- if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
889
- addChange(directiveArgumentDefaultValueChanged(directive, oldArg, newArg));
890
- }
891
- if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
892
- addChange(directiveArgumentTypeChanged(directive, oldArg, newArg));
893
- }
894
- }
895
-
896
- function enumValueRemoved(oldEnum, value) {
897
- return {
898
- criticality: {
899
- level: exports.CriticalityLevel.Breaking,
900
- reason: `Removing an enum value will cause existing queries that use this enum value to error.`,
901
- },
902
- type: exports.ChangeType.EnumValueRemoved,
903
- message: `Enum value '${value.name}' ${isDeprecated(value) ? '(deprecated) ' : ''}was removed from enum '${oldEnum.name}'`,
904
- path: [oldEnum.name, value.name].join('.'),
905
- };
906
- }
907
- function enumValueAdded(newEnum, value) {
908
- return {
909
- criticality: {
910
- level: exports.CriticalityLevel.Dangerous,
911
- reason: `Adding an enum value may break existing clients that were not programming defensively against an added case when querying an enum.`,
912
- },
913
- type: exports.ChangeType.EnumValueAdded,
914
- message: `Enum value '${value.name}' was added to enum '${newEnum.name}'`,
915
- path: [newEnum.name, value.name].join('.'),
916
- };
917
- }
918
- function enumValueDescriptionChanged(newEnum, oldValue, newValue) {
919
- return {
920
- criticality: {
921
- level: exports.CriticalityLevel.NonBreaking,
922
- },
923
- type: exports.ChangeType.EnumValueDescriptionChanged,
924
- message: typeof oldValue.description === 'undefined'
925
- ? `Description '${newValue.description}' was added to enum value '${newEnum.name}.${newValue.name}'`
926
- : `Description for enum value '${newEnum.name}.${newValue.name}' changed from '${oldValue.description}' to '${newValue.description}'`,
927
- path: [newEnum.name, oldValue.name].join('.'),
928
- };
929
- }
930
- function enumValueDeprecationReasonChanged(newEnum, oldValue, newValue) {
931
- return {
932
- criticality: {
933
- level: exports.CriticalityLevel.NonBreaking,
934
- },
935
- type: exports.ChangeType.EnumValueDeprecationReasonChanged,
936
- message: `Enum value '${newEnum.name}.${newValue.name}' deprecation reason changed from '${oldValue.deprecationReason}' to '${newValue.deprecationReason}'`,
937
- path: [newEnum.name, oldValue.name].join('.'),
938
- };
939
- }
940
- function enumValueDeprecationReasonAdded(newEnum, oldValue, newValue) {
941
- return {
942
- criticality: {
943
- level: exports.CriticalityLevel.NonBreaking,
944
- },
945
- type: exports.ChangeType.EnumValueDeprecationReasonAdded,
946
- message: `Enum value '${newEnum.name}.${newValue.name}' was deprecated with reason '${newValue.deprecationReason}'`,
947
- path: [newEnum.name, oldValue.name].join('.'),
948
- };
949
- }
950
- function enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue) {
951
- return {
952
- criticality: {
953
- level: exports.CriticalityLevel.NonBreaking,
954
- },
955
- type: exports.ChangeType.EnumValueDeprecationReasonRemoved,
956
- message: `Deprecation reason was removed from enum value '${newEnum.name}.${newValue.name}'`,
957
- path: [newEnum.name, oldValue.name].join('.'),
958
- };
959
- }
960
-
961
- function changesInEnum(oldEnum, newEnum, addChange) {
962
- compareLists(oldEnum.getValues(), newEnum.getValues(), {
963
- onAdded(value) {
964
- addChange(enumValueAdded(newEnum, value));
965
- },
966
- onRemoved(value) {
967
- addChange(enumValueRemoved(oldEnum, value));
968
- },
969
- onMutual(value) {
970
- const oldValue = value.oldVersion;
971
- const newValue = value.newVersion;
972
- if (isNotEqual(oldValue.description, newValue.description)) {
973
- addChange(enumValueDescriptionChanged(newEnum, oldValue, newValue));
974
- }
975
- if (isNotEqual(oldValue.deprecationReason, newValue.deprecationReason)) {
976
- if (isVoid(oldValue.deprecationReason)) {
977
- addChange(enumValueDeprecationReasonAdded(newEnum, oldValue, newValue));
978
- }
979
- else if (isVoid(newValue.deprecationReason)) {
980
- addChange(enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue));
981
- }
982
- else {
983
- addChange(enumValueDeprecationReasonChanged(newEnum, oldValue, newValue));
984
- }
985
- }
986
- },
987
- });
988
- }
989
-
990
- function compareTwoStrings(str1, str2) {
991
- if (!str1.length && !str2.length)
992
- return 1;
993
- if (!str1.length || !str2.length)
994
- return 0;
995
- if (str1.toUpperCase() === str2.toUpperCase())
996
- return 1;
997
- if (str1.length === 1 && str2.length === 1)
998
- return 0;
999
- const pairs1 = wordLetterPairs(str1);
1000
- const pairs2 = wordLetterPairs(str2);
1001
- const union = pairs1.length + pairs2.length;
1002
- let intersection = 0;
1003
- pairs1.forEach(pair1 => {
1004
- for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
1005
- if (pair1 !== pair2)
1006
- continue;
1007
- intersection++;
1008
- pairs2.splice(i, 1);
1009
- break;
1010
- }
1011
- });
1012
- return (intersection * 2) / union;
1013
- }
1014
- function findBestMatch(mainString, targetStrings) {
1015
- if (!areArgsValid(mainString, targetStrings))
1016
- throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
1017
- const ratings = targetStrings.map(target => ({
1018
- target,
1019
- rating: compareTwoStrings(mainString, target.value),
1020
- }));
1021
- const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
1022
- return { ratings, bestMatch };
1023
- }
1024
- function flattenDeep(arr) {
1025
- return Array.isArray(arr) ? arr.reduce((a, b) => a.concat(flattenDeep(b)), []) : [arr];
1026
- }
1027
- function areArgsValid(mainString, targetStrings) {
1028
- if (typeof mainString !== 'string')
1029
- return false;
1030
- if (!Array.isArray(targetStrings))
1031
- return false;
1032
- if (!targetStrings.length)
1033
- return false;
1034
- if (targetStrings.find(s => typeof s.value !== 'string'))
1035
- return false;
1036
- return true;
1037
- }
1038
- function letterPairs(str) {
1039
- const pairs = [];
1040
- for (let i = 0, max = str.length - 1; i < max; i++)
1041
- pairs[i] = str.substring(i, i + 2);
1042
- return pairs;
1043
- }
1044
- function wordLetterPairs(str) {
1045
- const pairs = str.toUpperCase().split(' ').map(letterPairs);
1046
- return flattenDeep(pairs);
1047
- }
1048
- function safeString(obj) {
1049
- return inspect(obj)
1050
- .replace(/\[Object: null prototype\] /g, '')
1051
- .replace(/(^')|('$)/g, '');
1052
- }
1053
-
1054
- function inputFieldRemoved(input, field) {
1055
- return {
1056
- criticality: {
1057
- level: exports.CriticalityLevel.Breaking,
1058
- reason: 'Removing an input field will cause existing queries that use this input field to error.',
1059
- },
1060
- type: exports.ChangeType.InputFieldRemoved,
1061
- message: `Input field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from input object type '${input.name}'`,
1062
- path: [input.name, field.name].join('.'),
1063
- };
1064
- }
1065
- function inputFieldAdded(input, field) {
1066
- return {
1067
- criticality: graphql.isNonNullType(field.type)
1068
- ? {
1069
- level: exports.CriticalityLevel.Breaking,
1070
- reason: 'Adding a required input field to an existing input object type is a breaking change because it will cause existing uses of this input object type to error.',
1071
- }
1072
- : {
1073
- level: exports.CriticalityLevel.Dangerous,
1074
- },
1075
- type: exports.ChangeType.InputFieldAdded,
1076
- message: `Input field '${field.name}' was added to input object type '${input.name}'`,
1077
- path: [input.name, field.name].join('.'),
1078
- };
1079
- }
1080
- function inputFieldDescriptionAdded(type, field) {
1081
- return {
1082
- criticality: {
1083
- level: exports.CriticalityLevel.NonBreaking,
1084
- },
1085
- type: exports.ChangeType.InputFieldDescriptionAdded,
1086
- message: `Input field '${type.name}.${field.name}' has description '${field.description}'`,
1087
- path: [type.name, field.name].join('.'),
1088
- };
1089
- }
1090
- function inputFieldDescriptionRemoved(type, field) {
1091
- return {
1092
- criticality: {
1093
- level: exports.CriticalityLevel.NonBreaking,
1094
- },
1095
- type: exports.ChangeType.InputFieldDescriptionRemoved,
1096
- message: `Description was removed from input field '${type.name}.${field.name}'`,
1097
- path: [type.name, field.name].join('.'),
1098
- };
1099
- }
1100
- function inputFieldDescriptionChanged(input, oldField, newField) {
1101
- return {
1102
- criticality: {
1103
- level: exports.CriticalityLevel.NonBreaking,
1104
- },
1105
- type: exports.ChangeType.InputFieldDescriptionChanged,
1106
- message: `Input field '${input.name}.${oldField.name}' description changed from '${oldField.description}' to '${newField.description}'`,
1107
- path: [input.name, oldField.name].join('.'),
1108
- };
1109
- }
1110
- function inputFieldDefaultValueChanged(input, oldField, newField) {
1111
- return {
1112
- criticality: {
1113
- level: exports.CriticalityLevel.Dangerous,
1114
- reason: 'Changing the default value for an argument may change the runtime behavior of a field if it was never provided.',
1115
- },
1116
- type: exports.ChangeType.InputFieldDefaultValueChanged,
1117
- message: `Input field '${input.name}.${oldField.name}' default value changed from '${safeString(oldField.defaultValue)}' to '${safeString(newField.defaultValue)}'`,
1118
- path: [input.name, oldField.name].join('.'),
1119
- };
1120
- }
1121
- function inputFieldTypeChanged(input, oldField, newField) {
1122
- return {
1123
- criticality: safeChangeForInputValue(oldField.type, newField.type)
1124
- ? {
1125
- level: exports.CriticalityLevel.NonBreaking,
1126
- reason: 'Changing an input field from non-null to null is considered non-breaking.',
1127
- }
1128
- : {
1129
- level: exports.CriticalityLevel.Breaking,
1130
- reason: 'Changing the type of an input field can cause existing queries that use this field to error.',
1131
- },
1132
- type: exports.ChangeType.InputFieldTypeChanged,
1133
- message: `Input field '${input.name}.${oldField.name}' changed type from '${oldField.type.toString()}' to '${newField.type.toString()}'`,
1134
- path: [input.name, oldField.name].join('.'),
1135
- };
1136
- }
1137
-
1138
- function changesInInputObject(oldInput, newInput, addChange) {
1139
- const oldFields = oldInput.getFields();
1140
- const newFields = newInput.getFields();
1141
- compareLists(Object.values(oldFields), Object.values(newFields), {
1142
- onAdded(field) {
1143
- addChange(inputFieldAdded(newInput, field));
1144
- },
1145
- onRemoved(field) {
1146
- addChange(inputFieldRemoved(oldInput, field));
1147
- },
1148
- onMutual(field) {
1149
- changesInInputField(oldInput, field.oldVersion, field.newVersion, addChange);
1150
- },
1151
- });
1152
- }
1153
- function changesInInputField(input, oldField, newField, addChange) {
1154
- if (isNotEqual(oldField.description, newField.description)) {
1155
- if (isVoid(oldField.description)) {
1156
- addChange(inputFieldDescriptionAdded(input, newField));
1157
- }
1158
- else if (isVoid(newField.description)) {
1159
- addChange(inputFieldDescriptionRemoved(input, oldField));
1160
- }
1161
- else {
1162
- addChange(inputFieldDescriptionChanged(input, oldField, newField));
1163
- }
1164
- }
1165
- if (isNotEqual(oldField.defaultValue, newField.defaultValue)) {
1166
- if (Array.isArray(oldField.defaultValue) && Array.isArray(newField.defaultValue)) {
1167
- if (diffArrays(oldField.defaultValue, newField.defaultValue).length > 0) {
1168
- addChange(inputFieldDefaultValueChanged(input, oldField, newField));
1169
- }
1170
- }
1171
- else if (JSON.stringify(oldField.defaultValue) !== JSON.stringify(newField.defaultValue)) {
1172
- addChange(inputFieldDefaultValueChanged(input, oldField, newField));
1173
- }
1174
- }
1175
- if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
1176
- addChange(inputFieldTypeChanged(input, oldField, newField));
1177
- }
1178
- }
1179
-
1180
- function fieldRemoved(type, field) {
1181
- const entity = graphql.isInterfaceType(type) ? 'interface' : 'object type';
1182
- return {
1183
- criticality: {
1184
- level: exports.CriticalityLevel.Breaking,
1185
- reason: field.deprecationReason
1186
- ? `Removing a deprecated field is a breaking change. Before removing it, you may want to look at the field's usage to see the impact of removing the field.`
1187
- : `Removing a field is a breaking change. It is preferable to deprecate the field before removing it.`,
1188
- },
1189
- type: exports.ChangeType.FieldRemoved,
1190
- message: `Field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from ${entity} '${type.name}'`,
1191
- path: [type.name, field.name].join('.'),
1192
- };
1193
- }
1194
- function fieldAdded(type, field) {
1195
- const entity = graphql.isInterfaceType(type) ? 'interface' : 'object type';
1196
- return {
1197
- criticality: {
1198
- level: exports.CriticalityLevel.NonBreaking,
1199
- },
1200
- type: exports.ChangeType.FieldAdded,
1201
- message: `Field '${field.name}' was added to ${entity} '${type.name}'`,
1202
- path: [type.name, field.name].join('.'),
1203
- };
1204
- }
1205
- function fieldDescriptionChanged(type, oldField, newField) {
1206
- return {
1207
- criticality: {
1208
- level: exports.CriticalityLevel.NonBreaking,
1209
- },
1210
- type: exports.ChangeType.FieldDescriptionChanged,
1211
- message: `Field '${type.name}.${oldField.name}' description changed from '${oldField.description}' to '${newField.description}'`,
1212
- path: [type.name, oldField.name].join('.'),
1213
- };
1214
- }
1215
- function fieldDescriptionAdded(type, field) {
1216
- return {
1217
- criticality: {
1218
- level: exports.CriticalityLevel.NonBreaking,
1219
- },
1220
- type: exports.ChangeType.FieldDescriptionAdded,
1221
- message: `Field '${type.name}.${field.name}' has description '${field.description}'`,
1222
- path: [type.name, field.name].join('.'),
1223
- };
1224
- }
1225
- function fieldDescriptionRemoved(type, field) {
1226
- return {
1227
- criticality: {
1228
- level: exports.CriticalityLevel.NonBreaking,
1229
- },
1230
- type: exports.ChangeType.FieldDescriptionRemoved,
1231
- message: `Description was removed from field '${type.name}.${field.name}'`,
1232
- path: [type.name, field.name].join('.'),
1233
- };
1234
- }
1235
- function fieldDeprecationAdded(type, field) {
1236
- return {
1237
- criticality: {
1238
- level: exports.CriticalityLevel.NonBreaking,
1239
- },
1240
- type: exports.ChangeType.FieldDeprecationAdded,
1241
- message: `Field '${type.name}.${field.name}' is deprecated`,
1242
- path: [type.name, field.name].join('.'),
1243
- };
1244
- }
1245
- function fieldDeprecationRemoved(type, field) {
1246
- return {
1247
- criticality: {
1248
- level: exports.CriticalityLevel.Dangerous,
1249
- },
1250
- type: exports.ChangeType.FieldDeprecationRemoved,
1251
- message: `Field '${type.name}.${field.name}' is no longer deprecated`,
1252
- path: [type.name, field.name].join('.'),
1253
- };
1254
- }
1255
- function fieldDeprecationReasonChanged(type, oldField, newField) {
1256
- return {
1257
- criticality: {
1258
- level: exports.CriticalityLevel.NonBreaking,
1259
- },
1260
- type: exports.ChangeType.FieldDeprecationReasonChanged,
1261
- message: `Deprecation reason on field '${type.name}.${newField.name}' has changed from '${oldField.deprecationReason}' to '${newField.deprecationReason}'`,
1262
- path: [type.name, oldField.name].join('.'),
1263
- };
1264
- }
1265
- function fieldDeprecationReasonAdded(type, field) {
1266
- return {
1267
- criticality: {
1268
- level: exports.CriticalityLevel.NonBreaking,
1269
- },
1270
- type: exports.ChangeType.FieldDeprecationReasonAdded,
1271
- message: `Field '${type.name}.${field.name}' has deprecation reason '${field.deprecationReason}'`,
1272
- path: [type.name, field.name].join('.'),
1273
- };
1274
- }
1275
- function fieldDeprecationReasonRemoved(type, field) {
1276
- return {
1277
- criticality: {
1278
- level: exports.CriticalityLevel.NonBreaking,
1279
- },
1280
- type: exports.ChangeType.FieldDeprecationReasonRemoved,
1281
- message: `Deprecation reason was removed from field '${type.name}.${field.name}'`,
1282
- path: [type.name, field.name].join('.'),
1283
- };
1284
- }
1285
- function fieldTypeChanged(type, oldField, newField) {
1286
- return {
1287
- criticality: {
1288
- level: safeChangeForField(oldField.type, newField.type)
1289
- ? exports.CriticalityLevel.NonBreaking
1290
- : exports.CriticalityLevel.Breaking,
1291
- },
1292
- type: exports.ChangeType.FieldTypeChanged,
1293
- message: `Field '${type}.${oldField.name}' changed type from '${oldField.type}' to '${newField.type}'`,
1294
- path: [type.name, oldField.name].join('.'),
1295
- };
1296
- }
1297
- function fieldArgumentAdded(type, field, arg) {
1298
- const isBreaking = graphql.isNonNullType(arg.type) && typeof arg.defaultValue === 'undefined';
1299
- const defaultValueMsg = typeof arg.defaultValue === 'undefined' ? ' ' : ' (with default value) ';
1300
- return {
1301
- criticality: isBreaking
1302
- ? {
1303
- level: exports.CriticalityLevel.Breaking,
1304
- reason: `Adding a required argument to an existing field is a breaking change because it will cause existing uses of this field to error.`,
1305
- }
1306
- : {
1307
- level: exports.CriticalityLevel.Dangerous,
1308
- reason: `Adding a new argument to an existing field may involve a change in resolve function logic that potentially may cause some side effects.`,
1309
- },
1310
- type: exports.ChangeType.FieldArgumentAdded,
1311
- message: `Argument '${arg.name}: ${arg.type}'${defaultValueMsg}added to field '${type.name}.${field.name}'`,
1312
- path: [type.name, field.name, arg.name].join('.'),
1313
- };
1314
- }
1315
- function fieldArgumentRemoved(type, field, arg) {
1316
- return {
1317
- criticality: {
1318
- level: exports.CriticalityLevel.Breaking,
1319
- reason: `Removing a field argument is a breaking change because it will cause existing queries that use this argument to error.`,
1320
- },
1321
- type: exports.ChangeType.FieldArgumentRemoved,
1322
- message: `Argument '${arg.name}: ${arg.type}' was removed from field '${type.name}.${field.name}'`,
1323
- path: [type.name, field.name, arg.name].join('.'),
1324
- };
1325
- }
1326
-
1327
- function fieldArgumentDescriptionChanged(type, field, oldArg, newArg) {
1328
- return {
1329
- criticality: {
1330
- level: exports.CriticalityLevel.NonBreaking,
1331
- },
1332
- type: exports.ChangeType.FieldArgumentDescriptionChanged,
1333
- message: `Description for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.description}' to '${newArg.description}'`,
1334
- path: [type.name, field.name, oldArg.name].join('.'),
1335
- };
1336
- }
1337
- function fieldArgumentDefaultChanged(type, field, oldArg, newArg) {
1338
- return {
1339
- criticality: {
1340
- level: exports.CriticalityLevel.Dangerous,
1341
- reason: 'Changing the default value for an argument may change the runtime behaviour of a field if it was never provided.',
1342
- },
1343
- type: exports.ChangeType.FieldArgumentDefaultChanged,
1344
- message: typeof oldArg.defaultValue === 'undefined'
1345
- ? `Default value '${safeString(newArg.defaultValue)}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
1346
- : `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${safeString(oldArg.defaultValue)}' to '${safeString(newArg.defaultValue)}'`,
1347
- path: [type.name, field.name, oldArg.name].join('.'),
1348
- };
1349
- }
1350
- function fieldArgumentTypeChanged(type, field, oldArg, newArg) {
1351
- return {
1352
- criticality: safeChangeForInputValue(oldArg.type, newArg.type)
1353
- ? {
1354
- level: exports.CriticalityLevel.NonBreaking,
1355
- reason: `Changing an input field from non-null to null is considered non-breaking.`,
1356
- }
1357
- : {
1358
- level: exports.CriticalityLevel.Breaking,
1359
- reason: `Changing the type of a field's argument can cause existing queries that use this argument to error.`,
1360
- },
1361
- type: exports.ChangeType.FieldArgumentTypeChanged,
1362
- message: `Type for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.type}' to '${newArg.type}'`,
1363
- path: [type.name, field.name, oldArg.name].join('.'),
1364
- };
1365
- }
1366
-
1367
- function changesInArgument(type, field, oldArg, newArg, addChange) {
1368
- if (isNotEqual(oldArg.description, newArg.description)) {
1369
- addChange(fieldArgumentDescriptionChanged(type, field, oldArg, newArg));
1370
- }
1371
- if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
1372
- if (Array.isArray(oldArg.defaultValue) && Array.isArray(newArg.defaultValue)) {
1373
- const diff = diffArrays(oldArg.defaultValue, newArg.defaultValue);
1374
- if (diff.length > 0) {
1375
- addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
1376
- }
1377
- }
1378
- else if (JSON.stringify(oldArg.defaultValue) !== JSON.stringify(newArg.defaultValue)) {
1379
- addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
1380
- }
1381
- }
1382
- if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
1383
- addChange(fieldArgumentTypeChanged(type, field, oldArg, newArg));
1384
- }
1385
- }
1386
-
1387
- function changesInField(type, oldField, newField, addChange) {
1388
- if (isNotEqual(oldField.description, newField.description)) {
1389
- if (isVoid(oldField.description)) {
1390
- addChange(fieldDescriptionAdded(type, newField));
1391
- }
1392
- else if (isVoid(newField.description)) {
1393
- addChange(fieldDescriptionRemoved(type, oldField));
1394
- }
1395
- else {
1396
- addChange(fieldDescriptionChanged(type, oldField, newField));
1397
- }
1398
- }
1399
- if (isNotEqual(isDeprecated(oldField), isDeprecated(newField))) {
1400
- if (isDeprecated(newField)) {
1401
- addChange(fieldDeprecationAdded(type, newField));
1402
- }
1403
- else {
1404
- addChange(fieldDeprecationRemoved(type, oldField));
1405
- }
1406
- }
1407
- if (isNotEqual(oldField.deprecationReason, newField.deprecationReason)) {
1408
- if (isVoid(oldField.deprecationReason)) {
1409
- addChange(fieldDeprecationReasonAdded(type, newField));
1410
- }
1411
- else if (isVoid(newField.deprecationReason)) {
1412
- addChange(fieldDeprecationReasonRemoved(type, oldField));
1413
- }
1414
- else {
1415
- addChange(fieldDeprecationReasonChanged(type, oldField, newField));
1416
- }
1417
- }
1418
- if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
1419
- addChange(fieldTypeChanged(type, oldField, newField));
1420
- }
1421
- compareLists(oldField.args, newField.args, {
1422
- onAdded(arg) {
1423
- addChange(fieldArgumentAdded(type, newField, arg));
1424
- },
1425
- onRemoved(arg) {
1426
- addChange(fieldArgumentRemoved(type, oldField, arg));
1427
- },
1428
- onMutual(arg) {
1429
- changesInArgument(type, oldField, arg.oldVersion, arg.newVersion, addChange);
1430
- },
1431
- });
1432
- }
1433
-
1434
- function changesInInterface(oldInterface, newInterface, addChange) {
1435
- compareLists(Object.values(oldInterface.getFields()), Object.values(newInterface.getFields()), {
1436
- onAdded(field) {
1437
- addChange(fieldAdded(newInterface, field));
1438
- },
1439
- onRemoved(field) {
1440
- addChange(fieldRemoved(oldInterface, field));
1441
- },
1442
- onMutual(field) {
1443
- changesInField(oldInterface, field.oldVersion, field.newVersion, addChange);
1444
- },
1445
- });
1446
- }
1447
-
1448
- function objectTypeInterfaceAdded(iface, type) {
1449
- return {
1450
- criticality: {
1451
- level: exports.CriticalityLevel.Dangerous,
1452
- reason: 'Adding an interface to an object type may break existing clients that were not programming defensively against a new possible type.',
1453
- },
1454
- type: exports.ChangeType.ObjectTypeInterfaceAdded,
1455
- message: `'${type.name}' object implements '${iface.name}' interface`,
1456
- path: type.name,
1457
- };
1458
- }
1459
- function objectTypeInterfaceRemoved(iface, type) {
1460
- return {
1461
- criticality: {
1462
- level: exports.CriticalityLevel.Breaking,
1463
- reason: 'Removing an interface from an object type can cause existing queries that use this in a fragment spread to error.',
1464
- },
1465
- type: exports.ChangeType.ObjectTypeInterfaceRemoved,
1466
- message: `'${type.name}' object type no longer implements '${iface.name}' interface`,
1467
- path: type.name,
1468
- };
1469
- }
1470
-
1471
- function changesInObject(oldType, newType, addChange) {
1472
- const oldInterfaces = oldType.getInterfaces();
1473
- const newInterfaces = newType.getInterfaces();
1474
- const oldFields = oldType.getFields();
1475
- const newFields = newType.getFields();
1476
- compareLists(oldInterfaces, newInterfaces, {
1477
- onAdded(i) {
1478
- addChange(objectTypeInterfaceAdded(i, newType));
1479
- },
1480
- onRemoved(i) {
1481
- addChange(objectTypeInterfaceRemoved(i, oldType));
1482
- },
1483
- });
1484
- compareLists(Object.values(oldFields), Object.values(newFields), {
1485
- onAdded(f) {
1486
- addChange(fieldAdded(newType, f));
1487
- },
1488
- onRemoved(f) {
1489
- addChange(fieldRemoved(oldType, f));
1490
- },
1491
- onMutual(f) {
1492
- changesInField(oldType, f.oldVersion, f.newVersion, addChange);
1493
- },
1494
- });
1495
- }
1496
-
1497
- function unionMemberRemoved(union, type) {
1498
- return {
1499
- criticality: {
1500
- level: exports.CriticalityLevel.Breaking,
1501
- reason: 'Removing a union member from a union can cause existing queries that use this union member in a fragment spread to error.',
1502
- },
1503
- type: exports.ChangeType.UnionMemberRemoved,
1504
- message: `Member '${type.name}' was removed from Union type '${union.name}'`,
1505
- path: union.name,
1506
- };
1507
- }
1508
- function unionMemberAdded(union, type) {
1509
- return {
1510
- criticality: {
1511
- level: exports.CriticalityLevel.Dangerous,
1512
- reason: 'Adding a possible type to Unions may break existing clients that were not programming defensively against a new possible type.',
1513
- },
1514
- type: exports.ChangeType.UnionMemberAdded,
1515
- message: `Member '${type.name}' was added to Union type '${union.name}'`,
1516
- path: union.name,
1517
- };
1518
- }
1519
-
1520
- function changesInUnion(oldUnion, newUnion, addChange) {
1521
- const oldTypes = oldUnion.getTypes();
1522
- const newTypes = newUnion.getTypes();
1523
- compareLists(oldTypes, newTypes, {
1524
- onAdded(t) {
1525
- addChange(unionMemberAdded(newUnion, t));
1526
- },
1527
- onRemoved(t) {
1528
- addChange(unionMemberRemoved(oldUnion, t));
1529
- },
1530
- });
1531
- }
1532
-
1533
- function diffSchema(oldSchema, newSchema) {
1534
- const changes = [];
1535
- function addChange(change) {
1536
- changes.push(change);
1537
- }
1538
- changesInSchema(oldSchema, newSchema, addChange);
1539
- compareLists(Object.values(oldSchema.getTypeMap()).filter(t => !isPrimitive(t)), Object.values(newSchema.getTypeMap()).filter(t => !isPrimitive(t)), {
1540
- onAdded(type) {
1541
- addChange(typeAdded(type));
1542
- },
1543
- onRemoved(type) {
1544
- addChange(typeRemoved(type));
1545
- },
1546
- onMutual(type) {
1547
- changesInType(type.oldVersion, type.newVersion, addChange);
1548
- },
1549
- });
1550
- compareLists(oldSchema.getDirectives(), newSchema.getDirectives(), {
1551
- onAdded(directive) {
1552
- addChange(directiveAdded(directive));
1553
- },
1554
- onRemoved(directive) {
1555
- addChange(directiveRemoved(directive));
1556
- },
1557
- onMutual(directive) {
1558
- changesInDirective(directive.oldVersion, directive.newVersion, addChange);
1559
- },
1560
- });
1561
- return changes;
1562
- }
1563
- function changesInSchema(oldSchema, newSchema, addChange) {
1564
- var _a, _b, _c, _d, _e, _f;
1565
- const defaultNames = {
1566
- query: 'Query',
1567
- mutation: 'Mutation',
1568
- subscription: 'Subscription',
1569
- };
1570
- const oldRoot = {
1571
- query: (_a = (oldSchema.getQueryType() || {}).name) !== null && _a !== void 0 ? _a : defaultNames.query,
1572
- mutation: (_b = (oldSchema.getMutationType() || {}).name) !== null && _b !== void 0 ? _b : defaultNames.mutation,
1573
- subscription: (_c = (oldSchema.getSubscriptionType() || {}).name) !== null && _c !== void 0 ? _c : defaultNames.subscription,
1574
- };
1575
- const newRoot = {
1576
- query: (_d = (newSchema.getQueryType() || {}).name) !== null && _d !== void 0 ? _d : defaultNames.query,
1577
- mutation: (_e = (newSchema.getMutationType() || {}).name) !== null && _e !== void 0 ? _e : defaultNames.mutation,
1578
- subscription: (_f = (newSchema.getSubscriptionType() || {}).name) !== null && _f !== void 0 ? _f : defaultNames.subscription,
1579
- };
1580
- if (isNotEqual(oldRoot.query, newRoot.query)) {
1581
- addChange(schemaQueryTypeChanged(oldSchema, newSchema));
1582
- }
1583
- if (isNotEqual(oldRoot.mutation, newRoot.mutation)) {
1584
- addChange(schemaMutationTypeChanged(oldSchema, newSchema));
1585
- }
1586
- if (isNotEqual(oldRoot.subscription, newRoot.subscription)) {
1587
- addChange(schemaSubscriptionTypeChanged(oldSchema, newSchema));
1588
- }
1589
- }
1590
- function changesInType(oldType, newType, addChange) {
1591
- if (graphql.isEnumType(oldType) && graphql.isEnumType(newType)) {
1592
- changesInEnum(oldType, newType, addChange);
1593
- }
1594
- else if (graphql.isUnionType(oldType) && graphql.isUnionType(newType)) {
1595
- changesInUnion(oldType, newType, addChange);
1596
- }
1597
- else if (graphql.isInputObjectType(oldType) && graphql.isInputObjectType(newType)) {
1598
- changesInInputObject(oldType, newType, addChange);
1599
- }
1600
- else if (graphql.isObjectType(oldType) && graphql.isObjectType(newType)) {
1601
- changesInObject(oldType, newType, addChange);
1602
- }
1603
- else if (graphql.isInterfaceType(oldType) && graphql.isInterfaceType(newType)) {
1604
- changesInInterface(oldType, newType, addChange);
1605
- }
1606
- else if (graphql.isScalarType(oldType) && graphql.isScalarType(newType)) ;
1607
- else {
1608
- addChange(typeKindChanged(oldType, newType));
1609
- }
1610
- if (isNotEqual(oldType.description, newType.description)) {
1611
- if (isVoid(oldType.description)) {
1612
- addChange(typeDescriptionAdded(newType));
1613
- }
1614
- else if (isVoid(newType.description)) {
1615
- addChange(typeDescriptionRemoved(oldType));
1616
- }
1617
- else {
1618
- addChange(typeDescriptionChanged(oldType, newType));
1619
- }
1620
- }
1621
- }
1622
-
1623
- const DiffRule = rules;
1624
- function diff(oldSchema, newSchema, rules = [], config) {
1625
- const changes = diffSchema(oldSchema, newSchema);
1626
- return rules.reduce((prev, rule) => tslib.__awaiter(this, void 0, void 0, function* () {
1627
- const prevChanges = yield prev;
1628
- return rule({
1629
- changes: prevChanges,
1630
- oldSchema,
1631
- newSchema,
1632
- config,
1633
- });
1634
- }), Promise.resolve(changes));
1635
- }
1636
-
1637
- function similar(schema, typeName, threshold = 0.4) {
1638
- const typeMap = schema.getTypeMap();
1639
- const targets = Object.keys(schema.getTypeMap())
1640
- .filter(name => !isPrimitive(name) && !isForIntrospection(name))
1641
- .map(name => ({
1642
- typeId: name,
1643
- value: stripType(typeMap[name]),
1644
- }));
1645
- const results = {};
1646
- if (typeof typeName !== 'undefined' && !targets.some(t => t.typeId === typeName)) {
1647
- throw new Error(`Type '${typeName}' doesn't exist`);
1648
- }
1649
- (typeName ? [{ typeId: typeName, value: '' }] : targets).forEach(source => {
1650
- const sourceType = schema.getType(source.typeId);
1651
- const matchWith = targets.filter(target => schema.getType(target.typeId).astNode.kind === sourceType.astNode.kind &&
1652
- target.typeId !== source.typeId);
1653
- if (matchWith.length > 0) {
1654
- const found = similarTo(sourceType, matchWith, threshold);
1655
- if (found) {
1656
- results[source.typeId] = found;
1657
- }
1658
- }
1659
- });
1660
- return results;
1661
- }
1662
- function similarTo(type, targets, threshold) {
1663
- const types = targets.filter(target => target.typeId !== type.name);
1664
- const result = findBestMatch(stripType(type), types);
1665
- if (result.bestMatch.rating < threshold) {
1666
- return;
1667
- }
1668
- return {
1669
- bestMatch: result.bestMatch,
1670
- ratings: result.ratings
1671
- .filter(r => r.rating >= threshold && r.target !== result.bestMatch.target)
1672
- .sort((a, b) => a.rating - b.rating)
1673
- .reverse(),
1674
- };
1675
- }
1676
- function stripType(type) {
1677
- return graphql.printType(type)
1678
- .trim()
1679
- .replace(/^[a-z]+ [^{]+\{/g, '')
1680
- .replace(/\}$/g, '')
1681
- .trim()
1682
- .split('\n')
1683
- .map(s => s.trim())
1684
- .sort((a, b) => a.localeCompare(b))
1685
- .join(' ');
1686
- }
1687
-
1688
- function transformDocumentWithApollo(doc, { keepClientFields }) {
1689
- return graphql.visit(doc, {
1690
- Field(node) {
1691
- return keepClientFields
1692
- ? removeDirectives(node, ['client'])
1693
- : removeFieldIfDirectives(node, ['client']);
1694
- },
1695
- });
1696
- }
1697
- function transformSchemaWithApollo(schema) {
1698
- return graphql.extendSchema(schema, graphql.parse(/* GraphQL */ `
1699
- directive @connection(key: String!, filter: [String]) on FIELD
1700
- `));
1701
- }
1702
-
1703
- function validateAliasCount({ source, doc, maxAliasCount, fragmentGraph, }) {
1704
- var _a;
1705
- const getFragmentByFragmentName = (fragmentName) => fragmentGraph.getNodeData(fragmentName);
1706
- for (const definition of doc.definitions) {
1707
- if (definition.kind !== graphql.Kind.OPERATION_DEFINITION) {
1708
- continue;
1709
- }
1710
- const aliasCount = countAliases(definition, getFragmentByFragmentName);
1711
- if (aliasCount > maxAliasCount) {
1712
- return new graphql.GraphQLError(`Too many aliases (${aliasCount}). Maximum allowed is ${maxAliasCount}`, [definition], source, ((_a = definition.loc) === null || _a === void 0 ? void 0 : _a.start) ? [definition.loc.start] : undefined);
1713
- }
1714
- }
1715
- }
1716
- function countAliases(node, getFragmentByName) {
1717
- let aliases = 0;
1718
- if ('alias' in node && node.alias) {
1719
- ++aliases;
1720
- }
1721
- if ('selectionSet' in node && node.selectionSet) {
1722
- for (const child of node.selectionSet.selections) {
1723
- aliases += countAliases(child, getFragmentByName);
1724
- }
1725
- }
1726
- else if (node.kind === graphql.Kind.FRAGMENT_SPREAD) {
1727
- const fragmentNode = getFragmentByName(node.name.value);
1728
- if (fragmentNode) {
1729
- aliases += countAliases(fragmentNode, getFragmentByName);
1730
- }
1731
- }
1732
- return aliases;
1733
- }
1734
-
1735
- function validateDirectiveCount({ source, doc, maxDirectiveCount, fragmentGraph, }) {
1736
- var _a;
1737
- const getFragmentByFragmentName = (fragmentName) => fragmentGraph.getNodeData(fragmentName);
1738
- for (const definition of doc.definitions) {
1739
- if (definition.kind !== graphql.Kind.OPERATION_DEFINITION) {
1740
- continue;
1741
- }
1742
- const directiveCount = countDirectives(definition, getFragmentByFragmentName);
1743
- if (directiveCount > maxDirectiveCount) {
1744
- return new graphql.GraphQLError(`Too many directives (${directiveCount}). Maximum allowed is ${maxDirectiveCount}`, [definition], source, ((_a = definition.loc) === null || _a === void 0 ? void 0 : _a.start) ? [definition.loc.start] : undefined);
1745
- }
1746
- }
1747
- }
1748
- function countDirectives(node, getFragmentByName) {
1749
- let directives = 0;
1750
- if (node.directives) {
1751
- directives += node.directives.length;
1752
- }
1753
- if ('selectionSet' in node && node.selectionSet) {
1754
- for (const child of node.selectionSet.selections) {
1755
- directives += countDirectives(child, getFragmentByName);
1756
- }
1757
- }
1758
- if (node.kind == graphql.Kind.FRAGMENT_SPREAD) {
1759
- const fragment = getFragmentByName(node.name.value);
1760
- if (fragment) {
1761
- directives += countDirectives(fragment, getFragmentByName);
1762
- }
1763
- }
1764
- return directives;
1765
- }
1766
-
1767
- function validateQueryDepth({ source, doc, maxDepth, fragmentGraph, }) {
1768
- var _a;
1769
- try {
1770
- calculateDepth({
1771
- node: doc,
1772
- currentDepth: 0,
1773
- maxDepth,
1774
- getFragment(name) {
1775
- return fragmentGraph.getNodeData(name);
1776
- },
1777
- });
1778
- }
1779
- catch (errorOrNode) {
1780
- if (errorOrNode instanceof Error) {
1781
- throw errorOrNode;
1782
- }
1783
- const node = errorOrNode;
1784
- return new graphql.GraphQLError(`Query exceeds maximum depth of ${maxDepth}`, node, source, ((_a = node.loc) === null || _a === void 0 ? void 0 : _a.start) ? [node.loc.start] : undefined);
1785
- }
1786
- }
1787
- function calculateDepth({ node, currentDepth, maxDepth, getFragment, }) {
1788
- if (maxDepth && currentDepth > maxDepth) {
1789
- throw node;
1790
- }
1791
- switch (node.kind) {
1792
- case graphql.Kind.FIELD: {
1793
- if (node.name.value.startsWith('__') || !node.selectionSet) {
1794
- return 0;
1795
- }
1796
- const maxInnerDepth = calculateDepth({
1797
- node: node.selectionSet,
1798
- currentDepth: currentDepth + 1,
1799
- maxDepth,
1800
- getFragment,
1801
- });
1802
- return 1 + maxInnerDepth;
1803
- }
1804
- case graphql.Kind.SELECTION_SET: {
1805
- return Math.max(...node.selections.map(selection => {
1806
- return calculateDepth({
1807
- node: selection,
1808
- currentDepth,
1809
- maxDepth,
1810
- getFragment,
1811
- });
1812
- }));
1813
- }
1814
- case graphql.Kind.DOCUMENT: {
1815
- return Math.max(...node.definitions.map(def => {
1816
- return calculateDepth({
1817
- node: def,
1818
- currentDepth,
1819
- maxDepth,
1820
- getFragment,
1821
- });
1822
- }));
1823
- }
1824
- case graphql.Kind.OPERATION_DEFINITION:
1825
- case graphql.Kind.INLINE_FRAGMENT:
1826
- case graphql.Kind.FRAGMENT_DEFINITION: {
1827
- return Math.max(...node.selectionSet.selections.map(selection => {
1828
- return calculateDepth({
1829
- node: selection,
1830
- currentDepth,
1831
- maxDepth,
1832
- getFragment,
1833
- });
1834
- }));
1835
- }
1836
- case graphql.Kind.FRAGMENT_SPREAD:
1837
- return calculateDepth({
1838
- node: getFragment(node.name.value),
1839
- currentDepth,
1840
- maxDepth,
1841
- getFragment,
1842
- });
1843
- default: {
1844
- throw new Error(`Couldn't handle ${node.kind}`);
1845
- }
1846
- }
1847
- }
1848
- function countDepth(node, parentDepth, getFragmentReference) {
1849
- let depth = parentDepth;
1850
- if ('selectionSet' in node && node.selectionSet) {
1851
- for (const child of node.selectionSet.selections) {
1852
- depth = Math.max(depth, countDepth(child, parentDepth + 1, getFragmentReference));
1853
- }
1854
- }
1855
- if (node.kind == graphql.Kind.FRAGMENT_SPREAD) {
1856
- const fragment = getFragmentReference(node.name.value);
1857
- if (fragment) {
1858
- depth = Math.max(depth, countDepth(fragment, parentDepth + 1, getFragmentReference));
1859
- }
1860
- }
1861
- return depth;
1862
- }
1863
-
1864
- class ParserWithLexer extends parser.Parser {
1865
- constructor(source, options) {
1866
- super(source, options);
1867
- this.__tokenCount = 0;
1868
- const lexer = this._lexer;
1869
- this._lexer = new Proxy(lexer, {
1870
- get: (target, prop, receiver) => {
1871
- if (prop === 'advance') {
1872
- return () => {
1873
- const token = target.advance();
1874
- if (token.kind !== graphql.TokenKind.EOF) {
1875
- this.__tokenCount++;
1876
- }
1877
- return token;
1878
- };
1879
- }
1880
- return Reflect.get(target, prop, receiver);
1881
- },
1882
- });
1883
- }
1884
- get tokenCount() {
1885
- return this.__tokenCount;
1886
- }
1887
- }
1888
- function calculateTokenCount(args) {
1889
- const parser = new ParserWithLexer(args.source);
1890
- const document = parser.parseDocument();
1891
- let { tokenCount } = parser;
1892
- graphql.visit(document, {
1893
- FragmentSpread(node) {
1894
- const fragmentSource = args.getReferencedFragmentSource(node.name.value);
1895
- if (fragmentSource) {
1896
- tokenCount += calculateTokenCount({
1897
- source: fragmentSource,
1898
- getReferencedFragmentSource: args.getReferencedFragmentSource,
1899
- });
1900
- }
1901
- },
1902
- });
1903
- return tokenCount;
1904
- }
1905
- function validateTokenCount(args) {
1906
- var _a;
1907
- const tokenCount = calculateTokenCount(args);
1908
- if (tokenCount > args.maxTokenCount) {
1909
- return new graphql.GraphQLError(`Query exceeds maximum token count of ${args.maxTokenCount} (actual: ${tokenCount})`, args.document, args.source, ((_a = args.document.loc) === null || _a === void 0 ? void 0 : _a.start) ? [args.document.loc.start] : undefined);
1910
- }
1911
- }
1912
-
1913
- function validate(schema, sources, options) {
1914
- const config = Object.assign({ strictDeprecated: true, strictFragments: true, keepClientFields: false, apollo: false }, options);
1915
- const invalidDocuments = [];
1916
- // read documents
1917
- const documents = sources.map(readDocument);
1918
- // keep all named fragments
1919
- const fragments = [];
1920
- const fragmentNames = [];
1921
- const graph = new dependencyGraph.DepGraph({ circular: true });
1922
- documents.forEach(doc => {
1923
- doc.fragments.forEach(fragment => {
1924
- fragmentNames.push(fragment.node.name.value);
1925
- fragments.push(fragment);
1926
- graph.addNode(fragment.node.name.value, fragment.node);
1927
- });
1928
- });
1929
- fragments.forEach(fragment => {
1930
- const depends = extractFragments(graphql.print(fragment.node));
1931
- if (depends) {
1932
- depends.forEach(name => {
1933
- graph.addDependency(fragment.node.name.value, name);
1934
- });
1935
- }
1936
- });
1937
- documents
1938
- // since we include fragments, validate only operations
1939
- .filter(doc => doc.hasOperations)
1940
- .forEach(doc => {
1941
- const docWithOperations = {
1942
- kind: graphql.Kind.DOCUMENT,
1943
- definitions: doc.operations.map(d => d.node),
1944
- };
1945
- const extractedFragments = (extractFragments(graphql.print(docWithOperations)) || [])
1946
- // resolve all nested fragments
1947
- .map(fragmentName => resolveFragment(graph.getNodeData(fragmentName), graph))
1948
- // flatten arrays
1949
- .reduce((list, current) => list.concat(current), [])
1950
- // remove duplicates
1951
- .filter((def, i, all) => all.findIndex(item => item.name.value === def.name.value) === i);
1952
- const merged = {
1953
- kind: graphql.Kind.DOCUMENT,
1954
- definitions: [...docWithOperations.definitions, ...extractedFragments],
1955
- };
1956
- const transformedSchema = config.apollo ? transformSchemaWithApollo(schema) : schema;
1957
- const transformedDoc = config.apollo
1958
- ? transformDocumentWithApollo(merged, {
1959
- keepClientFields: config.keepClientFields,
1960
- })
1961
- : merged;
1962
- const errors = graphql.validate(transformedSchema, transformedDoc) || [];
1963
- if (config.maxDepth) {
1964
- const depthError = validateQueryDepth({
1965
- source: doc.source,
1966
- doc: transformedDoc,
1967
- maxDepth: config.maxDepth,
1968
- fragmentGraph: graph,
1969
- });
1970
- if (depthError) {
1971
- errors.push(depthError);
1972
- }
1973
- }
1974
- if (config.maxAliasCount) {
1975
- const aliasError = validateAliasCount({
1976
- source: doc.source,
1977
- doc: transformedDoc,
1978
- maxAliasCount: config.maxAliasCount,
1979
- fragmentGraph: graph,
1980
- });
1981
- if (aliasError) {
1982
- errors.push(aliasError);
1983
- }
1984
- }
1985
- if (config.maxDirectiveCount) {
1986
- const directiveError = validateDirectiveCount({
1987
- source: doc.source,
1988
- doc: transformedDoc,
1989
- maxDirectiveCount: config.maxDirectiveCount,
1990
- fragmentGraph: graph,
1991
- });
1992
- if (directiveError) {
1993
- errors.push(directiveError);
1994
- }
1995
- }
1996
- if (config.maxTokenCount) {
1997
- const tokenCountError = validateTokenCount({
1998
- source: doc.source,
1999
- document: transformedDoc,
2000
- maxTokenCount: config.maxTokenCount,
2001
- getReferencedFragmentSource: fragmentName => graphql.print(graph.getNodeData(fragmentName)),
2002
- });
2003
- if (tokenCountError) {
2004
- errors.push(tokenCountError);
2005
- }
2006
- }
2007
- const deprecated = config.strictDeprecated
2008
- ? findDeprecatedUsages(transformedSchema, transformedDoc)
2009
- : [];
2010
- const duplicatedFragments = config.strictFragments
2011
- ? findDuplicatedFragments(fragmentNames)
2012
- : [];
2013
- if (sumLengths(errors, duplicatedFragments, deprecated) > 0) {
2014
- invalidDocuments.push({
2015
- source: doc.source,
2016
- errors: [...errors, ...duplicatedFragments],
2017
- deprecated,
2018
- });
2019
- }
2020
- });
2021
- return invalidDocuments;
2022
- }
2023
- function findDuplicatedFragments(fragmentNames) {
2024
- return fragmentNames
2025
- .filter((name, i, all) => all.indexOf(name) !== i)
2026
- .map(name => new graphql.GraphQLError(`Name of '${name}' fragment is not unique`));
2027
- }
2028
- //
2029
- // PostInfo -> AuthorInfo
2030
- // AuthorInfo -> None
2031
- //
2032
- function resolveFragment(fragment, graph) {
2033
- return graph
2034
- .dependenciesOf(fragment.name.value)
2035
- .reduce((list, current) => [...list, ...resolveFragment(graph.getNodeData(current), graph)], [fragment]);
2036
- }
2037
- function extractFragments(document) {
2038
- return (document.match(/[.]{3}[a-z0-9_]+\b/gi) || []).map(name => name.replace('...', ''));
2039
- }
2040
- function sumLengths(...arrays) {
2041
- return arrays.reduce((sum, { length }) => sum + length, 0);
2042
- }
2043
-
2044
- function calculateOperationComplexity(node, config, getFragmentByName, depth = 0) {
2045
- let cost = config.scalarCost;
2046
- if ('selectionSet' in node && node.selectionSet) {
2047
- cost = config.objectCost;
2048
- for (const child of node.selectionSet.selections) {
2049
- cost +=
2050
- config.depthCostFactor *
2051
- calculateOperationComplexity(child, config, getFragmentByName, depth + 1);
2052
- }
2053
- }
2054
- if (node.kind == graphql.Kind.FRAGMENT_SPREAD) {
2055
- const fragment = getFragmentByName(node.name.value);
2056
- if (fragment) {
2057
- cost +=
2058
- config.depthCostFactor *
2059
- calculateOperationComplexity(fragment, config, getFragmentByName, depth + 1);
2060
- }
2061
- }
2062
- return cost;
2063
- }
2064
-
2065
- exports.DiffRule = DiffRule;
2066
- exports.calculateOperationComplexity = calculateOperationComplexity;
2067
- exports.calculateTokenCount = calculateTokenCount;
2068
- exports.countAliases = countAliases;
2069
- exports.countDepth = countDepth;
2070
- exports.countDirectives = countDirectives;
2071
- exports.coverage = coverage;
2072
- exports.diff = diff;
2073
- exports.getTypePrefix = getTypePrefix;
2074
- exports.similar = similar;
2075
- exports.validate = validate;