@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,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateTokenCount = exports.calculateTokenCount = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
const parser_js_1 = require("graphql/language/parser.js");
|
|
6
|
+
class ParserWithLexer extends parser_js_1.Parser {
|
|
7
|
+
get tokenCount() {
|
|
8
|
+
return this.__tokenCount;
|
|
9
|
+
}
|
|
10
|
+
constructor(source, options) {
|
|
11
|
+
super(source, options);
|
|
12
|
+
this.__tokenCount = 0;
|
|
13
|
+
const lexer = this._lexer;
|
|
14
|
+
this._lexer = new Proxy(lexer, {
|
|
15
|
+
get: (target, prop, receiver) => {
|
|
16
|
+
if (prop === 'advance') {
|
|
17
|
+
return () => {
|
|
18
|
+
const token = target.advance();
|
|
19
|
+
if (token.kind !== graphql_1.TokenKind.EOF) {
|
|
20
|
+
this.__tokenCount++;
|
|
21
|
+
}
|
|
22
|
+
return token;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return Reflect.get(target, prop, receiver);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function calculateTokenCount(args) {
|
|
31
|
+
const parser = new ParserWithLexer(args.source);
|
|
32
|
+
const document = parser.parseDocument();
|
|
33
|
+
let { tokenCount } = parser;
|
|
34
|
+
(0, graphql_1.visit)(document, {
|
|
35
|
+
FragmentSpread(node) {
|
|
36
|
+
const fragmentSource = args.getReferencedFragmentSource(node.name.value);
|
|
37
|
+
if (fragmentSource) {
|
|
38
|
+
tokenCount += calculateTokenCount({
|
|
39
|
+
source: fragmentSource,
|
|
40
|
+
getReferencedFragmentSource: args.getReferencedFragmentSource,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
return tokenCount;
|
|
46
|
+
}
|
|
47
|
+
exports.calculateTokenCount = calculateTokenCount;
|
|
48
|
+
function validateTokenCount(args) {
|
|
49
|
+
var _a;
|
|
50
|
+
const tokenCount = calculateTokenCount(args);
|
|
51
|
+
if (tokenCount > args.maxTokenCount) {
|
|
52
|
+
return new graphql_1.GraphQLError(`Query exceeds maximum token count of ${args.maxTokenCount} (actual: ${tokenCount})`, args.document, args.source, ((_a = args.document.loc) === null || _a === void 0 ? void 0 : _a.start) ? [args.document.loc.start] : undefined);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.validateTokenCount = validateTokenCount;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Kind, parse, } from 'graphql';
|
|
2
|
+
export function readDocument(source) {
|
|
3
|
+
const result = {
|
|
4
|
+
source,
|
|
5
|
+
fragments: [],
|
|
6
|
+
operations: [],
|
|
7
|
+
hasFragments: false,
|
|
8
|
+
hasOperations: false,
|
|
9
|
+
};
|
|
10
|
+
const documentNode = parse(source.body);
|
|
11
|
+
const filepath = source.name;
|
|
12
|
+
const definitions = documentNode.definitions || [];
|
|
13
|
+
definitions.forEach((node) => {
|
|
14
|
+
if (isOperation(node)) {
|
|
15
|
+
result.operations.push({
|
|
16
|
+
node,
|
|
17
|
+
source: filepath,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
else if (isFragment(node)) {
|
|
21
|
+
result.fragments.push({
|
|
22
|
+
node,
|
|
23
|
+
source: filepath,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
result.hasFragments = result.fragments.length > 0;
|
|
28
|
+
result.hasOperations = result.operations.length > 0;
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
function isOperation(node) {
|
|
32
|
+
return node.kind === Kind.OPERATION_DEFINITION;
|
|
33
|
+
}
|
|
34
|
+
function isFragment(node) {
|
|
35
|
+
return node.kind === Kind.FRAGMENT_DEFINITION;
|
|
36
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { isInterfaceType, isObjectType, TypeInfo, visit, visitWithTypeInfo, } from 'graphql';
|
|
2
|
+
import { readDocument } from '../ast/document.js';
|
|
3
|
+
import { isForIntrospection, isPrimitive } from '../utils/graphql.js';
|
|
4
|
+
export function coverage(schema, sources) {
|
|
5
|
+
const coverage = {
|
|
6
|
+
sources,
|
|
7
|
+
types: {},
|
|
8
|
+
stats: {
|
|
9
|
+
numTypes: 0,
|
|
10
|
+
numTypesCoveredFully: 0,
|
|
11
|
+
numTypesCovered: 0,
|
|
12
|
+
numFields: 0,
|
|
13
|
+
numFiledsCovered: 0,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const typeMap = schema.getTypeMap();
|
|
17
|
+
const typeInfo = new TypeInfo(schema);
|
|
18
|
+
const visitor = source => ({
|
|
19
|
+
Field(node) {
|
|
20
|
+
const fieldDef = typeInfo.getFieldDef();
|
|
21
|
+
const parent = typeInfo.getParentType();
|
|
22
|
+
if ((parent === null || parent === void 0 ? void 0 : parent.name) &&
|
|
23
|
+
!isForIntrospection(parent.name) &&
|
|
24
|
+
(fieldDef === null || fieldDef === void 0 ? void 0 : fieldDef.name) &&
|
|
25
|
+
fieldDef.name !== '__typename' &&
|
|
26
|
+
fieldDef.name !== '__schema') {
|
|
27
|
+
const sourceName = source.name;
|
|
28
|
+
const typeCoverage = coverage.types[parent.name];
|
|
29
|
+
const fieldCoverage = typeCoverage.children[fieldDef.name];
|
|
30
|
+
const locations = fieldCoverage.locations[sourceName];
|
|
31
|
+
typeCoverage.hits++;
|
|
32
|
+
fieldCoverage.hits++;
|
|
33
|
+
if (node.loc) {
|
|
34
|
+
fieldCoverage.locations[sourceName] = [node.loc, ...(locations || [])];
|
|
35
|
+
}
|
|
36
|
+
if (node.arguments) {
|
|
37
|
+
for (const argNode of node.arguments) {
|
|
38
|
+
const argCoverage = fieldCoverage.children[argNode.name.value];
|
|
39
|
+
argCoverage.hits++;
|
|
40
|
+
if (argNode.loc) {
|
|
41
|
+
argCoverage.locations[sourceName] = [
|
|
42
|
+
argNode.loc,
|
|
43
|
+
...(argCoverage.locations[sourceName] || []),
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
for (const typename in typeMap) {
|
|
52
|
+
if (!isForIntrospection(typename) && !isPrimitive(typename)) {
|
|
53
|
+
const type = typeMap[typename];
|
|
54
|
+
if (isObjectType(type) || isInterfaceType(type)) {
|
|
55
|
+
const typeCoverage = {
|
|
56
|
+
hits: 0,
|
|
57
|
+
fieldsCount: 0,
|
|
58
|
+
fieldsCountCovered: 0,
|
|
59
|
+
type,
|
|
60
|
+
children: {},
|
|
61
|
+
};
|
|
62
|
+
const fieldMap = type.getFields();
|
|
63
|
+
for (const fieldname in fieldMap) {
|
|
64
|
+
const field = fieldMap[fieldname];
|
|
65
|
+
typeCoverage.children[field.name] = {
|
|
66
|
+
hits: 0,
|
|
67
|
+
fieldsCount: 0,
|
|
68
|
+
fieldsCountCovered: 0,
|
|
69
|
+
locations: {},
|
|
70
|
+
children: {},
|
|
71
|
+
};
|
|
72
|
+
for (const arg of field.args) {
|
|
73
|
+
typeCoverage.children[field.name].children[arg.name] = {
|
|
74
|
+
hits: 0,
|
|
75
|
+
fieldsCount: 0,
|
|
76
|
+
fieldsCountCovered: 0,
|
|
77
|
+
locations: {},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
coverage.types[type.name] = typeCoverage;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const documents = coverage.sources.map(readDocument);
|
|
86
|
+
documents.forEach((doc, i) => {
|
|
87
|
+
const source = coverage.sources[i];
|
|
88
|
+
doc.operations.forEach(op => {
|
|
89
|
+
visit(op.node, visitWithTypeInfo(typeInfo, visitor(source)));
|
|
90
|
+
});
|
|
91
|
+
doc.fragments.forEach(fr => {
|
|
92
|
+
visit(fr.node, visitWithTypeInfo(typeInfo, visitor(source)));
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
for (const key in coverage.types) {
|
|
96
|
+
const me = coverage.types[key];
|
|
97
|
+
processStats(me);
|
|
98
|
+
coverage.stats.numTypes++;
|
|
99
|
+
if (me.fieldsCountCovered > 0)
|
|
100
|
+
coverage.stats.numTypesCovered++;
|
|
101
|
+
if (me.fieldsCount == me.fieldsCountCovered)
|
|
102
|
+
coverage.stats.numTypesCoveredFully++;
|
|
103
|
+
coverage.stats.numFields += me.fieldsCount;
|
|
104
|
+
coverage.stats.numFiledsCovered += me.fieldsCountCovered;
|
|
105
|
+
}
|
|
106
|
+
return coverage;
|
|
107
|
+
}
|
|
108
|
+
function processStats(me) {
|
|
109
|
+
const children = me.children;
|
|
110
|
+
if (children) {
|
|
111
|
+
for (const k in children) {
|
|
112
|
+
const ch = children[k];
|
|
113
|
+
if (ch.children !== undefined) {
|
|
114
|
+
processStats(ch);
|
|
115
|
+
me.fieldsCount += ch.fieldsCount;
|
|
116
|
+
me.fieldsCountCovered += ch.fieldsCountCovered;
|
|
117
|
+
}
|
|
118
|
+
me.fieldsCount++;
|
|
119
|
+
if (ch.hits > 0) {
|
|
120
|
+
me.fieldsCountCovered++;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { diffArrays, isNotEqual } from '../utils/compare.js';
|
|
2
|
+
import { fieldArgumentDefaultChanged, fieldArgumentDescriptionChanged, fieldArgumentTypeChanged, } from './changes/argument.js';
|
|
3
|
+
export function changesInArgument(type, field, oldArg, newArg, addChange) {
|
|
4
|
+
if (isNotEqual(oldArg.description, newArg.description)) {
|
|
5
|
+
addChange(fieldArgumentDescriptionChanged(type, field, oldArg, newArg));
|
|
6
|
+
}
|
|
7
|
+
if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
|
|
8
|
+
if (Array.isArray(oldArg.defaultValue) && Array.isArray(newArg.defaultValue)) {
|
|
9
|
+
const diff = diffArrays(oldArg.defaultValue, newArg.defaultValue);
|
|
10
|
+
if (diff.length > 0) {
|
|
11
|
+
addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
else if (JSON.stringify(oldArg.defaultValue) !== JSON.stringify(newArg.defaultValue)) {
|
|
15
|
+
addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
|
|
19
|
+
addChange(fieldArgumentTypeChanged(type, field, oldArg, newArg));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { safeChangeForInputValue } from '../../utils/graphql.js';
|
|
2
|
+
import { safeString } from '../../utils/string.js';
|
|
3
|
+
import { ChangeType, CriticalityLevel } from './change.js';
|
|
4
|
+
export function fieldArgumentDescriptionChanged(type, field, oldArg, newArg) {
|
|
5
|
+
return {
|
|
6
|
+
criticality: {
|
|
7
|
+
level: CriticalityLevel.NonBreaking,
|
|
8
|
+
},
|
|
9
|
+
type: ChangeType.FieldArgumentDescriptionChanged,
|
|
10
|
+
message: `Description for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.description}' to '${newArg.description}'`,
|
|
11
|
+
path: [type.name, field.name, oldArg.name].join('.'),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function fieldArgumentDefaultChanged(type, field, oldArg, newArg) {
|
|
15
|
+
return {
|
|
16
|
+
criticality: {
|
|
17
|
+
level: CriticalityLevel.Dangerous,
|
|
18
|
+
reason: 'Changing the default value for an argument may change the runtime behaviour of a field if it was never provided.',
|
|
19
|
+
},
|
|
20
|
+
type: ChangeType.FieldArgumentDefaultChanged,
|
|
21
|
+
message: typeof oldArg.defaultValue === 'undefined'
|
|
22
|
+
? `Default value '${safeString(newArg.defaultValue)}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
|
|
23
|
+
: `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${safeString(oldArg.defaultValue)}' to '${safeString(newArg.defaultValue)}'`,
|
|
24
|
+
path: [type.name, field.name, oldArg.name].join('.'),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function fieldArgumentTypeChanged(type, field, oldArg, newArg) {
|
|
28
|
+
return {
|
|
29
|
+
criticality: safeChangeForInputValue(oldArg.type, newArg.type)
|
|
30
|
+
? {
|
|
31
|
+
level: CriticalityLevel.NonBreaking,
|
|
32
|
+
reason: `Changing an input field from non-null to null is considered non-breaking.`,
|
|
33
|
+
}
|
|
34
|
+
: {
|
|
35
|
+
level: CriticalityLevel.Breaking,
|
|
36
|
+
reason: `Changing the type of a field's argument can cause existing queries that use this argument to error.`,
|
|
37
|
+
},
|
|
38
|
+
type: ChangeType.FieldArgumentTypeChanged,
|
|
39
|
+
message: `Type for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.type}' to '${newArg.type}'`,
|
|
40
|
+
path: [type.name, field.name, oldArg.name].join('.'),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export var ChangeType;
|
|
2
|
+
(function (ChangeType) {
|
|
3
|
+
// Argument
|
|
4
|
+
ChangeType["FieldArgumentDescriptionChanged"] = "FIELD_ARGUMENT_DESCRIPTION_CHANGED";
|
|
5
|
+
ChangeType["FieldArgumentDefaultChanged"] = "FIELD_ARGUMENT_DEFAULT_CHANGED";
|
|
6
|
+
ChangeType["FieldArgumentTypeChanged"] = "FIELD_ARGUMENT_TYPE_CHANGED";
|
|
7
|
+
// Directive
|
|
8
|
+
ChangeType["DirectiveRemoved"] = "DIRECTIVE_REMOVED";
|
|
9
|
+
ChangeType["DirectiveAdded"] = "DIRECTIVE_ADDED";
|
|
10
|
+
ChangeType["DirectiveDescriptionChanged"] = "DIRECTIVE_DESCRIPTION_CHANGED";
|
|
11
|
+
ChangeType["DirectiveLocationAdded"] = "DIRECTIVE_LOCATION_ADDED";
|
|
12
|
+
ChangeType["DirectiveLocationRemoved"] = "DIRECTIVE_LOCATION_REMOVED";
|
|
13
|
+
ChangeType["DirectiveArgumentAdded"] = "DIRECTIVE_ARGUMENT_ADDED";
|
|
14
|
+
ChangeType["DirectiveArgumentRemoved"] = "DIRECTIVE_ARGUMENT_REMOVED";
|
|
15
|
+
ChangeType["DirectiveArgumentDescriptionChanged"] = "DIRECTIVE_ARGUMENT_DESCRIPTION_CHANGED";
|
|
16
|
+
ChangeType["DirectiveArgumentDefaultValueChanged"] = "DIRECTIVE_ARGUMENT_DEFAULT_VALUE_CHANGED";
|
|
17
|
+
ChangeType["DirectiveArgumentTypeChanged"] = "DIRECTIVE_ARGUMENT_TYPE_CHANGED";
|
|
18
|
+
// Enum
|
|
19
|
+
ChangeType["EnumValueRemoved"] = "ENUM_VALUE_REMOVED";
|
|
20
|
+
ChangeType["EnumValueAdded"] = "ENUM_VALUE_ADDED";
|
|
21
|
+
ChangeType["EnumValueDescriptionChanged"] = "ENUM_VALUE_DESCRIPTION_CHANGED";
|
|
22
|
+
ChangeType["EnumValueDeprecationReasonChanged"] = "ENUM_VALUE_DEPRECATION_REASON_CHANGED";
|
|
23
|
+
ChangeType["EnumValueDeprecationReasonAdded"] = "ENUM_VALUE_DEPRECATION_REASON_ADDED";
|
|
24
|
+
ChangeType["EnumValueDeprecationReasonRemoved"] = "ENUM_VALUE_DEPRECATION_REASON_REMOVED";
|
|
25
|
+
// Field
|
|
26
|
+
ChangeType["FieldRemoved"] = "FIELD_REMOVED";
|
|
27
|
+
ChangeType["FieldAdded"] = "FIELD_ADDED";
|
|
28
|
+
ChangeType["FieldDescriptionChanged"] = "FIELD_DESCRIPTION_CHANGED";
|
|
29
|
+
ChangeType["FieldDescriptionAdded"] = "FIELD_DESCRIPTION_ADDED";
|
|
30
|
+
ChangeType["FieldDescriptionRemoved"] = "FIELD_DESCRIPTION_REMOVED";
|
|
31
|
+
ChangeType["FieldDeprecationAdded"] = "FIELD_DEPRECATION_ADDED";
|
|
32
|
+
ChangeType["FieldDeprecationRemoved"] = "FIELD_DEPRECATION_REMOVED";
|
|
33
|
+
ChangeType["FieldDeprecationReasonChanged"] = "FIELD_DEPRECATION_REASON_CHANGED";
|
|
34
|
+
ChangeType["FieldDeprecationReasonAdded"] = "FIELD_DEPRECATION_REASON_ADDED";
|
|
35
|
+
ChangeType["FieldDeprecationReasonRemoved"] = "FIELD_DEPRECATION_REASON_REMOVED";
|
|
36
|
+
ChangeType["FieldTypeChanged"] = "FIELD_TYPE_CHANGED";
|
|
37
|
+
ChangeType["FieldArgumentAdded"] = "FIELD_ARGUMENT_ADDED";
|
|
38
|
+
ChangeType["FieldArgumentRemoved"] = "FIELD_ARGUMENT_REMOVED";
|
|
39
|
+
// Input
|
|
40
|
+
ChangeType["InputFieldRemoved"] = "INPUT_FIELD_REMOVED";
|
|
41
|
+
ChangeType["InputFieldAdded"] = "INPUT_FIELD_ADDED";
|
|
42
|
+
ChangeType["InputFieldDescriptionAdded"] = "INPUT_FIELD_DESCRIPTION_ADDED";
|
|
43
|
+
ChangeType["InputFieldDescriptionRemoved"] = "INPUT_FIELD_DESCRIPTION_REMOVED";
|
|
44
|
+
ChangeType["InputFieldDescriptionChanged"] = "INPUT_FIELD_DESCRIPTION_CHANGED";
|
|
45
|
+
ChangeType["InputFieldDefaultValueChanged"] = "INPUT_FIELD_DEFAULT_VALUE_CHANGED";
|
|
46
|
+
ChangeType["InputFieldTypeChanged"] = "INPUT_FIELD_TYPE_CHANGED";
|
|
47
|
+
// Type
|
|
48
|
+
ChangeType["ObjectTypeInterfaceAdded"] = "OBJECT_TYPE_INTERFACE_ADDED";
|
|
49
|
+
ChangeType["ObjectTypeInterfaceRemoved"] = "OBJECT_TYPE_INTERFACE_REMOVED";
|
|
50
|
+
// Schema
|
|
51
|
+
ChangeType["SchemaQueryTypeChanged"] = "SCHEMA_QUERY_TYPE_CHANGED";
|
|
52
|
+
ChangeType["SchemaMutationTypeChanged"] = "SCHEMA_MUTATION_TYPE_CHANGED";
|
|
53
|
+
ChangeType["SchemaSubscriptionTypeChanged"] = "SCHEMA_SUBSCRIPTION_TYPE_CHANGED";
|
|
54
|
+
// Type
|
|
55
|
+
ChangeType["TypeRemoved"] = "TYPE_REMOVED";
|
|
56
|
+
ChangeType["TypeAdded"] = "TYPE_ADDED";
|
|
57
|
+
ChangeType["TypeKindChanged"] = "TYPE_KIND_CHANGED";
|
|
58
|
+
ChangeType["TypeDescriptionChanged"] = "TYPE_DESCRIPTION_CHANGED";
|
|
59
|
+
// TODO
|
|
60
|
+
ChangeType["TypeDescriptionRemoved"] = "TYPE_DESCRIPTION_REMOVED";
|
|
61
|
+
// TODO
|
|
62
|
+
ChangeType["TypeDescriptionAdded"] = "TYPE_DESCRIPTION_ADDED";
|
|
63
|
+
// Union
|
|
64
|
+
ChangeType["UnionMemberRemoved"] = "UNION_MEMBER_REMOVED";
|
|
65
|
+
ChangeType["UnionMemberAdded"] = "UNION_MEMBER_ADDED";
|
|
66
|
+
})(ChangeType || (ChangeType = {}));
|
|
67
|
+
export var CriticalityLevel;
|
|
68
|
+
(function (CriticalityLevel) {
|
|
69
|
+
CriticalityLevel["Breaking"] = "BREAKING";
|
|
70
|
+
CriticalityLevel["NonBreaking"] = "NON_BREAKING";
|
|
71
|
+
CriticalityLevel["Dangerous"] = "DANGEROUS";
|
|
72
|
+
})(CriticalityLevel || (CriticalityLevel = {}));
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { isNonNullType } from 'graphql';
|
|
2
|
+
import { safeChangeForInputValue } from '../../utils/graphql.js';
|
|
3
|
+
import { ChangeType, CriticalityLevel } from './change.js';
|
|
4
|
+
export function directiveRemoved(directive) {
|
|
5
|
+
return {
|
|
6
|
+
criticality: {
|
|
7
|
+
level: CriticalityLevel.Breaking,
|
|
8
|
+
},
|
|
9
|
+
type: ChangeType.DirectiveRemoved,
|
|
10
|
+
message: `Directive '${directive.name}' was removed`,
|
|
11
|
+
path: `@${directive.name}`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function directiveAdded(directive) {
|
|
15
|
+
return {
|
|
16
|
+
criticality: {
|
|
17
|
+
level: CriticalityLevel.NonBreaking,
|
|
18
|
+
},
|
|
19
|
+
type: ChangeType.DirectiveAdded,
|
|
20
|
+
message: `Directive '${directive.name}' was added`,
|
|
21
|
+
path: `@${directive.name}`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function directiveDescriptionChanged(oldDirective, newDirective) {
|
|
25
|
+
return {
|
|
26
|
+
criticality: {
|
|
27
|
+
level: CriticalityLevel.NonBreaking,
|
|
28
|
+
},
|
|
29
|
+
type: ChangeType.DirectiveDescriptionChanged,
|
|
30
|
+
message: `Directive '${oldDirective.name}' description changed from '${oldDirective.description}' to '${newDirective.description}'`,
|
|
31
|
+
path: `@${oldDirective.name}`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function directiveLocationAdded(directive, location) {
|
|
35
|
+
return {
|
|
36
|
+
criticality: {
|
|
37
|
+
level: CriticalityLevel.NonBreaking,
|
|
38
|
+
},
|
|
39
|
+
type: ChangeType.DirectiveLocationAdded,
|
|
40
|
+
message: `Location '${location}' was added to directive '${directive.name}'`,
|
|
41
|
+
path: `@${directive.name}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export function directiveLocationRemoved(directive, location) {
|
|
45
|
+
return {
|
|
46
|
+
criticality: {
|
|
47
|
+
level: CriticalityLevel.Breaking,
|
|
48
|
+
},
|
|
49
|
+
type: ChangeType.DirectiveLocationRemoved,
|
|
50
|
+
message: `Location '${location}' was removed from directive '${directive.name}'`,
|
|
51
|
+
path: `@${directive.name}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function directiveArgumentAdded(directive, arg) {
|
|
55
|
+
return {
|
|
56
|
+
criticality: {
|
|
57
|
+
level: isNonNullType(arg.type) ? CriticalityLevel.Breaking : CriticalityLevel.NonBreaking,
|
|
58
|
+
},
|
|
59
|
+
type: ChangeType.DirectiveArgumentAdded,
|
|
60
|
+
message: `Argument '${arg.name}' was added to directive '${directive.name}'`,
|
|
61
|
+
path: `@${directive.name}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
export function directiveArgumentRemoved(directive, arg) {
|
|
65
|
+
return {
|
|
66
|
+
criticality: {
|
|
67
|
+
level: CriticalityLevel.Breaking,
|
|
68
|
+
},
|
|
69
|
+
type: ChangeType.DirectiveArgumentRemoved,
|
|
70
|
+
message: `Argument '${arg.name}' was removed from directive '${directive.name}'`,
|
|
71
|
+
path: `@${directive.name}.${arg.name}`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function directiveArgumentDescriptionChanged(directive, oldArg, newArg) {
|
|
75
|
+
return {
|
|
76
|
+
criticality: {
|
|
77
|
+
level: CriticalityLevel.NonBreaking,
|
|
78
|
+
},
|
|
79
|
+
type: ChangeType.DirectiveArgumentDescriptionChanged,
|
|
80
|
+
message: `Description for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.description}' to '${newArg.description}'`,
|
|
81
|
+
path: `@${directive.name}.${oldArg.name}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export function directiveArgumentDefaultValueChanged(directive, oldArg, newArg) {
|
|
85
|
+
return {
|
|
86
|
+
criticality: {
|
|
87
|
+
level: CriticalityLevel.Dangerous,
|
|
88
|
+
reason: 'Changing the default value for an argument may change the runtime behaviour of a field if it was never provided.',
|
|
89
|
+
},
|
|
90
|
+
type: ChangeType.DirectiveArgumentDefaultValueChanged,
|
|
91
|
+
message: typeof oldArg.defaultValue === 'undefined'
|
|
92
|
+
? `Default value '${newArg.defaultValue}' was added to argument '${newArg.name}' on directive '${directive.name}'`
|
|
93
|
+
: `Default value for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.defaultValue}' to '${newArg.defaultValue}'`,
|
|
94
|
+
path: `@${directive.name}.${oldArg.name}`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export function directiveArgumentTypeChanged(directive, oldArg, newArg) {
|
|
98
|
+
return {
|
|
99
|
+
criticality: safeChangeForInputValue(oldArg.type, newArg.type)
|
|
100
|
+
? {
|
|
101
|
+
level: CriticalityLevel.NonBreaking,
|
|
102
|
+
reason: 'Changing an input field from non-null to null is considered non-breaking.',
|
|
103
|
+
}
|
|
104
|
+
: {
|
|
105
|
+
level: CriticalityLevel.Breaking,
|
|
106
|
+
},
|
|
107
|
+
type: ChangeType.DirectiveArgumentTypeChanged,
|
|
108
|
+
message: `Type for argument '${oldArg.name}' on directive '${directive.name}' changed from '${oldArg.type}' to '${newArg.type}'`,
|
|
109
|
+
path: `@${directive.name}.${oldArg.name}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { isDeprecated } from '../../utils/is-deprecated.js';
|
|
2
|
+
import { ChangeType, CriticalityLevel } from './change.js';
|
|
3
|
+
export function enumValueRemoved(oldEnum, value) {
|
|
4
|
+
return {
|
|
5
|
+
criticality: {
|
|
6
|
+
level: CriticalityLevel.Breaking,
|
|
7
|
+
reason: `Removing an enum value will cause existing queries that use this enum value to error.`,
|
|
8
|
+
},
|
|
9
|
+
type: ChangeType.EnumValueRemoved,
|
|
10
|
+
message: `Enum value '${value.name}' ${isDeprecated(value) ? '(deprecated) ' : ''}was removed from enum '${oldEnum.name}'`,
|
|
11
|
+
path: [oldEnum.name, value.name].join('.'),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function enumValueAdded(newEnum, value) {
|
|
15
|
+
return {
|
|
16
|
+
criticality: {
|
|
17
|
+
level: CriticalityLevel.Dangerous,
|
|
18
|
+
reason: `Adding an enum value may break existing clients that were not programming defensively against an added case when querying an enum.`,
|
|
19
|
+
},
|
|
20
|
+
type: ChangeType.EnumValueAdded,
|
|
21
|
+
message: `Enum value '${value.name}' was added to enum '${newEnum.name}'`,
|
|
22
|
+
path: [newEnum.name, value.name].join('.'),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function enumValueDescriptionChanged(newEnum, oldValue, newValue) {
|
|
26
|
+
return {
|
|
27
|
+
criticality: {
|
|
28
|
+
level: CriticalityLevel.NonBreaking,
|
|
29
|
+
},
|
|
30
|
+
type: ChangeType.EnumValueDescriptionChanged,
|
|
31
|
+
message: typeof oldValue.description === 'undefined'
|
|
32
|
+
? `Description '${newValue.description}' was added to enum value '${newEnum.name}.${newValue.name}'`
|
|
33
|
+
: `Description for enum value '${newEnum.name}.${newValue.name}' changed from '${oldValue.description}' to '${newValue.description}'`,
|
|
34
|
+
path: [newEnum.name, oldValue.name].join('.'),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function enumValueDeprecationReasonChanged(newEnum, oldValue, newValue) {
|
|
38
|
+
return {
|
|
39
|
+
criticality: {
|
|
40
|
+
level: CriticalityLevel.NonBreaking,
|
|
41
|
+
},
|
|
42
|
+
type: ChangeType.EnumValueDeprecationReasonChanged,
|
|
43
|
+
message: `Enum value '${newEnum.name}.${newValue.name}' deprecation reason changed from '${oldValue.deprecationReason}' to '${newValue.deprecationReason}'`,
|
|
44
|
+
path: [newEnum.name, oldValue.name].join('.'),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function enumValueDeprecationReasonAdded(newEnum, oldValue, newValue) {
|
|
48
|
+
return {
|
|
49
|
+
criticality: {
|
|
50
|
+
level: CriticalityLevel.NonBreaking,
|
|
51
|
+
},
|
|
52
|
+
type: ChangeType.EnumValueDeprecationReasonAdded,
|
|
53
|
+
message: `Enum value '${newEnum.name}.${newValue.name}' was deprecated with reason '${newValue.deprecationReason}'`,
|
|
54
|
+
path: [newEnum.name, oldValue.name].join('.'),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue) {
|
|
58
|
+
return {
|
|
59
|
+
criticality: {
|
|
60
|
+
level: CriticalityLevel.NonBreaking,
|
|
61
|
+
},
|
|
62
|
+
type: ChangeType.EnumValueDeprecationReasonRemoved,
|
|
63
|
+
message: `Deprecation reason was removed from enum value '${newEnum.name}.${newValue.name}'`,
|
|
64
|
+
path: [newEnum.name, oldValue.name].join('.'),
|
|
65
|
+
};
|
|
66
|
+
}
|