@graphql-inspector/core 4.0.2 → 4.0.3
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/cjs/ast/document.js +40 -0
- package/cjs/coverage/index.js +128 -0
- package/cjs/coverage/output/json.js +7 -0
- package/cjs/diff/argument.js +25 -0
- package/cjs/diff/changes/argument.js +48 -0
- package/cjs/diff/changes/change.js +75 -0
- package/cjs/diff/changes/directive.js +124 -0
- package/cjs/diff/changes/enum.js +75 -0
- package/cjs/diff/changes/field.js +166 -0
- package/cjs/diff/changes/input.js +98 -0
- package/cjs/diff/changes/object.js +28 -0
- package/cjs/diff/changes/schema.js +40 -0
- package/cjs/diff/changes/type.js +72 -0
- package/cjs/diff/changes/union.js +28 -0
- package/cjs/diff/directive.js +41 -0
- package/cjs/diff/enum.js +34 -0
- package/cjs/diff/field.js +54 -0
- package/cjs/diff/index.js +22 -0
- package/cjs/diff/input.js +47 -0
- package/cjs/diff/interface.js +20 -0
- package/cjs/diff/object.js +33 -0
- package/cjs/diff/onComplete/types.js +0 -0
- package/cjs/diff/rules/config.js +0 -0
- package/cjs/diff/rules/consider-usage.js +39 -0
- package/cjs/diff/rules/dangerous-breaking.js +13 -0
- package/cjs/diff/rules/ignore-description-changes.js +21 -0
- package/cjs/diff/rules/index.js +8 -0
- package/cjs/diff/rules/safe-unreachable.js +19 -0
- package/cjs/diff/rules/suppress-removal-of-deprecated-field.js +58 -0
- package/cjs/diff/rules/types.js +0 -0
- package/cjs/diff/schema.js +107 -0
- package/cjs/diff/union.js +18 -0
- package/cjs/index.js +27 -0
- package/cjs/package.json +1 -0
- package/cjs/similar/index.js +57 -0
- package/cjs/utils/apollo.js +21 -0
- package/cjs/utils/compare.js +91 -0
- package/cjs/utils/graphql.js +194 -0
- package/cjs/utils/is-deprecated.js +17 -0
- package/cjs/utils/path.js +7 -0
- package/cjs/utils/string.js +70 -0
- package/cjs/validate/alias-count.js +37 -0
- package/cjs/validate/complexity.js +39 -0
- package/cjs/validate/directive-count.js +37 -0
- package/cjs/validate/index.js +143 -0
- package/cjs/validate/query-depth.js +103 -0
- package/cjs/validate/token-count.js +55 -0
- package/esm/ast/document.js +36 -0
- package/esm/coverage/index.js +124 -0
- package/esm/coverage/output/json.js +3 -0
- package/esm/diff/argument.js +21 -0
- package/esm/diff/changes/argument.js +42 -0
- package/esm/diff/changes/change.js +72 -0
- package/esm/diff/changes/directive.js +111 -0
- package/esm/diff/changes/enum.js +66 -0
- package/esm/diff/changes/field.js +150 -0
- package/esm/diff/changes/input.js +88 -0
- package/esm/diff/changes/object.js +23 -0
- package/esm/diff/changes/schema.js +34 -0
- package/esm/diff/changes/type.js +63 -0
- package/esm/diff/changes/union.js +23 -0
- package/esm/diff/directive.js +37 -0
- package/esm/diff/enum.js +30 -0
- package/esm/diff/field.js +50 -0
- package/esm/diff/index.js +18 -0
- package/esm/diff/input.js +43 -0
- package/esm/diff/interface.js +16 -0
- package/esm/diff/object.js +29 -0
- package/esm/diff/onComplete/types.js +0 -0
- package/esm/diff/rules/config.js +0 -0
- package/esm/diff/rules/consider-usage.js +35 -0
- package/esm/diff/rules/dangerous-breaking.js +9 -0
- package/esm/diff/rules/ignore-description-changes.js +17 -0
- package/esm/diff/rules/index.js +5 -0
- package/esm/diff/rules/safe-unreachable.js +15 -0
- package/esm/diff/rules/suppress-removal-of-deprecated-field.js +54 -0
- package/esm/diff/rules/types.js +0 -0
- package/esm/diff/schema.js +103 -0
- package/esm/diff/union.js +14 -0
- package/esm/index.js +11 -0
- package/esm/similar/index.js +53 -0
- package/esm/utils/apollo.js +16 -0
- package/esm/utils/compare.js +82 -0
- package/esm/utils/graphql.js +181 -0
- package/esm/utils/is-deprecated.js +13 -0
- package/esm/utils/path.js +3 -0
- package/esm/utils/string.js +64 -0
- package/esm/validate/alias-count.js +32 -0
- package/esm/validate/complexity.js +34 -0
- package/esm/validate/directive-count.js +32 -0
- package/esm/validate/index.js +139 -0
- package/esm/validate/query-depth.js +97 -0
- package/esm/validate/token-count.js +50 -0
- package/package.json +30 -9
- package/typings/ast/document.d.ts +15 -0
- package/typings/coverage/index.d.ts +51 -0
- package/typings/coverage/output/json.d.cts +2 -0
- package/{coverage → typings/coverage}/output/json.d.ts +1 -1
- package/typings/diff/argument.d.cts +3 -0
- package/{diff → typings/diff}/argument.d.ts +1 -1
- package/typings/diff/changes/argument.d.cts +5 -0
- package/{diff → typings/diff}/changes/argument.d.ts +1 -1
- package/typings/diff/changes/change.d.ts +69 -0
- package/typings/diff/changes/directive.d.cts +12 -0
- package/{diff → typings/diff}/changes/directive.d.ts +1 -1
- package/typings/diff/changes/enum.d.cts +8 -0
- package/{diff → typings/diff}/changes/enum.d.ts +1 -1
- package/typings/diff/changes/field.d.cts +15 -0
- package/{diff → typings/diff}/changes/field.d.ts +1 -1
- package/typings/diff/changes/input.d.cts +9 -0
- package/{diff → typings/diff}/changes/input.d.ts +1 -1
- package/typings/diff/changes/object.d.cts +4 -0
- package/{diff → typings/diff}/changes/object.d.ts +1 -1
- package/typings/diff/changes/schema.d.cts +5 -0
- package/{diff → typings/diff}/changes/schema.d.ts +1 -1
- package/typings/diff/changes/type.d.cts +8 -0
- package/{diff → typings/diff}/changes/type.d.ts +1 -1
- package/typings/diff/changes/union.d.cts +4 -0
- package/{diff → typings/diff}/changes/union.d.ts +1 -1
- package/typings/diff/directive.d.cts +3 -0
- package/{diff → typings/diff}/directive.d.ts +1 -1
- package/typings/diff/enum.d.cts +3 -0
- package/{diff → typings/diff}/enum.d.ts +1 -1
- package/typings/diff/field.d.cts +3 -0
- package/{diff → typings/diff}/field.d.ts +1 -1
- package/typings/diff/index.d.cts +9 -0
- package/typings/diff/index.d.ts +9 -0
- package/typings/diff/input.d.cts +3 -0
- package/{diff → typings/diff}/input.d.ts +1 -1
- package/typings/diff/interface.d.cts +3 -0
- package/{diff → typings/diff}/interface.d.ts +1 -1
- package/typings/diff/object.d.cts +3 -0
- package/{diff → typings/diff}/object.d.ts +1 -1
- package/typings/diff/onComplete/types.d.cts +7 -0
- package/typings/diff/onComplete/types.d.ts +7 -0
- package/typings/diff/rules/config.d.cts +2 -0
- package/typings/diff/rules/config.d.ts +2 -0
- package/typings/diff/rules/consider-usage.d.cts +29 -0
- package/{diff → typings/diff}/rules/consider-usage.d.ts +2 -2
- package/typings/diff/rules/dangerous-breaking.d.cts +2 -0
- package/{diff → typings/diff}/rules/dangerous-breaking.d.ts +1 -1
- package/typings/diff/rules/ignore-description-changes.d.cts +2 -0
- package/{diff → typings/diff}/rules/ignore-description-changes.d.ts +1 -1
- package/typings/diff/rules/index.d.cts +5 -0
- package/typings/diff/rules/index.d.ts +5 -0
- package/typings/diff/rules/safe-unreachable.d.cts +2 -0
- package/{diff → typings/diff}/rules/safe-unreachable.d.ts +1 -1
- package/typings/diff/rules/suppress-removal-of-deprecated-field.d.cts +2 -0
- package/{diff → typings/diff}/rules/suppress-removal-of-deprecated-field.d.ts +1 -1
- package/typings/diff/rules/types.d.cts +8 -0
- package/{diff → typings/diff}/rules/types.d.ts +2 -2
- package/typings/diff/schema.d.cts +4 -0
- package/{diff → typings/diff}/schema.d.ts +2 -2
- package/typings/diff/union.d.cts +3 -0
- package/{diff → typings/diff}/union.d.ts +1 -1
- package/typings/index.d.cts +12 -0
- package/typings/index.d.ts +12 -0
- package/typings/similar/index.d.cts +6 -0
- package/{similar → typings/similar}/index.d.ts +1 -1
- package/typings/utils/apollo.d.ts +5 -0
- package/typings/utils/compare.d.ts +22 -0
- package/typings/utils/graphql.d.ts +11 -0
- package/typings/utils/is-deprecated.d.cts +2 -0
- package/{utils → typings/utils}/is-deprecated.d.ts +1 -1
- package/typings/utils/path.d.ts +1 -0
- package/typings/utils/string.d.ts +14 -0
- package/typings/validate/alias-count.d.ts +10 -0
- package/typings/validate/complexity.d.cts +16 -0
- package/{validate → typings/validate}/complexity.d.ts +1 -1
- package/typings/validate/directive-count.d.ts +10 -0
- package/typings/validate/index.d.ts +49 -0
- package/typings/validate/query-depth.d.ts +15 -0
- package/typings/validate/token-count.d.ts +12 -0
- package/diff/index.d.ts +0 -9
- package/diff/onComplete/types.d.ts +0 -7
- package/diff/rules/config.d.ts +0 -2
- package/diff/rules/index.d.ts +0 -5
- package/index.d.ts +0 -12
- package/index.js +0 -2075
- package/index.mjs +0 -2061
- /package/{ast/document.d.ts → typings/ast/document.d.cts} +0 -0
- /package/{coverage/index.d.ts → typings/coverage/index.d.cts} +0 -0
- /package/{diff/changes/change.d.ts → typings/diff/changes/change.d.cts} +0 -0
- /package/{utils/apollo.d.ts → typings/utils/apollo.d.cts} +0 -0
- /package/{utils/compare.d.ts → typings/utils/compare.d.cts} +0 -0
- /package/{utils/graphql.d.ts → typings/utils/graphql.d.cts} +0 -0
- /package/{utils/path.d.ts → typings/utils/path.d.cts} +0 -0
- /package/{utils/string.d.ts → typings/utils/string.d.cts} +0 -0
- /package/{validate/alias-count.d.ts → typings/validate/alias-count.d.cts} +0 -0
- /package/{validate/directive-count.d.ts → typings/validate/directive-count.d.cts} +0 -0
- /package/{validate/index.d.ts → typings/validate/index.d.cts} +0 -0
- /package/{validate/query-depth.d.ts → typings/validate/query-depth.d.cts} +0 -0
- /package/{validate/token-count.d.ts → typings/validate/token-count.d.cts} +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ChangeType } from '../changes/change.js';
|
|
2
|
+
const descriptionChangeTypes = [
|
|
3
|
+
ChangeType.FieldArgumentDescriptionChanged,
|
|
4
|
+
ChangeType.DirectiveDescriptionChanged,
|
|
5
|
+
ChangeType.DirectiveArgumentDescriptionChanged,
|
|
6
|
+
ChangeType.EnumValueDescriptionChanged,
|
|
7
|
+
ChangeType.FieldDescriptionChanged,
|
|
8
|
+
ChangeType.FieldDescriptionAdded,
|
|
9
|
+
ChangeType.FieldDescriptionRemoved,
|
|
10
|
+
ChangeType.InputFieldDescriptionAdded,
|
|
11
|
+
ChangeType.InputFieldDescriptionRemoved,
|
|
12
|
+
ChangeType.InputFieldDescriptionChanged,
|
|
13
|
+
ChangeType.TypeDescriptionChanged,
|
|
14
|
+
];
|
|
15
|
+
export const ignoreDescriptionChanges = ({ changes }) => {
|
|
16
|
+
return changes.filter(change => !descriptionChangeTypes.includes(change.type));
|
|
17
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getReachableTypes } from '../../utils/graphql.js';
|
|
2
|
+
import { parsePath } from '../../utils/path.js';
|
|
3
|
+
import { CriticalityLevel } from './../changes/change.js';
|
|
4
|
+
export const safeUnreachable = ({ changes, oldSchema }) => {
|
|
5
|
+
const reachable = getReachableTypes(oldSchema);
|
|
6
|
+
return changes.map(change => {
|
|
7
|
+
if (change.criticality.level === CriticalityLevel.Breaking && change.path) {
|
|
8
|
+
const [typeName] = parsePath(change.path);
|
|
9
|
+
if (!reachable.has(typeName)) {
|
|
10
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.NonBreaking }), message: 'Unreachable from root' });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return change;
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { isEnumType, isInputObjectType, isInterfaceType, isObjectType } from 'graphql';
|
|
2
|
+
import { isDeprecated } from '../../utils/is-deprecated.js';
|
|
3
|
+
import { parsePath } from '../../utils/path.js';
|
|
4
|
+
import { ChangeType, CriticalityLevel } from './../changes/change.js';
|
|
5
|
+
export const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, newSchema }) => {
|
|
6
|
+
return changes.map(change => {
|
|
7
|
+
if (change.type === ChangeType.FieldRemoved &&
|
|
8
|
+
change.criticality.level === CriticalityLevel.Breaking &&
|
|
9
|
+
change.path) {
|
|
10
|
+
const [typeName, fieldName] = parsePath(change.path);
|
|
11
|
+
const type = oldSchema.getType(typeName);
|
|
12
|
+
if (isObjectType(type) || isInterfaceType(type)) {
|
|
13
|
+
const field = type.getFields()[fieldName];
|
|
14
|
+
if (isDeprecated(field)) {
|
|
15
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.Dangerous }) });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (change.type === ChangeType.EnumValueRemoved &&
|
|
20
|
+
change.criticality.level === CriticalityLevel.Breaking &&
|
|
21
|
+
change.path) {
|
|
22
|
+
const [enumName, enumItem] = parsePath(change.path);
|
|
23
|
+
const type = oldSchema.getType(enumName);
|
|
24
|
+
if (isEnumType(type)) {
|
|
25
|
+
const item = type.getValue(enumItem);
|
|
26
|
+
if (item && isDeprecated(item)) {
|
|
27
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.Dangerous }) });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (change.type === ChangeType.InputFieldRemoved &&
|
|
32
|
+
change.criticality.level === CriticalityLevel.Breaking &&
|
|
33
|
+
change.path) {
|
|
34
|
+
const [inputName, inputItem] = parsePath(change.path);
|
|
35
|
+
const type = oldSchema.getType(inputName);
|
|
36
|
+
if (isInputObjectType(type)) {
|
|
37
|
+
const item = type.getFields()[inputItem];
|
|
38
|
+
if (item && isDeprecated(item)) {
|
|
39
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.Dangerous }) });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (change.type === ChangeType.TypeRemoved &&
|
|
44
|
+
change.criticality.level === CriticalityLevel.Breaking &&
|
|
45
|
+
change.path) {
|
|
46
|
+
const [typeName] = parsePath(change.path);
|
|
47
|
+
const type = newSchema.getType(typeName);
|
|
48
|
+
if (!type) {
|
|
49
|
+
return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: CriticalityLevel.Dangerous }) });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return change;
|
|
53
|
+
});
|
|
54
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { isEnumType, isInputObjectType, isInterfaceType, isObjectType, isScalarType, isUnionType, } from 'graphql';
|
|
2
|
+
import { compareLists, isNotEqual, isVoid } from '../utils/compare.js';
|
|
3
|
+
import { isPrimitive } from '../utils/graphql.js';
|
|
4
|
+
import { directiveAdded, directiveRemoved } from './changes/directive.js';
|
|
5
|
+
import { schemaMutationTypeChanged, schemaQueryTypeChanged, schemaSubscriptionTypeChanged, } from './changes/schema.js';
|
|
6
|
+
import { typeAdded, typeDescriptionAdded, typeDescriptionChanged, typeDescriptionRemoved, typeKindChanged, typeRemoved, } from './changes/type.js';
|
|
7
|
+
import { changesInDirective } from './directive.js';
|
|
8
|
+
import { changesInEnum } from './enum.js';
|
|
9
|
+
import { changesInInputObject } from './input.js';
|
|
10
|
+
import { changesInInterface } from './interface.js';
|
|
11
|
+
import { changesInObject } from './object.js';
|
|
12
|
+
import { changesInUnion } from './union.js';
|
|
13
|
+
export function diffSchema(oldSchema, newSchema) {
|
|
14
|
+
const changes = [];
|
|
15
|
+
function addChange(change) {
|
|
16
|
+
changes.push(change);
|
|
17
|
+
}
|
|
18
|
+
changesInSchema(oldSchema, newSchema, addChange);
|
|
19
|
+
compareLists(Object.values(oldSchema.getTypeMap()).filter(t => !isPrimitive(t)), Object.values(newSchema.getTypeMap()).filter(t => !isPrimitive(t)), {
|
|
20
|
+
onAdded(type) {
|
|
21
|
+
addChange(typeAdded(type));
|
|
22
|
+
},
|
|
23
|
+
onRemoved(type) {
|
|
24
|
+
addChange(typeRemoved(type));
|
|
25
|
+
},
|
|
26
|
+
onMutual(type) {
|
|
27
|
+
changesInType(type.oldVersion, type.newVersion, addChange);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
compareLists(oldSchema.getDirectives(), newSchema.getDirectives(), {
|
|
31
|
+
onAdded(directive) {
|
|
32
|
+
addChange(directiveAdded(directive));
|
|
33
|
+
},
|
|
34
|
+
onRemoved(directive) {
|
|
35
|
+
addChange(directiveRemoved(directive));
|
|
36
|
+
},
|
|
37
|
+
onMutual(directive) {
|
|
38
|
+
changesInDirective(directive.oldVersion, directive.newVersion, addChange);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return changes;
|
|
42
|
+
}
|
|
43
|
+
function changesInSchema(oldSchema, newSchema, addChange) {
|
|
44
|
+
var _a, _b, _c, _d, _e, _f;
|
|
45
|
+
const defaultNames = {
|
|
46
|
+
query: 'Query',
|
|
47
|
+
mutation: 'Mutation',
|
|
48
|
+
subscription: 'Subscription',
|
|
49
|
+
};
|
|
50
|
+
const oldRoot = {
|
|
51
|
+
query: (_a = (oldSchema.getQueryType() || {}).name) !== null && _a !== void 0 ? _a : defaultNames.query,
|
|
52
|
+
mutation: (_b = (oldSchema.getMutationType() || {}).name) !== null && _b !== void 0 ? _b : defaultNames.mutation,
|
|
53
|
+
subscription: (_c = (oldSchema.getSubscriptionType() || {}).name) !== null && _c !== void 0 ? _c : defaultNames.subscription,
|
|
54
|
+
};
|
|
55
|
+
const newRoot = {
|
|
56
|
+
query: (_d = (newSchema.getQueryType() || {}).name) !== null && _d !== void 0 ? _d : defaultNames.query,
|
|
57
|
+
mutation: (_e = (newSchema.getMutationType() || {}).name) !== null && _e !== void 0 ? _e : defaultNames.mutation,
|
|
58
|
+
subscription: (_f = (newSchema.getSubscriptionType() || {}).name) !== null && _f !== void 0 ? _f : defaultNames.subscription,
|
|
59
|
+
};
|
|
60
|
+
if (isNotEqual(oldRoot.query, newRoot.query)) {
|
|
61
|
+
addChange(schemaQueryTypeChanged(oldSchema, newSchema));
|
|
62
|
+
}
|
|
63
|
+
if (isNotEqual(oldRoot.mutation, newRoot.mutation)) {
|
|
64
|
+
addChange(schemaMutationTypeChanged(oldSchema, newSchema));
|
|
65
|
+
}
|
|
66
|
+
if (isNotEqual(oldRoot.subscription, newRoot.subscription)) {
|
|
67
|
+
addChange(schemaSubscriptionTypeChanged(oldSchema, newSchema));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function changesInType(oldType, newType, addChange) {
|
|
71
|
+
if (isEnumType(oldType) && isEnumType(newType)) {
|
|
72
|
+
changesInEnum(oldType, newType, addChange);
|
|
73
|
+
}
|
|
74
|
+
else if (isUnionType(oldType) && isUnionType(newType)) {
|
|
75
|
+
changesInUnion(oldType, newType, addChange);
|
|
76
|
+
}
|
|
77
|
+
else if (isInputObjectType(oldType) && isInputObjectType(newType)) {
|
|
78
|
+
changesInInputObject(oldType, newType, addChange);
|
|
79
|
+
}
|
|
80
|
+
else if (isObjectType(oldType) && isObjectType(newType)) {
|
|
81
|
+
changesInObject(oldType, newType, addChange);
|
|
82
|
+
}
|
|
83
|
+
else if (isInterfaceType(oldType) && isInterfaceType(newType)) {
|
|
84
|
+
changesInInterface(oldType, newType, addChange);
|
|
85
|
+
}
|
|
86
|
+
else if (isScalarType(oldType) && isScalarType(newType)) {
|
|
87
|
+
// what to do with scalar types?
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
addChange(typeKindChanged(oldType, newType));
|
|
91
|
+
}
|
|
92
|
+
if (isNotEqual(oldType.description, newType.description)) {
|
|
93
|
+
if (isVoid(oldType.description)) {
|
|
94
|
+
addChange(typeDescriptionAdded(newType));
|
|
95
|
+
}
|
|
96
|
+
else if (isVoid(newType.description)) {
|
|
97
|
+
addChange(typeDescriptionRemoved(oldType));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
addChange(typeDescriptionChanged(oldType, newType));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { compareLists } from '../utils/compare.js';
|
|
2
|
+
import { unionMemberAdded, unionMemberRemoved } from './changes/union.js';
|
|
3
|
+
export function changesInUnion(oldUnion, newUnion, addChange) {
|
|
4
|
+
const oldTypes = oldUnion.getTypes();
|
|
5
|
+
const newTypes = newUnion.getTypes();
|
|
6
|
+
compareLists(oldTypes, newTypes, {
|
|
7
|
+
onAdded(t) {
|
|
8
|
+
addChange(unionMemberAdded(newUnion, t));
|
|
9
|
+
},
|
|
10
|
+
onRemoved(t) {
|
|
11
|
+
addChange(unionMemberRemoved(oldUnion, t));
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
}
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './coverage/index.js';
|
|
2
|
+
export { diff, DiffRule, } from './diff/index.js';
|
|
3
|
+
export { ChangeType, CriticalityLevel } from './diff/changes/change.js';
|
|
4
|
+
export { similar } from './similar/index.js';
|
|
5
|
+
export { getTypePrefix } from './utils/graphql.js';
|
|
6
|
+
export { validate } from './validate/index.js';
|
|
7
|
+
export { countAliases } from './validate/alias-count.js';
|
|
8
|
+
export { calculateOperationComplexity, } from './validate/complexity.js';
|
|
9
|
+
export { countDirectives } from './validate/directive-count.js';
|
|
10
|
+
export { countDepth } from './validate/query-depth.js';
|
|
11
|
+
export { calculateTokenCount } from './validate/token-count.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { printType } from 'graphql';
|
|
2
|
+
import { isForIntrospection, isPrimitive } from '../utils/graphql.js';
|
|
3
|
+
import { findBestMatch } from '../utils/string.js';
|
|
4
|
+
export function similar(schema, typeName, threshold = 0.4) {
|
|
5
|
+
const typeMap = schema.getTypeMap();
|
|
6
|
+
const targets = Object.keys(schema.getTypeMap())
|
|
7
|
+
.filter(name => !isPrimitive(name) && !isForIntrospection(name))
|
|
8
|
+
.map(name => ({
|
|
9
|
+
typeId: name,
|
|
10
|
+
value: stripType(typeMap[name]),
|
|
11
|
+
}));
|
|
12
|
+
const results = {};
|
|
13
|
+
if (typeof typeName !== 'undefined' && !targets.some(t => t.typeId === typeName)) {
|
|
14
|
+
throw new Error(`Type '${typeName}' doesn't exist`);
|
|
15
|
+
}
|
|
16
|
+
(typeName ? [{ typeId: typeName, value: '' }] : targets).forEach(source => {
|
|
17
|
+
const sourceType = schema.getType(source.typeId);
|
|
18
|
+
const matchWith = targets.filter(target => schema.getType(target.typeId).astNode.kind === sourceType.astNode.kind &&
|
|
19
|
+
target.typeId !== source.typeId);
|
|
20
|
+
if (matchWith.length > 0) {
|
|
21
|
+
const found = similarTo(sourceType, matchWith, threshold);
|
|
22
|
+
if (found) {
|
|
23
|
+
results[source.typeId] = found;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return results;
|
|
28
|
+
}
|
|
29
|
+
function similarTo(type, targets, threshold) {
|
|
30
|
+
const types = targets.filter(target => target.typeId !== type.name);
|
|
31
|
+
const result = findBestMatch(stripType(type), types);
|
|
32
|
+
if (result.bestMatch.rating < threshold) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
bestMatch: result.bestMatch,
|
|
37
|
+
ratings: result.ratings
|
|
38
|
+
.filter(r => r.rating >= threshold && r.target !== result.bestMatch.target)
|
|
39
|
+
.sort((a, b) => a.rating - b.rating)
|
|
40
|
+
.reverse(),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function stripType(type) {
|
|
44
|
+
return printType(type)
|
|
45
|
+
.trim()
|
|
46
|
+
.replace(/^[a-z]+ [^{]+\{/g, '')
|
|
47
|
+
.replace(/\}$/g, '')
|
|
48
|
+
.trim()
|
|
49
|
+
.split('\n')
|
|
50
|
+
.map(s => s.trim())
|
|
51
|
+
.sort((a, b) => a.localeCompare(b))
|
|
52
|
+
.join(' ');
|
|
53
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { extendSchema, parse, visit } from 'graphql';
|
|
2
|
+
import { removeDirectives, removeFieldIfDirectives } from './graphql.js';
|
|
3
|
+
export function transformDocumentWithApollo(doc, { keepClientFields }) {
|
|
4
|
+
return visit(doc, {
|
|
5
|
+
Field(node) {
|
|
6
|
+
return keepClientFields
|
|
7
|
+
? removeDirectives(node, ['client'])
|
|
8
|
+
: removeFieldIfDirectives(node, ['client']);
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function transformSchemaWithApollo(schema) {
|
|
13
|
+
return extendSchema(schema, parse(/* GraphQL */ `
|
|
14
|
+
directive @connection(key: String!, filter: [String]) on FIELD
|
|
15
|
+
`));
|
|
16
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export function keyMap(list, keyFn) {
|
|
2
|
+
return list.reduce((map, item) => {
|
|
3
|
+
map[keyFn(item)] = item;
|
|
4
|
+
return map;
|
|
5
|
+
}, Object.create(null));
|
|
6
|
+
}
|
|
7
|
+
export function isEqual(a, b) {
|
|
8
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
9
|
+
if (a.length !== b.length)
|
|
10
|
+
return false;
|
|
11
|
+
for (let index = 0; index < a.length; index++) {
|
|
12
|
+
if (!isEqual(a[index], b[index])) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
19
|
+
const aRecord = a;
|
|
20
|
+
const bRecord = b;
|
|
21
|
+
const aKeys = Object.keys(aRecord);
|
|
22
|
+
const bKeys = Object.keys(bRecord);
|
|
23
|
+
if (aKeys.length !== bKeys.length)
|
|
24
|
+
return false;
|
|
25
|
+
for (const key of aKeys) {
|
|
26
|
+
if (!isEqual(aRecord[key], bRecord[key])) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return a === b || (!a && !b);
|
|
33
|
+
}
|
|
34
|
+
export function isNotEqual(a, b) {
|
|
35
|
+
return !isEqual(a, b);
|
|
36
|
+
}
|
|
37
|
+
export function isVoid(a) {
|
|
38
|
+
return typeof a === 'undefined' || a === null;
|
|
39
|
+
}
|
|
40
|
+
export function diffArrays(a, b) {
|
|
41
|
+
return a.filter(c => !b.some(d => isEqual(d, c)));
|
|
42
|
+
}
|
|
43
|
+
export function compareLists(oldList, newList, callbacks) {
|
|
44
|
+
const oldMap = keyMap(oldList, ({ name }) => name);
|
|
45
|
+
const newMap = keyMap(newList, ({ name }) => name);
|
|
46
|
+
const added = [];
|
|
47
|
+
const removed = [];
|
|
48
|
+
const mutual = [];
|
|
49
|
+
for (const oldItem of oldList) {
|
|
50
|
+
const newItem = newMap[oldItem.name];
|
|
51
|
+
if (newItem === undefined) {
|
|
52
|
+
removed.push(oldItem);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
mutual.push({
|
|
56
|
+
newVersion: newItem,
|
|
57
|
+
oldVersion: oldItem,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const newItem of newList) {
|
|
62
|
+
if (oldMap[newItem.name] === undefined) {
|
|
63
|
+
added.push(newItem);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (callbacks) {
|
|
67
|
+
if (callbacks.onAdded) {
|
|
68
|
+
added.forEach(callbacks.onAdded);
|
|
69
|
+
}
|
|
70
|
+
if (callbacks.onRemoved) {
|
|
71
|
+
removed.forEach(callbacks.onRemoved);
|
|
72
|
+
}
|
|
73
|
+
if (callbacks.onMutual) {
|
|
74
|
+
mutual.forEach(callbacks.onMutual);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
added,
|
|
79
|
+
removed,
|
|
80
|
+
mutual,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { getNamedType, GraphQLError, isInputObjectType, isInterfaceType, isListType, isNonNullType, isObjectType, isScalarType, isUnionType, isWrappingType, Kind, TypeInfo, visit, visitWithTypeInfo, } from 'graphql';
|
|
2
|
+
import { isDeprecated } from './is-deprecated.js';
|
|
3
|
+
export function safeChangeForField(oldType, newType) {
|
|
4
|
+
if (!isWrappingType(oldType) && !isWrappingType(newType)) {
|
|
5
|
+
return oldType.toString() === newType.toString();
|
|
6
|
+
}
|
|
7
|
+
if (isNonNullType(newType)) {
|
|
8
|
+
const ofType = isNonNullType(oldType) ? oldType.ofType : oldType;
|
|
9
|
+
return safeChangeForField(ofType, newType.ofType);
|
|
10
|
+
}
|
|
11
|
+
if (isListType(oldType)) {
|
|
12
|
+
return ((isListType(newType) && safeChangeForField(oldType.ofType, newType.ofType)) ||
|
|
13
|
+
(isNonNullType(newType) && safeChangeForField(oldType, newType.ofType)));
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
export function safeChangeForInputValue(oldType, newType) {
|
|
18
|
+
if (!isWrappingType(oldType) && !isWrappingType(newType)) {
|
|
19
|
+
return oldType.toString() === newType.toString();
|
|
20
|
+
}
|
|
21
|
+
if (isListType(oldType) && isListType(newType)) {
|
|
22
|
+
return safeChangeForInputValue(oldType.ofType, newType.ofType);
|
|
23
|
+
}
|
|
24
|
+
if (isNonNullType(oldType)) {
|
|
25
|
+
const ofType = isNonNullType(newType) ? newType.ofType : newType;
|
|
26
|
+
return safeChangeForInputValue(oldType.ofType, ofType);
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
export function getKind(type) {
|
|
31
|
+
const node = type.astNode;
|
|
32
|
+
return (node === null || node === void 0 ? void 0 : node.kind) || '';
|
|
33
|
+
}
|
|
34
|
+
export function getTypePrefix(type) {
|
|
35
|
+
const kind = getKind(type);
|
|
36
|
+
const kindsMap = {
|
|
37
|
+
[Kind.SCALAR_TYPE_DEFINITION]: 'scalar',
|
|
38
|
+
[Kind.OBJECT_TYPE_DEFINITION]: 'type',
|
|
39
|
+
[Kind.INTERFACE_TYPE_DEFINITION]: 'interface',
|
|
40
|
+
[Kind.UNION_TYPE_DEFINITION]: 'union',
|
|
41
|
+
[Kind.ENUM_TYPE_DEFINITION]: 'enum',
|
|
42
|
+
[Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
|
|
43
|
+
};
|
|
44
|
+
return kindsMap[kind.toString()];
|
|
45
|
+
}
|
|
46
|
+
export function isPrimitive(type) {
|
|
47
|
+
return ['String', 'Int', 'Float', 'Boolean', 'ID'].includes(typeof type === 'string' ? type : type.name);
|
|
48
|
+
}
|
|
49
|
+
export function isForIntrospection(type) {
|
|
50
|
+
return [
|
|
51
|
+
'__Schema',
|
|
52
|
+
'__Type',
|
|
53
|
+
'__TypeKind',
|
|
54
|
+
'__Field',
|
|
55
|
+
'__InputValue',
|
|
56
|
+
'__EnumValue',
|
|
57
|
+
'__Directive',
|
|
58
|
+
'__DirectiveLocation',
|
|
59
|
+
].includes(typeof type === 'string' ? type : type.name);
|
|
60
|
+
}
|
|
61
|
+
export function findDeprecatedUsages(schema, ast) {
|
|
62
|
+
const errors = [];
|
|
63
|
+
const typeInfo = new TypeInfo(schema);
|
|
64
|
+
visit(ast, visitWithTypeInfo(typeInfo, {
|
|
65
|
+
Argument(node) {
|
|
66
|
+
const argument = typeInfo.getArgument();
|
|
67
|
+
if (argument) {
|
|
68
|
+
const reason = argument.deprecationReason;
|
|
69
|
+
if (reason) {
|
|
70
|
+
const fieldDef = typeInfo.getFieldDef();
|
|
71
|
+
if (fieldDef) {
|
|
72
|
+
errors.push(new GraphQLError(`The argument '${argument === null || argument === void 0 ? void 0 : argument.name}' of '${fieldDef.name}' is deprecated. ${reason}`, [node]));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
Field(node) {
|
|
78
|
+
const fieldDef = typeInfo.getFieldDef();
|
|
79
|
+
if (fieldDef && isDeprecated(fieldDef)) {
|
|
80
|
+
const parentType = typeInfo.getParentType();
|
|
81
|
+
if (parentType) {
|
|
82
|
+
const reason = fieldDef.deprecationReason;
|
|
83
|
+
errors.push(new GraphQLError(`The field '${parentType.name}.${fieldDef.name}' is deprecated.${reason ? ' ' + reason : ''}`, [node]));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
EnumValue(node) {
|
|
88
|
+
const enumVal = typeInfo.getEnumValue();
|
|
89
|
+
if (enumVal && isDeprecated(enumVal)) {
|
|
90
|
+
const type = getNamedType(typeInfo.getInputType());
|
|
91
|
+
if (type) {
|
|
92
|
+
const reason = enumVal.deprecationReason;
|
|
93
|
+
errors.push(new GraphQLError(`The enum value '${type.name}.${enumVal.name}' is deprecated.${reason ? ' ' + reason : ''}`, [node]));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
}));
|
|
98
|
+
return errors;
|
|
99
|
+
}
|
|
100
|
+
export function removeFieldIfDirectives(node, directiveNames) {
|
|
101
|
+
var _a;
|
|
102
|
+
if ((_a = node.directives) === null || _a === void 0 ? void 0 : _a.some(d => directiveNames.includes(d.name.value))) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
return node;
|
|
106
|
+
}
|
|
107
|
+
export function removeDirectives(node, directiveNames) {
|
|
108
|
+
if (node.directives) {
|
|
109
|
+
return Object.assign(Object.assign({}, node), { directives: node.directives.filter(d => !directiveNames.includes(d.name.value)) });
|
|
110
|
+
}
|
|
111
|
+
return node;
|
|
112
|
+
}
|
|
113
|
+
export function getReachableTypes(schema) {
|
|
114
|
+
const reachableTypes = new Set();
|
|
115
|
+
const collect = (type) => {
|
|
116
|
+
const typeName = type.name;
|
|
117
|
+
if (reachableTypes.has(typeName)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
reachableTypes.add(typeName);
|
|
121
|
+
if (isScalarType(type)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (isInterfaceType(type) || isObjectType(type)) {
|
|
125
|
+
if (isInterfaceType(type)) {
|
|
126
|
+
const { objects, interfaces } = schema.getImplementations(type);
|
|
127
|
+
for (const child of objects) {
|
|
128
|
+
collect(child);
|
|
129
|
+
}
|
|
130
|
+
for (const child of interfaces) {
|
|
131
|
+
collect(child);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const fields = type.getFields();
|
|
135
|
+
for (const fieldName in fields) {
|
|
136
|
+
const field = fields[fieldName];
|
|
137
|
+
collect(resolveOutputType(field.type));
|
|
138
|
+
const args = field.args;
|
|
139
|
+
for (const argName in args) {
|
|
140
|
+
const arg = args[argName];
|
|
141
|
+
collect(resolveInputType(arg.type));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (isUnionType(type)) {
|
|
146
|
+
const types = type.getTypes();
|
|
147
|
+
for (const child of types) {
|
|
148
|
+
collect(child);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (isInputObjectType(type)) {
|
|
152
|
+
const fields = type.getFields();
|
|
153
|
+
for (const fieldName in fields) {
|
|
154
|
+
const field = fields[fieldName];
|
|
155
|
+
collect(resolveInputType(field.type));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
for (const type of [
|
|
160
|
+
schema.getQueryType(),
|
|
161
|
+
schema.getMutationType(),
|
|
162
|
+
schema.getSubscriptionType(),
|
|
163
|
+
]) {
|
|
164
|
+
if (type) {
|
|
165
|
+
collect(type);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return reachableTypes;
|
|
169
|
+
}
|
|
170
|
+
function resolveOutputType(output) {
|
|
171
|
+
if (isListType(output) || isNonNullType(output)) {
|
|
172
|
+
return resolveOutputType(output.ofType);
|
|
173
|
+
}
|
|
174
|
+
return output;
|
|
175
|
+
}
|
|
176
|
+
function resolveInputType(input) {
|
|
177
|
+
if (isListType(input) || isNonNullType(input)) {
|
|
178
|
+
return resolveInputType(input.ofType);
|
|
179
|
+
}
|
|
180
|
+
return input;
|
|
181
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function isDeprecated(fieldOrEnumValue) {
|
|
2
|
+
var _a, _b;
|
|
3
|
+
if ('isDeprecated' in fieldOrEnumValue) {
|
|
4
|
+
return fieldOrEnumValue['isDeprecated'];
|
|
5
|
+
}
|
|
6
|
+
if (fieldOrEnumValue.deprecationReason != null) {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
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')) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import inspect from 'object-inspect';
|
|
2
|
+
function compareTwoStrings(str1, str2) {
|
|
3
|
+
if (!str1.length && !str2.length)
|
|
4
|
+
return 1;
|
|
5
|
+
if (!str1.length || !str2.length)
|
|
6
|
+
return 0;
|
|
7
|
+
if (str1.toUpperCase() === str2.toUpperCase())
|
|
8
|
+
return 1;
|
|
9
|
+
if (str1.length === 1 && str2.length === 1)
|
|
10
|
+
return 0;
|
|
11
|
+
const pairs1 = wordLetterPairs(str1);
|
|
12
|
+
const pairs2 = wordLetterPairs(str2);
|
|
13
|
+
const union = pairs1.length + pairs2.length;
|
|
14
|
+
let intersection = 0;
|
|
15
|
+
pairs1.forEach(pair1 => {
|
|
16
|
+
for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
|
|
17
|
+
if (pair1 !== pair2)
|
|
18
|
+
continue;
|
|
19
|
+
intersection++;
|
|
20
|
+
pairs2.splice(i, 1);
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return (intersection * 2) / union;
|
|
25
|
+
}
|
|
26
|
+
export function findBestMatch(mainString, targetStrings) {
|
|
27
|
+
if (!areArgsValid(mainString, targetStrings))
|
|
28
|
+
throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
|
|
29
|
+
const ratings = targetStrings.map(target => ({
|
|
30
|
+
target,
|
|
31
|
+
rating: compareTwoStrings(mainString, target.value),
|
|
32
|
+
}));
|
|
33
|
+
const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
|
|
34
|
+
return { ratings, bestMatch };
|
|
35
|
+
}
|
|
36
|
+
function flattenDeep(arr) {
|
|
37
|
+
return Array.isArray(arr) ? arr.reduce((a, b) => a.concat(flattenDeep(b)), []) : [arr];
|
|
38
|
+
}
|
|
39
|
+
function areArgsValid(mainString, targetStrings) {
|
|
40
|
+
if (typeof mainString !== 'string')
|
|
41
|
+
return false;
|
|
42
|
+
if (!Array.isArray(targetStrings))
|
|
43
|
+
return false;
|
|
44
|
+
if (!targetStrings.length)
|
|
45
|
+
return false;
|
|
46
|
+
if (targetStrings.find(s => typeof s.value !== 'string'))
|
|
47
|
+
return false;
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
function letterPairs(str) {
|
|
51
|
+
const pairs = [];
|
|
52
|
+
for (let i = 0, max = str.length - 1; i < max; i++)
|
|
53
|
+
pairs[i] = str.substring(i, i + 2);
|
|
54
|
+
return pairs;
|
|
55
|
+
}
|
|
56
|
+
function wordLetterPairs(str) {
|
|
57
|
+
const pairs = str.toUpperCase().split(' ').map(letterPairs);
|
|
58
|
+
return flattenDeep(pairs);
|
|
59
|
+
}
|
|
60
|
+
export function safeString(obj) {
|
|
61
|
+
return inspect(obj)
|
|
62
|
+
.replace(/\[Object: null prototype\] /g, '')
|
|
63
|
+
.replace(/(^')|('$)/g, '');
|
|
64
|
+
}
|