@graphql-inspector/core 0.0.0-canary.da4bdcc → 0.0.0-canary.e88122e
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 +6 -6
- 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 +1 -1
- package/{index.cjs.js → index.js} +604 -409
- package/{index.esm.js → index.mjs} +603 -410
- 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/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) {
|
|
@@ -55,7 +129,7 @@ function safeChangeForInputValue(oldType, newType) {
|
|
|
55
129
|
return safeChangeForInputValue(oldType.ofType, newType.ofType);
|
|
56
130
|
}
|
|
57
131
|
if (graphql.isNonNullType(oldType)) {
|
|
58
|
-
const ofType = graphql.isNonNullType(newType) ? newType : newType;
|
|
132
|
+
const ofType = graphql.isNonNullType(newType) ? newType.ofType : newType;
|
|
59
133
|
return safeChangeForInputValue(oldType.ofType, ofType);
|
|
60
134
|
}
|
|
61
135
|
return false;
|
|
@@ -74,7 +148,7 @@ function getTypePrefix(type) {
|
|
|
74
148
|
[graphql.Kind.ENUM_TYPE_DEFINITION]: 'enum',
|
|
75
149
|
[graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
|
|
76
150
|
};
|
|
77
|
-
return kindsMap[kind];
|
|
151
|
+
return kindsMap[kind.toString()];
|
|
78
152
|
}
|
|
79
153
|
function isPrimitive(type) {
|
|
80
154
|
return (['String', 'Int', 'Float', 'Boolean', 'ID'].indexOf(typeof type === 'string' ? type : type.name) !== -1);
|
|
@@ -95,9 +169,21 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
95
169
|
const errors = [];
|
|
96
170
|
const typeInfo = new graphql.TypeInfo(schema);
|
|
97
171
|
graphql.visit(ast, graphql.visitWithTypeInfo(typeInfo, {
|
|
172
|
+
Argument(node) {
|
|
173
|
+
const argument = typeInfo.getArgument();
|
|
174
|
+
if (argument) {
|
|
175
|
+
const reason = argument.deprecationReason;
|
|
176
|
+
if (reason) {
|
|
177
|
+
const fieldDef = typeInfo.getFieldDef();
|
|
178
|
+
if (fieldDef) {
|
|
179
|
+
errors.push(new graphql.GraphQLError(`The argument '${argument === null || argument === void 0 ? void 0 : argument.name}' of '${fieldDef.name}' is deprecated. ${reason}`, [node]));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
98
184
|
Field(node) {
|
|
99
185
|
const fieldDef = typeInfo.getFieldDef();
|
|
100
|
-
if (fieldDef && fieldDef
|
|
186
|
+
if (fieldDef && isDeprecated(fieldDef)) {
|
|
101
187
|
const parentType = typeInfo.getParentType();
|
|
102
188
|
if (parentType) {
|
|
103
189
|
const reason = fieldDef.deprecationReason;
|
|
@@ -107,7 +193,7 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
107
193
|
},
|
|
108
194
|
EnumValue(node) {
|
|
109
195
|
const enumVal = typeInfo.getEnumValue();
|
|
110
|
-
if (enumVal && enumVal
|
|
196
|
+
if (enumVal && isDeprecated(enumVal)) {
|
|
111
197
|
const type = graphql.getNamedType(typeInfo.getInputType());
|
|
112
198
|
if (type) {
|
|
113
199
|
const reason = enumVal.deprecationReason;
|
|
@@ -120,7 +206,7 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
120
206
|
}
|
|
121
207
|
function removeFieldIfDirectives(node, directiveNames) {
|
|
122
208
|
if (node.directives) {
|
|
123
|
-
if (node.directives.some(d => directiveNames.indexOf(d.name.value) !== -1)) {
|
|
209
|
+
if (node.directives.some((d) => directiveNames.indexOf(d.name.value) !== -1)) {
|
|
124
210
|
return null;
|
|
125
211
|
}
|
|
126
212
|
}
|
|
@@ -128,10 +214,79 @@ function removeFieldIfDirectives(node, directiveNames) {
|
|
|
128
214
|
}
|
|
129
215
|
function removeDirectives(node, directiveNames) {
|
|
130
216
|
if (node.directives) {
|
|
131
|
-
return Object.assign(Object.assign({}, node), { directives: node.directives.filter(d => directiveNames.indexOf(d.name.value) === -1) });
|
|
217
|
+
return Object.assign(Object.assign({}, node), { directives: node.directives.filter((d) => directiveNames.indexOf(d.name.value) === -1) });
|
|
132
218
|
}
|
|
133
219
|
return node;
|
|
134
220
|
}
|
|
221
|
+
function getReachableTypes(schema) {
|
|
222
|
+
const reachableTypes = new Set();
|
|
223
|
+
const collect = (type) => {
|
|
224
|
+
const typeName = type.name;
|
|
225
|
+
if (reachableTypes.has(typeName)) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
reachableTypes.add(typeName);
|
|
229
|
+
if (graphql.isScalarType(type)) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
else if (graphql.isInterfaceType(type) || graphql.isObjectType(type)) {
|
|
233
|
+
if (graphql.isInterfaceType(type)) {
|
|
234
|
+
const { objects, interfaces } = schema.getImplementations(type);
|
|
235
|
+
for (const child of objects) {
|
|
236
|
+
collect(child);
|
|
237
|
+
}
|
|
238
|
+
for (const child of interfaces) {
|
|
239
|
+
collect(child);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const fields = type.getFields();
|
|
243
|
+
for (const fieldName in fields) {
|
|
244
|
+
const field = fields[fieldName];
|
|
245
|
+
collect(resolveOutputType(field.type));
|
|
246
|
+
const args = field.args;
|
|
247
|
+
for (const argName in args) {
|
|
248
|
+
const arg = args[argName];
|
|
249
|
+
collect(resolveInputType(arg.type));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else if (graphql.isUnionType(type)) {
|
|
254
|
+
const types = type.getTypes();
|
|
255
|
+
for (const child of types) {
|
|
256
|
+
collect(child);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else if (graphql.isInputObjectType(type)) {
|
|
260
|
+
const fields = type.getFields();
|
|
261
|
+
for (const fieldName in fields) {
|
|
262
|
+
const field = fields[fieldName];
|
|
263
|
+
collect(resolveInputType(field.type));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
for (const type of [
|
|
268
|
+
schema.getQueryType(),
|
|
269
|
+
schema.getMutationType(),
|
|
270
|
+
schema.getSubscriptionType(),
|
|
271
|
+
]) {
|
|
272
|
+
if (type) {
|
|
273
|
+
collect(type);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return reachableTypes;
|
|
277
|
+
}
|
|
278
|
+
function resolveOutputType(output) {
|
|
279
|
+
if (graphql.isListType(output) || graphql.isNonNullType(output)) {
|
|
280
|
+
return resolveOutputType(output.ofType);
|
|
281
|
+
}
|
|
282
|
+
return output;
|
|
283
|
+
}
|
|
284
|
+
function resolveInputType(input) {
|
|
285
|
+
if (graphql.isListType(input) || graphql.isNonNullType(input)) {
|
|
286
|
+
return resolveInputType(input.ofType);
|
|
287
|
+
}
|
|
288
|
+
return input;
|
|
289
|
+
}
|
|
135
290
|
|
|
136
291
|
(function (ChangeType) {
|
|
137
292
|
// Argument
|
|
@@ -154,6 +309,8 @@ function removeDirectives(node, directiveNames) {
|
|
|
154
309
|
ChangeType["EnumValueAdded"] = "ENUM_VALUE_ADDED";
|
|
155
310
|
ChangeType["EnumValueDescriptionChanged"] = "ENUM_VALUE_DESCRIPTION_CHANGED";
|
|
156
311
|
ChangeType["EnumValueDeprecationReasonChanged"] = "ENUM_VALUE_DEPRECATION_REASON_CHANGED";
|
|
312
|
+
ChangeType["EnumValueDeprecationReasonAdded"] = "ENUM_VALUE_DEPRECATION_REASON_ADDED";
|
|
313
|
+
ChangeType["EnumValueDeprecationReasonRemoved"] = "ENUM_VALUE_DEPRECATION_REASON_REMOVED";
|
|
157
314
|
// Field
|
|
158
315
|
ChangeType["FieldRemoved"] = "FIELD_REMOVED";
|
|
159
316
|
ChangeType["FieldAdded"] = "FIELD_ADDED";
|
|
@@ -416,7 +573,7 @@ function enumValueRemoved(oldEnum, value) {
|
|
|
416
573
|
reason: `Removing an enum value will cause existing queries that use this enum value to error.`,
|
|
417
574
|
},
|
|
418
575
|
type: exports.ChangeType.EnumValueRemoved,
|
|
419
|
-
message: `Enum value '${value.name}' was removed from enum '${oldEnum.name}'`,
|
|
576
|
+
message: `Enum value '${value.name}' ${isDeprecated(value) ? '(deprecated) ' : ''}was removed from enum '${oldEnum.name}'`,
|
|
420
577
|
path: [oldEnum.name, value.name].join('.'),
|
|
421
578
|
};
|
|
422
579
|
}
|
|
@@ -448,35 +605,59 @@ function enumValueDeprecationReasonChanged(newEnum, oldValue, newValue) {
|
|
|
448
605
|
criticality: {
|
|
449
606
|
level: exports.CriticalityLevel.NonBreaking,
|
|
450
607
|
},
|
|
451
|
-
type: exports.ChangeType.
|
|
452
|
-
message: oldValue.deprecationReason
|
|
453
|
-
|
|
454
|
-
|
|
608
|
+
type: exports.ChangeType.EnumValueDeprecationReasonChanged,
|
|
609
|
+
message: `Enum value '${newEnum.name}.${newValue.name}' deprecation reason changed from '${oldValue.deprecationReason}' to '${newValue.deprecationReason}'`,
|
|
610
|
+
path: [newEnum.name, oldValue.name].join('.'),
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
function enumValueDeprecationReasonAdded(newEnum, oldValue, newValue) {
|
|
614
|
+
return {
|
|
615
|
+
criticality: {
|
|
616
|
+
level: exports.CriticalityLevel.NonBreaking,
|
|
617
|
+
},
|
|
618
|
+
type: exports.ChangeType.EnumValueDeprecationReasonAdded,
|
|
619
|
+
message: `Enum value '${newEnum.name}.${newValue.name}' was deprecated with reason '${newValue.deprecationReason}'`,
|
|
620
|
+
path: [newEnum.name, oldValue.name].join('.'),
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
function enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue) {
|
|
624
|
+
return {
|
|
625
|
+
criticality: {
|
|
626
|
+
level: exports.CriticalityLevel.NonBreaking,
|
|
627
|
+
},
|
|
628
|
+
type: exports.ChangeType.EnumValueDeprecationReasonRemoved,
|
|
629
|
+
message: `Deprecation reason was removed from enum value '${newEnum.name}.${newValue.name}'`,
|
|
455
630
|
path: [newEnum.name, oldValue.name].join('.'),
|
|
456
631
|
};
|
|
457
632
|
}
|
|
458
633
|
|
|
459
|
-
function changesInEnum(oldEnum, newEnum) {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
634
|
+
function changesInEnum(oldEnum, newEnum, addChange) {
|
|
635
|
+
compareLists(oldEnum.getValues(), newEnum.getValues(), {
|
|
636
|
+
onAdded(value) {
|
|
637
|
+
addChange(enumValueAdded(newEnum, value));
|
|
638
|
+
},
|
|
639
|
+
onRemoved(value) {
|
|
640
|
+
addChange(enumValueRemoved(oldEnum, value));
|
|
641
|
+
},
|
|
642
|
+
onMutual(value) {
|
|
643
|
+
const oldValue = value.oldVersion;
|
|
644
|
+
const newValue = value.newVersion;
|
|
645
|
+
if (isNotEqual(oldValue.description, newValue.description)) {
|
|
646
|
+
addChange(enumValueDescriptionChanged(newEnum, oldValue, newValue));
|
|
647
|
+
}
|
|
648
|
+
if (isNotEqual(oldValue.deprecationReason, newValue.deprecationReason)) {
|
|
649
|
+
if (isVoid(oldValue.deprecationReason)) {
|
|
650
|
+
addChange(enumValueDeprecationReasonAdded(newEnum, oldValue, newValue));
|
|
651
|
+
}
|
|
652
|
+
else if (isVoid(newValue.deprecationReason)) {
|
|
653
|
+
addChange(enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue));
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
addChange(enumValueDeprecationReasonChanged(newEnum, oldValue, newValue));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
},
|
|
478
660
|
});
|
|
479
|
-
return changes;
|
|
480
661
|
}
|
|
481
662
|
|
|
482
663
|
function unionMemberRemoved(union, type) {
|
|
@@ -502,17 +683,81 @@ function unionMemberAdded(union, type) {
|
|
|
502
683
|
};
|
|
503
684
|
}
|
|
504
685
|
|
|
505
|
-
function changesInUnion(oldUnion, newUnion) {
|
|
506
|
-
const changes = [];
|
|
686
|
+
function changesInUnion(oldUnion, newUnion, addChange) {
|
|
507
687
|
const oldTypes = oldUnion.getTypes();
|
|
508
688
|
const newTypes = newUnion.getTypes();
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
689
|
+
compareLists(oldTypes, newTypes, {
|
|
690
|
+
onAdded(t) {
|
|
691
|
+
addChange(unionMemberAdded(newUnion, t));
|
|
692
|
+
},
|
|
693
|
+
onRemoved(t) {
|
|
694
|
+
addChange(unionMemberRemoved(oldUnion, t));
|
|
695
|
+
},
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function compareTwoStrings(str1, str2) {
|
|
700
|
+
if (!str1.length && !str2.length)
|
|
701
|
+
return 1;
|
|
702
|
+
if (!str1.length || !str2.length)
|
|
703
|
+
return 0;
|
|
704
|
+
if (str1.toUpperCase() === str2.toUpperCase())
|
|
705
|
+
return 1;
|
|
706
|
+
if (str1.length === 1 && str2.length === 1)
|
|
707
|
+
return 0;
|
|
708
|
+
const pairs1 = wordLetterPairs(str1);
|
|
709
|
+
const pairs2 = wordLetterPairs(str2);
|
|
710
|
+
const union = pairs1.length + pairs2.length;
|
|
711
|
+
let intersection = 0;
|
|
712
|
+
pairs1.forEach((pair1) => {
|
|
713
|
+
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
714
|
+
if (pair1 !== pair2)
|
|
715
|
+
continue;
|
|
716
|
+
intersection++;
|
|
717
|
+
pairs2.splice(i, 1);
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
return (intersection * 2) / union;
|
|
722
|
+
}
|
|
723
|
+
function findBestMatch(mainString, targetStrings) {
|
|
724
|
+
if (!areArgsValid(mainString, targetStrings))
|
|
725
|
+
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
726
|
+
const ratings = targetStrings.map((target) => ({
|
|
727
|
+
target,
|
|
728
|
+
rating: compareTwoStrings(mainString, target.value),
|
|
729
|
+
}));
|
|
730
|
+
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
731
|
+
return { ratings, bestMatch };
|
|
732
|
+
}
|
|
733
|
+
function flattenDeep(arr) {
|
|
734
|
+
return Array.isArray(arr)
|
|
735
|
+
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
736
|
+
: [arr];
|
|
737
|
+
}
|
|
738
|
+
function areArgsValid(mainString, targetStrings) {
|
|
739
|
+
if (typeof mainString !== 'string')
|
|
740
|
+
return false;
|
|
741
|
+
if (!Array.isArray(targetStrings))
|
|
742
|
+
return false;
|
|
743
|
+
if (!targetStrings.length)
|
|
744
|
+
return false;
|
|
745
|
+
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
746
|
+
return false;
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
function letterPairs(str) {
|
|
750
|
+
const pairs = [];
|
|
751
|
+
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
752
|
+
pairs[i] = str.substring(i, i + 2);
|
|
753
|
+
return pairs;
|
|
754
|
+
}
|
|
755
|
+
function wordLetterPairs(str) {
|
|
756
|
+
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
757
|
+
return flattenDeep(pairs);
|
|
758
|
+
}
|
|
759
|
+
function safeString(obj) {
|
|
760
|
+
return inspect(obj).replace(/\[Object\: null prototype\] /g, '').replace(/(^')|('$)/g, '');
|
|
516
761
|
}
|
|
517
762
|
|
|
518
763
|
function inputFieldRemoved(input, field) {
|
|
@@ -522,7 +767,7 @@ function inputFieldRemoved(input, field) {
|
|
|
522
767
|
reason: 'Removing an input field will cause existing queries that use this input field to error.',
|
|
523
768
|
},
|
|
524
769
|
type: exports.ChangeType.InputFieldRemoved,
|
|
525
|
-
message: `Input field '${field.name}' was removed from input object type '${input.name}'`,
|
|
770
|
+
message: `Input field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from input object type '${input.name}'`,
|
|
526
771
|
path: [input.name, field.name].join('.'),
|
|
527
772
|
};
|
|
528
773
|
}
|
|
@@ -531,7 +776,7 @@ function inputFieldAdded(input, field) {
|
|
|
531
776
|
criticality: graphql.isNonNullType(field.type)
|
|
532
777
|
? {
|
|
533
778
|
level: exports.CriticalityLevel.Breaking,
|
|
534
|
-
reason: 'Adding a
|
|
779
|
+
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
780
|
}
|
|
536
781
|
: {
|
|
537
782
|
level: exports.CriticalityLevel.Dangerous,
|
|
@@ -575,10 +820,10 @@ function inputFieldDefaultValueChanged(input, oldField, newField) {
|
|
|
575
820
|
return {
|
|
576
821
|
criticality: {
|
|
577
822
|
level: exports.CriticalityLevel.Dangerous,
|
|
578
|
-
reason: 'Changing the default value for an argument may change the runtime
|
|
823
|
+
reason: 'Changing the default value for an argument may change the runtime behavior of a field if it was never provided.',
|
|
579
824
|
},
|
|
580
825
|
type: exports.ChangeType.InputFieldDefaultValueChanged,
|
|
581
|
-
message: `Input field '${input.name}.${oldField.name}' default value changed from '${oldField.defaultValue}' to '${newField.defaultValue}'`,
|
|
826
|
+
message: `Input field '${input.name}.${oldField.name}' default value changed from '${safeString(oldField.defaultValue)}' to '${safeString(newField.defaultValue)}'`,
|
|
582
827
|
path: [input.name, oldField.name].join('.'),
|
|
583
828
|
};
|
|
584
829
|
}
|
|
@@ -599,55 +844,48 @@ function inputFieldTypeChanged(input, oldField, newField) {
|
|
|
599
844
|
};
|
|
600
845
|
}
|
|
601
846
|
|
|
602
|
-
function changesInInputObject(oldInput, newInput) {
|
|
603
|
-
const changes = [];
|
|
847
|
+
function changesInInputObject(oldInput, newInput, addChange) {
|
|
604
848
|
const oldFields = oldInput.getFields();
|
|
605
849
|
const newFields = newInput.getFields();
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
850
|
+
compareLists(Object.values(oldFields), Object.values(newFields), {
|
|
851
|
+
onAdded(field) {
|
|
852
|
+
addChange(inputFieldAdded(newInput, field));
|
|
853
|
+
},
|
|
854
|
+
onRemoved(field) {
|
|
855
|
+
addChange(inputFieldRemoved(oldInput, field));
|
|
856
|
+
},
|
|
857
|
+
onMutual(field) {
|
|
858
|
+
changesInInputField(oldInput, field.oldVersion, field.newVersion, addChange);
|
|
859
|
+
},
|
|
616
860
|
});
|
|
617
|
-
changes.push(...added.map(field => inputFieldAdded(newInput, field)));
|
|
618
|
-
changes.push(...removed.map(field => inputFieldRemoved(oldInput, field)));
|
|
619
|
-
return changes;
|
|
620
861
|
}
|
|
621
|
-
function changesInInputField(input, oldField, newField) {
|
|
622
|
-
const changes = [];
|
|
862
|
+
function changesInInputField(input, oldField, newField, addChange) {
|
|
623
863
|
if (isNotEqual(oldField.description, newField.description)) {
|
|
624
864
|
if (isVoid(oldField.description)) {
|
|
625
|
-
|
|
865
|
+
addChange(inputFieldDescriptionAdded(input, newField));
|
|
626
866
|
}
|
|
627
867
|
else if (isVoid(newField.description)) {
|
|
628
|
-
|
|
868
|
+
addChange(inputFieldDescriptionRemoved(input, oldField));
|
|
629
869
|
}
|
|
630
870
|
else {
|
|
631
|
-
|
|
871
|
+
addChange(inputFieldDescriptionChanged(input, oldField, newField));
|
|
632
872
|
}
|
|
633
873
|
}
|
|
634
874
|
if (isNotEqual(oldField.defaultValue, newField.defaultValue)) {
|
|
635
875
|
if (Array.isArray(oldField.defaultValue) &&
|
|
636
876
|
Array.isArray(newField.defaultValue)) {
|
|
637
877
|
if (diffArrays(oldField.defaultValue, newField.defaultValue).length > 0) {
|
|
638
|
-
|
|
878
|
+
addChange(inputFieldDefaultValueChanged(input, oldField, newField));
|
|
639
879
|
}
|
|
640
880
|
}
|
|
641
881
|
else if (JSON.stringify(oldField.defaultValue) !==
|
|
642
882
|
JSON.stringify(newField.defaultValue)) {
|
|
643
|
-
|
|
883
|
+
addChange(inputFieldDefaultValueChanged(input, oldField, newField));
|
|
644
884
|
}
|
|
645
885
|
}
|
|
646
886
|
if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
|
|
647
|
-
|
|
887
|
+
addChange(inputFieldTypeChanged(input, oldField, newField));
|
|
648
888
|
}
|
|
649
|
-
// TODO: diff directives
|
|
650
|
-
return changes;
|
|
651
889
|
}
|
|
652
890
|
|
|
653
891
|
function objectTypeInterfaceAdded(iface, type) {
|
|
@@ -683,7 +921,7 @@ function fieldRemoved(type, field) {
|
|
|
683
921
|
: `Removing a field is a breaking change. It is preferable to deprecate the field before removing it.`,
|
|
684
922
|
},
|
|
685
923
|
type: exports.ChangeType.FieldRemoved,
|
|
686
|
-
message: `Field '${field.name}' ${field
|
|
924
|
+
message: `Field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from ${entity} '${type.name}'`,
|
|
687
925
|
path: [type.name, field.name].join('.'),
|
|
688
926
|
};
|
|
689
927
|
}
|
|
@@ -791,8 +1029,10 @@ function fieldTypeChanged(type, oldField, newField) {
|
|
|
791
1029
|
};
|
|
792
1030
|
}
|
|
793
1031
|
function fieldArgumentAdded(type, field, arg) {
|
|
1032
|
+
const isBreaking = graphql.isNonNullType(arg.type) && typeof arg.defaultValue === 'undefined';
|
|
1033
|
+
const defaultValueMsg = typeof arg.defaultValue !== 'undefined' ? ' (with default value) ' : ' ';
|
|
794
1034
|
return {
|
|
795
|
-
criticality:
|
|
1035
|
+
criticality: isBreaking
|
|
796
1036
|
? {
|
|
797
1037
|
level: exports.CriticalityLevel.Breaking,
|
|
798
1038
|
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 +1042,7 @@ function fieldArgumentAdded(type, field, arg) {
|
|
|
802
1042
|
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
1043
|
},
|
|
804
1044
|
type: exports.ChangeType.FieldArgumentAdded,
|
|
805
|
-
message: `Argument '${arg.name}: ${arg.type}'
|
|
1045
|
+
message: `Argument '${arg.name}: ${arg.type}'${defaultValueMsg}added to field '${type.name}.${field.name}'`,
|
|
806
1046
|
path: [type.name, field.name, arg.name].join('.'),
|
|
807
1047
|
};
|
|
808
1048
|
}
|
|
@@ -836,8 +1076,8 @@ function fieldArgumentDefaultChanged(type, field, oldArg, newArg) {
|
|
|
836
1076
|
},
|
|
837
1077
|
type: exports.ChangeType.FieldArgumentDefaultChanged,
|
|
838
1078
|
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}'`,
|
|
1079
|
+
? `Default value '${safeString(newArg.defaultValue)}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
1080
|
+
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${safeString(oldArg.defaultValue)}' to '${safeString(newArg.defaultValue)}'`,
|
|
841
1081
|
path: [type.name, field.name, oldArg.name].join('.'),
|
|
842
1082
|
};
|
|
843
1083
|
}
|
|
@@ -858,340 +1098,256 @@ function fieldArgumentTypeChanged(type, field, oldArg, newArg) {
|
|
|
858
1098
|
};
|
|
859
1099
|
}
|
|
860
1100
|
|
|
861
|
-
function changesInArgument(type, field, oldArg, newArg) {
|
|
862
|
-
const changes = [];
|
|
1101
|
+
function changesInArgument(type, field, oldArg, newArg, addChange) {
|
|
863
1102
|
if (isNotEqual(oldArg.description, newArg.description)) {
|
|
864
|
-
|
|
1103
|
+
addChange(fieldArgumentDescriptionChanged(type, field, oldArg, newArg));
|
|
865
1104
|
}
|
|
866
1105
|
if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
|
|
867
1106
|
if (Array.isArray(oldArg.defaultValue) &&
|
|
868
1107
|
Array.isArray(newArg.defaultValue)) {
|
|
869
1108
|
const diff = diffArrays(oldArg.defaultValue, newArg.defaultValue);
|
|
870
1109
|
if (diff.length > 0) {
|
|
871
|
-
|
|
1110
|
+
addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
872
1111
|
}
|
|
873
1112
|
}
|
|
874
1113
|
else if (JSON.stringify(oldArg.defaultValue) !==
|
|
875
1114
|
JSON.stringify(newArg.defaultValue)) {
|
|
876
|
-
|
|
1115
|
+
addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
877
1116
|
}
|
|
878
1117
|
}
|
|
879
1118
|
if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
|
|
880
|
-
|
|
1119
|
+
addChange(fieldArgumentTypeChanged(type, field, oldArg, newArg));
|
|
881
1120
|
}
|
|
882
|
-
return changes;
|
|
883
1121
|
}
|
|
884
1122
|
|
|
885
|
-
function changesInField(type, oldField, newField) {
|
|
886
|
-
const changes = [];
|
|
1123
|
+
function changesInField(type, oldField, newField, addChange) {
|
|
887
1124
|
if (isNotEqual(oldField.description, newField.description)) {
|
|
888
1125
|
if (isVoid(oldField.description)) {
|
|
889
|
-
|
|
1126
|
+
addChange(fieldDescriptionAdded(type, newField));
|
|
890
1127
|
}
|
|
891
1128
|
else if (isVoid(newField.description)) {
|
|
892
|
-
|
|
1129
|
+
addChange(fieldDescriptionRemoved(type, oldField));
|
|
893
1130
|
}
|
|
894
1131
|
else {
|
|
895
|
-
|
|
1132
|
+
addChange(fieldDescriptionChanged(type, oldField, newField));
|
|
896
1133
|
}
|
|
897
1134
|
}
|
|
898
|
-
if (isNotEqual(oldField
|
|
899
|
-
if (newField
|
|
900
|
-
|
|
1135
|
+
if (isNotEqual(isDeprecated(oldField), isDeprecated(newField))) {
|
|
1136
|
+
if (isDeprecated(newField)) {
|
|
1137
|
+
addChange(fieldDeprecationAdded(type, newField));
|
|
901
1138
|
}
|
|
902
1139
|
else {
|
|
903
|
-
|
|
1140
|
+
addChange(fieldDeprecationRemoved(type, oldField));
|
|
904
1141
|
}
|
|
905
1142
|
}
|
|
906
1143
|
if (isNotEqual(oldField.deprecationReason, newField.deprecationReason)) {
|
|
907
1144
|
if (isVoid(oldField.deprecationReason)) {
|
|
908
|
-
|
|
1145
|
+
addChange(fieldDeprecationReasonAdded(type, newField));
|
|
909
1146
|
}
|
|
910
1147
|
else if (isVoid(newField.deprecationReason)) {
|
|
911
|
-
|
|
1148
|
+
addChange(fieldDeprecationReasonRemoved(type, oldField));
|
|
912
1149
|
}
|
|
913
1150
|
else {
|
|
914
|
-
|
|
1151
|
+
addChange(fieldDeprecationReasonChanged(type, oldField, newField));
|
|
915
1152
|
}
|
|
916
1153
|
}
|
|
917
1154
|
if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
|
|
918
|
-
|
|
1155
|
+
addChange(fieldTypeChanged(type, oldField, newField));
|
|
919
1156
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
common.forEach(({ inOld, inNew }) => {
|
|
931
|
-
changes.push(...changesInArgument(type, oldField, inOld, inNew));
|
|
1157
|
+
compareLists(oldField.args, newField.args, {
|
|
1158
|
+
onAdded(arg) {
|
|
1159
|
+
addChange(fieldArgumentAdded(type, newField, arg));
|
|
1160
|
+
},
|
|
1161
|
+
onRemoved(arg) {
|
|
1162
|
+
addChange(fieldArgumentRemoved(type, oldField, arg));
|
|
1163
|
+
},
|
|
1164
|
+
onMutual(arg) {
|
|
1165
|
+
changesInArgument(type, oldField, arg.oldVersion, arg.newVersion, addChange);
|
|
1166
|
+
},
|
|
932
1167
|
});
|
|
933
|
-
changes.push(...added.map(arg => fieldArgumentAdded(type, newField, arg)));
|
|
934
|
-
changes.push(...removed.map(arg => fieldArgumentRemoved(type, oldField, arg)));
|
|
935
|
-
return changes;
|
|
936
1168
|
}
|
|
937
1169
|
|
|
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) {
|
|
1170
|
+
function changesInObject(oldType, newType, addChange) {
|
|
952
1171
|
const oldInterfaces = oldType.getInterfaces();
|
|
953
1172
|
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) {
|
|
961
|
-
const oldInterfaces = oldType.getInterfaces();
|
|
962
|
-
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
|
-
const oldFields = oldType.getFields();
|
|
980
|
-
const newFields = newType.getFields();
|
|
981
|
-
const oldNames = Object.keys(oldFields);
|
|
982
|
-
const newNames = Object.keys(newFields);
|
|
983
|
-
return diffArrays(oldNames, newNames)
|
|
984
|
-
.map(name => oldFields[name])
|
|
985
|
-
.map(f => fieldRemoved(oldType, f));
|
|
986
|
-
}
|
|
987
|
-
function changedFields(oldType, newType) {
|
|
988
1173
|
const oldFields = oldType.getFields();
|
|
989
1174
|
const newFields = newType.getFields();
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1175
|
+
compareLists(oldInterfaces, newInterfaces, {
|
|
1176
|
+
onAdded(i) {
|
|
1177
|
+
addChange(objectTypeInterfaceAdded(i, newType));
|
|
1178
|
+
},
|
|
1179
|
+
onRemoved(i) {
|
|
1180
|
+
addChange(objectTypeInterfaceRemoved(i, oldType));
|
|
1181
|
+
},
|
|
1182
|
+
});
|
|
1183
|
+
compareLists(Object.values(oldFields), Object.values(newFields), {
|
|
1184
|
+
onAdded(f) {
|
|
1185
|
+
addChange(fieldAdded(newType, f));
|
|
1186
|
+
},
|
|
1187
|
+
onRemoved(f) {
|
|
1188
|
+
addChange(fieldRemoved(oldType, f));
|
|
1189
|
+
},
|
|
1190
|
+
onMutual(f) {
|
|
1191
|
+
changesInField(oldType, f.oldVersion, f.newVersion, addChange);
|
|
1192
|
+
},
|
|
1193
|
+
});
|
|
996
1194
|
}
|
|
997
1195
|
|
|
998
|
-
function changesInInterface(oldInterface, newInterface) {
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1196
|
+
function changesInInterface(oldInterface, newInterface, addChange) {
|
|
1197
|
+
compareLists(Object.values(oldInterface.getFields()), Object.values(newInterface.getFields()), {
|
|
1198
|
+
onAdded(field) {
|
|
1199
|
+
addChange(fieldAdded(newInterface, field));
|
|
1200
|
+
},
|
|
1201
|
+
onRemoved(field) {
|
|
1202
|
+
addChange(fieldRemoved(oldInterface, field));
|
|
1203
|
+
},
|
|
1204
|
+
onMutual(field) {
|
|
1205
|
+
changesInField(oldInterface, field.oldVersion, field.newVersion, addChange);
|
|
1206
|
+
},
|
|
1004
1207
|
});
|
|
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
1208
|
}
|
|
1035
1209
|
|
|
1036
|
-
function changesInDirective(oldDirective, newDirective) {
|
|
1037
|
-
const changes = [];
|
|
1210
|
+
function changesInDirective(oldDirective, newDirective, addChange) {
|
|
1038
1211
|
if (isNotEqual(oldDirective.description, newDirective.description)) {
|
|
1039
|
-
|
|
1212
|
+
addChange(directiveDescriptionChanged(oldDirective, newDirective));
|
|
1040
1213
|
}
|
|
1214
|
+
const locations = {
|
|
1215
|
+
added: diffArrays(newDirective.locations, oldDirective.locations),
|
|
1216
|
+
removed: diffArrays(oldDirective.locations, newDirective.locations),
|
|
1217
|
+
};
|
|
1041
1218
|
// locations added
|
|
1042
|
-
|
|
1219
|
+
locations.added.forEach((location) => addChange(directiveLocationAdded(newDirective, location)));
|
|
1043
1220
|
// locations removed
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
changes.push(...changesInDirectiveArgument(oldDirective, oldArg, newArg));
|
|
1221
|
+
locations.removed.forEach((location) => addChange(directiveLocationRemoved(oldDirective, location)));
|
|
1222
|
+
compareLists(oldDirective.args, newDirective.args, {
|
|
1223
|
+
onAdded(arg) {
|
|
1224
|
+
addChange(directiveArgumentAdded(newDirective, arg));
|
|
1225
|
+
},
|
|
1226
|
+
onRemoved(arg) {
|
|
1227
|
+
addChange(directiveArgumentRemoved(oldDirective, arg));
|
|
1228
|
+
},
|
|
1229
|
+
onMutual(arg) {
|
|
1230
|
+
changesInDirectiveArgument(oldDirective, arg.oldVersion, arg.newVersion, addChange);
|
|
1231
|
+
},
|
|
1056
1232
|
});
|
|
1057
|
-
return changes;
|
|
1058
1233
|
}
|
|
1059
|
-
function changesInDirectiveArgument(directive, oldArg, newArg) {
|
|
1060
|
-
const changes = [];
|
|
1234
|
+
function changesInDirectiveArgument(directive, oldArg, newArg, addChange) {
|
|
1061
1235
|
if (isNotEqual(oldArg.description, newArg.description)) {
|
|
1062
|
-
|
|
1236
|
+
addChange(directiveArgumentDescriptionChanged(directive, oldArg, newArg));
|
|
1063
1237
|
}
|
|
1064
1238
|
if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
|
|
1065
|
-
|
|
1239
|
+
addChange(directiveArgumentDefaultValueChanged(directive, oldArg, newArg));
|
|
1066
1240
|
}
|
|
1067
1241
|
if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
|
|
1068
|
-
|
|
1242
|
+
addChange(directiveArgumentTypeChanged(directive, oldArg, newArg));
|
|
1069
1243
|
}
|
|
1070
|
-
return changes;
|
|
1071
1244
|
}
|
|
1072
1245
|
|
|
1073
1246
|
function diffSchema(oldSchema, newSchema) {
|
|
1074
1247
|
const changes = [];
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1248
|
+
function addChange(change) {
|
|
1249
|
+
changes.push(change);
|
|
1250
|
+
}
|
|
1251
|
+
changesInSchema(oldSchema, newSchema, addChange);
|
|
1252
|
+
compareLists(Object.values(oldSchema.getTypeMap()).filter((t) => !isPrimitive(t)), Object.values(newSchema.getTypeMap()).filter((t) => !isPrimitive(t)), {
|
|
1253
|
+
onAdded(type) {
|
|
1254
|
+
addChange(typeAdded(type));
|
|
1255
|
+
},
|
|
1256
|
+
onRemoved(type) {
|
|
1257
|
+
addChange(typeRemoved(type));
|
|
1258
|
+
},
|
|
1259
|
+
onMutual(type) {
|
|
1260
|
+
changesInType(type.oldVersion, type.newVersion, addChange);
|
|
1261
|
+
},
|
|
1088
1262
|
});
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1263
|
+
compareLists(oldSchema.getDirectives(), newSchema.getDirectives(), {
|
|
1264
|
+
onAdded(directive) {
|
|
1265
|
+
addChange(directiveAdded(directive));
|
|
1266
|
+
},
|
|
1267
|
+
onRemoved(directive) {
|
|
1268
|
+
addChange(directiveRemoved(directive));
|
|
1269
|
+
},
|
|
1270
|
+
onMutual(directive) {
|
|
1271
|
+
changesInDirective(directive.oldVersion, directive.newVersion, addChange);
|
|
1272
|
+
},
|
|
1092
1273
|
});
|
|
1093
1274
|
return changes;
|
|
1094
1275
|
}
|
|
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,
|
|
1276
|
+
function changesInSchema(oldSchema, newSchema, addChange) {
|
|
1277
|
+
var _a, _b, _c, _d, _e, _f;
|
|
1278
|
+
const defaultNames = {
|
|
1279
|
+
query: 'Query',
|
|
1280
|
+
mutation: 'Mutation',
|
|
1281
|
+
subscription: 'Subscription',
|
|
1110
1282
|
};
|
|
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,
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
function changesInSchema(oldSchema, newSchema) {
|
|
1130
|
-
const changes = [];
|
|
1131
1283
|
const oldRoot = {
|
|
1132
|
-
query: (oldSchema.getQueryType() || {}).name,
|
|
1133
|
-
mutation: (oldSchema.getMutationType() || {}).name,
|
|
1134
|
-
subscription: (oldSchema.getSubscriptionType() || {})
|
|
1135
|
-
.name,
|
|
1284
|
+
query: (_a = (oldSchema.getQueryType() || {}).name) !== null && _a !== void 0 ? _a : defaultNames.query,
|
|
1285
|
+
mutation: (_b = (oldSchema.getMutationType() || {}).name) !== null && _b !== void 0 ? _b : defaultNames.mutation,
|
|
1286
|
+
subscription: (_c = (oldSchema.getSubscriptionType() || {}).name) !== null && _c !== void 0 ? _c : defaultNames.subscription,
|
|
1136
1287
|
};
|
|
1137
1288
|
const newRoot = {
|
|
1138
|
-
query: (newSchema.getQueryType() || {}).name,
|
|
1139
|
-
mutation: (newSchema.getMutationType() || {}).name,
|
|
1140
|
-
subscription: (newSchema.getSubscriptionType() || {})
|
|
1141
|
-
.name,
|
|
1289
|
+
query: (_d = (newSchema.getQueryType() || {}).name) !== null && _d !== void 0 ? _d : defaultNames.query,
|
|
1290
|
+
mutation: (_e = (newSchema.getMutationType() || {}).name) !== null && _e !== void 0 ? _e : defaultNames.mutation,
|
|
1291
|
+
subscription: (_f = (newSchema.getSubscriptionType() || {}).name) !== null && _f !== void 0 ? _f : defaultNames.subscription,
|
|
1142
1292
|
};
|
|
1143
1293
|
if (isNotEqual(oldRoot.query, newRoot.query)) {
|
|
1144
|
-
|
|
1294
|
+
addChange(schemaQueryTypeChanged(oldSchema, newSchema));
|
|
1145
1295
|
}
|
|
1146
1296
|
if (isNotEqual(oldRoot.mutation, newRoot.mutation)) {
|
|
1147
|
-
|
|
1297
|
+
addChange(schemaMutationTypeChanged(oldSchema, newSchema));
|
|
1148
1298
|
}
|
|
1149
1299
|
if (isNotEqual(oldRoot.subscription, newRoot.subscription)) {
|
|
1150
|
-
|
|
1300
|
+
addChange(schemaSubscriptionTypeChanged(oldSchema, newSchema));
|
|
1151
1301
|
}
|
|
1152
|
-
return changes;
|
|
1153
1302
|
}
|
|
1154
|
-
function changesInType(oldType, newType) {
|
|
1155
|
-
let changes = [];
|
|
1303
|
+
function changesInType(oldType, newType, addChange) {
|
|
1156
1304
|
if (graphql.isEnumType(oldType) && graphql.isEnumType(newType)) {
|
|
1157
|
-
|
|
1305
|
+
changesInEnum(oldType, newType, addChange);
|
|
1158
1306
|
}
|
|
1159
1307
|
else if (graphql.isUnionType(oldType) && graphql.isUnionType(newType)) {
|
|
1160
|
-
|
|
1308
|
+
changesInUnion(oldType, newType, addChange);
|
|
1161
1309
|
}
|
|
1162
1310
|
else if (graphql.isInputObjectType(oldType) && graphql.isInputObjectType(newType)) {
|
|
1163
|
-
|
|
1311
|
+
changesInInputObject(oldType, newType, addChange);
|
|
1164
1312
|
}
|
|
1165
1313
|
else if (graphql.isObjectType(oldType) && graphql.isObjectType(newType)) {
|
|
1166
|
-
|
|
1314
|
+
changesInObject(oldType, newType, addChange);
|
|
1167
1315
|
}
|
|
1168
1316
|
else if (graphql.isInterfaceType(oldType) && graphql.isInterfaceType(newType)) {
|
|
1169
|
-
|
|
1317
|
+
changesInInterface(oldType, newType, addChange);
|
|
1170
1318
|
}
|
|
1171
1319
|
else if (graphql.isScalarType(oldType) && graphql.isScalarType(newType)) ;
|
|
1172
1320
|
else {
|
|
1173
|
-
|
|
1321
|
+
addChange(typeKindChanged(oldType, newType));
|
|
1174
1322
|
}
|
|
1175
1323
|
if (isNotEqual(oldType.description, newType.description)) {
|
|
1176
1324
|
if (isVoid(oldType.description)) {
|
|
1177
|
-
|
|
1325
|
+
addChange(typeDescriptionAdded(newType));
|
|
1178
1326
|
}
|
|
1179
1327
|
else if (isVoid(newType.description)) {
|
|
1180
|
-
|
|
1328
|
+
addChange(typeDescriptionRemoved(oldType));
|
|
1181
1329
|
}
|
|
1182
1330
|
else {
|
|
1183
|
-
|
|
1331
|
+
addChange(typeDescriptionChanged(oldType, newType));
|
|
1184
1332
|
}
|
|
1185
1333
|
}
|
|
1186
|
-
return changes;
|
|
1187
1334
|
}
|
|
1188
1335
|
|
|
1336
|
+
const dangerousBreaking = ({ changes }) => {
|
|
1337
|
+
return changes.map((change) => {
|
|
1338
|
+
if (change.criticality.level === exports.CriticalityLevel.Dangerous) {
|
|
1339
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Breaking }) });
|
|
1340
|
+
}
|
|
1341
|
+
return change;
|
|
1342
|
+
});
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1189
1345
|
function parsePath(path) {
|
|
1190
1346
|
return path.split('.');
|
|
1191
1347
|
}
|
|
1192
1348
|
|
|
1193
1349
|
const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
1194
|
-
return changes.map(change => {
|
|
1350
|
+
return changes.map((change) => {
|
|
1195
1351
|
if (change.type === exports.ChangeType.FieldRemoved &&
|
|
1196
1352
|
change.criticality.level === exports.CriticalityLevel.Breaking &&
|
|
1197
1353
|
change.path) {
|
|
@@ -1199,7 +1355,31 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
|
1199
1355
|
const type = oldSchema.getType(typeName);
|
|
1200
1356
|
if (graphql.isObjectType(type) || graphql.isInterfaceType(type)) {
|
|
1201
1357
|
const field = type.getFields()[fieldName];
|
|
1202
|
-
if (field
|
|
1358
|
+
if (isDeprecated(field)) {
|
|
1359
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (change.type === exports.ChangeType.EnumValueRemoved &&
|
|
1364
|
+
change.criticality.level === exports.CriticalityLevel.Breaking &&
|
|
1365
|
+
change.path) {
|
|
1366
|
+
const [enumName, enumItem] = parsePath(change.path);
|
|
1367
|
+
const type = oldSchema.getType(enumName);
|
|
1368
|
+
if (graphql.isEnumType(type)) {
|
|
1369
|
+
const item = type.getValue(enumItem);
|
|
1370
|
+
if (item && isDeprecated(item)) {
|
|
1371
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
if (change.type === exports.ChangeType.InputFieldRemoved &&
|
|
1376
|
+
change.criticality.level === exports.CriticalityLevel.Breaking &&
|
|
1377
|
+
change.path) {
|
|
1378
|
+
const [inputName, inputItem] = parsePath(change.path);
|
|
1379
|
+
const type = oldSchema.getType(inputName);
|
|
1380
|
+
if (graphql.isInputObjectType(type)) {
|
|
1381
|
+
const item = type.getFields()[inputItem];
|
|
1382
|
+
if (item && isDeprecated(item)) {
|
|
1203
1383
|
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1204
1384
|
}
|
|
1205
1385
|
}
|
|
@@ -1222,23 +1402,76 @@ const descriptionChangeTypes = [
|
|
|
1222
1402
|
exports.ChangeType.TypeDescriptionChanged,
|
|
1223
1403
|
];
|
|
1224
1404
|
const ignoreDescriptionChanges = ({ changes }) => {
|
|
1225
|
-
return changes.filter(change => descriptionChangeTypes.indexOf(change.type) === -1);
|
|
1405
|
+
return changes.filter((change) => descriptionChangeTypes.indexOf(change.type) === -1);
|
|
1406
|
+
};
|
|
1407
|
+
|
|
1408
|
+
const considerUsage = ({ changes, config, }) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
1409
|
+
if (!config) {
|
|
1410
|
+
throw new Error(`considerUsage rule is missing config`);
|
|
1411
|
+
}
|
|
1412
|
+
const collectedBreakingField = [];
|
|
1413
|
+
changes.forEach((change) => {
|
|
1414
|
+
if (change.criticality.level === exports.CriticalityLevel.Breaking && change.path) {
|
|
1415
|
+
const [typeName, fieldName, argumentName] = parsePath(change.path);
|
|
1416
|
+
collectedBreakingField.push({
|
|
1417
|
+
type: typeName,
|
|
1418
|
+
field: fieldName,
|
|
1419
|
+
argument: argumentName,
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
// True if safe to break, false otherwise
|
|
1424
|
+
const usageList = yield config.checkUsage(collectedBreakingField);
|
|
1425
|
+
// turns an array of booleans into an array of `Type.Field` strings
|
|
1426
|
+
// includes only those that are safe to break the api
|
|
1427
|
+
const suppressedPaths = collectedBreakingField
|
|
1428
|
+
.filter((_, i) => usageList[i] === true)
|
|
1429
|
+
.map(({ type, field, argument }) => [type, field, argument].filter(Boolean).join('.'));
|
|
1430
|
+
return changes.map((change) => {
|
|
1431
|
+
// Turns those "safe to break" changes into "dangerous"
|
|
1432
|
+
if (change.criticality.level === exports.CriticalityLevel.Breaking &&
|
|
1433
|
+
change.path &&
|
|
1434
|
+
suppressedPaths.some((p) => change.path.startsWith(p))) {
|
|
1435
|
+
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)` });
|
|
1436
|
+
}
|
|
1437
|
+
return change;
|
|
1438
|
+
});
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
const safeUnreachable = ({ changes, oldSchema }) => {
|
|
1442
|
+
const reachable = getReachableTypes(oldSchema);
|
|
1443
|
+
return changes.map((change) => {
|
|
1444
|
+
if (change.criticality.level === exports.CriticalityLevel.Breaking && change.path) {
|
|
1445
|
+
const [typeName] = parsePath(change.path);
|
|
1446
|
+
if (!reachable.has(typeName)) {
|
|
1447
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.NonBreaking }), message: 'Unreachable from root' });
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
return change;
|
|
1451
|
+
});
|
|
1226
1452
|
};
|
|
1227
1453
|
|
|
1228
1454
|
const rules = /*#__PURE__*/Object.freeze({
|
|
1229
1455
|
__proto__: null,
|
|
1456
|
+
dangerousBreaking: dangerousBreaking,
|
|
1230
1457
|
suppressRemovalOfDeprecatedField: suppressRemovalOfDeprecatedField,
|
|
1231
|
-
ignoreDescriptionChanges: ignoreDescriptionChanges
|
|
1458
|
+
ignoreDescriptionChanges: ignoreDescriptionChanges,
|
|
1459
|
+
considerUsage: considerUsage,
|
|
1460
|
+
safeUnreachable: safeUnreachable
|
|
1232
1461
|
});
|
|
1233
1462
|
|
|
1234
1463
|
const DiffRule = rules;
|
|
1235
|
-
function diff(oldSchema, newSchema, rules = []) {
|
|
1464
|
+
function diff(oldSchema, newSchema, rules = [], config) {
|
|
1236
1465
|
const changes = diffSchema(oldSchema, newSchema);
|
|
1237
|
-
return rules.reduce((prev, rule) =>
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1466
|
+
return rules.reduce((prev, rule) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1467
|
+
const prevChanges = yield prev;
|
|
1468
|
+
return rule({
|
|
1469
|
+
changes: prevChanges,
|
|
1470
|
+
oldSchema,
|
|
1471
|
+
newSchema,
|
|
1472
|
+
config,
|
|
1473
|
+
});
|
|
1474
|
+
}), Promise.resolve(changes));
|
|
1242
1475
|
}
|
|
1243
1476
|
|
|
1244
1477
|
function readDocument(source) {
|
|
@@ -1314,7 +1547,7 @@ function calculateDepth({ node, currentDepth, maxDepth, getFragment, }) {
|
|
|
1314
1547
|
return 1 + maxInnerDepth;
|
|
1315
1548
|
}
|
|
1316
1549
|
case graphql.Kind.SELECTION_SET: {
|
|
1317
|
-
return Math.max(...node.selections.map(selection => {
|
|
1550
|
+
return Math.max(...node.selections.map((selection) => {
|
|
1318
1551
|
return calculateDepth({
|
|
1319
1552
|
node: selection,
|
|
1320
1553
|
currentDepth: currentDepth,
|
|
@@ -1324,7 +1557,7 @@ function calculateDepth({ node, currentDepth, maxDepth, getFragment, }) {
|
|
|
1324
1557
|
}));
|
|
1325
1558
|
}
|
|
1326
1559
|
case graphql.Kind.DOCUMENT: {
|
|
1327
|
-
return Math.max(...node.definitions.map(def => {
|
|
1560
|
+
return Math.max(...node.definitions.map((def) => {
|
|
1328
1561
|
return calculateDepth({
|
|
1329
1562
|
node: def,
|
|
1330
1563
|
currentDepth: currentDepth,
|
|
@@ -1336,7 +1569,7 @@ function calculateDepth({ node, currentDepth, maxDepth, getFragment, }) {
|
|
|
1336
1569
|
case graphql.Kind.OPERATION_DEFINITION:
|
|
1337
1570
|
case graphql.Kind.INLINE_FRAGMENT:
|
|
1338
1571
|
case graphql.Kind.FRAGMENT_DEFINITION: {
|
|
1339
|
-
return Math.max(...node.selectionSet.selections.map(selection => {
|
|
1572
|
+
return Math.max(...node.selectionSet.selections.map((selection) => {
|
|
1340
1573
|
return calculateDepth({
|
|
1341
1574
|
node: selection,
|
|
1342
1575
|
currentDepth,
|
|
@@ -1382,41 +1615,41 @@ function validate(schema, sources, options) {
|
|
|
1382
1615
|
const fragments = [];
|
|
1383
1616
|
const fragmentNames = [];
|
|
1384
1617
|
const graph = new dependencyGraph.DepGraph({ circular: true });
|
|
1385
|
-
documents.forEach(doc => {
|
|
1386
|
-
doc.fragments.forEach(fragment => {
|
|
1618
|
+
documents.forEach((doc) => {
|
|
1619
|
+
doc.fragments.forEach((fragment) => {
|
|
1387
1620
|
fragmentNames.push(fragment.node.name.value);
|
|
1388
1621
|
fragments.push(fragment);
|
|
1389
1622
|
graph.addNode(fragment.node.name.value, fragment.node);
|
|
1390
1623
|
});
|
|
1391
1624
|
});
|
|
1392
|
-
fragments.forEach(fragment => {
|
|
1625
|
+
fragments.forEach((fragment) => {
|
|
1393
1626
|
const depends = extractFragments(graphql.print(fragment.node));
|
|
1394
1627
|
if (depends) {
|
|
1395
|
-
depends.forEach(name => {
|
|
1628
|
+
depends.forEach((name) => {
|
|
1396
1629
|
graph.addDependency(fragment.node.name.value, name);
|
|
1397
1630
|
});
|
|
1398
1631
|
}
|
|
1399
1632
|
});
|
|
1400
1633
|
documents
|
|
1401
1634
|
// since we include fragments, validate only operations
|
|
1402
|
-
.filter(doc => doc.hasOperations)
|
|
1403
|
-
.forEach(doc => {
|
|
1635
|
+
.filter((doc) => doc.hasOperations)
|
|
1636
|
+
.forEach((doc) => {
|
|
1404
1637
|
const docWithOperations = {
|
|
1405
|
-
kind:
|
|
1406
|
-
definitions: doc.operations.map(d => d.node),
|
|
1638
|
+
kind: graphql.Kind.DOCUMENT,
|
|
1639
|
+
definitions: doc.operations.map((d) => d.node),
|
|
1407
1640
|
};
|
|
1408
1641
|
const extractedFragments = (extractFragments(graphql.print(docWithOperations)) || [])
|
|
1409
1642
|
// resolve all nested fragments
|
|
1410
|
-
.map(fragmentName => resolveFragment(graph.getNodeData(fragmentName), graph))
|
|
1643
|
+
.map((fragmentName) => resolveFragment(graph.getNodeData(fragmentName), graph))
|
|
1411
1644
|
// flatten arrays
|
|
1412
1645
|
.reduce((list, current) => list.concat(current), [])
|
|
1413
1646
|
// remove duplicates
|
|
1414
|
-
.filter((def, i, all) => all.findIndex(item => item.name.value === def.name.value) === i);
|
|
1647
|
+
.filter((def, i, all) => all.findIndex((item) => item.name.value === def.name.value) === i);
|
|
1415
1648
|
const merged = {
|
|
1416
|
-
kind:
|
|
1649
|
+
kind: graphql.Kind.DOCUMENT,
|
|
1417
1650
|
definitions: [...docWithOperations.definitions, ...extractedFragments],
|
|
1418
1651
|
};
|
|
1419
|
-
|
|
1652
|
+
let transformedSchema = config.apollo
|
|
1420
1653
|
? transformSchemaWithApollo(schema)
|
|
1421
1654
|
: schema;
|
|
1422
1655
|
const transformedDoc = config.apollo
|
|
@@ -1437,7 +1670,7 @@ function validate(schema, sources, options) {
|
|
|
1437
1670
|
}
|
|
1438
1671
|
}
|
|
1439
1672
|
const deprecated = config.strictDeprecated
|
|
1440
|
-
? findDeprecatedUsages(transformedSchema,
|
|
1673
|
+
? findDeprecatedUsages(transformedSchema, transformedDoc)
|
|
1441
1674
|
: [];
|
|
1442
1675
|
const duplicatedFragments = config.strictFragments
|
|
1443
1676
|
? findDuplicatedFragments(fragmentNames)
|
|
@@ -1455,7 +1688,7 @@ function validate(schema, sources, options) {
|
|
|
1455
1688
|
function findDuplicatedFragments(fragmentNames) {
|
|
1456
1689
|
return fragmentNames
|
|
1457
1690
|
.filter((name, i, all) => all.indexOf(name) !== i)
|
|
1458
|
-
.map(name => new graphql.GraphQLError(`Name of '${name}' fragment is not unique`));
|
|
1691
|
+
.map((name) => new graphql.GraphQLError(`Name of '${name}' fragment is not unique`));
|
|
1459
1692
|
}
|
|
1460
1693
|
//
|
|
1461
1694
|
// PostInfo -> AuthorInfo
|
|
@@ -1470,92 +1703,28 @@ function resolveFragment(fragment, graph) {
|
|
|
1470
1703
|
], [fragment]);
|
|
1471
1704
|
}
|
|
1472
1705
|
function extractFragments(document) {
|
|
1473
|
-
return (document.match(/[\.]{3}[a-z0-9\_]+\b/gi) || []).map(name => name.replace('...', ''));
|
|
1706
|
+
return (document.match(/[\.]{3}[a-z0-9\_]+\b/gi) || []).map((name) => name.replace('...', ''));
|
|
1474
1707
|
}
|
|
1475
1708
|
function sumLengths(...arrays) {
|
|
1476
1709
|
return arrays.reduce((sum, { length }) => sum + length, 0);
|
|
1477
1710
|
}
|
|
1478
1711
|
|
|
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
1712
|
function similar(schema, typeName, threshold = 0.4) {
|
|
1544
1713
|
const typeMap = schema.getTypeMap();
|
|
1545
1714
|
const targets = Object.keys(schema.getTypeMap())
|
|
1546
|
-
.filter(name => !isPrimitive(name) && !isForIntrospection(name))
|
|
1547
|
-
.map(name => ({
|
|
1715
|
+
.filter((name) => !isPrimitive(name) && !isForIntrospection(name))
|
|
1716
|
+
.map((name) => ({
|
|
1548
1717
|
typeId: name,
|
|
1549
1718
|
value: stripType(typeMap[name]),
|
|
1550
1719
|
}));
|
|
1551
1720
|
const results = {};
|
|
1552
1721
|
if (typeof typeName !== 'undefined' &&
|
|
1553
|
-
!targets.some(t => t.typeId === typeName)) {
|
|
1722
|
+
!targets.some((t) => t.typeId === typeName)) {
|
|
1554
1723
|
throw new Error(`Type '${typeName}' doesn't exist`);
|
|
1555
1724
|
}
|
|
1556
|
-
(typeName ? [{ typeId: typeName, value: '' }] : targets).forEach(source => {
|
|
1725
|
+
(typeName ? [{ typeId: typeName, value: '' }] : targets).forEach((source) => {
|
|
1557
1726
|
const sourceType = schema.getType(source.typeId);
|
|
1558
|
-
const matchWith = targets.filter(target => schema.getType(target.typeId).astNode.kind ===
|
|
1727
|
+
const matchWith = targets.filter((target) => schema.getType(target.typeId).astNode.kind ===
|
|
1559
1728
|
sourceType.astNode.kind && target.typeId !== source.typeId);
|
|
1560
1729
|
if (matchWith.length > 0) {
|
|
1561
1730
|
const found = similarTo(sourceType, matchWith, threshold);
|
|
@@ -1567,7 +1736,7 @@ function similar(schema, typeName, threshold = 0.4) {
|
|
|
1567
1736
|
return results;
|
|
1568
1737
|
}
|
|
1569
1738
|
function similarTo(type, targets, threshold) {
|
|
1570
|
-
const types = targets.filter(target => target.typeId !== type.name);
|
|
1739
|
+
const types = targets.filter((target) => target.typeId !== type.name);
|
|
1571
1740
|
const result = findBestMatch(stripType(type), types);
|
|
1572
1741
|
if (result.bestMatch.rating < threshold) {
|
|
1573
1742
|
return;
|
|
@@ -1575,7 +1744,7 @@ function similarTo(type, targets, threshold) {
|
|
|
1575
1744
|
return {
|
|
1576
1745
|
bestMatch: result.bestMatch,
|
|
1577
1746
|
ratings: result.ratings
|
|
1578
|
-
.filter(r => r.rating >= threshold && r.target !== result.bestMatch.target)
|
|
1747
|
+
.filter((r) => r.rating >= threshold && r.target !== result.bestMatch.target)
|
|
1579
1748
|
.sort((a, b) => a.rating - b.rating)
|
|
1580
1749
|
.reverse(),
|
|
1581
1750
|
};
|
|
@@ -1587,7 +1756,7 @@ function stripType(type) {
|
|
|
1587
1756
|
.replace(/\}$/g, '')
|
|
1588
1757
|
.trim()
|
|
1589
1758
|
.split('\n')
|
|
1590
|
-
.map(s => s.trim())
|
|
1759
|
+
.map((s) => s.trim())
|
|
1591
1760
|
.sort((a, b) => a.localeCompare(b))
|
|
1592
1761
|
.join(' ');
|
|
1593
1762
|
}
|
|
@@ -1599,7 +1768,7 @@ function coverage(schema, sources) {
|
|
|
1599
1768
|
};
|
|
1600
1769
|
const typeMap = schema.getTypeMap();
|
|
1601
1770
|
const typeInfo = new graphql.TypeInfo(schema);
|
|
1602
|
-
const visitor = source => ({
|
|
1771
|
+
const visitor = (source) => ({
|
|
1603
1772
|
Field(node) {
|
|
1604
1773
|
const fieldDef = typeInfo.getFieldDef();
|
|
1605
1774
|
const parent = typeInfo.getParentType();
|
|
@@ -1610,10 +1779,30 @@ function coverage(schema, sources) {
|
|
|
1610
1779
|
fieldDef.name &&
|
|
1611
1780
|
fieldDef.name !== '__typename' &&
|
|
1612
1781
|
fieldDef.name !== '__schema') {
|
|
1613
|
-
const
|
|
1614
|
-
coverage.types[parent.name]
|
|
1615
|
-
|
|
1616
|
-
|
|
1782
|
+
const sourceName = source.name;
|
|
1783
|
+
const typeCoverage = coverage.types[parent.name];
|
|
1784
|
+
const fieldCoverage = typeCoverage.children[fieldDef.name];
|
|
1785
|
+
const locations = fieldCoverage.locations[sourceName];
|
|
1786
|
+
typeCoverage.hits++;
|
|
1787
|
+
fieldCoverage.hits++;
|
|
1788
|
+
if (node.loc) {
|
|
1789
|
+
fieldCoverage.locations[sourceName] = [
|
|
1790
|
+
node.loc,
|
|
1791
|
+
...(locations || []),
|
|
1792
|
+
];
|
|
1793
|
+
}
|
|
1794
|
+
if (node.arguments) {
|
|
1795
|
+
for (const argNode of node.arguments) {
|
|
1796
|
+
const argCoverage = fieldCoverage.children[argNode.name.value];
|
|
1797
|
+
argCoverage.hits++;
|
|
1798
|
+
if (argNode.loc) {
|
|
1799
|
+
argCoverage.locations[sourceName] = [
|
|
1800
|
+
argNode.loc,
|
|
1801
|
+
...(argCoverage.locations[sourceName] || []),
|
|
1802
|
+
];
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1617
1806
|
}
|
|
1618
1807
|
},
|
|
1619
1808
|
});
|
|
@@ -1632,7 +1821,14 @@ function coverage(schema, sources) {
|
|
|
1632
1821
|
typeCoverage.children[field.name] = {
|
|
1633
1822
|
hits: 0,
|
|
1634
1823
|
locations: {},
|
|
1824
|
+
children: {},
|
|
1635
1825
|
};
|
|
1826
|
+
for (const arg of field.args) {
|
|
1827
|
+
typeCoverage.children[field.name].children[arg.name] = {
|
|
1828
|
+
hits: 0,
|
|
1829
|
+
locations: {},
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1636
1832
|
}
|
|
1637
1833
|
coverage.types[type.name] = typeCoverage;
|
|
1638
1834
|
}
|
|
@@ -1641,10 +1837,10 @@ function coverage(schema, sources) {
|
|
|
1641
1837
|
const documents = coverage.sources.map(readDocument);
|
|
1642
1838
|
documents.forEach((doc, i) => {
|
|
1643
1839
|
const source = coverage.sources[i];
|
|
1644
|
-
doc.operations.forEach(op => {
|
|
1840
|
+
doc.operations.forEach((op) => {
|
|
1645
1841
|
graphql.visit(op.node, graphql.visitWithTypeInfo(typeInfo, visitor(source)));
|
|
1646
1842
|
});
|
|
1647
|
-
doc.fragments.forEach(fr => {
|
|
1843
|
+
doc.fragments.forEach((fr) => {
|
|
1648
1844
|
graphql.visit(fr.node, graphql.visitWithTypeInfo(typeInfo, visitor(source)));
|
|
1649
1845
|
});
|
|
1650
1846
|
});
|
|
@@ -1657,4 +1853,3 @@ exports.diff = diff;
|
|
|
1657
1853
|
exports.getTypePrefix = getTypePrefix;
|
|
1658
1854
|
exports.similar = similar;
|
|
1659
1855
|
exports.validate = validate;
|
|
1660
|
-
//# sourceMappingURL=index.cjs.js.map
|