ParseObject.js 84 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446
  1. "use strict";
  2. var _WeakMap = require("@babel/runtime-corejs3/core-js-stable/weak-map");
  3. var _Object$defineProperty2 = require("@babel/runtime-corejs3/core-js-stable/object/define-property");
  4. var _Object$getOwnPropertyDescriptor = require("@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor");
  5. var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");
  6. _Object$defineProperty2(exports, "__esModule", {
  7. value: true
  8. });
  9. exports.default = void 0;
  10. var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/defineProperty"));
  11. var _indexOf = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/index-of"));
  12. var _freeze = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/freeze"));
  13. var _keys = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/keys"));
  14. var _forEach = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/for-each"));
  15. var _stringify = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/json/stringify"));
  16. var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));
  17. var _getPrototypeOf = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/get-prototype-of"));
  18. var _concat = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/concat"));
  19. var _isArray = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/array/is-array"));
  20. var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));
  21. var _create = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/create"));
  22. var _defineProperty3 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/object/define-property"));
  23. var _find = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/find"));
  24. var _map = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/map"));
  25. var _CoreManager = _interopRequireDefault(require("./CoreManager"));
  26. var _canBeSerialized = _interopRequireDefault(require("./canBeSerialized"));
  27. var _decode = _interopRequireDefault(require("./decode"));
  28. var _encode = _interopRequireDefault(require("./encode"));
  29. var _escape = _interopRequireDefault(require("./escape"));
  30. var _ParseACL = _interopRequireDefault(require("./ParseACL"));
  31. var _parseDate = _interopRequireDefault(require("./parseDate"));
  32. var _ParseError = _interopRequireDefault(require("./ParseError"));
  33. var _ParseFile = _interopRequireDefault(require("./ParseFile"));
  34. var _promiseUtils = require("./promiseUtils");
  35. var _LocalDatastoreUtils = require("./LocalDatastoreUtils");
  36. var _uuid = _interopRequireDefault(require("./uuid"));
  37. var _ParseOp = require("./ParseOp");
  38. var _ParseRelation = _interopRequireDefault(require("./ParseRelation"));
  39. var SingleInstanceStateController = _interopRequireWildcard(require("./SingleInstanceStateController"));
  40. var _unique = _interopRequireDefault(require("./unique"));
  41. var UniqueInstanceStateController = _interopRequireWildcard(require("./UniqueInstanceStateController"));
  42. var _unsavedChildren = _interopRequireDefault(require("./unsavedChildren"));
  43. function _getRequireWildcardCache(e) {
  44. if ("function" != typeof _WeakMap) return null;
  45. var r = new _WeakMap(),
  46. t = new _WeakMap();
  47. return (_getRequireWildcardCache = function (e) {
  48. return e ? t : r;
  49. })(e);
  50. }
  51. function _interopRequireWildcard(e, r) {
  52. if (!r && e && e.__esModule) return e;
  53. if (null === e || "object" != typeof e && "function" != typeof e) return {
  54. default: e
  55. };
  56. var t = _getRequireWildcardCache(r);
  57. if (t && t.has(e)) return t.get(e);
  58. var n = {
  59. __proto__: null
  60. };
  61. for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {
  62. var i = _Object$defineProperty2 && _Object$getOwnPropertyDescriptor ? _Object$getOwnPropertyDescriptor(e, u) : null;
  63. i && (i.get || i.set) ? _Object$defineProperty2(n, u, i) : n[u] = e[u];
  64. }
  65. return n.default = e, t && t.set(e, n), n;
  66. }
  67. // Mapping of class names to constructors, so we can populate objects from the
  68. // server with appropriate subclasses of ParseObject
  69. const classMap = {};
  70. // Global counter for generating unique Ids for non-single-instance objects
  71. let objectCount = 0;
  72. // On web clients, objects are single-instance: any two objects with the same Id
  73. // will have the same attributes. However, this may be dangerous default
  74. // behavior in a server scenario
  75. let singleInstance = !_CoreManager.default.get('IS_NODE');
  76. if (singleInstance) {
  77. _CoreManager.default.setObjectStateController(SingleInstanceStateController);
  78. } else {
  79. _CoreManager.default.setObjectStateController(UniqueInstanceStateController);
  80. }
  81. function getServerUrlPath() {
  82. let serverUrl = _CoreManager.default.get('SERVER_URL');
  83. if (serverUrl[serverUrl.length - 1] !== '/') {
  84. serverUrl += '/';
  85. }
  86. const url = serverUrl.replace(/https?:\/\//, '');
  87. return url.substr((0, _indexOf.default)(url).call(url, '/'));
  88. }
  89. /**
  90. * Creates a new model with defined attributes.
  91. *
  92. * <p>You won't normally call this method directly. It is recommended that
  93. * you use a subclass of <code>Parse.Object</code> instead, created by calling
  94. * <code>extend</code>.</p>
  95. *
  96. * <p>However, if you don't want to use a subclass, or aren't sure which
  97. * subclass is appropriate, you can use this form:<pre>
  98. * var object = new Parse.Object("ClassName");
  99. * </pre>
  100. * That is basically equivalent to:<pre>
  101. * var MyClass = Parse.Object.extend("ClassName");
  102. * var object = new MyClass();
  103. * </pre></p>
  104. *
  105. * @alias Parse.Object
  106. */
  107. class ParseObject {
  108. /**
  109. * @param {string} className The class name for the object
  110. * @param {object} attributes The initial set of data to store in the object.
  111. * @param {object} options The options for this object instance.
  112. * @param {boolean} [options.ignoreValidation] Set to `true` ignore any attribute validation errors.
  113. */
  114. constructor(className, attributes, options) {
  115. /**
  116. * The ID of this object, unique within its class.
  117. *
  118. * @property {string} id
  119. */
  120. (0, _defineProperty2.default)(this, "id", void 0);
  121. (0, _defineProperty2.default)(this, "_localId", void 0);
  122. (0, _defineProperty2.default)(this, "_objCount", void 0);
  123. (0, _defineProperty2.default)(this, "className", void 0);
  124. // Enable legacy initializers
  125. if (typeof this.initialize === 'function') {
  126. this.initialize.apply(this, arguments);
  127. }
  128. let toSet = null;
  129. this._objCount = objectCount++;
  130. if (typeof className === 'string') {
  131. this.className = className;
  132. if (attributes && typeof attributes === 'object') {
  133. toSet = attributes;
  134. }
  135. } else if (className && typeof className === 'object') {
  136. this.className = className.className;
  137. toSet = {};
  138. for (const attr in className) {
  139. if (attr !== 'className') {
  140. toSet[attr] = className[attr];
  141. }
  142. }
  143. if (attributes && typeof attributes === 'object') {
  144. options = attributes;
  145. }
  146. }
  147. if (toSet && !this.set(toSet, options)) {
  148. throw new Error("Can't create an invalid Parse Object");
  149. }
  150. }
  151. /* Prototype getters / setters */
  152. get attributes() {
  153. const stateController = _CoreManager.default.getObjectStateController();
  154. return (0, _freeze.default)(stateController.estimateAttributes(this._getStateIdentifier()));
  155. }
  156. /**
  157. * The first time this object was saved on the server.
  158. *
  159. * @property {Date} createdAt
  160. * @returns {Date}
  161. */
  162. get createdAt() {
  163. return this._getServerData().createdAt;
  164. }
  165. /**
  166. * The last time this object was updated on the server.
  167. *
  168. * @property {Date} updatedAt
  169. * @returns {Date}
  170. */
  171. get updatedAt() {
  172. return this._getServerData().updatedAt;
  173. }
  174. /* Private methods */
  175. /**
  176. * Returns a local or server Id used uniquely identify this object
  177. *
  178. * @returns {string}
  179. */
  180. _getId() {
  181. if (typeof this.id === 'string') {
  182. return this.id;
  183. }
  184. if (typeof this._localId === 'string') {
  185. return this._localId;
  186. }
  187. const localId = 'local' + (0, _uuid.default)();
  188. this._localId = localId;
  189. return localId;
  190. }
  191. /**
  192. * Returns a unique identifier used to pull data from the State Controller.
  193. *
  194. * @returns {Parse.Object|object}
  195. */
  196. _getStateIdentifier() {
  197. if (singleInstance) {
  198. let id = this.id;
  199. if (!id) {
  200. id = this._getId();
  201. }
  202. return {
  203. id: id,
  204. className: this.className
  205. };
  206. } else {
  207. return this;
  208. }
  209. }
  210. _getServerData() {
  211. const stateController = _CoreManager.default.getObjectStateController();
  212. return stateController.getServerData(this._getStateIdentifier());
  213. }
  214. _clearServerData() {
  215. const serverData = this._getServerData();
  216. const unset = {};
  217. for (const attr in serverData) {
  218. unset[attr] = undefined;
  219. }
  220. const stateController = _CoreManager.default.getObjectStateController();
  221. stateController.setServerData(this._getStateIdentifier(), unset);
  222. }
  223. _getPendingOps() {
  224. const stateController = _CoreManager.default.getObjectStateController();
  225. return stateController.getPendingOps(this._getStateIdentifier());
  226. }
  227. /**
  228. * @param {Array<string>} [keysToClear] - if specified, only ops matching
  229. * these fields will be cleared
  230. */
  231. _clearPendingOps(keysToClear) {
  232. const pending = this._getPendingOps();
  233. const latest = pending[pending.length - 1];
  234. const keys = keysToClear || (0, _keys.default)(latest);
  235. (0, _forEach.default)(keys).call(keys, key => {
  236. delete latest[key];
  237. });
  238. }
  239. _getDirtyObjectAttributes() {
  240. const attributes = this.attributes;
  241. const stateController = _CoreManager.default.getObjectStateController();
  242. const objectCache = stateController.getObjectCache(this._getStateIdentifier());
  243. const dirty = {};
  244. for (const attr in attributes) {
  245. const val = attributes[attr];
  246. if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile.default) && !(val instanceof _ParseRelation.default)) {
  247. // Due to the way browsers construct maps, the key order will not change
  248. // unless the object is changed
  249. try {
  250. const json = (0, _encode.default)(val, false, true);
  251. const stringified = (0, _stringify.default)(json);
  252. if (objectCache[attr] !== stringified) {
  253. dirty[attr] = val;
  254. }
  255. } catch (e) {
  256. // Error occurred, possibly by a nested unsaved pointer in a mutable container
  257. // No matter how it happened, it indicates a change in the attribute
  258. dirty[attr] = val;
  259. }
  260. }
  261. }
  262. return dirty;
  263. }
  264. _toFullJSON(seen, offline) {
  265. const json = this.toJSON(seen, offline);
  266. json.__type = 'Object';
  267. json.className = this.className;
  268. return json;
  269. }
  270. _getSaveJSON() {
  271. const pending = this._getPendingOps();
  272. const dirtyObjects = this._getDirtyObjectAttributes();
  273. const json = {};
  274. for (var attr in dirtyObjects) {
  275. let isDotNotation = false;
  276. for (let i = 0; i < pending.length; i += 1) {
  277. for (const field in pending[i]) {
  278. // Dot notation operations are handled later
  279. if ((0, _includes.default)(field).call(field, '.')) {
  280. const fieldName = field.split('.')[0];
  281. if (fieldName === attr) {
  282. isDotNotation = true;
  283. break;
  284. }
  285. }
  286. }
  287. }
  288. if (!isDotNotation) {
  289. json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON();
  290. }
  291. }
  292. for (attr in pending[0]) {
  293. json[attr] = pending[0][attr].toJSON();
  294. }
  295. return json;
  296. }
  297. _getSaveParams() {
  298. let method = this.id ? 'PUT' : 'POST';
  299. const body = this._getSaveJSON();
  300. let path = 'classes/' + this.className;
  301. if (_CoreManager.default.get('ALLOW_CUSTOM_OBJECT_ID')) {
  302. if (!this.createdAt) {
  303. method = 'POST';
  304. body.objectId = this.id;
  305. } else {
  306. method = 'PUT';
  307. path += '/' + this.id;
  308. }
  309. } else if (this.id) {
  310. path += '/' + this.id;
  311. } else if (this.className === '_User') {
  312. path = 'users';
  313. }
  314. return {
  315. method,
  316. body,
  317. path
  318. };
  319. }
  320. _finishFetch(serverData) {
  321. if (!this.id && serverData.objectId) {
  322. this.id = serverData.objectId;
  323. }
  324. const stateController = _CoreManager.default.getObjectStateController();
  325. stateController.initializeState(this._getStateIdentifier());
  326. const decoded = {};
  327. for (const attr in serverData) {
  328. if (attr === 'ACL') {
  329. decoded[attr] = new _ParseACL.default(serverData[attr]);
  330. } else if (attr !== 'objectId') {
  331. decoded[attr] = (0, _decode.default)(serverData[attr]);
  332. if (decoded[attr] instanceof _ParseRelation.default) {
  333. decoded[attr]._ensureParentAndKey(this, attr);
  334. }
  335. }
  336. }
  337. if (decoded.createdAt && typeof decoded.createdAt === 'string') {
  338. decoded.createdAt = (0, _parseDate.default)(decoded.createdAt);
  339. }
  340. if (decoded.updatedAt && typeof decoded.updatedAt === 'string') {
  341. decoded.updatedAt = (0, _parseDate.default)(decoded.updatedAt);
  342. }
  343. if (!decoded.updatedAt && decoded.createdAt) {
  344. decoded.updatedAt = decoded.createdAt;
  345. }
  346. stateController.commitServerChanges(this._getStateIdentifier(), decoded);
  347. }
  348. _setExisted(existed) {
  349. const stateController = _CoreManager.default.getObjectStateController();
  350. const state = stateController.getState(this._getStateIdentifier());
  351. if (state) {
  352. state.existed = existed;
  353. }
  354. }
  355. _migrateId(serverId) {
  356. if (this._localId && serverId) {
  357. if (singleInstance) {
  358. const stateController = _CoreManager.default.getObjectStateController();
  359. const oldState = stateController.removeState(this._getStateIdentifier());
  360. this.id = serverId;
  361. delete this._localId;
  362. if (oldState) {
  363. stateController.initializeState(this._getStateIdentifier(), oldState);
  364. }
  365. } else {
  366. this.id = serverId;
  367. delete this._localId;
  368. }
  369. }
  370. }
  371. _handleSaveResponse(response, status) {
  372. const changes = {};
  373. const stateController = _CoreManager.default.getObjectStateController();
  374. const pending = stateController.popPendingState(this._getStateIdentifier());
  375. for (var attr in pending) {
  376. if (pending[attr] instanceof _ParseOp.RelationOp) {
  377. changes[attr] = pending[attr].applyTo(undefined, this, attr);
  378. } else if (!(attr in response)) {
  379. // Only SetOps and UnsetOps should not come back with results
  380. changes[attr] = pending[attr].applyTo(undefined);
  381. }
  382. }
  383. for (attr in response) {
  384. if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') {
  385. changes[attr] = (0, _parseDate.default)(response[attr]);
  386. } else if (attr === 'ACL') {
  387. changes[attr] = new _ParseACL.default(response[attr]);
  388. } else if (attr !== 'objectId') {
  389. const val = (0, _decode.default)(response[attr]);
  390. if (val && (0, _getPrototypeOf.default)(val) === Object.prototype) {
  391. changes[attr] = {
  392. ...this.attributes[attr],
  393. ...val
  394. };
  395. } else {
  396. changes[attr] = val;
  397. }
  398. if (changes[attr] instanceof _ParseOp.UnsetOp) {
  399. changes[attr] = undefined;
  400. }
  401. }
  402. }
  403. if (changes.createdAt && !changes.updatedAt) {
  404. changes.updatedAt = changes.createdAt;
  405. }
  406. this._migrateId(response.objectId);
  407. if (status !== 201) {
  408. this._setExisted(true);
  409. }
  410. stateController.commitServerChanges(this._getStateIdentifier(), changes);
  411. }
  412. _handleSaveError() {
  413. const stateController = _CoreManager.default.getObjectStateController();
  414. stateController.mergeFirstPendingState(this._getStateIdentifier());
  415. }
  416. static _getClassMap() {
  417. return classMap;
  418. }
  419. /* Public methods */
  420. initialize() {
  421. // NOOP
  422. }
  423. /**
  424. * Returns a JSON version of the object suitable for saving to Parse.
  425. *
  426. * @param seen
  427. * @param offline
  428. * @returns {object}
  429. */
  430. toJSON(seen, offline) {
  431. const seenEntry = this.id ? this.className + ':' + this.id : this;
  432. seen = seen || [seenEntry];
  433. const json = {};
  434. const attrs = this.attributes;
  435. for (const attr in attrs) {
  436. if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) {
  437. json[attr] = attrs[attr].toJSON();
  438. } else {
  439. json[attr] = (0, _encode.default)(attrs[attr], false, false, seen, offline);
  440. }
  441. }
  442. const pending = this._getPendingOps();
  443. for (const attr in pending[0]) {
  444. if ((0, _indexOf.default)(attr).call(attr, '.') < 0) {
  445. json[attr] = pending[0][attr].toJSON(offline);
  446. }
  447. }
  448. if (this.id) {
  449. json.objectId = this.id;
  450. }
  451. return json;
  452. }
  453. /**
  454. * Determines whether this ParseObject is equal to another ParseObject
  455. *
  456. * @param {object} other - An other object ot compare
  457. * @returns {boolean}
  458. */
  459. equals(other) {
  460. if (this === other) {
  461. return true;
  462. }
  463. return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined';
  464. }
  465. /**
  466. * Returns true if this object has been modified since its last
  467. * save/refresh. If an attribute is specified, it returns true only if that
  468. * particular attribute has been modified since the last save/refresh.
  469. *
  470. * @param {string} attr An attribute name (optional).
  471. * @returns {boolean}
  472. */
  473. dirty(attr) {
  474. if (!this.id) {
  475. return true;
  476. }
  477. const pendingOps = this._getPendingOps();
  478. const dirtyObjects = this._getDirtyObjectAttributes();
  479. if (attr) {
  480. if (dirtyObjects.hasOwnProperty(attr)) {
  481. return true;
  482. }
  483. for (let i = 0; i < pendingOps.length; i++) {
  484. if (pendingOps[i].hasOwnProperty(attr)) {
  485. return true;
  486. }
  487. }
  488. return false;
  489. }
  490. if ((0, _keys.default)(pendingOps[0]).length !== 0) {
  491. return true;
  492. }
  493. if ((0, _keys.default)(dirtyObjects).length !== 0) {
  494. return true;
  495. }
  496. return false;
  497. }
  498. /**
  499. * Returns an array of keys that have been modified since last save/refresh
  500. *
  501. * @returns {string[]}
  502. */
  503. dirtyKeys() {
  504. const pendingOps = this._getPendingOps();
  505. const keys = {};
  506. for (let i = 0; i < pendingOps.length; i++) {
  507. for (const attr in pendingOps[i]) {
  508. keys[attr] = true;
  509. }
  510. }
  511. const dirtyObjects = this._getDirtyObjectAttributes();
  512. for (const attr in dirtyObjects) {
  513. keys[attr] = true;
  514. }
  515. return (0, _keys.default)(keys);
  516. }
  517. /**
  518. * Returns true if the object has been fetched.
  519. *
  520. * @returns {boolean}
  521. */
  522. isDataAvailable() {
  523. const serverData = this._getServerData();
  524. return !!(0, _keys.default)(serverData).length;
  525. }
  526. /**
  527. * Gets a Pointer referencing this Object.
  528. *
  529. * @returns {Pointer}
  530. */
  531. toPointer() {
  532. if (!this.id) {
  533. throw new Error('Cannot create a pointer to an unsaved ParseObject');
  534. }
  535. return {
  536. __type: 'Pointer',
  537. className: this.className,
  538. objectId: this.id
  539. };
  540. }
  541. /**
  542. * Gets a Pointer referencing this Object.
  543. *
  544. * @returns {Pointer}
  545. */
  546. toOfflinePointer() {
  547. if (!this._localId) {
  548. throw new Error('Cannot create a offline pointer to a saved ParseObject');
  549. }
  550. return {
  551. __type: 'Object',
  552. className: this.className,
  553. _localId: this._localId
  554. };
  555. }
  556. /**
  557. * Gets the value of an attribute.
  558. *
  559. * @param {string} attr The string name of an attribute.
  560. * @returns {*}
  561. */
  562. get(attr) {
  563. return this.attributes[attr];
  564. }
  565. /**
  566. * Gets a relation on the given class for the attribute.
  567. *
  568. * @param {string} attr The attribute to get the relation for.
  569. * @returns {Parse.Relation}
  570. */
  571. relation(attr) {
  572. const value = this.get(attr);
  573. if (value) {
  574. if (!(value instanceof _ParseRelation.default)) {
  575. throw new Error('Called relation() on non-relation field ' + attr);
  576. }
  577. value._ensureParentAndKey(this, attr);
  578. return value;
  579. }
  580. return new _ParseRelation.default(this, attr);
  581. }
  582. /**
  583. * Gets the HTML-escaped value of an attribute.
  584. *
  585. * @param {string} attr The string name of an attribute.
  586. * @returns {string}
  587. */
  588. escape(attr) {
  589. let val = this.attributes[attr];
  590. if (val == null) {
  591. return '';
  592. }
  593. if (typeof val !== 'string') {
  594. if (typeof val.toString !== 'function') {
  595. return '';
  596. }
  597. val = val.toString();
  598. }
  599. return (0, _escape.default)(val);
  600. }
  601. /**
  602. * Returns <code>true</code> if the attribute contains a value that is not
  603. * null or undefined.
  604. *
  605. * @param {string} attr The string name of the attribute.
  606. * @returns {boolean}
  607. */
  608. has(attr) {
  609. const attributes = this.attributes;
  610. if (attributes.hasOwnProperty(attr)) {
  611. return attributes[attr] != null;
  612. }
  613. return false;
  614. }
  615. /**
  616. * Sets a hash of model attributes on the object.
  617. *
  618. * <p>You can call it with an object containing keys and values, with one
  619. * key and value, or dot notation. For example:<pre>
  620. * gameTurn.set({
  621. * player: player1,
  622. * diceRoll: 2
  623. * }, {
  624. * error: function(gameTurnAgain, error) {
  625. * // The set failed validation.
  626. * }
  627. * });
  628. *
  629. * game.set("currentPlayer", player2, {
  630. * error: function(gameTurnAgain, error) {
  631. * // The set failed validation.
  632. * }
  633. * });
  634. *
  635. * game.set("finished", true);</pre></p>
  636. *
  637. * game.set("player.score", 10);</pre></p>
  638. *
  639. * @param {(string|object)} key The key to set.
  640. * @param {(string|object)} value The value to give it.
  641. * @param {object} options A set of options for the set.
  642. * The only supported option is <code>error</code>.
  643. * @returns {(ParseObject|boolean)} true if the set succeeded.
  644. */
  645. set(key, value, options) {
  646. let changes = {};
  647. const newOps = {};
  648. if (key && typeof key === 'object') {
  649. changes = key;
  650. options = value;
  651. } else if (typeof key === 'string') {
  652. changes[key] = value;
  653. } else {
  654. return this;
  655. }
  656. options = options || {};
  657. let readonly = [];
  658. if (typeof this.constructor.readOnlyAttributes === 'function') {
  659. readonly = (0, _concat.default)(readonly).call(readonly, this.constructor.readOnlyAttributes());
  660. }
  661. for (const k in changes) {
  662. if (k === 'createdAt' || k === 'updatedAt') {
  663. // This property is read-only, but for legacy reasons we silently
  664. // ignore it
  665. continue;
  666. }
  667. if ((0, _indexOf.default)(readonly).call(readonly, k) > -1) {
  668. throw new Error('Cannot modify readonly attribute: ' + k);
  669. }
  670. if (options.unset) {
  671. newOps[k] = new _ParseOp.UnsetOp();
  672. } else if (changes[k] instanceof _ParseOp.Op) {
  673. newOps[k] = changes[k];
  674. } else if (changes[k] && typeof changes[k] === 'object' && typeof changes[k].__op === 'string') {
  675. newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);
  676. } else if (k === 'objectId' || k === 'id') {
  677. if (typeof changes[k] === 'string') {
  678. this.id = changes[k];
  679. }
  680. } else if (k === 'ACL' && typeof changes[k] === 'object' && !(changes[k] instanceof _ParseACL.default)) {
  681. newOps[k] = new _ParseOp.SetOp(new _ParseACL.default(changes[k]));
  682. } else if (changes[k] instanceof _ParseRelation.default) {
  683. const relation = new _ParseRelation.default(this, k);
  684. relation.targetClassName = changes[k].targetClassName;
  685. newOps[k] = new _ParseOp.SetOp(relation);
  686. } else {
  687. newOps[k] = new _ParseOp.SetOp(changes[k]);
  688. }
  689. }
  690. const currentAttributes = this.attributes;
  691. // Calculate new values
  692. const newValues = {};
  693. for (const attr in newOps) {
  694. if (newOps[attr] instanceof _ParseOp.RelationOp) {
  695. newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
  696. } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {
  697. newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
  698. }
  699. }
  700. // Validate changes
  701. if (!options.ignoreValidation) {
  702. const validation = this.validate(newValues);
  703. if (validation) {
  704. if (typeof options.error === 'function') {
  705. options.error(this, validation);
  706. }
  707. return false;
  708. }
  709. }
  710. // Consolidate Ops
  711. const pendingOps = this._getPendingOps();
  712. const last = pendingOps.length - 1;
  713. const stateController = _CoreManager.default.getObjectStateController();
  714. for (const attr in newOps) {
  715. const nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
  716. stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
  717. }
  718. return this;
  719. }
  720. /**
  721. * Remove an attribute from the model. This is a noop if the attribute doesn't
  722. * exist.
  723. *
  724. * @param {string} attr The string name of an attribute.
  725. * @param options
  726. * @returns {(ParseObject | boolean)}
  727. */
  728. unset(attr, options) {
  729. options = options || {};
  730. options.unset = true;
  731. return this.set(attr, null, options);
  732. }
  733. /**
  734. * Atomically increments the value of the given attribute the next time the
  735. * object is saved. If no amount is specified, 1 is used by default.
  736. *
  737. * @param attr {String} The key.
  738. * @param amount {Number} The amount to increment by (optional).
  739. * @returns {(ParseObject|boolean)}
  740. */
  741. increment(attr, amount) {
  742. if (typeof amount === 'undefined') {
  743. amount = 1;
  744. }
  745. if (typeof amount !== 'number') {
  746. throw new Error('Cannot increment by a non-numeric amount.');
  747. }
  748. return this.set(attr, new _ParseOp.IncrementOp(amount));
  749. }
  750. /**
  751. * Atomically decrements the value of the given attribute the next time the
  752. * object is saved. If no amount is specified, 1 is used by default.
  753. *
  754. * @param attr {String} The key.
  755. * @param amount {Number} The amount to decrement by (optional).
  756. * @returns {(ParseObject | boolean)}
  757. */
  758. decrement(attr, amount) {
  759. if (typeof amount === 'undefined') {
  760. amount = 1;
  761. }
  762. if (typeof amount !== 'number') {
  763. throw new Error('Cannot decrement by a non-numeric amount.');
  764. }
  765. return this.set(attr, new _ParseOp.IncrementOp(amount * -1));
  766. }
  767. /**
  768. * Atomically add an object to the end of the array associated with a given
  769. * key.
  770. *
  771. * @param attr {String} The key.
  772. * @param item {} The item to add.
  773. * @returns {(ParseObject | boolean)}
  774. */
  775. add(attr, item) {
  776. return this.set(attr, new _ParseOp.AddOp([item]));
  777. }
  778. /**
  779. * Atomically add the objects to the end of the array associated with a given
  780. * key.
  781. *
  782. * @param attr {String} The key.
  783. * @param items {Object[]} The items to add.
  784. * @returns {(ParseObject | boolean)}
  785. */
  786. addAll(attr, items) {
  787. return this.set(attr, new _ParseOp.AddOp(items));
  788. }
  789. /**
  790. * Atomically add an object to the array associated with a given key, only
  791. * if it is not already present in the array. The position of the insert is
  792. * not guaranteed.
  793. *
  794. * @param attr {String} The key.
  795. * @param item {} The object to add.
  796. * @returns {(ParseObject | boolean)}
  797. */
  798. addUnique(attr, item) {
  799. return this.set(attr, new _ParseOp.AddUniqueOp([item]));
  800. }
  801. /**
  802. * Atomically add the objects to the array associated with a given key, only
  803. * if it is not already present in the array. The position of the insert is
  804. * not guaranteed.
  805. *
  806. * @param attr {String} The key.
  807. * @param items {Object[]} The objects to add.
  808. * @returns {(ParseObject | boolean)}
  809. */
  810. addAllUnique(attr, items) {
  811. return this.set(attr, new _ParseOp.AddUniqueOp(items));
  812. }
  813. /**
  814. * Atomically remove all instances of an object from the array associated
  815. * with a given key.
  816. *
  817. * @param attr {String} The key.
  818. * @param item {} The object to remove.
  819. * @returns {(ParseObject | boolean)}
  820. */
  821. remove(attr, item) {
  822. return this.set(attr, new _ParseOp.RemoveOp([item]));
  823. }
  824. /**
  825. * Atomically remove all instances of the objects from the array associated
  826. * with a given key.
  827. *
  828. * @param attr {String} The key.
  829. * @param items {Object[]} The object to remove.
  830. * @returns {(ParseObject | boolean)}
  831. */
  832. removeAll(attr, items) {
  833. return this.set(attr, new _ParseOp.RemoveOp(items));
  834. }
  835. /**
  836. * Returns an instance of a subclass of Parse.Op describing what kind of
  837. * modification has been performed on this field since the last time it was
  838. * saved. For example, after calling object.increment("x"), calling
  839. * object.op("x") would return an instance of Parse.Op.Increment.
  840. *
  841. * @param attr {String} The key.
  842. * @returns {Parse.Op | undefined} The operation, or undefined if none.
  843. */
  844. op(attr) {
  845. const pending = this._getPendingOps();
  846. for (let i = pending.length; i--;) {
  847. if (pending[i][attr]) {
  848. return pending[i][attr];
  849. }
  850. }
  851. }
  852. /**
  853. * Creates a new model with identical attributes to this one.
  854. *
  855. * @returns {Parse.Object}
  856. */
  857. clone() {
  858. const clone = new this.constructor(this.className);
  859. let attributes = this.attributes;
  860. if (typeof this.constructor.readOnlyAttributes === 'function') {
  861. const readonly = this.constructor.readOnlyAttributes() || [];
  862. // Attributes are frozen, so we have to rebuild an object,
  863. // rather than delete readonly keys
  864. const copy = {};
  865. for (const a in attributes) {
  866. if ((0, _indexOf.default)(readonly).call(readonly, a) < 0) {
  867. copy[a] = attributes[a];
  868. }
  869. }
  870. attributes = copy;
  871. }
  872. if (clone.set) {
  873. clone.set(attributes);
  874. }
  875. return clone;
  876. }
  877. /**
  878. * Creates a new instance of this object. Not to be confused with clone()
  879. *
  880. * @returns {Parse.Object}
  881. */
  882. newInstance() {
  883. const clone = new this.constructor(this.className);
  884. clone.id = this.id;
  885. if (singleInstance) {
  886. // Just return an object with the right id
  887. return clone;
  888. }
  889. const stateController = _CoreManager.default.getObjectStateController();
  890. if (stateController) {
  891. stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
  892. }
  893. return clone;
  894. }
  895. /**
  896. * Returns true if this object has never been saved to Parse.
  897. *
  898. * @returns {boolean}
  899. */
  900. isNew() {
  901. return !this.id;
  902. }
  903. /**
  904. * Returns true if this object was created by the Parse server when the
  905. * object might have already been there (e.g. in the case of a Facebook
  906. * login)
  907. *
  908. * @returns {boolean}
  909. */
  910. existed() {
  911. if (!this.id) {
  912. return false;
  913. }
  914. const stateController = _CoreManager.default.getObjectStateController();
  915. const state = stateController.getState(this._getStateIdentifier());
  916. if (state) {
  917. return state.existed;
  918. }
  919. return false;
  920. }
  921. /**
  922. * Returns true if this object exists on the Server
  923. *
  924. * @param {object} options
  925. * Valid options are:<ul>
  926. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  927. * be used for this request.
  928. * <li>sessionToken: A valid session token, used for making a request on
  929. * behalf of a specific user.
  930. * </ul>
  931. * @returns {Promise<boolean>} A boolean promise that is fulfilled if object exists.
  932. */
  933. async exists(options) {
  934. if (!this.id) {
  935. return false;
  936. }
  937. try {
  938. const ParseQuery = _CoreManager.default.getParseQuery();
  939. const query = new ParseQuery(this.className);
  940. await query.get(this.id, options);
  941. return true;
  942. } catch (e) {
  943. if (e.code === _ParseError.default.OBJECT_NOT_FOUND) {
  944. return false;
  945. }
  946. throw e;
  947. }
  948. }
  949. /**
  950. * Checks if the model is currently in a valid state.
  951. *
  952. * @returns {boolean}
  953. */
  954. isValid() {
  955. return !this.validate(this.attributes);
  956. }
  957. /**
  958. * You should not call this function directly unless you subclass
  959. * <code>Parse.Object</code>, in which case you can override this method
  960. * to provide additional validation on <code>set</code> and
  961. * <code>save</code>. Your implementation should return
  962. *
  963. * @param {object} attrs The current data to validate.
  964. * @returns {Parse.Error|boolean} False if the data is valid. An error object otherwise.
  965. * @see Parse.Object#set
  966. */
  967. validate(attrs) {
  968. if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL.default)) {
  969. return new _ParseError.default(_ParseError.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');
  970. }
  971. for (const key in attrs) {
  972. if (!/^[A-Za-z][0-9A-Za-z_.]*$/.test(key)) {
  973. return new _ParseError.default(_ParseError.default.INVALID_KEY_NAME);
  974. }
  975. }
  976. return false;
  977. }
  978. /**
  979. * Returns the ACL for this object.
  980. *
  981. * @returns {Parse.ACL|null} An instance of Parse.ACL.
  982. * @see Parse.Object#get
  983. */
  984. getACL() {
  985. const acl = this.get('ACL');
  986. if (acl instanceof _ParseACL.default) {
  987. return acl;
  988. }
  989. return null;
  990. }
  991. /**
  992. * Sets the ACL to be used for this object.
  993. *
  994. * @param {Parse.ACL} acl An instance of Parse.ACL.
  995. * @param {object} options
  996. * @returns {(ParseObject | boolean)} Whether the set passed validation.
  997. * @see Parse.Object#set
  998. */
  999. setACL(acl, options) {
  1000. return this.set('ACL', acl, options);
  1001. }
  1002. /**
  1003. * Clears any (or specific) changes to this object made since the last call to save()
  1004. *
  1005. * @param {string} [keys] - specify which fields to revert
  1006. */
  1007. revert() {
  1008. let keysToRevert;
  1009. for (var _len = arguments.length, keys = new Array(_len), _key = 0; _key < _len; _key++) {
  1010. keys[_key] = arguments[_key];
  1011. }
  1012. if (keys.length) {
  1013. keysToRevert = [];
  1014. for (const key of keys) {
  1015. if (typeof key === 'string') {
  1016. keysToRevert.push(key);
  1017. } else {
  1018. throw new Error('Parse.Object#revert expects either no, or a list of string, arguments.');
  1019. }
  1020. }
  1021. }
  1022. this._clearPendingOps(keysToRevert);
  1023. }
  1024. /**
  1025. * Clears all attributes on a model
  1026. *
  1027. * @returns {(ParseObject | boolean)}
  1028. */
  1029. clear() {
  1030. const attributes = this.attributes;
  1031. const erasable = {};
  1032. let readonly = ['createdAt', 'updatedAt'];
  1033. if (typeof this.constructor.readOnlyAttributes === 'function') {
  1034. readonly = (0, _concat.default)(readonly).call(readonly, this.constructor.readOnlyAttributes());
  1035. }
  1036. for (const attr in attributes) {
  1037. if ((0, _indexOf.default)(readonly).call(readonly, attr) < 0) {
  1038. erasable[attr] = true;
  1039. }
  1040. }
  1041. return this.set(erasable, {
  1042. unset: true
  1043. });
  1044. }
  1045. /**
  1046. * Fetch the model from the server. If the server's representation of the
  1047. * model differs from its current attributes, they will be overriden.
  1048. *
  1049. * @param {object} options
  1050. * Valid options are:<ul>
  1051. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1052. * be used for this request.
  1053. * <li>sessionToken: A valid session token, used for making a request on
  1054. * behalf of a specific user.
  1055. * <li>include: The name(s) of the key(s) to include. Can be a string, an array of strings,
  1056. * or an array of array of strings.
  1057. * <li>context: A dictionary that is accessible in Cloud Code `beforeFind` trigger.
  1058. * </ul>
  1059. * @returns {Promise} A promise that is fulfilled when the fetch
  1060. * completes.
  1061. */
  1062. fetch(options) {
  1063. options = options || {};
  1064. const fetchOptions = {};
  1065. if (options.hasOwnProperty('useMasterKey')) {
  1066. fetchOptions.useMasterKey = options.useMasterKey;
  1067. }
  1068. if (options.hasOwnProperty('sessionToken')) {
  1069. fetchOptions.sessionToken = options.sessionToken;
  1070. }
  1071. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1072. fetchOptions.context = options.context;
  1073. }
  1074. if (options.hasOwnProperty('include')) {
  1075. fetchOptions.include = [];
  1076. if ((0, _isArray.default)(options.include)) {
  1077. var _context;
  1078. (0, _forEach.default)(_context = options.include).call(_context, key => {
  1079. if ((0, _isArray.default)(key)) {
  1080. var _context2;
  1081. fetchOptions.include = (0, _concat.default)(_context2 = fetchOptions.include).call(_context2, key);
  1082. } else {
  1083. fetchOptions.include.push(key);
  1084. }
  1085. });
  1086. } else {
  1087. fetchOptions.include.push(options.include);
  1088. }
  1089. }
  1090. const controller = _CoreManager.default.getObjectController();
  1091. return controller.fetch(this, true, fetchOptions);
  1092. }
  1093. /**
  1094. * Fetch the model from the server. If the server's representation of the
  1095. * model differs from its current attributes, they will be overriden.
  1096. *
  1097. * Includes nested Parse.Objects for the provided key. You can use dot
  1098. * notation to specify which fields in the included object are also fetched.
  1099. *
  1100. * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.
  1101. * @param {object} options
  1102. * Valid options are:<ul>
  1103. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1104. * be used for this request.
  1105. * <li>sessionToken: A valid session token, used for making a request on
  1106. * behalf of a specific user.
  1107. * </ul>
  1108. * @returns {Promise} A promise that is fulfilled when the fetch
  1109. * completes.
  1110. */
  1111. fetchWithInclude(keys, options) {
  1112. options = options || {};
  1113. options.include = keys;
  1114. return this.fetch(options);
  1115. }
  1116. /**
  1117. * Saves this object to the server at some unspecified time in the future,
  1118. * even if Parse is currently inaccessible.
  1119. *
  1120. * Use this when you may not have a solid network connection, and don't need to know when the save completes.
  1121. * If there is some problem with the object such that it can't be saved, it will be silently discarded.
  1122. *
  1123. * Objects saved with this method will be stored locally in an on-disk cache until they can be delivered to Parse.
  1124. * They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection is
  1125. * available. Objects saved this way will persist even after the app is closed, in which case they will be sent the
  1126. * next time the app is opened.
  1127. *
  1128. * @param {object} [options]
  1129. * Used to pass option parameters to method if arg1 and arg2 were both passed as strings.
  1130. * Valid options are:
  1131. * <ul>
  1132. * <li>sessionToken: A valid session token, used for making a request on
  1133. * behalf of a specific user.
  1134. * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).
  1135. * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.
  1136. * </ul>
  1137. * @returns {Promise} A promise that is fulfilled when the save
  1138. * completes.
  1139. */
  1140. async saveEventually(options) {
  1141. try {
  1142. await this.save(null, options);
  1143. } catch (e) {
  1144. if (e.code === _ParseError.default.CONNECTION_FAILED) {
  1145. await _CoreManager.default.getEventuallyQueue().save(this, options);
  1146. _CoreManager.default.getEventuallyQueue().poll();
  1147. }
  1148. }
  1149. return this;
  1150. }
  1151. /**
  1152. * Set a hash of model attributes, and save the model to the server.
  1153. * updatedAt will be updated when the request returns.
  1154. * You can either call it as:<pre>
  1155. * object.save();</pre>
  1156. * or<pre>
  1157. * object.save(attrs);</pre>
  1158. * or<pre>
  1159. * object.save(null, options);</pre>
  1160. * or<pre>
  1161. * object.save(attrs, options);</pre>
  1162. * or<pre>
  1163. * object.save(key, value);</pre>
  1164. * or<pre>
  1165. * object.save(key, value, options);</pre>
  1166. *
  1167. * Example 1: <pre>
  1168. * gameTurn.save({
  1169. * player: "Jake Cutter",
  1170. * diceRoll: 2
  1171. * }).then(function(gameTurnAgain) {
  1172. * // The save was successful.
  1173. * }, function(error) {
  1174. * // The save failed. Error is an instance of Parse.Error.
  1175. * });</pre>
  1176. *
  1177. * Example 2: <pre>
  1178. * gameTurn.save("player", "Jake Cutter");</pre>
  1179. *
  1180. * @param {string | object | null} [arg1]
  1181. * Valid options are:<ul>
  1182. * <li>`Object` - Key/value pairs to update on the object.</li>
  1183. * <li>`String` Key - Key of attribute to update (requires arg2 to also be string)</li>
  1184. * <li>`null` - Passing null for arg1 allows you to save the object with options passed in arg2.</li>
  1185. * </ul>
  1186. * @param {string | object} [arg2]
  1187. * <ul>
  1188. * <li>`String` Value - If arg1 was passed as a key, arg2 is the value that should be set on that key.</li>
  1189. * <li>`Object` Options - Valid options are:
  1190. * <ul>
  1191. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1192. * be used for this request.
  1193. * <li>sessionToken: A valid session token, used for making a request on
  1194. * behalf of a specific user.
  1195. * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).
  1196. * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.
  1197. * </ul>
  1198. * </li>
  1199. * </ul>
  1200. * @param {object} [arg3]
  1201. * Used to pass option parameters to method if arg1 and arg2 were both passed as strings.
  1202. * Valid options are:
  1203. * <ul>
  1204. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1205. * be used for this request.
  1206. * <li>sessionToken: A valid session token, used for making a request on
  1207. * behalf of a specific user.
  1208. * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).
  1209. * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.
  1210. * </ul>
  1211. * @returns {Promise} A promise that is fulfilled when the save
  1212. * completes.
  1213. */
  1214. save(arg1, arg2, arg3) {
  1215. let attrs;
  1216. let options;
  1217. if (typeof arg1 === 'object' || typeof arg1 === 'undefined') {
  1218. attrs = arg1;
  1219. if (typeof arg2 === 'object') {
  1220. options = arg2;
  1221. }
  1222. } else {
  1223. attrs = {};
  1224. attrs[arg1] = arg2;
  1225. options = arg3;
  1226. }
  1227. options = options || {};
  1228. if (attrs) {
  1229. let validationError;
  1230. options.error = (_, validation) => {
  1231. validationError = validation;
  1232. };
  1233. const success = this.set(attrs, options);
  1234. if (!success) {
  1235. return _promise.default.reject(validationError);
  1236. }
  1237. }
  1238. const saveOptions = {};
  1239. if (options.hasOwnProperty('useMasterKey')) {
  1240. saveOptions.useMasterKey = !!options.useMasterKey;
  1241. }
  1242. if (options.hasOwnProperty('sessionToken') && typeof options.sessionToken === 'string') {
  1243. saveOptions.sessionToken = options.sessionToken;
  1244. }
  1245. if (options.hasOwnProperty('installationId') && typeof options.installationId === 'string') {
  1246. saveOptions.installationId = options.installationId;
  1247. }
  1248. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1249. saveOptions.context = options.context;
  1250. }
  1251. const controller = _CoreManager.default.getObjectController();
  1252. const unsaved = options.cascadeSave !== false ? (0, _unsavedChildren.default)(this) : null;
  1253. return controller.save(unsaved, saveOptions).then(() => {
  1254. return controller.save(this, saveOptions);
  1255. });
  1256. }
  1257. /**
  1258. * Deletes this object from the server at some unspecified time in the future,
  1259. * even if Parse is currently inaccessible.
  1260. *
  1261. * Use this when you may not have a solid network connection,
  1262. * and don't need to know when the delete completes. If there is some problem with the object
  1263. * such that it can't be deleted, the request will be silently discarded.
  1264. *
  1265. * Delete instructions made with this method will be stored locally in an on-disk cache until they can be transmitted
  1266. * to Parse. They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection
  1267. * is available. Delete requests will persist even after the app is closed, in which case they will be sent the
  1268. * next time the app is opened.
  1269. *
  1270. * @param {object} [options]
  1271. * Valid options are:<ul>
  1272. * <li>sessionToken: A valid session token, used for making a request on
  1273. * behalf of a specific user.
  1274. * <li>context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers.
  1275. * </ul>
  1276. * @returns {Promise} A promise that is fulfilled when the destroy
  1277. * completes.
  1278. */
  1279. async destroyEventually(options) {
  1280. try {
  1281. await this.destroy(options);
  1282. } catch (e) {
  1283. if (e.code === _ParseError.default.CONNECTION_FAILED) {
  1284. await _CoreManager.default.getEventuallyQueue().destroy(this, options);
  1285. _CoreManager.default.getEventuallyQueue().poll();
  1286. }
  1287. }
  1288. return this;
  1289. }
  1290. /**
  1291. * Destroy this model on the server if it was already persisted.
  1292. *
  1293. * @param {object} options
  1294. * Valid options are:<ul>
  1295. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1296. * be used for this request.
  1297. * <li>sessionToken: A valid session token, used for making a request on
  1298. * behalf of a specific user.
  1299. * <li>context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers.
  1300. * </ul>
  1301. * @returns {Promise} A promise that is fulfilled when the destroy
  1302. * completes.
  1303. */
  1304. destroy(options) {
  1305. options = options || {};
  1306. const destroyOptions = {};
  1307. if (options.hasOwnProperty('useMasterKey')) {
  1308. destroyOptions.useMasterKey = options.useMasterKey;
  1309. }
  1310. if (options.hasOwnProperty('sessionToken')) {
  1311. destroyOptions.sessionToken = options.sessionToken;
  1312. }
  1313. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1314. destroyOptions.context = options.context;
  1315. }
  1316. if (!this.id) {
  1317. return _promise.default.resolve();
  1318. }
  1319. return _CoreManager.default.getObjectController().destroy(this, destroyOptions);
  1320. }
  1321. /**
  1322. * Asynchronously stores the object and every object it points to in the local datastore,
  1323. * recursively, using a default pin name: _default.
  1324. *
  1325. * If those other objects have not been fetched from Parse, they will not be stored.
  1326. * However, if they have changed data, all the changes will be retained.
  1327. *
  1328. * <pre>
  1329. * await object.pin();
  1330. * </pre>
  1331. *
  1332. * To retrieve object:
  1333. * <code>query.fromLocalDatastore()</code> or <code>query.fromPin()</code>
  1334. *
  1335. * @returns {Promise} A promise that is fulfilled when the pin completes.
  1336. */
  1337. pin() {
  1338. return ParseObject.pinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, [this]);
  1339. }
  1340. /**
  1341. * Asynchronously removes the object and every object it points to in the local datastore,
  1342. * recursively, using a default pin name: _default.
  1343. *
  1344. * <pre>
  1345. * await object.unPin();
  1346. * </pre>
  1347. *
  1348. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  1349. */
  1350. unPin() {
  1351. return ParseObject.unPinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, [this]);
  1352. }
  1353. /**
  1354. * Asynchronously returns if the object is pinned
  1355. *
  1356. * <pre>
  1357. * const isPinned = await object.isPinned();
  1358. * </pre>
  1359. *
  1360. * @returns {Promise<boolean>} A boolean promise that is fulfilled if object is pinned.
  1361. */
  1362. async isPinned() {
  1363. const localDatastore = _CoreManager.default.getLocalDatastore();
  1364. if (!localDatastore.isEnabled) {
  1365. return _promise.default.reject('Parse.enableLocalDatastore() must be called first');
  1366. }
  1367. const objectKey = localDatastore.getKeyForObject(this);
  1368. const pin = await localDatastore.fromPinWithName(objectKey);
  1369. return pin.length > 0;
  1370. }
  1371. /**
  1372. * Asynchronously stores the objects and every object they point to in the local datastore, recursively.
  1373. *
  1374. * If those other objects have not been fetched from Parse, they will not be stored.
  1375. * However, if they have changed data, all the changes will be retained.
  1376. *
  1377. * <pre>
  1378. * await object.pinWithName(name);
  1379. * </pre>
  1380. *
  1381. * To retrieve object:
  1382. * <code>query.fromLocalDatastore()</code> or <code>query.fromPinWithName(name)</code>
  1383. *
  1384. * @param {string} name Name of Pin.
  1385. * @returns {Promise} A promise that is fulfilled when the pin completes.
  1386. */
  1387. pinWithName(name) {
  1388. return ParseObject.pinAllWithName(name, [this]);
  1389. }
  1390. /**
  1391. * Asynchronously removes the object and every object it points to in the local datastore, recursively.
  1392. *
  1393. * <pre>
  1394. * await object.unPinWithName(name);
  1395. * </pre>
  1396. *
  1397. * @param {string} name Name of Pin.
  1398. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  1399. */
  1400. unPinWithName(name) {
  1401. return ParseObject.unPinAllWithName(name, [this]);
  1402. }
  1403. /**
  1404. * Asynchronously loads data from the local datastore into this object.
  1405. *
  1406. * <pre>
  1407. * await object.fetchFromLocalDatastore();
  1408. * </pre>
  1409. *
  1410. * You can create an unfetched pointer with <code>Parse.Object.createWithoutData()</code>
  1411. * and then call <code>fetchFromLocalDatastore()</code> on it.
  1412. *
  1413. * @returns {Promise} A promise that is fulfilled when the fetch completes.
  1414. */
  1415. async fetchFromLocalDatastore() {
  1416. const localDatastore = _CoreManager.default.getLocalDatastore();
  1417. if (!localDatastore.isEnabled) {
  1418. throw new Error('Parse.enableLocalDatastore() must be called first');
  1419. }
  1420. const objectKey = localDatastore.getKeyForObject(this);
  1421. const pinned = await localDatastore._serializeObject(objectKey);
  1422. if (!pinned) {
  1423. throw new Error('Cannot fetch an unsaved ParseObject');
  1424. }
  1425. const result = ParseObject.fromJSON(pinned);
  1426. this._finishFetch(result.toJSON());
  1427. return this;
  1428. }
  1429. /* Static methods */
  1430. static _clearAllState() {
  1431. const stateController = _CoreManager.default.getObjectStateController();
  1432. stateController.clearAllState();
  1433. }
  1434. /**
  1435. * Fetches the given list of Parse.Object.
  1436. * If any error is encountered, stops and calls the error handler.
  1437. *
  1438. * <pre>
  1439. * Parse.Object.fetchAll([object1, object2, ...])
  1440. * .then((list) => {
  1441. * // All the objects were fetched.
  1442. * }, (error) => {
  1443. * // An error occurred while fetching one of the objects.
  1444. * });
  1445. * </pre>
  1446. *
  1447. * @param {Array} list A list of <code>Parse.Object</code>.
  1448. * @param {object} options
  1449. * Valid options are:<ul>
  1450. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1451. * be used for this request.
  1452. * <li>sessionToken: A valid session token, used for making a request on
  1453. * behalf of a specific user.
  1454. * <li>include: The name(s) of the key(s) to include. Can be a string, an array of strings,
  1455. * or an array of array of strings.
  1456. * </ul>
  1457. * @static
  1458. * @returns {Parse.Object[]}
  1459. */
  1460. static fetchAll(list) {
  1461. let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  1462. const queryOptions = {};
  1463. if (options.hasOwnProperty('useMasterKey')) {
  1464. queryOptions.useMasterKey = options.useMasterKey;
  1465. }
  1466. if (options.hasOwnProperty('sessionToken')) {
  1467. queryOptions.sessionToken = options.sessionToken;
  1468. }
  1469. if (options.hasOwnProperty('include')) {
  1470. queryOptions.include = ParseObject.handleIncludeOptions(options);
  1471. }
  1472. return _CoreManager.default.getObjectController().fetch(list, true, queryOptions);
  1473. }
  1474. /**
  1475. * Fetches the given list of Parse.Object.
  1476. *
  1477. * Includes nested Parse.Objects for the provided key. You can use dot
  1478. * notation to specify which fields in the included object are also fetched.
  1479. *
  1480. * If any error is encountered, stops and calls the error handler.
  1481. *
  1482. * <pre>
  1483. * Parse.Object.fetchAllWithInclude([object1, object2, ...], [pointer1, pointer2, ...])
  1484. * .then((list) => {
  1485. * // All the objects were fetched.
  1486. * }, (error) => {
  1487. * // An error occurred while fetching one of the objects.
  1488. * });
  1489. * </pre>
  1490. *
  1491. * @param {Array} list A list of <code>Parse.Object</code>.
  1492. * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.
  1493. * @param {object} options
  1494. * Valid options are:<ul>
  1495. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1496. * be used for this request.
  1497. * <li>sessionToken: A valid session token, used for making a request on
  1498. * behalf of a specific user.
  1499. * </ul>
  1500. * @static
  1501. * @returns {Parse.Object[]}
  1502. */
  1503. static fetchAllWithInclude(list, keys, options) {
  1504. options = options || {};
  1505. options.include = keys;
  1506. return ParseObject.fetchAll(list, options);
  1507. }
  1508. /**
  1509. * Fetches the given list of Parse.Object if needed.
  1510. * If any error is encountered, stops and calls the error handler.
  1511. *
  1512. * Includes nested Parse.Objects for the provided key. You can use dot
  1513. * notation to specify which fields in the included object are also fetched.
  1514. *
  1515. * If any error is encountered, stops and calls the error handler.
  1516. *
  1517. * <pre>
  1518. * Parse.Object.fetchAllIfNeededWithInclude([object1, object2, ...], [pointer1, pointer2, ...])
  1519. * .then((list) => {
  1520. * // All the objects were fetched.
  1521. * }, (error) => {
  1522. * // An error occurred while fetching one of the objects.
  1523. * });
  1524. * </pre>
  1525. *
  1526. * @param {Array} list A list of <code>Parse.Object</code>.
  1527. * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.
  1528. * @param {object} options
  1529. * Valid options are:<ul>
  1530. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1531. * be used for this request.
  1532. * <li>sessionToken: A valid session token, used for making a request on
  1533. * behalf of a specific user.
  1534. * </ul>
  1535. * @static
  1536. * @returns {Parse.Object[]}
  1537. */
  1538. static fetchAllIfNeededWithInclude(list, keys, options) {
  1539. options = options || {};
  1540. options.include = keys;
  1541. return ParseObject.fetchAllIfNeeded(list, options);
  1542. }
  1543. /**
  1544. * Fetches the given list of Parse.Object if needed.
  1545. * If any error is encountered, stops and calls the error handler.
  1546. *
  1547. * <pre>
  1548. * Parse.Object.fetchAllIfNeeded([object1, ...])
  1549. * .then((list) => {
  1550. * // Objects were fetched and updated.
  1551. * }, (error) => {
  1552. * // An error occurred while fetching one of the objects.
  1553. * });
  1554. * </pre>
  1555. *
  1556. * @param {Array} list A list of <code>Parse.Object</code>.
  1557. * @param {object} options
  1558. * @static
  1559. * @returns {Parse.Object[]}
  1560. */
  1561. static fetchAllIfNeeded(list, options) {
  1562. options = options || {};
  1563. const queryOptions = {};
  1564. if (options.hasOwnProperty('useMasterKey')) {
  1565. queryOptions.useMasterKey = options.useMasterKey;
  1566. }
  1567. if (options.hasOwnProperty('sessionToken')) {
  1568. queryOptions.sessionToken = options.sessionToken;
  1569. }
  1570. if (options.hasOwnProperty('include')) {
  1571. queryOptions.include = ParseObject.handleIncludeOptions(options);
  1572. }
  1573. return _CoreManager.default.getObjectController().fetch(list, false, queryOptions);
  1574. }
  1575. static handleIncludeOptions(options) {
  1576. let include = [];
  1577. if ((0, _isArray.default)(options.include)) {
  1578. var _context3;
  1579. (0, _forEach.default)(_context3 = options.include).call(_context3, key => {
  1580. if ((0, _isArray.default)(key)) {
  1581. include = (0, _concat.default)(include).call(include, key);
  1582. } else {
  1583. include.push(key);
  1584. }
  1585. });
  1586. } else {
  1587. include.push(options.include);
  1588. }
  1589. return include;
  1590. }
  1591. /**
  1592. * Destroy the given list of models on the server if it was already persisted.
  1593. *
  1594. * <p>Unlike saveAll, if an error occurs while deleting an individual model,
  1595. * this method will continue trying to delete the rest of the models if
  1596. * possible, except in the case of a fatal error like a connection error.
  1597. *
  1598. * <p>In particular, the Parse.Error object returned in the case of error may
  1599. * be one of two types:
  1600. *
  1601. * <ul>
  1602. * <li>A Parse.Error.AGGREGATE_ERROR. This object's "errors" property is an
  1603. * array of other Parse.Error objects. Each error object in this array
  1604. * has an "object" property that references the object that could not be
  1605. * deleted (for instance, because that object could not be found).</li>
  1606. * <li>A non-aggregate Parse.Error. This indicates a serious error that
  1607. * caused the delete operation to be aborted partway through (for
  1608. * instance, a connection failure in the middle of the delete).</li>
  1609. * </ul>
  1610. *
  1611. * <pre>
  1612. * Parse.Object.destroyAll([object1, object2, ...])
  1613. * .then((list) => {
  1614. * // All the objects were deleted.
  1615. * }, (error) => {
  1616. * // An error occurred while deleting one or more of the objects.
  1617. * // If this is an aggregate error, then we can inspect each error
  1618. * // object individually to determine the reason why a particular
  1619. * // object was not deleted.
  1620. * if (error.code === Parse.Error.AGGREGATE_ERROR) {
  1621. * for (var i = 0; i < error.errors.length; i++) {
  1622. * console.log("Couldn't delete " + error.errors[i].object.id +
  1623. * "due to " + error.errors[i].message);
  1624. * }
  1625. * } else {
  1626. * console.log("Delete aborted because of " + error.message);
  1627. * }
  1628. * });
  1629. * </pre>
  1630. *
  1631. * @param {Array} list A list of <code>Parse.Object</code>.
  1632. * @param {object} options
  1633. * @static
  1634. * @returns {Promise} A promise that is fulfilled when the destroyAll
  1635. * completes.
  1636. */
  1637. static destroyAll(list) {
  1638. let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  1639. const destroyOptions = {};
  1640. if (options.hasOwnProperty('useMasterKey')) {
  1641. destroyOptions.useMasterKey = options.useMasterKey;
  1642. }
  1643. if (options.hasOwnProperty('sessionToken')) {
  1644. destroyOptions.sessionToken = options.sessionToken;
  1645. }
  1646. if (options.hasOwnProperty('batchSize') && typeof options.batchSize === 'number') {
  1647. destroyOptions.batchSize = options.batchSize;
  1648. }
  1649. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1650. destroyOptions.context = options.context;
  1651. }
  1652. return _CoreManager.default.getObjectController().destroy(list, destroyOptions);
  1653. }
  1654. /**
  1655. * Saves the given list of Parse.Object.
  1656. * If any error is encountered, stops and calls the error handler.
  1657. *
  1658. * <pre>
  1659. * Parse.Object.saveAll([object1, object2, ...])
  1660. * .then((list) => {
  1661. * // All the objects were saved.
  1662. * }, (error) => {
  1663. * // An error occurred while saving one of the objects.
  1664. * });
  1665. * </pre>
  1666. *
  1667. * @param {Array} list A list of <code>Parse.Object</code>.
  1668. * @param {object} options
  1669. * @static
  1670. * @returns {Parse.Object[]}
  1671. */
  1672. static saveAll(list) {
  1673. let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  1674. const saveOptions = {};
  1675. if (options.hasOwnProperty('useMasterKey')) {
  1676. saveOptions.useMasterKey = options.useMasterKey;
  1677. }
  1678. if (options.hasOwnProperty('sessionToken')) {
  1679. saveOptions.sessionToken = options.sessionToken;
  1680. }
  1681. if (options.hasOwnProperty('batchSize') && typeof options.batchSize === 'number') {
  1682. saveOptions.batchSize = options.batchSize;
  1683. }
  1684. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1685. saveOptions.context = options.context;
  1686. }
  1687. return _CoreManager.default.getObjectController().save(list, saveOptions);
  1688. }
  1689. /**
  1690. * Creates a reference to a subclass of Parse.Object with the given id. This
  1691. * does not exist on Parse.Object, only on subclasses.
  1692. *
  1693. * <p>A shortcut for: <pre>
  1694. * var Foo = Parse.Object.extend("Foo");
  1695. * var pointerToFoo = new Foo();
  1696. * pointerToFoo.id = "myObjectId";
  1697. * </pre>
  1698. *
  1699. * @param {string} id The ID of the object to create a reference to.
  1700. * @static
  1701. * @returns {Parse.Object} A Parse.Object reference.
  1702. */
  1703. static createWithoutData(id) {
  1704. const obj = new this();
  1705. obj.id = id;
  1706. return obj;
  1707. }
  1708. /**
  1709. * Creates a new instance of a Parse Object from a JSON representation.
  1710. *
  1711. * @param {object} json The JSON map of the Object's data
  1712. * @param {boolean} override In single instance mode, all old server data
  1713. * is overwritten if this is set to true
  1714. * @param {boolean} dirty Whether the Parse.Object should set JSON keys to dirty
  1715. * @static
  1716. * @returns {Parse.Object} A Parse.Object reference
  1717. */
  1718. static fromJSON(json, override, dirty) {
  1719. if (!json.className) {
  1720. throw new Error('Cannot create an object without a className');
  1721. }
  1722. const constructor = classMap[json.className];
  1723. const o = constructor ? new constructor(json.className) : new ParseObject(json.className);
  1724. const otherAttributes = {};
  1725. for (const attr in json) {
  1726. if (attr !== 'className' && attr !== '__type') {
  1727. otherAttributes[attr] = json[attr];
  1728. if (dirty) {
  1729. o.set(attr, json[attr]);
  1730. }
  1731. }
  1732. }
  1733. if (override) {
  1734. // id needs to be set before clearServerData can work
  1735. if (otherAttributes.objectId) {
  1736. o.id = otherAttributes.objectId;
  1737. }
  1738. let preserved = null;
  1739. if (typeof o._preserveFieldsOnFetch === 'function') {
  1740. preserved = o._preserveFieldsOnFetch();
  1741. }
  1742. o._clearServerData();
  1743. if (preserved) {
  1744. o._finishFetch(preserved);
  1745. }
  1746. }
  1747. o._finishFetch(otherAttributes);
  1748. if (json.objectId) {
  1749. o._setExisted(true);
  1750. }
  1751. return o;
  1752. }
  1753. /**
  1754. * Registers a subclass of Parse.Object with a specific class name.
  1755. * When objects of that class are retrieved from a query, they will be
  1756. * instantiated with this subclass.
  1757. * This is only necessary when using ES6 subclassing.
  1758. *
  1759. * @param {string} className The class name of the subclass
  1760. * @param {Function} constructor The subclass
  1761. */
  1762. static registerSubclass(className, constructor) {
  1763. if (typeof className !== 'string') {
  1764. throw new TypeError('The first argument must be a valid class name.');
  1765. }
  1766. if (typeof constructor === 'undefined') {
  1767. throw new TypeError('You must supply a subclass constructor.');
  1768. }
  1769. if (typeof constructor !== 'function') {
  1770. throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?');
  1771. }
  1772. classMap[className] = constructor;
  1773. if (!constructor.className) {
  1774. constructor.className = className;
  1775. }
  1776. }
  1777. /**
  1778. * Unegisters a subclass of Parse.Object with a specific class name.
  1779. *
  1780. * @param {string} className The class name of the subclass
  1781. */
  1782. static unregisterSubclass(className) {
  1783. if (typeof className !== 'string') {
  1784. throw new TypeError('The first argument must be a valid class name.');
  1785. }
  1786. delete classMap[className];
  1787. }
  1788. /**
  1789. * Creates a new subclass of Parse.Object for the given Parse class name.
  1790. *
  1791. * <p>Every extension of a Parse class will inherit from the most recent
  1792. * previous extension of that class. When a Parse.Object is automatically
  1793. * created by parsing JSON, it will use the most recent extension of that
  1794. * class.</p>
  1795. *
  1796. * <p>You should call either:<pre>
  1797. * var MyClass = Parse.Object.extend("MyClass", {
  1798. * <i>Instance methods</i>,
  1799. * initialize: function(attrs, options) {
  1800. * this.someInstanceProperty = [],
  1801. * <i>Other instance properties</i>
  1802. * }
  1803. * }, {
  1804. * <i>Class properties</i>
  1805. * });</pre>
  1806. * or, for Backbone compatibility:<pre>
  1807. * var MyClass = Parse.Object.extend({
  1808. * className: "MyClass",
  1809. * <i>Instance methods</i>,
  1810. * initialize: function(attrs, options) {
  1811. * this.someInstanceProperty = [],
  1812. * <i>Other instance properties</i>
  1813. * }
  1814. * }, {
  1815. * <i>Class properties</i>
  1816. * });</pre></p>
  1817. *
  1818. * @param {string} className The name of the Parse class backing this model.
  1819. * @param {object} [protoProps] Instance properties to add to instances of the
  1820. * class returned from this method.
  1821. * @param {object} [classProps] Class properties to add the class returned from
  1822. * this method.
  1823. * @returns {Parse.Object} A new subclass of Parse.Object.
  1824. */
  1825. static extend(className, protoProps, classProps) {
  1826. if (typeof className !== 'string') {
  1827. if (className && typeof className.className === 'string') {
  1828. return ParseObject.extend(className.className, className, protoProps);
  1829. } else {
  1830. throw new Error("Parse.Object.extend's first argument should be the className.");
  1831. }
  1832. }
  1833. let adjustedClassName = className;
  1834. if (adjustedClassName === 'User' && _CoreManager.default.get('PERFORM_USER_REWRITE')) {
  1835. adjustedClassName = '_User';
  1836. }
  1837. let parentProto = ParseObject.prototype;
  1838. if (this.hasOwnProperty('__super__') && this.__super__) {
  1839. parentProto = this.prototype;
  1840. }
  1841. let ParseObjectSubclass = function (attributes, options) {
  1842. this.className = adjustedClassName;
  1843. this._objCount = objectCount++;
  1844. // Enable legacy initializers
  1845. if (typeof this.initialize === 'function') {
  1846. this.initialize.apply(this, arguments);
  1847. }
  1848. if (this._initializers) {
  1849. for (const initializer of this._initializers) {
  1850. initializer.apply(this, arguments);
  1851. }
  1852. }
  1853. if (attributes && typeof attributes === 'object') {
  1854. if (!this.set(attributes || {}, options)) {
  1855. throw new Error("Can't create an invalid Parse Object");
  1856. }
  1857. }
  1858. };
  1859. if (classMap[adjustedClassName]) {
  1860. ParseObjectSubclass = classMap[adjustedClassName];
  1861. } else {
  1862. ParseObjectSubclass.extend = function (name, protoProps, classProps) {
  1863. if (typeof name === 'string') {
  1864. return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps);
  1865. }
  1866. return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps);
  1867. };
  1868. ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData;
  1869. ParseObjectSubclass.className = adjustedClassName;
  1870. ParseObjectSubclass.__super__ = parentProto;
  1871. ParseObjectSubclass.prototype = (0, _create.default)(parentProto, {
  1872. constructor: {
  1873. value: ParseObjectSubclass,
  1874. enumerable: false,
  1875. writable: true,
  1876. configurable: true
  1877. }
  1878. });
  1879. }
  1880. if (protoProps) {
  1881. for (const prop in protoProps) {
  1882. if (prop === 'initialize') {
  1883. (0, _defineProperty3.default)(ParseObjectSubclass.prototype, '_initializers', {
  1884. value: [...(ParseObjectSubclass.prototype._initializers || []), protoProps[prop]],
  1885. enumerable: false,
  1886. writable: true,
  1887. configurable: true
  1888. });
  1889. continue;
  1890. }
  1891. if (prop !== 'className') {
  1892. (0, _defineProperty3.default)(ParseObjectSubclass.prototype, prop, {
  1893. value: protoProps[prop],
  1894. enumerable: false,
  1895. writable: true,
  1896. configurable: true
  1897. });
  1898. }
  1899. }
  1900. }
  1901. if (classProps) {
  1902. for (const prop in classProps) {
  1903. if (prop !== 'className') {
  1904. (0, _defineProperty3.default)(ParseObjectSubclass, prop, {
  1905. value: classProps[prop],
  1906. enumerable: false,
  1907. writable: true,
  1908. configurable: true
  1909. });
  1910. }
  1911. }
  1912. }
  1913. classMap[adjustedClassName] = ParseObjectSubclass;
  1914. return ParseObjectSubclass;
  1915. }
  1916. /**
  1917. * Enable single instance objects, where any local objects with the same Id
  1918. * share the same attributes, and stay synchronized with each other.
  1919. * This is disabled by default in server environments, since it can lead to
  1920. * security issues.
  1921. *
  1922. * @static
  1923. */
  1924. static enableSingleInstance() {
  1925. singleInstance = true;
  1926. _CoreManager.default.setObjectStateController(SingleInstanceStateController);
  1927. }
  1928. /**
  1929. * Disable single instance objects, where any local objects with the same Id
  1930. * share the same attributes, and stay synchronized with each other.
  1931. * When disabled, you can have two instances of the same object in memory
  1932. * without them sharing attributes.
  1933. *
  1934. * @static
  1935. */
  1936. static disableSingleInstance() {
  1937. singleInstance = false;
  1938. _CoreManager.default.setObjectStateController(UniqueInstanceStateController);
  1939. }
  1940. /**
  1941. * Asynchronously stores the objects and every object they point to in the local datastore,
  1942. * recursively, using a default pin name: _default.
  1943. *
  1944. * If those other objects have not been fetched from Parse, they will not be stored.
  1945. * However, if they have changed data, all the changes will be retained.
  1946. *
  1947. * <pre>
  1948. * await Parse.Object.pinAll([...]);
  1949. * </pre>
  1950. *
  1951. * To retrieve object:
  1952. * <code>query.fromLocalDatastore()</code> or <code>query.fromPin()</code>
  1953. *
  1954. * @param {Array} objects A list of <code>Parse.Object</code>.
  1955. * @returns {Promise} A promise that is fulfilled when the pin completes.
  1956. * @static
  1957. */
  1958. static pinAll(objects) {
  1959. const localDatastore = _CoreManager.default.getLocalDatastore();
  1960. if (!localDatastore.isEnabled) {
  1961. return _promise.default.reject('Parse.enableLocalDatastore() must be called first');
  1962. }
  1963. return ParseObject.pinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, objects);
  1964. }
  1965. /**
  1966. * Asynchronously stores the objects and every object they point to in the local datastore, recursively.
  1967. *
  1968. * If those other objects have not been fetched from Parse, they will not be stored.
  1969. * However, if they have changed data, all the changes will be retained.
  1970. *
  1971. * <pre>
  1972. * await Parse.Object.pinAllWithName(name, [obj1, obj2, ...]);
  1973. * </pre>
  1974. *
  1975. * To retrieve object:
  1976. * <code>query.fromLocalDatastore()</code> or <code>query.fromPinWithName(name)</code>
  1977. *
  1978. * @param {string} name Name of Pin.
  1979. * @param {Array} objects A list of <code>Parse.Object</code>.
  1980. * @returns {Promise} A promise that is fulfilled when the pin completes.
  1981. * @static
  1982. */
  1983. static pinAllWithName(name, objects) {
  1984. const localDatastore = _CoreManager.default.getLocalDatastore();
  1985. if (!localDatastore.isEnabled) {
  1986. return _promise.default.reject('Parse.enableLocalDatastore() must be called first');
  1987. }
  1988. return localDatastore._handlePinAllWithName(name, objects);
  1989. }
  1990. /**
  1991. * Asynchronously removes the objects and every object they point to in the local datastore,
  1992. * recursively, using a default pin name: _default.
  1993. *
  1994. * <pre>
  1995. * await Parse.Object.unPinAll([...]);
  1996. * </pre>
  1997. *
  1998. * @param {Array} objects A list of <code>Parse.Object</code>.
  1999. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  2000. * @static
  2001. */
  2002. static unPinAll(objects) {
  2003. const localDatastore = _CoreManager.default.getLocalDatastore();
  2004. if (!localDatastore.isEnabled) {
  2005. return _promise.default.reject('Parse.enableLocalDatastore() must be called first');
  2006. }
  2007. return ParseObject.unPinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, objects);
  2008. }
  2009. /**
  2010. * Asynchronously removes the objects and every object they point to in the local datastore, recursively.
  2011. *
  2012. * <pre>
  2013. * await Parse.Object.unPinAllWithName(name, [obj1, obj2, ...]);
  2014. * </pre>
  2015. *
  2016. * @param {string} name Name of Pin.
  2017. * @param {Array} objects A list of <code>Parse.Object</code>.
  2018. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  2019. * @static
  2020. */
  2021. static unPinAllWithName(name, objects) {
  2022. const localDatastore = _CoreManager.default.getLocalDatastore();
  2023. if (!localDatastore.isEnabled) {
  2024. return _promise.default.reject('Parse.enableLocalDatastore() must be called first');
  2025. }
  2026. return localDatastore._handleUnPinAllWithName(name, objects);
  2027. }
  2028. /**
  2029. * Asynchronously removes all objects in the local datastore using a default pin name: _default.
  2030. *
  2031. * <pre>
  2032. * await Parse.Object.unPinAllObjects();
  2033. * </pre>
  2034. *
  2035. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  2036. * @static
  2037. */
  2038. static unPinAllObjects() {
  2039. const localDatastore = _CoreManager.default.getLocalDatastore();
  2040. if (!localDatastore.isEnabled) {
  2041. return _promise.default.reject('Parse.enableLocalDatastore() must be called first');
  2042. }
  2043. return localDatastore.unPinWithName(_LocalDatastoreUtils.DEFAULT_PIN);
  2044. }
  2045. /**
  2046. * Asynchronously removes all objects with the specified pin name.
  2047. * Deletes the pin name also.
  2048. *
  2049. * <pre>
  2050. * await Parse.Object.unPinAllObjectsWithName(name);
  2051. * </pre>
  2052. *
  2053. * @param {string} name Name of Pin.
  2054. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  2055. * @static
  2056. */
  2057. static unPinAllObjectsWithName(name) {
  2058. const localDatastore = _CoreManager.default.getLocalDatastore();
  2059. if (!localDatastore.isEnabled) {
  2060. return _promise.default.reject('Parse.enableLocalDatastore() must be called first');
  2061. }
  2062. return localDatastore.unPinWithName(_LocalDatastoreUtils.PIN_PREFIX + name);
  2063. }
  2064. }
  2065. const DefaultController = {
  2066. fetch(target, forceFetch, options) {
  2067. const localDatastore = _CoreManager.default.getLocalDatastore();
  2068. if ((0, _isArray.default)(target)) {
  2069. if (target.length < 1) {
  2070. return _promise.default.resolve([]);
  2071. }
  2072. const objs = [];
  2073. const ids = [];
  2074. let className = null;
  2075. const results = [];
  2076. let error = null;
  2077. (0, _forEach.default)(target).call(target, el => {
  2078. if (error) {
  2079. return;
  2080. }
  2081. if (!className) {
  2082. className = el.className;
  2083. }
  2084. if (className !== el.className) {
  2085. error = new _ParseError.default(_ParseError.default.INVALID_CLASS_NAME, 'All objects should be of the same class');
  2086. }
  2087. if (!el.id) {
  2088. error = new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'All objects must have an ID');
  2089. }
  2090. if (forceFetch || !el.isDataAvailable()) {
  2091. ids.push(el.id);
  2092. objs.push(el);
  2093. }
  2094. results.push(el);
  2095. });
  2096. if (error) {
  2097. return _promise.default.reject(error);
  2098. }
  2099. const ParseQuery = _CoreManager.default.getParseQuery();
  2100. const query = new ParseQuery(className);
  2101. query.containedIn('objectId', ids);
  2102. if (options && options.include) {
  2103. query.include(options.include);
  2104. }
  2105. query._limit = ids.length;
  2106. return (0, _find.default)(query).call(query, options).then(async objects => {
  2107. const idMap = {};
  2108. (0, _forEach.default)(objects).call(objects, o => {
  2109. idMap[o.id] = o;
  2110. });
  2111. for (let i = 0; i < objs.length; i++) {
  2112. const obj = objs[i];
  2113. if (!obj || !obj.id || !idMap[obj.id]) {
  2114. if (forceFetch) {
  2115. return _promise.default.reject(new _ParseError.default(_ParseError.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.'));
  2116. }
  2117. }
  2118. }
  2119. if (!singleInstance) {
  2120. // If single instance objects are disabled, we need to replace the
  2121. for (let i = 0; i < results.length; i++) {
  2122. const obj = results[i];
  2123. if (obj && obj.id && idMap[obj.id]) {
  2124. const id = obj.id;
  2125. obj._finishFetch(idMap[id].toJSON());
  2126. results[i] = idMap[id];
  2127. }
  2128. }
  2129. }
  2130. for (const object of results) {
  2131. await localDatastore._updateObjectIfPinned(object);
  2132. }
  2133. return _promise.default.resolve(results);
  2134. });
  2135. } else if (target instanceof ParseObject) {
  2136. if (!target.id) {
  2137. return _promise.default.reject(new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'Object does not have an ID'));
  2138. }
  2139. const RESTController = _CoreManager.default.getRESTController();
  2140. const params = {};
  2141. if (options && options.include) {
  2142. params.include = options.include.join();
  2143. }
  2144. return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), params, options).then(async response => {
  2145. target._clearPendingOps();
  2146. target._clearServerData();
  2147. target._finishFetch(response);
  2148. await localDatastore._updateObjectIfPinned(target);
  2149. return target;
  2150. });
  2151. }
  2152. return _promise.default.resolve(undefined);
  2153. },
  2154. async destroy(target, options) {
  2155. const batchSize = options && options.batchSize ? options.batchSize : _CoreManager.default.get('REQUEST_BATCH_SIZE');
  2156. const localDatastore = _CoreManager.default.getLocalDatastore();
  2157. const RESTController = _CoreManager.default.getRESTController();
  2158. if ((0, _isArray.default)(target)) {
  2159. if (target.length < 1) {
  2160. return _promise.default.resolve([]);
  2161. }
  2162. const batches = [[]];
  2163. (0, _forEach.default)(target).call(target, obj => {
  2164. if (!obj.id) {
  2165. return;
  2166. }
  2167. batches[batches.length - 1].push(obj);
  2168. if (batches[batches.length - 1].length >= batchSize) {
  2169. batches.push([]);
  2170. }
  2171. });
  2172. if (batches[batches.length - 1].length === 0) {
  2173. // If the last batch is empty, remove it
  2174. batches.pop();
  2175. }
  2176. let deleteCompleted = _promise.default.resolve();
  2177. const errors = [];
  2178. (0, _forEach.default)(batches).call(batches, batch => {
  2179. deleteCompleted = deleteCompleted.then(() => {
  2180. return RESTController.request('POST', 'batch', {
  2181. requests: (0, _map.default)(batch).call(batch, obj => {
  2182. return {
  2183. method: 'DELETE',
  2184. path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(),
  2185. body: {}
  2186. };
  2187. })
  2188. }, options).then(results => {
  2189. for (let i = 0; i < results.length; i++) {
  2190. if (results[i] && results[i].hasOwnProperty('error')) {
  2191. const err = new _ParseError.default(results[i].error.code, results[i].error.error);
  2192. err.object = batch[i];
  2193. errors.push(err);
  2194. }
  2195. }
  2196. });
  2197. });
  2198. });
  2199. return deleteCompleted.then(async () => {
  2200. if (errors.length) {
  2201. const aggregate = new _ParseError.default(_ParseError.default.AGGREGATE_ERROR);
  2202. aggregate.errors = errors;
  2203. return _promise.default.reject(aggregate);
  2204. }
  2205. for (const object of target) {
  2206. await localDatastore._destroyObjectIfPinned(object);
  2207. }
  2208. return _promise.default.resolve(target);
  2209. });
  2210. } else if (target instanceof ParseObject) {
  2211. return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(async () => {
  2212. await localDatastore._destroyObjectIfPinned(target);
  2213. return _promise.default.resolve(target);
  2214. });
  2215. }
  2216. return _promise.default.resolve(target);
  2217. },
  2218. save(target, options) {
  2219. const batchSize = options && options.batchSize ? options.batchSize : _CoreManager.default.get('REQUEST_BATCH_SIZE');
  2220. const localDatastore = _CoreManager.default.getLocalDatastore();
  2221. const mapIdForPin = {};
  2222. const RESTController = _CoreManager.default.getRESTController();
  2223. const stateController = _CoreManager.default.getObjectStateController();
  2224. const allowCustomObjectId = _CoreManager.default.get('ALLOW_CUSTOM_OBJECT_ID');
  2225. options = options || {};
  2226. options.returnStatus = options.returnStatus || true;
  2227. if ((0, _isArray.default)(target)) {
  2228. if (target.length < 1) {
  2229. return _promise.default.resolve([]);
  2230. }
  2231. let unsaved = (0, _concat.default)(target).call(target);
  2232. for (let i = 0; i < target.length; i++) {
  2233. const target_i = target[i];
  2234. if (target_i instanceof ParseObject) {
  2235. unsaved = (0, _concat.default)(unsaved).call(unsaved, (0, _unsavedChildren.default)(target_i, true));
  2236. }
  2237. }
  2238. unsaved = (0, _unique.default)(unsaved);
  2239. const filesSaved = [];
  2240. let pending = [];
  2241. (0, _forEach.default)(unsaved).call(unsaved, el => {
  2242. if (el instanceof _ParseFile.default) {
  2243. filesSaved.push(el.save(options));
  2244. } else if (el instanceof ParseObject) {
  2245. pending.push(el);
  2246. }
  2247. });
  2248. return _promise.default.all(filesSaved).then(() => {
  2249. let objectError = null;
  2250. return (0, _promiseUtils.continueWhile)(() => {
  2251. return pending.length > 0;
  2252. }, () => {
  2253. const batch = [];
  2254. const nextPending = [];
  2255. (0, _forEach.default)(pending).call(pending, el => {
  2256. if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(el, 'id') && !el.id) {
  2257. throw new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'objectId must not be empty or null');
  2258. }
  2259. if (batch.length < batchSize && (0, _canBeSerialized.default)(el)) {
  2260. batch.push(el);
  2261. } else {
  2262. nextPending.push(el);
  2263. }
  2264. });
  2265. pending = nextPending;
  2266. if (batch.length < 1) {
  2267. return _promise.default.reject(new _ParseError.default(_ParseError.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.'));
  2268. }
  2269. // Queue up tasks for each object in the batch.
  2270. // When every task is ready, the API request will execute
  2271. const batchReturned = (0, _promiseUtils.resolvingPromise)();
  2272. const batchReady = [];
  2273. const batchTasks = [];
  2274. (0, _forEach.default)(batch).call(batch, (obj, index) => {
  2275. const ready = (0, _promiseUtils.resolvingPromise)();
  2276. batchReady.push(ready);
  2277. stateController.pushPendingState(obj._getStateIdentifier());
  2278. batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () {
  2279. ready.resolve();
  2280. return batchReturned.then(responses => {
  2281. if (responses[index].hasOwnProperty('success')) {
  2282. const objectId = responses[index].success.objectId;
  2283. const status = responses[index]._status;
  2284. delete responses[index]._status;
  2285. delete responses[index]._headers;
  2286. delete responses[index]._xhr;
  2287. mapIdForPin[objectId] = obj._localId;
  2288. obj._handleSaveResponse(responses[index].success, status);
  2289. } else {
  2290. if (!objectError && responses[index].hasOwnProperty('error')) {
  2291. const serverError = responses[index].error;
  2292. objectError = new _ParseError.default(serverError.code, serverError.error);
  2293. // Cancel the rest of the save
  2294. pending = [];
  2295. }
  2296. obj._handleSaveError();
  2297. }
  2298. });
  2299. }));
  2300. });
  2301. (0, _promiseUtils.when)(batchReady).then(() => {
  2302. // Kick off the batch request
  2303. return RESTController.request('POST', 'batch', {
  2304. requests: (0, _map.default)(batch).call(batch, obj => {
  2305. const params = obj._getSaveParams();
  2306. params.path = getServerUrlPath() + params.path;
  2307. return params;
  2308. })
  2309. }, options);
  2310. }).then(batchReturned.resolve, error => {
  2311. batchReturned.reject(new _ParseError.default(_ParseError.default.INCORRECT_TYPE, error.message));
  2312. });
  2313. return (0, _promiseUtils.when)(batchTasks);
  2314. }).then(async () => {
  2315. if (objectError) {
  2316. return _promise.default.reject(objectError);
  2317. }
  2318. for (const object of target) {
  2319. // Make sure that it is a ParseObject before updating it into the localDataStore
  2320. if (object instanceof ParseObject) {
  2321. await localDatastore._updateLocalIdForObject(mapIdForPin[object.id], object);
  2322. await localDatastore._updateObjectIfPinned(object);
  2323. }
  2324. }
  2325. return _promise.default.resolve(target);
  2326. });
  2327. });
  2328. } else if (target instanceof ParseObject) {
  2329. if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(target, 'id') && !target.id) {
  2330. throw new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'objectId must not be empty or null');
  2331. }
  2332. // generate _localId in case if cascadeSave=false
  2333. target._getId();
  2334. const localId = target._localId;
  2335. // copying target lets guarantee the pointer isn't modified elsewhere
  2336. const targetCopy = target;
  2337. const task = function () {
  2338. const params = targetCopy._getSaveParams();
  2339. return RESTController.request(params.method, params.path, params.body, options).then(response => {
  2340. const status = response._status;
  2341. delete response._status;
  2342. delete response._headers;
  2343. delete response._xhr;
  2344. targetCopy._handleSaveResponse(response, status);
  2345. }, error => {
  2346. targetCopy._handleSaveError();
  2347. return _promise.default.reject(error);
  2348. });
  2349. };
  2350. stateController.pushPendingState(target._getStateIdentifier());
  2351. return stateController.enqueueTask(target._getStateIdentifier(), task).then(async () => {
  2352. await localDatastore._updateLocalIdForObject(localId, target);
  2353. await localDatastore._updateObjectIfPinned(target);
  2354. return target;
  2355. }, error => {
  2356. return _promise.default.reject(error);
  2357. });
  2358. }
  2359. return _promise.default.resolve(undefined);
  2360. }
  2361. };
  2362. _CoreManager.default.setParseObject(ParseObject);
  2363. _CoreManager.default.setObjectController(DefaultController);
  2364. var _default = exports.default = ParseObject;