@graphql-inspector/core 3.0.0 → 3.1.1
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/index.js +97 -75
- package/index.mjs +95 -75
- package/package.json +2 -2
- package/utils/compare.d.ts +2 -2
- package/utils/isDeprecated.d.ts +2 -0
package/index.js
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
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
|
+
|
|
5
7
|
const tslib = require('tslib');
|
|
6
8
|
const graphql = require('graphql');
|
|
9
|
+
const inspect = _interopDefault(require('object-inspect'));
|
|
7
10
|
const dependencyGraph = require('dependency-graph');
|
|
8
|
-
require('object-inspect');
|
|
9
11
|
|
|
10
12
|
function keyMap(list, keyFn) {
|
|
11
13
|
return list.reduce((map, item) => {
|
|
@@ -76,6 +78,20 @@ function compareLists(oldList, newList, callbacks) {
|
|
|
76
78
|
};
|
|
77
79
|
}
|
|
78
80
|
|
|
81
|
+
function isDeprecated(fieldOrEnumValue) {
|
|
82
|
+
var _a, _b;
|
|
83
|
+
if ('isDeprecated' in fieldOrEnumValue) {
|
|
84
|
+
return fieldOrEnumValue['isDeprecated'];
|
|
85
|
+
}
|
|
86
|
+
if (fieldOrEnumValue.deprecationReason != null) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
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")) {
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
79
95
|
function safeChangeForField(oldType, newType) {
|
|
80
96
|
if (!graphql.isWrappingType(oldType) && !graphql.isWrappingType(newType)) {
|
|
81
97
|
return oldType.toString() === newType.toString();
|
|
@@ -118,7 +134,7 @@ function getTypePrefix(type) {
|
|
|
118
134
|
[graphql.Kind.ENUM_TYPE_DEFINITION]: 'enum',
|
|
119
135
|
[graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
|
|
120
136
|
};
|
|
121
|
-
return kindsMap[kind];
|
|
137
|
+
return kindsMap[kind.toString()];
|
|
122
138
|
}
|
|
123
139
|
function isPrimitive(type) {
|
|
124
140
|
return (['String', 'Int', 'Float', 'Boolean', 'ID'].indexOf(typeof type === 'string' ? type : type.name) !== -1);
|
|
@@ -153,7 +169,7 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
153
169
|
},
|
|
154
170
|
Field(node) {
|
|
155
171
|
const fieldDef = typeInfo.getFieldDef();
|
|
156
|
-
if (fieldDef && fieldDef
|
|
172
|
+
if (fieldDef && isDeprecated(fieldDef)) {
|
|
157
173
|
const parentType = typeInfo.getParentType();
|
|
158
174
|
if (parentType) {
|
|
159
175
|
const reason = fieldDef.deprecationReason;
|
|
@@ -163,7 +179,7 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
163
179
|
},
|
|
164
180
|
EnumValue(node) {
|
|
165
181
|
const enumVal = typeInfo.getEnumValue();
|
|
166
|
-
if (enumVal && enumVal
|
|
182
|
+
if (enumVal && isDeprecated(enumVal)) {
|
|
167
183
|
const type = graphql.getNamedType(typeInfo.getInputType());
|
|
168
184
|
if (type) {
|
|
169
185
|
const reason = enumVal.deprecationReason;
|
|
@@ -474,7 +490,7 @@ function enumValueRemoved(oldEnum, value) {
|
|
|
474
490
|
reason: `Removing an enum value will cause existing queries that use this enum value to error.`,
|
|
475
491
|
},
|
|
476
492
|
type: exports.ChangeType.EnumValueRemoved,
|
|
477
|
-
message: `Enum value '${value.name}' ${value
|
|
493
|
+
message: `Enum value '${value.name}' ${isDeprecated(value) ? '(deprecated) ' : ''}was removed from enum '${oldEnum.name}'`,
|
|
478
494
|
path: [oldEnum.name, value.name].join('.'),
|
|
479
495
|
};
|
|
480
496
|
}
|
|
@@ -758,7 +774,7 @@ function fieldRemoved(type, field) {
|
|
|
758
774
|
: `Removing a field is a breaking change. It is preferable to deprecate the field before removing it.`,
|
|
759
775
|
},
|
|
760
776
|
type: exports.ChangeType.FieldRemoved,
|
|
761
|
-
message: `Field '${field.name}' ${field
|
|
777
|
+
message: `Field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from ${entity} '${type.name}'`,
|
|
762
778
|
path: [type.name, field.name].join('.'),
|
|
763
779
|
};
|
|
764
780
|
}
|
|
@@ -893,6 +909,73 @@ function fieldArgumentRemoved(type, field, arg) {
|
|
|
893
909
|
};
|
|
894
910
|
}
|
|
895
911
|
|
|
912
|
+
function compareTwoStrings(str1, str2) {
|
|
913
|
+
if (!str1.length && !str2.length)
|
|
914
|
+
return 1;
|
|
915
|
+
if (!str1.length || !str2.length)
|
|
916
|
+
return 0;
|
|
917
|
+
if (str1.toUpperCase() === str2.toUpperCase())
|
|
918
|
+
return 1;
|
|
919
|
+
if (str1.length === 1 && str2.length === 1)
|
|
920
|
+
return 0;
|
|
921
|
+
const pairs1 = wordLetterPairs(str1);
|
|
922
|
+
const pairs2 = wordLetterPairs(str2);
|
|
923
|
+
const union = pairs1.length + pairs2.length;
|
|
924
|
+
let intersection = 0;
|
|
925
|
+
pairs1.forEach((pair1) => {
|
|
926
|
+
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
927
|
+
if (pair1 !== pair2)
|
|
928
|
+
continue;
|
|
929
|
+
intersection++;
|
|
930
|
+
pairs2.splice(i, 1);
|
|
931
|
+
break;
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
return (intersection * 2) / union;
|
|
935
|
+
}
|
|
936
|
+
function findBestMatch(mainString, targetStrings) {
|
|
937
|
+
if (!areArgsValid(mainString, targetStrings))
|
|
938
|
+
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
939
|
+
const ratings = targetStrings.map((target) => ({
|
|
940
|
+
target,
|
|
941
|
+
rating: compareTwoStrings(mainString, target.value),
|
|
942
|
+
}));
|
|
943
|
+
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
944
|
+
return { ratings, bestMatch };
|
|
945
|
+
}
|
|
946
|
+
function flattenDeep(arr) {
|
|
947
|
+
return Array.isArray(arr)
|
|
948
|
+
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
949
|
+
: [arr];
|
|
950
|
+
}
|
|
951
|
+
function areArgsValid(mainString, targetStrings) {
|
|
952
|
+
if (typeof mainString !== 'string')
|
|
953
|
+
return false;
|
|
954
|
+
if (!Array.isArray(targetStrings))
|
|
955
|
+
return false;
|
|
956
|
+
if (!targetStrings.length)
|
|
957
|
+
return false;
|
|
958
|
+
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
959
|
+
return false;
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
function letterPairs(str) {
|
|
963
|
+
const pairs = [];
|
|
964
|
+
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
965
|
+
pairs[i] = str.substring(i, i + 2);
|
|
966
|
+
return pairs;
|
|
967
|
+
}
|
|
968
|
+
function wordLetterPairs(str) {
|
|
969
|
+
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
970
|
+
return flattenDeep(pairs);
|
|
971
|
+
}
|
|
972
|
+
function safeString(obj) {
|
|
973
|
+
if (obj != null && typeof obj.toString === 'function') {
|
|
974
|
+
return `${obj}`;
|
|
975
|
+
}
|
|
976
|
+
return inspect(obj);
|
|
977
|
+
}
|
|
978
|
+
|
|
896
979
|
function fieldArgumentDescriptionChanged(type, field, oldArg, newArg) {
|
|
897
980
|
return {
|
|
898
981
|
criticality: {
|
|
@@ -911,8 +994,8 @@ function fieldArgumentDefaultChanged(type, field, oldArg, newArg) {
|
|
|
911
994
|
},
|
|
912
995
|
type: exports.ChangeType.FieldArgumentDefaultChanged,
|
|
913
996
|
message: typeof oldArg.defaultValue === 'undefined'
|
|
914
|
-
? `Default value '${newArg.defaultValue}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
915
|
-
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.defaultValue}' to '${newArg.defaultValue}'`,
|
|
997
|
+
? `Default value '${safeString(newArg.defaultValue)}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
998
|
+
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${safeString(oldArg.defaultValue)}' to '${safeString(newArg.defaultValue)}'`,
|
|
916
999
|
path: [type.name, field.name, oldArg.name].join('.'),
|
|
917
1000
|
};
|
|
918
1001
|
}
|
|
@@ -967,8 +1050,8 @@ function changesInField(type, oldField, newField, addChange) {
|
|
|
967
1050
|
addChange(fieldDescriptionChanged(type, oldField, newField));
|
|
968
1051
|
}
|
|
969
1052
|
}
|
|
970
|
-
if (isNotEqual(oldField
|
|
971
|
-
if (newField
|
|
1053
|
+
if (isNotEqual(isDeprecated(oldField), isDeprecated(newField))) {
|
|
1054
|
+
if (isDeprecated(newField)) {
|
|
972
1055
|
addChange(fieldDeprecationAdded(type, newField));
|
|
973
1056
|
}
|
|
974
1057
|
else {
|
|
@@ -1190,7 +1273,7 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
|
1190
1273
|
const type = oldSchema.getType(typeName);
|
|
1191
1274
|
if (graphql.isObjectType(type) || graphql.isInterfaceType(type)) {
|
|
1192
1275
|
const field = type.getFields()[fieldName];
|
|
1193
|
-
if (field
|
|
1276
|
+
if (isDeprecated(field)) {
|
|
1194
1277
|
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1195
1278
|
}
|
|
1196
1279
|
}
|
|
@@ -1202,7 +1285,7 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
|
1202
1285
|
const type = oldSchema.getType(enumName);
|
|
1203
1286
|
if (graphql.isEnumType(type)) {
|
|
1204
1287
|
const item = type.getValue(enumItem);
|
|
1205
|
-
if (item && item
|
|
1288
|
+
if (item && isDeprecated(item)) {
|
|
1206
1289
|
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
|
|
1207
1290
|
}
|
|
1208
1291
|
}
|
|
@@ -1444,7 +1527,7 @@ function validate(schema, sources, options) {
|
|
|
1444
1527
|
.filter((doc) => doc.hasOperations)
|
|
1445
1528
|
.forEach((doc) => {
|
|
1446
1529
|
const docWithOperations = {
|
|
1447
|
-
kind:
|
|
1530
|
+
kind: graphql.Kind.DOCUMENT,
|
|
1448
1531
|
definitions: doc.operations.map((d) => d.node),
|
|
1449
1532
|
};
|
|
1450
1533
|
const extractedFragments = (extractFragments(graphql.print(docWithOperations)) || [])
|
|
@@ -1455,7 +1538,7 @@ function validate(schema, sources, options) {
|
|
|
1455
1538
|
// remove duplicates
|
|
1456
1539
|
.filter((def, i, all) => all.findIndex((item) => item.name.value === def.name.value) === i);
|
|
1457
1540
|
const merged = {
|
|
1458
|
-
kind:
|
|
1541
|
+
kind: graphql.Kind.DOCUMENT,
|
|
1459
1542
|
definitions: [...docWithOperations.definitions, ...extractedFragments],
|
|
1460
1543
|
};
|
|
1461
1544
|
let transformedSchema = config.apollo
|
|
@@ -1518,67 +1601,6 @@ function sumLengths(...arrays) {
|
|
|
1518
1601
|
return arrays.reduce((sum, { length }) => sum + length, 0);
|
|
1519
1602
|
}
|
|
1520
1603
|
|
|
1521
|
-
function compareTwoStrings(str1, str2) {
|
|
1522
|
-
if (!str1.length && !str2.length)
|
|
1523
|
-
return 1;
|
|
1524
|
-
if (!str1.length || !str2.length)
|
|
1525
|
-
return 0;
|
|
1526
|
-
if (str1.toUpperCase() === str2.toUpperCase())
|
|
1527
|
-
return 1;
|
|
1528
|
-
if (str1.length === 1 && str2.length === 1)
|
|
1529
|
-
return 0;
|
|
1530
|
-
const pairs1 = wordLetterPairs(str1);
|
|
1531
|
-
const pairs2 = wordLetterPairs(str2);
|
|
1532
|
-
const union = pairs1.length + pairs2.length;
|
|
1533
|
-
let intersection = 0;
|
|
1534
|
-
pairs1.forEach((pair1) => {
|
|
1535
|
-
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
1536
|
-
if (pair1 !== pair2)
|
|
1537
|
-
continue;
|
|
1538
|
-
intersection++;
|
|
1539
|
-
pairs2.splice(i, 1);
|
|
1540
|
-
break;
|
|
1541
|
-
}
|
|
1542
|
-
});
|
|
1543
|
-
return (intersection * 2) / union;
|
|
1544
|
-
}
|
|
1545
|
-
function findBestMatch(mainString, targetStrings) {
|
|
1546
|
-
if (!areArgsValid(mainString, targetStrings))
|
|
1547
|
-
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
1548
|
-
const ratings = targetStrings.map((target) => ({
|
|
1549
|
-
target,
|
|
1550
|
-
rating: compareTwoStrings(mainString, target.value),
|
|
1551
|
-
}));
|
|
1552
|
-
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
1553
|
-
return { ratings, bestMatch };
|
|
1554
|
-
}
|
|
1555
|
-
function flattenDeep(arr) {
|
|
1556
|
-
return Array.isArray(arr)
|
|
1557
|
-
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
1558
|
-
: [arr];
|
|
1559
|
-
}
|
|
1560
|
-
function areArgsValid(mainString, targetStrings) {
|
|
1561
|
-
if (typeof mainString !== 'string')
|
|
1562
|
-
return false;
|
|
1563
|
-
if (!Array.isArray(targetStrings))
|
|
1564
|
-
return false;
|
|
1565
|
-
if (!targetStrings.length)
|
|
1566
|
-
return false;
|
|
1567
|
-
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
1568
|
-
return false;
|
|
1569
|
-
return true;
|
|
1570
|
-
}
|
|
1571
|
-
function letterPairs(str) {
|
|
1572
|
-
const pairs = [];
|
|
1573
|
-
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
1574
|
-
pairs[i] = str.substring(i, i + 2);
|
|
1575
|
-
return pairs;
|
|
1576
|
-
}
|
|
1577
|
-
function wordLetterPairs(str) {
|
|
1578
|
-
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
1579
|
-
return flattenDeep(pairs);
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
1604
|
function similar(schema, typeName, threshold = 0.4) {
|
|
1583
1605
|
const typeMap = schema.getTypeMap();
|
|
1584
1606
|
const targets = Object.keys(schema.getTypeMap())
|
package/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { __awaiter } from 'tslib';
|
|
2
2
|
import { Kind, TypeInfo, visit, visitWithTypeInfo, GraphQLError, getNamedType, isWrappingType, isListType, isNonNullType, isInterfaceType, isEnumType, isUnionType, isInputObjectType, isObjectType, isScalarType, parse, extendSchema, print, validate as validate$1, printType } from 'graphql';
|
|
3
|
+
import inspect from 'object-inspect';
|
|
3
4
|
import { DepGraph } from 'dependency-graph';
|
|
4
|
-
import 'object-inspect';
|
|
5
5
|
|
|
6
6
|
function keyMap(list, keyFn) {
|
|
7
7
|
return list.reduce((map, item) => {
|
|
@@ -72,6 +72,20 @@ function compareLists(oldList, newList, callbacks) {
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
function isDeprecated(fieldOrEnumValue) {
|
|
76
|
+
var _a, _b;
|
|
77
|
+
if ('isDeprecated' in fieldOrEnumValue) {
|
|
78
|
+
return fieldOrEnumValue['isDeprecated'];
|
|
79
|
+
}
|
|
80
|
+
if (fieldOrEnumValue.deprecationReason != null) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
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")) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
75
89
|
function safeChangeForField(oldType, newType) {
|
|
76
90
|
if (!isWrappingType(oldType) && !isWrappingType(newType)) {
|
|
77
91
|
return oldType.toString() === newType.toString();
|
|
@@ -114,7 +128,7 @@ function getTypePrefix(type) {
|
|
|
114
128
|
[Kind.ENUM_TYPE_DEFINITION]: 'enum',
|
|
115
129
|
[Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
|
|
116
130
|
};
|
|
117
|
-
return kindsMap[kind];
|
|
131
|
+
return kindsMap[kind.toString()];
|
|
118
132
|
}
|
|
119
133
|
function isPrimitive(type) {
|
|
120
134
|
return (['String', 'Int', 'Float', 'Boolean', 'ID'].indexOf(typeof type === 'string' ? type : type.name) !== -1);
|
|
@@ -149,7 +163,7 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
149
163
|
},
|
|
150
164
|
Field(node) {
|
|
151
165
|
const fieldDef = typeInfo.getFieldDef();
|
|
152
|
-
if (fieldDef && fieldDef
|
|
166
|
+
if (fieldDef && isDeprecated(fieldDef)) {
|
|
153
167
|
const parentType = typeInfo.getParentType();
|
|
154
168
|
if (parentType) {
|
|
155
169
|
const reason = fieldDef.deprecationReason;
|
|
@@ -159,7 +173,7 @@ function findDeprecatedUsages(schema, ast) {
|
|
|
159
173
|
},
|
|
160
174
|
EnumValue(node) {
|
|
161
175
|
const enumVal = typeInfo.getEnumValue();
|
|
162
|
-
if (enumVal && enumVal
|
|
176
|
+
if (enumVal && isDeprecated(enumVal)) {
|
|
163
177
|
const type = getNamedType(typeInfo.getInputType());
|
|
164
178
|
if (type) {
|
|
165
179
|
const reason = enumVal.deprecationReason;
|
|
@@ -472,7 +486,7 @@ function enumValueRemoved(oldEnum, value) {
|
|
|
472
486
|
reason: `Removing an enum value will cause existing queries that use this enum value to error.`,
|
|
473
487
|
},
|
|
474
488
|
type: ChangeType.EnumValueRemoved,
|
|
475
|
-
message: `Enum value '${value.name}' ${value
|
|
489
|
+
message: `Enum value '${value.name}' ${isDeprecated(value) ? '(deprecated) ' : ''}was removed from enum '${oldEnum.name}'`,
|
|
476
490
|
path: [oldEnum.name, value.name].join('.'),
|
|
477
491
|
};
|
|
478
492
|
}
|
|
@@ -756,7 +770,7 @@ function fieldRemoved(type, field) {
|
|
|
756
770
|
: `Removing a field is a breaking change. It is preferable to deprecate the field before removing it.`,
|
|
757
771
|
},
|
|
758
772
|
type: ChangeType.FieldRemoved,
|
|
759
|
-
message: `Field '${field.name}' ${field
|
|
773
|
+
message: `Field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from ${entity} '${type.name}'`,
|
|
760
774
|
path: [type.name, field.name].join('.'),
|
|
761
775
|
};
|
|
762
776
|
}
|
|
@@ -891,6 +905,73 @@ function fieldArgumentRemoved(type, field, arg) {
|
|
|
891
905
|
};
|
|
892
906
|
}
|
|
893
907
|
|
|
908
|
+
function compareTwoStrings(str1, str2) {
|
|
909
|
+
if (!str1.length && !str2.length)
|
|
910
|
+
return 1;
|
|
911
|
+
if (!str1.length || !str2.length)
|
|
912
|
+
return 0;
|
|
913
|
+
if (str1.toUpperCase() === str2.toUpperCase())
|
|
914
|
+
return 1;
|
|
915
|
+
if (str1.length === 1 && str2.length === 1)
|
|
916
|
+
return 0;
|
|
917
|
+
const pairs1 = wordLetterPairs(str1);
|
|
918
|
+
const pairs2 = wordLetterPairs(str2);
|
|
919
|
+
const union = pairs1.length + pairs2.length;
|
|
920
|
+
let intersection = 0;
|
|
921
|
+
pairs1.forEach((pair1) => {
|
|
922
|
+
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
923
|
+
if (pair1 !== pair2)
|
|
924
|
+
continue;
|
|
925
|
+
intersection++;
|
|
926
|
+
pairs2.splice(i, 1);
|
|
927
|
+
break;
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
return (intersection * 2) / union;
|
|
931
|
+
}
|
|
932
|
+
function findBestMatch(mainString, targetStrings) {
|
|
933
|
+
if (!areArgsValid(mainString, targetStrings))
|
|
934
|
+
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
935
|
+
const ratings = targetStrings.map((target) => ({
|
|
936
|
+
target,
|
|
937
|
+
rating: compareTwoStrings(mainString, target.value),
|
|
938
|
+
}));
|
|
939
|
+
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
940
|
+
return { ratings, bestMatch };
|
|
941
|
+
}
|
|
942
|
+
function flattenDeep(arr) {
|
|
943
|
+
return Array.isArray(arr)
|
|
944
|
+
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
945
|
+
: [arr];
|
|
946
|
+
}
|
|
947
|
+
function areArgsValid(mainString, targetStrings) {
|
|
948
|
+
if (typeof mainString !== 'string')
|
|
949
|
+
return false;
|
|
950
|
+
if (!Array.isArray(targetStrings))
|
|
951
|
+
return false;
|
|
952
|
+
if (!targetStrings.length)
|
|
953
|
+
return false;
|
|
954
|
+
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
955
|
+
return false;
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
function letterPairs(str) {
|
|
959
|
+
const pairs = [];
|
|
960
|
+
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
961
|
+
pairs[i] = str.substring(i, i + 2);
|
|
962
|
+
return pairs;
|
|
963
|
+
}
|
|
964
|
+
function wordLetterPairs(str) {
|
|
965
|
+
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
966
|
+
return flattenDeep(pairs);
|
|
967
|
+
}
|
|
968
|
+
function safeString(obj) {
|
|
969
|
+
if (obj != null && typeof obj.toString === 'function') {
|
|
970
|
+
return `${obj}`;
|
|
971
|
+
}
|
|
972
|
+
return inspect(obj);
|
|
973
|
+
}
|
|
974
|
+
|
|
894
975
|
function fieldArgumentDescriptionChanged(type, field, oldArg, newArg) {
|
|
895
976
|
return {
|
|
896
977
|
criticality: {
|
|
@@ -909,8 +990,8 @@ function fieldArgumentDefaultChanged(type, field, oldArg, newArg) {
|
|
|
909
990
|
},
|
|
910
991
|
type: ChangeType.FieldArgumentDefaultChanged,
|
|
911
992
|
message: typeof oldArg.defaultValue === 'undefined'
|
|
912
|
-
? `Default value '${newArg.defaultValue}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
913
|
-
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.defaultValue}' to '${newArg.defaultValue}'`,
|
|
993
|
+
? `Default value '${safeString(newArg.defaultValue)}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
994
|
+
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${safeString(oldArg.defaultValue)}' to '${safeString(newArg.defaultValue)}'`,
|
|
914
995
|
path: [type.name, field.name, oldArg.name].join('.'),
|
|
915
996
|
};
|
|
916
997
|
}
|
|
@@ -965,8 +1046,8 @@ function changesInField(type, oldField, newField, addChange) {
|
|
|
965
1046
|
addChange(fieldDescriptionChanged(type, oldField, newField));
|
|
966
1047
|
}
|
|
967
1048
|
}
|
|
968
|
-
if (isNotEqual(oldField
|
|
969
|
-
if (newField
|
|
1049
|
+
if (isNotEqual(isDeprecated(oldField), isDeprecated(newField))) {
|
|
1050
|
+
if (isDeprecated(newField)) {
|
|
970
1051
|
addChange(fieldDeprecationAdded(type, newField));
|
|
971
1052
|
}
|
|
972
1053
|
else {
|
|
@@ -1188,7 +1269,7 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
|
1188
1269
|
const type = oldSchema.getType(typeName);
|
|
1189
1270
|
if (isObjectType(type) || isInterfaceType(type)) {
|
|
1190
1271
|
const field = type.getFields()[fieldName];
|
|
1191
|
-
if (field
|
|
1272
|
+
if (isDeprecated(field)) {
|
|
1192
1273
|
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.Dangerous }) });
|
|
1193
1274
|
}
|
|
1194
1275
|
}
|
|
@@ -1200,7 +1281,7 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
|
|
|
1200
1281
|
const type = oldSchema.getType(enumName);
|
|
1201
1282
|
if (isEnumType(type)) {
|
|
1202
1283
|
const item = type.getValue(enumItem);
|
|
1203
|
-
if (item && item
|
|
1284
|
+
if (item && isDeprecated(item)) {
|
|
1204
1285
|
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.Dangerous }) });
|
|
1205
1286
|
}
|
|
1206
1287
|
}
|
|
@@ -1442,7 +1523,7 @@ function validate(schema, sources, options) {
|
|
|
1442
1523
|
.filter((doc) => doc.hasOperations)
|
|
1443
1524
|
.forEach((doc) => {
|
|
1444
1525
|
const docWithOperations = {
|
|
1445
|
-
kind:
|
|
1526
|
+
kind: Kind.DOCUMENT,
|
|
1446
1527
|
definitions: doc.operations.map((d) => d.node),
|
|
1447
1528
|
};
|
|
1448
1529
|
const extractedFragments = (extractFragments(print(docWithOperations)) || [])
|
|
@@ -1453,7 +1534,7 @@ function validate(schema, sources, options) {
|
|
|
1453
1534
|
// remove duplicates
|
|
1454
1535
|
.filter((def, i, all) => all.findIndex((item) => item.name.value === def.name.value) === i);
|
|
1455
1536
|
const merged = {
|
|
1456
|
-
kind:
|
|
1537
|
+
kind: Kind.DOCUMENT,
|
|
1457
1538
|
definitions: [...docWithOperations.definitions, ...extractedFragments],
|
|
1458
1539
|
};
|
|
1459
1540
|
let transformedSchema = config.apollo
|
|
@@ -1516,67 +1597,6 @@ function sumLengths(...arrays) {
|
|
|
1516
1597
|
return arrays.reduce((sum, { length }) => sum + length, 0);
|
|
1517
1598
|
}
|
|
1518
1599
|
|
|
1519
|
-
function compareTwoStrings(str1, str2) {
|
|
1520
|
-
if (!str1.length && !str2.length)
|
|
1521
|
-
return 1;
|
|
1522
|
-
if (!str1.length || !str2.length)
|
|
1523
|
-
return 0;
|
|
1524
|
-
if (str1.toUpperCase() === str2.toUpperCase())
|
|
1525
|
-
return 1;
|
|
1526
|
-
if (str1.length === 1 && str2.length === 1)
|
|
1527
|
-
return 0;
|
|
1528
|
-
const pairs1 = wordLetterPairs(str1);
|
|
1529
|
-
const pairs2 = wordLetterPairs(str2);
|
|
1530
|
-
const union = pairs1.length + pairs2.length;
|
|
1531
|
-
let intersection = 0;
|
|
1532
|
-
pairs1.forEach((pair1) => {
|
|
1533
|
-
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
1534
|
-
if (pair1 !== pair2)
|
|
1535
|
-
continue;
|
|
1536
|
-
intersection++;
|
|
1537
|
-
pairs2.splice(i, 1);
|
|
1538
|
-
break;
|
|
1539
|
-
}
|
|
1540
|
-
});
|
|
1541
|
-
return (intersection * 2) / union;
|
|
1542
|
-
}
|
|
1543
|
-
function findBestMatch(mainString, targetStrings) {
|
|
1544
|
-
if (!areArgsValid(mainString, targetStrings))
|
|
1545
|
-
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
1546
|
-
const ratings = targetStrings.map((target) => ({
|
|
1547
|
-
target,
|
|
1548
|
-
rating: compareTwoStrings(mainString, target.value),
|
|
1549
|
-
}));
|
|
1550
|
-
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
1551
|
-
return { ratings, bestMatch };
|
|
1552
|
-
}
|
|
1553
|
-
function flattenDeep(arr) {
|
|
1554
|
-
return Array.isArray(arr)
|
|
1555
|
-
? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
|
|
1556
|
-
: [arr];
|
|
1557
|
-
}
|
|
1558
|
-
function areArgsValid(mainString, targetStrings) {
|
|
1559
|
-
if (typeof mainString !== 'string')
|
|
1560
|
-
return false;
|
|
1561
|
-
if (!Array.isArray(targetStrings))
|
|
1562
|
-
return false;
|
|
1563
|
-
if (!targetStrings.length)
|
|
1564
|
-
return false;
|
|
1565
|
-
if (targetStrings.find((s) => typeof s.value !== 'string'))
|
|
1566
|
-
return false;
|
|
1567
|
-
return true;
|
|
1568
|
-
}
|
|
1569
|
-
function letterPairs(str) {
|
|
1570
|
-
const pairs = [];
|
|
1571
|
-
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
1572
|
-
pairs[i] = str.substring(i, i + 2);
|
|
1573
|
-
return pairs;
|
|
1574
|
-
}
|
|
1575
|
-
function wordLetterPairs(str) {
|
|
1576
|
-
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
1577
|
-
return flattenDeep(pairs);
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
1600
|
function similar(schema, typeName, threshold = 0.4) {
|
|
1581
1601
|
const typeMap = schema.getTypeMap();
|
|
1582
1602
|
const targets = Object.keys(schema.getTypeMap())
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphql-inspector/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Tooling for GraphQL. Compare GraphQL Schemas, check documents, find breaking changes, find similar types.",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
7
|
-
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0"
|
|
7
|
+
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"dependency-graph": "0.11.0",
|
package/utils/compare.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ export declare function keyMap<T>(list: readonly T[], keyFn: (item: T) => string
|
|
|
2
2
|
export declare function isEqual<T>(a: T, b: T): boolean;
|
|
3
3
|
export declare function isNotEqual<T>(a: T, b: T): boolean;
|
|
4
4
|
export declare function isVoid<T>(a: T): boolean;
|
|
5
|
-
export declare function diffArrays(a:
|
|
6
|
-
export declare function unionArrays(a:
|
|
5
|
+
export declare function diffArrays<T>(a: T[] | readonly T[], b: T[] | readonly T[]): T[];
|
|
6
|
+
export declare function unionArrays<T>(a: T[] | readonly T[], b: T[] | readonly T[]): T[];
|
|
7
7
|
export declare function compareLists<T extends {
|
|
8
8
|
name: string;
|
|
9
9
|
}>(oldList: readonly T[], newList: readonly T[], callbacks?: {
|