@graphql-inspector/core 0.0.0-canary.da4bdcc → 0.0.0-canary.e5ed377

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.
@@ -2,15 +2,39 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
+
7
+ const tslib = require('tslib');
5
8
  const graphql = require('graphql');
9
+ const inspect = _interopDefault(require('object-inspect'));
6
10
  const dependencyGraph = require('dependency-graph');
7
11
 
12
+ function keyMap(list, keyFn) {
13
+ return list.reduce((map, item) => {
14
+ map[keyFn(item)] = item;
15
+ return map;
16
+ }, Object.create(null));
17
+ }
8
18
  function isEqual(a, b) {
9
19
  if (Array.isArray(a) && Array.isArray(b)) {
10
20
  if (a.length !== b.length)
11
21
  return false;
12
22
  for (var index = 0; index < a.length; index++) {
13
- if (a[index] !== b[index]) {
23
+ if (!isEqual(a[index], b[index])) {
24
+ return false;
25
+ }
26
+ }
27
+ return true;
28
+ }
29
+ if (a && b && typeof a === 'object' && typeof b === 'object') {
30
+ const aRecord = a;
31
+ const bRecord = b;
32
+ const aKeys = Object.keys(aRecord);
33
+ const bKeys = Object.keys(bRecord);
34
+ if (aKeys.length !== bKeys.length)
35
+ return false;
36
+ for (const key of aKeys) {
37
+ if (!isEqual(aRecord[key], bRecord[key])) {
14
38
  return false;
15
39
  }
16
40
  }
@@ -24,12 +48,62 @@ function isNotEqual(a, b) {
24
48
  function isVoid(a) {
25
49
  return typeof a === 'undefined' || a === null;
26
50
  }
27
-
28
51
  function diffArrays(a, b) {
29
- return a.filter(c => !b.some(d => d === c));
52
+ return a.filter(c => !b.some(d => isEqual(d, c)));
53
+ }
54
+ function compareLists(oldList, newList, callbacks) {
55
+ const oldMap = keyMap(oldList, ({ name }) => name);
56
+ const newMap = keyMap(newList, ({ name }) => name);
57
+ const added = [];
58
+ const removed = [];
59
+ const mutual = [];
60
+ for (const oldItem of oldList) {
61
+ const newItem = newMap[oldItem.name];
62
+ if (newItem === undefined) {
63
+ removed.push(oldItem);
64
+ }
65
+ else {
66
+ mutual.push({
67
+ newVersion: newItem,
68
+ oldVersion: oldItem,
69
+ });
70
+ }
71
+ }
72
+ for (const newItem of newList) {
73
+ if (oldMap[newItem.name] === undefined) {
74
+ added.push(newItem);
75
+ }
76
+ }
77
+ if (callbacks) {
78
+ if (callbacks.onAdded) {
79
+ added.forEach(callbacks.onAdded);
80
+ }
81
+ if (callbacks.onRemoved) {
82
+ removed.forEach(callbacks.onRemoved);
83
+ }
84
+ if (callbacks.onMutual) {
85
+ mutual.forEach(callbacks.onMutual);
86
+ }
87
+ }
88
+ return {
89
+ added,
90
+ removed,
91
+ mutual,
92
+ };
30
93
  }
31
- function unionArrays(a, b) {
32
- return a.filter(c => b.some(d => d === c));
94
+
95
+ function isDeprecated(fieldOrEnumValue) {
96
+ var _a, _b;
97
+ if ('isDeprecated' in fieldOrEnumValue) {
98
+ return fieldOrEnumValue['isDeprecated'];
99
+ }
100
+ if (fieldOrEnumValue.deprecationReason != null) {
101
+ return true;
102
+ }
103
+ if ((_b = (_a = fieldOrEnumValue.astNode) === null || _a === void 0 ? void 0 : _a.directives) === null || _b === void 0 ? void 0 : _b.some(directive => directive.name.value === 'deprecated')) {
104
+ return true;
105
+ }
106
+ return false;
33
107
  }
34
108
 
35
109
  function safeChangeForField(oldType, newType) {
@@ -41,8 +115,7 @@ function safeChangeForField(oldType, newType) {
41
115
  return safeChangeForField(ofType, newType.ofType);
42
116
  }
43
117
  if (graphql.isListType(oldType)) {
44
- return ((graphql.isListType(newType) &&
45
- safeChangeForField(oldType.ofType, newType.ofType)) ||
118
+ return ((graphql.isListType(newType) && safeChangeForField(oldType.ofType, newType.ofType)) ||
46
119
  (graphql.isNonNullType(newType) && safeChangeForField(oldType, newType.ofType)));
47
120
  }
48
121
  return false;
@@ -55,7 +128,7 @@ function safeChangeForInputValue(oldType, newType) {
55
128
  return safeChangeForInputValue(oldType.ofType, newType.ofType);
56
129
  }
57
130
  if (graphql.isNonNullType(oldType)) {
58
- const ofType = graphql.isNonNullType(newType) ? newType : newType;
131
+ const ofType = graphql.isNonNullType(newType) ? newType.ofType : newType;
59
132
  return safeChangeForInputValue(oldType.ofType, ofType);
60
133
  }
61
134
  return false;
@@ -74,10 +147,10 @@ function getTypePrefix(type) {
74
147
  [graphql.Kind.ENUM_TYPE_DEFINITION]: 'enum',
75
148
  [graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION]: 'input',
76
149
  };
77
- return kindsMap[kind];
150
+ return kindsMap[kind.toString()];
78
151
  }
79
152
  function isPrimitive(type) {
80
- return (['String', 'Int', 'Float', 'Boolean', 'ID'].indexOf(typeof type === 'string' ? type : type.name) !== -1);
153
+ return ['String', 'Int', 'Float', 'Boolean', 'ID'].indexOf(typeof type === 'string' ? type : type.name) !== -1;
81
154
  }
82
155
  function isForIntrospection(type) {
83
156
  return ([
@@ -95,9 +168,23 @@ function findDeprecatedUsages(schema, ast) {
95
168
  const errors = [];
96
169
  const typeInfo = new graphql.TypeInfo(schema);
97
170
  graphql.visit(ast, graphql.visitWithTypeInfo(typeInfo, {
171
+ Argument(node) {
172
+ const argument = typeInfo.getArgument();
173
+ if (argument) {
174
+ const reason = argument.deprecationReason;
175
+ if (reason) {
176
+ const fieldDef = typeInfo.getFieldDef();
177
+ if (fieldDef) {
178
+ errors.push(new graphql.GraphQLError(`The argument '${argument === null || argument === void 0 ? void 0 : argument.name}' of '${fieldDef.name}' is deprecated. ${reason}`, [
179
+ node,
180
+ ]));
181
+ }
182
+ }
183
+ }
184
+ },
98
185
  Field(node) {
99
186
  const fieldDef = typeInfo.getFieldDef();
100
- if (fieldDef && fieldDef.isDeprecated) {
187
+ if (fieldDef && isDeprecated(fieldDef)) {
101
188
  const parentType = typeInfo.getParentType();
102
189
  if (parentType) {
103
190
  const reason = fieldDef.deprecationReason;
@@ -107,7 +194,7 @@ function findDeprecatedUsages(schema, ast) {
107
194
  },
108
195
  EnumValue(node) {
109
196
  const enumVal = typeInfo.getEnumValue();
110
- if (enumVal && enumVal.isDeprecated) {
197
+ if (enumVal && isDeprecated(enumVal)) {
111
198
  const type = graphql.getNamedType(typeInfo.getInputType());
112
199
  if (type) {
113
200
  const reason = enumVal.deprecationReason;
@@ -132,6 +219,71 @@ function removeDirectives(node, directiveNames) {
132
219
  }
133
220
  return node;
134
221
  }
222
+ function getReachableTypes(schema) {
223
+ const reachableTypes = new Set();
224
+ const collect = (type) => {
225
+ const typeName = type.name;
226
+ if (reachableTypes.has(typeName)) {
227
+ return;
228
+ }
229
+ reachableTypes.add(typeName);
230
+ if (graphql.isScalarType(type)) {
231
+ return;
232
+ }
233
+ else if (graphql.isInterfaceType(type) || graphql.isObjectType(type)) {
234
+ if (graphql.isInterfaceType(type)) {
235
+ const { objects, interfaces } = schema.getImplementations(type);
236
+ for (const child of objects) {
237
+ collect(child);
238
+ }
239
+ for (const child of interfaces) {
240
+ collect(child);
241
+ }
242
+ }
243
+ const fields = type.getFields();
244
+ for (const fieldName in fields) {
245
+ const field = fields[fieldName];
246
+ collect(resolveOutputType(field.type));
247
+ const args = field.args;
248
+ for (const argName in args) {
249
+ const arg = args[argName];
250
+ collect(resolveInputType(arg.type));
251
+ }
252
+ }
253
+ }
254
+ else if (graphql.isUnionType(type)) {
255
+ const types = type.getTypes();
256
+ for (const child of types) {
257
+ collect(child);
258
+ }
259
+ }
260
+ else if (graphql.isInputObjectType(type)) {
261
+ const fields = type.getFields();
262
+ for (const fieldName in fields) {
263
+ const field = fields[fieldName];
264
+ collect(resolveInputType(field.type));
265
+ }
266
+ }
267
+ };
268
+ for (const type of [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()]) {
269
+ if (type) {
270
+ collect(type);
271
+ }
272
+ }
273
+ return reachableTypes;
274
+ }
275
+ function resolveOutputType(output) {
276
+ if (graphql.isListType(output) || graphql.isNonNullType(output)) {
277
+ return resolveOutputType(output.ofType);
278
+ }
279
+ return output;
280
+ }
281
+ function resolveInputType(input) {
282
+ if (graphql.isListType(input) || graphql.isNonNullType(input)) {
283
+ return resolveInputType(input.ofType);
284
+ }
285
+ return input;
286
+ }
135
287
 
136
288
  (function (ChangeType) {
137
289
  // Argument
@@ -154,6 +306,8 @@ function removeDirectives(node, directiveNames) {
154
306
  ChangeType["EnumValueAdded"] = "ENUM_VALUE_ADDED";
155
307
  ChangeType["EnumValueDescriptionChanged"] = "ENUM_VALUE_DESCRIPTION_CHANGED";
156
308
  ChangeType["EnumValueDeprecationReasonChanged"] = "ENUM_VALUE_DEPRECATION_REASON_CHANGED";
309
+ ChangeType["EnumValueDeprecationReasonAdded"] = "ENUM_VALUE_DEPRECATION_REASON_ADDED";
310
+ ChangeType["EnumValueDeprecationReasonRemoved"] = "ENUM_VALUE_DEPRECATION_REASON_REMOVED";
157
311
  // Field
158
312
  ChangeType["FieldRemoved"] = "FIELD_REMOVED";
159
313
  ChangeType["FieldAdded"] = "FIELD_ADDED";
@@ -351,9 +505,7 @@ function directiveLocationRemoved(directive, location) {
351
505
  function directiveArgumentAdded(directive, arg) {
352
506
  return {
353
507
  criticality: {
354
- level: graphql.isNonNullType(arg.type)
355
- ? exports.CriticalityLevel.Breaking
356
- : exports.CriticalityLevel.NonBreaking,
508
+ level: graphql.isNonNullType(arg.type) ? exports.CriticalityLevel.Breaking : exports.CriticalityLevel.NonBreaking,
357
509
  },
358
510
  type: exports.ChangeType.DirectiveArgumentAdded,
359
511
  message: `Argument '${arg.name}' was added to directive '${directive.name}'`,
@@ -416,7 +568,7 @@ function enumValueRemoved(oldEnum, value) {
416
568
  reason: `Removing an enum value will cause existing queries that use this enum value to error.`,
417
569
  },
418
570
  type: exports.ChangeType.EnumValueRemoved,
419
- message: `Enum value '${value.name}' was removed from enum '${oldEnum.name}'`,
571
+ message: `Enum value '${value.name}' ${isDeprecated(value) ? '(deprecated) ' : ''}was removed from enum '${oldEnum.name}'`,
420
572
  path: [oldEnum.name, value.name].join('.'),
421
573
  };
422
574
  }
@@ -448,35 +600,59 @@ function enumValueDeprecationReasonChanged(newEnum, oldValue, newValue) {
448
600
  criticality: {
449
601
  level: exports.CriticalityLevel.NonBreaking,
450
602
  },
451
- type: exports.ChangeType.EnumValueDescriptionChanged,
452
- message: oldValue.deprecationReason
453
- ? `Enum value '${newEnum.name}.${newValue.name}' deprecation reason changed from '${oldValue.deprecationReason}' to '${newValue.deprecationReason}'`
454
- : `Enum value '${newEnum.name}.${newValue.name}' was deprecated with reason '${newValue.deprecationReason}'`,
603
+ type: exports.ChangeType.EnumValueDeprecationReasonChanged,
604
+ message: `Enum value '${newEnum.name}.${newValue.name}' deprecation reason changed from '${oldValue.deprecationReason}' to '${newValue.deprecationReason}'`,
605
+ path: [newEnum.name, oldValue.name].join('.'),
606
+ };
607
+ }
608
+ function enumValueDeprecationReasonAdded(newEnum, oldValue, newValue) {
609
+ return {
610
+ criticality: {
611
+ level: exports.CriticalityLevel.NonBreaking,
612
+ },
613
+ type: exports.ChangeType.EnumValueDeprecationReasonAdded,
614
+ message: `Enum value '${newEnum.name}.${newValue.name}' was deprecated with reason '${newValue.deprecationReason}'`,
615
+ path: [newEnum.name, oldValue.name].join('.'),
616
+ };
617
+ }
618
+ function enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue) {
619
+ return {
620
+ criticality: {
621
+ level: exports.CriticalityLevel.NonBreaking,
622
+ },
623
+ type: exports.ChangeType.EnumValueDeprecationReasonRemoved,
624
+ message: `Deprecation reason was removed from enum value '${newEnum.name}.${newValue.name}'`,
455
625
  path: [newEnum.name, oldValue.name].join('.'),
456
626
  };
457
627
  }
458
628
 
459
- function changesInEnum(oldEnum, newEnum) {
460
- const changes = [];
461
- const oldNames = oldEnum.getValues().map(v => v.name);
462
- const newNames = newEnum.getValues().map(v => v.name);
463
- const added = diffArrays(newNames, oldNames).map(name => newEnum.getValue(name));
464
- const removed = diffArrays(oldNames, newNames).map(name => oldEnum.getValue(name));
465
- const common = unionArrays(oldNames, newNames).map(name => ({
466
- oldValue: oldEnum.getValue(name),
467
- newValue: newEnum.getValue(name),
468
- }));
469
- changes.push(...added.map(v => enumValueAdded(newEnum, v)));
470
- changes.push(...removed.map(v => enumValueRemoved(oldEnum, v)));
471
- common.forEach(({ oldValue, newValue }) => {
472
- if (isNotEqual(oldValue.description, newValue.description)) {
473
- changes.push(enumValueDescriptionChanged(newEnum, oldValue, newValue));
474
- }
475
- if (isNotEqual(oldValue.deprecationReason, newValue.deprecationReason)) {
476
- changes.push(enumValueDeprecationReasonChanged(newEnum, oldValue, newValue));
477
- }
629
+ function changesInEnum(oldEnum, newEnum, addChange) {
630
+ compareLists(oldEnum.getValues(), newEnum.getValues(), {
631
+ onAdded(value) {
632
+ addChange(enumValueAdded(newEnum, value));
633
+ },
634
+ onRemoved(value) {
635
+ addChange(enumValueRemoved(oldEnum, value));
636
+ },
637
+ onMutual(value) {
638
+ const oldValue = value.oldVersion;
639
+ const newValue = value.newVersion;
640
+ if (isNotEqual(oldValue.description, newValue.description)) {
641
+ addChange(enumValueDescriptionChanged(newEnum, oldValue, newValue));
642
+ }
643
+ if (isNotEqual(oldValue.deprecationReason, newValue.deprecationReason)) {
644
+ if (isVoid(oldValue.deprecationReason)) {
645
+ addChange(enumValueDeprecationReasonAdded(newEnum, oldValue, newValue));
646
+ }
647
+ else if (isVoid(newValue.deprecationReason)) {
648
+ addChange(enumValueDeprecationReasonRemoved(newEnum, oldValue, newValue));
649
+ }
650
+ else {
651
+ addChange(enumValueDeprecationReasonChanged(newEnum, oldValue, newValue));
652
+ }
653
+ }
654
+ },
478
655
  });
479
- return changes;
480
656
  }
481
657
 
482
658
  function unionMemberRemoved(union, type) {
@@ -502,17 +678,81 @@ function unionMemberAdded(union, type) {
502
678
  };
503
679
  }
504
680
 
505
- function changesInUnion(oldUnion, newUnion) {
506
- const changes = [];
681
+ function changesInUnion(oldUnion, newUnion, addChange) {
507
682
  const oldTypes = oldUnion.getTypes();
508
683
  const newTypes = newUnion.getTypes();
509
- const oldTypenames = oldTypes.map(t => t.name);
510
- const newTypenames = newTypes.map(t => t.name);
511
- const added = diffArrays(newTypenames, oldTypenames).map(name => newTypes.find(t => t.name === name));
512
- const removed = diffArrays(oldTypenames, newTypenames).map(name => oldTypes.find(t => t.name === name));
513
- changes.push(...added.map(type => unionMemberAdded(newUnion, type)));
514
- changes.push(...removed.map(type => unionMemberRemoved(oldUnion, type)));
515
- return changes;
684
+ compareLists(oldTypes, newTypes, {
685
+ onAdded(t) {
686
+ addChange(unionMemberAdded(newUnion, t));
687
+ },
688
+ onRemoved(t) {
689
+ addChange(unionMemberRemoved(oldUnion, t));
690
+ },
691
+ });
692
+ }
693
+
694
+ function compareTwoStrings(str1, str2) {
695
+ if (!str1.length && !str2.length)
696
+ return 1;
697
+ if (!str1.length || !str2.length)
698
+ return 0;
699
+ if (str1.toUpperCase() === str2.toUpperCase())
700
+ return 1;
701
+ if (str1.length === 1 && str2.length === 1)
702
+ return 0;
703
+ const pairs1 = wordLetterPairs(str1);
704
+ const pairs2 = wordLetterPairs(str2);
705
+ const union = pairs1.length + pairs2.length;
706
+ let intersection = 0;
707
+ pairs1.forEach(pair1 => {
708
+ for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
709
+ if (pair1 !== pair2)
710
+ continue;
711
+ intersection++;
712
+ pairs2.splice(i, 1);
713
+ break;
714
+ }
715
+ });
716
+ return (intersection * 2) / union;
717
+ }
718
+ function findBestMatch(mainString, targetStrings) {
719
+ if (!areArgsValid(mainString, targetStrings))
720
+ throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
721
+ const ratings = targetStrings.map(target => ({
722
+ target,
723
+ rating: compareTwoStrings(mainString, target.value),
724
+ }));
725
+ const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
726
+ return { ratings, bestMatch };
727
+ }
728
+ function flattenDeep(arr) {
729
+ return Array.isArray(arr) ? arr.reduce((a, b) => a.concat(flattenDeep(b)), []) : [arr];
730
+ }
731
+ function areArgsValid(mainString, targetStrings) {
732
+ if (typeof mainString !== 'string')
733
+ return false;
734
+ if (!Array.isArray(targetStrings))
735
+ return false;
736
+ if (!targetStrings.length)
737
+ return false;
738
+ if (targetStrings.find(s => typeof s.value !== 'string'))
739
+ return false;
740
+ return true;
741
+ }
742
+ function letterPairs(str) {
743
+ const pairs = [];
744
+ for (let i = 0, max = str.length - 1; i < max; i++)
745
+ pairs[i] = str.substring(i, i + 2);
746
+ return pairs;
747
+ }
748
+ function wordLetterPairs(str) {
749
+ const pairs = str.toUpperCase().split(' ').map(letterPairs);
750
+ return flattenDeep(pairs);
751
+ }
752
+ function safeString(obj) {
753
+ return inspect(obj)
754
+ .replace(/\[Object\: null prototype\] /g, '')
755
+ .replace(/(^')|('$)/g, '');
516
756
  }
517
757
 
518
758
  function inputFieldRemoved(input, field) {
@@ -522,7 +762,7 @@ function inputFieldRemoved(input, field) {
522
762
  reason: 'Removing an input field will cause existing queries that use this input field to error.',
523
763
  },
524
764
  type: exports.ChangeType.InputFieldRemoved,
525
- message: `Input field '${field.name}' was removed from input object type '${input.name}'`,
765
+ message: `Input field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from input object type '${input.name}'`,
526
766
  path: [input.name, field.name].join('.'),
527
767
  };
528
768
  }
@@ -531,7 +771,7 @@ function inputFieldAdded(input, field) {
531
771
  criticality: graphql.isNonNullType(field.type)
532
772
  ? {
533
773
  level: exports.CriticalityLevel.Breaking,
534
- reason: 'Adding a possible type to Unions may break existing clients that were not programming defensively against a new possible type.',
774
+ reason: 'Adding a required input field to an existing input object type is a breaking change because it will cause existing uses of this input object type to error.',
535
775
  }
536
776
  : {
537
777
  level: exports.CriticalityLevel.Dangerous,
@@ -575,10 +815,10 @@ function inputFieldDefaultValueChanged(input, oldField, newField) {
575
815
  return {
576
816
  criticality: {
577
817
  level: exports.CriticalityLevel.Dangerous,
578
- reason: 'Changing the default value for an argument may change the runtime behaviour of a field if it was never provided.',
818
+ reason: 'Changing the default value for an argument may change the runtime behavior of a field if it was never provided.',
579
819
  },
580
820
  type: exports.ChangeType.InputFieldDefaultValueChanged,
581
- message: `Input field '${input.name}.${oldField.name}' default value changed from '${oldField.defaultValue}' to '${newField.defaultValue}'`,
821
+ message: `Input field '${input.name}.${oldField.name}' default value changed from '${safeString(oldField.defaultValue)}' to '${safeString(newField.defaultValue)}'`,
582
822
  path: [input.name, oldField.name].join('.'),
583
823
  };
584
824
  }
@@ -599,55 +839,46 @@ function inputFieldTypeChanged(input, oldField, newField) {
599
839
  };
600
840
  }
601
841
 
602
- function changesInInputObject(oldInput, newInput) {
603
- const changes = [];
842
+ function changesInInputObject(oldInput, newInput, addChange) {
604
843
  const oldFields = oldInput.getFields();
605
844
  const newFields = newInput.getFields();
606
- const oldNames = Object.keys(oldFields).map(name => oldFields[name].name);
607
- const newNames = Object.keys(newFields).map(name => newFields[name].name);
608
- const added = diffArrays(newNames, oldNames).map(name => newFields[name]);
609
- const removed = diffArrays(oldNames, newNames).map(name => oldFields[name]);
610
- const common = unionArrays(oldNames, newNames).map(name => ({
611
- inOld: oldFields[name],
612
- inNew: newFields[name],
613
- }));
614
- common.forEach(({ inOld, inNew }) => {
615
- changes.push(...changesInInputField(oldInput, inOld, inNew));
845
+ compareLists(Object.values(oldFields), Object.values(newFields), {
846
+ onAdded(field) {
847
+ addChange(inputFieldAdded(newInput, field));
848
+ },
849
+ onRemoved(field) {
850
+ addChange(inputFieldRemoved(oldInput, field));
851
+ },
852
+ onMutual(field) {
853
+ changesInInputField(oldInput, field.oldVersion, field.newVersion, addChange);
854
+ },
616
855
  });
617
- changes.push(...added.map(field => inputFieldAdded(newInput, field)));
618
- changes.push(...removed.map(field => inputFieldRemoved(oldInput, field)));
619
- return changes;
620
856
  }
621
- function changesInInputField(input, oldField, newField) {
622
- const changes = [];
857
+ function changesInInputField(input, oldField, newField, addChange) {
623
858
  if (isNotEqual(oldField.description, newField.description)) {
624
859
  if (isVoid(oldField.description)) {
625
- changes.push(inputFieldDescriptionAdded(input, newField));
860
+ addChange(inputFieldDescriptionAdded(input, newField));
626
861
  }
627
862
  else if (isVoid(newField.description)) {
628
- changes.push(inputFieldDescriptionRemoved(input, oldField));
863
+ addChange(inputFieldDescriptionRemoved(input, oldField));
629
864
  }
630
865
  else {
631
- changes.push(inputFieldDescriptionChanged(input, oldField, newField));
866
+ addChange(inputFieldDescriptionChanged(input, oldField, newField));
632
867
  }
633
868
  }
634
869
  if (isNotEqual(oldField.defaultValue, newField.defaultValue)) {
635
- if (Array.isArray(oldField.defaultValue) &&
636
- Array.isArray(newField.defaultValue)) {
870
+ if (Array.isArray(oldField.defaultValue) && Array.isArray(newField.defaultValue)) {
637
871
  if (diffArrays(oldField.defaultValue, newField.defaultValue).length > 0) {
638
- changes.push(inputFieldDefaultValueChanged(input, oldField, newField));
872
+ addChange(inputFieldDefaultValueChanged(input, oldField, newField));
639
873
  }
640
874
  }
641
- else if (JSON.stringify(oldField.defaultValue) !==
642
- JSON.stringify(newField.defaultValue)) {
643
- changes.push(inputFieldDefaultValueChanged(input, oldField, newField));
875
+ else if (JSON.stringify(oldField.defaultValue) !== JSON.stringify(newField.defaultValue)) {
876
+ addChange(inputFieldDefaultValueChanged(input, oldField, newField));
644
877
  }
645
878
  }
646
879
  if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
647
- changes.push(inputFieldTypeChanged(input, oldField, newField));
880
+ addChange(inputFieldTypeChanged(input, oldField, newField));
648
881
  }
649
- // TODO: diff directives
650
- return changes;
651
882
  }
652
883
 
653
884
  function objectTypeInterfaceAdded(iface, type) {
@@ -683,7 +914,7 @@ function fieldRemoved(type, field) {
683
914
  : `Removing a field is a breaking change. It is preferable to deprecate the field before removing it.`,
684
915
  },
685
916
  type: exports.ChangeType.FieldRemoved,
686
- message: `Field '${field.name}' ${field.isDeprecated ? '(deprecated) ' : ''}was removed from ${entity} '${type.name}'`,
917
+ message: `Field '${field.name}' ${isDeprecated(field) ? '(deprecated) ' : ''}was removed from ${entity} '${type.name}'`,
687
918
  path: [type.name, field.name].join('.'),
688
919
  };
689
920
  }
@@ -791,8 +1022,10 @@ function fieldTypeChanged(type, oldField, newField) {
791
1022
  };
792
1023
  }
793
1024
  function fieldArgumentAdded(type, field, arg) {
1025
+ const isBreaking = graphql.isNonNullType(arg.type) && typeof arg.defaultValue === 'undefined';
1026
+ const defaultValueMsg = typeof arg.defaultValue !== 'undefined' ? ' (with default value) ' : ' ';
794
1027
  return {
795
- criticality: graphql.isNonNullType(arg.type)
1028
+ criticality: isBreaking
796
1029
  ? {
797
1030
  level: exports.CriticalityLevel.Breaking,
798
1031
  reason: `Adding a required argument to an existing field is a breaking change because it will cause existing uses of this field to error.`,
@@ -802,7 +1035,7 @@ function fieldArgumentAdded(type, field, arg) {
802
1035
  reason: `Adding a new argument to an existing field may involve a change in resolve function logic that potentially may cause some side effects.`,
803
1036
  },
804
1037
  type: exports.ChangeType.FieldArgumentAdded,
805
- message: `Argument '${arg.name}: ${arg.type}' added to field '${type.name}.${field.name}'`,
1038
+ message: `Argument '${arg.name}: ${arg.type}'${defaultValueMsg}added to field '${type.name}.${field.name}'`,
806
1039
  path: [type.name, field.name, arg.name].join('.'),
807
1040
  };
808
1041
  }
@@ -836,8 +1069,8 @@ function fieldArgumentDefaultChanged(type, field, oldArg, newArg) {
836
1069
  },
837
1070
  type: exports.ChangeType.FieldArgumentDefaultChanged,
838
1071
  message: typeof oldArg.defaultValue === 'undefined'
839
- ? `Default value '${newArg.defaultValue}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
840
- : `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${oldArg.defaultValue}' to '${newArg.defaultValue}'`,
1072
+ ? `Default value '${safeString(newArg.defaultValue)}' was added to argument '${newArg.name}' on field '${type.name}.${field.name}'`
1073
+ : `Default value for argument '${newArg.name}' on field '${type.name}.${field.name}' changed from '${safeString(oldArg.defaultValue)}' to '${safeString(newArg.defaultValue)}'`,
841
1074
  path: [type.name, field.name, oldArg.name].join('.'),
842
1075
  };
843
1076
  }
@@ -858,339 +1091,253 @@ function fieldArgumentTypeChanged(type, field, oldArg, newArg) {
858
1091
  };
859
1092
  }
860
1093
 
861
- function changesInArgument(type, field, oldArg, newArg) {
862
- const changes = [];
1094
+ function changesInArgument(type, field, oldArg, newArg, addChange) {
863
1095
  if (isNotEqual(oldArg.description, newArg.description)) {
864
- changes.push(fieldArgumentDescriptionChanged(type, field, oldArg, newArg));
1096
+ addChange(fieldArgumentDescriptionChanged(type, field, oldArg, newArg));
865
1097
  }
866
1098
  if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
867
- if (Array.isArray(oldArg.defaultValue) &&
868
- Array.isArray(newArg.defaultValue)) {
1099
+ if (Array.isArray(oldArg.defaultValue) && Array.isArray(newArg.defaultValue)) {
869
1100
  const diff = diffArrays(oldArg.defaultValue, newArg.defaultValue);
870
1101
  if (diff.length > 0) {
871
- changes.push(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
1102
+ addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
872
1103
  }
873
1104
  }
874
- else if (JSON.stringify(oldArg.defaultValue) !==
875
- JSON.stringify(newArg.defaultValue)) {
876
- changes.push(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
1105
+ else if (JSON.stringify(oldArg.defaultValue) !== JSON.stringify(newArg.defaultValue)) {
1106
+ addChange(fieldArgumentDefaultChanged(type, field, oldArg, newArg));
877
1107
  }
878
1108
  }
879
1109
  if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
880
- changes.push(fieldArgumentTypeChanged(type, field, oldArg, newArg));
1110
+ addChange(fieldArgumentTypeChanged(type, field, oldArg, newArg));
881
1111
  }
882
- return changes;
883
1112
  }
884
1113
 
885
- function changesInField(type, oldField, newField) {
886
- const changes = [];
1114
+ function changesInField(type, oldField, newField, addChange) {
887
1115
  if (isNotEqual(oldField.description, newField.description)) {
888
1116
  if (isVoid(oldField.description)) {
889
- changes.push(fieldDescriptionAdded(type, newField));
1117
+ addChange(fieldDescriptionAdded(type, newField));
890
1118
  }
891
1119
  else if (isVoid(newField.description)) {
892
- changes.push(fieldDescriptionRemoved(type, oldField));
1120
+ addChange(fieldDescriptionRemoved(type, oldField));
893
1121
  }
894
1122
  else {
895
- changes.push(fieldDescriptionChanged(type, oldField, newField));
1123
+ addChange(fieldDescriptionChanged(type, oldField, newField));
896
1124
  }
897
1125
  }
898
- if (isNotEqual(oldField.isDeprecated, newField.isDeprecated)) {
899
- if (newField.isDeprecated) {
900
- changes.push(fieldDeprecationAdded(type, newField));
1126
+ if (isNotEqual(isDeprecated(oldField), isDeprecated(newField))) {
1127
+ if (isDeprecated(newField)) {
1128
+ addChange(fieldDeprecationAdded(type, newField));
901
1129
  }
902
1130
  else {
903
- changes.push(fieldDeprecationRemoved(type, oldField));
1131
+ addChange(fieldDeprecationRemoved(type, oldField));
904
1132
  }
905
1133
  }
906
1134
  if (isNotEqual(oldField.deprecationReason, newField.deprecationReason)) {
907
1135
  if (isVoid(oldField.deprecationReason)) {
908
- changes.push(fieldDeprecationReasonAdded(type, newField));
1136
+ addChange(fieldDeprecationReasonAdded(type, newField));
909
1137
  }
910
1138
  else if (isVoid(newField.deprecationReason)) {
911
- changes.push(fieldDeprecationReasonRemoved(type, oldField));
1139
+ addChange(fieldDeprecationReasonRemoved(type, oldField));
912
1140
  }
913
1141
  else {
914
- changes.push(fieldDeprecationReasonChanged(type, oldField, newField));
1142
+ addChange(fieldDeprecationReasonChanged(type, oldField, newField));
915
1143
  }
916
1144
  }
917
1145
  if (isNotEqual(oldField.type.toString(), newField.type.toString())) {
918
- changes.push(fieldTypeChanged(type, oldField, newField));
1146
+ addChange(fieldTypeChanged(type, oldField, newField));
919
1147
  }
920
- const oldArgs = oldField.args;
921
- const newArgs = newField.args;
922
- const oldNames = oldArgs.map(a => a.name);
923
- const newNames = newArgs.map(a => a.name);
924
- const added = diffArrays(newNames, oldNames).map(name => newArgs.find(a => a.name === name));
925
- const removed = diffArrays(oldNames, newNames).map(name => oldArgs.find(a => a.name === name));
926
- const common = unionArrays(oldNames, newNames).map(name => ({
927
- inOld: oldArgs.find(a => a.name === name),
928
- inNew: newArgs.find(a => a.name === name),
929
- }));
930
- common.forEach(({ inOld, inNew }) => {
931
- changes.push(...changesInArgument(type, oldField, inOld, inNew));
1148
+ compareLists(oldField.args, newField.args, {
1149
+ onAdded(arg) {
1150
+ addChange(fieldArgumentAdded(type, newField, arg));
1151
+ },
1152
+ onRemoved(arg) {
1153
+ addChange(fieldArgumentRemoved(type, oldField, arg));
1154
+ },
1155
+ onMutual(arg) {
1156
+ changesInArgument(type, oldField, arg.oldVersion, arg.newVersion, addChange);
1157
+ },
932
1158
  });
933
- changes.push(...added.map(arg => fieldArgumentAdded(type, newField, arg)));
934
- changes.push(...removed.map(arg => fieldArgumentRemoved(type, oldField, arg)));
935
- return changes;
936
1159
  }
937
1160
 
938
- function changesInObject(oldType, newType) {
939
- const changes = [];
940
- // Interfaces
941
- changes.push(...addedInterfaces(oldType, newType));
942
- changes.push(...removedInterfaces(oldType, newType));
943
- // Fields
944
- changes.push(...addedFields(oldType, newType));
945
- changes.push(...removedFields(oldType, newType));
946
- changedFields(oldType, newType).forEach(({ inOld, inNew }) => {
947
- changes.push(...changesInField(oldType, inOld, inNew));
948
- });
949
- return changes;
950
- }
951
- function addedInterfaces(oldType, newType) {
952
- const oldInterfaces = oldType.getInterfaces();
953
- const newInterfaces = newType.getInterfaces();
954
- const oldNames = oldInterfaces.map(i => i.name);
955
- const newNames = newInterfaces.map(i => i.name);
956
- return diffArrays(newNames, oldNames)
957
- .map(name => newInterfaces.find(i => i.name === name))
958
- .map(i => objectTypeInterfaceAdded(i, newType));
959
- }
960
- function removedInterfaces(oldType, newType) {
1161
+ function changesInObject(oldType, newType, addChange) {
961
1162
  const oldInterfaces = oldType.getInterfaces();
962
1163
  const newInterfaces = newType.getInterfaces();
963
- const oldNames = oldInterfaces.map(i => i.name);
964
- const newNames = newInterfaces.map(i => i.name);
965
- return diffArrays(oldNames, newNames)
966
- .map(name => oldInterfaces.find(i => i.name === name))
967
- .map(i => objectTypeInterfaceRemoved(i, newType));
968
- }
969
- function addedFields(oldType, newType) {
970
- const oldFields = oldType.getFields();
971
- const newFields = newType.getFields();
972
- const oldNames = Object.keys(oldFields);
973
- const newNames = Object.keys(newFields);
974
- return diffArrays(newNames, oldNames)
975
- .map(name => newFields[name])
976
- .map(f => fieldAdded(newType, f));
977
- }
978
- function removedFields(oldType, newType) {
979
1164
  const oldFields = oldType.getFields();
980
1165
  const newFields = newType.getFields();
981
- const oldNames = Object.keys(oldFields);
982
- const newNames = Object.keys(newFields);
983
- return diffArrays(oldNames, newNames)
984
- .map(name => oldFields[name])
985
- .map(f => fieldRemoved(oldType, f));
986
- }
987
- function changedFields(oldType, newType) {
988
- const oldFields = oldType.getFields();
989
- const newFields = newType.getFields();
990
- const oldNames = Object.keys(oldFields);
991
- const newNames = Object.keys(newFields);
992
- return unionArrays(oldNames, newNames).map(name => ({
993
- inOld: oldFields[name],
994
- inNew: newFields[name],
995
- }));
1166
+ compareLists(oldInterfaces, newInterfaces, {
1167
+ onAdded(i) {
1168
+ addChange(objectTypeInterfaceAdded(i, newType));
1169
+ },
1170
+ onRemoved(i) {
1171
+ addChange(objectTypeInterfaceRemoved(i, oldType));
1172
+ },
1173
+ });
1174
+ compareLists(Object.values(oldFields), Object.values(newFields), {
1175
+ onAdded(f) {
1176
+ addChange(fieldAdded(newType, f));
1177
+ },
1178
+ onRemoved(f) {
1179
+ addChange(fieldRemoved(oldType, f));
1180
+ },
1181
+ onMutual(f) {
1182
+ changesInField(oldType, f.oldVersion, f.newVersion, addChange);
1183
+ },
1184
+ });
996
1185
  }
997
1186
 
998
- function changesInInterface(oldInterface, newInterface) {
999
- const changes = [];
1000
- changes.push(...addedFields$1(oldInterface, newInterface));
1001
- changes.push(...removedFields$1(oldInterface, newInterface));
1002
- changedFields$1(oldInterface, newInterface).forEach(({ inOld, inNew }) => {
1003
- changes.push(...changesInField(oldInterface, inOld, inNew));
1187
+ function changesInInterface(oldInterface, newInterface, addChange) {
1188
+ compareLists(Object.values(oldInterface.getFields()), Object.values(newInterface.getFields()), {
1189
+ onAdded(field) {
1190
+ addChange(fieldAdded(newInterface, field));
1191
+ },
1192
+ onRemoved(field) {
1193
+ addChange(fieldRemoved(oldInterface, field));
1194
+ },
1195
+ onMutual(field) {
1196
+ changesInField(oldInterface, field.oldVersion, field.newVersion, addChange);
1197
+ },
1004
1198
  });
1005
- return changes;
1006
- }
1007
- function addedFields$1(oldInterface, newInterface) {
1008
- const oldFields = oldInterface.getFields();
1009
- const newFields = newInterface.getFields();
1010
- const oldNames = Object.keys(oldFields);
1011
- const newNames = Object.keys(newFields);
1012
- return diffArrays(newNames, oldNames)
1013
- .map(name => newFields[name])
1014
- .map(f => fieldAdded(newInterface, f));
1015
- }
1016
- function removedFields$1(oldInterface, newInterface) {
1017
- const oldFields = oldInterface.getFields();
1018
- const newFields = newInterface.getFields();
1019
- const oldNames = Object.keys(oldFields);
1020
- const newNames = Object.keys(newFields);
1021
- return diffArrays(oldNames, newNames)
1022
- .map(name => oldFields[name])
1023
- .map(f => fieldRemoved(oldInterface, f));
1024
- }
1025
- function changedFields$1(oldInterface, newInterface) {
1026
- const oldFields = oldInterface.getFields();
1027
- const newFields = newInterface.getFields();
1028
- const oldNames = Object.keys(oldFields);
1029
- const newNames = Object.keys(newFields);
1030
- return unionArrays(oldNames, newNames).map(name => ({
1031
- inOld: oldFields[name],
1032
- inNew: newFields[name],
1033
- }));
1034
1199
  }
1035
1200
 
1036
- function changesInDirective(oldDirective, newDirective) {
1037
- const changes = [];
1201
+ function changesInDirective(oldDirective, newDirective, addChange) {
1038
1202
  if (isNotEqual(oldDirective.description, newDirective.description)) {
1039
- changes.push(directiveDescriptionChanged(oldDirective, newDirective));
1203
+ addChange(directiveDescriptionChanged(oldDirective, newDirective));
1040
1204
  }
1205
+ const locations = {
1206
+ added: diffArrays(newDirective.locations, oldDirective.locations),
1207
+ removed: diffArrays(oldDirective.locations, newDirective.locations),
1208
+ };
1041
1209
  // locations added
1042
- changes.push(...diffArrays(newDirective.locations, oldDirective.locations).map(location => directiveLocationAdded(newDirective, location)));
1210
+ locations.added.forEach(location => addChange(directiveLocationAdded(newDirective, location)));
1043
1211
  // locations removed
1044
- changes.push(...diffArrays(oldDirective.locations, newDirective.locations).map(location => directiveLocationRemoved(oldDirective, location)));
1045
- const oldNames = oldDirective.args.map(a => a.name);
1046
- const newNames = newDirective.args.map(a => a.name);
1047
- // arguments added
1048
- changes.push(...diffArrays(newNames, oldNames).map(name => directiveArgumentAdded(newDirective, newDirective.args.find(a => a.name === name))));
1049
- // arguments removed
1050
- changes.push(...diffArrays(oldNames, newNames).map(name => directiveArgumentRemoved(oldDirective, oldDirective.args.find(a => a.name === name))));
1051
- // common arguments
1052
- unionArrays(oldNames, newNames).forEach(name => {
1053
- const oldArg = oldDirective.args.find(a => a.name === name);
1054
- const newArg = newDirective.args.find(a => a.name === name);
1055
- changes.push(...changesInDirectiveArgument(oldDirective, oldArg, newArg));
1212
+ locations.removed.forEach(location => addChange(directiveLocationRemoved(oldDirective, location)));
1213
+ compareLists(oldDirective.args, newDirective.args, {
1214
+ onAdded(arg) {
1215
+ addChange(directiveArgumentAdded(newDirective, arg));
1216
+ },
1217
+ onRemoved(arg) {
1218
+ addChange(directiveArgumentRemoved(oldDirective, arg));
1219
+ },
1220
+ onMutual(arg) {
1221
+ changesInDirectiveArgument(oldDirective, arg.oldVersion, arg.newVersion, addChange);
1222
+ },
1056
1223
  });
1057
- return changes;
1058
1224
  }
1059
- function changesInDirectiveArgument(directive, oldArg, newArg) {
1060
- const changes = [];
1225
+ function changesInDirectiveArgument(directive, oldArg, newArg, addChange) {
1061
1226
  if (isNotEqual(oldArg.description, newArg.description)) {
1062
- changes.push(directiveArgumentDescriptionChanged(directive, oldArg, newArg));
1227
+ addChange(directiveArgumentDescriptionChanged(directive, oldArg, newArg));
1063
1228
  }
1064
1229
  if (isNotEqual(oldArg.defaultValue, newArg.defaultValue)) {
1065
- changes.push(directiveArgumentDefaultValueChanged(directive, oldArg, newArg));
1230
+ addChange(directiveArgumentDefaultValueChanged(directive, oldArg, newArg));
1066
1231
  }
1067
1232
  if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) {
1068
- changes.push(directiveArgumentTypeChanged(directive, oldArg, newArg));
1233
+ addChange(directiveArgumentTypeChanged(directive, oldArg, newArg));
1069
1234
  }
1070
- return changes;
1071
1235
  }
1072
1236
 
1073
1237
  function diffSchema(oldSchema, newSchema) {
1074
1238
  const changes = [];
1075
- const types = diffTypes(oldSchema, newSchema);
1076
- const directives = diffDirectives(oldSchema, newSchema);
1077
- // Added and removed types
1078
- changes.push(...types.added.map(typeAdded));
1079
- changes.push(...types.removed.map(typeRemoved));
1080
- // Added and removed directives
1081
- changes.push(...directives.added.map(directiveAdded));
1082
- changes.push(...directives.removed.map(directiveRemoved));
1083
- // Changes in Schema
1084
- changes.push(...changesInSchema(oldSchema, newSchema));
1085
- // Changes in Type
1086
- types.common.forEach(({ inOld, inNew }) => {
1087
- changes.push(...changesInType(inOld, inNew));
1239
+ function addChange(change) {
1240
+ changes.push(change);
1241
+ }
1242
+ changesInSchema(oldSchema, newSchema, addChange);
1243
+ compareLists(Object.values(oldSchema.getTypeMap()).filter(t => !isPrimitive(t)), Object.values(newSchema.getTypeMap()).filter(t => !isPrimitive(t)), {
1244
+ onAdded(type) {
1245
+ addChange(typeAdded(type));
1246
+ },
1247
+ onRemoved(type) {
1248
+ addChange(typeRemoved(type));
1249
+ },
1250
+ onMutual(type) {
1251
+ changesInType(type.oldVersion, type.newVersion, addChange);
1252
+ },
1088
1253
  });
1089
- // Changes in Directives
1090
- directives.common.forEach(({ inOld, inNew }) => {
1091
- changes.push(...changesInDirective(inOld, inNew));
1254
+ compareLists(oldSchema.getDirectives(), newSchema.getDirectives(), {
1255
+ onAdded(directive) {
1256
+ addChange(directiveAdded(directive));
1257
+ },
1258
+ onRemoved(directive) {
1259
+ addChange(directiveRemoved(directive));
1260
+ },
1261
+ onMutual(directive) {
1262
+ changesInDirective(directive.oldVersion, directive.newVersion, addChange);
1263
+ },
1092
1264
  });
1093
1265
  return changes;
1094
1266
  }
1095
- function diffTypes(oldSchema, newSchema) {
1096
- const oldTypeMap = oldSchema.getTypeMap();
1097
- const newTypeMap = newSchema.getTypeMap();
1098
- const oldTypenames = Object.keys(oldTypeMap).filter(name => !isPrimitive(name));
1099
- const newTypenames = Object.keys(newTypeMap).filter(name => !isPrimitive(name));
1100
- const added = diffArrays(newTypenames, oldTypenames).map(name => newTypeMap[name]);
1101
- const removed = diffArrays(oldTypenames, newTypenames).map(name => oldTypeMap[name]);
1102
- const common = unionArrays(oldTypenames, newTypenames).map(name => ({
1103
- inOld: oldTypeMap[name],
1104
- inNew: newTypeMap[name],
1105
- }));
1106
- return {
1107
- added,
1108
- removed,
1109
- common,
1110
- };
1111
- }
1112
- function diffDirectives(oldSchema, newSchema) {
1113
- const oldDirectives = oldSchema.getDirectives();
1114
- const newDirectives = newSchema.getDirectives();
1115
- const oldNames = oldDirectives.map(d => d.name);
1116
- const newNames = newDirectives.map(d => d.name);
1117
- const added = diffArrays(newNames, oldNames).map(name => newDirectives.find(d => d.name === name));
1118
- const removed = diffArrays(oldNames, newNames).map(name => oldDirectives.find(d => d.name === name));
1119
- const common = unionArrays(oldNames, newNames).map(name => ({
1120
- inOld: oldDirectives.find(d => d.name === name),
1121
- inNew: newDirectives.find(d => d.name === name),
1122
- }));
1123
- return {
1124
- added,
1125
- removed,
1126
- common,
1267
+ function changesInSchema(oldSchema, newSchema, addChange) {
1268
+ var _a, _b, _c, _d, _e, _f;
1269
+ const defaultNames = {
1270
+ query: 'Query',
1271
+ mutation: 'Mutation',
1272
+ subscription: 'Subscription',
1127
1273
  };
1128
- }
1129
- function changesInSchema(oldSchema, newSchema) {
1130
- const changes = [];
1131
1274
  const oldRoot = {
1132
- query: (oldSchema.getQueryType() || {}).name,
1133
- mutation: (oldSchema.getMutationType() || {}).name,
1134
- subscription: (oldSchema.getSubscriptionType() || {})
1135
- .name,
1275
+ query: (_a = (oldSchema.getQueryType() || {}).name) !== null && _a !== void 0 ? _a : defaultNames.query,
1276
+ mutation: (_b = (oldSchema.getMutationType() || {}).name) !== null && _b !== void 0 ? _b : defaultNames.mutation,
1277
+ subscription: (_c = (oldSchema.getSubscriptionType() || {}).name) !== null && _c !== void 0 ? _c : defaultNames.subscription,
1136
1278
  };
1137
1279
  const newRoot = {
1138
- query: (newSchema.getQueryType() || {}).name,
1139
- mutation: (newSchema.getMutationType() || {}).name,
1140
- subscription: (newSchema.getSubscriptionType() || {})
1141
- .name,
1280
+ query: (_d = (newSchema.getQueryType() || {}).name) !== null && _d !== void 0 ? _d : defaultNames.query,
1281
+ mutation: (_e = (newSchema.getMutationType() || {}).name) !== null && _e !== void 0 ? _e : defaultNames.mutation,
1282
+ subscription: (_f = (newSchema.getSubscriptionType() || {}).name) !== null && _f !== void 0 ? _f : defaultNames.subscription,
1142
1283
  };
1143
1284
  if (isNotEqual(oldRoot.query, newRoot.query)) {
1144
- changes.push(schemaQueryTypeChanged(oldSchema, newSchema));
1285
+ addChange(schemaQueryTypeChanged(oldSchema, newSchema));
1145
1286
  }
1146
1287
  if (isNotEqual(oldRoot.mutation, newRoot.mutation)) {
1147
- changes.push(schemaMutationTypeChanged(oldSchema, newSchema));
1288
+ addChange(schemaMutationTypeChanged(oldSchema, newSchema));
1148
1289
  }
1149
1290
  if (isNotEqual(oldRoot.subscription, newRoot.subscription)) {
1150
- changes.push(schemaSubscriptionTypeChanged(oldSchema, newSchema));
1291
+ addChange(schemaSubscriptionTypeChanged(oldSchema, newSchema));
1151
1292
  }
1152
- return changes;
1153
1293
  }
1154
- function changesInType(oldType, newType) {
1155
- let changes = [];
1294
+ function changesInType(oldType, newType, addChange) {
1156
1295
  if (graphql.isEnumType(oldType) && graphql.isEnumType(newType)) {
1157
- changes = changesInEnum(oldType, newType);
1296
+ changesInEnum(oldType, newType, addChange);
1158
1297
  }
1159
1298
  else if (graphql.isUnionType(oldType) && graphql.isUnionType(newType)) {
1160
- changes = changesInUnion(oldType, newType);
1299
+ changesInUnion(oldType, newType, addChange);
1161
1300
  }
1162
1301
  else if (graphql.isInputObjectType(oldType) && graphql.isInputObjectType(newType)) {
1163
- changes = changesInInputObject(oldType, newType);
1302
+ changesInInputObject(oldType, newType, addChange);
1164
1303
  }
1165
1304
  else if (graphql.isObjectType(oldType) && graphql.isObjectType(newType)) {
1166
- changes = changesInObject(oldType, newType);
1305
+ changesInObject(oldType, newType, addChange);
1167
1306
  }
1168
1307
  else if (graphql.isInterfaceType(oldType) && graphql.isInterfaceType(newType)) {
1169
- changes = changesInInterface(oldType, newType);
1308
+ changesInInterface(oldType, newType, addChange);
1170
1309
  }
1171
1310
  else if (graphql.isScalarType(oldType) && graphql.isScalarType(newType)) ;
1172
1311
  else {
1173
- changes = [typeKindChanged(oldType, newType)];
1312
+ addChange(typeKindChanged(oldType, newType));
1174
1313
  }
1175
1314
  if (isNotEqual(oldType.description, newType.description)) {
1176
1315
  if (isVoid(oldType.description)) {
1177
- changes.push(typeDescriptionAdded(newType));
1316
+ addChange(typeDescriptionAdded(newType));
1178
1317
  }
1179
1318
  else if (isVoid(newType.description)) {
1180
- changes.push(typeDescriptionRemoved(oldType));
1319
+ addChange(typeDescriptionRemoved(oldType));
1181
1320
  }
1182
1321
  else {
1183
- changes.push(typeDescriptionChanged(oldType, newType));
1322
+ addChange(typeDescriptionChanged(oldType, newType));
1184
1323
  }
1185
1324
  }
1186
- return changes;
1187
1325
  }
1188
1326
 
1327
+ const dangerousBreaking = ({ changes }) => {
1328
+ return changes.map(change => {
1329
+ if (change.criticality.level === exports.CriticalityLevel.Dangerous) {
1330
+ return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Breaking }) });
1331
+ }
1332
+ return change;
1333
+ });
1334
+ };
1335
+
1189
1336
  function parsePath(path) {
1190
1337
  return path.split('.');
1191
1338
  }
1192
1339
 
1193
- const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
1340
+ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema }) => {
1194
1341
  return changes.map(change => {
1195
1342
  if (change.type === exports.ChangeType.FieldRemoved &&
1196
1343
  change.criticality.level === exports.CriticalityLevel.Breaking &&
@@ -1199,7 +1346,31 @@ const suppressRemovalOfDeprecatedField = ({ changes, oldSchema, }) => {
1199
1346
  const type = oldSchema.getType(typeName);
1200
1347
  if (graphql.isObjectType(type) || graphql.isInterfaceType(type)) {
1201
1348
  const field = type.getFields()[fieldName];
1202
- if (field.isDeprecated) {
1349
+ if (isDeprecated(field)) {
1350
+ return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
1351
+ }
1352
+ }
1353
+ }
1354
+ if (change.type === exports.ChangeType.EnumValueRemoved &&
1355
+ change.criticality.level === exports.CriticalityLevel.Breaking &&
1356
+ change.path) {
1357
+ const [enumName, enumItem] = parsePath(change.path);
1358
+ const type = oldSchema.getType(enumName);
1359
+ if (graphql.isEnumType(type)) {
1360
+ const item = type.getValue(enumItem);
1361
+ if (item && isDeprecated(item)) {
1362
+ return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
1363
+ }
1364
+ }
1365
+ }
1366
+ if (change.type === exports.ChangeType.InputFieldRemoved &&
1367
+ change.criticality.level === exports.CriticalityLevel.Breaking &&
1368
+ change.path) {
1369
+ const [inputName, inputItem] = parsePath(change.path);
1370
+ const type = oldSchema.getType(inputName);
1371
+ if (graphql.isInputObjectType(type)) {
1372
+ const item = type.getFields()[inputItem];
1373
+ if (item && isDeprecated(item)) {
1203
1374
  return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }) });
1204
1375
  }
1205
1376
  }
@@ -1225,20 +1396,73 @@ const ignoreDescriptionChanges = ({ changes }) => {
1225
1396
  return changes.filter(change => descriptionChangeTypes.indexOf(change.type) === -1);
1226
1397
  };
1227
1398
 
1399
+ const considerUsage = ({ changes, config }) => tslib.__awaiter(void 0, void 0, void 0, function* () {
1400
+ if (!config) {
1401
+ throw new Error(`considerUsage rule is missing config`);
1402
+ }
1403
+ const collectedBreakingField = [];
1404
+ changes.forEach(change => {
1405
+ if (change.criticality.level === exports.CriticalityLevel.Breaking && change.path) {
1406
+ const [typeName, fieldName, argumentName] = parsePath(change.path);
1407
+ collectedBreakingField.push({
1408
+ type: typeName,
1409
+ field: fieldName,
1410
+ argument: argumentName,
1411
+ });
1412
+ }
1413
+ });
1414
+ // True if safe to break, false otherwise
1415
+ const usageList = yield config.checkUsage(collectedBreakingField);
1416
+ // turns an array of booleans into an array of `Type.Field` strings
1417
+ // includes only those that are safe to break the api
1418
+ const suppressedPaths = collectedBreakingField
1419
+ .filter((_, i) => usageList[i] === true)
1420
+ .map(({ type, field, argument }) => [type, field, argument].filter(Boolean).join('.'));
1421
+ return changes.map(change => {
1422
+ // Turns those "safe to break" changes into "dangerous"
1423
+ if (change.criticality.level === exports.CriticalityLevel.Breaking &&
1424
+ change.path &&
1425
+ suppressedPaths.some(p => change.path.startsWith(p))) {
1426
+ return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.Dangerous }), message: `${change.message} (non-breaking based on usage)` });
1427
+ }
1428
+ return change;
1429
+ });
1430
+ });
1431
+
1432
+ const safeUnreachable = ({ changes, oldSchema }) => {
1433
+ const reachable = getReachableTypes(oldSchema);
1434
+ return changes.map(change => {
1435
+ if (change.criticality.level === exports.CriticalityLevel.Breaking && change.path) {
1436
+ const [typeName] = parsePath(change.path);
1437
+ if (!reachable.has(typeName)) {
1438
+ return Object.assign(Object.assign({}, change), { criticality: Object.assign(Object.assign({}, change.criticality), { level: exports.CriticalityLevel.NonBreaking }), message: 'Unreachable from root' });
1439
+ }
1440
+ }
1441
+ return change;
1442
+ });
1443
+ };
1444
+
1228
1445
  const rules = /*#__PURE__*/Object.freeze({
1229
1446
  __proto__: null,
1447
+ dangerousBreaking: dangerousBreaking,
1230
1448
  suppressRemovalOfDeprecatedField: suppressRemovalOfDeprecatedField,
1231
- ignoreDescriptionChanges: ignoreDescriptionChanges
1449
+ ignoreDescriptionChanges: ignoreDescriptionChanges,
1450
+ considerUsage: considerUsage,
1451
+ safeUnreachable: safeUnreachable
1232
1452
  });
1233
1453
 
1234
1454
  const DiffRule = rules;
1235
- function diff(oldSchema, newSchema, rules = []) {
1455
+ function diff(oldSchema, newSchema, rules = [], config) {
1236
1456
  const changes = diffSchema(oldSchema, newSchema);
1237
- return rules.reduce((prev, rule) => rule({
1238
- changes: prev,
1239
- oldSchema,
1240
- newSchema,
1241
- }), changes);
1457
+ return rules.reduce((prev, rule) => tslib.__awaiter(this, void 0, void 0, function* () {
1458
+ const prevChanges = yield prev;
1459
+ return rule({
1460
+ changes: prevChanges,
1461
+ oldSchema,
1462
+ newSchema,
1463
+ config,
1464
+ });
1465
+ }), Promise.resolve(changes));
1242
1466
  }
1243
1467
 
1244
1468
  function readDocument(source) {
@@ -1357,13 +1581,26 @@ function calculateDepth({ node, currentDepth, maxDepth, getFragment, }) {
1357
1581
  }
1358
1582
  }
1359
1583
  }
1584
+ function countDepth(node, parentDepth, getFragmentReference) {
1585
+ let depth = parentDepth;
1586
+ if ('selectionSet' in node && node.selectionSet) {
1587
+ for (let child of node.selectionSet.selections) {
1588
+ depth = Math.max(depth, countDepth(child, parentDepth + 1, getFragmentReference));
1589
+ }
1590
+ }
1591
+ if (node.kind == graphql.Kind.FRAGMENT_SPREAD) {
1592
+ const fragment = getFragmentReference(node.name.value);
1593
+ if (fragment) {
1594
+ depth = Math.max(depth, countDepth(fragment, parentDepth + 1, getFragmentReference));
1595
+ }
1596
+ }
1597
+ return depth;
1598
+ }
1360
1599
 
1361
1600
  function transformDocumentWithApollo(doc, { keepClientFields }) {
1362
1601
  return graphql.visit(doc, {
1363
1602
  Field(node) {
1364
- return keepClientFields
1365
- ? removeDirectives(node, ['client'])
1366
- : removeFieldIfDirectives(node, ['client']);
1603
+ return keepClientFields ? removeDirectives(node, ['client']) : removeFieldIfDirectives(node, ['client']);
1367
1604
  },
1368
1605
  });
1369
1606
  }
@@ -1373,6 +1610,68 @@ function transformSchemaWithApollo(schema) {
1373
1610
  `));
1374
1611
  }
1375
1612
 
1613
+ function validateAliasCount({ source, doc, maxAliasCount, fragmentGraph, }) {
1614
+ const getFragmentByFragmentName = (fragmentName) => fragmentGraph.getNodeData(fragmentName);
1615
+ for (const definition of doc.definitions) {
1616
+ if (definition.kind !== graphql.Kind.OPERATION_DEFINITION) {
1617
+ continue;
1618
+ }
1619
+ const aliasCount = countAliases(definition, getFragmentByFragmentName);
1620
+ if (aliasCount > maxAliasCount) {
1621
+ return new graphql.GraphQLError(`Too many aliases (${aliasCount}). Maximum allowed is ${maxAliasCount}`, [definition], source, definition.loc && definition.loc.start ? [definition.loc.start] : undefined);
1622
+ }
1623
+ }
1624
+ }
1625
+ function countAliases(node, getFragmentByName) {
1626
+ let aliases = 0;
1627
+ if ('alias' in node && node.alias) {
1628
+ ++aliases;
1629
+ }
1630
+ if ('selectionSet' in node && node.selectionSet) {
1631
+ for (let child of node.selectionSet.selections) {
1632
+ aliases += countAliases(child, getFragmentByName);
1633
+ }
1634
+ }
1635
+ else if (node.kind === graphql.Kind.FRAGMENT_SPREAD) {
1636
+ const fragmentNode = getFragmentByName(node.name.value);
1637
+ if (fragmentNode) {
1638
+ aliases += countAliases(fragmentNode, getFragmentByName);
1639
+ }
1640
+ }
1641
+ return aliases;
1642
+ }
1643
+
1644
+ function validateDirectiveCount({ source, doc, maxDirectiveCount, fragmentGraph, }) {
1645
+ const getFragmentByFragmentName = (fragmentName) => fragmentGraph.getNodeData(fragmentName);
1646
+ for (const definition of doc.definitions) {
1647
+ if (definition.kind !== graphql.Kind.OPERATION_DEFINITION) {
1648
+ continue;
1649
+ }
1650
+ const directiveCount = countDirectives(definition, getFragmentByFragmentName);
1651
+ if (directiveCount > maxDirectiveCount) {
1652
+ return new graphql.GraphQLError(`Too many directives (${directiveCount}). Maximum allowed is ${maxDirectiveCount}`, [definition], source, definition.loc && definition.loc.start ? [definition.loc.start] : undefined);
1653
+ }
1654
+ }
1655
+ }
1656
+ function countDirectives(node, getFragmentByName) {
1657
+ let directives = 0;
1658
+ if (node.directives) {
1659
+ directives += node.directives.length;
1660
+ }
1661
+ if ('selectionSet' in node && node.selectionSet) {
1662
+ for (let child of node.selectionSet.selections) {
1663
+ directives += countDirectives(child, getFragmentByName);
1664
+ }
1665
+ }
1666
+ if (node.kind == graphql.Kind.FRAGMENT_SPREAD) {
1667
+ const fragment = getFragmentByName(node.name.value);
1668
+ if (fragment) {
1669
+ directives += countDirectives(fragment, getFragmentByName);
1670
+ }
1671
+ }
1672
+ return directives;
1673
+ }
1674
+
1376
1675
  function validate(schema, sources, options) {
1377
1676
  const config = Object.assign({ strictDeprecated: true, strictFragments: true, keepClientFields: false, apollo: false }, options);
1378
1677
  const invalidDocuments = [];
@@ -1402,7 +1701,7 @@ function validate(schema, sources, options) {
1402
1701
  .filter(doc => doc.hasOperations)
1403
1702
  .forEach(doc => {
1404
1703
  const docWithOperations = {
1405
- kind: 'Document',
1704
+ kind: graphql.Kind.DOCUMENT,
1406
1705
  definitions: doc.operations.map(d => d.node),
1407
1706
  };
1408
1707
  const extractedFragments = (extractFragments(graphql.print(docWithOperations)) || [])
@@ -1413,12 +1712,10 @@ function validate(schema, sources, options) {
1413
1712
  // remove duplicates
1414
1713
  .filter((def, i, all) => all.findIndex(item => item.name.value === def.name.value) === i);
1415
1714
  const merged = {
1416
- kind: 'Document',
1715
+ kind: graphql.Kind.DOCUMENT,
1417
1716
  definitions: [...docWithOperations.definitions, ...extractedFragments],
1418
1717
  };
1419
- const transformedSchema = config.apollo
1420
- ? transformSchemaWithApollo(schema)
1421
- : schema;
1718
+ let transformedSchema = config.apollo ? transformSchemaWithApollo(schema) : schema;
1422
1719
  const transformedDoc = config.apollo
1423
1720
  ? transformDocumentWithApollo(merged, {
1424
1721
  keepClientFields: config.keepClientFields,
@@ -1436,12 +1733,30 @@ function validate(schema, sources, options) {
1436
1733
  errors.push(depthError);
1437
1734
  }
1438
1735
  }
1439
- const deprecated = config.strictDeprecated
1440
- ? findDeprecatedUsages(transformedSchema, graphql.parse(doc.source.body))
1441
- : [];
1442
- const duplicatedFragments = config.strictFragments
1443
- ? findDuplicatedFragments(fragmentNames)
1444
- : [];
1736
+ if (config.maxAliasCount) {
1737
+ const aliasError = validateAliasCount({
1738
+ source: doc.source,
1739
+ doc: transformedDoc,
1740
+ maxAliasCount: config.maxAliasCount,
1741
+ fragmentGraph: graph,
1742
+ });
1743
+ if (aliasError) {
1744
+ errors.push(aliasError);
1745
+ }
1746
+ }
1747
+ if (config.maxDirectiveCount) {
1748
+ const directiveError = validateDirectiveCount({
1749
+ source: doc.source,
1750
+ doc: transformedDoc,
1751
+ maxDirectiveCount: config.maxDirectiveCount,
1752
+ fragmentGraph: graph,
1753
+ });
1754
+ if (directiveError) {
1755
+ errors.push(directiveError);
1756
+ }
1757
+ }
1758
+ const deprecated = config.strictDeprecated ? findDeprecatedUsages(transformedSchema, transformedDoc) : [];
1759
+ const duplicatedFragments = config.strictFragments ? findDuplicatedFragments(fragmentNames) : [];
1445
1760
  if (sumLengths(errors, duplicatedFragments, deprecated) > 0) {
1446
1761
  invalidDocuments.push({
1447
1762
  source: doc.source,
@@ -1464,10 +1779,7 @@ function findDuplicatedFragments(fragmentNames) {
1464
1779
  function resolveFragment(fragment, graph) {
1465
1780
  return graph
1466
1781
  .dependenciesOf(fragment.name.value)
1467
- .reduce((list, current) => [
1468
- ...list,
1469
- ...resolveFragment(graph.getNodeData(current), graph),
1470
- ], [fragment]);
1782
+ .reduce((list, current) => [...list, ...resolveFragment(graph.getNodeData(current), graph)], [fragment]);
1471
1783
  }
1472
1784
  function extractFragments(document) {
1473
1785
  return (document.match(/[\.]{3}[a-z0-9\_]+\b/gi) || []).map(name => name.replace('...', ''));
@@ -1476,70 +1788,6 @@ function sumLengths(...arrays) {
1476
1788
  return arrays.reduce((sum, { length }) => sum + length, 0);
1477
1789
  }
1478
1790
 
1479
- function compareTwoStrings(str1, str2) {
1480
- if (!str1.length && !str2.length)
1481
- return 1;
1482
- if (!str1.length || !str2.length)
1483
- return 0;
1484
- if (str1.toUpperCase() === str2.toUpperCase())
1485
- return 1;
1486
- if (str1.length === 1 && str2.length === 1)
1487
- return 0;
1488
- const pairs1 = wordLetterPairs(str1);
1489
- const pairs2 = wordLetterPairs(str2);
1490
- const union = pairs1.length + pairs2.length;
1491
- let intersection = 0;
1492
- pairs1.forEach(pair1 => {
1493
- for (let i = 0, pair2; (pair2 = pairs2[i]); i++) {
1494
- if (pair1 !== pair2)
1495
- continue;
1496
- intersection++;
1497
- pairs2.splice(i, 1);
1498
- break;
1499
- }
1500
- });
1501
- return (intersection * 2) / union;
1502
- }
1503
- function findBestMatch(mainString, targetStrings) {
1504
- if (!areArgsValid(mainString, targetStrings))
1505
- throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');
1506
- const ratings = targetStrings.map(target => ({
1507
- target,
1508
- rating: compareTwoStrings(mainString, target.value),
1509
- }));
1510
- const bestMatch = Array.from(ratings).sort((a, b) => b.rating - a.rating)[0];
1511
- return { ratings, bestMatch };
1512
- }
1513
- function flattenDeep(arr) {
1514
- return Array.isArray(arr)
1515
- ? arr.reduce((a, b) => a.concat(flattenDeep(b)), [])
1516
- : [arr];
1517
- }
1518
- function areArgsValid(mainString, targetStrings) {
1519
- if (typeof mainString !== 'string')
1520
- return false;
1521
- if (!Array.isArray(targetStrings))
1522
- return false;
1523
- if (!targetStrings.length)
1524
- return false;
1525
- if (targetStrings.find(s => typeof s.value !== 'string'))
1526
- return false;
1527
- return true;
1528
- }
1529
- function letterPairs(str) {
1530
- const pairs = [];
1531
- for (let i = 0, max = str.length - 1; i < max; i++)
1532
- pairs[i] = str.substring(i, i + 2);
1533
- return pairs;
1534
- }
1535
- function wordLetterPairs(str) {
1536
- const pairs = str
1537
- .toUpperCase()
1538
- .split(' ')
1539
- .map(letterPairs);
1540
- return flattenDeep(pairs);
1541
- }
1542
-
1543
1791
  function similar(schema, typeName, threshold = 0.4) {
1544
1792
  const typeMap = schema.getTypeMap();
1545
1793
  const targets = Object.keys(schema.getTypeMap())
@@ -1549,14 +1797,13 @@ function similar(schema, typeName, threshold = 0.4) {
1549
1797
  value: stripType(typeMap[name]),
1550
1798
  }));
1551
1799
  const results = {};
1552
- if (typeof typeName !== 'undefined' &&
1553
- !targets.some(t => t.typeId === typeName)) {
1800
+ if (typeof typeName !== 'undefined' && !targets.some(t => t.typeId === typeName)) {
1554
1801
  throw new Error(`Type '${typeName}' doesn't exist`);
1555
1802
  }
1556
1803
  (typeName ? [{ typeId: typeName, value: '' }] : targets).forEach(source => {
1557
1804
  const sourceType = schema.getType(source.typeId);
1558
- const matchWith = targets.filter(target => schema.getType(target.typeId).astNode.kind ===
1559
- sourceType.astNode.kind && target.typeId !== source.typeId);
1805
+ const matchWith = targets.filter(target => schema.getType(target.typeId).astNode.kind === sourceType.astNode.kind &&
1806
+ target.typeId !== source.typeId);
1560
1807
  if (matchWith.length > 0) {
1561
1808
  const found = similarTo(sourceType, matchWith, threshold);
1562
1809
  if (found) {
@@ -1610,10 +1857,24 @@ function coverage(schema, sources) {
1610
1857
  fieldDef.name &&
1611
1858
  fieldDef.name !== '__typename' &&
1612
1859
  fieldDef.name !== '__schema') {
1613
- const locations = coverage.types[parent.name].children[fieldDef.name].locations[source.name];
1614
- coverage.types[parent.name].hits++;
1615
- coverage.types[parent.name].children[fieldDef.name].locations[source.name] = [node.loc].concat(locations || []);
1616
- coverage.types[parent.name].children[fieldDef.name].hits++;
1860
+ const sourceName = source.name;
1861
+ const typeCoverage = coverage.types[parent.name];
1862
+ const fieldCoverage = typeCoverage.children[fieldDef.name];
1863
+ const locations = fieldCoverage.locations[sourceName];
1864
+ typeCoverage.hits++;
1865
+ fieldCoverage.hits++;
1866
+ if (node.loc) {
1867
+ fieldCoverage.locations[sourceName] = [node.loc, ...(locations || [])];
1868
+ }
1869
+ if (node.arguments) {
1870
+ for (const argNode of node.arguments) {
1871
+ const argCoverage = fieldCoverage.children[argNode.name.value];
1872
+ argCoverage.hits++;
1873
+ if (argNode.loc) {
1874
+ argCoverage.locations[sourceName] = [argNode.loc, ...(argCoverage.locations[sourceName] || [])];
1875
+ }
1876
+ }
1877
+ }
1617
1878
  }
1618
1879
  },
1619
1880
  });
@@ -1632,7 +1893,14 @@ function coverage(schema, sources) {
1632
1893
  typeCoverage.children[field.name] = {
1633
1894
  hits: 0,
1634
1895
  locations: {},
1896
+ children: {},
1635
1897
  };
1898
+ for (const arg of field.args) {
1899
+ typeCoverage.children[field.name].children[arg.name] = {
1900
+ hits: 0,
1901
+ locations: {},
1902
+ };
1903
+ }
1636
1904
  }
1637
1905
  coverage.types[type.name] = typeCoverage;
1638
1906
  }
@@ -1652,9 +1920,11 @@ function coverage(schema, sources) {
1652
1920
  }
1653
1921
 
1654
1922
  exports.DiffRule = DiffRule;
1923
+ exports.countAliases = countAliases;
1924
+ exports.countDepth = countDepth;
1925
+ exports.countDirectives = countDirectives;
1655
1926
  exports.coverage = coverage;
1656
1927
  exports.diff = diff;
1657
1928
  exports.getTypePrefix = getTypePrefix;
1658
1929
  exports.similar = similar;
1659
1930
  exports.validate = validate;
1660
- //# sourceMappingURL=index.cjs.js.map