/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * * @format */ 'use strict'; var _objectSpread2 = require("@babel/runtime/helpers/interopRequireDefault")(require("@babel/runtime/helpers/objectSpread")); /** * @public * * Low-level record manipulation methods. * * A note about perf: we use long-hand property access rather than computed * properties in this file for speed ie. * * const object = {}; * object[KEY] = value; * record[storageKey] = object; * * instead of: * * record[storageKey] = { * [KEY]: value, * }; * * The latter gets transformed by Babel into something like: * * function _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; * } * * record[storageKey] = _defineProperty({}, KEY, value); * * A quick benchmark shows that computed property access is an order of * magnitude slower (times in seconds for 100,000 iterations): * * best avg sd * computed 0.02175 0.02292 0.00113 * manual 0.00110 0.00123 0.00008 */ /** * @public * * Clone a record. */ function clone(record) { return (0, _objectSpread2["default"])({}, record); } /** * @public * * Copies all fields from `source` to `sink`, excluding `__id` and `__typename`. * * NOTE: This function does not treat `id` specially. To preserve the id, * manually reset it after calling this function. Also note that values are * copied by reference and not value; callers should ensure that values are * copied on write. */ function copyFields(source, sink) { for (var key in source) { if (source.hasOwnProperty(key)) { if (key !== require("./RelayStoreUtils").ID_KEY && key !== require("./RelayStoreUtils").TYPENAME_KEY) { sink[key] = source[key]; } } } } /** * @public * * Create a new record. */ function create(dataID, typeName) { // See perf note above for why we aren't using computed property access. var record = {}; record[require("./RelayStoreUtils").ID_KEY] = dataID; record[require("./RelayStoreUtils").TYPENAME_KEY] = typeName; return record; } /** * @public * * Get the record's `id` if available or the client-generated identifier. */ function getDataID(record) { return record[require("./RelayStoreUtils").ID_KEY]; } /** * @public * * Get the concrete type of the record. */ function getType(record) { return record[require("./RelayStoreUtils").TYPENAME_KEY]; } /** * @public * * Get a scalar (non-link) field value. */ function getValue(record, storageKey) { var value = record[storageKey]; if (value && typeof value === 'object') { !(!value.hasOwnProperty(require("./RelayStoreUtils").REF_KEY) && !value.hasOwnProperty(require("./RelayStoreUtils").REFS_KEY)) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernRecord.getValue(): Expected a scalar (non-link) value for `%s.%s` ' + 'but found %s.', record[require("./RelayStoreUtils").ID_KEY], storageKey, value.hasOwnProperty(require("./RelayStoreUtils").REF_KEY) ? 'a linked record' : 'plural linked records') : require("fbjs/lib/invariant")(false) : void 0; } return value; } /** * @public * * Get the value of a field as a reference to another record. Throws if the * field has a different type. */ function getLinkedRecordID(record, storageKey) { var link = record[storageKey]; if (link == null) { return link; } !(typeof link === 'object' && link && typeof link[require("./RelayStoreUtils").REF_KEY] === 'string') ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernRecord.getLinkedRecordID(): Expected `%s.%s` to be a linked ID, ' + 'was `%s`.', record[require("./RelayStoreUtils").ID_KEY], storageKey, JSON.stringify(link)) : require("fbjs/lib/invariant")(false) : void 0; return link[require("./RelayStoreUtils").REF_KEY]; } /** * @public * * Get the value of a field as a list of references to other records. Throws if * the field has a different type. */ function getLinkedRecordIDs(record, storageKey) { var links = record[storageKey]; if (links == null) { return links; } !(typeof links === 'object' && Array.isArray(links[require("./RelayStoreUtils").REFS_KEY])) ? process.env.NODE_ENV !== "production" ? require("fbjs/lib/invariant")(false, 'RelayModernRecord.getLinkedRecordIDs(): Expected `%s.%s` to contain an array ' + 'of linked IDs, got `%s`.', record[require("./RelayStoreUtils").ID_KEY], storageKey, JSON.stringify(links)) : require("fbjs/lib/invariant")(false) : void 0; // assume items of the array are ids return links[require("./RelayStoreUtils").REFS_KEY]; } /** * @public * * Compares the fields of a previous and new record, returning either the * previous record if all fields are equal or a new record (with merged fields) * if any fields have changed. */ function update(prevRecord, nextRecord) { if (process.env.NODE_ENV !== "production") { var _getType, _getType2; var prevID = getDataID(prevRecord); var nextID = getDataID(nextRecord); process.env.NODE_ENV !== "production" ? require("fbjs/lib/warning")(prevID === nextID, 'RelayModernRecord: Invalid record update, expected both versions of ' + 'the record to have the same id, got `%s` and `%s`.', prevID, nextID) : void 0; // note: coalesce null/undefined to null var prevType = (_getType = getType(prevRecord)) !== null && _getType !== void 0 ? _getType : null; var nextType = (_getType2 = getType(nextRecord)) !== null && _getType2 !== void 0 ? _getType2 : null; process.env.NODE_ENV !== "production" ? require("fbjs/lib/warning")(prevType === nextType, 'RelayModernRecord: Invalid record update, expected both versions of ' + 'record `%s` to have the same `%s` but got conflicting types `%s` ' + 'and `%s`. The GraphQL server likely violated the globally unique ' + 'id requirement by returning the same id for different objects.', prevID, require("./RelayStoreUtils").TYPENAME_KEY, prevType, nextType) : void 0; } var updated = null; var keys = Object.keys(nextRecord); for (var ii = 0; ii < keys.length; ii++) { var key = keys[ii]; if (updated || !require("fbjs/lib/areEqual")(prevRecord[key], nextRecord[key])) { updated = updated !== null ? updated : (0, _objectSpread2["default"])({}, prevRecord); if (nextRecord[key] !== require("./RelayStoreUtils").UNPUBLISH_FIELD_SENTINEL) { updated[key] = nextRecord[key]; } else { delete updated[key]; } } } return updated !== null ? updated : prevRecord; } /** * @public * * Returns a new record with the contents of the given records. Fields in the * second record will overwrite identical fields in the first record. */ function merge(record1, record2) { if (process.env.NODE_ENV !== "production") { var _getType3, _getType4; var prevID = getDataID(record1); var nextID = getDataID(record2); process.env.NODE_ENV !== "production" ? require("fbjs/lib/warning")(prevID === nextID, 'RelayModernRecord: Invalid record merge, expected both versions of ' + 'the record to have the same id, got `%s` and `%s`.', prevID, nextID) : void 0; // note: coalesce null/undefined to null var prevType = (_getType3 = getType(record1)) !== null && _getType3 !== void 0 ? _getType3 : null; var nextType = (_getType4 = getType(record2)) !== null && _getType4 !== void 0 ? _getType4 : null; process.env.NODE_ENV !== "production" ? require("fbjs/lib/warning")(prevType === nextType, 'RelayModernRecord: Invalid record merge, expected both versions of ' + 'record `%s` to have the same `%s` but got conflicting types `%s` ' + 'and `%s`. The GraphQL server likely violated the globally unique ' + 'id requirement by returning the same id for different objects.', prevID, require("./RelayStoreUtils").TYPENAME_KEY, prevType, nextType) : void 0; } return Object.assign({}, record1, record2); } /** * @public * * Prevent modifications to the record. Attempts to call `set*` functions on a * frozen record will fatal at runtime. */ function freeze(record) { require("./deepFreeze")(record); } /** * @public * * Set the value of a storageKey to a scalar. */ function setValue(record, storageKey, value) { if (process.env.NODE_ENV !== "production") { var prevID = getDataID(record); if (storageKey === require("./RelayStoreUtils").ID_KEY) { process.env.NODE_ENV !== "production" ? require("fbjs/lib/warning")(prevID === value, 'RelayModernRecord: Invalid field update, expected both versions of ' + 'the record to have the same id, got `%s` and `%s`.', prevID, value) : void 0; } else if (storageKey === require("./RelayStoreUtils").TYPENAME_KEY) { var _getType5, _value; // note: coalesce null/undefined to null var prevType = (_getType5 = getType(record)) !== null && _getType5 !== void 0 ? _getType5 : null; var nextType = (_value = value) !== null && _value !== void 0 ? _value : null; process.env.NODE_ENV !== "production" ? require("fbjs/lib/warning")(prevType === nextType, 'RelayModernRecord: Invalid field update, expected both versions of ' + 'record `%s` to have the same `%s` but got conflicting types `%s` ' + 'and `%s`. The GraphQL server likely violated the globally unique ' + 'id requirement by returning the same id for different objects.', prevID, require("./RelayStoreUtils").TYPENAME_KEY, prevType, nextType) : void 0; } } record[storageKey] = value; } /** * @public * * Set the value of a field to a reference to another record. */ function setLinkedRecordID(record, storageKey, linkedID) { // See perf note above for why we aren't using computed property access. var link = {}; link[require("./RelayStoreUtils").REF_KEY] = linkedID; record[storageKey] = link; } /** * @public * * Set the value of a field to a list of references other records. */ function setLinkedRecordIDs(record, storageKey, linkedIDs) { // See perf note above for why we aren't using computed property access. var links = {}; links[require("./RelayStoreUtils").REFS_KEY] = linkedIDs; record[storageKey] = links; } module.exports = { clone: clone, copyFields: copyFields, create: create, freeze: freeze, getDataID: getDataID, getLinkedRecordID: getLinkedRecordID, getLinkedRecordIDs: getLinkedRecordIDs, getType: getType, getValue: getValue, merge: merge, setValue: setValue, setLinkedRecordID: setLinkedRecordID, setLinkedRecordIDs: setLinkedRecordIDs, update: update };