@graphql-inspector/core 0.0.0-canary.f71f99b → 0.0.0-canary.fec2df4
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.
- package/README.md +11 -9
- package/ast/document.d.ts +12 -12
- package/coverage/index.d.ts +9 -0
- package/coverage/output/json.d.ts +1 -1
- package/diff/argument.d.ts +3 -13
- package/diff/changes/argument.d.ts +5 -25
- package/diff/changes/change.d.ts +2 -0
- package/diff/changes/directive.d.ts +10 -41
- package/diff/changes/enum.d.ts +8 -20
- package/diff/changes/field.d.ts +15 -64
- package/diff/changes/input.d.ts +9 -33
- package/diff/changes/object.d.ts +4 -10
- package/diff/changes/schema.d.ts +5 -14
- package/diff/changes/union.d.ts +4 -10
- package/diff/directive.d.ts +2 -2
- package/diff/enum.d.ts +2 -2
- package/diff/field.d.ts +2 -2
- package/diff/index.d.ts +5 -8
- package/diff/input.d.ts +2 -2
- package/diff/interface.d.ts +2 -2
- package/diff/object.d.ts +2 -2
- package/diff/rules/config.d.ts +2 -0
- package/diff/rules/consider-usage.d.ts +29 -0
- package/diff/rules/dangerous-breaking.d.ts +2 -0
- package/diff/rules/index.d.ts +3 -0
- package/diff/rules/safe-unreachable.d.ts +2 -0
- package/diff/rules/types.d.ts +8 -7
- package/diff/schema.d.ts +1 -0
- package/diff/union.d.ts +2 -2
- package/index.d.ts +11 -11
- package/index.js +1992 -0
- package/index.mjs +1978 -0
- package/package.json +16 -5
- package/utils/apollo.d.ts +4 -11
- package/utils/compare.d.ts +22 -0
- package/utils/graphql.d.ts +1 -0
- package/utils/isDeprecated.d.ts +2 -0
- package/{diff/common → utils}/path.d.ts +0 -0
- package/utils/string.d.ts +1 -0
- package/validate/alias-count.d.ts +10 -0
- package/validate/complexity.d.ts +16 -0
- package/validate/directive-count.d.ts +10 -0
- package/validate/index.d.ts +12 -2
- package/validate/query-depth.d.ts +2 -1
- package/validate/token-count.d.ts +5 -0
- package/diff/common/compare.d.ts +0 -3
- package/index.cjs.js +0 -1688
- package/index.cjs.js.map +0 -1
- package/index.esm.js +0 -1681
- package/index.esm.js.map +0 -1
- package/utils/arrays.d.ts +0 -2
package/index.esm.js
DELETED
|
@@ -1,1681 +0,0 @@
|
|
|
1
|
-
import { Kind, TypeInfo, visit, visitWithTypeInfo, GraphQLError, getNamedType, isWrappingType, isListType, isNonNullType, isInterfaceType, isEnumType, isUnionType, isInputObjectType, isObjectType, isScalarType, parse, extendSchema, print, validate as validate$1, printType } from 'graphql';
|
|
2
|
-
import { DepGraph } from 'dependency-graph';
|
|
3
|
-
|
|
4
|
-
function isEqual(a, b) {
|
|
5
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
6
|
-
if (a.length !== b.length) return false;
|
|
7
|
-
for (var index = 0; index < a.length; index++) {
|
|
8
|
-
if (a[index] !== b[index]) {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
return a === b || (!a && !b);
|
|
15
|
-
}
|
|
16
|
-
function isNotEqual(a, b) {
|
|
17
|
-
return !isEqual(a, b);
|
|
18
|
-
}
|
|
19
|
-
function isVoid(a) {
|
|
20
|
-
return typeof a === 'undefined' || a === null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function diffArrays(a, b) {
|
|
24
|
-
return a.filter((c) => !b.some((d) => d === c));
|
|
25
|
-
}
|
|
26
|
-
function unionArrays(a, b) {
|
|
27
|
-
return a.filter((c) => b.some((d) => d === c));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function safeChangeForField(oldType, newType) {
|
|
31
|
-
if (!isWrappingType(oldType) && !isWrappingType(newType)) {
|
|
32
|
-
return oldType.toString() === newType.toString();
|
|
33
|
-
}
|
|
34
|
-
if (isNonNullType(newType)) {
|
|
35
|
-
const ofType = isNonNullType(oldType) ? oldType.ofType : oldType;
|
|
36
|
-
return safeChangeForField(ofType, newType.ofType);
|
|
37
|
-
}
|
|
38
|
-
if (isListType(oldType)) {
|
|
39
|
-
return ((isListType(newType) &&
|
|
40
|
-
safeChangeForField(oldType.ofType, newType.ofType)) ||
|
|
41
|
-
(isNonNullType(newType) && safeChangeForField(oldType, newType.ofType)));
|
|
42
|
-
}
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
function safeChangeForInputValue(oldType, newType) {
|
|
46
|
-
if (!isWrappingType(oldType) && !isWrappingType(newType)) {
|
|
47
|
-
return oldType.toString() === newType.toString();
|
|
48
|
-
}
|
|
49
|
-
if (isListType(oldType) && isListType(newType)) {
|
|
50
|
-
return safeChangeForInputValue(oldType.ofType, newType.ofType);
|
|
51
|
-
}
|
|
52
|
-
if (isNonNullType(oldType)) {
|
|
53
|
-
const ofType = isNonNullType(newType) ? newType : newType;
|
|
54
|
-
return safeChangeForInputValue(oldType.ofType, ofType);
|
|
55
|
-
}
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
function getKind(type) {
|
|
59
|
-
const node = type.astNode;
|
|
60
|
-
return (node && node.kind) || '';
|
|
61
|
-
}
|
|
62
|
-
function getTypePrefix(type) {
|
|
63
|
-
const kind = getKind(type);
|
|
64
|
-
const kindsMap = {
|
|
65
|
-
[Kind.SCALAR_TYPE_DEFINITION]: 'scalar',
|
|
66
|
-
[Kind.OBJECT_TYPE_DEFINITION]: 'type',
|
|
67
|
-
[Kind.INTERFACE_TYPE_DEFINITION]: 'interface',
|
|
68
|
-
[Kind.UNION_TYPE_DEFINITION]: 'union',
|
|
69
|
-
[Kind.ENUM_TYPE_DEFINITION]: 'enum',
|
|
70
|
-
[Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
|
|
71
|
-
};
|
|
72
|
-
return kindsMap[kind];
|
|
73
|
-
}
|
|
74
|
-
function isPrimitive(type) {
|
|
75
|
-
return (['String', 'Int', 'Float', 'Boolean', 'ID'].indexOf(typeof type === 'string' ? type : type.name) !== -1);
|
|
76
|
-
}
|
|
77
|
-
function isForIntrospection(type) {
|
|
78
|
-
return ([
|
|
79
|
-
'__Schema',
|
|
80
|
-
'__Type',
|
|
81
|
-
'__TypeKind',
|
|
82
|
-
'__Field',
|
|
83
|
-
'__InputValue',
|
|
84
|
-
'__EnumValue',
|
|
85
|
-
'__Directive',
|
|
86
|
-
'__DirectiveLocation',
|
|
87
|
-
].indexOf(typeof type === 'string' ? type : type.name) !== -1);
|
|
88
|
-
}
|
|
89
|
-
function findDeprecatedUsages(schema, ast) {
|
|
90
|
-
const errors = [];
|
|
91
|
-
const typeInfo = new TypeInfo(schema);
|
|
92
|
-
visit(ast, visitWithTypeInfo(typeInfo, {
|
|
93
|
-
Field(node) {
|
|
94
|
-
const fieldDef = typeInfo.getFieldDef();
|
|
95
|
-
if (fieldDef && fieldDef.isDeprecated) {
|
|
96
|
-
const parentType = typeInfo.getParentType();
|
|
97
|
-
if (parentType) {
|
|
98
|
-
const reason = fieldDef.deprecationReason;
|
|
99
|
-
errors.push(new GraphQLError(`The field '${parentType.name}.${fieldDef.name}' is deprecated.${reason ? ' ' + reason : ''}`, [node]));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
EnumValue(node) {
|
|
104
|
-
const enumVal = typeInfo.getEnumValue();
|
|
105
|
-
if (enumVal && enumVal.isDeprecated) {
|
|
106
|
-
const type = getNamedType(typeInfo.getInputType());
|
|
107
|
-
if (type) {
|
|
108
|
-
const reason = enumVal.deprecationReason;
|
|
109
|
-
errors.push(new GraphQLError(`The enum value '${type.name}.${enumVal.name}' is deprecated.${reason ? ' ' + reason : ''}`, [node]));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
}));
|
|
114
|
-
return errors;
|
|
115
|
-
}
|
|
116
|
-
function removeFieldIfDirectives(node, directiveNames) {
|
|
117
|
-
if (node.directives) {
|
|
118
|
-
if (node.directives.some((d) => directiveNames.indexOf(d.name.value) !== -1)) {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return node;
|
|
123
|
-
}
|
|
124
|
-
function removeDirectives(node, directiveNames) {
|
|
125
|
-
if (node.directives) {
|
|
126
|
-
return Object.assign(Object.assign({}, node), { directives: node.directives.filter((d) => directiveNames.indexOf(d.name.value) === -1) });
|
|
127
|
-
}
|
|
128
|
-
return node;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
var ChangeType;
|
|
132
|
-
(function (ChangeType) {
|
|
133
|
-
// Argument
|
|
134
|
-
ChangeType["FieldArgumentDescriptionChanged"] = "FIELD_ARGUMENT_DESCRIPTION_CHANGED";
|
|
135
|
-
ChangeType["FieldArgumentDefaultChanged"] = "FIELD_ARGUMENT_DEFAULT_CHANGED";
|
|
136
|
-
ChangeType["FieldArgumentTypeChanged"] = "FIELD_ARGUMENT_TYPE_CHANGED";
|
|
137
|
-
// Directive
|
|
138
|
-
ChangeType["DirectiveRemoved"] = "DIRECTIVE_REMOVED";
|
|
139
|
-
ChangeType["DirectiveAdded"] = "DIRECTIVE_ADDED";
|
|
140
|
-
ChangeType["DirectiveDescriptionChanged"] = "DIRECTIVE_DESCRIPTION_CHANGED";
|
|
141
|
-
ChangeType["DirectiveLocationAdded"] = "DIRECTIVE_LOCATION_ADDED";
|
|
142
|
-
ChangeType["DirectiveLocationRemoved"] = "DIRECTIVE_LOCATION_REMOVED";
|
|
143
|
-
ChangeType["DirectiveArgumentAdded"] = "DIRECTIVE_ARGUMENT_ADDED";
|
|
144
|
-
ChangeType["DirectiveArgumentRemoved"] = "DIRECTIVE_ARGUMENT_REMOVED";
|
|
145
|
-
ChangeType["DirectiveArgumentDescriptionChanged"] = "DIRECTIVE_ARGUMENT_DESCRIPTION_CHANGED";
|
|
146
|
-
ChangeType["DirectiveArgumentDefaultValueChanged"] = "DIRECTIVE_ARGUMENT_DEFAULT_VALUE_CHANGED";
|
|
147
|
-
ChangeType["DirectiveArgumentTypeChanged"] = "DIRECTIVE_ARGUMENT_TYPE_CHANGED";
|
|
148
|
-
// Enum
|
|
149
|
-
ChangeType["EnumValueRemoved"] = "ENUM_VALUE_REMOVED";
|
|
150
|
-
ChangeType["EnumValueAdded"] = "ENUM_VALUE_ADDED";
|
|
151
|
-
ChangeType["EnumValueDescriptionChanged"] = "ENUM_VALUE_DESCRIPTION_CHANGED";
|
|
152
|
-
ChangeType["EnumValueDeprecationReasonChanged"] = "ENUM_VALUE_DEPRECATION_REASON_CHANGED";
|
|
153
|
-
// Field
|
|
154
|
-
ChangeType["FieldRemoved"] = "FIELD_REMOVED";
|
|
155
|
-
ChangeType["FieldAdded"] = "FIELD_ADDED";
|
|
156
|
-
ChangeType["FieldDescriptionChanged"] = "FIELD_DESCRIPTION_CHANGED";
|
|
157
|
-
ChangeType["FieldDescriptionAdded"] = "FIELD_DESCRIPTION_ADDED";
|
|
158
|
-
ChangeType["FieldDescriptionRemoved"] = "FIELD_DESCRIPTION_REMOVED";
|
|
159
|
-
ChangeType["FieldDeprecationAdded"] = "FIELD_DEPRECATION_ADDED";
|
|
160
|
-
ChangeType["FieldDeprecationRemoved"] = "FIELD_DEPRECATION_REMOVED";
|
|
161
|
-
ChangeType["FieldDeprecationReasonChanged"] = "FIELD_DEPRECATION_REASON_CHANGED";
|
|
162
|
-
ChangeType["FieldDeprecationReasonAdded"] = "FIELD_DEPRECATION_REASON_ADDED";
|
|
163
|
-
ChangeType["FieldDeprecationReasonRemoved"] = "FIELD_DEPRECATION_REASON_REMOVED";
|
|
164
|
-
ChangeType["FieldTypeChanged"] = "FIELD_TYPE_CHANGED";
|
|
165
|
-
ChangeType["FieldArgumentAdded"] = "FIELD_ARGUMENT_ADDED";
|
|
166
|
-
ChangeType["FieldArgumentRemoved"] = "FIELD_ARGUMENT_REMOVED";
|
|
167
|
-
// Input
|
|
168
|
-
ChangeType["InputFieldRemoved"] = "INPUT_FIELD_REMOVED";
|
|
169
|
-
ChangeType["InputFieldAdded"] = "INPUT_FIELD_ADDED";
|
|
170
|
-
ChangeType["InputFieldDescriptionAdded"] = "INPUT_FIELD_DESCRIPTION_ADDED";
|
|
171
|
-
ChangeType["InputFieldDescriptionRemoved"] = "INPUT_FIELD_DESCRIPTION_REMOVED";
|
|
172
|
-
ChangeType["InputFieldDescriptionChanged"] = "INPUT_FIELD_DESCRIPTION_CHANGED";
|
|
173
|
-
ChangeType["InputFieldDefaultValueChanged"] = "INPUT_FIELD_DEFAULT_VALUE_CHANGED";
|
|
174
|
-
ChangeType["InputFieldTypeChanged"] = "INPUT_FIELD_TYPE_CHANGED";
|
|
175
|
-
// Type
|
|
176
|
-
ChangeType["ObjectTypeInterfaceAdded"] = "OBJECT_TYPE_INTERFACE_ADDED";
|
|
177
|
-
ChangeType["ObjectTypeInterfaceRemoved"] = "OBJECT_TYPE_INTERFACE_REMOVED";
|
|
178
|
-
// Schema
|
|
179
|
-
ChangeType["SchemaQueryTypeChanged"] = "SCHEMA_QUERY_TYPE_CHANGED";
|
|
180
|
-
ChangeType["SchemaMutationTypeChanged"] = "SCHEMA_MUTATION_TYPE_CHANGED";
|
|
181
|
-
ChangeType["SchemaSubscriptionTypeChanged"] = "SCHEMA_SUBSCRIPTION_TYPE_CHANGED";
|
|
182
|
-
// Type
|
|
183
|
-
ChangeType["TypeRemoved"] = "TYPE_REMOVED";
|
|
184
|
-
ChangeType["TypeAdded"] = "TYPE_ADDED";
|
|
185
|
-
ChangeType["TypeKindChanged"] = "TYPE_KIND_CHANGED";
|
|
186
|
-
ChangeType["TypeDescriptionChanged"] = "TYPE_DESCRIPTION_CHANGED";
|
|
187
|
-
// TODO
|
|
188
|
-
ChangeType["TypeDescriptionRemoved"] = "TYPE_DESCRIPTION_REMOVED";
|
|
189
|
-
// TODO
|
|
190
|
-
ChangeType["TypeDescriptionAdded"] = "TYPE_DESCRIPTION_ADDED";
|
|
191
|
-
// Union
|
|
192
|
-
ChangeType["UnionMemberRemoved"] = "UNION_MEMBER_REMOVED";
|
|
193
|
-
ChangeType["UnionMemberAdded"] = "UNION_MEMBER_ADDED";
|
|
194
|
-
})(ChangeType || (ChangeType = {}));
|
|
195
|
-
var CriticalityLevel;
|
|
196
|
-
(function (CriticalityLevel) {
|
|
197
|
-
CriticalityLevel["Breaking"] = "BREAKING";
|
|
198
|
-
CriticalityLevel["NonBreaking"] = "NON_BREAKING";
|
|
199
|
-
CriticalityLevel["Dangerous"] = "DANGEROUS";
|
|
200
|
-
})(CriticalityLevel || (CriticalityLevel = {}));
|
|
201
|
-
|
|
202
|
-
function schemaQueryTypeChanged(oldSchema, newSchema) {
|
|
203
|
-
const oldName = (oldSchema.getQueryType() || {}).name || 'unknown';
|
|
204
|
-
const newName = (newSchema.getQueryType() || {}).name || 'unknown';
|
|
205
|
-
return {
|
|
206
|
-
criticality: {
|
|
207
|
-
level: CriticalityLevel.Breaking,
|
|
208
|
-
},
|
|
209
|
-
type: ChangeType.SchemaQueryTypeChanged,
|
|
210
|
-
message: `Schema query root has changed from '${oldName}' to '${newName}'`,
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
function schemaMutationTypeChanged(oldSchema, newSchema) {
|
|
214
|
-
const oldName = (oldSchema.getMutationType() || {}).name || 'unknown';
|
|
215
|
-
const newName = (newSchema.getMutationType() || {}).name || 'unknown';
|
|
216
|
-
return {
|
|
217
|
-
criticality: {
|
|
218
|
-
level: CriticalityLevel.Breaking,
|
|
219
|
-
},
|
|
220
|
-
type: ChangeType.SchemaMutationTypeChanged,
|
|
221
|
-
message: `Schema mutation root has changed from '${oldName}' to '${newName}'`,
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
function schemaSubscriptionTypeChanged(oldSchema, newSchema) {
|
|
225
|
-
const oldName = (oldSchema.getSubscriptionType() || {}).name || 'unknown';
|
|
226
|
-
const newName = (newSchema.getSubscriptionType() || {}).name || 'unknown';
|
|
227
|
-
return {
|
|
228
|
-
criticality: {
|
|
229
|
-
level: CriticalityLevel.Breaking,
|
|
230
|
-
},
|
|
231
|
-
type: ChangeType.SchemaSubscriptionTypeChanged,
|
|
232
|
-
message: `Schema subscription root has changed from '${oldName}' to '${newName}'`,
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function typeRemoved(type) {
|
|
237
|
-
return {
|
|
238
|
-
criticality: {
|
|
239
|
-
level: CriticalityLevel.Breaking,
|
|
240
|
-
},
|
|
241
|
-
type: ChangeType.TypeRemoved,
|
|
242
|
-
message: `Type '${type.name}' was removed`,
|
|
243
|
-
path: type.name,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
function typeAdded(type) {
|
|
247
|
-
return {
|
|
248
|
-
criticality: {
|
|
249
|
-
level: CriticalityLevel.NonBreaking,
|
|
250
|
-
},
|
|
251
|
-
type: ChangeType.TypeAdded,
|
|
252
|
-
message: `Type '${type.name}' was added`,
|
|
253
|
-
path: type.name,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
function typeKindChanged(oldType, newType) {
|
|
257
|
-
return {
|
|
258
|
-
criticality: {
|
|
259
|
-
level: CriticalityLevel.Breaking,
|
|
260
|
-
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.`,
|
|
261
|
-
},
|
|
262
|
-
type: ChangeType.TypeKindChanged,
|
|
263
|
-
message: `'${oldType.name}' kind changed from '${getKind(oldType)}' to '${getKind(newType)}'`,
|
|
264
|
-
path: oldType.name,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
function typeDescriptionChanged(oldType, newType) {
|
|
268
|
-
return {
|
|
269
|
-
criticality: {
|
|
270
|
-
level: CriticalityLevel.NonBreaking,
|
|
271
|
-
},
|
|
272
|
-
type: ChangeType.TypeDescriptionChanged,
|
|
273
|
-
message: `Description '${oldType.description}' on type '${oldType.name}' has changed to '${newType.description}'`,
|
|
274
|
-
path: oldType.name,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
function typeDescriptionRemoved(type) {
|
|
278
|
-
return {
|
|
279
|
-
criticality: {
|
|
280
|
-
level: CriticalityLevel.NonBreaking,
|
|
281
|
-
},
|
|
282
|
-
type: ChangeType.TypeDescriptionRemoved,
|
|
283
|
-
message: `Description '${type.description}' was removed from object type '${type.name}'`,
|
|
284
|
-
path: type.name,
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
function typeDescriptionAdded(type) {
|
|
288
|
-
return {
|
|
289
|
-
criticality: {
|
|
290
|
-
level: CriticalityLevel.NonBreaking,
|
|
291
|
-
},
|
|
292
|
-
type: ChangeType.TypeDescriptionAdded,
|
|
293
|
-
message: `Object type '${type.name}' has description '${type.description}'`,
|
|
294
|
-
path: type.name,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function directiveRemoved(directive) {
|
|
299
|
-
return {
|
|
300
|
-
criticality: {
|
|
301
|
-
level: CriticalityLevel.Breaking,
|
|
302
|
-
},
|
|
303
|
-
type: ChangeType.DirectiveRemoved,
|
|
304
|
-
message: `Directive '${directive.name}' was removed`,
|
|
305
|
-
path: `@${directive.name}`,
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
function directiveAdded(directive) {
|
|
309
|
-
return {
|
|
310
|
-
criticality: {
|
|
311
|
-
level: CriticalityLevel.NonBreaking,
|
|
312
|
-
},
|
|
313
|
-
type: ChangeType.DirectiveAdded,
|
|
314
|
-
message: `Directive '${directive.name}' was added`,
|
|
315
|
-
path: `@${directive.name}`,
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
function directiveDescriptionChanged(oldDirective, newDirective) {
|
|
319
|
-
return {
|
|
320
|
-
criticality: {
|
|
321
|
-
level: CriticalityLevel.NonBreaking,
|
|
322
|
-
},
|
|
323
|
-
type: ChangeType.DirectiveDescriptionChanged,
|
|
324
|
-
message: `Directive '${oldDirective.name}' description changed from '${oldDirective.description}' to '${newDirective.description}'`,
|
|
325
|
-
path: `@${oldDirective.name}`,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
function directiveLocationAdded(directive, location) {
|
|
329
|
-
return {
|
|
330
|
-
criticality: {
|
|
331
|
-
level: CriticalityLevel.NonBreaking,
|
|
332
|
-
},
|
|
333
|
-
type: ChangeType.DirectiveLocationAdded,
|
|
334
|
-
message: `Location '${location}' was added to directive '${directive.name}'`,
|
|
335
|
-
path: `@${directive.name}`,
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
function directiveLocationRemoved(directive, location) {
|
|
339
|
-
return {
|
|
340
|
-
criticality: {
|
|
341
|
-
level: CriticalityLevel.Breaking,
|
|
342
|
-
},
|
|
343
|
-
type: ChangeType.DirectiveLocationRemoved,
|
|
344
|
-
message: `Location '${location}' was removed from directive '${directive.name}'`,
|
|
345
|
-
path: `@${directive.name}`,
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
function directiveArgumentAdded(directive, arg) {
|
|
349
|
-
return {
|
|
350
|
-
criticality: {
|
|
351
|
-
level: isNonNullType(arg.type)
|
|
352
|
-
? CriticalityLevel.Breaking
|
|
353
|
-
: CriticalityLevel.NonBreaking,
|
|
354
|
-
},
|
|
355
|
-
type: ChangeType.DirectiveArgumentAdded,
|
|
356
|
-
message: `Argument '${arg.name}' was added to directive '${directive.name}'`,
|
|
357
|
-
path: `@${directive.name}`,
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
function directiveArgumentRemoved(directive, arg) {
|
|
361
|
-
return {
|
|
362
|
-
criticality: {
|
|
363
|
-
level: CriticalityLevel.Breaking,
|
|
364
|
-
},
|
|
365
|
-
type: ChangeType.DirectiveArgumentRemoved,
|
|
366
|
-
message: `Argument '${arg.name}' was removed from directive '${directive.name}'`,
|
|
367
|
-
path: `@${directive.name}.${arg.name}`,
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
function directiveArgumentDescriptionChanged(directive, oldArg, newArg) {
|
|
371
|
-
return {
|
|
372
|
-
criticality: {
|
|
373
|
-
level: CriticalityLevel.NonBreaking,
|
|
374
|
-
},
|
|
375
|
-
type: ChangeType.DirectiveArgumentDescriptionChanged,
|
|
376
|
-
message: `Description for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.description}' to '${newArg.description}'`,
|
|
377
|
-
path: `@${directive.name}.${oldArg.name}`,
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
function directiveArgumentDefaultValueChanged(
|
|
381
|
-
directive,
|
|
382
|
-
oldArg,
|
|
383
|
-
newArg,
|
|
384
|
-
) {
|
|
385
|
-
return {
|
|
386
|
-
criticality: {
|
|
387
|
-
level: CriticalityLevel.Dangerous,
|
|
388
|
-
reason:
|
|
389
|
-
'Changing the default value for an argument may change the runtime behaviour of a field if it was never provided.',
|
|
390
|
-
},
|
|
391
|
-
type: ChangeType.DirectiveArgumentDefaultValueChanged,
|
|
392
|
-
message:
|
|
393
|
-
typeof oldArg.defaultValue === 'undefined'
|
|
394
|
-
? `Default value '${newArg.defaultValue}' was added to argument '${newArg.name}' on directive '${directive.name}'`
|
|
395
|
-
: `Default value for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.defaultValue}' to '${newArg.defaultValue}'`,
|
|
396
|
-
path: `@${directive.name}.${oldArg.name}`,
|
|
397
|
-
};
|
|
398
|
-
}
|
|
399
|
-
function directiveArgumentTypeChanged(directive, oldArg, newArg) {
|
|
400
|
-
return {
|
|
401
|
-
criticality: safeChangeForInputValue(oldArg.type, newArg.type)
|
|
402
|
-
? {
|
|
403
|
-
level: CriticalityLevel.NonBreaking,
|
|
404
|
-
reason:
|
|
405
|
-
'Changing an input field from non-null to null is considered non-breaking.',
|
|
406
|
-
}
|
|
407
|
-
: {
|
|
408
|
-
level: CriticalityLevel.Breaking,
|
|
409
|
-
},
|
|
410
|
-
type: ChangeType.DirectiveArgumentTypeChanged,
|
|
411
|
-
message: `Type for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.type}' to '${newArg.type}'`,
|
|
412
|
-
path: `@${directive.name}.${oldArg.name}`,
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
function enumValueRemoved(oldEnum, value) {
|
|
417
|
-
return {
|
|
418
|
-
criticality: {
|
|
419
|
-
level: CriticalityLevel.Breaking,
|
|
420
|
-
reason: `Removing an enum value will cause existing queries that use this enum value to error.`,
|
|
421
|
-
},
|
|
422
|
-
type: ChangeType.EnumValueRemoved,
|
|
423
|
-
message: `Enum value '${value.name}' was removed from enum '${oldEnum.name}'`,
|
|
424
|
-
path: [oldEnum.name, value.name].join('.'),
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
function enumValueAdded(newEnum, value) {
|
|
428
|
-
return {
|
|
429
|
-
criticality: {
|
|
430
|
-
level: CriticalityLevel.Dangerous,
|
|
431
|
-
reason: `Adding an enum value may break existing clients that were not programming defensively against an added case when querying an enum.`,
|
|
432
|
-
},
|
|
433
|
-
type: ChangeType.EnumValueAdded,
|
|
434
|
-
message: `Enum value '${value.name}' was added to enum '${newEnum.name}'`,
|
|
435
|
-
path: [newEnum.name, value.name].join('.'),
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
function enumValueDescriptionChanged(newEnum, oldValue, newValue) {
|
|
439
|
-
return {
|
|
440
|
-
criticality: {
|
|
441
|
-
level: CriticalityLevel.NonBreaking,
|
|
442
|
-
},
|
|
443
|
-
type: ChangeType.EnumValueDescriptionChanged,
|
|
444
|
-
message:
|
|
445
|
-
typeof oldValue.description === 'undefined'
|
|
446
|
-
? `Description '${newValue.description}' was added to enum value '${newEnum.name}.${newValue.name}'`
|
|
447
|
-
: `Description for enum value '${newEnum.name}.${newValue.name}' changed from '${oldValue.description}' to '${newValue.description}'`,
|
|
448
|
-
path: [newEnum.name, oldValue.name].join('.'),
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
function enumValueDeprecationReasonChanged(newEnum, oldValue, newValue) {
|
|
452
|
-
return {
|
|
453
|
-
criticality: {
|
|
454
|
-
level: CriticalityLevel.NonBreaking,
|
|
455
|
-
},
|
|
456
|
-
type: ChangeType.EnumValueDescriptionChanged,
|
|
457
|
-
message: oldValue.deprecationReason
|
|
458
|
-
? `Enum value '${newEnum.name}.${newValue.name}' deprecation reason changed from '${oldValue.deprecationReason}' to '${newValue.deprecationReason}'`
|
|
459
|
-
: `Enum value '${newEnum.name}.${newValue.name}' was deprecated with reason '${newValue.deprecationReason}'`,
|
|
460
|
-
path: [newEnum.name, oldValue.name].join('.'),
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function changesInEnum(oldEnum, newEnum) {
|
|
465
|
-
const changes = [];
|
|
466
|
-
const oldNames = oldEnum.getValues().map((v) => v.name);
|
|
467
|
-
const newNames = newEnum.getValues().map((v) => v.name);
|
|
468
|
-
const added = diffArrays(newNames, oldNames).map((name) => newEnum.getValue(name));
|
|
469
|
-
const removed = diffArrays(oldNames, newNames).map((name) => oldEnum.getValue(name));
|
|
470
|
-
const common = unionArrays(oldNames, newNames).map((name) => ({
|
|
471
|
-
oldValue: oldEnum.getValue(name),
|
|
472
|
-
newValue: newEnum.getValue(name),
|
|
473
|
-
}));
|
|
474
|
-
changes.push(...added.map((v) => enumValueAdded(newEnum, v)));
|
|
475
|
-
changes.push(...removed.map((v) => enumValueRemoved(oldEnum, v)));
|
|
476
|
-
common.forEach(({ oldValue, newValue }) => {
|
|
477
|
-
if (isNotEqual(oldValue.description, newValue.description)) {
|
|
478
|
-
changes.push(enumValueDescriptionChanged(newEnum, oldValue, newValue));
|
|
479
|
-
}
|
|
480
|
-
if (isNotEqual(oldValue.deprecationReason, newValue.deprecationReason)) {
|
|
481
|
-
changes.push(enumValueDeprecationReasonChanged(newEnum, oldValue, newValue));
|
|
482
|
-
}
|
|
483
|
-
});
|
|
484
|
-
return changes;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
function unionMemberRemoved(union, type) {
|
|
488
|
-
return {
|
|
489
|
-
criticality: {
|
|
490
|
-
level: CriticalityLevel.Breaking,
|
|
491
|
-
reason:
|
|
492
|
-
'Removing a union member from a union can cause existing queries that use this union member in a fragment spread to error.',
|
|
493
|
-
},
|
|
494
|
-
type: ChangeType.UnionMemberRemoved,
|
|
495
|
-
message: `Member '${type.name}' was removed from Union type '${union.name}'`,
|
|
496
|
-
path: union.name,
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
function unionMemberAdded(union, type) {
|
|
500
|
-
return {
|
|
501
|
-
criticality: {
|
|
502
|
-
level: CriticalityLevel.Dangerous,
|
|
503
|
-
reason:
|
|
504
|
-
'Adding a possible type to Unions may break existing clients that were not programming defensively against a new possible type.',
|
|
505
|
-
},
|
|
506
|
-
type: ChangeType.UnionMemberAdded,
|
|
507
|
-
message: `Member '${type.name}' was added to Union type '${union.name}'`,
|
|
508
|
-
path: union.name,
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function changesInUnion(oldUnion, newUnion) {
|
|
513
|
-
const changes = [];
|
|
514
|
-
const oldTypes = oldUnion.getTypes();
|
|
515
|
-
const newTypes = newUnion.getTypes();
|
|
516
|
-
const oldTypenames = oldTypes.map((t) => t.name);
|
|
517
|
-
const newTypenames = newTypes.map((t) => t.name);
|
|
518
|
-
const added = diffArrays(newTypenames, oldTypenames).map((name) => newTypes.find((t) => t.name === name));
|
|
519
|
-
const removed = diffArrays(oldTypenames, newTypenames).map((name) => oldTypes.find((t) => t.name === name));
|
|
520
|
-
changes.push(...added.map((type) => unionMemberAdded(newUnion, type)));
|
|
521
|
-
changes.push(...removed.map((type) => unionMemberRemoved(oldUnion, type)));
|
|
522
|
-
return changes;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
function inputFieldRemoved(input, field) {
|
|
526
|
-
return {
|
|
527
|
-
criticality: {
|
|
528
|
-
level: CriticalityLevel.Breaking,
|
|
529
|
-
reason:
|
|
530
|
-
'Removing an input field will cause existing queries that use this input field to error.',
|
|
531
|
-
},
|
|
532
|
-
type: ChangeType.InputFieldRemoved,
|
|
533
|
-
message: `Input field '${field.name}' was removed from input object type '${input.name}'`,
|
|
534
|
-
path: [input.name, field.name].join('.'),
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
function inputFieldAdded(input, field) {
|
|
538
|
-
return {
|
|
539
|
-
criticality: isNonNullType(field.type)
|
|
540
|
-
? {
|
|
541
|
-
level: CriticalityLevel.Breaking,
|
|
542
|
-
reason:
|
|
543
|
-
'Adding a possible type to Unions may break existing clients that were not programming defensively against a new possible type.',
|
|
544
|
-
}
|
|
545
|
-
: {
|
|
546
|
-
level: CriticalityLevel.Dangerous,
|
|
547
|
-
},
|
|
548
|
-
type: ChangeType.InputFieldAdded,
|
|
549
|
-
message: `Input field '${field.name}' was added to input object type '${input.name}'`,
|
|
550
|
-
path: [input.name, field.name].join('.'),
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
function inputFieldDescriptionAdded(type, field) {
|
|
554
|
-
return {
|
|
555
|
-
criticality: {
|
|
556
|
-
level: CriticalityLevel.NonBreaking,
|
|
557
|
-
},
|
|
558
|
-
type: ChangeType.InputFieldDescriptionAdded,
|
|
559
|
-
message: `Input field '${type.name}.${field.name}' has description '${field.description}'`,
|
|
560
|
-
path: [type.name, field.name].join('.'),
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
function inputFieldDescriptionRemoved(type, field) {
|
|
564
|
-
return {
|
|
565
|
-
criticality: {
|
|
566
|
-
level: CriticalityLevel.NonBreaking,
|
|
567
|
-
},
|
|
568
|
-
type: ChangeType.InputFieldDescriptionRemoved,
|
|
569
|
-
message: `Description was removed from input field '${type.name}.${field.name}'`,
|
|
570
|
-
path: [type.name, field.name].join('.'),
|
|
571
|
-
};
|
|
572
|
-
}
|
|
573
|
-
function inputFieldDescriptionChanged(input, oldField, newField) {
|
|
574
|
-
return {
|
|
575
|
-
criticality: {
|
|
576
|
-
level: CriticalityLevel.NonBreaking,
|
|
577
|
-
},
|
|
578
|
-
type: ChangeType.InputFieldDescriptionChanged,
|
|
579
|
-
message: `Input field '${input.name}.${oldField.name}' description changed from '${oldField.description}' to '${newField.description}'`,
|
|
580
|
-
path: [input.name, oldField.name].join('.'),
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
function inputFieldDefaultValueChanged(input, oldField, newField) {
|
|
584
|
-
return {
|
|
585
|
-
criticality: {
|
|
586
|
-
level: CriticalityLevel.Dangerous,
|
|
587
|
-
reason:
|
|
588
|
-
'Changing the default value for an argument may change the runtime behaviour of a field if it was never provided.',
|
|
589
|
-
},
|
|
590
|
-
type: ChangeType.InputFieldDefaultValueChanged,
|
|
591
|
-
message: `Input field '${input.name}.${oldField.name}' default value changed from '${oldField.defaultValue}' to '${newField.defaultValue}'`,
|
|
592
|
-
path: [input.name, oldField.name].join('.'),
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
function inputFieldTypeChanged(input, oldField, newField) {
|
|
596
|
-
return {
|
|
597
|
-
criticality: safeChangeForInputValue(oldField.type, newField.type)
|
|
598
|
-
? {
|
|
599
|
-
level: CriticalityLevel.NonBreaking,
|
|
600
|
-
reason:
|
|
601
|
-
'Changing an input field from non-null to null is considered non-breaking.',
|
|
602
|
-
}
|
|
603
|
-
: {
|
|
604
|
-
level: CriticalityLevel.Breaking,
|
|
605
|
-
reason:
|
|
606
|
-
'Changing the type of an input field can cause existing queries that use this field to error.',
|
|
607
|
-
},
|
|
608
|
-
type: ChangeType.InputFieldTypeChanged,
|
|
609
|
-
message: `Input field '${input.name}.${
|
|
610
|
-
oldField.name
|
|
611
|
-
}' changed type from '${oldField.type.toString()}' to '${newField.type.toString()}'`,
|
|
612
|
-
path: [input.name, oldField.name].join('.'),
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
function changesInInputObject(oldInput, newInput) {
|
|
617
|
-
const changes = [];
|
|
618
|
-
const oldFields = oldInput.getFields();
|
|
619
|
-
const newFields = newInput.getFields();
|
|
620
|
-
const oldNames = Object.keys(oldFields).map((name) => oldFields[name].name);
|
|
621
|
-
const newNames = Object.keys(newFields).map((name) => newFields[name].name);
|
|
622
|
-
const added = diffArrays(newNames, oldNames).map((name) => newFields[name]);
|
|
623
|
-
const removed = diffArrays(oldNames, newNames).map((name) => oldFields[name]);
|
|
624
|
-
const common = unionArrays(oldNames, newNames).map((name) => ({
|
|
625
|
-
inOld: oldFields[name],
|
|
626
|
-
inNew: newFields[name],
|
|
627
|
-
}));
|
|
628
|
-
common.forEach(({ inOld, inNew }) => {
|
|
629
|
-
changes.push(...changesInInputField(oldInput, inOld, inNew));
|
|
630
|
-
});
|
|
631
|
-
changes.push(...added.map((field) => inputFieldAdded(newInput, field)));
|
|
632
|
-
changes.push(...removed.map((field) => inputFieldRemoved(oldInput, field)));
|
|
633
|
-
return changes;
|
|
634
|
-
}
|
|
635
|
-
function changesInInputField(input, oldField, newField) {
|
|
636
|
-
const changes = [];
|
|
637
|
-
if (isNotEqual(oldField.description, newField.description)) {
|
|
638
|
-
if (isVoid(oldField.description)) {
|
|
639
|
-
changes.push(inputFieldDescriptionAdded(input, newField));
|
|
640
|
-
}
|
|
641
|
-
else if (isVoid(newField.description)) {
|
|
642
|
-
changes.push(inputFieldDescriptionRemoved(input, oldField));
|
|
643
|
-
}
|
|
644
|
-
else {
|
|
645
|
-
changes.push(inputFieldDescriptionChanged(input, oldField, newField));
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
if (isNotEqual(oldField.defaultValue, newField.defaultValue)) {
|
|
649
|
-
if (Array.isArray(oldField.defaultValue) &&
|
|
650
|
-
Array.isArray(newField.defaultValue)) {
|
|
651
|
-
if (diffArrays(oldField.defaultValue, newField.defaultValue).length > 0) {
|
|
652
|
-
changes.push(inputFieldDefaultValueChanged(input, oldField, newField));
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
else if (JSON.stringify(oldField.defaultValue) !==
|
|
656
|
-
JSON.stringify(newField.defaultValue)) {
|
|
657
|
-
changes.push(inputFieldDefaultValueChanged(input, oldField, newField));
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
|
|
661
|
-
changes.push(inputFieldTypeChanged(input, oldField, newField));
|
|
662
|
-
}
|
|
663
|
-
// TODO: diff directives
|
|
664
|
-
return changes;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
function objectTypeInterfaceAdded(iface, type) {
|
|
668
|
-
return {
|
|
669
|
-
criticality: {
|
|
670
|
-
level: CriticalityLevel.Dangerous,
|
|
671
|
-
reason:
|
|
672
|
-
'Adding an interface to an object type may break existing clients that were not programming defensively against a new possible type.',
|
|
673
|
-
},
|
|
674
|
-
type: ChangeType.ObjectTypeInterfaceAdded,
|
|
675
|
-
message: `'${type.name}' object implements '${iface.name}' interface`,
|
|
676
|
-
path: type.name,
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function objectTypeInterfaceRemoved(iface, type) {
|
|
680
|
-
return {
|
|
681
|
-
criticality: {
|
|
682
|
-
level: CriticalityLevel.Breaking,
|
|
683
|
-
reason:
|
|
684
|
-
'Removing an interface from an object type can cause existing queries that use this in a fragment spread to error.',
|
|
685
|
-
},
|
|
686
|
-
type: ChangeType.ObjectTypeInterfaceRemoved,
|
|
687
|
-
message: `'${type.name}' object type no longer implements '${iface.name}' interface`,
|
|
688
|
-
path: type.name,
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function fieldRemoved(type, field) {
|
|
693
|
-
const entity = isInterfaceType(type) ? 'interface' : 'object type';
|
|
694
|
-
return {
|
|
695
|
-
criticality: {
|
|
696
|
-
level: CriticalityLevel.Breaking,
|
|
697
|
-
reason: field.deprecationReason
|
|
698
|
-
? `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.`
|
|
699
|
-
: `Removing a field is a breaking change. It is preferable to deprecate the field before removing it.`,
|
|
700
|
-
},
|
|
701
|
-
type: ChangeType.FieldRemoved,
|
|
702
|
-
message: `Field '${field.name}' ${
|
|
703
|
-
field.isDeprecated ? '(deprecated) ' : ''
|
|
704
|
-
}was removed from ${entity} '${type.name}'`,
|
|
705
|
-
path: [type.name, field.name].join('.'),
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
function fieldAdded(type, field) {
|
|
709
|
-
const entity = isInterfaceType(type) ? 'interface' : 'object type';
|
|
710
|
-
return {
|
|
711
|
-
criticality: {
|
|
712
|
-
level: CriticalityLevel.NonBreaking,
|
|
713
|
-
},
|
|
714
|
-
type: ChangeType.FieldAdded,
|
|
715
|
-
message: `Field '${field.name}' was added to ${entity} '${type.name}'`,
|
|
716
|
-
path: [type.name, field.name].join('.'),
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
function fieldDescriptionChanged(type, oldField, newField) {
|
|
720
|
-
return {
|
|
721
|
-
criticality: {
|
|
722
|
-
level: CriticalityLevel.NonBreaking,
|
|
723
|
-
},
|
|
724
|
-
type: ChangeType.FieldDescriptionChanged,
|
|
725
|
-
message: `Field '${type.name}.${oldField.name}' description changed from '${oldField.description}' to '${newField.description}'`,
|
|
726
|
-
path: [type.name, oldField.name].join('.'),
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
function fieldDescriptionAdded(type, field) {
|
|
730
|
-
return {
|
|
731
|
-
criticality: {
|
|
732
|
-
level: CriticalityLevel.NonBreaking,
|
|
733
|
-
},
|
|
734
|
-
type: ChangeType.FieldDescriptionAdded,
|
|
735
|
-
message: `Field '${type.name}.${field.name}' has description '${field.description}'`,
|
|
736
|
-
path: [type.name, field.name].join('.'),
|
|
737
|
-
};
|
|
738
|
-
}
|
|
739
|
-
function fieldDescriptionRemoved(type, field) {
|
|
740
|
-
return {
|
|
741
|
-
criticality: {
|
|
742
|
-
level: CriticalityLevel.NonBreaking,
|
|
743
|
-
},
|
|
744
|
-
type: ChangeType.FieldDescriptionRemoved,
|
|
745
|
-
message: `Description was removed from field '${type.name}.${field.name}'`,
|
|
746
|
-
path: [type.name, field.name].join('.'),
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
function fieldDeprecationAdded(type, field) {
|
|
750
|
-
return {
|
|
751
|
-
criticality: {
|
|
752
|
-
level: CriticalityLevel.NonBreaking,
|
|
753
|
-
},
|
|
754
|
-
type: ChangeType.FieldDeprecationAdded,
|
|
755
|
-
message: `Field '${type.name}.${field.name}' is deprecated`,
|
|
756
|
-
path: [type.name, field.name].join('.'),
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
function fieldDeprecationRemoved(type, field) {
|
|
760
|
-
return {
|
|
761
|
-
criticality: {
|
|
762
|
-
level: CriticalityLevel.Dangerous,
|
|
763
|
-
},
|
|
764
|
-
type: ChangeType.FieldDeprecationRemoved,
|
|
765
|
-
message: `Field '${type.name}.${field.name}' is no longer deprecated`,
|
|
766
|
-
path: [type.name, field.name].join('.'),
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
function fieldDeprecationReasonChanged(type, oldField, newField) {
|
|
770
|
-
return {
|
|
771
|
-
criticality: {
|
|
772
|
-
level: CriticalityLevel.NonBreaking,
|
|
773
|
-
},
|
|
774
|
-
type: ChangeType.FieldDeprecationReasonChanged,
|
|
775
|
-
message: `Deprecation reason on field '${type.name}.${newField.name}' has changed from '${oldField.deprecationReason}' to '${newField.deprecationReason}'`,
|
|
776
|
-
path: [type.name, oldField.name].join('.'),
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
function fieldDeprecationReasonAdded(type, field) {
|
|
780
|
-
return {
|
|
781
|
-
criticality: {
|
|
782
|
-
level: CriticalityLevel.NonBreaking,
|
|
783
|
-
},
|
|
784
|
-
type: ChangeType.FieldDeprecationReasonAdded,
|
|
785
|
-
message: `Field '${type.name}.${field.name}' has deprecation reason '${field.deprecationReason}'`,
|
|
786
|
-
path: [type.name, field.name].join('.'),
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
|
-
function fieldDeprecationReasonRemoved(type, field) {
|
|
790
|
-
return {
|
|
791
|
-
criticality: {
|
|
792
|
-
level: CriticalityLevel.NonBreaking,
|
|
793
|
-
},
|
|
794
|
-
type: ChangeType.FieldDeprecationReasonRemoved,
|
|
795
|
-
message: `Deprecation reason was removed from field '${type.name}.${field.name}'`,
|
|
796
|
-
path: [type.name, field.name].join('.'),
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
function fieldTypeChanged(type, oldField, newField) {
|
|
800
|
-
return {
|
|
801
|
-
criticality: {
|
|
802
|
-
level: safeChangeForField(oldField.type, newField.type)
|
|
803
|
-
? CriticalityLevel.NonBreaking
|
|
804
|
-
: CriticalityLevel.Breaking,
|
|
805
|
-
},
|
|
806
|
-
type: ChangeType.FieldTypeChanged,
|
|
807
|
-
message: `Field '${type}.${oldField.name}' changed type from '${oldField.type}' to '${newField.type}'`,
|
|
808
|
-
path: [type.name, oldField.name].join('.'),
|
|
809
|
-
};
|
|
810
|
-
}
|
|
811
|
-
function fieldArgumentAdded(type, field, arg) {
|
|
812
|
-
return {
|
|
813
|
-
criticality: isNonNullType(arg.type)
|
|
814
|
-
? {
|
|
815
|
-
level: CriticalityLevel.Breaking,
|
|
816
|
-
reason: `Adding a required argument to an existing field is a breaking change because it will cause existing uses of this field to error.`,
|
|
817
|
-
}
|
|
818
|
-
: {
|
|
819
|
-
level: CriticalityLevel.Dangerous,
|
|
820
|
-
reason: `Adding a new argument to an existing field may involve a change in resolve function logic that potentially may cause some side effects.`,
|
|
821
|
-
},
|
|
822
|
-
type: ChangeType.FieldArgumentAdded,
|
|
823
|
-
message: `Argument '${arg.name}: ${arg.type}' added to field '${type.name}.${field.name}'`,
|
|
824
|
-
path: [type.name, field.name, arg.name].join('.'),
|
|
825
|
-
};
|
|
826
|
-
}
|
|
827
|
-
function fieldArgumentRemoved(type, field, arg) {
|
|
828
|
-
return {
|
|
829
|
-
criticality: {
|
|
830
|
-
level: CriticalityLevel.Breaking,
|
|
831
|
-
reason: `Removing a field argument is a breaking change because it will cause existing queries that use this argument to error.`,
|
|
832
|
-
},
|
|
833
|
-
type: ChangeType.FieldArgumentRemoved,
|
|
834
|
-
message: `Argument '${arg.name}: ${arg.type}' was removed from field '${type.name}.${field.name}'`,
|
|
835
|
-
path: [type.name, field.name, arg.name].join('.'),
|
|
836
|
-
};
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
function fieldArgumentDescriptionChanged(type, field, oldArg, newArg) {
|
|
840
|
-
return {
|
|
841
|
-
criticality: {
|
|
842
|
-
level: CriticalityLevel.NonBreaking,
|
|
843
|
-
},
|
|
844
|
-
type: ChangeType.FieldArgumentDescriptionChanged,
|
|
845
|
-
message: `Description for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.description}' to '${newArg.description}'`,
|
|
846
|
-
path: [type.name, field.name, oldArg.name].join('.'),
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
function fieldArgumentDefaultChanged(type, field, oldArg, newArg) {
|
|
850
|
-
return {
|
|
851
|
-
criticality: {
|
|
852
|
-
level: CriticalityLevel.Dangerous,
|
|
853
|
-
reason:
|
|
854
|
-
'Changing the default value for an argument may change the runtime behaviour of a field if it was never provided.',
|
|
855
|
-
},
|
|
856
|
-
type: ChangeType.FieldArgumentDefaultChanged,
|
|
857
|
-
message:
|
|
858
|
-
typeof oldArg.defaultValue === 'undefined'
|
|
859
|
-
? `Default value '${newArg.defaultValue}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
860
|
-
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.defaultValue}' to '${newArg.defaultValue}'`,
|
|
861
|
-
path: [type.name, field.name, oldArg.name].join('.'),
|
|
862
|
-
};
|
|
863
|
-
}
|
|
864
|
-
function fieldArgumentTypeChanged(type, field, oldArg, newArg) {
|
|
865
|
-
return {
|
|
866
|
-
criticality: safeChangeForInputValue(oldArg.type, newArg.type)
|
|
867
|
-
? {
|
|
868
|
-
level: CriticalityLevel.NonBreaking,
|
|
869
|
-
reason: `Changing an input field from non-null to null is considered non-breaking.`,
|
|
870
|
-
}
|
|
871
|
-
: {
|
|
872
|
-
level: CriticalityLevel.Breaking,
|
|
873
|
-
reason: `Changing the type of a field's argument can cause existing queries that use this argument to error.`,
|
|
874
|
-
},
|
|
875
|
-
type: ChangeType.FieldArgumentTypeChanged,
|
|
876
|
-
message: `Type for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.type}' to '${newArg.type}'`,
|
|
877
|
-
path: [type.name, field.name, oldArg.name].join('.'),
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
function changesInArgument(type, field, oldArg, newArg) {
|
|
882
|
-
const changes = [];
|
|
883
|
-
if (isNotEqual(oldArg.description, newArg.description)) {
|
|
884
|
-
changes.push(fieldArgumentDescriptionChanged(type, field, oldArg, newArg));
|
|
885
|
-
}
|
|
886
|
-
if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
|
|
887
|
-
if (
|
|
888
|
-
Array.isArray(oldArg.defaultValue) &&
|
|
889
|
-
Array.isArray(newArg.defaultValue)
|
|
890
|
-
) {
|
|
891
|
-
const diff = diffArrays(oldArg.defaultValue, newArg.defaultValue);
|
|
892
|
-
if (diff.length > 0) {
|
|
893
|
-
changes.push(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
894
|
-
}
|
|
895
|
-
} else if (
|
|
896
|
-
JSON.stringify(oldArg.defaultValue) !==
|
|
897
|
-
JSON.stringify(newArg.defaultValue)
|
|
898
|
-
) {
|
|
899
|
-
changes.push(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
|
|
903
|
-
changes.push(fieldArgumentTypeChanged(type, field, oldArg, newArg));
|
|
904
|
-
}
|
|
905
|
-
return changes;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
function changesInField(type, oldField, newField) {
|
|
909
|
-
const changes = [];
|
|
910
|
-
if (isNotEqual(oldField.description, newField.description)) {
|
|
911
|
-
if (isVoid(oldField.description)) {
|
|
912
|
-
changes.push(fieldDescriptionAdded(type, newField));
|
|
913
|
-
}
|
|
914
|
-
else if (isVoid(newField.description)) {
|
|
915
|
-
changes.push(fieldDescriptionRemoved(type, oldField));
|
|
916
|
-
}
|
|
917
|
-
else {
|
|
918
|
-
changes.push(fieldDescriptionChanged(type, oldField, newField));
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
if (isNotEqual(oldField.isDeprecated, newField.isDeprecated)) {
|
|
922
|
-
if (newField.isDeprecated) {
|
|
923
|
-
changes.push(fieldDeprecationAdded(type, newField));
|
|
924
|
-
}
|
|
925
|
-
else {
|
|
926
|
-
changes.push(fieldDeprecationRemoved(type, oldField));
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
if (isNotEqual(oldField.deprecationReason, newField.deprecationReason)) {
|
|
930
|
-
if (isVoid(oldField.deprecationReason)) {
|
|
931
|
-
changes.push(fieldDeprecationReasonAdded(type, newField));
|
|
932
|
-
}
|
|
933
|
-
else if (isVoid(newField.deprecationReason)) {
|
|
934
|
-
changes.push(fieldDeprecationReasonRemoved(type, oldField));
|
|
935
|
-
}
|
|
936
|
-
else {
|
|
937
|
-
changes.push(fieldDeprecationReasonChanged(type, oldField, newField));
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
|
|
941
|
-
changes.push(fieldTypeChanged(type, oldField, newField));
|
|
942
|
-
}
|
|
943
|
-
const oldArgs = oldField.args;
|
|
944
|
-
const newArgs = newField.args;
|
|
945
|
-
const oldNames = oldArgs.map((a) => a.name);
|
|
946
|
-
const newNames = newArgs.map((a) => a.name);
|
|
947
|
-
const added = diffArrays(newNames, oldNames).map((name) => newArgs.find((a) => a.name === name));
|
|
948
|
-
const removed = diffArrays(oldNames, newNames).map((name) => oldArgs.find((a) => a.name === name));
|
|
949
|
-
const common = unionArrays(oldNames, newNames).map((name) => ({
|
|
950
|
-
inOld: oldArgs.find((a) => a.name === name),
|
|
951
|
-
inNew: newArgs.find((a) => a.name === name),
|
|
952
|
-
}));
|
|
953
|
-
common.forEach(({ inOld, inNew }) => {
|
|
954
|
-
changes.push(...changesInArgument(type, oldField, inOld, inNew));
|
|
955
|
-
});
|
|
956
|
-
changes.push(...added.map((arg) => fieldArgumentAdded(type, newField, arg)));
|
|
957
|
-
changes.push(...removed.map((arg) => fieldArgumentRemoved(type, oldField, arg)));
|
|
958
|
-
return changes;
|
|
959
|
-
}
|
|
960
|
-
|
|
961
|
-
function changesInObject(oldType, newType) {
|
|
962
|
-
const changes = [];
|
|
963
|
-
// Interfaces
|
|
964
|
-
changes.push(...addedInterfaces(oldType, newType));
|
|
965
|
-
changes.push(...removedInterfaces(oldType, newType));
|
|
966
|
-
// Fields
|
|
967
|
-
changes.push(...addedFields(oldType, newType));
|
|
968
|
-
changes.push(...removedFields(oldType, newType));
|
|
969
|
-
changedFields(oldType, newType).forEach(({ inOld, inNew }) => {
|
|
970
|
-
changes.push(...changesInField(oldType, inOld, inNew));
|
|
971
|
-
});
|
|
972
|
-
return changes;
|
|
973
|
-
}
|
|
974
|
-
function addedInterfaces(oldType, newType) {
|
|
975
|
-
const oldInterfaces = oldType.getInterfaces();
|
|
976
|
-
const newInterfaces = newType.getInterfaces();
|
|
977
|
-
const oldNames = oldInterfaces.map((i) => i.name);
|
|
978
|
-
const newNames = newInterfaces.map((i) => i.name);
|
|
979
|
-
return diffArrays(newNames, oldNames)
|
|
980
|
-
.map((name) => newInterfaces.find((i) => i.name === name))
|
|
981
|
-
.map((i) => objectTypeInterfaceAdded(i, newType));
|
|
982
|
-
}
|
|
983
|
-
function removedInterfaces(oldType, newType) {
|
|
984
|
-
const oldInterfaces = oldType.getInterfaces();
|
|
985
|
-
const newInterfaces = newType.getInterfaces();
|
|
986
|
-
const oldNames = oldInterfaces.map((i) => i.name);
|
|
987
|
-
const newNames = newInterfaces.map((i) => i.name);
|
|
988
|
-
return diffArrays(oldNames, newNames)
|
|
989
|
-
.map((name) => oldInterfaces.find((i) => i.name === name))
|
|
990
|
-
.map((i) => objectTypeInterfaceRemoved(i, newType));
|
|
991
|
-
}
|
|
992
|
-
function addedFields(oldType, newType) {
|
|
993
|
-
const oldFields = oldType.getFields();
|
|
994
|
-
const newFields = newType.getFields();
|
|
995
|
-
const oldNames = Object.keys(oldFields);
|
|
996
|
-
const newNames = Object.keys(newFields);
|
|
997
|
-
return diffArrays(newNames, oldNames)
|
|
998
|
-
.map((name) => newFields[name])
|
|
999
|
-
.map((f) => fieldAdded(newType, f));
|
|
1000
|
-
}
|
|
1001
|
-
function removedFields(oldType, newType) {
|
|
1002
|
-
const oldFields = oldType.getFields();
|
|
1003
|
-
const newFields = newType.getFields();
|
|
1004
|
-
const oldNames = Object.keys(oldFields);
|
|
1005
|
-
const newNames = Object.keys(newFields);
|
|
1006
|
-
return diffArrays(oldNames, newNames)
|
|
1007
|
-
.map((name) => oldFields[name])
|
|
1008
|
-
.map((f) => fieldRemoved(oldType, f));
|
|
1009
|
-
}
|
|
1010
|
-
function changedFields(oldType, newType) {
|
|
1011
|
-
const oldFields = oldType.getFields();
|
|
1012
|
-
const newFields = newType.getFields();
|
|
1013
|
-
const oldNames = Object.keys(oldFields);
|
|
1014
|
-
const newNames = Object.keys(newFields);
|
|
1015
|
-
return unionArrays(oldNames, newNames).map((name) => ({
|
|
1016
|
-
inOld: oldFields[name],
|
|
1017
|
-
inNew: newFields[name],
|
|
1018
|
-
}));
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
function changesInInterface(oldInterface, newInterface) {
|
|
1022
|
-
const changes = [];
|
|
1023
|
-
changes.push(...addedFields$1(oldInterface, newInterface));
|
|
1024
|
-
changes.push(...removedFields$1(oldInterface, newInterface));
|
|
1025
|
-
changedFields$1(oldInterface, newInterface).forEach(({ inOld, inNew }) => {
|
|
1026
|
-
changes.push(...changesInField(oldInterface, inOld, inNew));
|
|
1027
|
-
});
|
|
1028
|
-
return changes;
|
|
1029
|
-
}
|
|
1030
|
-
function addedFields$1(oldInterface, newInterface) {
|
|
1031
|
-
const oldFields = oldInterface.getFields();
|
|
1032
|
-
const newFields = newInterface.getFields();
|
|
1033
|
-
const oldNames = Object.keys(oldFields);
|
|
1034
|
-
const newNames = Object.keys(newFields);
|
|
1035
|
-
return diffArrays(newNames, oldNames)
|
|
1036
|
-
.map((name) => newFields[name])
|
|
1037
|
-
.map((f) => fieldAdded(newInterface, f));
|
|
1038
|
-
}
|
|
1039
|
-
function removedFields$1(oldInterface, newInterface) {
|
|
1040
|
-
const oldFields = oldInterface.getFields();
|
|
1041
|
-
const newFields = newInterface.getFields();
|
|
1042
|
-
const oldNames = Object.keys(oldFields);
|
|
1043
|
-
const newNames = Object.keys(newFields);
|
|
1044
|
-
return diffArrays(oldNames, newNames)
|
|
1045
|
-
.map((name) => oldFields[name])
|
|
1046
|
-
.map((f) => fieldRemoved(oldInterface, f));
|
|
1047
|
-
}
|
|
1048
|
-
function changedFields$1(oldInterface, newInterface) {
|
|
1049
|
-
const oldFields = oldInterface.getFields();
|
|
1050
|
-
const newFields = newInterface.getFields();
|
|
1051
|
-
const oldNames = Object.keys(oldFields);
|
|
1052
|
-
const newNames = Object.keys(newFields);
|
|
1053
|
-
return unionArrays(oldNames, newNames).map((name) => ({
|
|
1054
|
-
inOld: oldFields[name],
|
|
1055
|
-
inNew: newFields[name],
|
|
1056
|
-
}));
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
function changesInDirective(oldDirective, newDirective) {
|
|
1060
|
-
const changes = [];
|
|
1061
|
-
if (isNotEqual(oldDirective.description, newDirective.description)) {
|
|
1062
|
-
changes.push(directiveDescriptionChanged(oldDirective, newDirective));
|
|
1063
|
-
}
|
|
1064
|
-
// locations added
|
|
1065
|
-
changes.push(...diffArrays(newDirective.locations, oldDirective.locations).map((location) => directiveLocationAdded(newDirective, location)));
|
|
1066
|
-
// locations removed
|
|
1067
|
-
changes.push(...diffArrays(oldDirective.locations, newDirective.locations).map((location) => directiveLocationRemoved(oldDirective, location)));
|
|
1068
|
-
const oldNames = oldDirective.args.map((a) => a.name);
|
|
1069
|
-
const newNames = newDirective.args.map((a) => a.name);
|
|
1070
|
-
// arguments added
|
|
1071
|
-
changes.push(...diffArrays(newNames, oldNames).map((name) => directiveArgumentAdded(newDirective, newDirective.args.find((a) => a.name === name))));
|
|
1072
|
-
// arguments removed
|
|
1073
|
-
changes.push(...diffArrays(oldNames, newNames).map((name) => directiveArgumentRemoved(oldDirective, oldDirective.args.find((a) => a.name === name))));
|
|
1074
|
-
// common arguments
|
|
1075
|
-
unionArrays(oldNames, newNames).forEach((name) => {
|
|
1076
|
-
const oldArg = oldDirective.args.find((a) => a.name === name);
|
|
1077
|
-
const newArg = newDirective.args.find((a) => a.name === name);
|
|
1078
|
-
changes.push(...changesInDirectiveArgument(oldDirective, oldArg, newArg));
|
|
1079
|
-
});
|
|
1080
|
-
return changes;
|
|
1081
|
-
}
|
|
1082
|
-
function changesInDirectiveArgument(directive, oldArg, newArg) {
|
|
1083
|
-
const changes = [];
|
|
1084
|
-
if (isNotEqual(oldArg.description, newArg.description)) {
|
|
1085
|
-
changes.push(directiveArgumentDescriptionChanged(directive, oldArg, newArg));
|
|
1086
|
-
}
|
|
1087
|
-
if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
|
|
1088
|
-
changes.push(directiveArgumentDefaultValueChanged(directive, oldArg, newArg));
|
|
1089
|
-
}
|
|
1090
|
-
if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
|
|
1091
|
-
changes.push(directiveArgumentTypeChanged(directive, oldArg, newArg));
|
|
1092
|
-
}
|
|
1093
|
-
return changes;
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
function diffSchema(oldSchema, newSchema) {
|
|
1097
|
-
const changes = [];
|
|
1098
|
-
const types = diffTypes(oldSchema, newSchema);
|
|
1099
|
-
const directives = diffDirectives(oldSchema, newSchema);
|
|
1100
|
-
// Added and removed types
|
|
1101
|
-
changes.push(...types.added.map(typeAdded));
|
|
1102
|
-
changes.push(...types.removed.map(typeRemoved));
|
|
1103
|
-
// Added and removed directives
|
|
1104
|
-
changes.push(...directives.added.map(directiveAdded));
|
|
1105
|
-
changes.push(...directives.removed.map(directiveRemoved));
|
|
1106
|
-
// Changes in Schema
|
|
1107
|
-
changes.push(...changesInSchema(oldSchema, newSchema));
|
|
1108
|
-
// Changes in Type
|
|
1109
|
-
types.common.forEach(({ inOld, inNew }) => {
|
|
1110
|
-
changes.push(...changesInType(inOld, inNew));
|
|
1111
|
-
});
|
|
1112
|
-
// Changes in Directives
|
|
1113
|
-
directives.common.forEach(({ inOld, inNew }) => {
|
|
1114
|
-
changes.push(...changesInDirective(inOld, inNew));
|
|
1115
|
-
});
|
|
1116
|
-
return changes;
|
|
1117
|
-
}
|
|
1118
|
-
function diffTypes(oldSchema, newSchema) {
|
|
1119
|
-
const oldTypeMap = oldSchema.getTypeMap();
|
|
1120
|
-
const newTypeMap = newSchema.getTypeMap();
|
|
1121
|
-
const oldTypenames = Object.keys(oldTypeMap).filter((name) => !isPrimitive(name));
|
|
1122
|
-
const newTypenames = Object.keys(newTypeMap).filter((name) => !isPrimitive(name));
|
|
1123
|
-
const added = diffArrays(newTypenames, oldTypenames).map((name) => newTypeMap[name]);
|
|
1124
|
-
const removed = diffArrays(oldTypenames, newTypenames).map((name) => oldTypeMap[name]);
|
|
1125
|
-
const common = unionArrays(oldTypenames, newTypenames).map((name) => ({
|
|
1126
|
-
inOld: oldTypeMap[name],
|
|
1127
|
-
inNew: newTypeMap[name],
|
|
1128
|
-
}));
|
|
1129
|
-
return {
|
|
1130
|
-
added,
|
|
1131
|
-
removed,
|
|
1132
|
-
common,
|
|
1133
|
-
};
|
|
1134
|
-
}
|
|
1135
|
-
function diffDirectives(oldSchema, newSchema) {
|
|
1136
|
-
const oldDirectives = oldSchema.getDirectives();
|
|
1137
|
-
const newDirectives = newSchema.getDirectives();
|
|
1138
|
-
const oldNames = oldDirectives.map((d) => d.name);
|
|
1139
|
-
const newNames = newDirectives.map((d) => d.name);
|
|
1140
|
-
const added = diffArrays(newNames, oldNames).map((name) => newDirectives.find((d) => d.name === name));
|
|
1141
|
-
const removed = diffArrays(oldNames, newNames).map((name) => oldDirectives.find((d) => d.name === name));
|
|
1142
|
-
const common = unionArrays(oldNames, newNames).map((name) => ({
|
|
1143
|
-
inOld: oldDirectives.find((d) => d.name === name),
|
|
1144
|
-
inNew: newDirectives.find((d) => d.name === name),
|
|
1145
|
-
}));
|
|
1146
|
-
return {
|
|
1147
|
-
added,
|
|
1148
|
-
removed,
|
|
1149
|
-
common,
|
|
1150
|
-
};
|
|
1151
|
-
}
|
|
1152
|
-
function changesInSchema(oldSchema, newSchema) {
|
|
1153
|
-
const changes = [];
|
|
1154
|
-
const oldRoot = {
|
|
1155
|
-
query: (oldSchema.getQueryType() || {}).name,
|
|
1156
|
-
mutation: (oldSchema.getMutationType() || {}).name,
|
|
1157
|
-
subscription: (oldSchema.getSubscriptionType() || {})
|
|
1158
|
-
.name,
|
|
1159
|
-
};
|
|
1160
|
-
const newRoot = {
|
|
1161
|
-
query: (newSchema.getQueryType() || {}).name,
|
|
1162
|
-
mutation: (newSchema.getMutationType() || {}).name,
|
|
1163
|
-
subscription: (newSchema.getSubscriptionType() || {})
|
|
1164
|
-
.name,
|
|
1165
|
-
};
|
|
1166
|
-
if (isNotEqual(oldRoot.query, newRoot.query)) {
|
|
1167
|
-
changes.push(schemaQueryTypeChanged(oldSchema, newSchema));
|
|
1168
|
-
}
|
|
1169
|
-
if (isNotEqual(oldRoot.mutation, newRoot.mutation)) {
|
|
1170
|
-
changes.push(schemaMutationTypeChanged(oldSchema, newSchema));
|
|
1171
|
-
}
|
|
1172
|
-
if (isNotEqual(oldRoot.subscription, newRoot.subscription)) {
|
|
1173
|
-
changes.push(schemaSubscriptionTypeChanged(oldSchema, newSchema));
|
|
1174
|
-
}
|
|
1175
|
-
return changes;
|
|
1176
|
-
}
|
|
1177
|
-
function changesInType(oldType, newType) {
|
|
1178
|
-
let changes = [];
|
|
1179
|
-
if (isEnumType(oldType) && isEnumType(newType)) {
|
|
1180
|
-
changes = changesInEnum(oldType, newType);
|
|
1181
|
-
}
|
|
1182
|
-
else if (isUnionType(oldType) && isUnionType(newType)) {
|
|
1183
|
-
changes = changesInUnion(oldType, newType);
|
|
1184
|
-
}
|
|
1185
|
-
else if (isInputObjectType(oldType) && isInputObjectType(newType)) {
|
|
1186
|
-
changes = changesInInputObject(oldType, newType);
|
|
1187
|
-
}
|
|
1188
|
-
else if (isObjectType(oldType) && isObjectType(newType)) {
|
|
1189
|
-
changes = changesInObject(oldType, newType);
|
|
1190
|
-
}
|
|
1191
|
-
else if (isInterfaceType(oldType) && isInterfaceType(newType)) {
|
|
1192
|
-
changes = changesInInterface(oldType, newType);
|
|
1193
|
-
}
|
|
1194
|
-
else if (isScalarType(oldType) && isScalarType(newType)) ;
|
|
1195
|
-
else {
|
|
1196
|
-
changes = [typeKindChanged(oldType, newType)];
|
|
1197
|
-
}
|
|
1198
|
-
if (isNotEqual(oldType.description, newType.description)) {
|
|
1199
|
-
if (isVoid(oldType.description)) {
|
|
1200
|
-
changes.push(typeDescriptionAdded(newType));
|
|
1201
|
-
}
|
|
1202
|
-
else if (isVoid(newType.description)) {
|
|
1203
|
-
changes.push(typeDescriptionRemoved(oldType));
|
|
1204
|
-
}
|
|
1205
|
-
else {
|
|
1206
|
-
changes.push(typeDescriptionChanged(oldType, newType));
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
return changes;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
function parsePath(path) {
|
|
1213
|
-
return path.split('.');
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
1217
|
-
return changes.map((change) => {
|
|
1218
|
-
if (change.type === ChangeType.FieldRemoved &&
|
|
1219
|
-
change.criticality.level === CriticalityLevel.Breaking &&
|
|
1220
|
-
change.path) {
|
|
1221
|
-
const [typeName, fieldName] = parsePath(change.path);
|
|
1222
|
-
const type = oldSchema.getType(typeName);
|
|
1223
|
-
if (isObjectType(type) || isInterfaceType(type)) {
|
|
1224
|
-
const field = type.getFields()[fieldName];
|
|
1225
|
-
if (field.isDeprecated) {
|
|
1226
|
-
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.Dangerous }) });
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
return change;
|
|
1231
|
-
});
|
|
1232
|
-
};
|
|
1233
|
-
|
|
1234
|
-
const descriptionChangeTypes = [
|
|
1235
|
-
ChangeType.FieldArgumentDescriptionChanged,
|
|
1236
|
-
ChangeType.DirectiveDescriptionChanged,
|
|
1237
|
-
ChangeType.DirectiveArgumentDescriptionChanged,
|
|
1238
|
-
ChangeType.EnumValueDescriptionChanged,
|
|
1239
|
-
ChangeType.FieldDescriptionChanged,
|
|
1240
|
-
ChangeType.FieldDescriptionAdded,
|
|
1241
|
-
ChangeType.FieldDescriptionRemoved,
|
|
1242
|
-
ChangeType.InputFieldDescriptionAdded,
|
|
1243
|
-
ChangeType.InputFieldDescriptionRemoved,
|
|
1244
|
-
ChangeType.InputFieldDescriptionChanged,
|
|
1245
|
-
ChangeType.TypeDescriptionChanged,
|
|
1246
|
-
];
|
|
1247
|
-
const ignoreDescriptionChanges = ({ changes }) => {
|
|
1248
|
-
return changes.filter((change) => descriptionChangeTypes.indexOf(change.type) === -1);
|
|
1249
|
-
};
|
|
1250
|
-
|
|
1251
|
-
const rules = /*#__PURE__*/Object.freeze({
|
|
1252
|
-
__proto__: null,
|
|
1253
|
-
suppressRemovalOfDeprecatedField: suppressRemovalOfDeprecatedField,
|
|
1254
|
-
ignoreDescriptionChanges: ignoreDescriptionChanges
|
|
1255
|
-
});
|
|
1256
|
-
|
|
1257
|
-
const DiffRule = rules;
|
|
1258
|
-
function diff(oldSchema, newSchema, rules = []) {
|
|
1259
|
-
const changes = diffSchema(oldSchema, newSchema);
|
|
1260
|
-
return rules.reduce(
|
|
1261
|
-
(prev, rule) =>
|
|
1262
|
-
rule({
|
|
1263
|
-
changes: prev,
|
|
1264
|
-
oldSchema,
|
|
1265
|
-
newSchema,
|
|
1266
|
-
}),
|
|
1267
|
-
changes,
|
|
1268
|
-
);
|
|
1269
|
-
}
|
|
1270
|
-
|
|
1271
|
-
function readDocument(source) {
|
|
1272
|
-
const result = {
|
|
1273
|
-
source,
|
|
1274
|
-
fragments: [],
|
|
1275
|
-
operations: [],
|
|
1276
|
-
hasFragments: false,
|
|
1277
|
-
hasOperations: false,
|
|
1278
|
-
};
|
|
1279
|
-
const documentNode = parse(source.body);
|
|
1280
|
-
const filepath = source.name;
|
|
1281
|
-
const definitions = documentNode.definitions || [];
|
|
1282
|
-
definitions.forEach((node) => {
|
|
1283
|
-
if (isOperation(node)) {
|
|
1284
|
-
result.operations.push({
|
|
1285
|
-
node,
|
|
1286
|
-
source: filepath,
|
|
1287
|
-
});
|
|
1288
|
-
} else if (isFragment(node)) {
|
|
1289
|
-
result.fragments.push({
|
|
1290
|
-
node,
|
|
1291
|
-
source: filepath,
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
});
|
|
1295
|
-
result.hasFragments = result.fragments.length > 0;
|
|
1296
|
-
result.hasOperations = result.operations.length > 0;
|
|
1297
|
-
return result;
|
|
1298
|
-
}
|
|
1299
|
-
function isOperation(node) {
|
|
1300
|
-
return node.kind === Kind.OPERATION_DEFINITION;
|
|
1301
|
-
}
|
|
1302
|
-
function isFragment(node) {
|
|
1303
|
-
return node.kind === Kind.FRAGMENT_DEFINITION;
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
function validateQueryDepth({ source, doc, maxDepth, fragmentGraph, }) {
|
|
1307
|
-
try {
|
|
1308
|
-
calculateDepth({
|
|
1309
|
-
node: doc,
|
|
1310
|
-
currentDepth: 0,
|
|
1311
|
-
maxDepth,
|
|
1312
|
-
getFragment(name) {
|
|
1313
|
-
return fragmentGraph.getNodeData(name);
|
|
1314
|
-
},
|
|
1315
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
catch (errorOrNode) {
|
|
1318
|
-
if (errorOrNode instanceof Error) {
|
|
1319
|
-
throw errorOrNode;
|
|
1320
|
-
}
|
|
1321
|
-
const node = errorOrNode;
|
|
1322
|
-
return new GraphQLError(`Query exceeds maximum depth of ${maxDepth}`, node, source, node.loc && node.loc.start ? [node.loc.start] : undefined);
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
function calculateDepth({ node, currentDepth, maxDepth, getFragment, }) {
|
|
1326
|
-
if (maxDepth && currentDepth > maxDepth) {
|
|
1327
|
-
throw node;
|
|
1328
|
-
}
|
|
1329
|
-
switch (node.kind) {
|
|
1330
|
-
case Kind.FIELD: {
|
|
1331
|
-
if (node.name.value.startsWith('__') || !node.selectionSet) {
|
|
1332
|
-
return 0;
|
|
1333
|
-
}
|
|
1334
|
-
const maxInnerDepth = calculateDepth({
|
|
1335
|
-
node: node.selectionSet,
|
|
1336
|
-
currentDepth: currentDepth + 1,
|
|
1337
|
-
maxDepth,
|
|
1338
|
-
getFragment,
|
|
1339
|
-
});
|
|
1340
|
-
return 1 + maxInnerDepth;
|
|
1341
|
-
}
|
|
1342
|
-
case Kind.SELECTION_SET: {
|
|
1343
|
-
return Math.max(...node.selections.map((selection) => {
|
|
1344
|
-
return calculateDepth({
|
|
1345
|
-
node: selection,
|
|
1346
|
-
currentDepth: currentDepth,
|
|
1347
|
-
maxDepth,
|
|
1348
|
-
getFragment,
|
|
1349
|
-
});
|
|
1350
|
-
}));
|
|
1351
|
-
}
|
|
1352
|
-
case Kind.DOCUMENT: {
|
|
1353
|
-
return Math.max(...node.definitions.map((def) => {
|
|
1354
|
-
return calculateDepth({
|
|
1355
|
-
node: def,
|
|
1356
|
-
currentDepth: currentDepth,
|
|
1357
|
-
maxDepth,
|
|
1358
|
-
getFragment,
|
|
1359
|
-
});
|
|
1360
|
-
}));
|
|
1361
|
-
}
|
|
1362
|
-
case Kind.OPERATION_DEFINITION:
|
|
1363
|
-
case Kind.INLINE_FRAGMENT:
|
|
1364
|
-
case Kind.FRAGMENT_DEFINITION: {
|
|
1365
|
-
return Math.max(...node.selectionSet.selections.map((selection) => {
|
|
1366
|
-
return calculateDepth({
|
|
1367
|
-
node: selection,
|
|
1368
|
-
currentDepth,
|
|
1369
|
-
maxDepth,
|
|
1370
|
-
getFragment,
|
|
1371
|
-
});
|
|
1372
|
-
}));
|
|
1373
|
-
}
|
|
1374
|
-
case Kind.FRAGMENT_SPREAD:
|
|
1375
|
-
return calculateDepth({
|
|
1376
|
-
node: getFragment(node.name.value),
|
|
1377
|
-
currentDepth,
|
|
1378
|
-
maxDepth,
|
|
1379
|
-
getFragment,
|
|
1380
|
-
});
|
|
1381
|
-
default: {
|
|
1382
|
-
throw new Error(`Couldn't handle ${node.kind}`);
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
function transformDocumentWithApollo(doc, {keepClientFields}) {
|
|
1388
|
-
return visit(doc, {
|
|
1389
|
-
Field(node) {
|
|
1390
|
-
return keepClientFields
|
|
1391
|
-
? removeDirectives(node, ['client'])
|
|
1392
|
-
: removeFieldIfDirectives(node, ['client']);
|
|
1393
|
-
},
|
|
1394
|
-
});
|
|
1395
|
-
}
|
|
1396
|
-
function transformSchemaWithApollo(schema) {
|
|
1397
|
-
return extendSchema(
|
|
1398
|
-
schema,
|
|
1399
|
-
parse(/* GraphQL */ `
|
|
1400
|
-
directive @connection(key: String!, filter: [String]) on FIELD
|
|
1401
|
-
`),
|
|
1402
|
-
);
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
function validate(schema, sources, options) {
|
|
1406
|
-
const config = Object.assign({ strictDeprecated: true, strictFragments: true, keepClientFields: false, apollo: false }, options);
|
|
1407
|
-
const invalidDocuments = [];
|
|
1408
|
-
// read documents
|
|
1409
|
-
const documents = sources.map(readDocument);
|
|
1410
|
-
// keep all named fragments
|
|
1411
|
-
const fragments = [];
|
|
1412
|
-
const fragmentNames = [];
|
|
1413
|
-
const graph = new DepGraph({ circular: true });
|
|
1414
|
-
documents.forEach((doc) => {
|
|
1415
|
-
doc.fragments.forEach((fragment) => {
|
|
1416
|
-
fragmentNames.push(fragment.node.name.value);
|
|
1417
|
-
fragments.push(fragment);
|
|
1418
|
-
graph.addNode(fragment.node.name.value, fragment.node);
|
|
1419
|
-
});
|
|
1420
|
-
});
|
|
1421
|
-
fragments.forEach((fragment) => {
|
|
1422
|
-
const depends = extractFragments(print(fragment.node));
|
|
1423
|
-
if (depends) {
|
|
1424
|
-
depends.forEach((name) => {
|
|
1425
|
-
graph.addDependency(fragment.node.name.value, name);
|
|
1426
|
-
});
|
|
1427
|
-
}
|
|
1428
|
-
});
|
|
1429
|
-
documents
|
|
1430
|
-
// since we include fragments, validate only operations
|
|
1431
|
-
.filter((doc) => doc.hasOperations)
|
|
1432
|
-
.forEach((doc) => {
|
|
1433
|
-
const docWithOperations = {
|
|
1434
|
-
kind: 'Document',
|
|
1435
|
-
definitions: doc.operations.map((d) => d.node),
|
|
1436
|
-
};
|
|
1437
|
-
const extractedFragments = (extractFragments(print(docWithOperations)) || [])
|
|
1438
|
-
// resolve all nested fragments
|
|
1439
|
-
.map((fragmentName) => resolveFragment(graph.getNodeData(fragmentName), graph))
|
|
1440
|
-
// flatten arrays
|
|
1441
|
-
.reduce((list, current) => list.concat(current), [])
|
|
1442
|
-
// remove duplicates
|
|
1443
|
-
.filter((def, i, all) => all.findIndex((item) => item.name.value === def.name.value) === i);
|
|
1444
|
-
const merged = {
|
|
1445
|
-
kind: 'Document',
|
|
1446
|
-
definitions: [...docWithOperations.definitions, ...extractedFragments],
|
|
1447
|
-
};
|
|
1448
|
-
const transformedSchema = config.apollo
|
|
1449
|
-
? transformSchemaWithApollo(schema)
|
|
1450
|
-
: schema;
|
|
1451
|
-
const transformedDoc = config.apollo
|
|
1452
|
-
? transformDocumentWithApollo(merged, {
|
|
1453
|
-
keepClientFields: config.keepClientFields,
|
|
1454
|
-
})
|
|
1455
|
-
: merged;
|
|
1456
|
-
const errors = validate$1(transformedSchema, transformedDoc) || [];
|
|
1457
|
-
if (config.maxDepth) {
|
|
1458
|
-
const depthError = validateQueryDepth({
|
|
1459
|
-
source: doc.source,
|
|
1460
|
-
doc: transformedDoc,
|
|
1461
|
-
maxDepth: config.maxDepth,
|
|
1462
|
-
fragmentGraph: graph,
|
|
1463
|
-
});
|
|
1464
|
-
if (depthError) {
|
|
1465
|
-
errors.push(depthError);
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
const deprecated = config.strictDeprecated
|
|
1469
|
-
? findDeprecatedUsages(transformedSchema, parse(doc.source.body))
|
|
1470
|
-
: [];
|
|
1471
|
-
const duplicatedFragments = config.strictFragments
|
|
1472
|
-
? findDuplicatedFragments(fragmentNames)
|
|
1473
|
-
: [];
|
|
1474
|
-
if (sumLengths(errors, duplicatedFragments, deprecated) > 0) {
|
|
1475
|
-
invalidDocuments.push({
|
|
1476
|
-
source: doc.source,
|
|
1477
|
-
errors: [...errors, ...duplicatedFragments],
|
|
1478
|
-
deprecated,
|
|
1479
|
-
});
|
|
1480
|
-
}
|
|
1481
|
-
});
|
|
1482
|
-
return invalidDocuments;
|
|
1483
|
-
}
|
|
1484
|
-
function findDuplicatedFragments(fragmentNames) {
|
|
1485
|
-
return fragmentNames
|
|
1486
|
-
.filter((name, i, all) => all.indexOf(name) !== i)
|
|
1487
|
-
.map((name) => new GraphQLError(`Name of '${name}' fragment is not unique`));
|
|
1488
|
-
}
|
|
1489
|
-
//
|
|
1490
|
-
// PostInfo -> AuthorInfo
|
|
1491
|
-
// AuthorInfo -> None
|
|
1492
|
-
//
|
|
1493
|
-
function resolveFragment(fragment, graph) {
|
|
1494
|
-
return graph
|
|
1495
|
-
.dependenciesOf(fragment.name.value)
|
|
1496
|
-
.reduce((list, current) => [
|
|
1497
|
-
...list,
|
|
1498
|
-
...resolveFragment(graph.getNodeData(current), graph),
|
|
1499
|
-
], [fragment]);
|
|
1500
|
-
}
|
|
1501
|
-
function extractFragments(document) {
|
|
1502
|
-
return (document.match(/[\.]{3}[a-z0-9\_]+\b/gi) || []).map((name) => name.replace('...', ''));
|
|
1503
|
-
}
|
|
1504
|
-
function sumLengths(...arrays) {
|
|
1505
|
-
return arrays.reduce((sum, { length }) => sum + length, 0);
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
|
-
function compareTwoStrings(str1, str2) {
|
|
1509
|
-
if (!str1.length && !str2.length)
|
|
1510
|
-
return 1;
|
|
1511
|
-
if (!str1.length || !str2.length)
|
|
1512
|
-
return 0;
|
|
1513
|
-
if (str1.toUpperCase() === str2.toUpperCase())
|
|
1514
|
-
return 1;
|
|
1515
|
-
if (str1.length === 1 && str2.length === 1)
|
|
1516
|
-
return 0;
|
|
1517
|
-
const pairs1 = wordLetterPairs(str1);
|
|
1518
|
-
const pairs2 = wordLetterPairs(str2);
|
|
1519
|
-
const union = pairs1.length + pairs2.length;
|
|
1520
|
-
let intersection = 0;
|
|
1521
|
-
pairs1.forEach((pair1) => {
|
|
1522
|
-
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
1523
|
-
if (pair1 !== pair2)
|
|
1524
|
-
continue;
|
|
1525
|
-
intersection++;
|
|
1526
|
-
pairs2.splice(i, 1);
|
|
1527
|
-
break;
|
|
1528
|
-
}
|
|
1529
|
-
});
|
|
1530
|
-
return (intersection * 2) / union;
|
|
1531
|
-
}
|
|
1532
|
-
function findBestMatch(mainString, targetStrings) {
|
|
1533
|
-
if (!areArgsValid(mainString, targetStrings))
|
|
1534
|
-
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
1535
|
-
const ratings = targetStrings.map((target) => ({
|
|
1536
|
-
target,
|
|
1537
|
-
rating: compareTwoStrings(mainString, target.value),
|
|
1538
|
-
}));
|
|
1539
|
-
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
1540
|
-
return { ratings, bestMatch };
|
|
1541
|
-
}
|
|
1542
|
-
function flattenDeep(arr) {
|
|
1543
|
-
return Array.isArray(arr)
|
|
1544
|
-
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
1545
|
-
: [arr];
|
|
1546
|
-
}
|
|
1547
|
-
function areArgsValid(mainString, targetStrings) {
|
|
1548
|
-
if (typeof mainString !== 'string')
|
|
1549
|
-
return false;
|
|
1550
|
-
if (!Array.isArray(targetStrings))
|
|
1551
|
-
return false;
|
|
1552
|
-
if (!targetStrings.length)
|
|
1553
|
-
return false;
|
|
1554
|
-
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
1555
|
-
return false;
|
|
1556
|
-
return true;
|
|
1557
|
-
}
|
|
1558
|
-
function letterPairs(str) {
|
|
1559
|
-
const pairs = [];
|
|
1560
|
-
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
1561
|
-
pairs[i] = str.substring(i, i + 2);
|
|
1562
|
-
return pairs;
|
|
1563
|
-
}
|
|
1564
|
-
function wordLetterPairs(str) {
|
|
1565
|
-
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
1566
|
-
return flattenDeep(pairs);
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
function similar(schema, typeName, threshold = 0.4) {
|
|
1570
|
-
const typeMap = schema.getTypeMap();
|
|
1571
|
-
const targets = Object.keys(schema.getTypeMap())
|
|
1572
|
-
.filter((name) => !isPrimitive(name) && !isForIntrospection(name))
|
|
1573
|
-
.map((name) => ({
|
|
1574
|
-
typeId: name,
|
|
1575
|
-
value: stripType(typeMap[name]),
|
|
1576
|
-
}));
|
|
1577
|
-
const results = {};
|
|
1578
|
-
if (typeof typeName !== 'undefined' &&
|
|
1579
|
-
!targets.some((t) => t.typeId === typeName)) {
|
|
1580
|
-
throw new Error(`Type '${typeName}' doesn't exist`);
|
|
1581
|
-
}
|
|
1582
|
-
(typeName ? [{ typeId: typeName, value: '' }] : targets).forEach((source) => {
|
|
1583
|
-
const sourceType = schema.getType(source.typeId);
|
|
1584
|
-
const matchWith = targets.filter((target) => schema.getType(target.typeId).astNode.kind ===
|
|
1585
|
-
sourceType.astNode.kind && target.typeId !== source.typeId);
|
|
1586
|
-
if (matchWith.length > 0) {
|
|
1587
|
-
const found = similarTo(sourceType, matchWith, threshold);
|
|
1588
|
-
if (found) {
|
|
1589
|
-
results[source.typeId] = found;
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
});
|
|
1593
|
-
return results;
|
|
1594
|
-
}
|
|
1595
|
-
function similarTo(type, targets, threshold) {
|
|
1596
|
-
const types = targets.filter((target) => target.typeId !== type.name);
|
|
1597
|
-
const result = findBestMatch(stripType(type), types);
|
|
1598
|
-
if (result.bestMatch.rating < threshold) {
|
|
1599
|
-
return;
|
|
1600
|
-
}
|
|
1601
|
-
return {
|
|
1602
|
-
bestMatch: result.bestMatch,
|
|
1603
|
-
ratings: result.ratings
|
|
1604
|
-
.filter((r) => r.rating >= threshold && r.target !== result.bestMatch.target)
|
|
1605
|
-
.sort((a, b) => a.rating - b.rating)
|
|
1606
|
-
.reverse(),
|
|
1607
|
-
};
|
|
1608
|
-
}
|
|
1609
|
-
function stripType(type) {
|
|
1610
|
-
return printType(type)
|
|
1611
|
-
.trim()
|
|
1612
|
-
.replace(/^[a-z]+ [^\{]+\{/g, '')
|
|
1613
|
-
.replace(/\}$/g, '')
|
|
1614
|
-
.trim()
|
|
1615
|
-
.split('\n')
|
|
1616
|
-
.map((s) => s.trim())
|
|
1617
|
-
.sort((a, b) => a.localeCompare(b))
|
|
1618
|
-
.join(' ');
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
function coverage(schema, sources) {
|
|
1622
|
-
const coverage = {
|
|
1623
|
-
sources,
|
|
1624
|
-
types: {},
|
|
1625
|
-
};
|
|
1626
|
-
const typeMap = schema.getTypeMap();
|
|
1627
|
-
const typeInfo = new TypeInfo(schema);
|
|
1628
|
-
const visitor = (source) => ({
|
|
1629
|
-
Field(node) {
|
|
1630
|
-
const fieldDef = typeInfo.getFieldDef();
|
|
1631
|
-
const parent = typeInfo.getParentType();
|
|
1632
|
-
if (parent &&
|
|
1633
|
-
parent.name &&
|
|
1634
|
-
!isForIntrospection(parent.name) &&
|
|
1635
|
-
fieldDef &&
|
|
1636
|
-
fieldDef.name &&
|
|
1637
|
-
fieldDef.name !== '__typename' &&
|
|
1638
|
-
fieldDef.name !== '__schema') {
|
|
1639
|
-
const locations = coverage.types[parent.name].children[fieldDef.name].locations[source.name];
|
|
1640
|
-
coverage.types[parent.name].hits++;
|
|
1641
|
-
coverage.types[parent.name].children[fieldDef.name].locations[source.name] = [node.loc].concat(locations || []);
|
|
1642
|
-
coverage.types[parent.name].children[fieldDef.name].hits++;
|
|
1643
|
-
}
|
|
1644
|
-
},
|
|
1645
|
-
});
|
|
1646
|
-
for (const typename in typeMap) {
|
|
1647
|
-
if (!isForIntrospection(typename) && !isPrimitive(typename)) {
|
|
1648
|
-
const type = typeMap[typename];
|
|
1649
|
-
if (isObjectType(type) || isInterfaceType(type)) {
|
|
1650
|
-
const typeCoverage = {
|
|
1651
|
-
hits: 0,
|
|
1652
|
-
type,
|
|
1653
|
-
children: {},
|
|
1654
|
-
};
|
|
1655
|
-
const fieldMap = type.getFields();
|
|
1656
|
-
for (const fieldname in fieldMap) {
|
|
1657
|
-
const field = fieldMap[fieldname];
|
|
1658
|
-
typeCoverage.children[field.name] = {
|
|
1659
|
-
hits: 0,
|
|
1660
|
-
locations: {},
|
|
1661
|
-
};
|
|
1662
|
-
}
|
|
1663
|
-
coverage.types[type.name] = typeCoverage;
|
|
1664
|
-
}
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
const documents = coverage.sources.map(readDocument);
|
|
1668
|
-
documents.forEach((doc, i) => {
|
|
1669
|
-
const source = coverage.sources[i];
|
|
1670
|
-
doc.operations.forEach((op) => {
|
|
1671
|
-
visit(op.node, visitWithTypeInfo(typeInfo, visitor(source)));
|
|
1672
|
-
});
|
|
1673
|
-
doc.fragments.forEach((fr) => {
|
|
1674
|
-
visit(fr.node, visitWithTypeInfo(typeInfo, visitor(source)));
|
|
1675
|
-
});
|
|
1676
|
-
});
|
|
1677
|
-
return coverage;
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
export { ChangeType, CriticalityLevel, DiffRule, coverage, diff, getTypePrefix, similar, validate };
|
|
1681
|
-
//# sourceMappingURL=index.esm.js.map
|