@graphql-inspector/core 3.1.3 → 3.3.0
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/{dist/ast → ast}/document.d.ts +0 -0
- package/{dist/coverage → coverage}/index.d.ts +0 -0
- package/{dist/coverage → coverage}/output/json.d.ts +0 -0
- package/{dist/diff → diff}/argument.d.ts +0 -0
- package/{dist/diff → diff}/changes/argument.d.ts +0 -0
- package/{dist/diff → diff}/changes/change.d.ts +0 -0
- package/{dist/diff → diff}/changes/directive.d.ts +0 -0
- package/{dist/diff → diff}/changes/enum.d.ts +0 -0
- package/{dist/diff → diff}/changes/field.d.ts +0 -0
- package/{dist/diff → diff}/changes/input.d.ts +0 -0
- package/{dist/diff → diff}/changes/object.d.ts +0 -0
- package/{dist/diff → diff}/changes/schema.d.ts +0 -0
- package/{dist/diff → diff}/changes/type.d.ts +0 -0
- package/{dist/diff → diff}/changes/union.d.ts +0 -0
- package/{dist/diff → diff}/directive.d.ts +0 -0
- package/{dist/diff → diff}/enum.d.ts +0 -0
- package/{dist/diff → diff}/field.d.ts +0 -0
- package/{dist/diff → diff}/index.d.ts +0 -0
- package/{dist/diff → diff}/input.d.ts +0 -0
- package/{dist/diff → diff}/interface.d.ts +0 -0
- package/{dist/diff → diff}/object.d.ts +0 -0
- package/{dist/diff → diff}/onComplete/types.d.ts +0 -0
- package/{dist/diff → diff}/rules/config.d.ts +0 -0
- package/{dist/diff → diff}/rules/consider-usage.d.ts +0 -0
- package/{dist/diff → diff}/rules/dangerous-breaking.d.ts +0 -0
- package/{dist/diff → diff}/rules/ignore-description-changes.d.ts +0 -0
- package/{dist/diff → diff}/rules/index.d.ts +0 -0
- package/{dist/diff → diff}/rules/safe-unreachable.d.ts +0 -0
- package/{dist/diff → diff}/rules/suppress-removal-of-deprecated-field.d.ts +0 -0
- package/{dist/diff → diff}/rules/types.d.ts +0 -0
- package/{dist/diff → diff}/schema.d.ts +0 -0
- package/{dist/diff → diff}/union.d.ts +0 -0
- package/{dist/index.d.ts → index.d.ts} +0 -0
- package/{dist/index.js → index.js} +85 -71
- package/{dist/index.mjs → index.mjs} +85 -71
- package/package.json +29 -35
- package/{dist/similar → similar}/index.d.ts +0 -0
- package/{dist/utils → utils}/apollo.d.ts +0 -0
- package/{dist/utils → utils}/compare.d.ts +0 -0
- package/{dist/utils → utils}/graphql.d.ts +0 -0
- package/utils/isDeprecated.d.ts +2 -0
- package/{dist/utils → utils}/path.d.ts +0 -0
- package/{dist/utils → utils}/string.d.ts +0 -0
- package/{dist/validate → validate}/index.d.ts +0 -0
- package/{dist/validate → validate}/query-depth.d.ts +0 -0
- package/dist/LICENSE +0 -21
- package/dist/README.md +0 -55
- package/dist/package.json +0 -46
- package/dist/utils/isDeprecated.d.ts +0 -2
- package/utils/testing.ts +0 -11
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -26,7 +26,7 @@ function isEqual(a, b) {
|
|
|
26
26
|
}
|
|
27
27
|
return true;
|
|
28
28
|
}
|
|
29
|
-
if (typeof a === 'object' && typeof b === 'object') {
|
|
29
|
+
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
30
30
|
const aRecord = a;
|
|
31
31
|
const bRecord = b;
|
|
32
32
|
const aKeys = Object.keys(aRecord);
|
|
@@ -129,7 +129,7 @@ function safeChangeForInputValue(oldType, newType) {
|
|
|
129
129
|
return safeChangeForInputValue(oldType.ofType, newType.ofType);
|
|
130
130
|
}
|
|
131
131
|
if (graphql.isNonNullType(oldType)) {
|
|
132
|
-
const ofType = graphql.isNonNullType(newType) ? newType : newType;
|
|
132
|
+
const ofType = graphql.isNonNullType(newType) ? newType.ofType : newType;
|
|
133
133
|
return safeChangeForInputValue(oldType.ofType, ofType);
|
|
134
134
|
}
|
|
135
135
|
return false;
|
|
@@ -696,6 +696,70 @@ function changesInUnion(oldUnion, newUnion, addChange) {
|
|
|
696
696
|
});
|
|
697
697
|
}
|
|
698
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, '');
|
|
761
|
+
}
|
|
762
|
+
|
|
699
763
|
function inputFieldRemoved(input, field) {
|
|
700
764
|
return {
|
|
701
765
|
criticality: {
|
|
@@ -703,7 +767,7 @@ function inputFieldRemoved(input, field) {
|
|
|
703
767
|
reason: 'Removing an input field will cause existing queries that use this input field to error.',
|
|
704
768
|
},
|
|
705
769
|
type: exports.ChangeType.InputFieldRemoved,
|
|
706
|
-
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}'`,
|
|
707
771
|
path: [input.name, field.name].join('.'),
|
|
708
772
|
};
|
|
709
773
|
}
|
|
@@ -756,10 +820,10 @@ function inputFieldDefaultValueChanged(input, oldField, newField) {
|
|
|
756
820
|
return {
|
|
757
821
|
criticality: {
|
|
758
822
|
level: exports.CriticalityLevel.Dangerous,
|
|
759
|
-
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.',
|
|
760
824
|
},
|
|
761
825
|
type: exports.ChangeType.InputFieldDefaultValueChanged,
|
|
762
|
-
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)}'`,
|
|
763
827
|
path: [input.name, oldField.name].join('.'),
|
|
764
828
|
};
|
|
765
829
|
}
|
|
@@ -965,8 +1029,10 @@ function fieldTypeChanged(type, oldField, newField) {
|
|
|
965
1029
|
};
|
|
966
1030
|
}
|
|
967
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) ' : ' ';
|
|
968
1034
|
return {
|
|
969
|
-
criticality:
|
|
1035
|
+
criticality: isBreaking
|
|
970
1036
|
? {
|
|
971
1037
|
level: exports.CriticalityLevel.Breaking,
|
|
972
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.`,
|
|
@@ -976,7 +1042,7 @@ function fieldArgumentAdded(type, field, arg) {
|
|
|
976
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.`,
|
|
977
1043
|
},
|
|
978
1044
|
type: exports.ChangeType.FieldArgumentAdded,
|
|
979
|
-
message: `Argument '${arg.name}: ${arg.type}'
|
|
1045
|
+
message: `Argument '${arg.name}: ${arg.type}'${defaultValueMsg}added to field '${type.name}.${field.name}'`,
|
|
980
1046
|
path: [type.name, field.name, arg.name].join('.'),
|
|
981
1047
|
};
|
|
982
1048
|
}
|
|
@@ -992,70 +1058,6 @@ function fieldArgumentRemoved(type, field, arg) {
|
|
|
992
1058
|
};
|
|
993
1059
|
}
|
|
994
1060
|
|
|
995
|
-
function compareTwoStrings(str1, str2) {
|
|
996
|
-
if (!str1.length && !str2.length)
|
|
997
|
-
return 1;
|
|
998
|
-
if (!str1.length || !str2.length)
|
|
999
|
-
return 0;
|
|
1000
|
-
if (str1.toUpperCase() === str2.toUpperCase())
|
|
1001
|
-
return 1;
|
|
1002
|
-
if (str1.length === 1 && str2.length === 1)
|
|
1003
|
-
return 0;
|
|
1004
|
-
const pairs1 = wordLetterPairs(str1);
|
|
1005
|
-
const pairs2 = wordLetterPairs(str2);
|
|
1006
|
-
const union = pairs1.length + pairs2.length;
|
|
1007
|
-
let intersection = 0;
|
|
1008
|
-
pairs1.forEach((pair1) => {
|
|
1009
|
-
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
1010
|
-
if (pair1 !== pair2)
|
|
1011
|
-
continue;
|
|
1012
|
-
intersection++;
|
|
1013
|
-
pairs2.splice(i, 1);
|
|
1014
|
-
break;
|
|
1015
|
-
}
|
|
1016
|
-
});
|
|
1017
|
-
return (intersection * 2) / union;
|
|
1018
|
-
}
|
|
1019
|
-
function findBestMatch(mainString, targetStrings) {
|
|
1020
|
-
if (!areArgsValid(mainString, targetStrings))
|
|
1021
|
-
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
1022
|
-
const ratings = targetStrings.map((target) => ({
|
|
1023
|
-
target,
|
|
1024
|
-
rating: compareTwoStrings(mainString, target.value),
|
|
1025
|
-
}));
|
|
1026
|
-
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
1027
|
-
return { ratings, bestMatch };
|
|
1028
|
-
}
|
|
1029
|
-
function flattenDeep(arr) {
|
|
1030
|
-
return Array.isArray(arr)
|
|
1031
|
-
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
1032
|
-
: [arr];
|
|
1033
|
-
}
|
|
1034
|
-
function areArgsValid(mainString, targetStrings) {
|
|
1035
|
-
if (typeof mainString !== 'string')
|
|
1036
|
-
return false;
|
|
1037
|
-
if (!Array.isArray(targetStrings))
|
|
1038
|
-
return false;
|
|
1039
|
-
if (!targetStrings.length)
|
|
1040
|
-
return false;
|
|
1041
|
-
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
1042
|
-
return false;
|
|
1043
|
-
return true;
|
|
1044
|
-
}
|
|
1045
|
-
function letterPairs(str) {
|
|
1046
|
-
const pairs = [];
|
|
1047
|
-
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
1048
|
-
pairs[i] = str.substring(i, i + 2);
|
|
1049
|
-
return pairs;
|
|
1050
|
-
}
|
|
1051
|
-
function wordLetterPairs(str) {
|
|
1052
|
-
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
1053
|
-
return flattenDeep(pairs);
|
|
1054
|
-
}
|
|
1055
|
-
function safeString(obj) {
|
|
1056
|
-
return inspect(obj).replace(/\[Object\: null prototype\] /g, '');
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
1061
|
function fieldArgumentDescriptionChanged(type, field, oldArg, newArg) {
|
|
1060
1062
|
return {
|
|
1061
1063
|
criticality: {
|
|
@@ -1370,6 +1372,18 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
|
1370
1372
|
}
|
|
1371
1373
|
}
|
|
1372
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)) {
|
|
1383
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1373
1387
|
return change;
|
|
1374
1388
|
});
|
|
1375
1389
|
};
|
|
@@ -20,7 +20,7 @@ function isEqual(a, b) {
|
|
|
20
20
|
}
|
|
21
21
|
return true;
|
|
22
22
|
}
|
|
23
|
-
if (typeof a === 'object' && typeof b === 'object') {
|
|
23
|
+
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
24
24
|
const aRecord = a;
|
|
25
25
|
const bRecord = b;
|
|
26
26
|
const aKeys = Object.keys(aRecord);
|
|
@@ -123,7 +123,7 @@ function safeChangeForInputValue(oldType, newType) {
|
|
|
123
123
|
return safeChangeForInputValue(oldType.ofType, newType.ofType);
|
|
124
124
|
}
|
|
125
125
|
if (isNonNullType(oldType)) {
|
|
126
|
-
const ofType = isNonNullType(newType) ? newType : newType;
|
|
126
|
+
const ofType = isNonNullType(newType) ? newType.ofType : newType;
|
|
127
127
|
return safeChangeForInputValue(oldType.ofType, ofType);
|
|
128
128
|
}
|
|
129
129
|
return false;
|
|
@@ -692,6 +692,70 @@ function changesInUnion(oldUnion, newUnion, addChange) {
|
|
|
692
692
|
});
|
|
693
693
|
}
|
|
694
694
|
|
|
695
|
+
function compareTwoStrings(str1, str2) {
|
|
696
|
+
if (!str1.length && !str2.length)
|
|
697
|
+
return 1;
|
|
698
|
+
if (!str1.length || !str2.length)
|
|
699
|
+
return 0;
|
|
700
|
+
if (str1.toUpperCase() === str2.toUpperCase())
|
|
701
|
+
return 1;
|
|
702
|
+
if (str1.length === 1 && str2.length === 1)
|
|
703
|
+
return 0;
|
|
704
|
+
const pairs1 = wordLetterPairs(str1);
|
|
705
|
+
const pairs2 = wordLetterPairs(str2);
|
|
706
|
+
const union = pairs1.length + pairs2.length;
|
|
707
|
+
let intersection = 0;
|
|
708
|
+
pairs1.forEach((pair1) => {
|
|
709
|
+
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
710
|
+
if (pair1 !== pair2)
|
|
711
|
+
continue;
|
|
712
|
+
intersection++;
|
|
713
|
+
pairs2.splice(i, 1);
|
|
714
|
+
break;
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
return (intersection * 2) / union;
|
|
718
|
+
}
|
|
719
|
+
function findBestMatch(mainString, targetStrings) {
|
|
720
|
+
if (!areArgsValid(mainString, targetStrings))
|
|
721
|
+
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
722
|
+
const ratings = targetStrings.map((target) => ({
|
|
723
|
+
target,
|
|
724
|
+
rating: compareTwoStrings(mainString, target.value),
|
|
725
|
+
}));
|
|
726
|
+
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
727
|
+
return { ratings, bestMatch };
|
|
728
|
+
}
|
|
729
|
+
function flattenDeep(arr) {
|
|
730
|
+
return Array.isArray(arr)
|
|
731
|
+
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
732
|
+
: [arr];
|
|
733
|
+
}
|
|
734
|
+
function areArgsValid(mainString, targetStrings) {
|
|
735
|
+
if (typeof mainString !== 'string')
|
|
736
|
+
return false;
|
|
737
|
+
if (!Array.isArray(targetStrings))
|
|
738
|
+
return false;
|
|
739
|
+
if (!targetStrings.length)
|
|
740
|
+
return false;
|
|
741
|
+
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
742
|
+
return false;
|
|
743
|
+
return true;
|
|
744
|
+
}
|
|
745
|
+
function letterPairs(str) {
|
|
746
|
+
const pairs = [];
|
|
747
|
+
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
748
|
+
pairs[i] = str.substring(i, i + 2);
|
|
749
|
+
return pairs;
|
|
750
|
+
}
|
|
751
|
+
function wordLetterPairs(str) {
|
|
752
|
+
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
753
|
+
return flattenDeep(pairs);
|
|
754
|
+
}
|
|
755
|
+
function safeString(obj) {
|
|
756
|
+
return inspect(obj).replace(/\[Object\: null prototype\] /g, '').replace(/(^')|('$)/g, '');
|
|
757
|
+
}
|
|
758
|
+
|
|
695
759
|
function inputFieldRemoved(input, field) {
|
|
696
760
|
return {
|
|
697
761
|
criticality: {
|
|
@@ -699,7 +763,7 @@ function inputFieldRemoved(input, field) {
|
|
|
699
763
|
reason: 'Removing an input field will cause existing queries that use this input field to error.',
|
|
700
764
|
},
|
|
701
765
|
type: ChangeType.InputFieldRemoved,
|
|
702
|
-
message: `Input field '${field.name}' was removed from input object type '${input.name}'`,
|
|
766
|
+
message: `Input field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from input object type '${input.name}'`,
|
|
703
767
|
path: [input.name, field.name].join('.'),
|
|
704
768
|
};
|
|
705
769
|
}
|
|
@@ -752,10 +816,10 @@ function inputFieldDefaultValueChanged(input, oldField, newField) {
|
|
|
752
816
|
return {
|
|
753
817
|
criticality: {
|
|
754
818
|
level: CriticalityLevel.Dangerous,
|
|
755
|
-
reason: 'Changing the default value for an argument may change the runtime
|
|
819
|
+
reason: 'Changing the default value for an argument may change the runtime behavior of a field if it was never provided.',
|
|
756
820
|
},
|
|
757
821
|
type: ChangeType.InputFieldDefaultValueChanged,
|
|
758
|
-
message: `Input field '${input.name}.${oldField.name}' default value changed from '${oldField.defaultValue}' to '${newField.defaultValue}'`,
|
|
822
|
+
message: `Input field '${input.name}.${oldField.name}' default value changed from '${safeString(oldField.defaultValue)}' to '${safeString(newField.defaultValue)}'`,
|
|
759
823
|
path: [input.name, oldField.name].join('.'),
|
|
760
824
|
};
|
|
761
825
|
}
|
|
@@ -961,8 +1025,10 @@ function fieldTypeChanged(type, oldField, newField) {
|
|
|
961
1025
|
};
|
|
962
1026
|
}
|
|
963
1027
|
function fieldArgumentAdded(type, field, arg) {
|
|
1028
|
+
const isBreaking = isNonNullType(arg.type) && typeof arg.defaultValue === 'undefined';
|
|
1029
|
+
const defaultValueMsg = typeof arg.defaultValue !== 'undefined' ? ' (with default value) ' : ' ';
|
|
964
1030
|
return {
|
|
965
|
-
criticality:
|
|
1031
|
+
criticality: isBreaking
|
|
966
1032
|
? {
|
|
967
1033
|
level: CriticalityLevel.Breaking,
|
|
968
1034
|
reason: `Adding a required argument to an existing field is a breaking change because it will cause existing uses of this field to error.`,
|
|
@@ -972,7 +1038,7 @@ function fieldArgumentAdded(type, field, arg) {
|
|
|
972
1038
|
reason: `Adding a new argument to an existing field may involve a change in resolve function logic that potentially may cause some side effects.`,
|
|
973
1039
|
},
|
|
974
1040
|
type: ChangeType.FieldArgumentAdded,
|
|
975
|
-
message: `Argument '${arg.name}: ${arg.type}'
|
|
1041
|
+
message: `Argument '${arg.name}: ${arg.type}'${defaultValueMsg}added to field '${type.name}.${field.name}'`,
|
|
976
1042
|
path: [type.name, field.name, arg.name].join('.'),
|
|
977
1043
|
};
|
|
978
1044
|
}
|
|
@@ -988,70 +1054,6 @@ function fieldArgumentRemoved(type, field, arg) {
|
|
|
988
1054
|
};
|
|
989
1055
|
}
|
|
990
1056
|
|
|
991
|
-
function compareTwoStrings(str1, str2) {
|
|
992
|
-
if (!str1.length && !str2.length)
|
|
993
|
-
return 1;
|
|
994
|
-
if (!str1.length || !str2.length)
|
|
995
|
-
return 0;
|
|
996
|
-
if (str1.toUpperCase() === str2.toUpperCase())
|
|
997
|
-
return 1;
|
|
998
|
-
if (str1.length === 1 && str2.length === 1)
|
|
999
|
-
return 0;
|
|
1000
|
-
const pairs1 = wordLetterPairs(str1);
|
|
1001
|
-
const pairs2 = wordLetterPairs(str2);
|
|
1002
|
-
const union = pairs1.length + pairs2.length;
|
|
1003
|
-
let intersection = 0;
|
|
1004
|
-
pairs1.forEach((pair1) => {
|
|
1005
|
-
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
1006
|
-
if (pair1 !== pair2)
|
|
1007
|
-
continue;
|
|
1008
|
-
intersection++;
|
|
1009
|
-
pairs2.splice(i, 1);
|
|
1010
|
-
break;
|
|
1011
|
-
}
|
|
1012
|
-
});
|
|
1013
|
-
return (intersection * 2) / union;
|
|
1014
|
-
}
|
|
1015
|
-
function findBestMatch(mainString, targetStrings) {
|
|
1016
|
-
if (!areArgsValid(mainString, targetStrings))
|
|
1017
|
-
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
1018
|
-
const ratings = targetStrings.map((target) => ({
|
|
1019
|
-
target,
|
|
1020
|
-
rating: compareTwoStrings(mainString, target.value),
|
|
1021
|
-
}));
|
|
1022
|
-
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
1023
|
-
return { ratings, bestMatch };
|
|
1024
|
-
}
|
|
1025
|
-
function flattenDeep(arr) {
|
|
1026
|
-
return Array.isArray(arr)
|
|
1027
|
-
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
1028
|
-
: [arr];
|
|
1029
|
-
}
|
|
1030
|
-
function areArgsValid(mainString, targetStrings) {
|
|
1031
|
-
if (typeof mainString !== 'string')
|
|
1032
|
-
return false;
|
|
1033
|
-
if (!Array.isArray(targetStrings))
|
|
1034
|
-
return false;
|
|
1035
|
-
if (!targetStrings.length)
|
|
1036
|
-
return false;
|
|
1037
|
-
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
1038
|
-
return false;
|
|
1039
|
-
return true;
|
|
1040
|
-
}
|
|
1041
|
-
function letterPairs(str) {
|
|
1042
|
-
const pairs = [];
|
|
1043
|
-
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
1044
|
-
pairs[i] = str.substring(i, i + 2);
|
|
1045
|
-
return pairs;
|
|
1046
|
-
}
|
|
1047
|
-
function wordLetterPairs(str) {
|
|
1048
|
-
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
1049
|
-
return flattenDeep(pairs);
|
|
1050
|
-
}
|
|
1051
|
-
function safeString(obj) {
|
|
1052
|
-
return inspect(obj).replace(/\[Object\: null prototype\] /g, '');
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
1057
|
function fieldArgumentDescriptionChanged(type, field, oldArg, newArg) {
|
|
1056
1058
|
return {
|
|
1057
1059
|
criticality: {
|
|
@@ -1366,6 +1368,18 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
|
1366
1368
|
}
|
|
1367
1369
|
}
|
|
1368
1370
|
}
|
|
1371
|
+
if (change.type === ChangeType.InputFieldRemoved &&
|
|
1372
|
+
change.criticality.level === CriticalityLevel.Breaking &&
|
|
1373
|
+
change.path) {
|
|
1374
|
+
const [inputName, inputItem] = parsePath(change.path);
|
|
1375
|
+
const type = oldSchema.getType(inputName);
|
|
1376
|
+
if (isInputObjectType(type)) {
|
|
1377
|
+
const item = type.getFields()[inputItem];
|
|
1378
|
+
if (item && isDeprecated(item)) {
|
|
1379
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.Dangerous }) });
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1369
1383
|
return change;
|
|
1370
1384
|
});
|
|
1371
1385
|
};
|
package/package.json
CHANGED
|
@@ -1,52 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphql-inspector/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"peerDependencies": {
|
|
7
|
+
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"dependency-graph": "0.11.0",
|
|
11
|
+
"object-inspect": "1.10.3",
|
|
12
|
+
"tslib": "^2.0.0"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "kamilkisiela/graphql-inspector",
|
|
17
|
+
"directory": "packages/core"
|
|
18
|
+
},
|
|
5
19
|
"keywords": [
|
|
6
20
|
"graphql",
|
|
7
21
|
"graphql-inspector",
|
|
8
22
|
"tools"
|
|
9
23
|
],
|
|
10
|
-
"sideEffects": false,
|
|
11
|
-
"main": "dist/index.js",
|
|
12
|
-
"module": "dist/index.mjs",
|
|
13
|
-
"exports": {
|
|
14
|
-
".": {
|
|
15
|
-
"require": "./dist/index.js",
|
|
16
|
-
"import": "./dist/index.mjs"
|
|
17
|
-
},
|
|
18
|
-
"./*": {
|
|
19
|
-
"require": "./dist/*.js",
|
|
20
|
-
"import": "./dist/*.mjs"
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"typings": "dist/index.d.ts",
|
|
24
|
-
"typescript": {
|
|
25
|
-
"definition": "dist/index.d.ts"
|
|
26
|
-
},
|
|
27
24
|
"author": {
|
|
28
25
|
"name": "Kamil Kisiela",
|
|
29
26
|
"email": "kamil.kisiela@gmail.com",
|
|
30
27
|
"url": "https://github.com/kamilkisiela"
|
|
31
28
|
},
|
|
32
29
|
"license": "MIT",
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"peerDependencies": {
|
|
39
|
-
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
40
|
-
},
|
|
41
|
-
"dependencies": {
|
|
42
|
-
"dependency-graph": "0.11.0",
|
|
43
|
-
"object-inspect": "1.10.3",
|
|
44
|
-
"tslib": "^2.0.0"
|
|
45
|
-
},
|
|
46
|
-
"devDependencies": {
|
|
47
|
-
"@types/object-inspect": "1.8.0"
|
|
30
|
+
"main": "index.js",
|
|
31
|
+
"module": "index.mjs",
|
|
32
|
+
"typings": "index.d.ts",
|
|
33
|
+
"typescript": {
|
|
34
|
+
"definition": "index.d.ts"
|
|
48
35
|
},
|
|
49
|
-
"
|
|
50
|
-
"
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"require": "./index.js",
|
|
39
|
+
"import": "./index.mjs"
|
|
40
|
+
},
|
|
41
|
+
"./*": {
|
|
42
|
+
"require": "./*.js",
|
|
43
|
+
"import": "./*.mjs"
|
|
44
|
+
}
|
|
51
45
|
}
|
|
52
46
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/dist/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2018 Kamil Kisiela
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/README.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# GraphQL Inspector
|
|
2
|
-
|
|
3
|
-
[](https://circleci.com/gh/kamilkisiela/graphql-inspector)
|
|
4
|
-
[](https://npmjs.com/package/@graphql-inspector/core)
|
|
5
|
-
|
|
6
|
-
**GraphQL Inspector** ouputs a list of changes between two GraphQL schemas. Every change is precisely explained and marked as breaking, non-breaking or dangerous.
|
|
7
|
-
It helps you validate documents and fragments against a schema and even find similar or duplicated types.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
11
|
-
Major features:
|
|
12
|
-
|
|
13
|
-
- **Compares schemas**
|
|
14
|
-
- **Finds breaking or dangerous changes**
|
|
15
|
-
- **Validates documents against a schema**
|
|
16
|
-
- **Finds similar / duplicated types**
|
|
17
|
-
- **Schema coverage based on documents**
|
|
18
|
-
- **Serves a GraphQL server with faked data and GraphQL Playground**
|
|
19
|
-
|
|
20
|
-
GraphQL Inspector has a **CLI** and also a **programatic API**, so you can use it however you want to and even build tools on top of it.
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
yarn add @graphql-inspector/core
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Examples
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
import {
|
|
32
|
-
diff,
|
|
33
|
-
validate,
|
|
34
|
-
similar,
|
|
35
|
-
coverage,
|
|
36
|
-
Change,
|
|
37
|
-
InvalidDocument,
|
|
38
|
-
SimilarMap,
|
|
39
|
-
SchemaCoverage
|
|
40
|
-
} from '@graphql-inspector/core'
|
|
41
|
-
|
|
42
|
-
// diff
|
|
43
|
-
const changes: Change[] = diff(schemaA, schemaB)
|
|
44
|
-
// validate
|
|
45
|
-
const invalid: InvalidDocument[] = validate(documentsGlob, schema)
|
|
46
|
-
// similar
|
|
47
|
-
const similar: SimilarMap = similar(schema, typename, threshold)
|
|
48
|
-
// coverage
|
|
49
|
-
const schemaCoverage: SchemaCoverage = coverage(schema, documents)
|
|
50
|
-
// ...
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## License
|
|
54
|
-
|
|
55
|
-
[MIT](https://github.com/kamilkisiela/graphql-inspector/blob/master/LICENSE) © Kamil Kisiela
|
package/dist/package.json
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@graphql-inspector/core",
|
|
3
|
-
"version": "3.1.3",
|
|
4
|
-
"description": "Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.",
|
|
5
|
-
"sideEffects": false,
|
|
6
|
-
"peerDependencies": {
|
|
7
|
-
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
|
-
},
|
|
9
|
-
"dependencies": {
|
|
10
|
-
"dependency-graph": "0.11.0",
|
|
11
|
-
"object-inspect": "1.10.3",
|
|
12
|
-
"tslib": "^2.0.0"
|
|
13
|
-
},
|
|
14
|
-
"repository": {
|
|
15
|
-
"type": "git",
|
|
16
|
-
"url": "kamilkisiela/graphql-inspector",
|
|
17
|
-
"directory": "packages/core"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"graphql",
|
|
21
|
-
"graphql-inspector",
|
|
22
|
-
"tools"
|
|
23
|
-
],
|
|
24
|
-
"author": {
|
|
25
|
-
"name": "Kamil Kisiela",
|
|
26
|
-
"email": "kamil.kisiela@gmail.com",
|
|
27
|
-
"url": "https://github.com/kamilkisiela"
|
|
28
|
-
},
|
|
29
|
-
"license": "MIT",
|
|
30
|
-
"main": "index.js",
|
|
31
|
-
"module": "index.mjs",
|
|
32
|
-
"typings": "index.d.ts",
|
|
33
|
-
"typescript": {
|
|
34
|
-
"definition": "index.d.ts"
|
|
35
|
-
},
|
|
36
|
-
"exports": {
|
|
37
|
-
".": {
|
|
38
|
-
"require": "./index.js",
|
|
39
|
-
"import": "./index.mjs"
|
|
40
|
-
},
|
|
41
|
-
"./*": {
|
|
42
|
-
"require": "./*.js",
|
|
43
|
-
"import": "./*.mjs"
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
package/utils/testing.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// import { findDangerousChanges } from 'graphql';
|
|
2
|
-
|
|
3
|
-
import { Change } from '../src/diff/changes/change';
|
|
4
|
-
|
|
5
|
-
export function findChangesByPath(changes: Change[], path: string) {
|
|
6
|
-
return changes.filter((c) => c.path === path);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function findFirstChangeByPath(changes: Change[], path: string) {
|
|
10
|
-
return findChangesByPath(changes, path)[0];
|
|
11
|
-
}
|