@graphql-inspector/core 0.0.0-canary.da4bdcc → 0.0.0-canary.e5ed377
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/coverage/index.d.ts +9 -0
- package/diff/argument.d.ts +2 -2
- package/diff/changes/change.d.ts +2 -0
- package/diff/changes/enum.d.ts +2 -0
- 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 +2 -1
- 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 +3 -2
- package/diff/schema.d.ts +1 -0
- package/diff/union.d.ts +2 -2
- package/index.d.ts +5 -2
- package/{index.cjs.js → index.js} +683 -413
- package/{index.esm.js → index.mjs} +680 -415
- package/package.json +16 -5
- 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/directive-count.d.ts +10 -0
- package/validate/index.d.ts +12 -2
- package/validate/query-depth.d.ts +2 -1
- package/diff/common/compare.d.ts +0 -3
- package/index.cjs.js.map +0 -1
- package/index.esm.js.map +0 -1
- package/utils/arrays.d.ts +0 -2
|
@@ -2,15 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
6
|
+
|
|
7
|
+
const tslib = require('tslib');
|
|
5
8
|
const graphql = require('graphql');
|
|
9
|
+
const inspect = _interopDefault(require('object-inspect'));
|
|
6
10
|
const dependencyGraph = require('dependency-graph');
|
|
7
11
|
|
|
12
|
+
function keyMap(list, keyFn) {
|
|
13
|
+
return list.reduce((map, item) => {
|
|
14
|
+
map[keyFn(item)] = item;
|
|
15
|
+
return map;
|
|
16
|
+
}, Object.create(null));
|
|
17
|
+
}
|
|
8
18
|
function isEqual(a, b) {
|
|
9
19
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
10
20
|
if (a.length !== b.length)
|
|
11
21
|
return false;
|
|
12
22
|
for (var index = 0; index < a.length; index++) {
|
|
13
|
-
if (a[index]
|
|
23
|
+
if (!isEqual(a[index], b[index])) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
30
|
+
const aRecord = a;
|
|
31
|
+
const bRecord = b;
|
|
32
|
+
const aKeys = Object.keys(aRecord);
|
|
33
|
+
const bKeys = Object.keys(bRecord);
|
|
34
|
+
if (aKeys.length !== bKeys.length)
|
|
35
|
+
return false;
|
|
36
|
+
for (const key of aKeys) {
|
|
37
|
+
if (!isEqual(aRecord[key], bRecord[key])) {
|
|
14
38
|
return false;
|
|
15
39
|
}
|
|
16
40
|
}
|
|
@@ -24,12 +48,62 @@ function isNotEqual(a, b) {
|
|
|
24
48
|
function isVoid(a) {
|
|
25
49
|
return typeof a === 'undefined' || a === null;
|
|
26
50
|
}
|
|
27
|
-
|
|
28
51
|
function diffArrays(a, b) {
|
|
29
|
-
return a.filter(c => !b.some(d => d
|
|
52
|
+
return a.filter(c => !b.some(d => isEqual(d, c)));
|
|
53
|
+
}
|
|
54
|
+
function compareLists(oldList, newList, callbacks) {
|
|
55
|
+
const oldMap = keyMap(oldList, ({ name }) => name);
|
|
56
|
+
const newMap = keyMap(newList, ({ name }) => name);
|
|
57
|
+
const added = [];
|
|
58
|
+
const removed = [];
|
|
59
|
+
const mutual = [];
|
|
60
|
+
for (const oldItem of oldList) {
|
|
61
|
+
const newItem = newMap[oldItem.name];
|
|
62
|
+
if (newItem === undefined) {
|
|
63
|
+
removed.push(oldItem);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
mutual.push({
|
|
67
|
+
newVersion: newItem,
|
|
68
|
+
oldVersion: oldItem,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const newItem of newList) {
|
|
73
|
+
if (oldMap[newItem.name] === undefined) {
|
|
74
|
+
added.push(newItem);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (callbacks) {
|
|
78
|
+
if (callbacks.onAdded) {
|
|
79
|
+
added.forEach(callbacks.onAdded);
|
|
80
|
+
}
|
|
81
|
+
if (callbacks.onRemoved) {
|
|
82
|
+
removed.forEach(callbacks.onRemoved);
|
|
83
|
+
}
|
|
84
|
+
if (callbacks.onMutual) {
|
|
85
|
+
mutual.forEach(callbacks.onMutual);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
added,
|
|
90
|
+
removed,
|
|
91
|
+
mutual,
|
|
92
|
+
};
|
|
30
93
|
}
|
|
31
|
-
|
|
32
|
-
|
|
94
|
+
|
|
95
|
+
function isDeprecated(fieldOrEnumValue) {
|
|
96
|
+
var _a, _b;
|
|
97
|
+
if ('isDeprecated' in fieldOrEnumValue) {
|
|
98
|
+
return fieldOrEnumValue['isDeprecated'];
|
|
99
|
+
}
|
|
100
|
+
if (fieldOrEnumValue.deprecationReason != null) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
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')) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
33
107
|
}
|
|
34
108
|
|
|
35
109
|
function safeChangeForField(oldType, newType) {
|
|
@@ -41,8 +115,7 @@ function safeChangeForField(oldType, newType) {
|
|
|
41
115
|
return safeChangeForField(ofType, newType.ofType);
|
|
42
116
|
}
|
|
43
117
|
if (graphql.isListType(oldType)) {
|
|
44
|
-
return ((graphql.isListType(newType) &&
|
|
45
|
-
safeChangeForField(oldType.ofType, newType.ofType)) ||
|
|
118
|
+
return ((graphql.isListType(newType) && safeChangeForField(oldType.ofType, newType.ofType)) ||
|
|
46
119
|
(graphql.isNonNullType(newType) && safeChangeForField(oldType, newType.ofType)));
|
|
47
120
|
}
|
|
48
121
|
return false;
|
|
@@ -55,7 +128,7 @@ function safeChangeForInputValue(oldType, newType) {
|
|
|
55
128
|
return safeChangeForInputValue(oldType.ofType, newType.ofType);
|
|
56
129
|
}
|
|
57
130
|
if (graphql.isNonNullType(oldType)) {
|
|
58
|
-
const ofType = graphql.isNonNullType(newType) ? newType : newType;
|
|
131
|
+
const ofType = graphql.isNonNullType(newType) ? newType.ofType : newType;
|
|
59
132
|
return safeChangeForInputValue(oldType.ofType, ofType);
|
|
60
133
|
}
|
|
61
134
|
return false;
|
|
@@ -74,10 +147,10 @@ function getTypePrefix(type) {
|
|
|
74
147
|
[graphql.Kind.ENUM_TYPE_DEFINITION]: 'enum',
|
|
75
148
|
[graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
|
|
76
149
|
};
|
|
77
|
-
return kindsMap[kind];
|
|
150
|
+
return kindsMap[kind.toString()];
|
|
78
151
|
}
|
|
79
152
|
function isPrimitive(type) {
|
|
80
|
-
return
|
|
153
|
+
return ['String', 'Int', 'Float', 'Boolean', 'ID'].indexOf(typeof type === 'string' ? type : type.name) !== -1;
|
|
81
154
|
}
|
|
82
155
|
function isForIntrospection(type) {
|
|
83
156
|
return ([
|
|
@@ -95,9 +168,23 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
95
168
|
const errors = [];
|
|
96
169
|
const typeInfo = new graphql.TypeInfo(schema);
|
|
97
170
|
graphql.visit(ast, graphql.visitWithTypeInfo(typeInfo, {
|
|
171
|
+
Argument(node) {
|
|
172
|
+
const argument = typeInfo.getArgument();
|
|
173
|
+
if (argument) {
|
|
174
|
+
const reason = argument.deprecationReason;
|
|
175
|
+
if (reason) {
|
|
176
|
+
const fieldDef = typeInfo.getFieldDef();
|
|
177
|
+
if (fieldDef) {
|
|
178
|
+
errors.push(new graphql.GraphQLError(`The argument '${argument === null || argument === void 0 ? void 0 : argument.name}' of '${fieldDef.name}' is deprecated. ${reason}`, [
|
|
179
|
+
node,
|
|
180
|
+
]));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
98
185
|
Field(node) {
|
|
99
186
|
const fieldDef = typeInfo.getFieldDef();
|
|
100
|
-
if (fieldDef && fieldDef
|
|
187
|
+
if (fieldDef && isDeprecated(fieldDef)) {
|
|
101
188
|
const parentType = typeInfo.getParentType();
|
|
102
189
|
if (parentType) {
|
|
103
190
|
const reason = fieldDef.deprecationReason;
|
|
@@ -107,7 +194,7 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
107
194
|
},
|
|
108
195
|
EnumValue(node) {
|
|
109
196
|
const enumVal = typeInfo.getEnumValue();
|
|
110
|
-
if (enumVal && enumVal
|
|
197
|
+
if (enumVal && isDeprecated(enumVal)) {
|
|
111
198
|
const type = graphql.getNamedType(typeInfo.getInputType());
|
|
112
199
|
if (type) {
|
|
113
200
|
const reason = enumVal.deprecationReason;
|
|
@@ -132,6 +219,71 @@ function removeDirectives(node, directiveNames) {
|
|
|
132
219
|
}
|
|
133
220
|
return node;
|
|
134
221
|
}
|
|
222
|
+
function getReachableTypes(schema) {
|
|
223
|
+
const reachableTypes = new Set();
|
|
224
|
+
const collect = (type) => {
|
|
225
|
+
const typeName = type.name;
|
|
226
|
+
if (reachableTypes.has(typeName)) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
reachableTypes.add(typeName);
|
|
230
|
+
if (graphql.isScalarType(type)) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
else if (graphql.isInterfaceType(type) || graphql.isObjectType(type)) {
|
|
234
|
+
if (graphql.isInterfaceType(type)) {
|
|
235
|
+
const { objects, interfaces } = schema.getImplementations(type);
|
|
236
|
+
for (const child of objects) {
|
|
237
|
+
collect(child);
|
|
238
|
+
}
|
|
239
|
+
for (const child of interfaces) {
|
|
240
|
+
collect(child);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const fields = type.getFields();
|
|
244
|
+
for (const fieldName in fields) {
|
|
245
|
+
const field = fields[fieldName];
|
|
246
|
+
collect(resolveOutputType(field.type));
|
|
247
|
+
const args = field.args;
|
|
248
|
+
for (const argName in args) {
|
|
249
|
+
const arg = args[argName];
|
|
250
|
+
collect(resolveInputType(arg.type));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else if (graphql.isUnionType(type)) {
|
|
255
|
+
const types = type.getTypes();
|
|
256
|
+
for (const child of types) {
|
|
257
|
+
collect(child);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (graphql.isInputObjectType(type)) {
|
|
261
|
+
const fields = type.getFields();
|
|
262
|
+
for (const fieldName in fields) {
|
|
263
|
+
const field = fields[fieldName];
|
|
264
|
+
collect(resolveInputType(field.type));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
for (const type of [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]) {
|
|
269
|
+
if (type) {
|
|
270
|
+
collect(type);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return reachableTypes;
|
|
274
|
+
}
|
|
275
|
+
function resolveOutputType(output) {
|
|
276
|
+
if (graphql.isListType(output) || graphql.isNonNullType(output)) {
|
|
277
|
+
return resolveOutputType(output.ofType);
|
|
278
|
+
}
|
|
279
|
+
return output;
|
|
280
|
+
}
|
|
281
|
+
function resolveInputType(input) {
|
|
282
|
+
if (graphql.isListType(input) || graphql.isNonNullType(input)) {
|
|
283
|
+
return resolveInputType(input.ofType);
|
|
284
|
+
}
|
|
285
|
+
return input;
|
|
286
|
+
}
|
|
135
287
|
|
|
136
288
|
(function (ChangeType) {
|
|
137
289
|
// Argument
|
|
@@ -154,6 +306,8 @@ function removeDirectives(node, directiveNames) {
|
|
|
154
306
|
ChangeType["EnumValueAdded"] = "ENUM_VALUE_ADDED";
|
|
155
307
|
ChangeType["EnumValueDescriptionChanged"] = "ENUM_VALUE_DESCRIPTION_CHANGED";
|
|
156
308
|
ChangeType["EnumValueDeprecationReasonChanged"] = "ENUM_VALUE_DEPRECATION_REASON_CHANGED";
|
|
309
|
+
ChangeType["EnumValueDeprecationReasonAdded"] = "ENUM_VALUE_DEPRECATION_REASON_ADDED";
|
|
310
|
+
ChangeType["EnumValueDeprecationReasonRemoved"] = "ENUM_VALUE_DEPRECATION_REASON_REMOVED";
|
|
157
311
|
// Field
|
|
158
312
|
ChangeType["FieldRemoved"] = "FIELD_REMOVED";
|
|
159
313
|
ChangeType["FieldAdded"] = "FIELD_ADDED";
|
|
@@ -351,9 +505,7 @@ function directiveLocationRemoved(directive, location) {
|
|
|
351
505
|
function directiveArgumentAdded(directive, arg) {
|
|
352
506
|
return {
|
|
353
507
|
criticality: {
|
|
354
|
-
level: graphql.isNonNullType(arg.type)
|
|
355
|
-
? exports.CriticalityLevel.Breaking
|
|
356
|
-
: exports.CriticalityLevel.NonBreaking,
|
|
508
|
+
level: graphql.isNonNullType(arg.type) ? exports.CriticalityLevel.Breaking : exports.CriticalityLevel.NonBreaking,
|
|
357
509
|
},
|
|
358
510
|
type: exports.ChangeType.DirectiveArgumentAdded,
|
|
359
511
|
message: `Argument '${arg.name}' was added to directive '${directive.name}'`,
|
|
@@ -416,7 +568,7 @@ function enumValueRemoved(oldEnum, value) {
|
|
|
416
568
|
reason: `Removing an enum value will cause existing queries that use this enum value to error.`,
|
|
417
569
|
},
|
|
418
570
|
type: exports.ChangeType.EnumValueRemoved,
|
|
419
|
-
message: `Enum value '${value.name}' was removed from enum '${oldEnum.name}'`,
|
|
571
|
+
message: `Enum value '${value.name}' ${isDeprecated(value) ? '(deprecated) ' : ''}was removed from enum '${oldEnum.name}'`,
|
|
420
572
|
path: [oldEnum.name, value.name].join('.'),
|
|
421
573
|
};
|
|
422
574
|
}
|
|
@@ -448,35 +600,59 @@ function enumValueDeprecationReasonChanged(newEnum, oldValue, newValue) {
|
|
|
448
600
|
criticality: {
|
|
449
601
|
level: exports.CriticalityLevel.NonBreaking,
|
|
450
602
|
},
|
|
451
|
-
type: exports.ChangeType.
|
|
452
|
-
message: oldValue.deprecationReason
|
|
453
|
-
|
|
454
|
-
|
|
603
|
+
type: exports.ChangeType.EnumValueDeprecationReasonChanged,
|
|
604
|
+
message: `Enum value '${newEnum.name}.${newValue.name}' deprecation reason changed from '${oldValue.deprecationReason}' to '${newValue.deprecationReason}'`,
|
|
605
|
+
path: [newEnum.name, oldValue.name].join('.'),
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
function enumValueDeprecationReasonAdded(newEnum, oldValue, newValue) {
|
|
609
|
+
return {
|
|
610
|
+
criticality: {
|
|
611
|
+
level: exports.CriticalityLevel.NonBreaking,
|
|
612
|
+
},
|
|
613
|
+
type: exports.ChangeType.EnumValueDeprecationReasonAdded,
|
|
614
|
+
message: `Enum value '${newEnum.name}.${newValue.name}' was deprecated with reason '${newValue.deprecationReason}'`,
|
|
615
|
+
path: [newEnum.name, oldValue.name].join('.'),
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
function enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue) {
|
|
619
|
+
return {
|
|
620
|
+
criticality: {
|
|
621
|
+
level: exports.CriticalityLevel.NonBreaking,
|
|
622
|
+
},
|
|
623
|
+
type: exports.ChangeType.EnumValueDeprecationReasonRemoved,
|
|
624
|
+
message: `Deprecation reason was removed from enum value '${newEnum.name}.${newValue.name}'`,
|
|
455
625
|
path: [newEnum.name, oldValue.name].join('.'),
|
|
456
626
|
};
|
|
457
627
|
}
|
|
458
628
|
|
|
459
|
-
function changesInEnum(oldEnum, newEnum) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
629
|
+
function changesInEnum(oldEnum, newEnum, addChange) {
|
|
630
|
+
compareLists(oldEnum.getValues(), newEnum.getValues(), {
|
|
631
|
+
onAdded(value) {
|
|
632
|
+
addChange(enumValueAdded(newEnum, value));
|
|
633
|
+
},
|
|
634
|
+
onRemoved(value) {
|
|
635
|
+
addChange(enumValueRemoved(oldEnum, value));
|
|
636
|
+
},
|
|
637
|
+
onMutual(value) {
|
|
638
|
+
const oldValue = value.oldVersion;
|
|
639
|
+
const newValue = value.newVersion;
|
|
640
|
+
if (isNotEqual(oldValue.description, newValue.description)) {
|
|
641
|
+
addChange(enumValueDescriptionChanged(newEnum, oldValue, newValue));
|
|
642
|
+
}
|
|
643
|
+
if (isNotEqual(oldValue.deprecationReason, newValue.deprecationReason)) {
|
|
644
|
+
if (isVoid(oldValue.deprecationReason)) {
|
|
645
|
+
addChange(enumValueDeprecationReasonAdded(newEnum, oldValue, newValue));
|
|
646
|
+
}
|
|
647
|
+
else if (isVoid(newValue.deprecationReason)) {
|
|
648
|
+
addChange(enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue));
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
addChange(enumValueDeprecationReasonChanged(newEnum, oldValue, newValue));
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
},
|
|
478
655
|
});
|
|
479
|
-
return changes;
|
|
480
656
|
}
|
|
481
657
|
|
|
482
658
|
function unionMemberRemoved(union, type) {
|
|
@@ -502,17 +678,81 @@ function unionMemberAdded(union, type) {
|
|
|
502
678
|
};
|
|
503
679
|
}
|
|
504
680
|
|
|
505
|
-
function changesInUnion(oldUnion, newUnion) {
|
|
506
|
-
const changes = [];
|
|
681
|
+
function changesInUnion(oldUnion, newUnion, addChange) {
|
|
507
682
|
const oldTypes = oldUnion.getTypes();
|
|
508
683
|
const newTypes = newUnion.getTypes();
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
684
|
+
compareLists(oldTypes, newTypes, {
|
|
685
|
+
onAdded(t) {
|
|
686
|
+
addChange(unionMemberAdded(newUnion, t));
|
|
687
|
+
},
|
|
688
|
+
onRemoved(t) {
|
|
689
|
+
addChange(unionMemberRemoved(oldUnion, t));
|
|
690
|
+
},
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function compareTwoStrings(str1, str2) {
|
|
695
|
+
if (!str1.length && !str2.length)
|
|
696
|
+
return 1;
|
|
697
|
+
if (!str1.length || !str2.length)
|
|
698
|
+
return 0;
|
|
699
|
+
if (str1.toUpperCase() === str2.toUpperCase())
|
|
700
|
+
return 1;
|
|
701
|
+
if (str1.length === 1 && str2.length === 1)
|
|
702
|
+
return 0;
|
|
703
|
+
const pairs1 = wordLetterPairs(str1);
|
|
704
|
+
const pairs2 = wordLetterPairs(str2);
|
|
705
|
+
const union = pairs1.length + pairs2.length;
|
|
706
|
+
let intersection = 0;
|
|
707
|
+
pairs1.forEach(pair1 => {
|
|
708
|
+
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
709
|
+
if (pair1 !== pair2)
|
|
710
|
+
continue;
|
|
711
|
+
intersection++;
|
|
712
|
+
pairs2.splice(i, 1);
|
|
713
|
+
break;
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
return (intersection * 2) / union;
|
|
717
|
+
}
|
|
718
|
+
function findBestMatch(mainString, targetStrings) {
|
|
719
|
+
if (!areArgsValid(mainString, targetStrings))
|
|
720
|
+
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
721
|
+
const ratings = targetStrings.map(target => ({
|
|
722
|
+
target,
|
|
723
|
+
rating: compareTwoStrings(mainString, target.value),
|
|
724
|
+
}));
|
|
725
|
+
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
726
|
+
return { ratings, bestMatch };
|
|
727
|
+
}
|
|
728
|
+
function flattenDeep(arr) {
|
|
729
|
+
return Array.isArray(arr) ? arr.reduce((a, b) => a.concat(flattenDeep(b)), []) : [arr];
|
|
730
|
+
}
|
|
731
|
+
function areArgsValid(mainString, targetStrings) {
|
|
732
|
+
if (typeof mainString !== 'string')
|
|
733
|
+
return false;
|
|
734
|
+
if (!Array.isArray(targetStrings))
|
|
735
|
+
return false;
|
|
736
|
+
if (!targetStrings.length)
|
|
737
|
+
return false;
|
|
738
|
+
if (targetStrings.find(s => typeof s.value !== 'string'))
|
|
739
|
+
return false;
|
|
740
|
+
return true;
|
|
741
|
+
}
|
|
742
|
+
function letterPairs(str) {
|
|
743
|
+
const pairs = [];
|
|
744
|
+
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
745
|
+
pairs[i] = str.substring(i, i + 2);
|
|
746
|
+
return pairs;
|
|
747
|
+
}
|
|
748
|
+
function wordLetterPairs(str) {
|
|
749
|
+
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
750
|
+
return flattenDeep(pairs);
|
|
751
|
+
}
|
|
752
|
+
function safeString(obj) {
|
|
753
|
+
return inspect(obj)
|
|
754
|
+
.replace(/\[Object\: null prototype\] /g, '')
|
|
755
|
+
.replace(/(^')|('$)/g, '');
|
|
516
756
|
}
|
|
517
757
|
|
|
518
758
|
function inputFieldRemoved(input, field) {
|
|
@@ -522,7 +762,7 @@ function inputFieldRemoved(input, field) {
|
|
|
522
762
|
reason: 'Removing an input field will cause existing queries that use this input field to error.',
|
|
523
763
|
},
|
|
524
764
|
type: exports.ChangeType.InputFieldRemoved,
|
|
525
|
-
message: `Input field '${field.name}' was removed from input object type '${input.name}'`,
|
|
765
|
+
message: `Input field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from input object type '${input.name}'`,
|
|
526
766
|
path: [input.name, field.name].join('.'),
|
|
527
767
|
};
|
|
528
768
|
}
|
|
@@ -531,7 +771,7 @@ function inputFieldAdded(input, field) {
|
|
|
531
771
|
criticality: graphql.isNonNullType(field.type)
|
|
532
772
|
? {
|
|
533
773
|
level: exports.CriticalityLevel.Breaking,
|
|
534
|
-
reason: 'Adding a
|
|
774
|
+
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.',
|
|
535
775
|
}
|
|
536
776
|
: {
|
|
537
777
|
level: exports.CriticalityLevel.Dangerous,
|
|
@@ -575,10 +815,10 @@ function inputFieldDefaultValueChanged(input, oldField, newField) {
|
|
|
575
815
|
return {
|
|
576
816
|
criticality: {
|
|
577
817
|
level: exports.CriticalityLevel.Dangerous,
|
|
578
|
-
reason: 'Changing the default value for an argument may change the runtime
|
|
818
|
+
reason: 'Changing the default value for an argument may change the runtime behavior of a field if it was never provided.',
|
|
579
819
|
},
|
|
580
820
|
type: exports.ChangeType.InputFieldDefaultValueChanged,
|
|
581
|
-
message: `Input field '${input.name}.${oldField.name}' default value changed from '${oldField.defaultValue}' to '${newField.defaultValue}'`,
|
|
821
|
+
message: `Input field '${input.name}.${oldField.name}' default value changed from '${safeString(oldField.defaultValue)}' to '${safeString(newField.defaultValue)}'`,
|
|
582
822
|
path: [input.name, oldField.name].join('.'),
|
|
583
823
|
};
|
|
584
824
|
}
|
|
@@ -599,55 +839,46 @@ function inputFieldTypeChanged(input, oldField, newField) {
|
|
|
599
839
|
};
|
|
600
840
|
}
|
|
601
841
|
|
|
602
|
-
function changesInInputObject(oldInput, newInput) {
|
|
603
|
-
const changes = [];
|
|
842
|
+
function changesInInputObject(oldInput, newInput, addChange) {
|
|
604
843
|
const oldFields = oldInput.getFields();
|
|
605
844
|
const newFields = newInput.getFields();
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
845
|
+
compareLists(Object.values(oldFields), Object.values(newFields), {
|
|
846
|
+
onAdded(field) {
|
|
847
|
+
addChange(inputFieldAdded(newInput, field));
|
|
848
|
+
},
|
|
849
|
+
onRemoved(field) {
|
|
850
|
+
addChange(inputFieldRemoved(oldInput, field));
|
|
851
|
+
},
|
|
852
|
+
onMutual(field) {
|
|
853
|
+
changesInInputField(oldInput, field.oldVersion, field.newVersion, addChange);
|
|
854
|
+
},
|
|
616
855
|
});
|
|
617
|
-
changes.push(...added.map(field => inputFieldAdded(newInput, field)));
|
|
618
|
-
changes.push(...removed.map(field => inputFieldRemoved(oldInput, field)));
|
|
619
|
-
return changes;
|
|
620
856
|
}
|
|
621
|
-
function changesInInputField(input, oldField, newField) {
|
|
622
|
-
const changes = [];
|
|
857
|
+
function changesInInputField(input, oldField, newField, addChange) {
|
|
623
858
|
if (isNotEqual(oldField.description, newField.description)) {
|
|
624
859
|
if (isVoid(oldField.description)) {
|
|
625
|
-
|
|
860
|
+
addChange(inputFieldDescriptionAdded(input, newField));
|
|
626
861
|
}
|
|
627
862
|
else if (isVoid(newField.description)) {
|
|
628
|
-
|
|
863
|
+
addChange(inputFieldDescriptionRemoved(input, oldField));
|
|
629
864
|
}
|
|
630
865
|
else {
|
|
631
|
-
|
|
866
|
+
addChange(inputFieldDescriptionChanged(input, oldField, newField));
|
|
632
867
|
}
|
|
633
868
|
}
|
|
634
869
|
if (isNotEqual(oldField.defaultValue, newField.defaultValue)) {
|
|
635
|
-
if (Array.isArray(oldField.defaultValue) &&
|
|
636
|
-
Array.isArray(newField.defaultValue)) {
|
|
870
|
+
if (Array.isArray(oldField.defaultValue) && Array.isArray(newField.defaultValue)) {
|
|
637
871
|
if (diffArrays(oldField.defaultValue, newField.defaultValue).length > 0) {
|
|
638
|
-
|
|
872
|
+
addChange(inputFieldDefaultValueChanged(input, oldField, newField));
|
|
639
873
|
}
|
|
640
874
|
}
|
|
641
|
-
else if (JSON.stringify(oldField.defaultValue) !==
|
|
642
|
-
|
|
643
|
-
changes.push(inputFieldDefaultValueChanged(input, oldField, newField));
|
|
875
|
+
else if (JSON.stringify(oldField.defaultValue) !== JSON.stringify(newField.defaultValue)) {
|
|
876
|
+
addChange(inputFieldDefaultValueChanged(input, oldField, newField));
|
|
644
877
|
}
|
|
645
878
|
}
|
|
646
879
|
if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
|
|
647
|
-
|
|
880
|
+
addChange(inputFieldTypeChanged(input, oldField, newField));
|
|
648
881
|
}
|
|
649
|
-
// TODO: diff directives
|
|
650
|
-
return changes;
|
|
651
882
|
}
|
|
652
883
|
|
|
653
884
|
function objectTypeInterfaceAdded(iface, type) {
|
|
@@ -683,7 +914,7 @@ function fieldRemoved(type, field) {
|
|
|
683
914
|
: `Removing a field is a breaking change. It is preferable to deprecate the field before removing it.`,
|
|
684
915
|
},
|
|
685
916
|
type: exports.ChangeType.FieldRemoved,
|
|
686
|
-
message: `Field '${field.name}' ${field
|
|
917
|
+
message: `Field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from ${entity} '${type.name}'`,
|
|
687
918
|
path: [type.name, field.name].join('.'),
|
|
688
919
|
};
|
|
689
920
|
}
|
|
@@ -791,8 +1022,10 @@ function fieldTypeChanged(type, oldField, newField) {
|
|
|
791
1022
|
};
|
|
792
1023
|
}
|
|
793
1024
|
function fieldArgumentAdded(type, field, arg) {
|
|
1025
|
+
const isBreaking = graphql.isNonNullType(arg.type) && typeof arg.defaultValue === 'undefined';
|
|
1026
|
+
const defaultValueMsg = typeof arg.defaultValue !== 'undefined' ? ' (with default value) ' : ' ';
|
|
794
1027
|
return {
|
|
795
|
-
criticality:
|
|
1028
|
+
criticality: isBreaking
|
|
796
1029
|
? {
|
|
797
1030
|
level: exports.CriticalityLevel.Breaking,
|
|
798
1031
|
reason: `Adding a required argument to an existing field is a breaking change because it will cause existing uses of this field to error.`,
|
|
@@ -802,7 +1035,7 @@ function fieldArgumentAdded(type, field, arg) {
|
|
|
802
1035
|
reason: `Adding a new argument to an existing field may involve a change in resolve function logic that potentially may cause some side effects.`,
|
|
803
1036
|
},
|
|
804
1037
|
type: exports.ChangeType.FieldArgumentAdded,
|
|
805
|
-
message: `Argument '${arg.name}: ${arg.type}'
|
|
1038
|
+
message: `Argument '${arg.name}: ${arg.type}'${defaultValueMsg}added to field '${type.name}.${field.name}'`,
|
|
806
1039
|
path: [type.name, field.name, arg.name].join('.'),
|
|
807
1040
|
};
|
|
808
1041
|
}
|
|
@@ -836,8 +1069,8 @@ function fieldArgumentDefaultChanged(type, field, oldArg, newArg) {
|
|
|
836
1069
|
},
|
|
837
1070
|
type: exports.ChangeType.FieldArgumentDefaultChanged,
|
|
838
1071
|
message: typeof oldArg.defaultValue === 'undefined'
|
|
839
|
-
? `Default value '${newArg.defaultValue}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
840
|
-
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.defaultValue}' to '${newArg.defaultValue}'`,
|
|
1072
|
+
? `Default value '${safeString(newArg.defaultValue)}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
1073
|
+
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${safeString(oldArg.defaultValue)}' to '${safeString(newArg.defaultValue)}'`,
|
|
841
1074
|
path: [type.name, field.name, oldArg.name].join('.'),
|
|
842
1075
|
};
|
|
843
1076
|
}
|
|
@@ -858,339 +1091,253 @@ function fieldArgumentTypeChanged(type, field, oldArg, newArg) {
|
|
|
858
1091
|
};
|
|
859
1092
|
}
|
|
860
1093
|
|
|
861
|
-
function changesInArgument(type, field, oldArg, newArg) {
|
|
862
|
-
const changes = [];
|
|
1094
|
+
function changesInArgument(type, field, oldArg, newArg, addChange) {
|
|
863
1095
|
if (isNotEqual(oldArg.description, newArg.description)) {
|
|
864
|
-
|
|
1096
|
+
addChange(fieldArgumentDescriptionChanged(type, field, oldArg, newArg));
|
|
865
1097
|
}
|
|
866
1098
|
if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
|
|
867
|
-
if (Array.isArray(oldArg.defaultValue) &&
|
|
868
|
-
Array.isArray(newArg.defaultValue)) {
|
|
1099
|
+
if (Array.isArray(oldArg.defaultValue) && Array.isArray(newArg.defaultValue)) {
|
|
869
1100
|
const diff = diffArrays(oldArg.defaultValue, newArg.defaultValue);
|
|
870
1101
|
if (diff.length > 0) {
|
|
871
|
-
|
|
1102
|
+
addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
872
1103
|
}
|
|
873
1104
|
}
|
|
874
|
-
else if (JSON.stringify(oldArg.defaultValue) !==
|
|
875
|
-
|
|
876
|
-
changes.push(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
1105
|
+
else if (JSON.stringify(oldArg.defaultValue) !== JSON.stringify(newArg.defaultValue)) {
|
|
1106
|
+
addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
877
1107
|
}
|
|
878
1108
|
}
|
|
879
1109
|
if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
|
|
880
|
-
|
|
1110
|
+
addChange(fieldArgumentTypeChanged(type, field, oldArg, newArg));
|
|
881
1111
|
}
|
|
882
|
-
return changes;
|
|
883
1112
|
}
|
|
884
1113
|
|
|
885
|
-
function changesInField(type, oldField, newField) {
|
|
886
|
-
const changes = [];
|
|
1114
|
+
function changesInField(type, oldField, newField, addChange) {
|
|
887
1115
|
if (isNotEqual(oldField.description, newField.description)) {
|
|
888
1116
|
if (isVoid(oldField.description)) {
|
|
889
|
-
|
|
1117
|
+
addChange(fieldDescriptionAdded(type, newField));
|
|
890
1118
|
}
|
|
891
1119
|
else if (isVoid(newField.description)) {
|
|
892
|
-
|
|
1120
|
+
addChange(fieldDescriptionRemoved(type, oldField));
|
|
893
1121
|
}
|
|
894
1122
|
else {
|
|
895
|
-
|
|
1123
|
+
addChange(fieldDescriptionChanged(type, oldField, newField));
|
|
896
1124
|
}
|
|
897
1125
|
}
|
|
898
|
-
if (isNotEqual(oldField
|
|
899
|
-
if (newField
|
|
900
|
-
|
|
1126
|
+
if (isNotEqual(isDeprecated(oldField), isDeprecated(newField))) {
|
|
1127
|
+
if (isDeprecated(newField)) {
|
|
1128
|
+
addChange(fieldDeprecationAdded(type, newField));
|
|
901
1129
|
}
|
|
902
1130
|
else {
|
|
903
|
-
|
|
1131
|
+
addChange(fieldDeprecationRemoved(type, oldField));
|
|
904
1132
|
}
|
|
905
1133
|
}
|
|
906
1134
|
if (isNotEqual(oldField.deprecationReason, newField.deprecationReason)) {
|
|
907
1135
|
if (isVoid(oldField.deprecationReason)) {
|
|
908
|
-
|
|
1136
|
+
addChange(fieldDeprecationReasonAdded(type, newField));
|
|
909
1137
|
}
|
|
910
1138
|
else if (isVoid(newField.deprecationReason)) {
|
|
911
|
-
|
|
1139
|
+
addChange(fieldDeprecationReasonRemoved(type, oldField));
|
|
912
1140
|
}
|
|
913
1141
|
else {
|
|
914
|
-
|
|
1142
|
+
addChange(fieldDeprecationReasonChanged(type, oldField, newField));
|
|
915
1143
|
}
|
|
916
1144
|
}
|
|
917
1145
|
if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
|
|
918
|
-
|
|
1146
|
+
addChange(fieldTypeChanged(type, oldField, newField));
|
|
919
1147
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
common.forEach(({ inOld, inNew }) => {
|
|
931
|
-
changes.push(...changesInArgument(type, oldField, inOld, inNew));
|
|
1148
|
+
compareLists(oldField.args, newField.args, {
|
|
1149
|
+
onAdded(arg) {
|
|
1150
|
+
addChange(fieldArgumentAdded(type, newField, arg));
|
|
1151
|
+
},
|
|
1152
|
+
onRemoved(arg) {
|
|
1153
|
+
addChange(fieldArgumentRemoved(type, oldField, arg));
|
|
1154
|
+
},
|
|
1155
|
+
onMutual(arg) {
|
|
1156
|
+
changesInArgument(type, oldField, arg.oldVersion, arg.newVersion, addChange);
|
|
1157
|
+
},
|
|
932
1158
|
});
|
|
933
|
-
changes.push(...added.map(arg => fieldArgumentAdded(type, newField, arg)));
|
|
934
|
-
changes.push(...removed.map(arg => fieldArgumentRemoved(type, oldField, arg)));
|
|
935
|
-
return changes;
|
|
936
1159
|
}
|
|
937
1160
|
|
|
938
|
-
function changesInObject(oldType, newType) {
|
|
939
|
-
const changes = [];
|
|
940
|
-
// Interfaces
|
|
941
|
-
changes.push(...addedInterfaces(oldType, newType));
|
|
942
|
-
changes.push(...removedInterfaces(oldType, newType));
|
|
943
|
-
// Fields
|
|
944
|
-
changes.push(...addedFields(oldType, newType));
|
|
945
|
-
changes.push(...removedFields(oldType, newType));
|
|
946
|
-
changedFields(oldType, newType).forEach(({ inOld, inNew }) => {
|
|
947
|
-
changes.push(...changesInField(oldType, inOld, inNew));
|
|
948
|
-
});
|
|
949
|
-
return changes;
|
|
950
|
-
}
|
|
951
|
-
function addedInterfaces(oldType, newType) {
|
|
952
|
-
const oldInterfaces = oldType.getInterfaces();
|
|
953
|
-
const newInterfaces = newType.getInterfaces();
|
|
954
|
-
const oldNames = oldInterfaces.map(i => i.name);
|
|
955
|
-
const newNames = newInterfaces.map(i => i.name);
|
|
956
|
-
return diffArrays(newNames, oldNames)
|
|
957
|
-
.map(name => newInterfaces.find(i => i.name === name))
|
|
958
|
-
.map(i => objectTypeInterfaceAdded(i, newType));
|
|
959
|
-
}
|
|
960
|
-
function removedInterfaces(oldType, newType) {
|
|
1161
|
+
function changesInObject(oldType, newType, addChange) {
|
|
961
1162
|
const oldInterfaces = oldType.getInterfaces();
|
|
962
1163
|
const newInterfaces = newType.getInterfaces();
|
|
963
|
-
const oldNames = oldInterfaces.map(i => i.name);
|
|
964
|
-
const newNames = newInterfaces.map(i => i.name);
|
|
965
|
-
return diffArrays(oldNames, newNames)
|
|
966
|
-
.map(name => oldInterfaces.find(i => i.name === name))
|
|
967
|
-
.map(i => objectTypeInterfaceRemoved(i, newType));
|
|
968
|
-
}
|
|
969
|
-
function addedFields(oldType, newType) {
|
|
970
|
-
const oldFields = oldType.getFields();
|
|
971
|
-
const newFields = newType.getFields();
|
|
972
|
-
const oldNames = Object.keys(oldFields);
|
|
973
|
-
const newNames = Object.keys(newFields);
|
|
974
|
-
return diffArrays(newNames, oldNames)
|
|
975
|
-
.map(name => newFields[name])
|
|
976
|
-
.map(f => fieldAdded(newType, f));
|
|
977
|
-
}
|
|
978
|
-
function removedFields(oldType, newType) {
|
|
979
1164
|
const oldFields = oldType.getFields();
|
|
980
1165
|
const newFields = newType.getFields();
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1166
|
+
compareLists(oldInterfaces, newInterfaces, {
|
|
1167
|
+
onAdded(i) {
|
|
1168
|
+
addChange(objectTypeInterfaceAdded(i, newType));
|
|
1169
|
+
},
|
|
1170
|
+
onRemoved(i) {
|
|
1171
|
+
addChange(objectTypeInterfaceRemoved(i, oldType));
|
|
1172
|
+
},
|
|
1173
|
+
});
|
|
1174
|
+
compareLists(Object.values(oldFields), Object.values(newFields), {
|
|
1175
|
+
onAdded(f) {
|
|
1176
|
+
addChange(fieldAdded(newType, f));
|
|
1177
|
+
},
|
|
1178
|
+
onRemoved(f) {
|
|
1179
|
+
addChange(fieldRemoved(oldType, f));
|
|
1180
|
+
},
|
|
1181
|
+
onMutual(f) {
|
|
1182
|
+
changesInField(oldType, f.oldVersion, f.newVersion, addChange);
|
|
1183
|
+
},
|
|
1184
|
+
});
|
|
996
1185
|
}
|
|
997
1186
|
|
|
998
|
-
function changesInInterface(oldInterface, newInterface) {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1187
|
+
function changesInInterface(oldInterface, newInterface, addChange) {
|
|
1188
|
+
compareLists(Object.values(oldInterface.getFields()), Object.values(newInterface.getFields()), {
|
|
1189
|
+
onAdded(field) {
|
|
1190
|
+
addChange(fieldAdded(newInterface, field));
|
|
1191
|
+
},
|
|
1192
|
+
onRemoved(field) {
|
|
1193
|
+
addChange(fieldRemoved(oldInterface, field));
|
|
1194
|
+
},
|
|
1195
|
+
onMutual(field) {
|
|
1196
|
+
changesInField(oldInterface, field.oldVersion, field.newVersion, addChange);
|
|
1197
|
+
},
|
|
1004
1198
|
});
|
|
1005
|
-
return changes;
|
|
1006
|
-
}
|
|
1007
|
-
function addedFields$1(oldInterface, newInterface) {
|
|
1008
|
-
const oldFields = oldInterface.getFields();
|
|
1009
|
-
const newFields = newInterface.getFields();
|
|
1010
|
-
const oldNames = Object.keys(oldFields);
|
|
1011
|
-
const newNames = Object.keys(newFields);
|
|
1012
|
-
return diffArrays(newNames, oldNames)
|
|
1013
|
-
.map(name => newFields[name])
|
|
1014
|
-
.map(f => fieldAdded(newInterface, f));
|
|
1015
|
-
}
|
|
1016
|
-
function removedFields$1(oldInterface, newInterface) {
|
|
1017
|
-
const oldFields = oldInterface.getFields();
|
|
1018
|
-
const newFields = newInterface.getFields();
|
|
1019
|
-
const oldNames = Object.keys(oldFields);
|
|
1020
|
-
const newNames = Object.keys(newFields);
|
|
1021
|
-
return diffArrays(oldNames, newNames)
|
|
1022
|
-
.map(name => oldFields[name])
|
|
1023
|
-
.map(f => fieldRemoved(oldInterface, f));
|
|
1024
|
-
}
|
|
1025
|
-
function changedFields$1(oldInterface, newInterface) {
|
|
1026
|
-
const oldFields = oldInterface.getFields();
|
|
1027
|
-
const newFields = newInterface.getFields();
|
|
1028
|
-
const oldNames = Object.keys(oldFields);
|
|
1029
|
-
const newNames = Object.keys(newFields);
|
|
1030
|
-
return unionArrays(oldNames, newNames).map(name => ({
|
|
1031
|
-
inOld: oldFields[name],
|
|
1032
|
-
inNew: newFields[name],
|
|
1033
|
-
}));
|
|
1034
1199
|
}
|
|
1035
1200
|
|
|
1036
|
-
function changesInDirective(oldDirective, newDirective) {
|
|
1037
|
-
const changes = [];
|
|
1201
|
+
function changesInDirective(oldDirective, newDirective, addChange) {
|
|
1038
1202
|
if (isNotEqual(oldDirective.description, newDirective.description)) {
|
|
1039
|
-
|
|
1203
|
+
addChange(directiveDescriptionChanged(oldDirective, newDirective));
|
|
1040
1204
|
}
|
|
1205
|
+
const locations = {
|
|
1206
|
+
added: diffArrays(newDirective.locations, oldDirective.locations),
|
|
1207
|
+
removed: diffArrays(oldDirective.locations, newDirective.locations),
|
|
1208
|
+
};
|
|
1041
1209
|
// locations added
|
|
1042
|
-
|
|
1210
|
+
locations.added.forEach(location => addChange(directiveLocationAdded(newDirective, location)));
|
|
1043
1211
|
// locations removed
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
changes.push(...changesInDirectiveArgument(oldDirective, oldArg, newArg));
|
|
1212
|
+
locations.removed.forEach(location => addChange(directiveLocationRemoved(oldDirective, location)));
|
|
1213
|
+
compareLists(oldDirective.args, newDirective.args, {
|
|
1214
|
+
onAdded(arg) {
|
|
1215
|
+
addChange(directiveArgumentAdded(newDirective, arg));
|
|
1216
|
+
},
|
|
1217
|
+
onRemoved(arg) {
|
|
1218
|
+
addChange(directiveArgumentRemoved(oldDirective, arg));
|
|
1219
|
+
},
|
|
1220
|
+
onMutual(arg) {
|
|
1221
|
+
changesInDirectiveArgument(oldDirective, arg.oldVersion, arg.newVersion, addChange);
|
|
1222
|
+
},
|
|
1056
1223
|
});
|
|
1057
|
-
return changes;
|
|
1058
1224
|
}
|
|
1059
|
-
function changesInDirectiveArgument(directive, oldArg, newArg) {
|
|
1060
|
-
const changes = [];
|
|
1225
|
+
function changesInDirectiveArgument(directive, oldArg, newArg, addChange) {
|
|
1061
1226
|
if (isNotEqual(oldArg.description, newArg.description)) {
|
|
1062
|
-
|
|
1227
|
+
addChange(directiveArgumentDescriptionChanged(directive, oldArg, newArg));
|
|
1063
1228
|
}
|
|
1064
1229
|
if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
|
|
1065
|
-
|
|
1230
|
+
addChange(directiveArgumentDefaultValueChanged(directive, oldArg, newArg));
|
|
1066
1231
|
}
|
|
1067
1232
|
if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
|
|
1068
|
-
|
|
1233
|
+
addChange(directiveArgumentTypeChanged(directive, oldArg, newArg));
|
|
1069
1234
|
}
|
|
1070
|
-
return changes;
|
|
1071
1235
|
}
|
|
1072
1236
|
|
|
1073
1237
|
function diffSchema(oldSchema, newSchema) {
|
|
1074
1238
|
const changes = [];
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1239
|
+
function addChange(change) {
|
|
1240
|
+
changes.push(change);
|
|
1241
|
+
}
|
|
1242
|
+
changesInSchema(oldSchema, newSchema, addChange);
|
|
1243
|
+
compareLists(Object.values(oldSchema.getTypeMap()).filter(t => !isPrimitive(t)), Object.values(newSchema.getTypeMap()).filter(t => !isPrimitive(t)), {
|
|
1244
|
+
onAdded(type) {
|
|
1245
|
+
addChange(typeAdded(type));
|
|
1246
|
+
},
|
|
1247
|
+
onRemoved(type) {
|
|
1248
|
+
addChange(typeRemoved(type));
|
|
1249
|
+
},
|
|
1250
|
+
onMutual(type) {
|
|
1251
|
+
changesInType(type.oldVersion, type.newVersion, addChange);
|
|
1252
|
+
},
|
|
1088
1253
|
});
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1254
|
+
compareLists(oldSchema.getDirectives(), newSchema.getDirectives(), {
|
|
1255
|
+
onAdded(directive) {
|
|
1256
|
+
addChange(directiveAdded(directive));
|
|
1257
|
+
},
|
|
1258
|
+
onRemoved(directive) {
|
|
1259
|
+
addChange(directiveRemoved(directive));
|
|
1260
|
+
},
|
|
1261
|
+
onMutual(directive) {
|
|
1262
|
+
changesInDirective(directive.oldVersion, directive.newVersion, addChange);
|
|
1263
|
+
},
|
|
1092
1264
|
});
|
|
1093
1265
|
return changes;
|
|
1094
1266
|
}
|
|
1095
|
-
function
|
|
1096
|
-
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
const removed = diffArrays(oldTypenames, newTypenames).map(name => oldTypeMap[name]);
|
|
1102
|
-
const common = unionArrays(oldTypenames, newTypenames).map(name => ({
|
|
1103
|
-
inOld: oldTypeMap[name],
|
|
1104
|
-
inNew: newTypeMap[name],
|
|
1105
|
-
}));
|
|
1106
|
-
return {
|
|
1107
|
-
added,
|
|
1108
|
-
removed,
|
|
1109
|
-
common,
|
|
1110
|
-
};
|
|
1111
|
-
}
|
|
1112
|
-
function diffDirectives(oldSchema, newSchema) {
|
|
1113
|
-
const oldDirectives = oldSchema.getDirectives();
|
|
1114
|
-
const newDirectives = newSchema.getDirectives();
|
|
1115
|
-
const oldNames = oldDirectives.map(d => d.name);
|
|
1116
|
-
const newNames = newDirectives.map(d => d.name);
|
|
1117
|
-
const added = diffArrays(newNames, oldNames).map(name => newDirectives.find(d => d.name === name));
|
|
1118
|
-
const removed = diffArrays(oldNames, newNames).map(name => oldDirectives.find(d => d.name === name));
|
|
1119
|
-
const common = unionArrays(oldNames, newNames).map(name => ({
|
|
1120
|
-
inOld: oldDirectives.find(d => d.name === name),
|
|
1121
|
-
inNew: newDirectives.find(d => d.name === name),
|
|
1122
|
-
}));
|
|
1123
|
-
return {
|
|
1124
|
-
added,
|
|
1125
|
-
removed,
|
|
1126
|
-
common,
|
|
1267
|
+
function changesInSchema(oldSchema, newSchema, addChange) {
|
|
1268
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1269
|
+
const defaultNames = {
|
|
1270
|
+
query: 'Query',
|
|
1271
|
+
mutation: 'Mutation',
|
|
1272
|
+
subscription: 'Subscription',
|
|
1127
1273
|
};
|
|
1128
|
-
}
|
|
1129
|
-
function changesInSchema(oldSchema, newSchema) {
|
|
1130
|
-
const changes = [];
|
|
1131
1274
|
const oldRoot = {
|
|
1132
|
-
query: (oldSchema.getQueryType() || {}).name,
|
|
1133
|
-
mutation: (oldSchema.getMutationType() || {}).name,
|
|
1134
|
-
subscription: (oldSchema.getSubscriptionType() || {})
|
|
1135
|
-
.name,
|
|
1275
|
+
query: (_a = (oldSchema.getQueryType() || {}).name) !== null && _a !== void 0 ? _a : defaultNames.query,
|
|
1276
|
+
mutation: (_b = (oldSchema.getMutationType() || {}).name) !== null && _b !== void 0 ? _b : defaultNames.mutation,
|
|
1277
|
+
subscription: (_c = (oldSchema.getSubscriptionType() || {}).name) !== null && _c !== void 0 ? _c : defaultNames.subscription,
|
|
1136
1278
|
};
|
|
1137
1279
|
const newRoot = {
|
|
1138
|
-
query: (newSchema.getQueryType() || {}).name,
|
|
1139
|
-
mutation: (newSchema.getMutationType() || {}).name,
|
|
1140
|
-
subscription: (newSchema.getSubscriptionType() || {})
|
|
1141
|
-
.name,
|
|
1280
|
+
query: (_d = (newSchema.getQueryType() || {}).name) !== null && _d !== void 0 ? _d : defaultNames.query,
|
|
1281
|
+
mutation: (_e = (newSchema.getMutationType() || {}).name) !== null && _e !== void 0 ? _e : defaultNames.mutation,
|
|
1282
|
+
subscription: (_f = (newSchema.getSubscriptionType() || {}).name) !== null && _f !== void 0 ? _f : defaultNames.subscription,
|
|
1142
1283
|
};
|
|
1143
1284
|
if (isNotEqual(oldRoot.query, newRoot.query)) {
|
|
1144
|
-
|
|
1285
|
+
addChange(schemaQueryTypeChanged(oldSchema, newSchema));
|
|
1145
1286
|
}
|
|
1146
1287
|
if (isNotEqual(oldRoot.mutation, newRoot.mutation)) {
|
|
1147
|
-
|
|
1288
|
+
addChange(schemaMutationTypeChanged(oldSchema, newSchema));
|
|
1148
1289
|
}
|
|
1149
1290
|
if (isNotEqual(oldRoot.subscription, newRoot.subscription)) {
|
|
1150
|
-
|
|
1291
|
+
addChange(schemaSubscriptionTypeChanged(oldSchema, newSchema));
|
|
1151
1292
|
}
|
|
1152
|
-
return changes;
|
|
1153
1293
|
}
|
|
1154
|
-
function changesInType(oldType, newType) {
|
|
1155
|
-
let changes = [];
|
|
1294
|
+
function changesInType(oldType, newType, addChange) {
|
|
1156
1295
|
if (graphql.isEnumType(oldType) && graphql.isEnumType(newType)) {
|
|
1157
|
-
|
|
1296
|
+
changesInEnum(oldType, newType, addChange);
|
|
1158
1297
|
}
|
|
1159
1298
|
else if (graphql.isUnionType(oldType) && graphql.isUnionType(newType)) {
|
|
1160
|
-
|
|
1299
|
+
changesInUnion(oldType, newType, addChange);
|
|
1161
1300
|
}
|
|
1162
1301
|
else if (graphql.isInputObjectType(oldType) && graphql.isInputObjectType(newType)) {
|
|
1163
|
-
|
|
1302
|
+
changesInInputObject(oldType, newType, addChange);
|
|
1164
1303
|
}
|
|
1165
1304
|
else if (graphql.isObjectType(oldType) && graphql.isObjectType(newType)) {
|
|
1166
|
-
|
|
1305
|
+
changesInObject(oldType, newType, addChange);
|
|
1167
1306
|
}
|
|
1168
1307
|
else if (graphql.isInterfaceType(oldType) && graphql.isInterfaceType(newType)) {
|
|
1169
|
-
|
|
1308
|
+
changesInInterface(oldType, newType, addChange);
|
|
1170
1309
|
}
|
|
1171
1310
|
else if (graphql.isScalarType(oldType) && graphql.isScalarType(newType)) ;
|
|
1172
1311
|
else {
|
|
1173
|
-
|
|
1312
|
+
addChange(typeKindChanged(oldType, newType));
|
|
1174
1313
|
}
|
|
1175
1314
|
if (isNotEqual(oldType.description, newType.description)) {
|
|
1176
1315
|
if (isVoid(oldType.description)) {
|
|
1177
|
-
|
|
1316
|
+
addChange(typeDescriptionAdded(newType));
|
|
1178
1317
|
}
|
|
1179
1318
|
else if (isVoid(newType.description)) {
|
|
1180
|
-
|
|
1319
|
+
addChange(typeDescriptionRemoved(oldType));
|
|
1181
1320
|
}
|
|
1182
1321
|
else {
|
|
1183
|
-
|
|
1322
|
+
addChange(typeDescriptionChanged(oldType, newType));
|
|
1184
1323
|
}
|
|
1185
1324
|
}
|
|
1186
|
-
return changes;
|
|
1187
1325
|
}
|
|
1188
1326
|
|
|
1327
|
+
const dangerousBreaking = ({ changes }) => {
|
|
1328
|
+
return changes.map(change => {
|
|
1329
|
+
if (change.criticality.level === exports.CriticalityLevel.Dangerous) {
|
|
1330
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Breaking }) });
|
|
1331
|
+
}
|
|
1332
|
+
return change;
|
|
1333
|
+
});
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1189
1336
|
function parsePath(path) {
|
|
1190
1337
|
return path.split('.');
|
|
1191
1338
|
}
|
|
1192
1339
|
|
|
1193
|
-
const suppressRemovalOfDeprecatedField = ({ changes, oldSchema
|
|
1340
|
+
const suppressRemovalOfDeprecatedField = ({ changes, oldSchema }) => {
|
|
1194
1341
|
return changes.map(change => {
|
|
1195
1342
|
if (change.type === exports.ChangeType.FieldRemoved &&
|
|
1196
1343
|
change.criticality.level === exports.CriticalityLevel.Breaking &&
|
|
@@ -1199,7 +1346,31 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
|
1199
1346
|
const type = oldSchema.getType(typeName);
|
|
1200
1347
|
if (graphql.isObjectType(type) || graphql.isInterfaceType(type)) {
|
|
1201
1348
|
const field = type.getFields()[fieldName];
|
|
1202
|
-
if (field
|
|
1349
|
+
if (isDeprecated(field)) {
|
|
1350
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
if (change.type === exports.ChangeType.EnumValueRemoved &&
|
|
1355
|
+
change.criticality.level === exports.CriticalityLevel.Breaking &&
|
|
1356
|
+
change.path) {
|
|
1357
|
+
const [enumName, enumItem] = parsePath(change.path);
|
|
1358
|
+
const type = oldSchema.getType(enumName);
|
|
1359
|
+
if (graphql.isEnumType(type)) {
|
|
1360
|
+
const item = type.getValue(enumItem);
|
|
1361
|
+
if (item && isDeprecated(item)) {
|
|
1362
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (change.type === exports.ChangeType.InputFieldRemoved &&
|
|
1367
|
+
change.criticality.level === exports.CriticalityLevel.Breaking &&
|
|
1368
|
+
change.path) {
|
|
1369
|
+
const [inputName, inputItem] = parsePath(change.path);
|
|
1370
|
+
const type = oldSchema.getType(inputName);
|
|
1371
|
+
if (graphql.isInputObjectType(type)) {
|
|
1372
|
+
const item = type.getFields()[inputItem];
|
|
1373
|
+
if (item && isDeprecated(item)) {
|
|
1203
1374
|
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1204
1375
|
}
|
|
1205
1376
|
}
|
|
@@ -1225,20 +1396,73 @@ const ignoreDescriptionChanges = ({ changes }) => {
|
|
|
1225
1396
|
return changes.filter(change => descriptionChangeTypes.indexOf(change.type) === -1);
|
|
1226
1397
|
};
|
|
1227
1398
|
|
|
1399
|
+
const considerUsage = ({ changes, config }) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
1400
|
+
if (!config) {
|
|
1401
|
+
throw new Error(`considerUsage rule is missing config`);
|
|
1402
|
+
}
|
|
1403
|
+
const collectedBreakingField = [];
|
|
1404
|
+
changes.forEach(change => {
|
|
1405
|
+
if (change.criticality.level === exports.CriticalityLevel.Breaking && change.path) {
|
|
1406
|
+
const [typeName, fieldName, argumentName] = parsePath(change.path);
|
|
1407
|
+
collectedBreakingField.push({
|
|
1408
|
+
type: typeName,
|
|
1409
|
+
field: fieldName,
|
|
1410
|
+
argument: argumentName,
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
});
|
|
1414
|
+
// True if safe to break, false otherwise
|
|
1415
|
+
const usageList = yield config.checkUsage(collectedBreakingField);
|
|
1416
|
+
// turns an array of booleans into an array of `Type.Field` strings
|
|
1417
|
+
// includes only those that are safe to break the api
|
|
1418
|
+
const suppressedPaths = collectedBreakingField
|
|
1419
|
+
.filter((_, i) => usageList[i] === true)
|
|
1420
|
+
.map(({ type, field, argument }) => [type, field, argument].filter(Boolean).join('.'));
|
|
1421
|
+
return changes.map(change => {
|
|
1422
|
+
// Turns those "safe to break" changes into "dangerous"
|
|
1423
|
+
if (change.criticality.level === exports.CriticalityLevel.Breaking &&
|
|
1424
|
+
change.path &&
|
|
1425
|
+
suppressedPaths.some(p => change.path.startsWith(p))) {
|
|
1426
|
+
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)` });
|
|
1427
|
+
}
|
|
1428
|
+
return change;
|
|
1429
|
+
});
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
const safeUnreachable = ({ changes, oldSchema }) => {
|
|
1433
|
+
const reachable = getReachableTypes(oldSchema);
|
|
1434
|
+
return changes.map(change => {
|
|
1435
|
+
if (change.criticality.level === exports.CriticalityLevel.Breaking && change.path) {
|
|
1436
|
+
const [typeName] = parsePath(change.path);
|
|
1437
|
+
if (!reachable.has(typeName)) {
|
|
1438
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.NonBreaking }), message: 'Unreachable from root' });
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
return change;
|
|
1442
|
+
});
|
|
1443
|
+
};
|
|
1444
|
+
|
|
1228
1445
|
const rules = /*#__PURE__*/Object.freeze({
|
|
1229
1446
|
__proto__: null,
|
|
1447
|
+
dangerousBreaking: dangerousBreaking,
|
|
1230
1448
|
suppressRemovalOfDeprecatedField: suppressRemovalOfDeprecatedField,
|
|
1231
|
-
ignoreDescriptionChanges: ignoreDescriptionChanges
|
|
1449
|
+
ignoreDescriptionChanges: ignoreDescriptionChanges,
|
|
1450
|
+
considerUsage: considerUsage,
|
|
1451
|
+
safeUnreachable: safeUnreachable
|
|
1232
1452
|
});
|
|
1233
1453
|
|
|
1234
1454
|
const DiffRule = rules;
|
|
1235
|
-
function diff(oldSchema, newSchema, rules = []) {
|
|
1455
|
+
function diff(oldSchema, newSchema, rules = [], config) {
|
|
1236
1456
|
const changes = diffSchema(oldSchema, newSchema);
|
|
1237
|
-
return rules.reduce((prev, rule) =>
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1457
|
+
return rules.reduce((prev, rule) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1458
|
+
const prevChanges = yield prev;
|
|
1459
|
+
return rule({
|
|
1460
|
+
changes: prevChanges,
|
|
1461
|
+
oldSchema,
|
|
1462
|
+
newSchema,
|
|
1463
|
+
config,
|
|
1464
|
+
});
|
|
1465
|
+
}), Promise.resolve(changes));
|
|
1242
1466
|
}
|
|
1243
1467
|
|
|
1244
1468
|
function readDocument(source) {
|
|
@@ -1357,13 +1581,26 @@ function calculateDepth({ node, currentDepth, maxDepth, getFragment, }) {
|
|
|
1357
1581
|
}
|
|
1358
1582
|
}
|
|
1359
1583
|
}
|
|
1584
|
+
function countDepth(node, parentDepth, getFragmentReference) {
|
|
1585
|
+
let depth = parentDepth;
|
|
1586
|
+
if ('selectionSet' in node && node.selectionSet) {
|
|
1587
|
+
for (let child of node.selectionSet.selections) {
|
|
1588
|
+
depth = Math.max(depth, countDepth(child, parentDepth + 1, getFragmentReference));
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
if (node.kind == graphql.Kind.FRAGMENT_SPREAD) {
|
|
1592
|
+
const fragment = getFragmentReference(node.name.value);
|
|
1593
|
+
if (fragment) {
|
|
1594
|
+
depth = Math.max(depth, countDepth(fragment, parentDepth + 1, getFragmentReference));
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
return depth;
|
|
1598
|
+
}
|
|
1360
1599
|
|
|
1361
1600
|
function transformDocumentWithApollo(doc, { keepClientFields }) {
|
|
1362
1601
|
return graphql.visit(doc, {
|
|
1363
1602
|
Field(node) {
|
|
1364
|
-
return keepClientFields
|
|
1365
|
-
? removeDirectives(node, ['client'])
|
|
1366
|
-
: removeFieldIfDirectives(node, ['client']);
|
|
1603
|
+
return keepClientFields ? removeDirectives(node, ['client']) : removeFieldIfDirectives(node, ['client']);
|
|
1367
1604
|
},
|
|
1368
1605
|
});
|
|
1369
1606
|
}
|
|
@@ -1373,6 +1610,68 @@ function transformSchemaWithApollo(schema) {
|
|
|
1373
1610
|
`));
|
|
1374
1611
|
}
|
|
1375
1612
|
|
|
1613
|
+
function validateAliasCount({ source, doc, maxAliasCount, fragmentGraph, }) {
|
|
1614
|
+
const getFragmentByFragmentName = (fragmentName) => fragmentGraph.getNodeData(fragmentName);
|
|
1615
|
+
for (const definition of doc.definitions) {
|
|
1616
|
+
if (definition.kind !== graphql.Kind.OPERATION_DEFINITION) {
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
const aliasCount = countAliases(definition, getFragmentByFragmentName);
|
|
1620
|
+
if (aliasCount > maxAliasCount) {
|
|
1621
|
+
return new graphql.GraphQLError(`Too many aliases (${aliasCount}). Maximum allowed is ${maxAliasCount}`, [definition], source, definition.loc && definition.loc.start ? [definition.loc.start] : undefined);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
function countAliases(node, getFragmentByName) {
|
|
1626
|
+
let aliases = 0;
|
|
1627
|
+
if ('alias' in node && node.alias) {
|
|
1628
|
+
++aliases;
|
|
1629
|
+
}
|
|
1630
|
+
if ('selectionSet' in node && node.selectionSet) {
|
|
1631
|
+
for (let child of node.selectionSet.selections) {
|
|
1632
|
+
aliases += countAliases(child, getFragmentByName);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
else if (node.kind === graphql.Kind.FRAGMENT_SPREAD) {
|
|
1636
|
+
const fragmentNode = getFragmentByName(node.name.value);
|
|
1637
|
+
if (fragmentNode) {
|
|
1638
|
+
aliases += countAliases(fragmentNode, getFragmentByName);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
return aliases;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
function validateDirectiveCount({ source, doc, maxDirectiveCount, fragmentGraph, }) {
|
|
1645
|
+
const getFragmentByFragmentName = (fragmentName) => fragmentGraph.getNodeData(fragmentName);
|
|
1646
|
+
for (const definition of doc.definitions) {
|
|
1647
|
+
if (definition.kind !== graphql.Kind.OPERATION_DEFINITION) {
|
|
1648
|
+
continue;
|
|
1649
|
+
}
|
|
1650
|
+
const directiveCount = countDirectives(definition, getFragmentByFragmentName);
|
|
1651
|
+
if (directiveCount > maxDirectiveCount) {
|
|
1652
|
+
return new graphql.GraphQLError(`Too many directives (${directiveCount}). Maximum allowed is ${maxDirectiveCount}`, [definition], source, definition.loc && definition.loc.start ? [definition.loc.start] : undefined);
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
function countDirectives(node, getFragmentByName) {
|
|
1657
|
+
let directives = 0;
|
|
1658
|
+
if (node.directives) {
|
|
1659
|
+
directives += node.directives.length;
|
|
1660
|
+
}
|
|
1661
|
+
if ('selectionSet' in node && node.selectionSet) {
|
|
1662
|
+
for (let child of node.selectionSet.selections) {
|
|
1663
|
+
directives += countDirectives(child, getFragmentByName);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
if (node.kind == graphql.Kind.FRAGMENT_SPREAD) {
|
|
1667
|
+
const fragment = getFragmentByName(node.name.value);
|
|
1668
|
+
if (fragment) {
|
|
1669
|
+
directives += countDirectives(fragment, getFragmentByName);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
return directives;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1376
1675
|
function validate(schema, sources, options) {
|
|
1377
1676
|
const config = Object.assign({ strictDeprecated: true, strictFragments: true, keepClientFields: false, apollo: false }, options);
|
|
1378
1677
|
const invalidDocuments = [];
|
|
@@ -1402,7 +1701,7 @@ function validate(schema, sources, options) {
|
|
|
1402
1701
|
.filter(doc => doc.hasOperations)
|
|
1403
1702
|
.forEach(doc => {
|
|
1404
1703
|
const docWithOperations = {
|
|
1405
|
-
kind:
|
|
1704
|
+
kind: graphql.Kind.DOCUMENT,
|
|
1406
1705
|
definitions: doc.operations.map(d => d.node),
|
|
1407
1706
|
};
|
|
1408
1707
|
const extractedFragments = (extractFragments(graphql.print(docWithOperations)) || [])
|
|
@@ -1413,12 +1712,10 @@ function validate(schema, sources, options) {
|
|
|
1413
1712
|
// remove duplicates
|
|
1414
1713
|
.filter((def, i, all) => all.findIndex(item => item.name.value === def.name.value) === i);
|
|
1415
1714
|
const merged = {
|
|
1416
|
-
kind:
|
|
1715
|
+
kind: graphql.Kind.DOCUMENT,
|
|
1417
1716
|
definitions: [...docWithOperations.definitions, ...extractedFragments],
|
|
1418
1717
|
};
|
|
1419
|
-
|
|
1420
|
-
? transformSchemaWithApollo(schema)
|
|
1421
|
-
: schema;
|
|
1718
|
+
let transformedSchema = config.apollo ? transformSchemaWithApollo(schema) : schema;
|
|
1422
1719
|
const transformedDoc = config.apollo
|
|
1423
1720
|
? transformDocumentWithApollo(merged, {
|
|
1424
1721
|
keepClientFields: config.keepClientFields,
|
|
@@ -1436,12 +1733,30 @@ function validate(schema, sources, options) {
|
|
|
1436
1733
|
errors.push(depthError);
|
|
1437
1734
|
}
|
|
1438
1735
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1736
|
+
if (config.maxAliasCount) {
|
|
1737
|
+
const aliasError = validateAliasCount({
|
|
1738
|
+
source: doc.source,
|
|
1739
|
+
doc: transformedDoc,
|
|
1740
|
+
maxAliasCount: config.maxAliasCount,
|
|
1741
|
+
fragmentGraph: graph,
|
|
1742
|
+
});
|
|
1743
|
+
if (aliasError) {
|
|
1744
|
+
errors.push(aliasError);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
if (config.maxDirectiveCount) {
|
|
1748
|
+
const directiveError = validateDirectiveCount({
|
|
1749
|
+
source: doc.source,
|
|
1750
|
+
doc: transformedDoc,
|
|
1751
|
+
maxDirectiveCount: config.maxDirectiveCount,
|
|
1752
|
+
fragmentGraph: graph,
|
|
1753
|
+
});
|
|
1754
|
+
if (directiveError) {
|
|
1755
|
+
errors.push(directiveError);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
const deprecated = config.strictDeprecated ? findDeprecatedUsages(transformedSchema, transformedDoc) : [];
|
|
1759
|
+
const duplicatedFragments = config.strictFragments ? findDuplicatedFragments(fragmentNames) : [];
|
|
1445
1760
|
if (sumLengths(errors, duplicatedFragments, deprecated) > 0) {
|
|
1446
1761
|
invalidDocuments.push({
|
|
1447
1762
|
source: doc.source,
|
|
@@ -1464,10 +1779,7 @@ function findDuplicatedFragments(fragmentNames) {
|
|
|
1464
1779
|
function resolveFragment(fragment, graph) {
|
|
1465
1780
|
return graph
|
|
1466
1781
|
.dependenciesOf(fragment.name.value)
|
|
1467
|
-
.reduce((list, current) => [
|
|
1468
|
-
...list,
|
|
1469
|
-
...resolveFragment(graph.getNodeData(current), graph),
|
|
1470
|
-
], [fragment]);
|
|
1782
|
+
.reduce((list, current) => [...list, ...resolveFragment(graph.getNodeData(current), graph)], [fragment]);
|
|
1471
1783
|
}
|
|
1472
1784
|
function extractFragments(document) {
|
|
1473
1785
|
return (document.match(/[\.]{3}[a-z0-9\_]+\b/gi) || []).map(name => name.replace('...', ''));
|
|
@@ -1476,70 +1788,6 @@ function sumLengths(...arrays) {
|
|
|
1476
1788
|
return arrays.reduce((sum, { length }) => sum + length, 0);
|
|
1477
1789
|
}
|
|
1478
1790
|
|
|
1479
|
-
function compareTwoStrings(str1, str2) {
|
|
1480
|
-
if (!str1.length && !str2.length)
|
|
1481
|
-
return 1;
|
|
1482
|
-
if (!str1.length || !str2.length)
|
|
1483
|
-
return 0;
|
|
1484
|
-
if (str1.toUpperCase() === str2.toUpperCase())
|
|
1485
|
-
return 1;
|
|
1486
|
-
if (str1.length === 1 && str2.length === 1)
|
|
1487
|
-
return 0;
|
|
1488
|
-
const pairs1 = wordLetterPairs(str1);
|
|
1489
|
-
const pairs2 = wordLetterPairs(str2);
|
|
1490
|
-
const union = pairs1.length + pairs2.length;
|
|
1491
|
-
let intersection = 0;
|
|
1492
|
-
pairs1.forEach(pair1 => {
|
|
1493
|
-
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
1494
|
-
if (pair1 !== pair2)
|
|
1495
|
-
continue;
|
|
1496
|
-
intersection++;
|
|
1497
|
-
pairs2.splice(i, 1);
|
|
1498
|
-
break;
|
|
1499
|
-
}
|
|
1500
|
-
});
|
|
1501
|
-
return (intersection * 2) / union;
|
|
1502
|
-
}
|
|
1503
|
-
function findBestMatch(mainString, targetStrings) {
|
|
1504
|
-
if (!areArgsValid(mainString, targetStrings))
|
|
1505
|
-
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
1506
|
-
const ratings = targetStrings.map(target => ({
|
|
1507
|
-
target,
|
|
1508
|
-
rating: compareTwoStrings(mainString, target.value),
|
|
1509
|
-
}));
|
|
1510
|
-
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
1511
|
-
return { ratings, bestMatch };
|
|
1512
|
-
}
|
|
1513
|
-
function flattenDeep(arr) {
|
|
1514
|
-
return Array.isArray(arr)
|
|
1515
|
-
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
1516
|
-
: [arr];
|
|
1517
|
-
}
|
|
1518
|
-
function areArgsValid(mainString, targetStrings) {
|
|
1519
|
-
if (typeof mainString !== 'string')
|
|
1520
|
-
return false;
|
|
1521
|
-
if (!Array.isArray(targetStrings))
|
|
1522
|
-
return false;
|
|
1523
|
-
if (!targetStrings.length)
|
|
1524
|
-
return false;
|
|
1525
|
-
if (targetStrings.find(s => typeof s.value !== 'string'))
|
|
1526
|
-
return false;
|
|
1527
|
-
return true;
|
|
1528
|
-
}
|
|
1529
|
-
function letterPairs(str) {
|
|
1530
|
-
const pairs = [];
|
|
1531
|
-
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
1532
|
-
pairs[i] = str.substring(i, i + 2);
|
|
1533
|
-
return pairs;
|
|
1534
|
-
}
|
|
1535
|
-
function wordLetterPairs(str) {
|
|
1536
|
-
const pairs = str
|
|
1537
|
-
.toUpperCase()
|
|
1538
|
-
.split(' ')
|
|
1539
|
-
.map(letterPairs);
|
|
1540
|
-
return flattenDeep(pairs);
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
1791
|
function similar(schema, typeName, threshold = 0.4) {
|
|
1544
1792
|
const typeMap = schema.getTypeMap();
|
|
1545
1793
|
const targets = Object.keys(schema.getTypeMap())
|
|
@@ -1549,14 +1797,13 @@ function similar(schema, typeName, threshold = 0.4) {
|
|
|
1549
1797
|
value: stripType(typeMap[name]),
|
|
1550
1798
|
}));
|
|
1551
1799
|
const results = {};
|
|
1552
|
-
if (typeof typeName !== 'undefined' &&
|
|
1553
|
-
!targets.some(t => t.typeId === typeName)) {
|
|
1800
|
+
if (typeof typeName !== 'undefined' && !targets.some(t => t.typeId === typeName)) {
|
|
1554
1801
|
throw new Error(`Type '${typeName}' doesn't exist`);
|
|
1555
1802
|
}
|
|
1556
1803
|
(typeName ? [{ typeId: typeName, value: '' }] : targets).forEach(source => {
|
|
1557
1804
|
const sourceType = schema.getType(source.typeId);
|
|
1558
|
-
const matchWith = targets.filter(target => schema.getType(target.typeId).astNode.kind ===
|
|
1559
|
-
|
|
1805
|
+
const matchWith = targets.filter(target => schema.getType(target.typeId).astNode.kind === sourceType.astNode.kind &&
|
|
1806
|
+
target.typeId !== source.typeId);
|
|
1560
1807
|
if (matchWith.length > 0) {
|
|
1561
1808
|
const found = similarTo(sourceType, matchWith, threshold);
|
|
1562
1809
|
if (found) {
|
|
@@ -1610,10 +1857,24 @@ function coverage(schema, sources) {
|
|
|
1610
1857
|
fieldDef.name &&
|
|
1611
1858
|
fieldDef.name !== '__typename' &&
|
|
1612
1859
|
fieldDef.name !== '__schema') {
|
|
1613
|
-
const
|
|
1614
|
-
coverage.types[parent.name]
|
|
1615
|
-
|
|
1616
|
-
|
|
1860
|
+
const sourceName = source.name;
|
|
1861
|
+
const typeCoverage = coverage.types[parent.name];
|
|
1862
|
+
const fieldCoverage = typeCoverage.children[fieldDef.name];
|
|
1863
|
+
const locations = fieldCoverage.locations[sourceName];
|
|
1864
|
+
typeCoverage.hits++;
|
|
1865
|
+
fieldCoverage.hits++;
|
|
1866
|
+
if (node.loc) {
|
|
1867
|
+
fieldCoverage.locations[sourceName] = [node.loc, ...(locations || [])];
|
|
1868
|
+
}
|
|
1869
|
+
if (node.arguments) {
|
|
1870
|
+
for (const argNode of node.arguments) {
|
|
1871
|
+
const argCoverage = fieldCoverage.children[argNode.name.value];
|
|
1872
|
+
argCoverage.hits++;
|
|
1873
|
+
if (argNode.loc) {
|
|
1874
|
+
argCoverage.locations[sourceName] = [argNode.loc, ...(argCoverage.locations[sourceName] || [])];
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1617
1878
|
}
|
|
1618
1879
|
},
|
|
1619
1880
|
});
|
|
@@ -1632,7 +1893,14 @@ function coverage(schema, sources) {
|
|
|
1632
1893
|
typeCoverage.children[field.name] = {
|
|
1633
1894
|
hits: 0,
|
|
1634
1895
|
locations: {},
|
|
1896
|
+
children: {},
|
|
1635
1897
|
};
|
|
1898
|
+
for (const arg of field.args) {
|
|
1899
|
+
typeCoverage.children[field.name].children[arg.name] = {
|
|
1900
|
+
hits: 0,
|
|
1901
|
+
locations: {},
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1636
1904
|
}
|
|
1637
1905
|
coverage.types[type.name] = typeCoverage;
|
|
1638
1906
|
}
|
|
@@ -1652,9 +1920,11 @@ function coverage(schema, sources) {
|
|
|
1652
1920
|
}
|
|
1653
1921
|
|
|
1654
1922
|
exports.DiffRule = DiffRule;
|
|
1923
|
+
exports.countAliases = countAliases;
|
|
1924
|
+
exports.countDepth = countDepth;
|
|
1925
|
+
exports.countDirectives = countDirectives;
|
|
1655
1926
|
exports.coverage = coverage;
|
|
1656
1927
|
exports.diff = diff;
|
|
1657
1928
|
exports.getTypePrefix = getTypePrefix;
|
|
1658
1929
|
exports.similar = similar;
|
|
1659
1930
|
exports.validate = validate;
|
|
1660
|
-
//# sourceMappingURL=index.cjs.js.map
|