diff --git a/.eslintrc.json b/.eslintrc.json index 25d55dca6..757b9a816 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -40,6 +40,7 @@ "require-atomic-updates": "off", "prefer-spread": "off", "prefer-rest-params": "off", + "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/triple-slash-reference": "off", "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-explicit-any": "off", diff --git a/src/Analytics.js b/src/Analytics.ts similarity index 92% rename from src/Analytics.js rename to src/Analytics.ts index 32f4cf82f..9a38f5912 100644 --- a/src/Analytics.js +++ b/src/Analytics.ts @@ -1,7 +1,3 @@ -/** - * @flow - */ - import CoreManager from './CoreManager'; /** @@ -44,7 +40,7 @@ import CoreManager from './CoreManager'; * @returns {Promise} A promise that is resolved when the round-trip * to the server completes. */ -export function track(name: string, dimensions: { [key: string]: string }): Promise { +export function track(name: string, dimensions: { [key: string]: string }): Promise { name = name || ''; name = name.replace(/^\s*/, ''); name = name.replace(/\s*$/, ''); @@ -62,10 +58,10 @@ export function track(name: string, dimensions: { [key: string]: string }): Prom } const DefaultController = { - track(name, dimensions) { + track(name: string, dimensions: { [key: string]: string }) { const path = 'events/' + name; const RESTController = CoreManager.getRESTController(); - return RESTController.request('POST', path, { dimensions: dimensions }); + return RESTController.request('POST', path, { dimensions }); }, }; diff --git a/src/AnonymousUtils.js b/src/AnonymousUtils.ts similarity index 98% rename from src/AnonymousUtils.js rename to src/AnonymousUtils.ts index 2472f8b7d..99584c43f 100644 --- a/src/AnonymousUtils.js +++ b/src/AnonymousUtils.ts @@ -1,10 +1,6 @@ -/** - * @flow-weak - */ - import ParseUser from './ParseUser'; import type { RequestOptions } from './RESTController'; -const uuidv4 = require('./uuid'); +import uuidv4 from './uuid'; let registered = false; diff --git a/src/CloudCode.js b/src/CloudCode.ts similarity index 100% rename from src/CloudCode.js rename to src/CloudCode.ts diff --git a/src/CryptoController.js b/src/CryptoController.ts similarity index 75% rename from src/CryptoController.js rename to src/CryptoController.ts index 49b7fc725..211c5d283 100644 --- a/src/CryptoController.js +++ b/src/CryptoController.ts @@ -1,5 +1,5 @@ -let AES; -let ENC; +let AES: any; +let ENC: any; if (process.env.PARSE_BUILD === 'react-native') { const CryptoJS = require('react-native-crypto-js'); @@ -11,15 +11,16 @@ if (process.env.PARSE_BUILD === 'react-native') { } const CryptoController = { - encrypt(obj: any, secretKey: string): ?string { + encrypt(obj: any, secretKey: string): string { const encrypted = AES.encrypt(JSON.stringify(obj), secretKey); return encrypted.toString(); }, - decrypt(encryptedText: string, secretKey: string): ?string { + decrypt(encryptedText: string, secretKey: string): string { const decryptedStr = AES.decrypt(encryptedText, secretKey).toString(ENC); return decryptedStr; }, }; module.exports = CryptoController; +export default CryptoController; diff --git a/src/EventEmitter.js b/src/EventEmitter.ts similarity index 91% rename from src/EventEmitter.js rename to src/EventEmitter.ts index 1f1bcbd00..4f965641f 100644 --- a/src/EventEmitter.js +++ b/src/EventEmitter.ts @@ -2,7 +2,7 @@ * This is a simple wrapper to unify EventEmitter implementations across platforms. */ -let EventEmitter; +let EventEmitter: any; try { if (process.env.PARSE_BUILD === 'react-native') { @@ -18,3 +18,4 @@ try { // EventEmitter unavailable } module.exports = EventEmitter; +export default EventEmitter; diff --git a/src/LiveQueryClient.ts b/src/LiveQueryClient.ts index 469453ad3..b497fb4e9 100644 --- a/src/LiveQueryClient.ts +++ b/src/LiveQueryClient.ts @@ -72,9 +72,8 @@ const generateInterval = k => { * We expose three events to help you monitor the status of the LiveQueryClient. * *
- * let Parse = require('parse/node');
- * let LiveQueryClient = Parse.LiveQueryClient;
- * let client = new LiveQueryClient({
+ * const LiveQueryClient = Parse.LiveQueryClient;
+ * const client = new LiveQueryClient({
  *   applicationId: '',
  *   serverURL: '',
  *   javascriptKey: '',
diff --git a/src/LocalDatastore.ts b/src/LocalDatastore.ts
index 72556df64..4720d7f02 100644
--- a/src/LocalDatastore.ts
+++ b/src/LocalDatastore.ts
@@ -1,5 +1,5 @@
 import CoreManager from './CoreManager';
-
+import LocalDatastoreController from './LocalDatastoreController';
 import type ParseObject from './ParseObject';
 import ParseQuery from './ParseQuery';
 import { DEFAULT_PIN, PIN_PREFIX, OBJECT_PREFIX } from './LocalDatastoreUtils';
@@ -390,9 +390,5 @@ const LocalDatastore = {
 module.exports = LocalDatastore;
 export default LocalDatastore;
 
-if (process.env.PARSE_BUILD === 'react-native') {
-  CoreManager.setLocalDatastoreController(require('./LocalDatastoreController.react-native'));
-} else {
-  CoreManager.setLocalDatastoreController(require('./LocalDatastoreController'));
-}
+CoreManager.setLocalDatastoreController(LocalDatastoreController);
 CoreManager.setLocalDatastore(LocalDatastore);
diff --git a/src/LocalDatastoreController.js b/src/LocalDatastoreController.js
deleted file mode 100644
index df2e666d9..000000000
--- a/src/LocalDatastoreController.js
+++ /dev/null
@@ -1,5 +0,0 @@
-if (process.env.PARSE_BUILD === 'react-native') {
-  module.exports = require('./LocalDatastoreController.react-native');
-} else {
-  module.exports = require('./LocalDatastoreController.default');
-}
diff --git a/src/LocalDatastoreController.react-native.ts b/src/LocalDatastoreController.react-native.ts
index 66324465f..706323fa4 100644
--- a/src/LocalDatastoreController.react-native.ts
+++ b/src/LocalDatastoreController.react-native.ts
@@ -1,5 +1,5 @@
 import { isLocalDatastoreKey } from './LocalDatastoreUtils';
-const RNStorage = require('./StorageController.react-native');
+import RNStorage from './StorageController.react-native';
 
 const LocalDatastoreController = {
   async fromPinWithName(name: string): Promise> {
@@ -35,7 +35,7 @@ const LocalDatastoreController = {
       }
     }
     const LDS = {};
-    let results: any[] = [];
+    let results: any = [];
     try {
       results = await RNStorage.multiGet(batch);
     } catch (error) {
@@ -57,8 +57,8 @@ const LocalDatastoreController = {
   async getRawStorage(): Promise {
     const keys = await RNStorage.getAllKeysAsync();
     const storage = {};
-    const results = await RNStorage.multiGet(keys);
-    results.map(pair => {
+    const results = await RNStorage.multiGet(keys as string[]);
+    results!.map(pair => {
       const [key, value] = pair;
       storage[key] = value;
     });
@@ -74,7 +74,7 @@ const LocalDatastoreController = {
         batch.push(key);
       }
     }
-    return RNStorage.multiRemove(batch).catch(error =>
+    await RNStorage.multiRemove(batch).catch(error =>
       console.error('Error clearing local datastore: ', error)
     );
   },
diff --git a/src/LocalDatastoreController.ts b/src/LocalDatastoreController.ts
new file mode 100644
index 000000000..f0acfea5b
--- /dev/null
+++ b/src/LocalDatastoreController.ts
@@ -0,0 +1,10 @@
+import RNLocalDatastoreController from './LocalDatastoreController.react-native';
+import DefaultLocalDatastoreController from './LocalDatastoreController.default';
+
+let LocalDatastoreController: any = DefaultLocalDatastoreController;
+
+if (process.env.PARSE_BUILD === 'react-native') {
+  LocalDatastoreController = RNLocalDatastoreController;
+}
+module.exports = LocalDatastoreController;
+export default LocalDatastoreController;
diff --git a/src/OfflineQuery.js b/src/OfflineQuery.ts
similarity index 98%
rename from src/OfflineQuery.js
rename to src/OfflineQuery.ts
index c38e04227..603226212 100644
--- a/src/OfflineQuery.js
+++ b/src/OfflineQuery.ts
@@ -1,8 +1,8 @@
-const equalObjects = require('./equals').default;
-const decode = require('./decode').default;
-const ParseError = require('./ParseError').default;
-const ParsePolygon = require('./ParsePolygon').default;
-const ParseGeoPoint = require('./ParseGeoPoint').default;
+import equalObjects from './equals';
+import decode from './decode';
+import ParseError from './ParseError';
+import ParsePolygon from './ParsePolygon';
+import ParseGeoPoint from './ParseGeoPoint';
 /**
  * contains -- Determines if an object is contained in a list with special handling for Parse pointers.
  *
@@ -333,7 +333,9 @@ function matchesKeyConstraints(className, object, objects, key, constraints) {
     if (
       toString.call(compareTo) === '[object Date]' ||
       (typeof compareTo === 'string' &&
+        // @ts-ignore
         new Date(compareTo) !== 'Invalid Date' &&
+        // @ts-ignore
         !isNaN(new Date(compareTo)))
     ) {
       object[key] = new Date(object[key].iso ? object[key].iso : object[key]);
@@ -594,3 +596,4 @@ const OfflineQuery = {
 };
 
 module.exports = OfflineQuery;
+export default OfflineQuery;
diff --git a/src/Parse.ts b/src/Parse.ts
index a93779b65..142c3fe04 100644
--- a/src/Parse.ts
+++ b/src/Parse.ts
@@ -17,6 +17,7 @@ import Config from './ParseConfig';
 import ParseError from './ParseError';
 import FacebookUtils from './FacebookUtils';
 import File from './ParseFile';
+import * as Hooks from './ParseHooks';
 import GeoPoint from './ParseGeoPoint';
 import Polygon from './ParsePolygon';
 import Installation from './ParseInstallation';
@@ -474,7 +475,7 @@ if (process.env.PARSE_BUILD === 'node') {
   Parse.Cloud.useMasterKey = function () {
     CoreManager.set('USE_MASTER_KEY', true);
   };
-  Parse.Hooks = require('./ParseHooks');
+  Parse.Hooks = Hooks;
 }
 
 // For legacy requires, of the form `var Parse = require('parse').Parse`
diff --git a/src/ParseFile.ts b/src/ParseFile.ts
index b012dd398..3f656b84e 100644
--- a/src/ParseFile.ts
+++ b/src/ParseFile.ts
@@ -2,13 +2,14 @@
 import CoreManager from './CoreManager';
 import type { FullOptions } from './RESTController';
 import ParseError from './ParseError';
+import XhrWeapp from './Xhr.weapp';
 
-let XHR = null;
+let XHR: any = null;
 if (typeof XMLHttpRequest !== 'undefined') {
   XHR = XMLHttpRequest;
 }
 if (process.env.PARSE_BUILD === 'weapp') {
-  XHR = require('./Xhr.weapp');
+  XHR = XhrWeapp;
 }
 
 type Base64 = { base64: string };
@@ -240,7 +241,7 @@ class ParseFile {
    * 
    * @returns {Promise | undefined} Promise that is resolved when the save finishes.
    */
-  save(options?: FileSaveOptions): Promise | undefined {
+  save(options?: FileSaveOptions & { requestTask?: any }): Promise | undefined {
     options = options || {};
     options.requestTask = task => (this._requestTask = task);
     options.metadata = this._metadata;
diff --git a/src/ParseUser.ts b/src/ParseUser.ts
index f26566813..8c7ed5bd0 100644
--- a/src/ParseUser.ts
+++ b/src/ParseUser.ts
@@ -1059,7 +1059,7 @@ const DefaultController = {
       );
     }
     const path = Storage.generatePath(CURRENT_USER_KEY);
-    let userData = Storage.getItem(path);
+    let userData: any = Storage.getItem(path);
     currentUserCacheMatchesDisk = true;
     if (!userData) {
       currentUserCache = null;
@@ -1097,7 +1097,7 @@ const DefaultController = {
       return Promise.resolve(null);
     }
     const path = Storage.generatePath(CURRENT_USER_KEY);
-    return Storage.getItemAsync(path).then(userData => {
+    return Storage.getItemAsync(path).then((userData: any) => {
       currentUserCacheMatchesDisk = true;
       if (!userData) {
         currentUserCache = null;
diff --git a/src/Push.ts b/src/Push.ts
index 38b995fbb..0c02f065f 100644
--- a/src/Push.ts
+++ b/src/Push.ts
@@ -65,7 +65,7 @@ export function send(data: PushData, options: FullOptions = {}): Promise
     throw new Error('expiration_time and expiration_interval cannot both be set.');
   }
 
-  const pushOptions = { useMasterKey: true };
+  const pushOptions: FullOptions = { useMasterKey: true };
   if (options.hasOwnProperty('useMasterKey')) {
     pushOptions.useMasterKey = options.useMasterKey;
   }
@@ -90,7 +90,7 @@ export function getPushStatus(
   pushStatusId: string,
   options: FullOptions = {}
 ): Promise {
-  const pushOptions = { useMasterKey: true };
+  const pushOptions: FullOptions = { useMasterKey: true };
   if (options.hasOwnProperty('useMasterKey')) {
     pushOptions.useMasterKey = options.useMasterKey;
   }
@@ -99,8 +99,8 @@ export function getPushStatus(
 }
 
 const DefaultController = {
-  async send(data: PushData, options?: FullOptions) {
-    options.returnStatus = true;
+  async send(data: PushData, options?: FullOptions & { returnStatus?: boolean }) {
+    options!.returnStatus = true;
     const response = await CoreManager.getRESTController().request('POST', 'push', data, options);
     return response._headers?.['X-Parse-Push-Status-Id'];
   },
diff --git a/src/RESTController.js b/src/RESTController.ts
similarity index 94%
rename from src/RESTController.js
rename to src/RESTController.ts
index 95ad69bd0..1c70b156e 100644
--- a/src/RESTController.js
+++ b/src/RESTController.ts
@@ -1,12 +1,9 @@
-/**
- * @flow
- */
 /* global XMLHttpRequest, XDomainRequest */
-const uuidv4 = require('./uuid');
-
+import uuidv4 from './uuid';
 import CoreManager from './CoreManager';
 import ParseError from './ParseError';
 import { resolvingPromise } from './promiseUtils';
+import XhrWeapp from './Xhr.weapp';
 
 export type RequestOptions = {
   useMasterKey?: boolean;
@@ -31,7 +28,19 @@ export type FullOptions = {
   usePost?: boolean;
 };
 
-let XHR = null;
+type PayloadType = {
+  _context?: any;
+  _method?: string;
+  _ApplicationId: string;
+  _JavaScriptKey?: string;
+  _ClientVersion: string;
+  _MasterKey?: string;
+  _RevocableSession?: string;
+  _InstallationId?: string;
+  _SessionToken?: string;
+};
+
+let XHR: any = null;
 if (typeof XMLHttpRequest !== 'undefined') {
   XHR = XMLHttpRequest;
 }
@@ -39,16 +48,18 @@ if (process.env.PARSE_BUILD === 'node') {
   XHR = require('xmlhttprequest').XMLHttpRequest;
 }
 if (process.env.PARSE_BUILD === 'weapp') {
-  XHR = require('./Xhr.weapp');
+  XHR = XhrWeapp;
 }
 
 let useXDomainRequest = false;
+// @ts-ignore
 if (typeof XDomainRequest !== 'undefined' && !('withCredentials' in new XMLHttpRequest())) {
   useXDomainRequest = true;
 }
 
 function ajaxIE9(method: string, url: string, data: any, headers?: any, options?: FullOptions) {
   return new Promise((resolve, reject) => {
+    // @ts-ignore
     const xdr = new XDomainRequest();
     xdr.onload = function () {
       let response;
@@ -78,7 +89,9 @@ function ajaxIE9(method: string, url: string, data: any, headers?: any, options?
     };
     xdr.open(method, url);
     xdr.send(data);
+    // @ts-ignore
     if (options && typeof options.requestTask === 'function') {
+      // @ts-ignore
       options.requestTask(xdr);
     }
   });
@@ -203,8 +216,9 @@ const RESTController = {
         });
       };
       xhr.send(data);
-
+      // @ts-ignore
       if (options && typeof options.requestTask === 'function') {
+        // @ts-ignore
         options.requestTask(xhr);
       }
     };
@@ -221,7 +235,7 @@ const RESTController = {
     }
     url += path;
 
-    const payload = {};
+    const payload: Partial = {};
     if (data && typeof data === 'object') {
       for (const k in data) {
         payload[k] = data[k];
@@ -264,7 +278,7 @@ const RESTController = {
     }
 
     const installationId = options.installationId;
-    let installationIdPromise;
+    let installationIdPromise: Promise;
     if (installationId && typeof installationId === 'string') {
       installationIdPromise = Promise.resolve(installationId);
     } else {
@@ -307,7 +321,7 @@ const RESTController = {
       .catch(RESTController.handleError);
   },
 
-  handleError(response) {
+  handleError(response: any) {
     // Transform the error into an instance of ParseError by trying to parse
     // the error string as JSON
     let error;
@@ -342,3 +356,4 @@ const RESTController = {
 };
 
 module.exports = RESTController;
+export default RESTController;
diff --git a/src/Socket.weapp.js b/src/Socket.weapp.ts
similarity index 60%
rename from src/Socket.weapp.js
rename to src/Socket.weapp.ts
index 7cebe889e..768a48060 100644
--- a/src/Socket.weapp.js
+++ b/src/Socket.weapp.ts
@@ -1,36 +1,53 @@
-module.exports = class SocketWeapp {
+class SocketWeapp {
+  onopen: () => void;
+  onmessage: () => void;
+  onclose: () => void;
+  onerror: () => void;
+
   constructor(serverURL) {
     this.onopen = () => {};
     this.onmessage = () => {};
     this.onclose = () => {};
     this.onerror = () => {};
 
+    // @ts-ignore
     wx.onSocketOpen(() => {
       this.onopen();
     });
 
+    // @ts-ignore
     wx.onSocketMessage(msg => {
+      // @ts-ignore
       this.onmessage(msg);
     });
 
+    // @ts-ignore
     wx.onSocketClose(event => {
+      // @ts-ignore
       this.onclose(event);
     });
 
+    // @ts-ignore
     wx.onSocketError(error => {
+      // @ts-ignore
       this.onerror(error);
     });
 
+    // @ts-ignore
     wx.connectSocket({
       url: serverURL,
     });
   }
 
   send(data) {
+    // @ts-ignore
     wx.sendSocketMessage({ data });
   }
 
   close() {
+    // @ts-ignore
     wx.closeSocket();
   }
-};
+}
+module.exports = SocketWeapp;
+export default SocketWeapp;
diff --git a/src/Storage.js b/src/Storage.ts
similarity index 92%
rename from src/Storage.js
rename to src/Storage.ts
index 8f7599ea5..33261d52a 100644
--- a/src/Storage.js
+++ b/src/Storage.ts
@@ -1,7 +1,3 @@
-/**
- * @flow
- */
-
 import CoreManager from './CoreManager';
 
 const Storage = {
@@ -10,7 +6,7 @@ const Storage = {
     return !!controller.async;
   },
 
-  getItem(path: string): ?string {
+  getItem(path: string): string | null {
     const controller = CoreManager.getStorageController();
     if (controller.async === 1) {
       throw new Error('Synchronous storage is not supported by the current storage controller');
@@ -18,7 +14,7 @@ const Storage = {
     return controller.getItem(path);
   },
 
-  getItemAsync(path: string): Promise {
+  getItemAsync(path: string): Promise {
     const controller = CoreManager.getStorageController();
     if (controller.async === 1) {
       return controller.getItemAsync(path);
@@ -63,15 +59,15 @@ const Storage = {
     if (controller.async === 1) {
       throw new Error('Synchronous storage is not supported by the current storage controller');
     }
-    return controller.getAllKeys();
+    return controller.getAllKeys!();
   },
 
   getAllKeysAsync(): Promise> {
     const controller = CoreManager.getStorageController();
     if (controller.async === 1) {
-      return controller.getAllKeysAsync();
+      return controller.getAllKeysAsync!();
     }
-    return Promise.resolve(controller.getAllKeys());
+    return Promise.resolve(controller.getAllKeys!());
   },
 
   generatePath(path: string): string {
diff --git a/src/StorageController.browser.js b/src/StorageController.browser.ts
similarity index 79%
rename from src/StorageController.browser.js
rename to src/StorageController.browser.ts
index 0fdcd37b7..924b5a167 100644
--- a/src/StorageController.browser.js
+++ b/src/StorageController.browser.ts
@@ -1,13 +1,9 @@
-/**
- * @flow
- * @private
- */
 /* global localStorage */
 
 const StorageController = {
   async: 0,
 
-  getItem(path: string): ?string {
+  getItem(path: string): string | null {
     return localStorage.getItem(path);
   },
 
@@ -25,9 +21,9 @@ const StorageController = {
   },
 
   getAllKeys() {
-    const keys = [];
+    const keys: string[] = [];
     for (let i = 0; i < localStorage.length; i += 1) {
-      keys.push(localStorage.key(i));
+      keys.push(localStorage.key(i) as string);
     }
     return keys;
   },
@@ -38,3 +34,4 @@ const StorageController = {
 };
 
 module.exports = StorageController;
+export default StorageController;
diff --git a/src/StorageController.default.js b/src/StorageController.default.ts
similarity index 88%
rename from src/StorageController.default.js
rename to src/StorageController.default.ts
index cb21dd54f..f9a6e65f0 100644
--- a/src/StorageController.default.js
+++ b/src/StorageController.default.ts
@@ -1,14 +1,9 @@
-/**
- * @flow
- * @private
- */
-
 // When there is no native storage interface, we default to an in-memory map
 const memMap = {};
 const StorageController = {
   async: 0,
 
-  getItem(path: string): ?string {
+  getItem(path: string): string | null {
     if (memMap.hasOwnProperty(path)) {
       return memMap[path];
     }
@@ -37,3 +32,4 @@ const StorageController = {
 };
 
 module.exports = StorageController;
+export default StorageController;
diff --git a/src/StorageController.js b/src/StorageController.js
deleted file mode 100644
index 33508d75b..000000000
--- a/src/StorageController.js
+++ /dev/null
@@ -1,9 +0,0 @@
-if (process.env.PARSE_BUILD === 'react-native') {
-  module.exports = require('./StorageController.react-native');
-} else if (process.env.PARSE_BUILD === 'browser') {
-  module.exports = require('./StorageController.browser');
-} else if (process.env.PARSE_BUILD === 'weapp') {
-  module.exports = require('./StorageController.weapp');
-} else {
-  module.exports = require('./StorageController.default');
-}
diff --git a/src/StorageController.react-native.js b/src/StorageController.react-native.ts
similarity index 51%
rename from src/StorageController.react-native.js
rename to src/StorageController.react-native.ts
index e3ee63dce..23658a11c 100644
--- a/src/StorageController.react-native.js
+++ b/src/StorageController.react-native.ts
@@ -1,39 +1,35 @@
-/**
- * @flow
- * @private
- */
 import CoreManager from './CoreManager';
 
 const StorageController = {
   async: 1,
 
-  getItemAsync(path: string): Promise {
+  getItemAsync(path: string): Promise {
     return new Promise((resolve, reject) => {
-      CoreManager.getAsyncStorage().getItem(path, (err, value) => {
+      CoreManager.getAsyncStorage()!.getItem(path, (err, value) => {
         if (err) {
           reject(err);
         } else {
-          resolve(value);
+          resolve(value || null);
         }
       });
     });
   },
 
-  setItemAsync(path: string, value: string): Promise {
+  setItemAsync(path: string, value: string): Promise {
     return new Promise((resolve, reject) => {
-      CoreManager.getAsyncStorage().setItem(path, value, (err, value) => {
+      CoreManager.getAsyncStorage()!.setItem(path, value, err => {
         if (err) {
           reject(err);
         } else {
-          resolve(value);
+          resolve();
         }
       });
     });
   },
 
-  removeItemAsync(path: string): Promise {
+  removeItemAsync(path: string): Promise {
     return new Promise((resolve, reject) => {
-      CoreManager.getAsyncStorage().removeItem(path, err => {
+      CoreManager.getAsyncStorage()!.removeItem(path, err => {
         if (err) {
           reject(err);
         } else {
@@ -43,33 +39,33 @@ const StorageController = {
     });
   },
 
-  getAllKeysAsync(): Promise {
+  getAllKeysAsync(): Promise {
     return new Promise((resolve, reject) => {
-      CoreManager.getAsyncStorage().getAllKeys((err, keys) => {
+      CoreManager.getAsyncStorage()!.getAllKeys((err, keys) => {
         if (err) {
           reject(err);
         } else {
-          resolve(keys);
+          resolve(keys || []);
         }
       });
     });
   },
 
-  multiGet(keys: Array): Promise>> {
+  multiGet(keys: Array): Promise {
     return new Promise((resolve, reject) => {
-      CoreManager.getAsyncStorage().multiGet(keys, (err, result) => {
+      CoreManager.getAsyncStorage()!.multiGet(keys, (err, result) => {
         if (err) {
           reject(err);
         } else {
-          resolve(result);
+          resolve(result || null);
         }
       });
     });
   },
 
-  multiRemove(keys: Array): Promise {
+  multiRemove(keys: Array): Promise> {
     return new Promise((resolve, reject) => {
-      CoreManager.getAsyncStorage().multiRemove(keys, err => {
+      CoreManager.getAsyncStorage()!.multiRemove(keys, err => {
         if (err) {
           reject(err);
         } else {
@@ -80,8 +76,9 @@ const StorageController = {
   },
 
   clear() {
-    return CoreManager.getAsyncStorage().clear();
+    return CoreManager.getAsyncStorage()!.clear();
   },
 };
 
 module.exports = StorageController;
+export default StorageController;
diff --git a/src/StorageController.ts b/src/StorageController.ts
new file mode 100644
index 000000000..1c26076a0
--- /dev/null
+++ b/src/StorageController.ts
@@ -0,0 +1,16 @@
+import RNStorageController from './StorageController.react-native';
+import BrowserStorageController from './StorageController.browser';
+import WeappStorageController from './StorageController.weapp';
+import DefaultStorageController from './StorageController.default';
+
+let StorageController: any = DefaultStorageController;
+
+if (process.env.PARSE_BUILD === 'react-native') {
+  StorageController = RNStorageController;
+} else if (process.env.PARSE_BUILD === 'browser') {
+  StorageController = BrowserStorageController;
+} else if (process.env.PARSE_BUILD === 'weapp') {
+  StorageController = WeappStorageController;
+}
+module.exports = StorageController;
+export default StorageController;
diff --git a/src/StorageController.weapp.js b/src/StorageController.weapp.ts
similarity index 73%
rename from src/StorageController.weapp.js
rename to src/StorageController.weapp.ts
index 321172c4e..6de4ddd00 100644
--- a/src/StorageController.weapp.js
+++ b/src/StorageController.weapp.ts
@@ -1,17 +1,14 @@
-/**
- * @flow
- * @private
- */
-
 const StorageController = {
   async: 0,
 
-  getItem(path: string): ?string {
+  getItem(path: string): string | null {
+    // @ts-ignore
     return wx.getStorageSync(path);
   },
 
   setItem(path: string, value: string) {
     try {
+      // @ts-ignore
       wx.setStorageSync(path, value);
     } catch (e) {
       // Quota exceeded
@@ -19,17 +16,21 @@ const StorageController = {
   },
 
   removeItem(path: string) {
+    // @ts-ignore
     wx.removeStorageSync(path);
   },
 
   getAllKeys() {
+    // @ts-ignore
     const res = wx.getStorageInfoSync();
     return res.keys;
   },
 
   clear() {
+    // @ts-ignore
     wx.clearStorageSync();
   },
 };
 
 module.exports = StorageController;
+export default StorageController;
diff --git a/src/WebSocketController.js b/src/WebSocketController.ts
similarity index 75%
rename from src/WebSocketController.js
rename to src/WebSocketController.ts
index 5a95582c5..e5daa477a 100644
--- a/src/WebSocketController.js
+++ b/src/WebSocketController.ts
@@ -1,4 +1,6 @@
 /* global WebSocket */
+import ws from 'ws';
+import SocketWeapp from './Socket.weapp';
 
 let WebSocketController;
 
@@ -7,9 +9,9 @@ try {
     WebSocketController =
       typeof WebSocket === 'function' || typeof WebSocket === 'object' ? WebSocket : null;
   } else if (process.env.PARSE_BUILD === 'node') {
-    WebSocketController = require('ws');
+    WebSocketController = ws;
   } else if (process.env.PARSE_BUILD === 'weapp') {
-    WebSocketController = require('./Socket.weapp');
+    WebSocketController = SocketWeapp;
   } else if (process.env.PARSE_BUILD === 'react-native') {
     WebSocketController = WebSocket;
   }
@@ -17,3 +19,4 @@ try {
   // WebSocket unavailable
 }
 module.exports = WebSocketController;
+export default WebSocketController;
diff --git a/src/Xhr.weapp.js b/src/Xhr.weapp.ts
similarity index 78%
rename from src/Xhr.weapp.js
rename to src/Xhr.weapp.ts
index cb9f90121..b001c9e9b 100644
--- a/src/Xhr.weapp.js
+++ b/src/Xhr.weapp.ts
@@ -1,4 +1,24 @@
-module.exports = class XhrWeapp {
+class XhrWeapp {
+  UNSENT: number;
+  OPENED: number;
+  HEADERS_RECEIVED: number;
+  LOADING: number;
+  DONE: number;
+  header: any;
+  readyState: any;
+  status: number;
+  response: string | undefined;
+  responseType: string;
+  responseText: string;
+  responseHeader: any;
+  method: string;
+  url: string;
+  onabort: () => void;
+  onprogress: () => void;
+  onerror: () => void;
+  onreadystatechange: () => void;
+  requestTask: any;
+
   constructor() {
     this.UNSENT = 0;
     this.OPENED = 1;
@@ -55,6 +75,7 @@ module.exports = class XhrWeapp {
   }
 
   send(data) {
+    // @ts-ignore
     this.requestTask = wx.request({
       url: this.url,
       method: this.method,
@@ -71,6 +92,7 @@ module.exports = class XhrWeapp {
       },
       fail: err => {
         this.requestTask = null;
+        // @ts-ignore
         this.onerror(err);
       },
     });
@@ -80,7 +102,10 @@ module.exports = class XhrWeapp {
         loaded: res.totalBytesWritten,
         total: res.totalBytesExpectedToWrite,
       };
+      // @ts-ignore
       this.onprogress(event);
     });
   }
-};
+}
+module.exports = XhrWeapp;
+export default XhrWeapp;
diff --git a/types/Analytics.d.ts b/types/Analytics.d.ts
index 5d260ca6c..ae2ff3ac9 100644
--- a/types/Analytics.d.ts
+++ b/types/Analytics.d.ts
@@ -37,9 +37,9 @@
  * @returns {Promise} A promise that is resolved when the round-trip
  * to the server completes.
  */
-export function track(
+export declare function track(
   name: string,
   dimensions: {
     [key: string]: string;
   }
-): Promise;
+): Promise;
diff --git a/types/AnonymousUtils.d.ts b/types/AnonymousUtils.d.ts
index ddb0bd644..d7517bf94 100644
--- a/types/AnonymousUtils.d.ts
+++ b/types/AnonymousUtils.d.ts
@@ -1,5 +1,34 @@
-export default AnonymousUtils;
-declare namespace AnonymousUtils {
+import ParseUser from './ParseUser';
+import type { RequestOptions } from './RESTController';
+/**
+ * Provides utility functions for working with Anonymously logged-in users. 
+ * Anonymous users have some unique characteristics: + *
    + *
  • Anonymous users don't need a user name or password.
  • + *
      + *
    • Once logged out, an anonymous user cannot be recovered.
    • + *
    + *
  • signUp converts an anonymous user to a standard user with the given username and password.
  • + *
      + *
    • Data associated with the anonymous user is retained.
    • + *
    + *
  • logIn switches users without converting the anonymous user.
  • + *
      + *
    • Data associated with the anonymous user will be lost.
    • + *
    + *
  • Service logIn (e.g. Facebook, Twitter) will attempt to convert + * the anonymous user into a standard user by linking it to the service.
  • + *
      + *
    • If a user already exists that is linked to the service, it will instead switch to the existing user.
    • + *
    + *
  • Service linking (e.g. Facebook, Twitter) will convert the anonymous user + * into a standard user by linking it to the service.
  • + *
+ * + * @class Parse.AnonymousUtils + * @static + */ +declare const AnonymousUtils: { /** * Gets whether the user has their account linked to anonymous user. * @@ -11,7 +40,7 @@ declare namespace AnonymousUtils { * linked to an anonymous user. * @static */ - function isLinked(user: ParseUser): boolean; + isLinked(user: ParseUser): boolean; /** * Logs in a user Anonymously. * @@ -21,7 +50,7 @@ declare namespace AnonymousUtils { * @returns {Promise} Logged in user * @static */ - function logIn(options?: RequestOptions): Promise; + logIn(options?: RequestOptions): Promise; /** * Links Anonymous User to an existing PFUser. * @@ -32,7 +61,7 @@ declare namespace AnonymousUtils { * @returns {Promise} Linked with User * @static */ - function link(user: ParseUser, options?: RequestOptions): Promise; + link(user: ParseUser, options?: RequestOptions): Promise; /** * Returns true if Authentication Provider has been registered for use. * @@ -41,16 +70,15 @@ declare namespace AnonymousUtils { * @returns {boolean} * @static */ - function isRegistered(): boolean; - function _getAuthProvider(): { + isRegistered(): boolean; + _getAuthProvider(): { restoreAuthentication(): boolean; getAuthType(): string; getAuthData(): { authData: { - id: any; + id: string; }; }; }; -} -import ParseUser from './ParseUser'; -import { RequestOptions } from './RESTController'; +}; +export default AnonymousUtils; diff --git a/types/CloudCode.d.ts b/types/CloudCode.d.ts new file mode 100644 index 000000000..5307f7c29 --- /dev/null +++ b/types/CloudCode.d.ts @@ -0,0 +1,307 @@ +/** + * Defines a Cloud Function. + * + * **Available in Cloud Code only.** + * + * @function define + * @name Parse.Cloud.define + * @param {string} name The name of the Cloud Function + * @param {Function} data The Cloud Function to register. This function should take one parameter {@link Parse.Cloud.FunctionRequest} + */ +/** + * Registers an after delete function. + * + * **Available in Cloud Code only.** + * + * If you want to use afterDelete for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * ``` + * Parse.Cloud.afterDelete('MyCustomClass', (request) => { + * // code here + * }) + * + * Parse.Cloud.afterDelete(Parse.User, (request) => { + * // code here + * }) + *``` + * + * @function afterDelete + * @name Parse.Cloud.afterDelete + * @param {(string | Parse.Object)} arg1 The Parse.Object subclass to register the after delete function for. This can instead be a String that is the className of the subclass. + * @param {Function} func The function to run after a delete. This function should take just one parameter, {@link Parse.Cloud.TriggerRequest}. + */ +/** + * + * Registers an after save function. + * + * **Available in Cloud Code only.** + * + * If you want to use afterSave for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * + * ``` + * Parse.Cloud.afterSave('MyCustomClass', function(request) { + * // code here + * }) + * + * Parse.Cloud.afterSave(Parse.User, function(request) { + * // code here + * }) + * ``` + * + * @function afterSave + * @name Parse.Cloud.afterSave + * @param {(string | Parse.Object)} arg1 The Parse.Object subclass to register the after save function for. This can instead be a String that is the className of the subclass. + * @param {Function} func The function to run after a save. This function should take just one parameter, {@link Parse.Cloud.TriggerRequest}. + */ +/** + * Registers an before delete function. + * + * **Available in Cloud Code only.** + * + * If you want to use beforeDelete for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * ``` + * Parse.Cloud.beforeDelete('MyCustomClass', (request) => { + * // code here + * }) + * + * Parse.Cloud.beforeDelete(Parse.User, (request) => { + * // code here + * }) + *``` + * + * @function beforeDelete + * @name Parse.Cloud.beforeDelete + * @param {(string | Parse.Object)} arg1 The Parse.Object subclass to register the before delete function for. This can instead be a String that is the className of the subclass. + * @param {Function} func The function to run before a delete. This function should take just one parameter, {@link Parse.Cloud.TriggerRequest}. + */ +/** + * + * Registers an before save function. + * + * **Available in Cloud Code only.** + * + * If you want to use beforeSave for a predefined class in the Parse JavaScript SDK (e.g. {@link Parse.User}), you should pass the class itself and not the String for arg1. + * + * ``` + * Parse.Cloud.beforeSave('MyCustomClass', (request) => { + * // code here + * }) + * + * Parse.Cloud.beforeSave(Parse.User, (request) => { + * // code here + * }) + * ``` + * + * @function beforeSave + * @name Parse.Cloud.beforeSave + * @param {(string | Parse.Object)} arg1 The Parse.Object subclass to register the after save function for. This can instead be a String that is the className of the subclass. + * @param {Function} func The function to run before a save. This function should take just one parameter, {@link Parse.Cloud.TriggerRequest}. + */ +/** + * + * Registers an before save file function. A new Parse.File can be returned to override the file that gets saved. + * If you want to replace the rquesting Parse.File with a Parse.File that is already saved, simply return the already saved Parse.File. + * You can also add metadata to the file that will be stored via whatever file storage solution you're using. + * + * **Available in Cloud Code only.** + * + * Example: Adding metadata and tags + * ``` + * Parse.Cloud.beforeSaveFile(({ file, user }) => { + * file.addMetadata('foo', 'bar'); + * file.addTag('createdBy', user.id); + * }); + * + * ``` + * + * Example: replacing file with an already saved file + * + * ``` + * Parse.Cloud.beforeSaveFile(({ file, user }) => { + * return user.get('avatar'); + * }); + * + * ``` + * + * Example: replacing file with a different file + * + * ``` + * Parse.Cloud.beforeSaveFile(({ file, user }) => { + * const metadata = { foo: 'bar' }; + * const tags = { createdBy: user.id }; + * const newFile = new Parse.File(file.name(), , 'text/plain', metadata, tags); + * return newFile; + * }); + * + * ``` + * + * @function beforeSaveFile + * @name Parse.Cloud.beforeSaveFile + * @param {Function} func The function to run before a file saves. This function should take one parameter, a {@link Parse.Cloud.FileTriggerRequest}. + */ +/** + * + * Registers an after save file function. + * + * **Available in Cloud Code only.** + * + * Example: creating a new object that references this file in a separate collection + * ``` + * Parse.Cloud.afterSaveFile(async ({ file, user }) => { + * const fileObject = new Parse.Object('FileObject'); + * fileObject.set('metadata', file.metadata()); + * fileObject.set('tags', file.tags()); + * fileObject.set('name', file.name()); + * fileObject.set('createdBy', user); + * await fileObject.save({ sessionToken: user.getSessionToken() }); + * }); + * + * @method afterSaveFile + * @name Parse.Cloud.afterSaveFile + * @param {Function} func The function to run after a file saves. This function should take one parameter, a {@link Parse.Cloud.FileTriggerRequest}. + */ +/** + * @function beforeConnect + * @name Parse.Cloud.beforeConnect + * @param {Function} func The function to before connection is made. This function can be async and should take just one parameter, {@link Parse.Cloud.ConnectTriggerRequest}. + */ +/** + * + * Registers a before connect function. + * + * **Available in Cloud Code only.** + * + * Example: restrict LiveQueries to logged in users. + * ``` + * Parse.Cloud.beforeConnect((request) => { + * if (!request.user) { + * throw "Please login before you attempt to connect." + * } + * }); + * ``` + */ +/** + * @function beforeSubscribe + * @name Parse.Cloud.beforeSubscribe + * @param {(string | Parse.Object)} arg1 The Parse.Object subclass to register the before subscription function for. This can instead be a String that is the className of the subclass. + * @param {Function} func The function to run before a subscription. This function can be async and should take one parameter, a {@link Parse.Cloud.TriggerRequest}. + */ +/** + * + * Registers a before subscribe function. + * + * **Available in Cloud Code only.** + * Example: restrict subscriptions to MyObject to Admin accounts only. + * ``` + * Parse.Cloud.beforeSubscribe('MyObject', (request) => { + * if (!request.user.get('Admin')) { + * throw new Parse.Error(101, 'You are not authorized to subscribe to MyObject.'); + * } + * let query = request.query; // the Parse.Query + * query.select("name","year") + * }); + * ``` + */ +/** + * Makes an HTTP Request. + * + * **Available in Cloud Code only.** + * + * By default, Parse.Cloud.httpRequest does not follow redirects caused by HTTP 3xx response codes. You can use the followRedirects option in the {@link Parse.Cloud.HTTPOptions} object to change this behavior. + * + * Sample request: + * ``` + * Parse.Cloud.httpRequest({ + * url: 'http://www.example.com/' + * }).then(function(httpResponse) { + * // success + * console.log(httpResponse.text); + * },function(httpResponse) { + * // error + * console.error('Request failed with response code ' + httpResponse.status); + * }); + * ``` + * + * @function httpRequest + * @name Parse.Cloud.httpRequest + * @param {Parse.Cloud.HTTPOptions} options The Parse.Cloud.HTTPOptions object that makes the request. + * @returns {Promise} A promise that will be resolved with a {@link Parse.Cloud.HTTPResponse} object when the request completes. + */ +/** + * Defines a Background Job. + * + * **Available in Cloud Code only.** + * + * @function job + * @name Parse.Cloud.job + * @param {string} name The name of the Background Job + * @param {Function} func The Background Job to register. This function should take two parameters a {@link Parse.Cloud.JobRequest} and a {@link Parse.Cloud.JobStatus} + */ +/** + * @typedef Parse.Cloud.TriggerRequest + * @property {string} installationId If set, the installationId triggering the request. + * @property {boolean} master If true, means the master key was used. + * @property {Parse.User} user If set, the user that made the request. + * @property {Parse.Object} object The object triggering the hook. + * @property {string} ip The IP address of the client making the request. + * @property {object} headers The original HTTP headers for the request. + * @property {string} triggerName The name of the trigger (`beforeSave`, `afterSave`, ...) + * @property {object} log The current logger inside Parse Server. + * @property {Parse.Object} original If set, the object, as currently stored. + */ +/** + * @typedef Parse.Cloud.FileTriggerRequest + * @property {string} installationId If set, the installationId triggering the request. + * @property {boolean} master If true, means the master key was used. + * @property {Parse.User} user If set, the user that made the request. + * @property {Parse.File} file The file triggering the hook. + * @property {string} ip The IP address of the client making the request. + * @property {object} headers The original HTTP headers for the request. + * @property {string} triggerName The name of the trigger (`beforeSaveFile`, `afterSaveFile`, ...) + * @property {object} log The current logger inside Parse Server. + */ +/** + * @typedef Parse.Cloud.ConnectTriggerRequest + * @property {string} installationId If set, the installationId triggering the request. + * @property {boolean} useMasterKey If true, means the master key was used. + * @property {Parse.User} user If set, the user that made the request. + * @property {number} clients The number of clients connected. + * @property {number} subscriptions The number of subscriptions connected. + * @property {string} sessionToken If set, the session of the user that made the request. + */ +/** + * @typedef Parse.Cloud.FunctionRequest + * @property {string} installationId If set, the installationId triggering the request. + * @property {boolean} master If true, means the master key was used. + * @property {Parse.User} user If set, the user that made the request. + * @property {object} params The params passed to the cloud function. + */ +/** + * @typedef Parse.Cloud.JobRequest + * @property {object} params The params passed to the background job. + */ +/** + * @typedef Parse.Cloud.JobStatus + * @property {Function} error If error is called, will end the job unsuccessfully with an optional completion message to be stored in the job status. + * @property {Function} message If message is called with a string argument, will update the current message to be stored in the job status. + * @property {Function} success If success is called, will end the job successfullly with the optional completion message to be stored in the job status. + */ +/** + * @typedef Parse.Cloud.HTTPOptions + * @property {string | object} body The body of the request. If it is a JSON object, then the Content-Type set in the headers must be application/x-www-form-urlencoded or application/json. You can also set this to a {@link Buffer} object to send raw bytes. If you use a Buffer, you should also set the Content-Type header explicitly to describe what these bytes represent. + * @property {Function} error The function that is called when the request fails. It will be passed a Parse.Cloud.HTTPResponse object. + * @property {boolean} followRedirects Whether to follow redirects caused by HTTP 3xx responses. Defaults to false. + * @property {object} headers The headers for the request. + * @property {string} method The method of the request. GET, POST, PUT, DELETE, HEAD, and OPTIONS are supported. Will default to GET if not specified. + * @property {string | object} params The query portion of the url. You can pass a JSON object of key value pairs like params: {q : 'Sean Plott'} or a raw string like params:q=Sean Plott. + * @property {Function} success The function that is called when the request successfully completes. It will be passed a Parse.Cloud.HTTPResponse object. + * @property {string} url The url to send the request to. + */ +/** + * @typedef Parse.Cloud.HTTPResponse + * @property {Buffer} buffer The raw byte representation of the response body. Use this to receive binary data. See Buffer for more details. + * @property {object} cookies The cookies sent by the server. The keys in this object are the names of the cookies. The values are Parse.Cloud.Cookie objects. + * @property {object} data The parsed response body as a JavaScript object. This is only available when the response Content-Type is application/x-www-form-urlencoded or application/json. + * @property {object} headers The headers sent by the server. The keys in this object are the names of the headers. We do not support multiple response headers with the same name. In the common case of Set-Cookie headers, please use the cookies field instead. + * @property {number} status The status code. + * @property {string} text The raw text representation of the response body. + */ diff --git a/types/CryptoController.d.ts b/types/CryptoController.d.ts index 0cebcbf5f..362d3eca7 100644 --- a/types/CryptoController.d.ts +++ b/types/CryptoController.d.ts @@ -1,2 +1,5 @@ -export function encrypt(obj: any, secretKey: string): string; -export function decrypt(encryptedText: string, secretKey: string): string; +declare const CryptoController: { + encrypt(obj: any, secretKey: string): string; + decrypt(encryptedText: string, secretKey: string): string; +}; +export default CryptoController; diff --git a/types/EventEmitter.d.ts b/types/EventEmitter.d.ts index 14840f3fb..b995646ce 100644 --- a/types/EventEmitter.d.ts +++ b/types/EventEmitter.d.ts @@ -1,2 +1,5 @@ -export = EventEmitter; -export = EventEmitter; +/** + * This is a simple wrapper to unify EventEmitter implementations across platforms. + */ +declare let EventEmitter: any; +export default EventEmitter; diff --git a/types/LiveQueryClient.d.ts b/types/LiveQueryClient.d.ts index 301650880..49fbe38c5 100644 --- a/types/LiveQueryClient.d.ts +++ b/types/LiveQueryClient.d.ts @@ -14,9 +14,8 @@ import type ParseQuery from './ParseQuery'; * We expose three events to help you monitor the status of the LiveQueryClient. * *
- * let Parse = require('parse/node');
- * let LiveQueryClient = Parse.LiveQueryClient;
- * let client = new LiveQueryClient({
+ * const LiveQueryClient = Parse.LiveQueryClient;
+ * const client = new LiveQueryClient({
  *   applicationId: '',
  *   serverURL: '',
  *   javascriptKey: '',
diff --git a/types/LocalDatastoreController.d.ts b/types/LocalDatastoreController.d.ts
index cb0ff5c3b..bbfa60388 100644
--- a/types/LocalDatastoreController.d.ts
+++ b/types/LocalDatastoreController.d.ts
@@ -1 +1,2 @@
-export {};
+declare let LocalDatastoreController: any;
+export default LocalDatastoreController;
diff --git a/types/LocalDatastoreController.default.d.ts b/types/LocalDatastoreController.default.d.ts
index 5f36329c3..3737f59a7 100644
--- a/types/LocalDatastoreController.default.d.ts
+++ b/types/LocalDatastoreController.default.d.ts
@@ -1,7 +1,7 @@
 declare const LocalDatastoreController: {
   fromPinWithName(name: string): Promise>;
-  pinWithName(name: string, value: any): any;
-  unPinWithName(name: string): any;
+  pinWithName(name: string, value: any): Promise;
+  unPinWithName(name: string): Promise;
   getAllContents(): Promise;
   getRawStorage(): Promise;
   clear(): Promise;
diff --git a/types/OfflineQuery.d.ts b/types/OfflineQuery.d.ts
index a1044dcf4..1361e7efc 100644
--- a/types/OfflineQuery.d.ts
+++ b/types/OfflineQuery.d.ts
@@ -1,17 +1,20 @@
-export type RelativeTimeToDateResult = {
-  /**
-   * The conversion status, `error` if conversion failed or
-   * `success` if conversion succeeded.
-   */
-  status: string;
-  /**
-   * The error message if conversion failed, or the relative
-   * time indication (`past`, `present`, `future`) if conversion succeeded.
-   */
-  info: string;
-  /**
-   * The converted date, or `undefined` if conversion
-   * failed.
-   */
-  result: Date | undefined;
+/**
+ * matchesQuery -- Determines if an object would be returned by a Parse Query
+ * It's a lightweight, where-clause only implementation of a full query engine.
+ * Since we find queries that match objects, rather than objects that match
+ * queries, we can avoid building a full-blown query tool.
+ *
+ * @param className
+ * @param object
+ * @param objects
+ * @param query
+ * @private
+ * @returns {boolean}
+ */
+declare function matchesQuery(className: any, object: any, objects: any, query: any): boolean;
+declare function validateQuery(query: any): void;
+declare const OfflineQuery: {
+  matchesQuery: typeof matchesQuery;
+  validateQuery: typeof validateQuery;
 };
+export default OfflineQuery;
diff --git a/types/ParseFile.d.ts b/types/ParseFile.d.ts
index 48f1a4c4b..d7eab6ba3 100644
--- a/types/ParseFile.d.ts
+++ b/types/ParseFile.d.ts
@@ -136,7 +136,11 @@ declare class ParseFile {
    * 
    * @returns {Promise | undefined} Promise that is resolved when the save finishes.
    */
-  save(options?: FileSaveOptions): Promise | undefined;
+  save(
+    options?: FileSaveOptions & {
+      requestTask?: any;
+    }
+  ): Promise | undefined;
   /**
    * Aborts the request if it has already been sent.
    */
diff --git a/types/ParseObject.d.ts b/types/ParseObject.d.ts
index abe5efc25..3fbaf71be 100644
--- a/types/ParseObject.d.ts
+++ b/types/ParseObject.d.ts
@@ -866,7 +866,7 @@ declare class ParseObject {
   static saveAll(
     list: Array,
     options?: SaveOptions
-  ): Promise;
+  ): Promise;
   /**
    * Creates a reference to a subclass of Parse.Object with the given id. This
    * does not exist on Parse.Object, only on subclasses.
diff --git a/types/RESTController.d.ts b/types/RESTController.d.ts
index 1d27d9c04..e4e323917 100644
--- a/types/RESTController.d.ts
+++ b/types/RESTController.d.ts
@@ -1,4 +1,4 @@
-type RequestOptions = {
+export type RequestOptions = {
   useMasterKey?: boolean;
   sessionToken?: string;
   installationId?: string;
@@ -8,8 +8,9 @@ type RequestOptions = {
   progress?: any;
   context?: any;
   usePost?: boolean;
+  ignoreEmailVerification?: boolean;
 };
-type FullOptions = {
+export type FullOptions = {
   success?: any;
   error?: any;
   useMasterKey?: boolean;
@@ -18,4 +19,22 @@ type FullOptions = {
   progress?: any;
   usePost?: boolean;
 };
-export { RequestOptions, FullOptions };
+declare const RESTController: {
+  ajax(
+    method: string,
+    url: string,
+    data: any,
+    headers?: any,
+    options?: FullOptions
+  ):
+    | (Promise & {
+        resolve: (res: any) => void;
+        reject: (err: any) => void;
+      })
+    | Promise;
+  request(method: string, path: string, data: any, options?: RequestOptions): Promise;
+  handleError(response: any): Promise;
+  _setXHR(xhr: any): void;
+  _getXHR(): any;
+};
+export default RESTController;
diff --git a/types/Socket.weapp.d.ts b/types/Socket.weapp.d.ts
index d52d46d6e..de97ad385 100644
--- a/types/Socket.weapp.d.ts
+++ b/types/Socket.weapp.d.ts
@@ -1,10 +1,10 @@
-export = SocketWeapp;
 declare class SocketWeapp {
-  constructor(serverURL: any);
   onopen: () => void;
   onmessage: () => void;
   onclose: () => void;
   onerror: () => void;
+  constructor(serverURL: any);
   send(data: any): void;
   close(): void;
 }
+export default SocketWeapp;
diff --git a/types/Storage.d.ts b/types/Storage.d.ts
index 6d18df390..4ecb4c2f2 100644
--- a/types/Storage.d.ts
+++ b/types/Storage.d.ts
@@ -1,14 +1,14 @@
+declare const Storage: {
+  async(): boolean;
+  getItem(path: string): string | null;
+  getItemAsync(path: string): Promise;
+  setItem(path: string, value: string): void;
+  setItemAsync(path: string, value: string): Promise;
+  removeItem(path: string): void;
+  removeItemAsync(path: string): Promise;
+  getAllKeys(): Array;
+  getAllKeysAsync(): Promise>;
+  generatePath(path: string): string;
+  _clear(): void;
+};
 export default Storage;
-declare namespace Storage {
-  function async(): boolean;
-  function getItem(path: string): string;
-  function getItemAsync(path: string): Promise;
-  function setItem(path: string, value: string): void;
-  function setItemAsync(path: string, value: string): Promise;
-  function removeItem(path: string): void;
-  function removeItemAsync(path: string): Promise;
-  function getAllKeys(): string[];
-  function getAllKeysAsync(): Promise;
-  function generatePath(path: string): string;
-  function _clear(): void;
-}
diff --git a/types/StorageController.browser.d.ts b/types/StorageController.browser.d.ts
index b3f1f7fe0..1d736ba19 100644
--- a/types/StorageController.browser.d.ts
+++ b/types/StorageController.browser.d.ts
@@ -1,6 +1,9 @@
-export let async: number;
-export function getItem(path: string): string;
-export function setItem(path: string, value: string): void;
-export function removeItem(path: string): void;
-export function getAllKeys(): string[];
-export function clear(): void;
+declare const StorageController: {
+  async: number;
+  getItem(path: string): string | null;
+  setItem(path: string, value: string): void;
+  removeItem(path: string): void;
+  getAllKeys(): string[];
+  clear(): void;
+};
+export default StorageController;
diff --git a/types/StorageController.d.ts b/types/StorageController.d.ts
new file mode 100644
index 000000000..a0cf36bc1
--- /dev/null
+++ b/types/StorageController.d.ts
@@ -0,0 +1,2 @@
+declare let StorageController: any;
+export default StorageController;
diff --git a/types/StorageController.default.d.ts b/types/StorageController.default.d.ts
index b3f1f7fe0..1d736ba19 100644
--- a/types/StorageController.default.d.ts
+++ b/types/StorageController.default.d.ts
@@ -1,6 +1,9 @@
-export let async: number;
-export function getItem(path: string): string;
-export function setItem(path: string, value: string): void;
-export function removeItem(path: string): void;
-export function getAllKeys(): string[];
-export function clear(): void;
+declare const StorageController: {
+  async: number;
+  getItem(path: string): string | null;
+  setItem(path: string, value: string): void;
+  removeItem(path: string): void;
+  getAllKeys(): string[];
+  clear(): void;
+};
+export default StorageController;
diff --git a/types/StorageController.react-native.d.ts b/types/StorageController.react-native.d.ts
index cb0ff5c3b..538a3654a 100644
--- a/types/StorageController.react-native.d.ts
+++ b/types/StorageController.react-native.d.ts
@@ -1 +1,11 @@
-export {};
+declare const StorageController: {
+  async: number;
+  getItemAsync(path: string): Promise;
+  setItemAsync(path: string, value: string): Promise;
+  removeItemAsync(path: string): Promise;
+  getAllKeysAsync(): Promise;
+  multiGet(keys: Array): Promise;
+  multiRemove(keys: Array): Promise>;
+  clear(): Promise;
+};
+export default StorageController;
diff --git a/types/StorageController.weapp.d.ts b/types/StorageController.weapp.d.ts
index 91bafa45b..267cbfe81 100644
--- a/types/StorageController.weapp.d.ts
+++ b/types/StorageController.weapp.d.ts
@@ -1,6 +1,9 @@
-export let async: number;
-export function getItem(path: string): string;
-export function setItem(path: string, value: string): void;
-export function removeItem(path: string): void;
-export function getAllKeys(): any;
-export function clear(): void;
+declare const StorageController: {
+  async: number;
+  getItem(path: string): string | null;
+  setItem(path: string, value: string): void;
+  removeItem(path: string): void;
+  getAllKeys(): any;
+  clear(): void;
+};
+export default StorageController;
diff --git a/types/WebSocketController.d.ts b/types/WebSocketController.d.ts
new file mode 100644
index 000000000..72a78562d
--- /dev/null
+++ b/types/WebSocketController.d.ts
@@ -0,0 +1,2 @@
+declare let WebSocketController: any;
+export default WebSocketController;
diff --git a/types/Xhr.weapp.d.ts b/types/Xhr.weapp.d.ts
index b1e58ae81..c214a1677 100644
--- a/types/Xhr.weapp.d.ts
+++ b/types/Xhr.weapp.d.ts
@@ -1,17 +1,16 @@
-export = XhrWeapp;
 declare class XhrWeapp {
   UNSENT: number;
   OPENED: number;
   HEADERS_RECEIVED: number;
   LOADING: number;
   DONE: number;
-  header: {};
-  readyState: number;
+  header: any;
+  readyState: any;
   status: number;
-  response: string;
+  response: string | undefined;
   responseType: string;
   responseText: string;
-  responseHeader: {};
+  responseHeader: any;
   method: string;
   url: string;
   onabort: () => void;
@@ -19,6 +18,7 @@ declare class XhrWeapp {
   onerror: () => void;
   onreadystatechange: () => void;
   requestTask: any;
+  constructor();
   getAllResponseHeaders(): string;
   getResponseHeader(key: any): any;
   setRequestHeader(key: any, value: any): void;
@@ -26,3 +26,4 @@ declare class XhrWeapp {
   abort(): void;
   send(data: any): void;
 }
+export default XhrWeapp;