UNPKG

21 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.findBreakingChanges = findBreakingChanges;
7exports.findDangerousChanges = findDangerousChanges;
8exports.DangerousChangeType = exports.BreakingChangeType = void 0;
9
10var _objectValues = _interopRequireDefault(require("../polyfills/objectValues"));
11
12var _keyMap = _interopRequireDefault(require("../jsutils/keyMap"));
13
14var _inspect = _interopRequireDefault(require("../jsutils/inspect"));
15
16var _invariant = _interopRequireDefault(require("../jsutils/invariant"));
17
18var _printer = require("../language/printer");
19
20var _visitor = require("../language/visitor");
21
22var _definition = require("../type/definition");
23
24var _astFromValue = require("./astFromValue");
25
26function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
28function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
29
30function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
31
32function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
33
34var BreakingChangeType = Object.freeze({
35 TYPE_REMOVED: 'TYPE_REMOVED',
36 TYPE_CHANGED_KIND: 'TYPE_CHANGED_KIND',
37 TYPE_REMOVED_FROM_UNION: 'TYPE_REMOVED_FROM_UNION',
38 VALUE_REMOVED_FROM_ENUM: 'VALUE_REMOVED_FROM_ENUM',
39 REQUIRED_INPUT_FIELD_ADDED: 'REQUIRED_INPUT_FIELD_ADDED',
40 INTERFACE_REMOVED_FROM_OBJECT: 'INTERFACE_REMOVED_FROM_OBJECT',
41 FIELD_REMOVED: 'FIELD_REMOVED',
42 FIELD_CHANGED_KIND: 'FIELD_CHANGED_KIND',
43 REQUIRED_ARG_ADDED: 'REQUIRED_ARG_ADDED',
44 ARG_REMOVED: 'ARG_REMOVED',
45 ARG_CHANGED_KIND: 'ARG_CHANGED_KIND',
46 DIRECTIVE_REMOVED: 'DIRECTIVE_REMOVED',
47 DIRECTIVE_ARG_REMOVED: 'DIRECTIVE_ARG_REMOVED',
48 REQUIRED_DIRECTIVE_ARG_ADDED: 'REQUIRED_DIRECTIVE_ARG_ADDED',
49 DIRECTIVE_LOCATION_REMOVED: 'DIRECTIVE_LOCATION_REMOVED'
50});
51exports.BreakingChangeType = BreakingChangeType;
52var DangerousChangeType = Object.freeze({
53 VALUE_ADDED_TO_ENUM: 'VALUE_ADDED_TO_ENUM',
54 TYPE_ADDED_TO_UNION: 'TYPE_ADDED_TO_UNION',
55 OPTIONAL_INPUT_FIELD_ADDED: 'OPTIONAL_INPUT_FIELD_ADDED',
56 OPTIONAL_ARG_ADDED: 'OPTIONAL_ARG_ADDED',
57 INTERFACE_ADDED_TO_OBJECT: 'INTERFACE_ADDED_TO_OBJECT',
58 ARG_DEFAULT_VALUE_CHANGE: 'ARG_DEFAULT_VALUE_CHANGE'
59});
60exports.DangerousChangeType = DangerousChangeType;
61
62/**
63 * Given two schemas, returns an Array containing descriptions of all the types
64 * of breaking changes covered by the other functions down below.
65 */
66function findBreakingChanges(oldSchema, newSchema) {
67 var breakingChanges = findSchemaChanges(oldSchema, newSchema).filter(function (change) {
68 return change.type in BreakingChangeType;
69 });
70 return breakingChanges;
71}
72/**
73 * Given two schemas, returns an Array containing descriptions of all the types
74 * of potentially dangerous changes covered by the other functions down below.
75 */
76
77
78function findDangerousChanges(oldSchema, newSchema) {
79 var dangerousChanges = findSchemaChanges(oldSchema, newSchema).filter(function (change) {
80 return change.type in DangerousChangeType;
81 });
82 return dangerousChanges;
83}
84
85function findSchemaChanges(oldSchema, newSchema) {
86 return [].concat(findTypeChanges(oldSchema, newSchema), findDirectiveChanges(oldSchema, newSchema));
87}
88
89function findDirectiveChanges(oldSchema, newSchema) {
90 var schemaChanges = [];
91 var directivesDiff = diff(oldSchema.getDirectives(), newSchema.getDirectives());
92
93 for (var _i2 = 0, _directivesDiff$remov2 = directivesDiff.removed; _i2 < _directivesDiff$remov2.length; _i2++) {
94 var oldDirective = _directivesDiff$remov2[_i2];
95 schemaChanges.push({
96 type: BreakingChangeType.DIRECTIVE_REMOVED,
97 description: "".concat(oldDirective.name, " was removed.")
98 });
99 }
100
101 for (var _i4 = 0, _directivesDiff$persi2 = directivesDiff.persisted; _i4 < _directivesDiff$persi2.length; _i4++) {
102 var _ref2 = _directivesDiff$persi2[_i4];
103 var _oldDirective = _ref2[0];
104 var newDirective = _ref2[1];
105 var argsDiff = diff(_oldDirective.args, newDirective.args);
106
107 for (var _i6 = 0, _argsDiff$added2 = argsDiff.added; _i6 < _argsDiff$added2.length; _i6++) {
108 var newArg = _argsDiff$added2[_i6];
109
110 if ((0, _definition.isRequiredArgument)(newArg)) {
111 schemaChanges.push({
112 type: BreakingChangeType.REQUIRED_DIRECTIVE_ARG_ADDED,
113 description: "A required arg ".concat(newArg.name, " on directive ").concat(_oldDirective.name, " was added.")
114 });
115 }
116 }
117
118 for (var _i8 = 0, _argsDiff$removed2 = argsDiff.removed; _i8 < _argsDiff$removed2.length; _i8++) {
119 var oldArg = _argsDiff$removed2[_i8];
120 schemaChanges.push({
121 type: BreakingChangeType.DIRECTIVE_ARG_REMOVED,
122 description: "".concat(oldArg.name, " was removed from ").concat(_oldDirective.name, ".")
123 });
124 }
125
126 for (var _i10 = 0, _oldDirective$locatio2 = _oldDirective.locations; _i10 < _oldDirective$locatio2.length; _i10++) {
127 var location = _oldDirective$locatio2[_i10];
128
129 if (newDirective.locations.indexOf(location) === -1) {
130 schemaChanges.push({
131 type: BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
132 description: "".concat(location, " was removed from ").concat(_oldDirective.name, ".")
133 });
134 }
135 }
136 }
137
138 return schemaChanges;
139}
140
141function findTypeChanges(oldSchema, newSchema) {
142 var schemaChanges = [];
143 var typesDiff = diff((0, _objectValues.default)(oldSchema.getTypeMap()), (0, _objectValues.default)(newSchema.getTypeMap()));
144
145 for (var _i12 = 0, _typesDiff$removed2 = typesDiff.removed; _i12 < _typesDiff$removed2.length; _i12++) {
146 var oldType = _typesDiff$removed2[_i12];
147 schemaChanges.push({
148 type: BreakingChangeType.TYPE_REMOVED,
149 description: "".concat(oldType.name, " was removed.")
150 });
151 }
152
153 for (var _i14 = 0, _typesDiff$persisted2 = typesDiff.persisted; _i14 < _typesDiff$persisted2.length; _i14++) {
154 var _ref4 = _typesDiff$persisted2[_i14];
155 var _oldType = _ref4[0];
156 var newType = _ref4[1];
157
158 if ((0, _definition.isEnumType)(_oldType) && (0, _definition.isEnumType)(newType)) {
159 schemaChanges.push.apply(schemaChanges, findEnumTypeChanges(_oldType, newType));
160 } else if ((0, _definition.isUnionType)(_oldType) && (0, _definition.isUnionType)(newType)) {
161 schemaChanges.push.apply(schemaChanges, findUnionTypeChanges(_oldType, newType));
162 } else if ((0, _definition.isInputObjectType)(_oldType) && (0, _definition.isInputObjectType)(newType)) {
163 schemaChanges.push.apply(schemaChanges, findInputObjectTypeChanges(_oldType, newType));
164 } else if ((0, _definition.isObjectType)(_oldType) && (0, _definition.isObjectType)(newType)) {
165 schemaChanges.push.apply(schemaChanges, findObjectTypeChanges(_oldType, newType));
166 } else if ((0, _definition.isInterfaceType)(_oldType) && (0, _definition.isInterfaceType)(newType)) {
167 schemaChanges.push.apply(schemaChanges, findFieldChanges(_oldType, newType));
168 } else if (_oldType.constructor !== newType.constructor) {
169 schemaChanges.push({
170 type: BreakingChangeType.TYPE_CHANGED_KIND,
171 description: "".concat(_oldType.name, " changed from ") + "".concat(typeKindName(_oldType), " to ").concat(typeKindName(newType), ".")
172 });
173 }
174 }
175
176 return schemaChanges;
177}
178
179function findInputObjectTypeChanges(oldType, newType) {
180 var schemaChanges = [];
181 var fieldsDiff = diff((0, _objectValues.default)(oldType.getFields()), (0, _objectValues.default)(newType.getFields()));
182
183 for (var _i16 = 0, _fieldsDiff$added2 = fieldsDiff.added; _i16 < _fieldsDiff$added2.length; _i16++) {
184 var newField = _fieldsDiff$added2[_i16];
185
186 if ((0, _definition.isRequiredInputField)(newField)) {
187 schemaChanges.push({
188 type: BreakingChangeType.REQUIRED_INPUT_FIELD_ADDED,
189 description: "A required field ".concat(newField.name, " on input type ").concat(oldType.name, " was added.")
190 });
191 } else {
192 schemaChanges.push({
193 type: DangerousChangeType.OPTIONAL_INPUT_FIELD_ADDED,
194 description: "An optional field ".concat(newField.name, " on input type ").concat(oldType.name, " was added.")
195 });
196 }
197 }
198
199 for (var _i18 = 0, _fieldsDiff$removed2 = fieldsDiff.removed; _i18 < _fieldsDiff$removed2.length; _i18++) {
200 var oldField = _fieldsDiff$removed2[_i18];
201 schemaChanges.push({
202 type: BreakingChangeType.FIELD_REMOVED,
203 description: "".concat(oldType.name, ".").concat(oldField.name, " was removed.")
204 });
205 }
206
207 for (var _i20 = 0, _fieldsDiff$persisted2 = fieldsDiff.persisted; _i20 < _fieldsDiff$persisted2.length; _i20++) {
208 var _ref6 = _fieldsDiff$persisted2[_i20];
209 var _oldField = _ref6[0];
210 var _newField = _ref6[1];
211 var isSafe = isChangeSafeForInputObjectFieldOrFieldArg(_oldField.type, _newField.type);
212
213 if (!isSafe) {
214 schemaChanges.push({
215 type: BreakingChangeType.FIELD_CHANGED_KIND,
216 description: "".concat(oldType.name, ".").concat(_oldField.name, " changed type from ") + "".concat(String(_oldField.type), " to ").concat(String(_newField.type), ".")
217 });
218 }
219 }
220
221 return schemaChanges;
222}
223
224function findUnionTypeChanges(oldType, newType) {
225 var schemaChanges = [];
226 var possibleTypesDiff = diff(oldType.getTypes(), newType.getTypes());
227
228 for (var _i22 = 0, _possibleTypesDiff$ad2 = possibleTypesDiff.added; _i22 < _possibleTypesDiff$ad2.length; _i22++) {
229 var newPossibleType = _possibleTypesDiff$ad2[_i22];
230 schemaChanges.push({
231 type: DangerousChangeType.TYPE_ADDED_TO_UNION,
232 description: "".concat(newPossibleType.name, " was added to union type ").concat(oldType.name, ".")
233 });
234 }
235
236 for (var _i24 = 0, _possibleTypesDiff$re2 = possibleTypesDiff.removed; _i24 < _possibleTypesDiff$re2.length; _i24++) {
237 var oldPossibleType = _possibleTypesDiff$re2[_i24];
238 schemaChanges.push({
239 type: BreakingChangeType.TYPE_REMOVED_FROM_UNION,
240 description: "".concat(oldPossibleType.name, " was removed from union type ").concat(oldType.name, ".")
241 });
242 }
243
244 return schemaChanges;
245}
246
247function findEnumTypeChanges(oldType, newType) {
248 var schemaChanges = [];
249 var valuesDiff = diff(oldType.getValues(), newType.getValues());
250
251 for (var _i26 = 0, _valuesDiff$added2 = valuesDiff.added; _i26 < _valuesDiff$added2.length; _i26++) {
252 var newValue = _valuesDiff$added2[_i26];
253 schemaChanges.push({
254 type: DangerousChangeType.VALUE_ADDED_TO_ENUM,
255 description: "".concat(newValue.name, " was added to enum type ").concat(oldType.name, ".")
256 });
257 }
258
259 for (var _i28 = 0, _valuesDiff$removed2 = valuesDiff.removed; _i28 < _valuesDiff$removed2.length; _i28++) {
260 var oldValue = _valuesDiff$removed2[_i28];
261 schemaChanges.push({
262 type: BreakingChangeType.VALUE_REMOVED_FROM_ENUM,
263 description: "".concat(oldValue.name, " was removed from enum type ").concat(oldType.name, ".")
264 });
265 }
266
267 return schemaChanges;
268}
269
270function findObjectTypeChanges(oldType, newType) {
271 var schemaChanges = findFieldChanges(oldType, newType);
272 var interfacesDiff = diff(oldType.getInterfaces(), newType.getInterfaces());
273
274 for (var _i30 = 0, _interfacesDiff$added2 = interfacesDiff.added; _i30 < _interfacesDiff$added2.length; _i30++) {
275 var newInterface = _interfacesDiff$added2[_i30];
276 schemaChanges.push({
277 type: DangerousChangeType.INTERFACE_ADDED_TO_OBJECT,
278 description: "".concat(newInterface.name, " added to interfaces implemented by ").concat(oldType.name, ".")
279 });
280 }
281
282 for (var _i32 = 0, _interfacesDiff$remov2 = interfacesDiff.removed; _i32 < _interfacesDiff$remov2.length; _i32++) {
283 var oldInterface = _interfacesDiff$remov2[_i32];
284 schemaChanges.push({
285 type: BreakingChangeType.INTERFACE_REMOVED_FROM_OBJECT,
286 description: "".concat(oldType.name, " no longer implements interface ").concat(oldInterface.name, ".")
287 });
288 }
289
290 return schemaChanges;
291}
292
293function findFieldChanges(oldType, newType) {
294 var schemaChanges = [];
295 var fieldsDiff = diff((0, _objectValues.default)(oldType.getFields()), (0, _objectValues.default)(newType.getFields()));
296
297 for (var _i34 = 0, _fieldsDiff$removed4 = fieldsDiff.removed; _i34 < _fieldsDiff$removed4.length; _i34++) {
298 var oldField = _fieldsDiff$removed4[_i34];
299 schemaChanges.push({
300 type: BreakingChangeType.FIELD_REMOVED,
301 description: "".concat(oldType.name, ".").concat(oldField.name, " was removed.")
302 });
303 }
304
305 for (var _i36 = 0, _fieldsDiff$persisted4 = fieldsDiff.persisted; _i36 < _fieldsDiff$persisted4.length; _i36++) {
306 var _ref8 = _fieldsDiff$persisted4[_i36];
307 var _oldField2 = _ref8[0];
308 var newField = _ref8[1];
309 schemaChanges.push.apply(schemaChanges, findArgChanges(oldType, _oldField2, newField));
310 var isSafe = isChangeSafeForObjectOrInterfaceField(_oldField2.type, newField.type);
311
312 if (!isSafe) {
313 schemaChanges.push({
314 type: BreakingChangeType.FIELD_CHANGED_KIND,
315 description: "".concat(oldType.name, ".").concat(_oldField2.name, " changed type from ") + "".concat(String(_oldField2.type), " to ").concat(String(newField.type), ".")
316 });
317 }
318 }
319
320 return schemaChanges;
321}
322
323function findArgChanges(oldType, oldField, newField) {
324 var schemaChanges = [];
325 var argsDiff = diff(oldField.args, newField.args);
326
327 for (var _i38 = 0, _argsDiff$removed4 = argsDiff.removed; _i38 < _argsDiff$removed4.length; _i38++) {
328 var oldArg = _argsDiff$removed4[_i38];
329 schemaChanges.push({
330 type: BreakingChangeType.ARG_REMOVED,
331 description: "".concat(oldType.name, ".").concat(oldField.name, " arg ").concat(oldArg.name, " was removed.")
332 });
333 }
334
335 for (var _i40 = 0, _argsDiff$persisted2 = argsDiff.persisted; _i40 < _argsDiff$persisted2.length; _i40++) {
336 var _ref10 = _argsDiff$persisted2[_i40];
337 var _oldArg = _ref10[0];
338 var newArg = _ref10[1];
339 var isSafe = isChangeSafeForInputObjectFieldOrFieldArg(_oldArg.type, newArg.type);
340
341 if (!isSafe) {
342 schemaChanges.push({
343 type: BreakingChangeType.ARG_CHANGED_KIND,
344 description: "".concat(oldType.name, ".").concat(oldField.name, " arg ").concat(_oldArg.name, " has changed type from ") + "".concat(String(_oldArg.type), " to ").concat(String(newArg.type), ".")
345 });
346 } else if (_oldArg.defaultValue !== undefined) {
347 if (newArg.defaultValue === undefined) {
348 schemaChanges.push({
349 type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
350 description: "".concat(oldType.name, ".").concat(oldField.name, " arg ").concat(_oldArg.name, " defaultValue was removed.")
351 });
352 } else {
353 // Since we looking only for client's observable changes we should
354 // compare default values in the same representation as they are
355 // represented inside introspection.
356 var oldValueStr = stringifyValue(_oldArg.defaultValue, _oldArg.type);
357 var newValueStr = stringifyValue(newArg.defaultValue, newArg.type);
358
359 if (oldValueStr !== newValueStr) {
360 schemaChanges.push({
361 type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
362 description: "".concat(oldType.name, ".").concat(oldField.name, " arg ").concat(_oldArg.name, " has changed defaultValue from ").concat(oldValueStr, " to ").concat(newValueStr, ".")
363 });
364 }
365 }
366 }
367 }
368
369 for (var _i42 = 0, _argsDiff$added4 = argsDiff.added; _i42 < _argsDiff$added4.length; _i42++) {
370 var _newArg = _argsDiff$added4[_i42];
371
372 if ((0, _definition.isRequiredArgument)(_newArg)) {
373 schemaChanges.push({
374 type: BreakingChangeType.REQUIRED_ARG_ADDED,
375 description: "A required arg ".concat(_newArg.name, " on ").concat(oldType.name, ".").concat(oldField.name, " was added.")
376 });
377 } else {
378 schemaChanges.push({
379 type: DangerousChangeType.OPTIONAL_ARG_ADDED,
380 description: "An optional arg ".concat(_newArg.name, " on ").concat(oldType.name, ".").concat(oldField.name, " was added.")
381 });
382 }
383 }
384
385 return schemaChanges;
386}
387
388function isChangeSafeForObjectOrInterfaceField(oldType, newType) {
389 if ((0, _definition.isListType)(oldType)) {
390 return (// if they're both lists, make sure the underlying types are compatible
391 (0, _definition.isListType)(newType) && isChangeSafeForObjectOrInterfaceField(oldType.ofType, newType.ofType) || // moving from nullable to non-null of the same underlying type is safe
392 (0, _definition.isNonNullType)(newType) && isChangeSafeForObjectOrInterfaceField(oldType, newType.ofType)
393 );
394 }
395
396 if ((0, _definition.isNonNullType)(oldType)) {
397 // if they're both non-null, make sure the underlying types are compatible
398 return (0, _definition.isNonNullType)(newType) && isChangeSafeForObjectOrInterfaceField(oldType.ofType, newType.ofType);
399 }
400
401 return (// if they're both named types, see if their names are equivalent
402 (0, _definition.isNamedType)(newType) && oldType.name === newType.name || // moving from nullable to non-null of the same underlying type is safe
403 (0, _definition.isNonNullType)(newType) && isChangeSafeForObjectOrInterfaceField(oldType, newType.ofType)
404 );
405}
406
407function isChangeSafeForInputObjectFieldOrFieldArg(oldType, newType) {
408 if ((0, _definition.isListType)(oldType)) {
409 // if they're both lists, make sure the underlying types are compatible
410 return (0, _definition.isListType)(newType) && isChangeSafeForInputObjectFieldOrFieldArg(oldType.ofType, newType.ofType);
411 }
412
413 if ((0, _definition.isNonNullType)(oldType)) {
414 return (// if they're both non-null, make sure the underlying types are
415 // compatible
416 (0, _definition.isNonNullType)(newType) && isChangeSafeForInputObjectFieldOrFieldArg(oldType.ofType, newType.ofType) || // moving from non-null to nullable of the same underlying type is safe
417 !(0, _definition.isNonNullType)(newType) && isChangeSafeForInputObjectFieldOrFieldArg(oldType.ofType, newType)
418 );
419 } // if they're both named types, see if their names are equivalent
420
421
422 return (0, _definition.isNamedType)(newType) && oldType.name === newType.name;
423}
424
425function typeKindName(type) {
426 if ((0, _definition.isScalarType)(type)) {
427 return 'a Scalar type';
428 }
429
430 if ((0, _definition.isObjectType)(type)) {
431 return 'an Object type';
432 }
433
434 if ((0, _definition.isInterfaceType)(type)) {
435 return 'an Interface type';
436 }
437
438 if ((0, _definition.isUnionType)(type)) {
439 return 'a Union type';
440 }
441
442 if ((0, _definition.isEnumType)(type)) {
443 return 'an Enum type';
444 }
445
446 /* istanbul ignore else */
447 if ((0, _definition.isInputObjectType)(type)) {
448 return 'an Input type';
449 } // Not reachable. All possible named types have been considered.
450
451
452 /* istanbul ignore next */
453 (0, _invariant.default)(false, 'Unexpected type: ' + (0, _inspect.default)(type));
454}
455
456function stringifyValue(value, type) {
457 var ast = (0, _astFromValue.astFromValue)(value, type);
458
459 /* istanbul ignore next */
460 ast != null || (0, _invariant.default)(0);
461 var sortedAST = (0, _visitor.visit)(ast, {
462 ObjectValue: function ObjectValue(objectNode) {
463 var fields = [].concat(objectNode.fields).sort(function (fieldA, fieldB) {
464 return fieldA.name.value.localeCompare(fieldB.name.value);
465 });
466 return _objectSpread({}, objectNode, {
467 fields: fields
468 });
469 }
470 });
471 return (0, _printer.print)(sortedAST);
472}
473
474function diff(oldArray, newArray) {
475 var added = [];
476 var removed = [];
477 var persisted = [];
478 var oldMap = (0, _keyMap.default)(oldArray, function (_ref11) {
479 var name = _ref11.name;
480 return name;
481 });
482 var newMap = (0, _keyMap.default)(newArray, function (_ref12) {
483 var name = _ref12.name;
484 return name;
485 });
486
487 for (var _i44 = 0; _i44 < oldArray.length; _i44++) {
488 var oldItem = oldArray[_i44];
489 var newItem = newMap[oldItem.name];
490
491 if (newItem === undefined) {
492 removed.push(oldItem);
493 } else {
494 persisted.push([oldItem, newItem]);
495 }
496 }
497
498 for (var _i46 = 0; _i46 < newArray.length; _i46++) {
499 var _newItem = newArray[_i46];
500
501 if (oldMap[_newItem.name] === undefined) {
502 added.push(_newItem);
503 }
504 }
505
506 return {
507 added: added,
508 persisted: persisted,
509 removed: removed
510 };
511}