| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019 | "use strict";Object.defineProperty(exports, "__esModule", {  value: true});exports.default = void 0;var _CoreManager = _interopRequireDefault(require("./CoreManager"));var _encode = _interopRequireDefault(require("./encode"));var _promiseUtils = require("./promiseUtils");var _ParseError = _interopRequireDefault(require("./ParseError"));var _ParseGeoPoint = _interopRequireDefault(require("./ParseGeoPoint"));var _ParseObject = _interopRequireDefault(require("./ParseObject"));var _OfflineQuery = _interopRequireDefault(require("./OfflineQuery"));var _LocalDatastoreUtils = require("./LocalDatastoreUtils");function _interopRequireDefault(obj) {  return obj && obj.__esModule ? obj : {    default: obj  };}/* * @flow *//*:: import type LiveQuerySubscription from './LiveQuerySubscription';*//*:: import type { RequestOptions, FullOptions } from './RESTController';*//*:: type BatchOptions = FullOptions & { batchSize?: number };*//*:: export type WhereClause = {  [attr: string]: mixed,};*//*:: export type QueryJSON = {  where: WhereClause,  watch?: string,  include?: string,  excludeKeys?: string,  keys?: string,  limit?: number,  skip?: number,  order?: string,  className?: string,  count?: number,  hint?: mixed,  explain?: boolean,  readPreference?: string,  includeReadPreference?: string,  subqueryReadPreference?: string,};*//** * Converts a string into a regex that matches it. * Surrounding with \Q .. \E does this, we just need to escape any \E's in * the text separately. * * @param s * @private * @returns {string} */function quote(s /*: string*/) /*: string*/{  return '\\Q' + s.replace('\\E', '\\E\\\\E\\Q') + '\\E';}/** * Extracts the class name from queries. If not all queries have the same * class name an error will be thrown. * * @param queries * @private * @returns {string} */function _getClassNameFromQueries(queries /*: Array<ParseQuery>*/) /*: ?string*/{  let className = null;  queries.forEach(q => {    if (!className) {      className = q.className;    }    if (className !== q.className) {      throw new Error('All queries must be for the same class.');    }  });  return className;}/* * Handles pre-populating the result data of a query with select fields, * making sure that the data object contains keys for all objects that have * been requested with a select, so that our cached state updates correctly. */function handleSelectResult(data /*: any*/, select /*: Array<string>*/) {  const serverDataMask = {};  select.forEach(field => {    const hasSubObjectSelect = field.indexOf('.') !== -1;    if (!hasSubObjectSelect && !data.hasOwnProperty(field)) {      // this field was selected, but is missing from the retrieved data      data[field] = undefined;    } else if (hasSubObjectSelect) {      // this field references a sub-object,      // so we need to walk down the path components      const pathComponents = field.split('.');      let obj = data;      let serverMask = serverDataMask;      pathComponents.forEach((component, index, arr) => {        // add keys if the expected data is missing        if (obj && !obj.hasOwnProperty(component)) {          obj[component] = undefined;        }        if (obj && typeof obj === 'object') {          obj = obj[component];        }        //add this path component to the server mask so we can fill it in later if needed        if (index < arr.length - 1) {          if (!serverMask[component]) {            serverMask[component] = {};          }          serverMask = serverMask[component];        }      });    }  });  if (Object.keys(serverDataMask).length > 0) {    // When selecting from sub-objects, we don't want to blow away the missing    // information that we may have retrieved before. We've already added any    // missing selected keys to sub-objects, but we still need to add in the    // data for any previously retrieved sub-objects that were not selected.    const serverData = _CoreManager.default.getObjectStateController().getServerData({      id: data.objectId,      className: data.className    });    copyMissingDataWithMask(serverData, data, serverDataMask, false);  }}function copyMissingDataWithMask(src, dest, mask, copyThisLevel) {  //copy missing elements at this level  if (copyThisLevel) {    for (const key in src) {      if (src.hasOwnProperty(key) && !dest.hasOwnProperty(key)) {        dest[key] = src[key];      }    }  }  for (const key in mask) {    if (dest[key] !== undefined && dest[key] !== null && src !== undefined && src !== null) {      //traverse into objects as needed      copyMissingDataWithMask(src[key], dest[key], mask[key], true);    }  }}function handleOfflineSort(a, b, sorts) {  let order = sorts[0];  const operator = order.slice(0, 1);  const isDescending = operator === '-';  if (isDescending) {    order = order.substring(1);  }  if (order === '_created_at') {    order = 'createdAt';  }  if (order === '_updated_at') {    order = 'updatedAt';  }  if (!/^[A-Za-z][0-9A-Za-z_]*$/.test(order) || order === 'password') {    throw new _ParseError.default(_ParseError.default.INVALID_KEY_NAME, `Invalid Key: ${order}`);  }  const field1 = a.get(order);  const field2 = b.get(order);  if (field1 < field2) {    return isDescending ? 1 : -1;  }  if (field1 > field2) {    return isDescending ? -1 : 1;  }  if (sorts.length > 1) {    const remainingSorts = sorts.slice(1);    return handleOfflineSort(a, b, remainingSorts);  }  return 0;}/** * Creates a new parse Parse.Query for the given Parse.Object subclass. * * <p>Parse.Query defines a query that is used to fetch Parse.Objects. The * most common use case is finding all objects that match a query through the * <code>find</code> method. for example, this sample code fetches all objects * of class <code>myclass</code>. it calls a different function depending on * whether the fetch succeeded or not. * * <pre> * var query = new Parse.Query(myclass); * query.find().then((results) => { *   // results is an array of parse.object. * }).catch((error) =>  { *  // error is an instance of parse.error. * });</pre></p> * * <p>a Parse.Query can also be used to retrieve a single object whose id is * known, through the get method. for example, this sample code fetches an * object of class <code>myclass</code> and id <code>myid</code>. it calls a * different function depending on whether the fetch succeeded or not. * * <pre> * var query = new Parse.Query(myclass); * query.get(myid).then((object) => { *     // object is an instance of parse.object. * }).catch((error) =>  { *  // error is an instance of parse.error. * });</pre></p> * * <p>a Parse.Query can also be used to count the number of objects that match * the query without retrieving all of those objects. for example, this * sample code counts the number of objects of the class <code>myclass</code> * <pre> * var query = new Parse.Query(myclass); * query.count().then((number) => { *     // there are number instances of myclass. * }).catch((error) => { *     // error is an instance of Parse.Error. * });</pre></p> * * @alias Parse.Query */class ParseQuery {  /**   * @property {string} className   */  /*:: className: string;*/  /*:: _where: any;*/  /*:: _watch: Array<string>;*/  /*:: _include: Array<string>;*/  /*:: _exclude: Array<string>;*/  /*:: _select: Array<string>;*/  /*:: _limit: number;*/  /*:: _skip: number;*/  /*:: _count: boolean;*/  /*:: _order: Array<string>;*/  /*:: _readPreference: string;*/  /*:: _includeReadPreference: string;*/  /*:: _subqueryReadPreference: string;*/  /*:: _queriesLocalDatastore: boolean;*/  /*:: _localDatastorePinName: any;*/  /*:: _extraOptions: { [key: string]: mixed };*/  /*:: _hint: mixed;*/  /*:: _explain: boolean;*/  /*:: _xhrRequest: any;*/  /**   * @param {(string | Parse.Object)} objectClass An instance of a subclass of Parse.Object, or a Parse className string.   */  constructor(objectClass /*: string | ParseObject*/) {    if (typeof objectClass === 'string') {      if (objectClass === 'User' && _CoreManager.default.get('PERFORM_USER_REWRITE')) {        this.className = '_User';      } else {        this.className = objectClass;      }    } else if (objectClass instanceof _ParseObject.default) {      this.className = objectClass.className;    } else if (typeof objectClass === 'function') {      if (typeof objectClass.className === 'string') {        this.className = objectClass.className;      } else {        const obj = new objectClass();        this.className = obj.className;      }    } else {      throw new TypeError('A ParseQuery must be constructed with a ParseObject or class name.');    }    this._where = {};    this._watch = [];    this._include = [];    this._exclude = [];    this._count = false;    this._limit = -1; // negative limit is not sent in the server request    this._skip = 0;    this._readPreference = null;    this._includeReadPreference = null;    this._subqueryReadPreference = null;    this._queriesLocalDatastore = false;    this._localDatastorePinName = null;    this._extraOptions = {};    this._xhrRequest = {      task: null,      onchange: () => {}    };  }  /**   * Adds constraint that at least one of the passed in queries matches.   *   * @param {Array} queries   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  _orQuery(queries /*: Array<ParseQuery>*/) /*: ParseQuery*/{    const queryJSON = queries.map(q => {      return q.toJSON().where;    });    this._where.$or = queryJSON;    return this;  }  /**   * Adds constraint that all of the passed in queries match.   *   * @param {Array} queries   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  _andQuery(queries /*: Array<ParseQuery>*/) /*: ParseQuery*/{    const queryJSON = queries.map(q => {      return q.toJSON().where;    });    this._where.$and = queryJSON;    return this;  }  /**   * Adds constraint that none of the passed in queries match.   *   * @param {Array} queries   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  _norQuery(queries /*: Array<ParseQuery>*/) /*: ParseQuery*/{    const queryJSON = queries.map(q => {      return q.toJSON().where;    });    this._where.$nor = queryJSON;    return this;  }  /**   * Helper for condition queries   *   * @param key   * @param condition   * @param value   * @returns {Parse.Query}   */  _addCondition(key /*: string*/, condition /*: string*/, value /*: mixed*/) /*: ParseQuery*/{    if (!this._where[key] || typeof this._where[key] === 'string') {      this._where[key] = {};    }    this._where[key][condition] = (0, _encode.default)(value, false, true);    return this;  }  /**   * Converts string for regular expression at the beginning   *   * @param string   * @returns {string}   */  _regexStartWith(string /*: string*/) /*: string*/{    return '^' + quote(string);  }  async _handleOfflineQuery(params /*: any*/) {    _OfflineQuery.default.validateQuery(this);    const localDatastore = _CoreManager.default.getLocalDatastore();    const objects = await localDatastore._serializeObjectsFromPinName(this._localDatastorePinName);    let results = objects.map((json, index, arr) => {      const object = _ParseObject.default.fromJSON(json, false);      if (json._localId && !json.objectId) {        object._localId = json._localId;      }      if (!_OfflineQuery.default.matchesQuery(this.className, object, arr, this)) {        return null;      }      return object;    }).filter(object => object !== null);    if (params.keys) {      let keys = params.keys.split(',');      keys = keys.concat(['className', 'objectId', 'createdAt', 'updatedAt', 'ACL']);      results = results.map(object => {        const json = object._toFullJSON();        Object.keys(json).forEach(key => {          if (!keys.includes(key)) {            delete json[key];          }        });        return _ParseObject.default.fromJSON(json, false);      });    }    if (params.order) {      const sorts = params.order.split(',');      results.sort((a, b) => {        return handleOfflineSort(a, b, sorts);      });    }    let count; // count total before applying limit/skip    if (params.count) {      count = results.length; // total count from response    }    if (params.skip) {      if (params.skip >= results.length) {        results = [];      } else {        results = results.splice(params.skip, results.length);      }    }    let limit = results.length;    if (params.limit !== 0 && params.limit < results.length) {      limit = params.limit;    }    results = results.splice(0, limit);    if (typeof count === 'number') {      return {        results,        count      };    }    return results;  }  /**   * Returns a JSON representation of this query.   *   * @returns {object} The JSON representation of the query.   */  toJSON() /*: QueryJSON*/{    const params /*: QueryJSON*/ = {      where: this._where    };    if (this._watch.length) {      params.watch = this._watch.join(',');    }    if (this._include.length) {      params.include = this._include.join(',');    }    if (this._exclude.length) {      params.excludeKeys = this._exclude.join(',');    }    if (this._select) {      params.keys = this._select.join(',');    }    if (this._count) {      params.count = 1;    }    if (this._limit >= 0) {      params.limit = this._limit;    }    if (this._skip > 0) {      params.skip = this._skip;    }    if (this._order) {      params.order = this._order.join(',');    }    if (this._readPreference) {      params.readPreference = this._readPreference;    }    if (this._includeReadPreference) {      params.includeReadPreference = this._includeReadPreference;    }    if (this._subqueryReadPreference) {      params.subqueryReadPreference = this._subqueryReadPreference;    }    if (this._hint) {      params.hint = this._hint;    }    if (this._explain) {      params.explain = true;    }    for (const key in this._extraOptions) {      params[key] = this._extraOptions[key];    }    return params;  }  /**   * Return a query with conditions from json, can be useful to send query from server side to client   * Not static, all query conditions was set before calling this method will be deleted.   * For example on the server side we have   * var query = new Parse.Query("className");   * query.equalTo(key: value);   * query.limit(100);   * ... (others queries)   * Create JSON representation of Query Object   * var jsonFromServer = query.fromJSON();   *   * On client side getting query:   * var query = new Parse.Query("className");   * query.fromJSON(jsonFromServer);   *   * and continue to query...   * query.skip(100).find().then(...);   *   * @param {QueryJSON} json from Parse.Query.toJSON() method   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  withJSON(json /*: QueryJSON*/) /*: ParseQuery*/{    if (json.where) {      this._where = json.where;    }    if (json.watch) {      this._watch = json.watch.split(',');    }    if (json.include) {      this._include = json.include.split(',');    }    if (json.keys) {      this._select = json.keys.split(',');    }    if (json.excludeKeys) {      this._exclude = json.excludeKeys.split(',');    }    if (json.count) {      this._count = json.count === 1;    }    if (json.limit) {      this._limit = json.limit;    }    if (json.skip) {      this._skip = json.skip;    }    if (json.order) {      this._order = json.order.split(',');    }    if (json.readPreference) {      this._readPreference = json.readPreference;    }    if (json.includeReadPreference) {      this._includeReadPreference = json.includeReadPreference;    }    if (json.subqueryReadPreference) {      this._subqueryReadPreference = json.subqueryReadPreference;    }    if (json.hint) {      this._hint = json.hint;    }    if (json.explain) {      this._explain = !!json.explain;    }    for (const key in json) {      if (json.hasOwnProperty(key)) {        if (['where', 'include', 'keys', 'count', 'limit', 'skip', 'order', 'readPreference', 'includeReadPreference', 'subqueryReadPreference', 'hint', 'explain'].indexOf(key) === -1) {          this._extraOptions[key] = json[key];        }      }    }    return this;  }  /**   * Static method to restore Parse.Query by json representation   * Internally calling Parse.Query.withJSON   *   * @param {string} className   * @param {QueryJSON} json from Parse.Query.toJSON() method   * @returns {Parse.Query} new created query   */  static fromJSON(className /*: string*/, json /*: QueryJSON*/) /*: ParseQuery*/{    const query = new ParseQuery(className);    return query.withJSON(json);  }  /**   * Constructs a Parse.Object whose id is already known by fetching data from   * the server. Unlike the <code>first</code> method, it never returns undefined.   *   * @param {string} objectId The id of the object to be fetched.   * @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 `beforeFind` trigger.   *   <li>json: Return raw json without converting to Parse.Object   * </ul>   * @returns {Promise} A promise that is resolved with the result when   * the query completes.   */  get(objectId /*: string*/, options /*:: ?: FullOptions*/) /*: Promise<ParseObject>*/{    this.equalTo('objectId', objectId);    const firstOptions = {};    if (options && options.hasOwnProperty('useMasterKey')) {      firstOptions.useMasterKey = options.useMasterKey;    }    if (options && options.hasOwnProperty('sessionToken')) {      firstOptions.sessionToken = options.sessionToken;    }    if (options && options.hasOwnProperty('context') && typeof options.context === 'object') {      firstOptions.context = options.context;    }    if (options && options.hasOwnProperty('json')) {      firstOptions.json = options.json;    }    return this.first(firstOptions).then(response => {      if (response) {        return response;      }      const errorObject = new _ParseError.default(_ParseError.default.OBJECT_NOT_FOUND, 'Object not found.');      return Promise.reject(errorObject);    });  }  /**   * Retrieves a list of ParseObjects that satisfy this query.   *   * @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 `beforeFind` trigger.   *   <li>json: Return raw json without converting to Parse.Object   * </ul>   * @returns {Promise} A promise that is resolved with the results when   * the query completes.   */  find(options /*:: ?: FullOptions*/) /*: Promise<Array<ParseObject>>*/{    options = options || {};    const findOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      findOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      findOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('context') && typeof options.context === 'object') {      findOptions.context = options.context;    }    this._setRequestTask(findOptions);    const controller = _CoreManager.default.getQueryController();    const select = this._select;    if (this._queriesLocalDatastore) {      return this._handleOfflineQuery(this.toJSON());    }    return controller.find(this.className, this.toJSON(), findOptions).then(response => {      // Return generic object when explain is used      if (this._explain) {        return response.results;      }      const results = response.results.map(data => {        // In cases of relations, the server may send back a className        // on the top level of the payload        const override = response.className || this.className;        if (!data.className) {          data.className = override;        }        // Make sure the data object contains keys for all objects that        // have been requested with a select, so that our cached state        // updates correctly.        if (select) {          handleSelectResult(data, select);        }        if (options.json) {          return data;        } else {          return _ParseObject.default.fromJSON(data, !select);        }      });      const count = response.count;      if (typeof count === 'number') {        return {          results,          count        };      } else {        return results;      }    });  }  /**   * Retrieves a complete list of ParseObjects that satisfy this query.   * Using `eachBatch` under the hood to fetch all the valid objects.   *   * @param {object} options Valid options are:<ul>   *   <li>batchSize: How many objects to yield in each batch (default: 100)   *   <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 resolved with the results when   * the query completes.   */  async findAll(options /*:: ?: BatchOptions*/) /*: Promise<Array<ParseObject>>*/{    let result /*: ParseObject[]*/ = [];    await this.eachBatch((objects /*: ParseObject[]*/) => {      result = [...result, ...objects];    }, options);    return result;  }  /**   * Counts the number of objects that match this query.   *   * @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 resolved with the count when   * the query completes.   */  count(options /*:: ?: FullOptions*/) /*: Promise<number>*/{    options = options || {};    const findOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      findOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      findOptions.sessionToken = options.sessionToken;    }    this._setRequestTask(findOptions);    const controller = _CoreManager.default.getQueryController();    const params = this.toJSON();    params.limit = 0;    params.count = 1;    return controller.find(this.className, params, findOptions).then(result => {      return result.count;    });  }  /**   * Executes a distinct query and returns unique values   *   * @param {string} key A field to find distinct values   * @param {object} options   * Valid options are:<ul>   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   * </ul>   * @returns {Promise} A promise that is resolved with the query completes.   */  distinct(key /*: string*/, options /*:: ?: FullOptions*/) /*: Promise<Array<mixed>>*/{    options = options || {};    const distinctOptions = {};    distinctOptions.useMasterKey = true;    if (options.hasOwnProperty('sessionToken')) {      distinctOptions.sessionToken = options.sessionToken;    }    this._setRequestTask(distinctOptions);    const controller = _CoreManager.default.getQueryController();    const params = {      distinct: key,      where: this._where,      hint: this._hint    };    return controller.aggregate(this.className, params, distinctOptions).then(results => {      return results.results;    });  }  /**   * Executes an aggregate query and returns aggregate results   *   * @param {(Array|object)} pipeline Array or Object of stages to process query   * @param {object} options Valid options are:<ul>   *   <li>sessionToken: A valid session token, used for making a request on   *       behalf of a specific user.   * </ul>   * @returns {Promise} A promise that is resolved with the query completes.   */  aggregate(pipeline /*: mixed*/, options /*:: ?: FullOptions*/) /*: Promise<Array<mixed>>*/{    options = options || {};    const aggregateOptions = {};    aggregateOptions.useMasterKey = true;    if (options.hasOwnProperty('sessionToken')) {      aggregateOptions.sessionToken = options.sessionToken;    }    this._setRequestTask(aggregateOptions);    const controller = _CoreManager.default.getQueryController();    if (!Array.isArray(pipeline) && typeof pipeline !== 'object') {      throw new Error('Invalid pipeline must be Array or Object');    }    if (Object.keys(this._where || {}).length) {      if (!Array.isArray(pipeline)) {        pipeline = [pipeline];      }      pipeline.unshift({        $match: this._where      });    }    const params = {      pipeline,      hint: this._hint,      explain: this._explain,      readPreference: this._readPreference    };    return controller.aggregate(this.className, params, aggregateOptions).then(results => {      return results.results;    });  }  /**   * Retrieves at most one Parse.Object that satisfies this query.   *   * Returns the object if there is one, otherwise undefined.   *   * @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 `beforeFind` trigger.   *   <li>json: Return raw json without converting to Parse.Object   * </ul>   * @returns {Promise} A promise that is resolved with the object when   * the query completes.   */  first(options /*:: ?: FullOptions*/) /*: Promise<ParseObject | void>*/{    options = options || {};    const findOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      findOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      findOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('context') && typeof options.context === 'object') {      findOptions.context = options.context;    }    this._setRequestTask(findOptions);    const controller = _CoreManager.default.getQueryController();    const params = this.toJSON();    params.limit = 1;    const select = this._select;    if (this._queriesLocalDatastore) {      return this._handleOfflineQuery(params).then(objects => {        if (!objects[0]) {          return undefined;        }        return objects[0];      });    }    return controller.find(this.className, params, findOptions).then(response => {      const objects = response.results;      if (!objects[0]) {        return undefined;      }      if (!objects[0].className) {        objects[0].className = this.className;      }      // Make sure the data object contains keys for all objects that      // have been requested with a select, so that our cached state      // updates correctly.      if (select) {        handleSelectResult(objects[0], select);      }      if (options.json) {        return objects[0];      } else {        return _ParseObject.default.fromJSON(objects[0], !select);      }    });  }  /**   * Iterates over objects matching a query, calling a callback for each batch.   * If the callback returns a promise, the iteration will not continue until   * that promise has been fulfilled. If the callback returns a rejected   * promise, then iteration will stop with that error. The items are processed   * in an unspecified order. The query may not have any sort order, and may   * not use limit or skip.   *   * @param {Function} callback Callback that will be called with each result   *     of the query.   * @param {object} options Valid options are:<ul>   *   <li>batchSize: How many objects to yield in each batch (default: 100)   *   <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 `beforeFind` trigger.   * </ul>   * @returns {Promise} A promise that will be fulfilled once the   *     iteration has completed.   */  eachBatch(callback /*: (objs: Array<ParseObject>) => Promise<*>*/, options /*:: ?: BatchOptions*/) /*: Promise<void>*/{    options = options || {};    if (this._order || this._skip || this._limit >= 0) {      return Promise.reject('Cannot iterate on a query with sort, skip, or limit.');    }    const query = new ParseQuery(this.className);    query._limit = options.batchSize || 100;    query._include = this._include.map(i => {      return i;    });    if (this._select) {      query._select = this._select.map(s => {        return s;      });    }    query._hint = this._hint;    query._where = {};    for (const attr in this._where) {      const val = this._where[attr];      if (Array.isArray(val)) {        query._where[attr] = val.map(v => {          return v;        });      } else if (val && typeof val === 'object') {        const conditionMap = {};        query._where[attr] = conditionMap;        for (const cond in val) {          conditionMap[cond] = val[cond];        }      } else {        query._where[attr] = val;      }    }    query.ascending('objectId');    const findOptions = {};    if (options.hasOwnProperty('useMasterKey')) {      findOptions.useMasterKey = options.useMasterKey;    }    if (options.hasOwnProperty('sessionToken')) {      findOptions.sessionToken = options.sessionToken;    }    if (options.hasOwnProperty('context') && typeof options.context === 'object') {      findOptions.context = options.context;    }    if (options.hasOwnProperty('json')) {      findOptions.json = options.json;    }    let finished = false;    let previousResults = [];    return (0, _promiseUtils.continueWhile)(() => {      return !finished;    }, async () => {      const [results] = await Promise.all([query.find(findOptions), Promise.resolve(previousResults.length > 0 && callback(previousResults))]);      if (results.length >= query._limit) {        query.greaterThan('objectId', results[results.length - 1].id);        previousResults = results;      } else if (results.length > 0) {        await Promise.resolve(callback(results));        finished = true;      } else {        finished = true;      }    });  }  /**   * Iterates over each result of a query, calling a callback for each one. If   * the callback returns a promise, the iteration will not continue until   * that promise has been fulfilled. If the callback returns a rejected   * promise, then iteration will stop with that error. The items are   * processed in an unspecified order. The query may not have any sort order,   * and may not use limit or skip.   *   * @param {Function} callback Callback that will be called with each result   *     of the query.   * @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>json: Return raw json without converting to Parse.Object   * </ul>   * @returns {Promise} A promise that will be fulfilled once the   *     iteration has completed.   */  each(callback /*: (obj: ParseObject) => any*/, options /*:: ?: BatchOptions*/) /*: Promise<void>*/{    return this.eachBatch(results => {      let callbacksDone = Promise.resolve();      results.forEach(result => {        callbacksDone = callbacksDone.then(() => {          return callback(result);        });      });      return callbacksDone;    }, options);  }  /**   * Adds a hint to force index selection. (https://docs.mongodb.com/manual/reference/operator/meta/hint/)   *   * @param {(string|object)} value String or Object of index that should be used when executing query   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  hint(value /*: mixed*/) /*: ParseQuery*/{    if (typeof value === 'undefined') {      delete this._hint;    }    this._hint = value;    return this;  }  /**   * Investigates the query execution plan. Useful for optimizing queries. (https://docs.mongodb.com/manual/reference/operator/meta/explain/)   *   * @param {boolean} explain Used to toggle the information on the query plan.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  explain(explain /*: boolean*/ = true) /*: ParseQuery*/{    if (typeof explain !== 'boolean') {      throw new Error('You can only set explain to a boolean value');    }    this._explain = explain;    return this;  }  /**   * Iterates over each result of a query, calling a callback for each one. If   * the callback returns a promise, the iteration will not continue until   * that promise has been fulfilled. If the callback returns a rejected   * promise, then iteration will stop with that error. The items are   * processed in an unspecified order. The query may not have any sort order,   * and may not use limit or skip.   *   * @param {Function} callback Callback <ul>   *   <li>currentObject: The current Parse.Object being processed in the array.</li>   *   <li>index: The index of the current Parse.Object being processed in the array.</li>   *   <li>query: The query map was called upon.</li>   * </ul>   * @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 will be fulfilled once the   *     iteration has completed.   */  async map(callback /*: (currentObject: ParseObject, index: number, query: ParseQuery) => any*/, options /*:: ?: BatchOptions*/) /*: Promise<Array<any>>*/{    const array = [];    let index = 0;    await this.each(object => {      return Promise.resolve(callback(object, index, this)).then(result => {        array.push(result);        index += 1;      });    }, options);    return array;  }  /**   * Iterates over each result of a query, calling a callback for each one. If   * the callback returns a promise, the iteration will not continue until   * that promise has been fulfilled. If the callback returns a rejected   * promise, then iteration will stop with that error. The items are   * processed in an unspecified order. The query may not have any sort order,   * and may not use limit or skip.   *   * @param {Function} callback Callback <ul>   *   <li>accumulator: The accumulator accumulates the callback's return values. It is the accumulated value previously returned in the last invocation of the callback.</li>   *   <li>currentObject: The current Parse.Object being processed in the array.</li>   *   <li>index: The index of the current Parse.Object being processed in the array.</li>   * </ul>   * @param {*} initialValue A value to use as the first argument to the first call of the callback. If no initialValue is supplied, the first object in the query will be used and skipped.   * @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 will be fulfilled once the   *     iteration has completed.   */  async reduce(callback /*: (accumulator: any, currentObject: ParseObject, index: number) => any*/, initialValue /*: any*/, options /*:: ?: BatchOptions*/) /*: Promise<Array<any>>*/{    let accumulator = initialValue;    let index = 0;    await this.each(object => {      // If no initial value was given, we take the first object from the query      // as the initial value and don't call the callback with it.      if (index === 0 && initialValue === undefined) {        accumulator = object;        index += 1;        return;      }      return Promise.resolve(callback(accumulator, object, index)).then(result => {        accumulator = result;        index += 1;      });    }, options);    if (index === 0 && initialValue === undefined) {      // Match Array.reduce behavior: "Calling reduce() on an empty array      // without an initialValue will throw a TypeError".      throw new TypeError('Reducing empty query result set with no initial value');    }    return accumulator;  }  /**   * Iterates over each result of a query, calling a callback for each one. If   * the callback returns a promise, the iteration will not continue until   * that promise has been fulfilled. If the callback returns a rejected   * promise, then iteration will stop with that error. The items are   * processed in an unspecified order. The query may not have any sort order,   * and may not use limit or skip.   *   * @param {Function} callback Callback <ul>   *   <li>currentObject: The current Parse.Object being processed in the array.</li>   *   <li>index: The index of the current Parse.Object being processed in the array.</li>   *   <li>query: The query filter was called upon.</li>   * </ul>   * @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 will be fulfilled once the   *     iteration has completed.   */  async filter(callback /*: (currentObject: ParseObject, index: number, query: ParseQuery) => boolean*/, options /*:: ?: BatchOptions*/) /*: Promise<Array<ParseObject>>*/{    const array = [];    let index = 0;    await this.each(object => {      return Promise.resolve(callback(object, index, this)).then(flag => {        if (flag) {          array.push(object);        }        index += 1;      });    }, options);    return array;  }  /* Query Conditions */  /**   * Adds a constraint to the query that requires a particular key's value to   * be equal to the provided value.   *   * @param {string} key The key to check.   * @param value The value that the Parse.Object must contain.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  equalTo(key /*: string | { [key: string]: any }*/, value /*: ?mixed*/) /*: ParseQuery*/{    if (key && typeof key === 'object') {      Object.entries(key).forEach(([k, val]) => this.equalTo(k, val));      return this;    }    if (typeof value === 'undefined') {      return this.doesNotExist(key);    }    this._where[key] = (0, _encode.default)(value, false, true);    return this;  }  /**   * Adds a constraint to the query that requires a particular key's value to   * be not equal to the provided value.   *   * @param {string} key The key to check.   * @param value The value that must not be equalled.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  notEqualTo(key /*: string | { [key: string]: any }*/, value /*: ?mixed*/) /*: ParseQuery*/{    if (key && typeof key === 'object') {      Object.entries(key).forEach(([k, val]) => this.notEqualTo(k, val));      return this;    }    return this._addCondition(key, '$ne', value);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * be less than the provided value.   *   * @param {string} key The key to check.   * @param value The value that provides an upper bound.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  lessThan(key /*: string*/, value /*: mixed*/) /*: ParseQuery*/{    return this._addCondition(key, '$lt', value);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * be greater than the provided value.   *   * @param {string} key The key to check.   * @param value The value that provides an lower bound.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  greaterThan(key /*: string*/, value /*: mixed*/) /*: ParseQuery*/{    return this._addCondition(key, '$gt', value);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * be less than or equal to the provided value.   *   * @param {string} key The key to check.   * @param value The value that provides an upper bound.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  lessThanOrEqualTo(key /*: string*/, value /*: mixed*/) /*: ParseQuery*/{    return this._addCondition(key, '$lte', value);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * be greater than or equal to the provided value.   *   * @param {string} key The key to check.   * @param {*} value The value that provides an lower bound.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  greaterThanOrEqualTo(key /*: string*/, value /*: mixed*/) /*: ParseQuery*/{    return this._addCondition(key, '$gte', value);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * be contained in the provided list of values.   *   * @param {string} key The key to check.   * @param {Array<*>} value The values that will match.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  containedIn(key /*: string*/, value /*: Array<mixed>*/) /*: ParseQuery*/{    return this._addCondition(key, '$in', value);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * not be contained in the provided list of values.   *   * @param {string} key The key to check.   * @param {Array<*>} value The values that will not match.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  notContainedIn(key /*: string*/, value /*: Array<mixed>*/) /*: ParseQuery*/{    return this._addCondition(key, '$nin', value);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * be contained by the provided list of values. Get objects where all array elements match.   *   * @param {string} key The key to check.   * @param {Array} values The values that will match.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  containedBy(key /*: string*/, values /*: Array<mixed>*/) /*: ParseQuery*/{    return this._addCondition(key, '$containedBy', values);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * contain each one of the provided list of values.   *   * @param {string} key The key to check.  This key's value must be an array.   * @param {Array} values The values that will match.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  containsAll(key /*: string*/, values /*: Array<mixed>*/) /*: ParseQuery*/{    return this._addCondition(key, '$all', values);  }  /**   * Adds a constraint to the query that requires a particular key's value to   * contain each one of the provided list of values starting with given strings.   *   * @param {string} key The key to check.  This key's value must be an array.   * @param {Array<string>} values The string values that will match as starting string.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  containsAllStartingWith(key /*: string*/, values /*: Array<string>*/) /*: ParseQuery*/{    const _this = this;    if (!Array.isArray(values)) {      values = [values];    }    const regexObject = values.map(value => {      return {        $regex: _this._regexStartWith(value)      };    });    return this.containsAll(key, regexObject);  }  /**   * Adds a constraint for finding objects that contain the given key.   *   * @param {string} key The key that should exist.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  exists(key /*: string*/) /*: ParseQuery*/{    return this._addCondition(key, '$exists', true);  }  /**   * Adds a constraint for finding objects that do not contain a given key.   *   * @param {string} key The key that should not exist   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  doesNotExist(key /*: string*/) /*: ParseQuery*/{    return this._addCondition(key, '$exists', false);  }  /**   * Adds a regular expression constraint for finding string values that match   * the provided regular expression.   * This may be slow for large datasets.   *   * @param {string} key The key that the string to match is stored in.   * @param {RegExp} regex The regular expression pattern to match.   * @param {string} modifiers The regular expression mode.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  matches(key /*: string*/, regex /*: RegExp*/, modifiers /*: string*/) /*: ParseQuery*/{    this._addCondition(key, '$regex', regex);    if (!modifiers) {      modifiers = '';    }    if (regex.ignoreCase) {      modifiers += 'i';    }    if (regex.multiline) {      modifiers += 'm';    }    if (modifiers.length) {      this._addCondition(key, '$options', modifiers);    }    return this;  }  /**   * Adds a constraint that requires that a key's value matches a Parse.Query   * constraint.   *   * @param {string} key The key that the contains the object to match the   *                     query.   * @param {Parse.Query} query The query that should match.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  matchesQuery(key /*: string*/, query /*: ParseQuery*/) /*: ParseQuery*/{    const queryJSON = query.toJSON();    queryJSON.className = query.className;    return this._addCondition(key, '$inQuery', queryJSON);  }  /**   * Adds a constraint that requires that a key's value not matches a   * Parse.Query constraint.   *   * @param {string} key The key that the contains the object to match the   *                     query.   * @param {Parse.Query} query The query that should not match.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  doesNotMatchQuery(key /*: string*/, query /*: ParseQuery*/) /*: ParseQuery*/{    const queryJSON = query.toJSON();    queryJSON.className = query.className;    return this._addCondition(key, '$notInQuery', queryJSON);  }  /**   * Adds a constraint that requires that a key's value matches a value in   * an object returned by a different Parse.Query.   *   * @param {string} key The key that contains the value that is being   *                     matched.   * @param {string} queryKey The key in the objects returned by the query to   *                          match against.   * @param {Parse.Query} query The query to run.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  matchesKeyInQuery(key /*: string*/, queryKey /*: string*/, query /*: ParseQuery*/) /*: ParseQuery*/{    const queryJSON = query.toJSON();    queryJSON.className = query.className;    return this._addCondition(key, '$select', {      key: queryKey,      query: queryJSON    });  }  /**   * Adds a constraint that requires that a key's value not match a value in   * an object returned by a different Parse.Query.   *   * @param {string} key The key that contains the value that is being   *                     excluded.   * @param {string} queryKey The key in the objects returned by the query to   *                          match against.   * @param {Parse.Query} query The query to run.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  doesNotMatchKeyInQuery(key /*: string*/, queryKey /*: string*/, query /*: ParseQuery*/) /*: ParseQuery*/{    const queryJSON = query.toJSON();    queryJSON.className = query.className;    return this._addCondition(key, '$dontSelect', {      key: queryKey,      query: queryJSON    });  }  /**   * Adds a constraint for finding string values that contain a provided   * string.  This may be slow for large datasets.   *   * @param {string} key The key that the string to match is stored in.   * @param {string} substring The substring that the value must contain.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  contains(key /*: string*/, substring /*: string*/) /*: ParseQuery*/{    if (typeof substring !== 'string') {      throw new Error('The value being searched for must be a string.');    }    return this._addCondition(key, '$regex', quote(substring));  }  /**   * Adds a constraint for finding string values that contain a provided   * string. This may be slow for large datasets. Requires Parse-Server > 2.5.0   *   * In order to sort you must use select and ascending ($score is required)   *  <pre>   *   query.fullText('field', 'term');   *   query.ascending('$score');   *   query.select('$score');   *  </pre>   *   * To retrieve the weight / rank   *  <pre>   *   object->get('score');   *  </pre>   *   * You can define optionals by providing an object as a third parameter   *  <pre>   *   query.fullText('field', 'term', { language: 'es', diacriticSensitive: true });   *  </pre>   *   * @param {string} key The key that the string to match is stored in.   * @param {string} value The string to search   * @param {object} options (Optional)   * @param {string} options.language The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer.   * @param {boolean} options.caseSensitive A boolean flag to enable or disable case sensitive search.   * @param {boolean} options.diacriticSensitive A boolean flag to enable or disable diacritic sensitive search.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  fullText(key /*: string*/, value /*: string*/, options /*: ?Object*/) /*: ParseQuery*/{    options = options || {};    if (!key) {      throw new Error('A key is required.');    }    if (!value) {      throw new Error('A search term is required');    }    if (typeof value !== 'string') {      throw new Error('The value being searched for must be a string.');    }    const fullOptions = {};    fullOptions.$term = value;    for (const option in options) {      switch (option) {        case 'language':          fullOptions.$language = options[option];          break;        case 'caseSensitive':          fullOptions.$caseSensitive = options[option];          break;        case 'diacriticSensitive':          fullOptions.$diacriticSensitive = options[option];          break;        default:          throw new Error(`Unknown option: ${option}`);      }    }    return this._addCondition(key, '$text', {      $search: fullOptions    });  }  /**   * Method to sort the full text search by text score   *   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  sortByTextScore() {    this.ascending('$score');    this.select(['$score']);    return this;  }  /**   * Adds a constraint for finding string values that start with a provided   * string.  This query will use the backend index, so it will be fast even   * for large datasets.   *   * @param {string} key The key that the string to match is stored in.   * @param {string} prefix The substring that the value must start with.   * @param {string} modifiers The regular expression mode.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  startsWith(key /*: string*/, prefix /*: string*/, modifiers /*: string*/) /*: ParseQuery*/{    if (typeof prefix !== 'string') {      throw new Error('The value being searched for must be a string.');    }    return this.matches(key, this._regexStartWith(prefix), modifiers);  }  /**   * Adds a constraint for finding string values that end with a provided   * string.  This will be slow for large datasets.   *   * @param {string} key The key that the string to match is stored in.   * @param {string} suffix The substring that the value must end with.   * @param {string} modifiers The regular expression mode.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  endsWith(key /*: string*/, suffix /*: string*/, modifiers /*: string*/) /*: ParseQuery*/{    if (typeof suffix !== 'string') {      throw new Error('The value being searched for must be a string.');    }    return this.matches(key, quote(suffix) + '$', modifiers);  }  /**   * Adds a proximity based constraint for finding objects with key point   * values near the point given.   *   * @param {string} key The key that the Parse.GeoPoint is stored in.   * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  near(key /*: string*/, point /*: ParseGeoPoint*/) /*: ParseQuery*/{    if (!(point instanceof _ParseGeoPoint.default)) {      // Try to cast it as a GeoPoint      point = new _ParseGeoPoint.default(point);    }    return this._addCondition(key, '$nearSphere', point);  }  /**   * Adds a proximity based constraint for finding objects with key point   * values near the point given and within the maximum distance given.   *   * @param {string} key The key that the Parse.GeoPoint is stored in.   * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used.   * @param {number} maxDistance Maximum distance (in radians) of results to return.   * @param {boolean} sorted A Bool value that is true if results should be   * sorted by distance ascending, false is no sorting is required,   * defaults to true.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  withinRadians(key /*: string*/, point /*: ParseGeoPoint*/, maxDistance /*: number*/, sorted /*: boolean*/) /*: ParseQuery*/{    if (sorted || sorted === undefined) {      this.near(key, point);      return this._addCondition(key, '$maxDistance', maxDistance);    } else {      return this._addCondition(key, '$geoWithin', {        $centerSphere: [[point.longitude, point.latitude], maxDistance]      });    }  }  /**   * Adds a proximity based constraint for finding objects with key point   * values near the point given and within the maximum distance given.   * Radius of earth used is 3958.8 miles.   *   * @param {string} key The key that the Parse.GeoPoint is stored in.   * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used.   * @param {number} maxDistance Maximum distance (in miles) of results to return.   * @param {boolean} sorted A Bool value that is true if results should be   * sorted by distance ascending, false is no sorting is required,   * defaults to true.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  withinMiles(key /*: string*/, point /*: ParseGeoPoint*/, maxDistance /*: number*/, sorted /*: boolean*/) /*: ParseQuery*/{    return this.withinRadians(key, point, maxDistance / 3958.8, sorted);  }  /**   * Adds a proximity based constraint for finding objects with key point   * values near the point given and within the maximum distance given.   * Radius of earth used is 6371.0 kilometers.   *   * @param {string} key The key that the Parse.GeoPoint is stored in.   * @param {Parse.GeoPoint} point The reference Parse.GeoPoint that is used.   * @param {number} maxDistance Maximum distance (in kilometers) of results to return.   * @param {boolean} sorted A Bool value that is true if results should be   * sorted by distance ascending, false is no sorting is required,   * defaults to true.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  withinKilometers(key /*: string*/, point /*: ParseGeoPoint*/, maxDistance /*: number*/, sorted /*: boolean*/) /*: ParseQuery*/{    return this.withinRadians(key, point, maxDistance / 6371.0, sorted);  }  /**   * Adds a constraint to the query that requires a particular key's   * coordinates be contained within a given rectangular geographic bounding   * box.   *   * @param {string} key The key to be constrained.   * @param {Parse.GeoPoint} southwest   *     The lower-left inclusive corner of the box.   * @param {Parse.GeoPoint} northeast   *     The upper-right inclusive corner of the box.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  withinGeoBox(key /*: string*/, southwest /*: ParseGeoPoint*/, northeast /*: ParseGeoPoint*/) /*: ParseQuery*/{    if (!(southwest instanceof _ParseGeoPoint.default)) {      southwest = new _ParseGeoPoint.default(southwest);    }    if (!(northeast instanceof _ParseGeoPoint.default)) {      northeast = new _ParseGeoPoint.default(northeast);    }    this._addCondition(key, '$within', {      $box: [southwest, northeast]    });    return this;  }  /**   * Adds a constraint to the query that requires a particular key's   * coordinates be contained within and on the bounds of a given polygon.   * Supports closed and open (last point is connected to first) paths   *   * Polygon must have at least 3 points   *   * @param {string} key The key to be constrained.   * @param {Array} points Array of Coordinates / GeoPoints   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  withinPolygon(key /*: string*/, points /*: Array<Array<number>>*/) /*: ParseQuery*/{    return this._addCondition(key, '$geoWithin', {      $polygon: points    });  }  /**   * Add a constraint to the query that requires a particular key's   * coordinates that contains a ParseGeoPoint   *   * @param {string} key The key to be constrained.   * @param {Parse.GeoPoint} point   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  polygonContains(key /*: string*/, point /*: ParseGeoPoint*/) /*: ParseQuery*/{    return this._addCondition(key, '$geoIntersects', {      $point: point    });  }  /* Query Orderings */  /**   * Sorts the results in ascending order by the given key.   *   * @param {(string|string[])} keys The key to order by, which is a   * string of comma separated values, or an Array of keys, or multiple keys.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  ascending(...keys /*: Array<string>*/) /*: ParseQuery*/{    this._order = [];    return this.addAscending.apply(this, keys);  }  /**   * Sorts the results in ascending order by the given key,   * but can also add secondary sort descriptors without overwriting _order.   *   * @param {(string|string[])} keys The key to order by, which is a   * string of comma separated values, or an Array of keys, or multiple keys.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  addAscending(...keys /*: Array<string>*/) /*: ParseQuery*/{    if (!this._order) {      this._order = [];    }    keys.forEach(key => {      if (Array.isArray(key)) {        key = key.join();      }      this._order = this._order.concat(key.replace(/\s/g, '').split(','));    });    return this;  }  /**   * Sorts the results in descending order by the given key.   *   * @param {(string|string[])} keys The key to order by, which is a   * string of comma separated values, or an Array of keys, or multiple keys.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  descending(...keys /*: Array<string>*/) /*: ParseQuery*/{    this._order = [];    return this.addDescending.apply(this, keys);  }  /**   * Sorts the results in descending order by the given key,   * but can also add secondary sort descriptors without overwriting _order.   *   * @param {(string|string[])} keys The key to order by, which is a   * string of comma separated values, or an Array of keys, or multiple keys.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  addDescending(...keys /*: Array<string>*/) /*: ParseQuery*/{    if (!this._order) {      this._order = [];    }    keys.forEach(key => {      if (Array.isArray(key)) {        key = key.join();      }      this._order = this._order.concat(key.replace(/\s/g, '').split(',').map(k => {        return '-' + k;      }));    });    return this;  }  /* Query Options */  /**   * Sets the number of results to skip before returning any results.   * This is useful for pagination.   * Default is to skip zero results.   *   * @param {number} n the number of results to skip.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  skip(n /*: number*/) /*: ParseQuery*/{    if (typeof n !== 'number' || n < 0) {      throw new Error('You can only skip by a positive number');    }    this._skip = n;    return this;  }  /**   * Sets the limit of the number of results to return. The default limit is 100.   *   * @param {number} n the number of results to limit to.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  limit(n /*: number*/) /*: ParseQuery*/{    if (typeof n !== 'number') {      throw new Error('You can only set the limit to a numeric value');    }    this._limit = n;    return this;  }  /**   * Sets the flag to include with response the total number of objects satisfying this query,   * despite limits/skip. Might be useful for pagination.   * Note that result of this query will be wrapped as an object with   * `results`: holding {ParseObject} array and `count`: integer holding total number   *   * @param {boolean} includeCount false - disable, true - enable.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  withCount(includeCount /*: boolean*/ = true) /*: ParseQuery*/{    if (typeof includeCount !== 'boolean') {      throw new Error('You can only set withCount to a boolean value');    }    this._count = includeCount;    return this;  }  /**   * Includes nested Parse.Objects for the provided key.  You can use dot   * notation to specify which fields in the included object are also fetched.   *   * You can include all nested Parse.Objects by passing in '*'.   * Requires Parse Server 3.0.0+   * <pre>query.include('*');</pre>   *   * @param {...string|Array<string>} keys The name(s) of the key(s) to include.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  include(...keys /*: Array<string | Array<string>>*/) /*: ParseQuery*/{    keys.forEach(key => {      if (Array.isArray(key)) {        this._include = this._include.concat(key);      } else {        this._include.push(key);      }    });    return this;  }  /**   * Includes all nested Parse.Objects one level deep.   *   * Requires Parse Server 3.0.0+   *   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  includeAll() /*: ParseQuery*/{    return this.include('*');  }  /**   * Restricts the fields of the returned Parse.Objects to include only the   * provided keys.  If this is called multiple times, then all of the keys   * specified in each of the calls will be included.   *   * @param {...string|Array<string>} keys The name(s) of the key(s) to include.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  select(...keys /*: Array<string | Array<string>>*/) /*: ParseQuery*/{    if (!this._select) {      this._select = [];    }    keys.forEach(key => {      if (Array.isArray(key)) {        this._select = this._select.concat(key);      } else {        this._select.push(key);      }    });    return this;  }  /**   * Restricts the fields of the returned Parse.Objects to all keys except the   * provided keys. Exclude takes precedence over select and include.   *   * Requires Parse Server 3.6.0+   *   * @param {...string|Array<string>} keys The name(s) of the key(s) to exclude.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  exclude(...keys /*: Array<string | Array<string>>*/) /*: ParseQuery*/{    keys.forEach(key => {      if (Array.isArray(key)) {        this._exclude = this._exclude.concat(key);      } else {        this._exclude.push(key);      }    });    return this;  }  /**   * Restricts live query to trigger only for watched fields.   *   * Requires Parse Server 6.0.0+   *   * @param {...string|Array<string>} keys The name(s) of the key(s) to watch.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  watch(...keys /*: Array<string | Array<string>>*/) /*: ParseQuery*/{    keys.forEach(key => {      if (Array.isArray(key)) {        this._watch = this._watch.concat(key);      } else {        this._watch.push(key);      }    });    return this;  }  /**   * Changes the read preference that the backend will use when performing the query to the database.   *   * @param {string} readPreference The read preference for the main query.   * @param {string} includeReadPreference The read preference for the queries to include pointers.   * @param {string} subqueryReadPreference The read preference for the sub queries.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  readPreference(readPreference /*: string*/, includeReadPreference /*:: ?: string*/, subqueryReadPreference /*:: ?: string*/) /*: ParseQuery*/{    this._readPreference = readPreference;    this._includeReadPreference = includeReadPreference;    this._subqueryReadPreference = subqueryReadPreference;    return this;  }  /**   * Subscribe this query to get liveQuery updates   *   * @param {string} sessionToken (optional) Defaults to the currentUser   * @returns {Promise<LiveQuerySubscription>} Returns the liveQuerySubscription, it's an event emitter   * which can be used to get liveQuery updates.   */  async subscribe(sessionToken /*:: ?: string*/) /*: Promise<LiveQuerySubscription>*/{    const currentUser = await _CoreManager.default.getUserController().currentUserAsync();    if (!sessionToken) {      sessionToken = currentUser ? currentUser.getSessionToken() : undefined;    }    const liveQueryClient = await _CoreManager.default.getLiveQueryController().getDefaultLiveQueryClient();    if (liveQueryClient.shouldOpen()) {      liveQueryClient.open();    }    const subscription = liveQueryClient.subscribe(this, sessionToken);    return subscription.subscribePromise.then(() => {      return subscription;    });  }  /**   * Constructs a Parse.Query that is the OR of the passed in queries.  For   * example:   * <pre>var compoundQuery = Parse.Query.or(query1, query2, query3);</pre>   *   * will create a compoundQuery that is an or of the query1, query2, and   * query3.   *   * @param {...Parse.Query} queries The list of queries to OR.   * @static   * @returns {Parse.Query} The query that is the OR of the passed in queries.   */  static or(...queries /*: Array<ParseQuery>*/) /*: ParseQuery*/{    const className = _getClassNameFromQueries(queries);    const query = new ParseQuery(className);    query._orQuery(queries);    return query;  }  /**   * Constructs a Parse.Query that is the AND of the passed in queries.  For   * example:   * <pre>var compoundQuery = Parse.Query.and(query1, query2, query3);</pre>   *   * will create a compoundQuery that is an and of the query1, query2, and   * query3.   *   * @param {...Parse.Query} queries The list of queries to AND.   * @static   * @returns {Parse.Query} The query that is the AND of the passed in queries.   */  static and(...queries /*: Array<ParseQuery>*/) /*: ParseQuery*/{    const className = _getClassNameFromQueries(queries);    const query = new ParseQuery(className);    query._andQuery(queries);    return query;  }  /**   * Constructs a Parse.Query that is the NOR of the passed in queries.  For   * example:   * <pre>const compoundQuery = Parse.Query.nor(query1, query2, query3);</pre>   *   * will create a compoundQuery that is a nor of the query1, query2, and   * query3.   *   * @param {...Parse.Query} queries The list of queries to NOR.   * @static   * @returns {Parse.Query} The query that is the NOR of the passed in queries.   */  static nor(...queries /*: Array<ParseQuery>*/) /*: ParseQuery*/{    const className = _getClassNameFromQueries(queries);    const query = new ParseQuery(className);    query._norQuery(queries);    return query;  }  /**   * Change the source of this query to the server.   *   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  fromNetwork() /*: ParseQuery*/{    this._queriesLocalDatastore = false;    this._localDatastorePinName = null;    return this;  }  /**   * Changes the source of this query to all pinned objects.   *   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  fromLocalDatastore() /*: ParseQuery*/{    return this.fromPinWithName(null);  }  /**   * Changes the source of this query to the default group of pinned objects.   *   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  fromPin() /*: ParseQuery*/{    return this.fromPinWithName(_LocalDatastoreUtils.DEFAULT_PIN);  }  /**   * Changes the source of this query to a specific group of pinned objects.   *   * @param {string} name The name of query source.   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  fromPinWithName(name /*:: ?: string*/) /*: ParseQuery*/{    const localDatastore = _CoreManager.default.getLocalDatastore();    if (localDatastore.checkIfEnabled()) {      this._queriesLocalDatastore = true;      this._localDatastorePinName = name;    }    return this;  }  /**   * Cancels the current network request (if any is running).   *   * @returns {Parse.Query} Returns the query, so you can chain this call.   */  cancel() /*: ParseQuery*/{    if (this._xhrRequest.task && typeof this._xhrRequest.task.abort === 'function') {      this._xhrRequest.task._aborted = true;      this._xhrRequest.task.abort();      this._xhrRequest.task = null;      this._xhrRequest.onchange = () => {};      return this;    }    return this._xhrRequest.onchange = () => this.cancel();  }  _setRequestTask(options) {    options.requestTask = task => {      this._xhrRequest.task = task;      this._xhrRequest.onchange();    };  }}const DefaultController = {  find(className /*: string*/, params /*: QueryJSON*/, options /*: RequestOptions*/) /*: Promise<Array<ParseObject>>*/{    const RESTController = _CoreManager.default.getRESTController();    return RESTController.request('GET', 'classes/' + className, params, options);  },  aggregate(className /*: string*/, params /*: any*/, options /*: RequestOptions*/) /*: Promise<Array<mixed>>*/{    const RESTController = _CoreManager.default.getRESTController();    return RESTController.request('GET', 'aggregate/' + className, params, options);  }};_CoreManager.default.setQueryController(DefaultController);var _default = ParseQuery;exports.default = _default;
 |