| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447 | "use strict";Object.defineProperty(exports, "__esModule", {  value: true});exports.default = void 0;var _CoreManager = _interopRequireDefault(require("./CoreManager"));var _canBeSerialized = _interopRequireDefault(require("./canBeSerialized"));var _decode = _interopRequireDefault(require("./decode"));var _encode = _interopRequireDefault(require("./encode"));var _escape = _interopRequireDefault(require("./escape"));var _EventuallyQueue = _interopRequireDefault(require("./EventuallyQueue"));var _ParseACL = _interopRequireDefault(require("./ParseACL"));var _parseDate = _interopRequireDefault(require("./parseDate"));var _ParseError = _interopRequireDefault(require("./ParseError"));var _ParseFile = _interopRequireDefault(require("./ParseFile"));var _promiseUtils = require("./promiseUtils");var _LocalDatastoreUtils = require("./LocalDatastoreUtils");var _ParseOp = require("./ParseOp");var _ParseQuery = _interopRequireDefault(require("./ParseQuery"));var _ParseRelation = _interopRequireDefault(require("./ParseRelation"));var SingleInstanceStateController = _interopRequireWildcard(require("./SingleInstanceStateController"));var _unique = _interopRequireDefault(require("./unique"));var UniqueInstanceStateController = _interopRequireWildcard(require("./UniqueInstanceStateController"));var _unsavedChildren = _interopRequireDefault(require("./unsavedChildren"));function _getRequireWildcardCache(nodeInterop) {  if (typeof WeakMap !== "function") return null;  var cacheBabelInterop = new WeakMap();  var cacheNodeInterop = new WeakMap();  return (_getRequireWildcardCache = function (nodeInterop) {    return nodeInterop ? cacheNodeInterop : cacheBabelInterop;  })(nodeInterop);}function _interopRequireWildcard(obj, nodeInterop) {  if (!nodeInterop && obj && obj.__esModule) {    return obj;  }  if (obj === null || typeof obj !== "object" && typeof obj !== "function") {    return {      default: obj    };  }  var cache = _getRequireWildcardCache(nodeInterop);  if (cache && cache.has(obj)) {    return cache.get(obj);  }  var newObj = {};  var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;  for (var key in obj) {    if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {      var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;      if (desc && (desc.get || desc.set)) {        Object.defineProperty(newObj, key, desc);      } else {        newObj[key] = obj[key];      }    }  }  newObj.default = obj;  if (cache) {    cache.set(obj, newObj);  }  return newObj;}function _interopRequireDefault(obj) {  return obj && obj.__esModule ? obj : {    default: obj  };}/** * @flow *//*:: import type { AttributeMap, OpsMap } from './ObjectStateMutations';*//*:: import type { RequestOptions, FullOptions } from './RESTController';*/const uuidv4 = require('./uuid');/*:: export type Pointer = {  __type: string,  className: string,  objectId: string,};*//*:: type SaveParams = {  method: string,  path: string,  body: AttributeMap,};*//*:: export type SaveOptions = FullOptions & {  cascadeSave?: boolean,  context?: AttributeMap,};*/// Mapping of class names to constructors, so we can populate objects from the// server with appropriate subclasses of ParseObjectconst classMap = {};// Global counter for generating unique Ids for non-single-instance objectslet objectCount = 0;// On web clients, objects are single-instance: any two objects with the same Id// will have the same attributes. However, this may be dangerous default// behavior in a server scenariolet singleInstance = !_CoreManager.default.get('IS_NODE');if (singleInstance) {  _CoreManager.default.setObjectStateController(SingleInstanceStateController);} else {  _CoreManager.default.setObjectStateController(UniqueInstanceStateController);}function getServerUrlPath() {  let serverUrl = _CoreManager.default.get('SERVER_URL');  if (serverUrl[serverUrl.length - 1] !== '/') {    serverUrl += '/';  }  const url = serverUrl.replace(/https?:\/\//, '');  return url.substr(url.indexOf('/'));}/** * Creates a new model with defined attributes. * * <p>You won't normally call this method directly.  It is recommended that * you use a subclass of <code>Parse.Object</code> instead, created by calling * <code>extend</code>.</p> * * <p>However, if you don't want to use a subclass, or aren't sure which * subclass is appropriate, you can use this form:<pre> *     var object = new Parse.Object("ClassName"); * </pre> * That is basically equivalent to:<pre> *     var MyClass = Parse.Object.extend("ClassName"); *     var object = new MyClass(); * </pre></p> * * @alias Parse.Object */class ParseObject {  /**   * @param {string} className The class name for the object   * @param {object} attributes The initial set of data to store in the object.   * @param {object} options The options for this object instance.   */  constructor(className /*: ?string | { className: string, [attr: string]: mixed }*/, attributes /*:: ?: { [attr: string]: mixed }*/, options /*:: ?: { ignoreValidation: boolean }*/) {    // Enable legacy initializers    if (typeof this.initialize === 'function') {      this.initialize.apply(this, arguments);    }    let toSet = null;    this._objCount = objectCount++;    if (typeof className === 'string') {      this.className = className;      if (attributes && typeof attributes === 'object') {        toSet = attributes;      }    } else if (className && typeof className === 'object') {      this.className = className.className;      toSet = {};      for (const attr in className) {        if (attr !== 'className') {          toSet[attr] = className[attr];        }      }      if (attributes && typeof attributes === 'object') {        options = attributes;      }    }    if (toSet && !this.set(toSet, options)) {      throw new Error("Can't create an invalid Parse Object");    }  }  /**   * The ID of this object, unique within its class.   *   * @property {string} id   */  /*:: id: ?string;*/  /*:: _localId: ?string;*/  /*:: _objCount: number;*/  /*:: className: string;*/  /* Prototype getters / setters */  get attributes() /*: AttributeMap*/{    const stateController = _CoreManager.default.getObjectStateController();    return Object.freeze(stateController.estimateAttributes(this._getStateIdentifier()));  }  /**   * The first time this object was saved on the server.   *   * @property {Date} createdAt   * @returns {Date}   */  get createdAt() /*: ?Date*/{    return this._getServerData().createdAt;  }  /**   * The last time this object was updated on the server.   *   * @property {Date} updatedAt   * @returns {Date}   */  get updatedAt() /*: ?Date*/{    return this._getServerData().updatedAt;  }  /* Private methods */  /**   * Returns a local or server Id used uniquely identify this object   *   * @returns {string}   */  _getId() /*: string*/{    if (typeof this.id === 'string') {      return this.id;    }    if (typeof this._localId === 'string') {      return this._localId;    }    const localId = 'local' + uuidv4();    this._localId = localId;    return localId;  }  /**   * Returns a unique identifier used to pull data from the State Controller.   *   * @returns {Parse.Object|object}   */  _getStateIdentifier() /*: ParseObject | { id: string, className: string }*/{    if (singleInstance) {      let id = this.id;      if (!id) {        id = this._getId();      }      return {        id: id,        className: this.className      };    } else {      return this;    }  }  _getServerData() /*: AttributeMap*/{    const stateController = _CoreManager.default.getObjectStateController();    return stateController.getServerData(this._getStateIdentifier());  }  _clearServerData() {    const serverData = this._getServerData();    const unset = {};    for (const attr in serverData) {      unset[attr] = undefined;    }    const stateController = _CoreManager.default.getObjectStateController();    stateController.setServerData(this._getStateIdentifier(), unset);  }  _getPendingOps() /*: Array<OpsMap>*/{    const stateController = _CoreManager.default.getObjectStateController();    return stateController.getPendingOps(this._getStateIdentifier());  }  /**   * @param {Array<string>} [keysToClear] - if specified, only ops matching   * these fields will be cleared   */  _clearPendingOps(keysToClear /*:: ?: Array<string>*/) {    const pending = this._getPendingOps();    const latest = pending[pending.length - 1];    const keys = keysToClear || Object.keys(latest);    keys.forEach(key => {      delete latest[key];    });  }  _getDirtyObjectAttributes() /*: AttributeMap*/{    const attributes = this.attributes;    const stateController = _CoreManager.default.getObjectStateController();    const objectCache = stateController.getObjectCache(this._getStateIdentifier());    const dirty = {};    for (const attr in attributes) {      const val = attributes[attr];      if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile.default) && !(val instanceof _ParseRelation.default)) {        // Due to the way browsers construct maps, the key order will not change        // unless the object is changed        try {          const json = (0, _encode.default)(val, false, true);          const stringified = JSON.stringify(json);          if (objectCache[attr] !== stringified) {            dirty[attr] = val;          }        } catch (e) {          // Error occurred, possibly by a nested unsaved pointer in a mutable container          // No matter how it happened, it indicates a change in the attribute          dirty[attr] = val;        }      }    }    return dirty;  }  _toFullJSON(seen /*:: ?: Array<any>*/, offline /*:: ?: boolean*/) /*: AttributeMap*/{    const json /*: { [key: string]: mixed }*/ = this.toJSON(seen, offline);    json.__type = 'Object';    json.className = this.className;    return json;  }  _getSaveJSON() /*: AttributeMap*/{    const pending = this._getPendingOps();    const dirtyObjects = this._getDirtyObjectAttributes();    const json = {};    for (var attr in dirtyObjects) {      let isDotNotation = false;      for (let i = 0; i < pending.length; i += 1) {        for (const field in pending[i]) {          // Dot notation operations are handled later          if (field.includes('.')) {            const fieldName = field.split('.')[0];            if (fieldName === attr) {              isDotNotation = true;              break;            }          }        }      }      if (!isDotNotation) {        json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON();      }    }    for (attr in pending[0]) {      json[attr] = pending[0][attr].toJSON();    }    return json;  }  _getSaveParams() /*: SaveParams*/{    let method = this.id ? 'PUT' : 'POST';    const body = this._getSaveJSON();    let path = 'classes/' + this.className;    if (_CoreManager.default.get('ALLOW_CUSTOM_OBJECT_ID')) {      if (!this.createdAt) {        method = 'POST';        body.objectId = this.id;      } else {        method = 'PUT';        path += '/' + this.id;      }    } else if (this.id) {      path += '/' + this.id;    } else if (this.className === '_User') {      path = 'users';    }    return {      method,      body,      path    };  }  _finishFetch(serverData /*: AttributeMap*/) {    if (!this.id && serverData.objectId) {      this.id = serverData.objectId;    }    const stateController = _CoreManager.default.getObjectStateController();    stateController.initializeState(this._getStateIdentifier());    const decoded = {};    for (const attr in serverData) {      if (attr === 'ACL') {        decoded[attr] = new _ParseACL.default(serverData[attr]);      } else if (attr !== 'objectId') {        decoded[attr] = (0, _decode.default)(serverData[attr]);        if (decoded[attr] instanceof _ParseRelation.default) {          decoded[attr]._ensureParentAndKey(this, attr);        }      }    }    if (decoded.createdAt && typeof decoded.createdAt === 'string') {      decoded.createdAt = (0, _parseDate.default)(decoded.createdAt);    }    if (decoded.updatedAt && typeof decoded.updatedAt === 'string') {      decoded.updatedAt = (0, _parseDate.default)(decoded.updatedAt);    }    if (!decoded.updatedAt && decoded.createdAt) {      decoded.updatedAt = decoded.createdAt;    }    stateController.commitServerChanges(this._getStateIdentifier(), decoded);  }  _setExisted(existed /*: boolean*/) {    const stateController = _CoreManager.default.getObjectStateController();    const state = stateController.getState(this._getStateIdentifier());    if (state) {      state.existed = existed;    }  }  _migrateId(serverId /*: string*/) {    if (this._localId && serverId) {      if (singleInstance) {        const stateController = _CoreManager.default.getObjectStateController();        const oldState = stateController.removeState(this._getStateIdentifier());        this.id = serverId;        delete this._localId;        if (oldState) {          stateController.initializeState(this._getStateIdentifier(), oldState);        }      } else {        this.id = serverId;        delete this._localId;      }    }  }  _handleSaveResponse(response /*: AttributeMap*/, status /*: number*/) {    const changes = {};    const stateController = _CoreManager.default.getObjectStateController();    const pending = stateController.popPendingState(this._getStateIdentifier());    for (var attr in pending) {      if (pending[attr] instanceof _ParseOp.RelationOp) {        changes[attr] = pending[attr].applyTo(undefined, this, attr);      } else if (!(attr in response)) {        // Only SetOps and UnsetOps should not come back with results        changes[attr] = pending[attr].applyTo(undefined);      }    }    for (attr in response) {      if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') {        changes[attr] = (0, _parseDate.default)(response[attr]);      } else if (attr === 'ACL') {        changes[attr] = new _ParseACL.default(response[attr]);      } else if (attr !== 'objectId') {        const val = (0, _decode.default)(response[attr]);        if (val && Object.getPrototypeOf(val) === Object.prototype) {          changes[attr] = {            ...this.attributes[attr],            ...val          };        } else {          changes[attr] = val;        }        if (changes[attr] instanceof _ParseOp.UnsetOp) {          changes[attr] = undefined;        }      }    }    if (changes.createdAt && !changes.updatedAt) {      changes.updatedAt = changes.createdAt;    }    this._migrateId(response.objectId);    if (status !== 201) {      this._setExisted(true);    }    stateController.commitServerChanges(this._getStateIdentifier(), changes);  }  _handleSaveError() {    const stateController = _CoreManager.default.getObjectStateController();    stateController.mergeFirstPendingState(this._getStateIdentifier());  }  static _getClassMap() {    return classMap;  }  /* Public methods */  initialize() {    // NOOP  }  /**   * Returns a JSON version of the object suitable for saving to Parse.   *   * @param seen   * @param offline   * @returns {object}   */  toJSON(seen /*: Array<any> | void*/, offline /*:: ?: boolean*/) /*: AttributeMap*/{    const seenEntry = this.id ? this.className + ':' + this.id : this;    seen = seen || [seenEntry];    const json = {};    const attrs = this.attributes;    for (const attr in attrs) {      if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) {        json[attr] = attrs[attr].toJSON();      } else {        json[attr] = (0, _encode.default)(attrs[attr], false, false, seen, offline);      }    }    const pending = this._getPendingOps();    for (const attr in pending[0]) {      json[attr] = pending[0][attr].toJSON(offline);    }    if (this.id) {      json.objectId = this.id;    }    return json;  }  /**   * Determines whether this ParseObject is equal to another ParseObject   *   * @param {object} other - An other object ot compare   * @returns {boolean}   */  equals(other /*: mixed*/) /*: boolean*/{    if (this === other) {      return true;    }    return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined';  }  /**   * Returns true if this object has been modified since its last   * save/refresh.  If an attribute is specified, it returns true only if that   * particular attribute has been modified since the last save/refresh.   *   * @param {string} attr An attribute name (optional).   * @returns {boolean}   */  dirty(attr /*:: ?: string*/) /*: boolean*/{    if (!this.id) {      return true;    }    const pendingOps = this._getPendingOps();    const dirtyObjects = this._getDirtyObjectAttributes();    if (attr) {      if (dirtyObjects.hasOwnProperty(attr)) {        return true;      }      for (let i = 0; i < pendingOps.length; i++) {        if (pendingOps[i].hasOwnProperty(attr)) {          return true;        }      }      return false;    }    if (Object.keys(pendingOps[0]).length !== 0) {      return true;    }    if (Object.keys(dirtyObjects).length !== 0) {      return true;    }    return false;  }  /**   * Returns an array of keys that have been modified since last save/refresh   *   * @returns {string[]}   */  dirtyKeys() /*: Array<string>*/{    const pendingOps = this._getPendingOps();    const keys = {};    for (let i = 0; i < pendingOps.length; i++) {      for (const attr in pendingOps[i]) {        keys[attr] = true;      }    }    const dirtyObjects = this._getDirtyObjectAttributes();    for (const attr in dirtyObjects) {      keys[attr] = true;    }    return Object.keys(keys);  }  /**   * Returns true if the object has been fetched.   *   * @returns {boolean}   */  isDataAvailable() /*: boolean*/{    const serverData = this._getServerData();    return !!Object.keys(serverData).length;  }  /**   * Gets a Pointer referencing this Object.   *   * @returns {Pointer}   */  toPointer() /*: Pointer*/{    if (!this.id) {      throw new Error('Cannot create a pointer to an unsaved ParseObject');    }    return {      __type: 'Pointer',      className: this.className,      objectId: this.id    };  }  /**   * Gets a Pointer referencing this Object.   *   * @returns {Pointer}   */  toOfflinePointer() /*: Pointer*/{    if (!this._localId) {      throw new Error('Cannot create a offline pointer to a saved ParseObject');    }    return {      __type: 'Object',      className: this.className,      _localId: this._localId    };  }  /**   * Gets the value of an attribute.   *   * @param {string} attr The string name of an attribute.   * @returns {*}   */  get(attr /*: string*/) /*: mixed*/{    return this.attributes[attr];  }  /**   * Gets a relation on the given class for the attribute.   *   * @param {string} attr The attribute to get the relation for.   * @returns {Parse.Relation}   */  relation(attr /*: string*/) /*: ParseRelation*/{    const value = this.get(attr);    if (value) {      if (!(value instanceof _ParseRelation.default)) {        throw new Error('Called relation() on non-relation field ' + attr);      }      value._ensureParentAndKey(this, attr);      return value;    }    return new _ParseRelation.default(this, attr);  }  /**   * Gets the HTML-escaped value of an attribute.   *   * @param {string} attr The string name of an attribute.   * @returns {string}   */  escape(attr /*: string*/) /*: string*/{    let val = this.attributes[attr];    if (val == null) {      return '';    }    if (typeof val !== 'string') {      if (typeof val.toString !== 'function') {        return '';      }      val = val.toString();    }    return (0, _escape.default)(val);  }  /**   * Returns <code>true</code> if the attribute contains a value that is not   * null or undefined.   *   * @param {string} attr The string name of the attribute.   * @returns {boolean}   */  has(attr /*: string*/) /*: boolean*/{    const attributes = this.attributes;    if (attributes.hasOwnProperty(attr)) {      return attributes[attr] != null;    }    return false;  }  /**   * Sets a hash of model attributes on the object.   *   * <p>You can call it with an object containing keys and values, with one   * key and value, or dot notation.  For example:<pre>   *   gameTurn.set({   *     player: player1,   *     diceRoll: 2   *   }, {   *     error: function(gameTurnAgain, error) {   *       // The set failed validation.   *     }   *   });   *   *   game.set("currentPlayer", player2, {   *     error: function(gameTurnAgain, error) {   *       // The set failed validation.   *     }   *   });   *   *   game.set("finished", true);</pre></p>   *   *   game.set("player.score", 10);</pre></p>   *   * @param {(string|object)} key The key to set.   * @param {(string|object)} value The value to give it.   * @param {object} options A set of options for the set.   *     The only supported option is <code>error</code>.   * @returns {(ParseObject|boolean)} true if the set succeeded.   */  set(key /*: mixed*/, value /*: mixed*/, options /*:: ?: mixed*/) /*: ParseObject | boolean*/{    let changes = {};    const newOps = {};    if (key && typeof key === 'object') {      changes = key;      options = value;    } else if (typeof key === 'string') {      changes[key] = value;    } else {      return this;    }    options = options || {};    let readonly = [];    if (typeof this.constructor.readOnlyAttributes === 'function') {      readonly = readonly.concat(this.constructor.readOnlyAttributes());    }    for (const k in changes) {      if (k === 'createdAt' || k === 'updatedAt') {        // This property is read-only, but for legacy reasons we silently        // ignore it        continue;      }      if (readonly.indexOf(k) > -1) {        throw new Error('Cannot modify readonly attribute: ' + k);      }      if (options.unset) {        newOps[k] = new _ParseOp.UnsetOp();      } else if (changes[k] instanceof _ParseOp.Op) {        newOps[k] = changes[k];      } else if (changes[k] && typeof changes[k] === 'object' && typeof changes[k].__op === 'string') {        newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);      } else if (k === 'objectId' || k === 'id') {        if (typeof changes[k] === 'string') {          this.id = changes[k];        }      } else if (k === 'ACL' && typeof changes[k] === 'object' && !(changes[k] instanceof _ParseACL.default)) {        newOps[k] = new _ParseOp.SetOp(new _ParseACL.default(changes[k]));      } else if (changes[k] instanceof _ParseRelation.default) {        const relation = new _ParseRelation.default(this, k);        relation.targetClassName = changes[k].targetClassName;        newOps[k] = new _ParseOp.SetOp(relation);      } else {        newOps[k] = new _ParseOp.SetOp(changes[k]);      }    }    const currentAttributes = this.attributes;    // Calculate new values    const newValues = {};    for (const attr in newOps) {      if (newOps[attr] instanceof _ParseOp.RelationOp) {        newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);      } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {        newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);      }    }    // Validate changes    if (!options.ignoreValidation) {      const validation = this.validate(newValues);      if (validation) {        if (typeof options.error === 'function') {          options.error(this, validation);        }        return false;      }    }    // Consolidate Ops    const pendingOps = this._getPendingOps();    const last = pendingOps.length - 1;    const stateController = _CoreManager.default.getObjectStateController();    for (const attr in newOps) {      const nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);      stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);    }    return this;  }  /**   * Remove an attribute from the model. This is a noop if the attribute doesn't   * exist.   *   * @param {string} attr The string name of an attribute.   * @param options   * @returns {(ParseObject | boolean)}   */  unset(attr /*: string*/, options /*:: ?: { [opt: string]: mixed }*/) /*: ParseObject | boolean*/{    options = options || {};    options.unset = true;    return this.set(attr, null, options);  }  /**   * Atomically increments the value of the given attribute the next time the   * object is saved. If no amount is specified, 1 is used by default.   *   * @param attr {String} The key.   * @param amount {Number} The amount to increment by (optional).   * @returns {(ParseObject|boolean)}   */  increment(attr /*: string*/, amount /*:: ?: number*/) /*: ParseObject | boolean*/{    if (typeof amount === 'undefined') {      amount = 1;    }    if (typeof amount !== 'number') {      throw new Error('Cannot increment by a non-numeric amount.');    }    return this.set(attr, new _ParseOp.IncrementOp(amount));  }  /**   * Atomically decrements the value of the given attribute the next time the   * object is saved. If no amount is specified, 1 is used by default.   *   * @param attr {String} The key.   * @param amount {Number} The amount to decrement by (optional).   * @returns {(ParseObject | boolean)}   */  decrement(attr /*: string*/, amount /*:: ?: number*/) /*: ParseObject | boolean*/{    if (typeof amount === 'undefined') {      amount = 1;    }    if (typeof amount !== 'number') {      throw new Error('Cannot decrement by a non-numeric amount.');    }    return this.set(attr, new _ParseOp.IncrementOp(amount * -1));  }  /**   * Atomically add an object to the end of the array associated with a given   * key.   *   * @param attr {String} The key.   * @param item {} The item to add.   * @returns {(ParseObject | boolean)}   */  add(attr /*: string*/, item /*: mixed*/) /*: ParseObject | boolean*/{    return this.set(attr, new _ParseOp.AddOp([item]));  }  /**   * Atomically add the objects to the end of the array associated with a given   * key.   *   * @param attr {String} The key.   * @param items {Object[]} The items to add.   * @returns {(ParseObject | boolean)}   */  addAll(attr /*: string*/, items /*: Array<mixed>*/) /*: ParseObject | boolean*/{    return this.set(attr, new _ParseOp.AddOp(items));  }  /**   * Atomically add an object to the array associated with a given key, only   * if it is not already present in the array. The position of the insert is   * not guaranteed.   *   * @param attr {String} The key.   * @param item {} The object to add.   * @returns {(ParseObject | boolean)}   */  addUnique(attr /*: string*/, item /*: mixed*/) /*: ParseObject | boolean*/{    return this.set(attr, new _ParseOp.AddUniqueOp([item]));  }  /**   * Atomically add the objects to the array associated with a given key, only   * if it is not already present in the array. The position of the insert is   * not guaranteed.   *   * @param attr {String} The key.   * @param items {Object[]} The objects to add.   * @returns {(ParseObject | boolean)}   */  addAllUnique(attr /*: string*/, items /*: Array<mixed>*/) /*: ParseObject | boolean*/{    return this.set(attr, new _ParseOp.AddUniqueOp(items));  }  /**   * Atomically remove all instances of an object from the array associated   * with a given key.   *   * @param attr {String} The key.   * @param item {} The object to remove.   * @returns {(ParseObject | boolean)}   */  remove(attr /*: string*/, item /*: mixed*/) /*: ParseObject | boolean*/{    return this.set(attr, new _ParseOp.RemoveOp([item]));  }  /**   * Atomically remove all instances of the objects from the array associated   * with a given key.   *   * @param attr {String} The key.   * @param items {Object[]} The object to remove.   * @returns {(ParseObject | boolean)}   */  removeAll(attr /*: string*/, items /*: Array<mixed>*/) /*: ParseObject | boolean*/{    return this.set(attr, new _ParseOp.RemoveOp(items));  }  /**   * Returns an instance of a subclass of Parse.Op describing what kind of   * modification has been performed on this field since the last time it was   * saved. For example, after calling object.increment("x"), calling   * object.op("x") would return an instance of Parse.Op.Increment.   *   * @param attr {String} The key.   * @returns {Parse.Op | undefined} The operation, or undefined if none.   */  op(attr /*: string*/) /*: ?Op*/{    const pending = this._getPendingOps();    for (let i = pending.length; i--;) {      if (pending[i][attr]) {        return pending[i][attr];      }    }  }  /**   * Creates a new model with identical attributes to this one.   *   * @returns {Parse.Object}   */  clone() /*: any*/{    const clone = new this.constructor(this.className);    let attributes = this.attributes;    if (typeof this.constructor.readOnlyAttributes === 'function') {      const readonly = this.constructor.readOnlyAttributes() || [];      // Attributes are frozen, so we have to rebuild an object,      // rather than delete readonly keys      const copy = {};      for (const a in attributes) {        if (readonly.indexOf(a) < 0) {          copy[a] = attributes[a];        }      }      attributes = copy;    }    if (clone.set) {      clone.set(attributes);    }    return clone;  }  /**   * Creates a new instance of this object. Not to be confused with clone()   *   * @returns {Parse.Object}   */  newInstance() /*: any*/{    const clone = new this.constructor(this.className);    clone.id = this.id;    if (singleInstance) {      // Just return an object with the right id      return clone;    }    const stateController = _CoreManager.default.getObjectStateController();    if (stateController) {      stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());    }    return clone;  }  /**   * Returns true if this object has never been saved to Parse.   *   * @returns {boolean}   */  isNew() /*: boolean*/{    return !this.id;  }  /**   * Returns true if this object was created by the Parse server when the   * object might have already been there (e.g. in the case of a Facebook   * login)   *   * @returns {boolean}   */  existed() /*: boolean*/{    if (!this.id) {      return false;    }    const stateController = _CoreManager.default.getObjectStateController();    const state = stateController.getState(this._getStateIdentifier());    if (state) {      return state.existed;    }    return false;  }  /**   * Returns true if this object exists on the Server   *   * @param {object} options   * Valid options are:<ul>   *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   *     be used for this request.   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   * </ul>   * @returns {Promise<boolean>} A boolean promise that is fulfilled if object exists.   */  async exists(options /*:: ?: RequestOptions*/) /*: Promise<boolean>*/{    if (!this.id) {      return false;    }    try {      const query = new _ParseQuery.default(this.className);      await query.get(this.id, options);      return true;    } catch (e) {      if (e.code === _ParseError.default.OBJECT_NOT_FOUND) {        return false;      }      throw e;    }  }  /**   * Checks if the model is currently in a valid state.   *   * @returns {boolean}   */  isValid() /*: boolean*/{    return !this.validate(this.attributes);  }  /**   * You should not call this function directly unless you subclass   * <code>Parse.Object</code>, in which case you can override this method   * to provide additional validation on <code>set</code> and   * <code>save</code>.  Your implementation should return   *   * @param {object} attrs The current data to validate.   * @returns {Parse.Error|boolean} False if the data is valid.  An error object otherwise.   * @see Parse.Object#set   */  validate(attrs /*: AttributeMap*/) /*: ParseError | boolean*/{    if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL.default)) {      return new _ParseError.default(_ParseError.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');    }    for (const key in attrs) {      if (!/^[A-Za-z][0-9A-Za-z_.]*$/.test(key)) {        return new _ParseError.default(_ParseError.default.INVALID_KEY_NAME);      }    }    return false;  }  /**   * Returns the ACL for this object.   *   * @returns {Parse.ACL} An instance of Parse.ACL.   * @see Parse.Object#get   */  getACL() /*: ?ParseACL*/{    const acl = this.get('ACL');    if (acl instanceof _ParseACL.default) {      return acl;    }    return null;  }  /**   * Sets the ACL to be used for this object.   *   * @param {Parse.ACL} acl An instance of Parse.ACL.   * @param {object} options   * @returns {(ParseObject | boolean)} Whether the set passed validation.   * @see Parse.Object#set   */  setACL(acl /*: ParseACL*/, options /*:: ?: mixed*/) /*: ParseObject | boolean*/{    return this.set('ACL', acl, options);  }  /**   * Clears any (or specific) changes to this object made since the last call to save()   *   * @param {string} [keys] - specify which fields to revert   */  revert(...keys /*: Array<string>*/) /*: void*/{    let keysToRevert;    if (keys.length) {      keysToRevert = [];      for (const key of keys) {        if (typeof key === 'string') {          keysToRevert.push(key);        } else {          throw new Error('Parse.Object#revert expects either no, or a list of string, arguments.');        }      }    }    this._clearPendingOps(keysToRevert);  }  /**   * Clears all attributes on a model   *   * @returns {(ParseObject | boolean)}   */  clear() /*: ParseObject | boolean*/{    const attributes = this.attributes;    const erasable = {};    let readonly = ['createdAt', 'updatedAt'];    if (typeof this.constructor.readOnlyAttributes === 'function') {      readonly = readonly.concat(this.constructor.readOnlyAttributes());    }    for (const attr in attributes) {      if (readonly.indexOf(attr) < 0) {        erasable[attr] = true;      }    }    return this.set(erasable, {      unset: true    });  }  /**   * Fetch the model from the server. If the server's representation of the   * model differs from its current attributes, they will be overriden.   *   * @param {object} options   * Valid options are:<ul>   *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   *     be used for this request.   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   *   <li>include: The name(s) of the key(s) to include. Can be a string, an array of strings,   *       or an array of array of strings.   *   <li>context: A dictionary that is accessible in Cloud Code `beforeFind` trigger.   * </ul>   * @returns {Promise} A promise that is fulfilled when the fetch   *     completes.   */  fetch(options /*: RequestOptions*/) /*: Promise*/{    options = options || {};    const fetchOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      fetchOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      fetchOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('context') && typeof options.context === 'object') {      fetchOptions.context = options.context;    }    if (options.hasOwnProperty('include')) {      fetchOptions.include = [];      if (Array.isArray(options.include)) {        options.include.forEach(key => {          if (Array.isArray(key)) {            fetchOptions.include = fetchOptions.include.concat(key);          } else {            fetchOptions.include.push(key);          }        });      } else {        fetchOptions.include.push(options.include);      }    }    const controller = _CoreManager.default.getObjectController();    return controller.fetch(this, true, fetchOptions);  }  /**   * Fetch the model from the server. If the server's representation of the   * model differs from its current attributes, they will be overriden.   *   * Includes nested Parse.Objects for the provided key. You can use dot   * notation to specify which fields in the included object are also fetched.   *   * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.   * @param {object} options   * Valid options are:<ul>   *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   *     be used for this request.   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   * </ul>   * @returns {Promise} A promise that is fulfilled when the fetch   *     completes.   */  fetchWithInclude(keys /*: String | Array<string | Array<string>>*/, options /*: RequestOptions*/) /*: Promise*/{    options = options || {};    options.include = keys;    return this.fetch(options);  }  /**   * Saves this object to the server at some unspecified time in the future,   * even if Parse is currently inaccessible.   *   * Use this when you may not have a solid network connection, and don't need to know when the save completes.   * If there is some problem with the object such that it can't be saved, it will be silently discarded.   *   * Objects saved with this method will be stored locally in an on-disk cache until they can be delivered to Parse.   * They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection is   * available. Objects saved this way will persist even after the app is closed, in which case they will be sent the   * next time the app is opened.   *   * @param {object} [options]   * Used to pass option parameters to method if arg1 and arg2 were both passed as strings.   * Valid options are:   * <ul>   * <li>sessionToken: A valid session token, used for making a request on   * behalf of a specific user.   * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).   * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.   * </ul>   * @returns {Promise} A promise that is fulfilled when the save   * completes.   */  async saveEventually(options /*: SaveOptions*/) /*: Promise*/{    try {      await this.save(null, options);    } catch (e) {      if (e.message === 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {        await _EventuallyQueue.default.save(this, options);        _EventuallyQueue.default.poll();      }    }    return this;  }  /**   * Set a hash of model attributes, and save the model to the server.   * updatedAt will be updated when the request returns.   * You can either call it as:<pre>   * object.save();</pre>   * or<pre>   * object.save(attrs);</pre>   * or<pre>   * object.save(null, options);</pre>   * or<pre>   * object.save(attrs, options);</pre>   * or<pre>   * object.save(key, value);</pre>   * or<pre>   * object.save(key, value, options);</pre>   *   * Example 1: <pre>   * gameTurn.save({   * player: "Jake Cutter",   * diceRoll: 2   * }).then(function(gameTurnAgain) {   * // The save was successful.   * }, function(error) {   * // The save failed.  Error is an instance of Parse.Error.   * });</pre>   *   * Example 2: <pre>   * gameTurn.save("player", "Jake Cutter");</pre>   *   * @param {string | object | null} [arg1]   * Valid options are:<ul>   * <li>`Object` - Key/value pairs to update on the object.</li>   * <li>`String` Key - Key of attribute to update (requires arg2 to also be string)</li>   * <li>`null` - Passing null for arg1 allows you to save the object with options passed in arg2.</li>   * </ul>   * @param {string | object} [arg2]   * <ul>   * <li>`String` Value - If arg1 was passed as a key, arg2 is the value that should be set on that key.</li>   * <li>`Object` Options - Valid options are:   * <ul>   * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   * be used for this request.   * <li>sessionToken: A valid session token, used for making a request on   * behalf of a specific user.   * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).   * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.   * </ul>   * </li>   * </ul>   * @param {object} [arg3]   * Used to pass option parameters to method if arg1 and arg2 were both passed as strings.   * Valid options are:   * <ul>   * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   * be used for this request.   * <li>sessionToken: A valid session token, used for making a request on   * behalf of a specific user.   * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).   * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.   * </ul>   * @returns {Promise} A promise that is fulfilled when the save   * completes.   */  save(arg1 /*: ?string | { [attr: string]: mixed }*/, arg2 /*: SaveOptions | mixed*/, arg3 /*:: ?: SaveOptions*/) /*: Promise*/{    let attrs;    let options;    if (typeof arg1 === 'object' || typeof arg1 === 'undefined') {      attrs = arg1;      if (typeof arg2 === 'object') {        options = arg2;      }    } else {      attrs = {};      attrs[arg1] = arg2;      options = arg3;    }    if (attrs) {      const validation = this.validate(attrs);      if (validation) {        return Promise.reject(validation);      }      this.set(attrs, options);    }    options = options || {};    const saveOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      saveOptions.useMasterKey = !!options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken') && typeof options.sessionToken === 'string') {      saveOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('installationId') && typeof options.installationId === 'string') {      saveOptions.installationId = options.installationId;    }    if (options.hasOwnProperty('context') && typeof options.context === 'object') {      saveOptions.context = options.context;    }    const controller = _CoreManager.default.getObjectController();    const unsaved = options.cascadeSave !== false ? (0, _unsavedChildren.default)(this) : null;    return controller.save(unsaved, saveOptions).then(() => {      return controller.save(this, saveOptions);    });  }  /**   * Deletes this object from the server at some unspecified time in the future,   * even if Parse is currently inaccessible.   *   * Use this when you may not have a solid network connection,   * and don't need to know when the delete completes. If there is some problem with the object   * such that it can't be deleted, the request will be silently discarded.   *   * Delete instructions made with this method will be stored locally in an on-disk cache until they can be transmitted   * to Parse. They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection   * is available. Delete requests will persist even after the app is closed, in which case they will be sent the   * next time the app is opened.   *   * @param {object} [options]   * Valid options are:<ul>   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   *   <li>context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers.   * </ul>   * @returns {Promise} A promise that is fulfilled when the destroy   *     completes.   */  async destroyEventually(options /*: RequestOptions*/) /*: Promise*/{    try {      await this.destroy(options);    } catch (e) {      if (e.message === 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {        await _EventuallyQueue.default.destroy(this, options);        _EventuallyQueue.default.poll();      }    }    return this;  }  /**   * Destroy this model on the server if it was already persisted.   *   * @param {object} options   * Valid options are:<ul>   *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   *     be used for this request.   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   *   <li>context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers.   * </ul>   * @returns {Promise} A promise that is fulfilled when the destroy   *     completes.   */  destroy(options /*: RequestOptions*/) /*: Promise*/{    options = options || {};    const destroyOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      destroyOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      destroyOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('context') && typeof options.context === 'object') {      destroyOptions.context = options.context;    }    if (!this.id) {      return Promise.resolve();    }    return _CoreManager.default.getObjectController().destroy(this, destroyOptions);  }  /**   * Asynchronously stores the object and every object it points to in the local datastore,   * recursively, using a default pin name: _default.   *   * If those other objects have not been fetched from Parse, they will not be stored.   * However, if they have changed data, all the changes will be retained.   *   * <pre>   * await object.pin();   * </pre>   *   * To retrieve object:   * <code>query.fromLocalDatastore()</code> or <code>query.fromPin()</code>   *   * @returns {Promise} A promise that is fulfilled when the pin completes.   */  pin() /*: Promise<void>*/{    return ParseObject.pinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, [this]);  }  /**   * Asynchronously removes the object and every object it points to in the local datastore,   * recursively, using a default pin name: _default.   *   * <pre>   * await object.unPin();   * </pre>   *   * @returns {Promise} A promise that is fulfilled when the unPin completes.   */  unPin() /*: Promise<void>*/{    return ParseObject.unPinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, [this]);  }  /**   * Asynchronously returns if the object is pinned   *   * <pre>   * const isPinned = await object.isPinned();   * </pre>   *   * @returns {Promise<boolean>} A boolean promise that is fulfilled if object is pinned.   */  async isPinned() /*: Promise<boolean>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (!localDatastore.isEnabled) {      return Promise.reject('Parse.enableLocalDatastore() must be called first');    }    const objectKey = localDatastore.getKeyForObject(this);    const pin = await localDatastore.fromPinWithName(objectKey);    return pin.length > 0;  }  /**   * Asynchronously stores the objects and every object they point to in the local datastore, recursively.   *   * If those other objects have not been fetched from Parse, they will not be stored.   * However, if they have changed data, all the changes will be retained.   *   * <pre>   * await object.pinWithName(name);   * </pre>   *   * To retrieve object:   * <code>query.fromLocalDatastore()</code> or <code>query.fromPinWithName(name)</code>   *   * @param {string} name Name of Pin.   * @returns {Promise} A promise that is fulfilled when the pin completes.   */  pinWithName(name /*: string*/) /*: Promise<void>*/{    return ParseObject.pinAllWithName(name, [this]);  }  /**   * Asynchronously removes the object and every object it points to in the local datastore, recursively.   *   * <pre>   * await object.unPinWithName(name);   * </pre>   *   * @param {string} name Name of Pin.   * @returns {Promise} A promise that is fulfilled when the unPin completes.   */  unPinWithName(name /*: string*/) /*: Promise<void>*/{    return ParseObject.unPinAllWithName(name, [this]);  }  /**   * Asynchronously loads data from the local datastore into this object.   *   * <pre>   * await object.fetchFromLocalDatastore();   * </pre>   *   * You can create an unfetched pointer with <code>Parse.Object.createWithoutData()</code>   * and then call <code>fetchFromLocalDatastore()</code> on it.   *   * @returns {Promise} A promise that is fulfilled when the fetch completes.   */  async fetchFromLocalDatastore() /*: Promise<ParseObject>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (!localDatastore.isEnabled) {      throw new Error('Parse.enableLocalDatastore() must be called first');    }    const objectKey = localDatastore.getKeyForObject(this);    const pinned = await localDatastore._serializeObject(objectKey);    if (!pinned) {      throw new Error('Cannot fetch an unsaved ParseObject');    }    const result = ParseObject.fromJSON(pinned);    this._finishFetch(result.toJSON());    return this;  }  /* Static methods */  static _clearAllState() {    const stateController = _CoreManager.default.getObjectStateController();    stateController.clearAllState();  }  /**   * Fetches the given list of Parse.Object.   * If any error is encountered, stops and calls the error handler.   *   * <pre>   *   Parse.Object.fetchAll([object1, object2, ...])   *    .then((list) => {   *      // All the objects were fetched.   *    }, (error) => {   *      // An error occurred while fetching one of the objects.   *    });   * </pre>   *   * @param {Array} list A list of <code>Parse.Object</code>.   * @param {object} options   * Valid options are:<ul>   *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   *     be used for this request.   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   *   <li>include: The name(s) of the key(s) to include. Can be a string, an array of strings,   *       or an array of array of strings.   * </ul>   * @static   * @returns {Parse.Object[]}   */  static fetchAll(list /*: Array<ParseObject>*/, options /*: RequestOptions*/ = {}) {    const queryOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      queryOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      queryOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('include')) {      queryOptions.include = ParseObject.handleIncludeOptions(options);    }    return _CoreManager.default.getObjectController().fetch(list, true, queryOptions);  }  /**   * Fetches the given list of Parse.Object.   *   * Includes nested Parse.Objects for the provided key. You can use dot   * notation to specify which fields in the included object are also fetched.   *   * If any error is encountered, stops and calls the error handler.   *   * <pre>   *   Parse.Object.fetchAllWithInclude([object1, object2, ...], [pointer1, pointer2, ...])   *    .then((list) => {   *      // All the objects were fetched.   *    }, (error) => {   *      // An error occurred while fetching one of the objects.   *    });   * </pre>   *   * @param {Array} list A list of <code>Parse.Object</code>.   * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.   * @param {object} options   * Valid options are:<ul>   *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   *     be used for this request.   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   * </ul>   * @static   * @returns {Parse.Object[]}   */  static fetchAllWithInclude(list /*: Array<ParseObject>*/, keys /*: String | Array<string | Array<string>>*/, options /*: RequestOptions*/) {    options = options || {};    options.include = keys;    return ParseObject.fetchAll(list, options);  }  /**   * Fetches the given list of Parse.Object if needed.   * If any error is encountered, stops and calls the error handler.   *   * Includes nested Parse.Objects for the provided key. You can use dot   * notation to specify which fields in the included object are also fetched.   *   * If any error is encountered, stops and calls the error handler.   *   * <pre>   *   Parse.Object.fetchAllIfNeededWithInclude([object1, object2, ...], [pointer1, pointer2, ...])   *    .then((list) => {   *      // All the objects were fetched.   *    }, (error) => {   *      // An error occurred while fetching one of the objects.   *    });   * </pre>   *   * @param {Array} list A list of <code>Parse.Object</code>.   * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.   * @param {object} options   * Valid options are:<ul>   *   <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to   *     be used for this request.   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   * </ul>   * @static   * @returns {Parse.Object[]}   */  static fetchAllIfNeededWithInclude(list /*: Array<ParseObject>*/, keys /*: String | Array<string | Array<string>>*/, options /*: RequestOptions*/) {    options = options || {};    options.include = keys;    return ParseObject.fetchAllIfNeeded(list, options);  }  /**   * Fetches the given list of Parse.Object if needed.   * If any error is encountered, stops and calls the error handler.   *   * <pre>   *   Parse.Object.fetchAllIfNeeded([object1, ...])   *    .then((list) => {   *      // Objects were fetched and updated.   *    }, (error) => {   *      // An error occurred while fetching one of the objects.   *    });   * </pre>   *   * @param {Array} list A list of <code>Parse.Object</code>.   * @param {object} options   * @static   * @returns {Parse.Object[]}   */  static fetchAllIfNeeded(list /*: Array<ParseObject>*/, options) {    options = options || {};    const queryOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      queryOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      queryOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('include')) {      queryOptions.include = ParseObject.handleIncludeOptions(options);    }    return _CoreManager.default.getObjectController().fetch(list, false, queryOptions);  }  static handleIncludeOptions(options) {    let include = [];    if (Array.isArray(options.include)) {      options.include.forEach(key => {        if (Array.isArray(key)) {          include = include.concat(key);        } else {          include.push(key);        }      });    } else {      include.push(options.include);    }    return include;  }  /**   * Destroy the given list of models on the server if it was already persisted.   *   * <p>Unlike saveAll, if an error occurs while deleting an individual model,   * this method will continue trying to delete the rest of the models if   * possible, except in the case of a fatal error like a connection error.   *   * <p>In particular, the Parse.Error object returned in the case of error may   * be one of two types:   *   * <ul>   * <li>A Parse.Error.AGGREGATE_ERROR. This object's "errors" property is an   * array of other Parse.Error objects. Each error object in this array   * has an "object" property that references the object that could not be   * deleted (for instance, because that object could not be found).</li>   * <li>A non-aggregate Parse.Error. This indicates a serious error that   * caused the delete operation to be aborted partway through (for   * instance, a connection failure in the middle of the delete).</li>   * </ul>   *   * <pre>   * Parse.Object.destroyAll([object1, object2, ...])   * .then((list) => {   * // All the objects were deleted.   * }, (error) => {   * // An error occurred while deleting one or more of the objects.   * // If this is an aggregate error, then we can inspect each error   * // object individually to determine the reason why a particular   * // object was not deleted.   * if (error.code === Parse.Error.AGGREGATE_ERROR) {   * for (var i = 0; i < error.errors.length; i++) {   * console.log("Couldn't delete " + error.errors[i].object.id +   * "due to " + error.errors[i].message);   * }   * } else {   * console.log("Delete aborted because of " + error.message);   * }   * });   * </pre>   *   * @param {Array} list A list of <code>Parse.Object</code>.   * @param {object} options   * @static   * @returns {Promise} A promise that is fulfilled when the destroyAll   * completes.   */  static destroyAll(list /*: Array<ParseObject>*/, options = {}) {    const destroyOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      destroyOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      destroyOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('batchSize') && typeof options.batchSize === 'number') {      destroyOptions.batchSize = options.batchSize;    }    if (options.hasOwnProperty('context') && typeof options.context === 'object') {      destroyOptions.context = options.context;    }    return _CoreManager.default.getObjectController().destroy(list, destroyOptions);  }  /**   * Saves the given list of Parse.Object.   * If any error is encountered, stops and calls the error handler.   *   * <pre>   * Parse.Object.saveAll([object1, object2, ...])   * .then((list) => {   * // All the objects were saved.   * }, (error) => {   * // An error occurred while saving one of the objects.   * });   * </pre>   *   * @param {Array} list A list of <code>Parse.Object</code>.   * @param {object} options   * @static   * @returns {Parse.Object[]}   */  static saveAll(list /*: Array<ParseObject>*/, options /*: RequestOptions*/ = {}) {    const saveOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      saveOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      saveOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('batchSize') && typeof options.batchSize === 'number') {      saveOptions.batchSize = options.batchSize;    }    if (options.hasOwnProperty('context') && typeof options.context === 'object') {      saveOptions.context = options.context;    }    return _CoreManager.default.getObjectController().save(list, saveOptions);  }  /**   * Creates a reference to a subclass of Parse.Object with the given id. This   * does not exist on Parse.Object, only on subclasses.   *   * <p>A shortcut for: <pre>   *  var Foo = Parse.Object.extend("Foo");   *  var pointerToFoo = new Foo();   *  pointerToFoo.id = "myObjectId";   * </pre>   *   * @param {string} id The ID of the object to create a reference to.   * @static   * @returns {Parse.Object} A Parse.Object reference.   */  static createWithoutData(id /*: string*/) {    const obj = new this();    obj.id = id;    return obj;  }  /**   * Creates a new instance of a Parse Object from a JSON representation.   *   * @param {object} json The JSON map of the Object's data   * @param {boolean} override In single instance mode, all old server data   *   is overwritten if this is set to true   * @param {boolean} dirty Whether the Parse.Object should set JSON keys to dirty   * @static   * @returns {Parse.Object} A Parse.Object reference   */  static fromJSON(json /*: any*/, override /*:: ?: boolean*/, dirty /*:: ?: boolean*/) {    if (!json.className) {      throw new Error('Cannot create an object without a className');    }    const constructor = classMap[json.className];    const o = constructor ? new constructor(json.className) : new ParseObject(json.className);    const otherAttributes = {};    for (const attr in json) {      if (attr !== 'className' && attr !== '__type') {        otherAttributes[attr] = json[attr];        if (dirty) {          o.set(attr, json[attr]);        }      }    }    if (override) {      // id needs to be set before clearServerData can work      if (otherAttributes.objectId) {        o.id = otherAttributes.objectId;      }      let preserved = null;      if (typeof o._preserveFieldsOnFetch === 'function') {        preserved = o._preserveFieldsOnFetch();      }      o._clearServerData();      if (preserved) {        o._finishFetch(preserved);      }    }    o._finishFetch(otherAttributes);    if (json.objectId) {      o._setExisted(true);    }    return o;  }  /**   * Registers a subclass of Parse.Object with a specific class name.   * When objects of that class are retrieved from a query, they will be   * instantiated with this subclass.   * This is only necessary when using ES6 subclassing.   *   * @param {string} className The class name of the subclass   * @param {Function} constructor The subclass   */  static registerSubclass(className /*: string*/, constructor /*: any*/) {    if (typeof className !== 'string') {      throw new TypeError('The first argument must be a valid class name.');    }    if (typeof constructor === 'undefined') {      throw new TypeError('You must supply a subclass constructor.');    }    if (typeof constructor !== 'function') {      throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?');    }    classMap[className] = constructor;    if (!constructor.className) {      constructor.className = className;    }  }  /**   * Unegisters a subclass of Parse.Object with a specific class name.   *   * @param {string} className The class name of the subclass   */  static unregisterSubclass(className /*: string*/) {    if (typeof className !== 'string') {      throw new TypeError('The first argument must be a valid class name.');    }    delete classMap[className];  }  /**   * Creates a new subclass of Parse.Object for the given Parse class name.   *   * <p>Every extension of a Parse class will inherit from the most recent   * previous extension of that class. When a Parse.Object is automatically   * created by parsing JSON, it will use the most recent extension of that   * class.</p>   *   * <p>You should call either:<pre>   *     var MyClass = Parse.Object.extend("MyClass", {   *         <i>Instance methods</i>,   *         initialize: function(attrs, options) {   *             this.someInstanceProperty = [],   *             <i>Other instance properties</i>   *         }   *     }, {   *         <i>Class properties</i>   *     });</pre>   * or, for Backbone compatibility:<pre>   *     var MyClass = Parse.Object.extend({   *         className: "MyClass",   *         <i>Instance methods</i>,   *         initialize: function(attrs, options) {   *             this.someInstanceProperty = [],   *             <i>Other instance properties</i>   *         }   *     }, {   *         <i>Class properties</i>   *     });</pre></p>   *   * @param {string} className The name of the Parse class backing this model.   * @param {object} protoProps Instance properties to add to instances of the   *     class returned from this method.   * @param {object} classProps Class properties to add the class returned from   *     this method.   * @returns {Parse.Object} A new subclass of Parse.Object.   */  static extend(className /*: any*/, protoProps /*: any*/, classProps /*: any*/) {    if (typeof className !== 'string') {      if (className && typeof className.className === 'string') {        return ParseObject.extend(className.className, className, protoProps);      } else {        throw new Error("Parse.Object.extend's first argument should be the className.");      }    }    let adjustedClassName = className;    if (adjustedClassName === 'User' && _CoreManager.default.get('PERFORM_USER_REWRITE')) {      adjustedClassName = '_User';    }    let parentProto = ParseObject.prototype;    if (this.hasOwnProperty('__super__') && this.__super__) {      parentProto = this.prototype;    }    let ParseObjectSubclass = function (attributes, options) {      this.className = adjustedClassName;      this._objCount = objectCount++;      // Enable legacy initializers      if (typeof this.initialize === 'function') {        this.initialize.apply(this, arguments);      }      if (this._initializers) {        for (const initializer of this._initializers) {          initializer.apply(this, arguments);        }      }      if (attributes && typeof attributes === 'object') {        if (!this.set(attributes || {}, options)) {          throw new Error("Can't create an invalid Parse Object");        }      }    };    if (classMap[adjustedClassName]) {      ParseObjectSubclass = classMap[adjustedClassName];    } else {      ParseObjectSubclass.extend = function (name, protoProps, classProps) {        if (typeof name === 'string') {          return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps);        }        return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps);      };      ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData;      ParseObjectSubclass.className = adjustedClassName;      ParseObjectSubclass.__super__ = parentProto;      ParseObjectSubclass.prototype = Object.create(parentProto, {        constructor: {          value: ParseObjectSubclass,          enumerable: false,          writable: true,          configurable: true        }      });    }    if (protoProps) {      for (const prop in protoProps) {        if (prop === 'initialize') {          Object.defineProperty(ParseObjectSubclass.prototype, '_initializers', {            value: [...(ParseObjectSubclass.prototype._initializers || []), protoProps[prop]],            enumerable: false,            writable: true,            configurable: true          });          continue;        }        if (prop !== 'className') {          Object.defineProperty(ParseObjectSubclass.prototype, prop, {            value: protoProps[prop],            enumerable: false,            writable: true,            configurable: true          });        }      }    }    if (classProps) {      for (const prop in classProps) {        if (prop !== 'className') {          Object.defineProperty(ParseObjectSubclass, prop, {            value: classProps[prop],            enumerable: false,            writable: true,            configurable: true          });        }      }    }    classMap[adjustedClassName] = ParseObjectSubclass;    return ParseObjectSubclass;  }  /**   * Enable single instance objects, where any local objects with the same Id   * share the same attributes, and stay synchronized with each other.   * This is disabled by default in server environments, since it can lead to   * security issues.   *   * @static   */  static enableSingleInstance() {    singleInstance = true;    _CoreManager.default.setObjectStateController(SingleInstanceStateController);  }  /**   * Disable single instance objects, where any local objects with the same Id   * share the same attributes, and stay synchronized with each other.   * When disabled, you can have two instances of the same object in memory   * without them sharing attributes.   *   * @static   */  static disableSingleInstance() {    singleInstance = false;    _CoreManager.default.setObjectStateController(UniqueInstanceStateController);  }  /**   * Asynchronously stores the objects and every object they point to in the local datastore,   * recursively, using a default pin name: _default.   *   * If those other objects have not been fetched from Parse, they will not be stored.   * However, if they have changed data, all the changes will be retained.   *   * <pre>   * await Parse.Object.pinAll([...]);   * </pre>   *   * To retrieve object:   * <code>query.fromLocalDatastore()</code> or <code>query.fromPin()</code>   *   * @param {Array} objects A list of <code>Parse.Object</code>.   * @returns {Promise} A promise that is fulfilled when the pin completes.   * @static   */  static pinAll(objects /*: Array<ParseObject>*/) /*: Promise<void>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (!localDatastore.isEnabled) {      return Promise.reject('Parse.enableLocalDatastore() must be called first');    }    return ParseObject.pinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, objects);  }  /**   * Asynchronously stores the objects and every object they point to in the local datastore, recursively.   *   * If those other objects have not been fetched from Parse, they will not be stored.   * However, if they have changed data, all the changes will be retained.   *   * <pre>   * await Parse.Object.pinAllWithName(name, [obj1, obj2, ...]);   * </pre>   *   * To retrieve object:   * <code>query.fromLocalDatastore()</code> or <code>query.fromPinWithName(name)</code>   *   * @param {string} name Name of Pin.   * @param {Array} objects A list of <code>Parse.Object</code>.   * @returns {Promise} A promise that is fulfilled when the pin completes.   * @static   */  static pinAllWithName(name /*: string*/, objects /*: Array<ParseObject>*/) /*: Promise<void>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (!localDatastore.isEnabled) {      return Promise.reject('Parse.enableLocalDatastore() must be called first');    }    return localDatastore._handlePinAllWithName(name, objects);  }  /**   * Asynchronously removes the objects and every object they point to in the local datastore,   * recursively, using a default pin name: _default.   *   * <pre>   * await Parse.Object.unPinAll([...]);   * </pre>   *   * @param {Array} objects A list of <code>Parse.Object</code>.   * @returns {Promise} A promise that is fulfilled when the unPin completes.   * @static   */  static unPinAll(objects /*: Array<ParseObject>*/) /*: Promise<void>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (!localDatastore.isEnabled) {      return Promise.reject('Parse.enableLocalDatastore() must be called first');    }    return ParseObject.unPinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, objects);  }  /**   * Asynchronously removes the objects and every object they point to in the local datastore, recursively.   *   * <pre>   * await Parse.Object.unPinAllWithName(name, [obj1, obj2, ...]);   * </pre>   *   * @param {string} name Name of Pin.   * @param {Array} objects A list of <code>Parse.Object</code>.   * @returns {Promise} A promise that is fulfilled when the unPin completes.   * @static   */  static unPinAllWithName(name /*: string*/, objects /*: Array<ParseObject>*/) /*: Promise<void>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (!localDatastore.isEnabled) {      return Promise.reject('Parse.enableLocalDatastore() must be called first');    }    return localDatastore._handleUnPinAllWithName(name, objects);  }  /**   * Asynchronously removes all objects in the local datastore using a default pin name: _default.   *   * <pre>   * await Parse.Object.unPinAllObjects();   * </pre>   *   * @returns {Promise} A promise that is fulfilled when the unPin completes.   * @static   */  static unPinAllObjects() /*: Promise<void>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (!localDatastore.isEnabled) {      return Promise.reject('Parse.enableLocalDatastore() must be called first');    }    return localDatastore.unPinWithName(_LocalDatastoreUtils.DEFAULT_PIN);  }  /**   * Asynchronously removes all objects with the specified pin name.   * Deletes the pin name also.   *   * <pre>   * await Parse.Object.unPinAllObjectsWithName(name);   * </pre>   *   * @param {string} name Name of Pin.   * @returns {Promise} A promise that is fulfilled when the unPin completes.   * @static   */  static unPinAllObjectsWithName(name /*: string*/) /*: Promise<void>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (!localDatastore.isEnabled) {      return Promise.reject('Parse.enableLocalDatastore() must be called first');    }    return localDatastore.unPinWithName(_LocalDatastoreUtils.PIN_PREFIX + name);  }}const DefaultController = {  fetch(target /*: ParseObject | Array<ParseObject>*/, forceFetch /*: boolean*/, options /*: RequestOptions*/) /*: Promise<Array<void> | ParseObject>*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (Array.isArray(target)) {      if (target.length < 1) {        return Promise.resolve([]);      }      const objs = [];      const ids = [];      let className = null;      const results = [];      let error = null;      target.forEach(el => {        if (error) {          return;        }        if (!className) {          className = el.className;        }        if (className !== el.className) {          error = new _ParseError.default(_ParseError.default.INVALID_CLASS_NAME, 'All objects should be of the same class');        }        if (!el.id) {          error = new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'All objects must have an ID');        }        if (forceFetch || !el.isDataAvailable()) {          ids.push(el.id);          objs.push(el);        }        results.push(el);      });      if (error) {        return Promise.reject(error);      }      const query = new _ParseQuery.default(className);      query.containedIn('objectId', ids);      if (options && options.include) {        query.include(options.include);      }      query._limit = ids.length;      return query.find(options).then(async objects => {        const idMap = {};        objects.forEach(o => {          idMap[o.id] = o;        });        for (let i = 0; i < objs.length; i++) {          const obj = objs[i];          if (!obj || !obj.id || !idMap[obj.id]) {            if (forceFetch) {              return Promise.reject(new _ParseError.default(_ParseError.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.'));            }          }        }        if (!singleInstance) {          // If single instance objects are disabled, we need to replace the          for (let i = 0; i < results.length; i++) {            const obj = results[i];            if (obj && obj.id && idMap[obj.id]) {              const id = obj.id;              obj._finishFetch(idMap[id].toJSON());              results[i] = idMap[id];            }          }        }        for (const object of results) {          await localDatastore._updateObjectIfPinned(object);        }        return Promise.resolve(results);      });    } else if (target instanceof ParseObject) {      if (!target.id) {        return Promise.reject(new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'Object does not have an ID'));      }      const RESTController = _CoreManager.default.getRESTController();      const params = {};      if (options && options.include) {        params.include = options.include.join();      }      return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), params, options).then(async response => {        target._clearPendingOps();        target._clearServerData();        target._finishFetch(response);        await localDatastore._updateObjectIfPinned(target);        return target;      });    }    return Promise.resolve();  },  async destroy(target /*: ParseObject | Array<ParseObject>*/, options /*: RequestOptions*/) /*: Promise<Array<void> | ParseObject>*/{    const batchSize = options && options.batchSize ? options.batchSize : _CoreManager.default.get('REQUEST_BATCH_SIZE');    const localDatastore = _CoreManager.default.getLocalDatastore();    const RESTController = _CoreManager.default.getRESTController();    if (Array.isArray(target)) {      if (target.length < 1) {        return Promise.resolve([]);      }      const batches = [[]];      target.forEach(obj => {        if (!obj.id) {          return;        }        batches[batches.length - 1].push(obj);        if (batches[batches.length - 1].length >= batchSize) {          batches.push([]);        }      });      if (batches[batches.length - 1].length === 0) {        // If the last batch is empty, remove it        batches.pop();      }      let deleteCompleted = Promise.resolve();      const errors = [];      batches.forEach(batch => {        deleteCompleted = deleteCompleted.then(() => {          return RESTController.request('POST', 'batch', {            requests: batch.map(obj => {              return {                method: 'DELETE',                path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(),                body: {}              };            })          }, options).then(results => {            for (let i = 0; i < results.length; i++) {              if (results[i] && results[i].hasOwnProperty('error')) {                const err = new _ParseError.default(results[i].error.code, results[i].error.error);                err.object = batch[i];                errors.push(err);              }            }          });        });      });      return deleteCompleted.then(async () => {        if (errors.length) {          const aggregate = new _ParseError.default(_ParseError.default.AGGREGATE_ERROR);          aggregate.errors = errors;          return Promise.reject(aggregate);        }        for (const object of target) {          await localDatastore._destroyObjectIfPinned(object);        }        return Promise.resolve(target);      });    } else if (target instanceof ParseObject) {      return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(async () => {        await localDatastore._destroyObjectIfPinned(target);        return Promise.resolve(target);      });    }    return Promise.resolve(target);  },  save(target /*: ParseObject | Array<ParseObject | ParseFile>*/, options /*: RequestOptions*/) {    const batchSize = options && options.batchSize ? options.batchSize : _CoreManager.default.get('REQUEST_BATCH_SIZE');    const localDatastore = _CoreManager.default.getLocalDatastore();    const mapIdForPin = {};    const RESTController = _CoreManager.default.getRESTController();    const stateController = _CoreManager.default.getObjectStateController();    const allowCustomObjectId = _CoreManager.default.get('ALLOW_CUSTOM_OBJECT_ID');    options = options || {};    options.returnStatus = options.returnStatus || true;    if (Array.isArray(target)) {      if (target.length < 1) {        return Promise.resolve([]);      }      let unsaved = target.concat();      for (let i = 0; i < target.length; i++) {        if (target[i] instanceof ParseObject) {          unsaved = unsaved.concat((0, _unsavedChildren.default)(target[i], true));        }      }      unsaved = (0, _unique.default)(unsaved);      const filesSaved /*: Array<ParseFile>*/ = [];      let pending /*: Array<ParseObject>*/ = [];      unsaved.forEach(el => {        if (el instanceof _ParseFile.default) {          filesSaved.push(el.save(options));        } else if (el instanceof ParseObject) {          pending.push(el);        }      });      return Promise.all(filesSaved).then(() => {        let objectError = null;        return (0, _promiseUtils.continueWhile)(() => {          return pending.length > 0;        }, () => {          const batch = [];          const nextPending = [];          pending.forEach(el => {            if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(el, 'id') && !el.id) {              throw new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'objectId must not be empty or null');            }            if (batch.length < batchSize && (0, _canBeSerialized.default)(el)) {              batch.push(el);            } else {              nextPending.push(el);            }          });          pending = nextPending;          if (batch.length < 1) {            return Promise.reject(new _ParseError.default(_ParseError.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.'));          }          // Queue up tasks for each object in the batch.          // When every task is ready, the API request will execute          const batchReturned = new _promiseUtils.resolvingPromise();          const batchReady = [];          const batchTasks = [];          batch.forEach((obj, index) => {            const ready = new _promiseUtils.resolvingPromise();            batchReady.push(ready);            stateController.pushPendingState(obj._getStateIdentifier());            batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () {              ready.resolve();              return batchReturned.then(responses => {                if (responses[index].hasOwnProperty('success')) {                  const objectId = responses[index].success.objectId;                  const status = responses[index]._status;                  delete responses[index]._status;                  mapIdForPin[objectId] = obj._localId;                  obj._handleSaveResponse(responses[index].success, status);                } else {                  if (!objectError && responses[index].hasOwnProperty('error')) {                    const serverError = responses[index].error;                    objectError = new _ParseError.default(serverError.code, serverError.error);                    // Cancel the rest of the save                    pending = [];                  }                  obj._handleSaveError();                }              });            }));          });          (0, _promiseUtils.when)(batchReady).then(() => {            // Kick off the batch request            return RESTController.request('POST', 'batch', {              requests: batch.map(obj => {                const params = obj._getSaveParams();                params.path = getServerUrlPath() + params.path;                return params;              })            }, options);          }).then(batchReturned.resolve, error => {            batchReturned.reject(new _ParseError.default(_ParseError.default.INCORRECT_TYPE, error.message));          });          return (0, _promiseUtils.when)(batchTasks);        }).then(async () => {          if (objectError) {            return Promise.reject(objectError);          }          for (const object of target) {            // Make sure that it is a ParseObject before updating it into the localDataStore            if (object instanceof ParseObject) {              await localDatastore._updateLocalIdForObject(mapIdForPin[object.id], object);              await localDatastore._updateObjectIfPinned(object);            }          }          return Promise.resolve(target);        });      });    } else if (target instanceof ParseObject) {      if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(target, 'id') && !target.id) {        throw new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'objectId must not be empty or null');      }      // generate _localId in case if cascadeSave=false      target._getId();      const localId = target._localId;      // copying target lets Flow guarantee the pointer isn't modified elsewhere      const targetCopy = target;      const task = function () {        const params = targetCopy._getSaveParams();        return RESTController.request(params.method, params.path, params.body, options).then(response => {          const status = response._status;          delete response._status;          targetCopy._handleSaveResponse(response, status);        }, error => {          targetCopy._handleSaveError();          return Promise.reject(error);        });      };      stateController.pushPendingState(target._getStateIdentifier());      return stateController.enqueueTask(target._getStateIdentifier(), task).then(async () => {        await localDatastore._updateLocalIdForObject(localId, target);        await localDatastore._updateObjectIfPinned(target);        return target;      }, error => {        return Promise.reject(error);      });    }    return Promise.resolve();  }};_CoreManager.default.setObjectController(DefaultController);var _default = ParseObject;exports.default = _default;
 |