OverlappingFieldsCanBeMergedRule.mjs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. import find from "../../polyfills/find.mjs";
  2. import objectEntries from "../../polyfills/objectEntries.mjs";
  3. import inspect from "../../jsutils/inspect.mjs";
  4. import { GraphQLError } from "../../error/GraphQLError.mjs";
  5. import { Kind } from "../../language/kinds.mjs";
  6. import { print } from "../../language/printer.mjs";
  7. import { getNamedType, isNonNullType, isLeafType, isObjectType, isListType, isInterfaceType } from "../../type/definition.mjs";
  8. import { typeFromAST } from "../../utilities/typeFromAST.mjs";
  9. function reasonMessage(reason) {
  10. if (Array.isArray(reason)) {
  11. return reason.map(function (_ref) {
  12. var responseName = _ref[0],
  13. subReason = _ref[1];
  14. return "subfields \"".concat(responseName, "\" conflict because ") + reasonMessage(subReason);
  15. }).join(' and ');
  16. }
  17. return reason;
  18. }
  19. /**
  20. * Overlapping fields can be merged
  21. *
  22. * A selection set is only valid if all fields (including spreading any
  23. * fragments) either correspond to distinct response names or can be merged
  24. * without ambiguity.
  25. */
  26. export function OverlappingFieldsCanBeMergedRule(context) {
  27. // A memoization for when two fragments are compared "between" each other for
  28. // conflicts. Two fragments may be compared many times, so memoizing this can
  29. // dramatically improve the performance of this validator.
  30. var comparedFragmentPairs = new PairSet(); // A cache for the "field map" and list of fragment names found in any given
  31. // selection set. Selection sets may be asked for this information multiple
  32. // times, so this improves the performance of this validator.
  33. var cachedFieldsAndFragmentNames = new Map();
  34. return {
  35. SelectionSet: function SelectionSet(selectionSet) {
  36. var conflicts = findConflictsWithinSelectionSet(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, context.getParentType(), selectionSet);
  37. for (var _i2 = 0; _i2 < conflicts.length; _i2++) {
  38. var _ref3 = conflicts[_i2];
  39. var _ref2$ = _ref3[0];
  40. var responseName = _ref2$[0];
  41. var reason = _ref2$[1];
  42. var fields1 = _ref3[1];
  43. var fields2 = _ref3[2];
  44. var reasonMsg = reasonMessage(reason);
  45. context.reportError(new GraphQLError("Fields \"".concat(responseName, "\" conflict because ").concat(reasonMsg, ". Use different aliases on the fields to fetch both if this was intentional."), fields1.concat(fields2)));
  46. }
  47. }
  48. };
  49. }
  50. /**
  51. * Algorithm:
  52. *
  53. * Conflicts occur when two fields exist in a query which will produce the same
  54. * response name, but represent differing values, thus creating a conflict.
  55. * The algorithm below finds all conflicts via making a series of comparisons
  56. * between fields. In order to compare as few fields as possible, this makes
  57. * a series of comparisons "within" sets of fields and "between" sets of fields.
  58. *
  59. * Given any selection set, a collection produces both a set of fields by
  60. * also including all inline fragments, as well as a list of fragments
  61. * referenced by fragment spreads.
  62. *
  63. * A) Each selection set represented in the document first compares "within" its
  64. * collected set of fields, finding any conflicts between every pair of
  65. * overlapping fields.
  66. * Note: This is the *only time* that a the fields "within" a set are compared
  67. * to each other. After this only fields "between" sets are compared.
  68. *
  69. * B) Also, if any fragment is referenced in a selection set, then a
  70. * comparison is made "between" the original set of fields and the
  71. * referenced fragment.
  72. *
  73. * C) Also, if multiple fragments are referenced, then comparisons
  74. * are made "between" each referenced fragment.
  75. *
  76. * D) When comparing "between" a set of fields and a referenced fragment, first
  77. * a comparison is made between each field in the original set of fields and
  78. * each field in the the referenced set of fields.
  79. *
  80. * E) Also, if any fragment is referenced in the referenced selection set,
  81. * then a comparison is made "between" the original set of fields and the
  82. * referenced fragment (recursively referring to step D).
  83. *
  84. * F) When comparing "between" two fragments, first a comparison is made between
  85. * each field in the first referenced set of fields and each field in the the
  86. * second referenced set of fields.
  87. *
  88. * G) Also, any fragments referenced by the first must be compared to the
  89. * second, and any fragments referenced by the second must be compared to the
  90. * first (recursively referring to step F).
  91. *
  92. * H) When comparing two fields, if both have selection sets, then a comparison
  93. * is made "between" both selection sets, first comparing the set of fields in
  94. * the first selection set with the set of fields in the second.
  95. *
  96. * I) Also, if any fragment is referenced in either selection set, then a
  97. * comparison is made "between" the other set of fields and the
  98. * referenced fragment.
  99. *
  100. * J) Also, if two fragments are referenced in both selection sets, then a
  101. * comparison is made "between" the two fragments.
  102. *
  103. */
  104. // Find all conflicts found "within" a selection set, including those found
  105. // via spreading in fragments. Called when visiting each SelectionSet in the
  106. // GraphQL Document.
  107. function findConflictsWithinSelectionSet(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentType, selectionSet) {
  108. var conflicts = [];
  109. var _getFieldsAndFragment = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet),
  110. fieldMap = _getFieldsAndFragment[0],
  111. fragmentNames = _getFieldsAndFragment[1]; // (A) Find find all conflicts "within" the fields of this selection set.
  112. // Note: this is the *only place* `collectConflictsWithin` is called.
  113. collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap);
  114. if (fragmentNames.length !== 0) {
  115. // (B) Then collect conflicts between these fields and those represented by
  116. // each spread fragment name found.
  117. for (var i = 0; i < fragmentNames.length; i++) {
  118. collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, fieldMap, fragmentNames[i]); // (C) Then compare this fragment with all other fragments found in this
  119. // selection set to collect conflicts between fragments spread together.
  120. // This compares each item in the list of fragment names to every other
  121. // item in that same list (except for itself).
  122. for (var j = i + 1; j < fragmentNames.length; j++) {
  123. collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, fragmentNames[i], fragmentNames[j]);
  124. }
  125. }
  126. }
  127. return conflicts;
  128. } // Collect all conflicts found between a set of fields and a fragment reference
  129. // including via spreading in any nested fragments.
  130. function collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fragmentName) {
  131. var fragment = context.getFragment(fragmentName);
  132. if (!fragment) {
  133. return;
  134. }
  135. var _getReferencedFieldsA = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment),
  136. fieldMap2 = _getReferencedFieldsA[0],
  137. fragmentNames2 = _getReferencedFieldsA[1]; // Do not compare a fragment's fieldMap to itself.
  138. if (fieldMap === fieldMap2) {
  139. return;
  140. } // (D) First collect any conflicts between the provided collection of fields
  141. // and the collection of fields represented by the given fragment.
  142. collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fieldMap2); // (E) Then collect any conflicts between the provided collection of fields
  143. // and any fragment names found in the given fragment.
  144. for (var i = 0; i < fragmentNames2.length; i++) {
  145. collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap, fragmentNames2[i]);
  146. }
  147. } // Collect all conflicts found between two fragments, including via spreading in
  148. // any nested fragments.
  149. function collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, fragmentName2) {
  150. // No need to compare a fragment to itself.
  151. if (fragmentName1 === fragmentName2) {
  152. return;
  153. } // Memoize so two fragments are not compared for conflicts more than once.
  154. if (comparedFragmentPairs.has(fragmentName1, fragmentName2, areMutuallyExclusive)) {
  155. return;
  156. }
  157. comparedFragmentPairs.add(fragmentName1, fragmentName2, areMutuallyExclusive);
  158. var fragment1 = context.getFragment(fragmentName1);
  159. var fragment2 = context.getFragment(fragmentName2);
  160. if (!fragment1 || !fragment2) {
  161. return;
  162. }
  163. var _getReferencedFieldsA2 = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment1),
  164. fieldMap1 = _getReferencedFieldsA2[0],
  165. fragmentNames1 = _getReferencedFieldsA2[1];
  166. var _getReferencedFieldsA3 = getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment2),
  167. fieldMap2 = _getReferencedFieldsA3[0],
  168. fragmentNames2 = _getReferencedFieldsA3[1]; // (F) First, collect all conflicts between these two collections of fields
  169. // (not including any nested fragments).
  170. collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (G) Then collect conflicts between the first fragment and any nested
  171. // fragments spread in the second fragment.
  172. for (var j = 0; j < fragmentNames2.length; j++) {
  173. collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentName1, fragmentNames2[j]);
  174. } // (G) Then collect conflicts between the second fragment and any nested
  175. // fragments spread in the first fragment.
  176. for (var i = 0; i < fragmentNames1.length; i++) {
  177. collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentNames1[i], fragmentName2);
  178. }
  179. } // Find all conflicts found between two selection sets, including those found
  180. // via spreading in fragments. Called when determining if conflicts exist
  181. // between the sub-fields of two overlapping fields.
  182. function findConflictsBetweenSubSelectionSets(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, parentType1, selectionSet1, parentType2, selectionSet2) {
  183. var conflicts = [];
  184. var _getFieldsAndFragment2 = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType1, selectionSet1),
  185. fieldMap1 = _getFieldsAndFragment2[0],
  186. fragmentNames1 = _getFieldsAndFragment2[1];
  187. var _getFieldsAndFragment3 = getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType2, selectionSet2),
  188. fieldMap2 = _getFieldsAndFragment3[0],
  189. fragmentNames2 = _getFieldsAndFragment3[1]; // (H) First, collect all conflicts between these two collections of field.
  190. collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fieldMap2); // (I) Then collect conflicts between the first collection of fields and
  191. // those referenced by each fragment name associated with the second.
  192. if (fragmentNames2.length !== 0) {
  193. for (var j = 0; j < fragmentNames2.length; j++) {
  194. collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap1, fragmentNames2[j]);
  195. }
  196. } // (I) Then collect conflicts between the second collection of fields and
  197. // those referenced by each fragment name associated with the first.
  198. if (fragmentNames1.length !== 0) {
  199. for (var i = 0; i < fragmentNames1.length; i++) {
  200. collectConflictsBetweenFieldsAndFragment(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fieldMap2, fragmentNames1[i]);
  201. }
  202. } // (J) Also collect conflicts between any fragment names by the first and
  203. // fragment names by the second. This compares each item in the first set of
  204. // names to each item in the second set of names.
  205. for (var _i3 = 0; _i3 < fragmentNames1.length; _i3++) {
  206. for (var _j = 0; _j < fragmentNames2.length; _j++) {
  207. collectConflictsBetweenFragments(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, fragmentNames1[_i3], fragmentNames2[_j]);
  208. }
  209. }
  210. return conflicts;
  211. } // Collect all Conflicts "within" one collection of fields.
  212. function collectConflictsWithin(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, fieldMap) {
  213. // A field map is a keyed collection, where each key represents a response
  214. // name and the value at that key is a list of all fields which provide that
  215. // response name. For every response name, if there are multiple fields, they
  216. // must be compared to find a potential conflict.
  217. for (var _i5 = 0, _objectEntries2 = objectEntries(fieldMap); _i5 < _objectEntries2.length; _i5++) {
  218. var _ref5 = _objectEntries2[_i5];
  219. var responseName = _ref5[0];
  220. var fields = _ref5[1];
  221. // This compares every field in the list to every other field in this list
  222. // (except to itself). If the list only has one item, nothing needs to
  223. // be compared.
  224. if (fields.length > 1) {
  225. for (var i = 0; i < fields.length; i++) {
  226. for (var j = i + 1; j < fields.length; j++) {
  227. var conflict = findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, false, // within one collection is never mutually exclusive
  228. responseName, fields[i], fields[j]);
  229. if (conflict) {
  230. conflicts.push(conflict);
  231. }
  232. }
  233. }
  234. }
  235. }
  236. } // Collect all Conflicts between two collections of fields. This is similar to,
  237. // but different from the `collectConflictsWithin` function above. This check
  238. // assumes that `collectConflictsWithin` has already been called on each
  239. // provided collection of fields. This is true because this validator traverses
  240. // each individual selection set.
  241. function collectConflictsBetween(context, conflicts, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, fieldMap1, fieldMap2) {
  242. // A field map is a keyed collection, where each key represents a response
  243. // name and the value at that key is a list of all fields which provide that
  244. // response name. For any response name which appears in both provided field
  245. // maps, each field from the first field map must be compared to every field
  246. // in the second field map to find potential conflicts.
  247. for (var _i7 = 0, _Object$keys2 = Object.keys(fieldMap1); _i7 < _Object$keys2.length; _i7++) {
  248. var responseName = _Object$keys2[_i7];
  249. var fields2 = fieldMap2[responseName];
  250. if (fields2) {
  251. var fields1 = fieldMap1[responseName];
  252. for (var i = 0; i < fields1.length; i++) {
  253. for (var j = 0; j < fields2.length; j++) {
  254. var conflict = findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, fields1[i], fields2[j]);
  255. if (conflict) {
  256. conflicts.push(conflict);
  257. }
  258. }
  259. }
  260. }
  261. }
  262. } // Determines if there is a conflict between two particular fields, including
  263. // comparing their sub-fields.
  264. function findConflict(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, parentFieldsAreMutuallyExclusive, responseName, field1, field2) {
  265. var parentType1 = field1[0],
  266. node1 = field1[1],
  267. def1 = field1[2];
  268. var parentType2 = field2[0],
  269. node2 = field2[1],
  270. def2 = field2[2]; // If it is known that two fields could not possibly apply at the same
  271. // time, due to the parent types, then it is safe to permit them to diverge
  272. // in aliased field or arguments used as they will not present any ambiguity
  273. // by differing.
  274. // It is known that two parent types could never overlap if they are
  275. // different Object types. Interface or Union types might overlap - if not
  276. // in the current state of the schema, then perhaps in some future version,
  277. // thus may not safely diverge.
  278. var areMutuallyExclusive = parentFieldsAreMutuallyExclusive || parentType1 !== parentType2 && isObjectType(parentType1) && isObjectType(parentType2);
  279. if (!areMutuallyExclusive) {
  280. var _node1$arguments, _node2$arguments;
  281. // Two aliases must refer to the same field.
  282. var name1 = node1.name.value;
  283. var name2 = node2.name.value;
  284. if (name1 !== name2) {
  285. return [[responseName, "\"".concat(name1, "\" and \"").concat(name2, "\" are different fields")], [node1], [node2]];
  286. } // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  287. var args1 = (_node1$arguments = node1.arguments) !== null && _node1$arguments !== void 0 ? _node1$arguments : []; // istanbul ignore next (See: 'https://github.com/graphql/graphql-js/issues/2203')
  288. var args2 = (_node2$arguments = node2.arguments) !== null && _node2$arguments !== void 0 ? _node2$arguments : []; // Two field calls must have the same arguments.
  289. if (!sameArguments(args1, args2)) {
  290. return [[responseName, 'they have differing arguments'], [node1], [node2]];
  291. }
  292. } // The return type for each field.
  293. var type1 = def1 === null || def1 === void 0 ? void 0 : def1.type;
  294. var type2 = def2 === null || def2 === void 0 ? void 0 : def2.type;
  295. if (type1 && type2 && doTypesConflict(type1, type2)) {
  296. return [[responseName, "they return conflicting types \"".concat(inspect(type1), "\" and \"").concat(inspect(type2), "\"")], [node1], [node2]];
  297. } // Collect and compare sub-fields. Use the same "visited fragment names" list
  298. // for both collections so fields in a fragment reference are never
  299. // compared to themselves.
  300. var selectionSet1 = node1.selectionSet;
  301. var selectionSet2 = node2.selectionSet;
  302. if (selectionSet1 && selectionSet2) {
  303. var conflicts = findConflictsBetweenSubSelectionSets(context, cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, getNamedType(type1), selectionSet1, getNamedType(type2), selectionSet2);
  304. return subfieldConflicts(conflicts, responseName, node1, node2);
  305. }
  306. }
  307. function sameArguments(arguments1, arguments2) {
  308. if (arguments1.length !== arguments2.length) {
  309. return false;
  310. }
  311. return arguments1.every(function (argument1) {
  312. var argument2 = find(arguments2, function (argument) {
  313. return argument.name.value === argument1.name.value;
  314. });
  315. if (!argument2) {
  316. return false;
  317. }
  318. return sameValue(argument1.value, argument2.value);
  319. });
  320. }
  321. function sameValue(value1, value2) {
  322. return print(value1) === print(value2);
  323. } // Two types conflict if both types could not apply to a value simultaneously.
  324. // Composite types are ignored as their individual field types will be compared
  325. // later recursively. However List and Non-Null types must match.
  326. function doTypesConflict(type1, type2) {
  327. if (isListType(type1)) {
  328. return isListType(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true;
  329. }
  330. if (isListType(type2)) {
  331. return true;
  332. }
  333. if (isNonNullType(type1)) {
  334. return isNonNullType(type2) ? doTypesConflict(type1.ofType, type2.ofType) : true;
  335. }
  336. if (isNonNullType(type2)) {
  337. return true;
  338. }
  339. if (isLeafType(type1) || isLeafType(type2)) {
  340. return type1 !== type2;
  341. }
  342. return false;
  343. } // Given a selection set, return the collection of fields (a mapping of response
  344. // name to field nodes and definitions) as well as a list of fragment names
  345. // referenced via fragment spreads.
  346. function getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, parentType, selectionSet) {
  347. var cached = cachedFieldsAndFragmentNames.get(selectionSet);
  348. if (!cached) {
  349. var nodeAndDefs = Object.create(null);
  350. var fragmentNames = Object.create(null);
  351. _collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeAndDefs, fragmentNames);
  352. cached = [nodeAndDefs, Object.keys(fragmentNames)];
  353. cachedFieldsAndFragmentNames.set(selectionSet, cached);
  354. }
  355. return cached;
  356. } // Given a reference to a fragment, return the represented collection of fields
  357. // as well as a list of nested fragment names referenced via fragment spreads.
  358. function getReferencedFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragment) {
  359. // Short-circuit building a type from the node if possible.
  360. var cached = cachedFieldsAndFragmentNames.get(fragment.selectionSet);
  361. if (cached) {
  362. return cached;
  363. }
  364. var fragmentType = typeFromAST(context.getSchema(), fragment.typeCondition);
  365. return getFieldsAndFragmentNames(context, cachedFieldsAndFragmentNames, fragmentType, fragment.selectionSet);
  366. }
  367. function _collectFieldsAndFragmentNames(context, parentType, selectionSet, nodeAndDefs, fragmentNames) {
  368. for (var _i9 = 0, _selectionSet$selecti2 = selectionSet.selections; _i9 < _selectionSet$selecti2.length; _i9++) {
  369. var selection = _selectionSet$selecti2[_i9];
  370. switch (selection.kind) {
  371. case Kind.FIELD:
  372. {
  373. var fieldName = selection.name.value;
  374. var fieldDef = void 0;
  375. if (isObjectType(parentType) || isInterfaceType(parentType)) {
  376. fieldDef = parentType.getFields()[fieldName];
  377. }
  378. var responseName = selection.alias ? selection.alias.value : fieldName;
  379. if (!nodeAndDefs[responseName]) {
  380. nodeAndDefs[responseName] = [];
  381. }
  382. nodeAndDefs[responseName].push([parentType, selection, fieldDef]);
  383. break;
  384. }
  385. case Kind.FRAGMENT_SPREAD:
  386. fragmentNames[selection.name.value] = true;
  387. break;
  388. case Kind.INLINE_FRAGMENT:
  389. {
  390. var typeCondition = selection.typeCondition;
  391. var inlineFragmentType = typeCondition ? typeFromAST(context.getSchema(), typeCondition) : parentType;
  392. _collectFieldsAndFragmentNames(context, inlineFragmentType, selection.selectionSet, nodeAndDefs, fragmentNames);
  393. break;
  394. }
  395. }
  396. }
  397. } // Given a series of Conflicts which occurred between two sub-fields, generate
  398. // a single Conflict.
  399. function subfieldConflicts(conflicts, responseName, node1, node2) {
  400. if (conflicts.length > 0) {
  401. return [[responseName, conflicts.map(function (_ref6) {
  402. var reason = _ref6[0];
  403. return reason;
  404. })], conflicts.reduce(function (allFields, _ref7) {
  405. var fields1 = _ref7[1];
  406. return allFields.concat(fields1);
  407. }, [node1]), conflicts.reduce(function (allFields, _ref8) {
  408. var fields2 = _ref8[2];
  409. return allFields.concat(fields2);
  410. }, [node2])];
  411. }
  412. }
  413. /**
  414. * A way to keep track of pairs of things when the ordering of the pair does
  415. * not matter. We do this by maintaining a sort of double adjacency sets.
  416. */
  417. var PairSet = /*#__PURE__*/function () {
  418. function PairSet() {
  419. this._data = Object.create(null);
  420. }
  421. var _proto = PairSet.prototype;
  422. _proto.has = function has(a, b, areMutuallyExclusive) {
  423. var first = this._data[a];
  424. var result = first && first[b];
  425. if (result === undefined) {
  426. return false;
  427. } // areMutuallyExclusive being false is a superset of being true,
  428. // hence if we want to know if this PairSet "has" these two with no
  429. // exclusivity, we have to ensure it was added as such.
  430. if (areMutuallyExclusive === false) {
  431. return result === false;
  432. }
  433. return true;
  434. };
  435. _proto.add = function add(a, b, areMutuallyExclusive) {
  436. _pairSetAdd(this._data, a, b, areMutuallyExclusive);
  437. _pairSetAdd(this._data, b, a, areMutuallyExclusive);
  438. };
  439. return PairSet;
  440. }();
  441. function _pairSetAdd(data, a, b, areMutuallyExclusive) {
  442. var map = data[a];
  443. if (!map) {
  444. map = Object.create(null);
  445. data[a] = map;
  446. }
  447. map[b] = areMutuallyExclusive;
  448. }