diff --git a/package-lock.json b/package-lock.json
index 9e4808191..29df00318 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -35,6 +35,7 @@
"@semantic-release/github": "8.0.7",
"@semantic-release/npm": "9.0.2",
"@semantic-release/release-notes-generator": "10.0.3",
+ "@types/facebook-js-sdk": "3.3.9",
"babel-jest": "29.5.0",
"babel-plugin-inline-package-json": "2.0.0",
"babel-plugin-minify-dead-code-elimination": "0.5.2",
@@ -5049,6 +5050,12 @@
"@types/range-parser": "*"
}
},
+ "node_modules/@types/facebook-js-sdk": {
+ "version": "3.3.9",
+ "resolved": "https://registry.npmjs.org/@types/facebook-js-sdk/-/facebook-js-sdk-3.3.9.tgz",
+ "integrity": "sha512-uJiJ+ljEPzC7jHGXl8YT7gRUh0fGzzJYrdwyrjgwSqFvrcCwlWMu/nLLcJeIRoFA81uVBwZBOKQIkjXFknXPsA==",
+ "dev": true
+ },
"node_modules/@types/graceful-fs": {
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
@@ -31544,6 +31551,12 @@
"@types/range-parser": "*"
}
},
+ "@types/facebook-js-sdk": {
+ "version": "3.3.9",
+ "resolved": "https://registry.npmjs.org/@types/facebook-js-sdk/-/facebook-js-sdk-3.3.9.tgz",
+ "integrity": "sha512-uJiJ+ljEPzC7jHGXl8YT7gRUh0fGzzJYrdwyrjgwSqFvrcCwlWMu/nLLcJeIRoFA81uVBwZBOKQIkjXFknXPsA==",
+ "dev": true
+ },
"@types/graceful-fs": {
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz",
diff --git a/package.json b/package.json
index 22a92f152..12654f893 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
"@semantic-release/github": "8.0.7",
"@semantic-release/npm": "9.0.2",
"@semantic-release/release-notes-generator": "10.0.3",
+ "@types/facebook-js-sdk": "3.3.9",
"babel-jest": "29.5.0",
"babel-plugin-inline-package-json": "2.0.0",
"babel-plugin-minify-dead-code-elimination": "0.5.2",
diff --git a/src/Analytics.js b/src/Analytics.ts
similarity index 95%
rename from src/Analytics.js
rename to src/Analytics.ts
index 32f4cf82f..3a6b367c3 100644
--- a/src/Analytics.js
+++ b/src/Analytics.ts
@@ -44,7 +44,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
Parse.Error
.
- * @param {string} message A detailed description of the error.
- */
- constructor(code, message) {
- super(message);
- this.code = code;
- let customMessage = message;
- CoreManager.get('PARSE_ERRORS').forEach((error) => {
- if (error.code === code && error.code) {
- customMessage = error.message;
- }
- });
- Object.defineProperty(this, 'message', {
- enumerable: true,
- value: customMessage,
- });
- }
-
- toString() {
- return 'ParseError: ' + this.code + ' ' + this.message;
- }
-}
-
-/**
- * Error code indicating some error other than those enumerated here.
- *
- * @property {number} OTHER_CAUSE
- * @static
- */
-ParseError.OTHER_CAUSE = -1;
-
-/**
- * Error code indicating that something has gone wrong with the server.
- *
- * @property {number} INTERNAL_SERVER_ERROR
- * @static
- */
-ParseError.INTERNAL_SERVER_ERROR = 1;
-
-/**
- * Error code indicating the connection to the Parse servers failed.
- *
- * @property {number} CONNECTION_FAILED
- * @static
- */
-ParseError.CONNECTION_FAILED = 100;
-
-/**
- * Error code indicating the specified object doesn't exist.
- *
- * @property {number} OBJECT_NOT_FOUND
- * @static
- */
-ParseError.OBJECT_NOT_FOUND = 101;
-
-/**
- * Error code indicating you tried to query with a datatype that doesn't
- * support it, like exact matching an array or object.
- *
- * @property {number} INVALID_QUERY
- * @static
- */
-ParseError.INVALID_QUERY = 102;
-
-/**
- * Error code indicating a missing or invalid classname. Classnames are
- * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
- * only valid characters.
- *
- * @property {number} INVALID_CLASS_NAME
- * @static
- */
-ParseError.INVALID_CLASS_NAME = 103;
-
-/**
- * Error code indicating an unspecified object id.
- *
- * @property {number} MISSING_OBJECT_ID
- * @static
- */
-ParseError.MISSING_OBJECT_ID = 104;
-
-/**
- * Error code indicating an invalid key name. Keys are case-sensitive. They
- * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
- *
- * @property {number} INVALID_KEY_NAME
- * @static
- */
-ParseError.INVALID_KEY_NAME = 105;
-
-/**
- * Error code indicating a malformed pointer. You should not see this unless
- * you have been mucking about changing internal Parse code.
- *
- * @property {number} INVALID_POINTER
- * @static
- */
-ParseError.INVALID_POINTER = 106;
-
-/**
- * Error code indicating that badly formed JSON was received upstream. This
- * either indicates you have done something unusual with modifying how
- * things encode to JSON, or the network is failing badly.
- *
- * @property {number} INVALID_JSON
- * @static
- */
-ParseError.INVALID_JSON = 107;
-
-/**
- * Error code indicating that the feature you tried to access is only
- * available internally for testing purposes.
- *
- * @property {number} COMMAND_UNAVAILABLE
- * @static
- */
-ParseError.COMMAND_UNAVAILABLE = 108;
-
-/**
- * You must call Parse.initialize before using the Parse library.
- *
- * @property {number} NOT_INITIALIZED
- * @static
- */
-ParseError.NOT_INITIALIZED = 109;
-
-/**
- * Error code indicating that a field was set to an inconsistent type.
- *
- * @property {number} INCORRECT_TYPE
- * @static
- */
-ParseError.INCORRECT_TYPE = 111;
-
-/**
- * Error code indicating an invalid channel name. A channel name is either
- * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
- * characters and starts with a letter.
- *
- * @property {number} INVALID_CHANNEL_NAME
- * @static
- */
-ParseError.INVALID_CHANNEL_NAME = 112;
-
-/**
- * Error code indicating that push is misconfigured.
- *
- * @property {number} PUSH_MISCONFIGURED
- * @static
- */
-ParseError.PUSH_MISCONFIGURED = 115;
-
-/**
- * Error code indicating that the object is too large.
- *
- * @property {number} OBJECT_TOO_LARGE
- * @static
- */
-ParseError.OBJECT_TOO_LARGE = 116;
-
-/**
- * Error code indicating that the operation isn't allowed for clients.
- *
- * @property {number} OPERATION_FORBIDDEN
- * @static
- */
-ParseError.OPERATION_FORBIDDEN = 119;
-
-/**
- * Error code indicating the result was not found in the cache.
- *
- * @property {number} CACHE_MISS
- * @static
- */
-ParseError.CACHE_MISS = 120;
-
-/**
- * Error code indicating that an invalid key was used in a nested
- * JSONObject.
- *
- * @property {number} INVALID_NESTED_KEY
- * @static
- */
-ParseError.INVALID_NESTED_KEY = 121;
-
-/**
- * Error code indicating that an invalid filename was used for ParseFile.
- * A valid file name contains only a-zA-Z0-9_. characters and is between 1
- * and 128 characters.
- *
- * @property {number} INVALID_FILE_NAME
- * @static
- */
-ParseError.INVALID_FILE_NAME = 122;
-
-/**
- * Error code indicating an invalid ACL was provided.
- *
- * @property {number} INVALID_ACL
- * @static
- */
-ParseError.INVALID_ACL = 123;
-
-/**
- * Error code indicating that the request timed out on the server. Typically
- * this indicates that the request is too expensive to run.
- *
- * @property {number} TIMEOUT
- * @static
- */
-ParseError.TIMEOUT = 124;
-
-/**
- * Error code indicating that the email address was invalid.
- *
- * @property {number} INVALID_EMAIL_ADDRESS
- * @static
- */
-ParseError.INVALID_EMAIL_ADDRESS = 125;
-
-/**
- * Error code indicating a missing content type.
- *
- * @property {number} MISSING_CONTENT_TYPE
- * @static
- */
-ParseError.MISSING_CONTENT_TYPE = 126;
-
-/**
- * Error code indicating a missing content length.
- *
- * @property {number} MISSING_CONTENT_LENGTH
- * @static
- */
-ParseError.MISSING_CONTENT_LENGTH = 127;
-
-/**
- * Error code indicating an invalid content length.
- *
- * @property {number} INVALID_CONTENT_LENGTH
- * @static
- */
-ParseError.INVALID_CONTENT_LENGTH = 128;
-
-/**
- * Error code indicating a file that was too large.
- *
- * @property {number} FILE_TOO_LARGE
- * @static
- */
-ParseError.FILE_TOO_LARGE = 129;
-
-/**
- * Error code indicating an error saving a file.
- *
- * @property {number} FILE_SAVE_ERROR
- * @static
- */
-ParseError.FILE_SAVE_ERROR = 130;
-
-/**
- * Error code indicating that a unique field was given a value that is
- * already taken.
- *
- * @property {number} DUPLICATE_VALUE
- * @static
- */
-ParseError.DUPLICATE_VALUE = 137;
-
-/**
- * Error code indicating that a role's name is invalid.
- *
- * @property {number} INVALID_ROLE_NAME
- * @static
- */
-ParseError.INVALID_ROLE_NAME = 139;
-
-/**
- * Error code indicating that an application quota was exceeded. Upgrade to
- * resolve.
- *
- * @property {number} EXCEEDED_QUOTA
- * @static
- */
-ParseError.EXCEEDED_QUOTA = 140;
-
-/**
- * Error code indicating that a Cloud Code script failed.
- *
- * @property {number} SCRIPT_FAILED
- * @static
- */
-ParseError.SCRIPT_FAILED = 141;
-
-/**
- * Error code indicating that a Cloud Code validation failed.
- *
- * @property {number} VALIDATION_ERROR
- * @static
- */
-ParseError.VALIDATION_ERROR = 142;
-
-/**
- * Error code indicating that invalid image data was provided.
- *
- * @property {number} INVALID_IMAGE_DATA
- * @static
- */
-ParseError.INVALID_IMAGE_DATA = 143;
-
-/**
- * Error code indicating an unsaved file.
- *
- * @property {number} UNSAVED_FILE_ERROR
- * @static
- */
-ParseError.UNSAVED_FILE_ERROR = 151;
-
-/**
- * Error code indicating an invalid push time.
- *
- * @property {number} INVALID_PUSH_TIME_ERROR
- * @static
- */
-ParseError.INVALID_PUSH_TIME_ERROR = 152;
-
-/**
- * Error code indicating an error deleting a file.
- *
- * @property {number} FILE_DELETE_ERROR
- * @static
- */
-ParseError.FILE_DELETE_ERROR = 153;
-
-/**
- * Error code indicating an error deleting an unnamed file.
- *
- * @property {number} FILE_DELETE_UNNAMED_ERROR
- * @static
- */
-ParseError.FILE_DELETE_UNNAMED_ERROR = 161;
-
-/**
- * Error code indicating that the application has exceeded its request
- * limit.
- *
- * @property {number} REQUEST_LIMIT_EXCEEDED
- * @static
- */
-ParseError.REQUEST_LIMIT_EXCEEDED = 155;
-
-/**
- * Error code indicating that the request was a duplicate and has been discarded due to
- * idempotency rules.
- *
- * @property {number} DUPLICATE_REQUEST
- * @static
- */
-ParseError.DUPLICATE_REQUEST = 159;
-
-/**
- * Error code indicating an invalid event name.
- *
- * @property {number} INVALID_EVENT_NAME
- * @static
- */
-ParseError.INVALID_EVENT_NAME = 160;
-
-/**
- * Error code indicating that a field had an invalid value.
- *
- * @property {number} INVALID_VALUE
- * @static
- */
-ParseError.INVALID_VALUE = 162;
-
-/**
- * Error code indicating that the username is missing or empty.
- *
- * @property {number} USERNAME_MISSING
- * @static
- */
-ParseError.USERNAME_MISSING = 200;
-
-/**
- * Error code indicating that the password is missing or empty.
- *
- * @property {number} PASSWORD_MISSING
- * @static
- */
-ParseError.PASSWORD_MISSING = 201;
-
-/**
- * Error code indicating that the username has already been taken.
- *
- * @property {number} USERNAME_TAKEN
- * @static
- */
-ParseError.USERNAME_TAKEN = 202;
-
-/**
- * Error code indicating that the email has already been taken.
- *
- * @property {number} EMAIL_TAKEN
- * @static
- */
-ParseError.EMAIL_TAKEN = 203;
-
-/**
- * Error code indicating that the email is missing, but must be specified.
- *
- * @property {number} EMAIL_MISSING
- * @static
- */
-ParseError.EMAIL_MISSING = 204;
-
-/**
- * Error code indicating that a user with the specified email was not found.
- *
- * @property {number} EMAIL_NOT_FOUND
- * @static
- */
-ParseError.EMAIL_NOT_FOUND = 205;
-
-/**
- * Error code indicating that a user object without a valid session could
- * not be altered.
- *
- * @property {number} SESSION_MISSING
- * @static
- */
-ParseError.SESSION_MISSING = 206;
-
-/**
- * Error code indicating that a user can only be created through signup.
- *
- * @property {number} MUST_CREATE_USER_THROUGH_SIGNUP
- * @static
- */
-ParseError.MUST_CREATE_USER_THROUGH_SIGNUP = 207;
-
-/**
- * Error code indicating that an an account being linked is already linked
- * to another user.
- *
- * @property {number} ACCOUNT_ALREADY_LINKED
- * @static
- */
-ParseError.ACCOUNT_ALREADY_LINKED = 208;
-
-/**
- * Error code indicating that the current session token is invalid.
- *
- * @property {number} INVALID_SESSION_TOKEN
- * @static
- */
-ParseError.INVALID_SESSION_TOKEN = 209;
-
-/**
- * Error code indicating an error enabling or verifying MFA
- *
- * @property {number} MFA_ERROR
- * @static
- */
-ParseError.MFA_ERROR = 210;
-
-/**
- * Error code indicating that a valid MFA token must be provided
- *
- * @property {number} MFA_TOKEN_REQUIRED
- * @static
- */
-ParseError.MFA_TOKEN_REQUIRED = 211;
-
-/**
- * Error code indicating that a user cannot be linked to an account because
- * that account's id could not be found.
- *
- * @property {number} LINKED_ID_MISSING
- * @static
- */
-ParseError.LINKED_ID_MISSING = 250;
-
-/**
- * Error code indicating that a user with a linked (e.g. Facebook) account
- * has an invalid session.
- *
- * @property {number} INVALID_LINKED_SESSION
- * @static
- */
-ParseError.INVALID_LINKED_SESSION = 251;
-
-/**
- * Error code indicating that a service being linked (e.g. Facebook or
- * Twitter) is unsupported.
- *
- * @property {number} UNSUPPORTED_SERVICE
- * @static
- */
-ParseError.UNSUPPORTED_SERVICE = 252;
-
-/**
- * Error code indicating an invalid operation occured on schema
- *
- * @property {number} INVALID_SCHEMA_OPERATION
- * @static
- */
-ParseError.INVALID_SCHEMA_OPERATION = 255;
-
-/**
- * Error code indicating that there were multiple errors. Aggregate errors
- * have an "errors" property, which is an array of error objects with more
- * detail about each error that occurred.
- *
- * @property {number} AGGREGATE_ERROR
- * @static
- */
-ParseError.AGGREGATE_ERROR = 600;
-
-/**
- * Error code indicating the client was unable to read an input file.
- *
- * @property {number} FILE_READ_ERROR
- * @static
- */
-ParseError.FILE_READ_ERROR = 601;
-
-/**
- * Error code indicating a real error code is unavailable because
- * we had to use an XDomainRequest object to allow CORS requests in
- * Internet Explorer, which strips the body from HTTP responses that have
- * a non-2XX status code.
- *
- * @property {number} X_DOMAIN_REQUEST
- * @static
- */
-ParseError.X_DOMAIN_REQUEST = 602;
-
-export default ParseError;
diff --git a/src/ParseError.ts b/src/ParseError.ts
new file mode 100644
index 000000000..1f8e4e0b1
--- /dev/null
+++ b/src/ParseError.ts
@@ -0,0 +1,559 @@
+import CoreManager from './CoreManager';
+import type ParseObject from './ParseObject';
+
+/**
+ * Constructs a new Parse.Error object with the given code and message.
+ *
+ * Parse.CoreManager.set('PARSE_ERRORS', [{ code, message }]) can be use to override error messages.
+ *
+ * @alias Parse.Error
+ */
+class ParseError extends Error {
+ code: number;
+ message: string;
+ /** In case an error is associated with an object */
+ object?: ParseObject;
+ /** In case of aggregate errors, this is populated */
+ errors?: Error[];
+ /**
+ * @param {number} code An error code constant from Parse.Error
.
+ * @param {string} message A detailed description of the error.
+ */
+ constructor(code: number, message?: string) {
+ super(message);
+ this.code = code;
+ let customMessage = message;
+ CoreManager.get('PARSE_ERRORS').forEach((error: { code: number, message: string }) => {
+ if (error.code === code && error.code) {
+ customMessage = error.message;
+ }
+ });
+ Object.defineProperty(this, 'message', {
+ enumerable: true,
+ value: customMessage,
+ });
+ }
+
+ toString() {
+ return 'ParseError: ' + this.code + ' ' + this.message;
+ }
+
+
+ /**
+ * Error code indicating some error other than those enumerated here.
+ *
+ * @property {number} OTHER_CAUSE
+ * @static
+ */
+ static OTHER_CAUSE = -1;
+
+ /**
+ * Error code indicating that something has gone wrong with the server.
+ *
+ * @property {number} INTERNAL_SERVER_ERROR
+ * @static
+ */
+ static INTERNAL_SERVER_ERROR = 1;
+
+ /**
+ * Error code indicating the connection to the Parse servers failed.
+ *
+ * @property {number} CONNECTION_FAILED
+ * @static
+ */
+ static CONNECTION_FAILED = 100;
+
+ /**
+ * Error code indicating the specified object doesn't exist.
+ *
+ * @property {number} OBJECT_NOT_FOUND
+ * @static
+ */
+ static OBJECT_NOT_FOUND = 101;
+
+ /**
+ * Error code indicating you tried to query with a datatype that doesn't
+ * support it, like exact matching an array or object.
+ *
+ * @property {number} INVALID_QUERY
+ * @static
+ */
+ static INVALID_QUERY = 102;
+
+ /**
+ * Error code indicating a missing or invalid classname. Classnames are
+ * case-sensitive. They must start with a letter, and a-zA-Z0-9_ are the
+ * only valid characters.
+ *
+ * @property {number} INVALID_CLASS_NAME
+ * @static
+ */
+ static INVALID_CLASS_NAME = 103;
+
+ /**
+ * Error code indicating an unspecified object id.
+ *
+ * @property {number} MISSING_OBJECT_ID
+ * @static
+ */
+ static MISSING_OBJECT_ID = 104;
+
+ /**
+ * Error code indicating an invalid key name. Keys are case-sensitive. They
+ * must start with a letter, and a-zA-Z0-9_ are the only valid characters.
+ *
+ * @property {number} INVALID_KEY_NAME
+ * @static
+ */
+ static INVALID_KEY_NAME = 105;
+
+ /**
+ * Error code indicating a malformed pointer. You should not see this unless
+ * you have been mucking about changing internal Parse code.
+ *
+ * @property {number} INVALID_POINTER
+ * @static
+ */
+ static INVALID_POINTER = 106;
+
+ /**
+ * Error code indicating that badly formed JSON was received upstream. This
+ * either indicates you have done something unusual with modifying how
+ * things encode to JSON, or the network is failing badly.
+ *
+ * @property {number} INVALID_JSON
+ * @static
+ */
+ static INVALID_JSON = 107;
+
+ /**
+ * Error code indicating that the feature you tried to access is only
+ * available internally for testing purposes.
+ *
+ * @property {number} COMMAND_UNAVAILABLE
+ * @static
+ */
+ static COMMAND_UNAVAILABLE = 108;
+
+ /**
+ * You must call Parse.initialize before using the Parse library.
+ *
+ * @property {number} NOT_INITIALIZED
+ * @static
+ */
+ static NOT_INITIALIZED = 109;
+
+ /**
+ * Error code indicating that a field was set to an inconsistent type.
+ *
+ * @property {number} INCORRECT_TYPE
+ * @static
+ */
+ static INCORRECT_TYPE = 111;
+
+ /**
+ * Error code indicating an invalid channel name. A channel name is either
+ * an empty string (the broadcast channel) or contains only a-zA-Z0-9_
+ * characters and starts with a letter.
+ *
+ * @property {number} INVALID_CHANNEL_NAME
+ * @static
+ */
+ static INVALID_CHANNEL_NAME = 112;
+
+ /**
+ * Error code indicating that push is misconfigured.
+ *
+ * @property {number} PUSH_MISCONFIGURED
+ * @static
+ */
+ static PUSH_MISCONFIGURED = 115;
+
+ /**
+ * Error code indicating that the object is too large.
+ *
+ * @property {number} OBJECT_TOO_LARGE
+ * @static
+ */
+ static OBJECT_TOO_LARGE = 116;
+
+ /**
+ * Error code indicating that the operation isn't allowed for clients.
+ *
+ * @property {number} OPERATION_FORBIDDEN
+ * @static
+ */
+ static OPERATION_FORBIDDEN = 119;
+
+ /**
+ * Error code indicating the result was not found in the cache.
+ *
+ * @property {number} CACHE_MISS
+ * @static
+ */
+ static CACHE_MISS = 120;
+
+ /**
+ * Error code indicating that an invalid key was used in a nested
+ * JSONObject.
+ *
+ * @property {number} INVALID_NESTED_KEY
+ * @static
+ */
+ static INVALID_NESTED_KEY = 121;
+
+ /**
+ * Error code indicating that an invalid filename was used for ParseFile.
+ * A valid file name contains only a-zA-Z0-9_. characters and is between 1
+ * and 128 characters.
+ *
+ * @property {number} INVALID_FILE_NAME
+ * @static
+ */
+ static INVALID_FILE_NAME = 122;
+
+ /**
+ * Error code indicating an invalid ACL was provided.
+ *
+ * @property {number} INVALID_ACL
+ * @static
+ */
+ static INVALID_ACL = 123;
+
+ /**
+ * Error code indicating that the request timed out on the server. Typically
+ * this indicates that the request is too expensive to run.
+ *
+ * @property {number} TIMEOUT
+ * @static
+ */
+ static TIMEOUT = 124;
+
+ /**
+ * Error code indicating that the email address was invalid.
+ *
+ * @property {number} INVALID_EMAIL_ADDRESS
+ * @static
+ */
+ static INVALID_EMAIL_ADDRESS = 125;
+
+ /**
+ * Error code indicating a missing content type.
+ *
+ * @property {number} MISSING_CONTENT_TYPE
+ * @static
+ */
+ static MISSING_CONTENT_TYPE = 126;
+
+ /**
+ * Error code indicating a missing content length.
+ *
+ * @property {number} MISSING_CONTENT_LENGTH
+ * @static
+ */
+ static MISSING_CONTENT_LENGTH = 127;
+
+ /**
+ * Error code indicating an invalid content length.
+ *
+ * @property {number} INVALID_CONTENT_LENGTH
+ * @static
+ */
+ static INVALID_CONTENT_LENGTH = 128;
+
+ /**
+ * Error code indicating a file that was too large.
+ *
+ * @property {number} FILE_TOO_LARGE
+ * @static
+ */
+ static FILE_TOO_LARGE = 129;
+
+ /**
+ * Error code indicating an error saving a file.
+ *
+ * @property {number} FILE_SAVE_ERROR
+ * @static
+ */
+ static FILE_SAVE_ERROR = 130;
+
+ /**
+ * Error code indicating that a unique field was given a value that is
+ * already taken.
+ *
+ * @property {number} DUPLICATE_VALUE
+ * @static
+ */
+ static DUPLICATE_VALUE = 137;
+
+ /**
+ * Error code indicating that a role's name is invalid.
+ *
+ * @property {number} INVALID_ROLE_NAME
+ * @static
+ */
+ static INVALID_ROLE_NAME = 139;
+
+ /**
+ * Error code indicating that an application quota was exceeded. Upgrade to
+ * resolve.
+ *
+ * @property {number} EXCEEDED_QUOTA
+ * @static
+ */
+ static EXCEEDED_QUOTA = 140;
+
+ /**
+ * Error code indicating that a Cloud Code script failed.
+ *
+ * @property {number} SCRIPT_FAILED
+ * @static
+ */
+ static SCRIPT_FAILED = 141;
+
+ /**
+ * Error code indicating that a Cloud Code validation failed.
+ *
+ * @property {number} VALIDATION_ERROR
+ * @static
+ */
+ static VALIDATION_ERROR = 142;
+
+ /**
+ * Error code indicating that invalid image data was provided.
+ *
+ * @property {number} INVALID_IMAGE_DATA
+ * @static
+ */
+ static INVALID_IMAGE_DATA = 143;
+
+ /**
+ * Error code indicating an unsaved file.
+ *
+ * @property {number} UNSAVED_FILE_ERROR
+ * @static
+ */
+ static UNSAVED_FILE_ERROR = 151;
+
+ /**
+ * Error code indicating an invalid push time.
+ *
+ * @property {number} INVALID_PUSH_TIME_ERROR
+ * @static
+ */
+ static INVALID_PUSH_TIME_ERROR = 152;
+
+ /**
+ * Error code indicating an error deleting a file.
+ *
+ * @property {number} FILE_DELETE_ERROR
+ * @static
+ */
+ static FILE_DELETE_ERROR = 153;
+
+ /**
+ * Error code indicating an error deleting an unnamed file.
+ *
+ * @property {number} FILE_DELETE_UNNAMED_ERROR
+ * @static
+ */
+ static FILE_DELETE_UNNAMED_ERROR = 161;
+
+ /**
+ * Error code indicating that the application has exceeded its request
+ * limit.
+ *
+ * @property {number} REQUEST_LIMIT_EXCEEDED
+ * @static
+ */
+ static REQUEST_LIMIT_EXCEEDED = 155;
+
+ /**
+ * Error code indicating that the request was a duplicate and has been discarded due to
+ * idempotency rules.
+ *
+ * @property {number} DUPLICATE_REQUEST
+ * @static
+ */
+ static DUPLICATE_REQUEST = 159;
+
+ /**
+ * Error code indicating an invalid event name.
+ *
+ * @property {number} INVALID_EVENT_NAME
+ * @static
+ */
+ static INVALID_EVENT_NAME = 160;
+
+ /**
+ * Error code indicating that a field had an invalid value.
+ *
+ * @property {number} INVALID_VALUE
+ * @static
+ */
+ static INVALID_VALUE = 162;
+
+ /**
+ * Error code indicating that the username is missing or empty.
+ *
+ * @property {number} USERNAME_MISSING
+ * @static
+ */
+ static USERNAME_MISSING = 200;
+
+ /**
+ * Error code indicating that the password is missing or empty.
+ *
+ * @property {number} PASSWORD_MISSING
+ * @static
+ */
+ static PASSWORD_MISSING = 201;
+
+ /**
+ * Error code indicating that the username has already been taken.
+ *
+ * @property {number} USERNAME_TAKEN
+ * @static
+ */
+ static USERNAME_TAKEN = 202;
+
+ /**
+ * Error code indicating that the email has already been taken.
+ *
+ * @property {number} EMAIL_TAKEN
+ * @static
+ */
+ static EMAIL_TAKEN = 203;
+
+ /**
+ * Error code indicating that the email is missing, but must be specified.
+ *
+ * @property {number} EMAIL_MISSING
+ * @static
+ */
+ static EMAIL_MISSING = 204;
+
+ /**
+ * Error code indicating that a user with the specified email was not found.
+ *
+ * @property {number} EMAIL_NOT_FOUND
+ * @static
+ */
+ static EMAIL_NOT_FOUND = 205;
+
+ /**
+ * Error code indicating that a user object without a valid session could
+ * not be altered.
+ *
+ * @property {number} SESSION_MISSING
+ * @static
+ */
+ static SESSION_MISSING = 206;
+
+ /**
+ * Error code indicating that a user can only be created through signup.
+ *
+ * @property {number} MUST_CREATE_USER_THROUGH_SIGNUP
+ * @static
+ */
+ static MUST_CREATE_USER_THROUGH_SIGNUP = 207;
+
+ /**
+ * Error code indicating that an an account being linked is already linked
+ * to another user.
+ *
+ * @property {number} ACCOUNT_ALREADY_LINKED
+ * @static
+ */
+ static ACCOUNT_ALREADY_LINKED = 208;
+
+ /**
+ * Error code indicating that the current session token is invalid.
+ *
+ * @property {number} INVALID_SESSION_TOKEN
+ * @static
+ */
+ static INVALID_SESSION_TOKEN = 209;
+
+ /**
+ * Error code indicating an error enabling or verifying MFA
+ *
+ * @property {number} MFA_ERROR
+ * @static
+ */
+ static MFA_ERROR = 210;
+
+ /**
+ * Error code indicating that a valid MFA token must be provided
+ *
+ * @property {number} MFA_TOKEN_REQUIRED
+ * @static
+ */
+ static MFA_TOKEN_REQUIRED = 211;
+
+ /**
+ * Error code indicating that a user cannot be linked to an account because
+ * that account's id could not be found.
+ *
+ * @property {number} LINKED_ID_MISSING
+ * @static
+ */
+ static LINKED_ID_MISSING = 250;
+
+ /**
+ * Error code indicating that a user with a linked (e.g. Facebook) account
+ * has an invalid session.
+ *
+ * @property {number} INVALID_LINKED_SESSION
+ * @static
+ */
+ static INVALID_LINKED_SESSION = 251;
+
+ /**
+ * Error code indicating that a service being linked (e.g. Facebook or
+ * Twitter) is unsupported.
+ *
+ * @property {number} UNSUPPORTED_SERVICE
+ * @static
+ */
+ static UNSUPPORTED_SERVICE = 252;
+
+ /**
+ * Error code indicating an invalid operation occured on schema
+ *
+ * @property {number} INVALID_SCHEMA_OPERATION
+ * @static
+ */
+ static INVALID_SCHEMA_OPERATION = 255;
+
+ /**
+ * Error code indicating that there were multiple errors. Aggregate errors
+ * have an "errors" property, which is an array of error objects with more
+ * detail about each error that occurred.
+ *
+ * @property {number} AGGREGATE_ERROR
+ * @static
+ */
+ static AGGREGATE_ERROR = 600;
+
+ /**
+ * Error code indicating the client was unable to read an input file.
+ *
+ * @property {number} FILE_READ_ERROR
+ * @static
+ */
+ static FILE_READ_ERROR = 601;
+
+ /**
+ * Error code indicating a real error code is unavailable because
+ * we had to use an XDomainRequest object to allow CORS requests in
+ * Internet Explorer, which strips the body from HTTP responses that have
+ * a non-2XX status code.
+ *
+ * @property {number} X_DOMAIN_REQUEST
+ * @static
+ */
+ static X_DOMAIN_REQUEST = 602;
+}
+
+export default ParseError;
\ No newline at end of file
diff --git a/src/ParseFile.js b/src/ParseFile.ts
similarity index 86%
rename from src/ParseFile.js
rename to src/ParseFile.ts
index 1d7ff6945..cd397c8af 100644
--- a/src/ParseFile.js
+++ b/src/ParseFile.ts
@@ -4,10 +4,9 @@
/* global XMLHttpRequest, Blob */
import CoreManager from './CoreManager';
import type { FullOptions } from './RESTController';
+import ParseError from './ParseError'
-const ParseError = require('./ParseError').default;
-
-let XHR = null;
+let XHR: typeof XMLHttpRequest = null as any;
if (typeof XMLHttpRequest !== 'undefined') {
XHR = XMLHttpRequest;
}
@@ -20,22 +19,22 @@ type Uri = { uri: string };
type FileData = Array* @returns {Promise} Promise that is resolved when the delete finishes. */ - destroy(options?: FullOptions = {}) { + destroy(options: FullOptions = {}) { if (!this._name) { throw new ParseError(ParseError.FILE_DELETE_UNNAMED_ERROR, 'Cannot delete an unnamed file.'); } const destroyOptions = { useMasterKey: true }; if (options.hasOwnProperty('useMasterKey')) { - destroyOptions.useMasterKey = options.useMasterKey; + destroyOptions.useMasterKey = options.useMasterKey!; } const controller = CoreManager.getFileController(); return controller.deleteFile(this._name, destroyOptions).then(() => { - this._data = null; + this._data = undefined; this._requestTask = null; return this; }); } - toJSON(): { name: ?string, url: ?string } { + toJSON(): { __type: 'File', name?: string, url?: string } { return { __type: 'File', name: this._name, @@ -337,7 +342,7 @@ class ParseFile { }; } - equals(other: mixed): boolean { + equals(other: any): boolean { if (this === other) { return true; } @@ -409,8 +414,8 @@ class ParseFile { return file; } - static encodeBase64(bytes: Array): string { - const chunks = []; + static encodeBase64(bytes: Array | Uint8Array): string { + const chunks: string[] = []; chunks.length = Math.ceil(bytes.length / 3); for (let i = 0; i < chunks.length; i++) { const b1 = bytes[i * 3]; @@ -437,10 +442,10 @@ const DefaultController = { if (source.format !== 'file') { throw new Error('saveFile can only be used with File-type sources.'); } - const base64Data = await new Promise((res, rej) => { + const base64Data = await new Promise ((res, rej) => { // eslint-disable-next-line no-undef const reader = new FileReader(); - reader.onload = () => res(reader.result); + reader.onload = () => res(reader.result as string); reader.onerror = error => rej(error); reader.readAsDataURL(source.file); }); @@ -451,14 +456,14 @@ const DefaultController = { // use the entire string instead const data = second ? second : first; const newSource = { - format: 'base64', + format: 'base64' as const, base64: data, - type: source.type || (source.file ? source.file.type : null), + type: source.type || (source.file ? source.file.type : undefined), }; return await DefaultController.saveBase64(name, newSource, options); }, - saveBase64: function (name: string, source: FileSource, options?: FullOptions) { + saveBase64: function (name: string, source: FileSource, options: FileSaveOptions = {}) { if (source.format !== 'base64') { throw new Error('saveBase64 can only be used with Base64-type sources.'); } @@ -469,16 +474,17 @@ const DefaultController = { tags: { ...options.tags }, }, }; - delete options.metadata; - delete options.tags; + const restOptions = { ...options }; + delete restOptions.metadata; + delete restOptions.tags; if (source.type) { data._ContentType = source.type; } const path = 'files/' + name; - return CoreManager.getRESTController().request('POST', path, data, options); + return CoreManager.getRESTController().request('POST', path, data, restOptions); }, - download: function (uri, options) { + download: function (uri: string, options: any) { if (XHR) { return this.downloadAjax(uri, options); } else if (process.env.PARSE_BUILD === 'node') { @@ -506,7 +512,7 @@ const DefaultController = { } }, - downloadAjax: function (uri, options) { + downloadAjax: function (uri: string, options: any) { return new Promise((resolve, reject) => { const xhr = new XHR(); xhr.open('GET', uri, true); @@ -536,7 +542,7 @@ const DefaultController = { const headers = { 'X-Parse-Application-ID': CoreManager.get('APPLICATION_ID'), }; - if (options.useMasterKey) { + if (options?.useMasterKey) { headers['X-Parse-Master-Key'] = CoreManager.get('MASTER_KEY'); } let url = CoreManager.get('SERVER_URL'); @@ -568,4 +574,3 @@ const DefaultController = { CoreManager.setFileController(DefaultController); export default ParseFile; -exports.b64Digit = b64Digit; diff --git a/src/ParseGeoPoint.js b/src/ParseGeoPoint.ts similarity index 94% rename from src/ParseGeoPoint.js rename to src/ParseGeoPoint.ts index 3af089e98..95cdd8e95 100644 --- a/src/ParseGeoPoint.js +++ b/src/ParseGeoPoint.ts @@ -102,7 +102,7 @@ class ParseGeoPoint { }; } - equals(other: mixed): boolean { + equals(other: any): boolean { return ( other instanceof ParseGeoPoint && this.latitude === other.latitude && @@ -186,6 +186,9 @@ class ParseGeoPoint { * @static * @returns {Parse.GeoPoint} User's current location */ + // TODO: Typescript; How does this thing work? + // Seems we're using the power of Javascript by returning a value from a synchronous callback, so the value ends up correct somehow in tests? + // Should this be `async` instead for safety? Since it's a callback pattern static current() { return navigator.geolocation.getCurrentPosition(location => { return new ParseGeoPoint(location.coords.latitude, location.coords.longitude); diff --git a/src/ParseHooks.js b/src/ParseHooks.ts similarity index 54% rename from src/ParseHooks.js rename to src/ParseHooks.ts index 8a5982687..c6edf219c 100644 --- a/src/ParseHooks.js +++ b/src/ParseHooks.ts @@ -10,52 +10,54 @@ export function getTriggers() { return CoreManager.getHooksController().get('triggers'); } -export function getFunction(name) { +export function getFunction(name: string) { return CoreManager.getHooksController().get('functions', name); } -export function getTrigger(className, triggerName) { +export function getTrigger(className: string, triggerName: string) { return CoreManager.getHooksController().get('triggers', className, triggerName); } -export function createFunction(functionName, url) { +export function createFunction(functionName: string, url: string) { return create({ functionName: functionName, url: url }); } -export function createTrigger(className, triggerName, url) { +export function createTrigger(className: string, triggerName: string, url: string) { return create({ className: className, triggerName: triggerName, url: url }); } -export function create(hook) { +export function create(hook: HookDeclaration) { return CoreManager.getHooksController().create(hook); } -export function updateFunction(functionName, url) { +export function updateFunction(functionName: string, url: string) { return update({ functionName: functionName, url: url }); } -export function updateTrigger(className, triggerName, url) { +export function updateTrigger(className: string, triggerName: string, url: string) { return update({ className: className, triggerName: triggerName, url: url }); } -export function update(hook) { +export function update(hook: HookDeclaration) { return CoreManager.getHooksController().update(hook); } -export function removeFunction(functionName) { +export function removeFunction(functionName: string) { return remove({ functionName: functionName }); } -export function removeTrigger(className, triggerName) { +export function removeTrigger(className: string, triggerName: string) { return remove({ className: className, triggerName: triggerName }); } -export function remove(hook) { +export function remove(hook: HookDeleteArg) { return CoreManager.getHooksController().remove(hook); } +export type HookDeclaration = { functionName: string, url: string } | { className: string, triggerName: string, url: string }; +export type HookDeleteArg = { functionName: string } | { className: string, triggerName: string }; const DefaultController = { - get(type, functionName, triggerName) { + get(type: string, functionName?: string, triggerName?: string) { let url = '/hooks/' + type; if (functionName) { url += '/' + functionName; @@ -66,11 +68,11 @@ const DefaultController = { return this.sendRequest('GET', url); }, - create(hook) { - let url; - if (hook.functionName && hook.url) { + create(hook: HookDeclaration) { + let url: string; + if ('functionName' in hook && hook.url) { url = '/hooks/functions'; - } else if (hook.className && hook.triggerName && hook.url) { + } else if ('className' in hook && hook.triggerName && hook.url) { url = '/hooks/triggers'; } else { return Promise.reject({ error: 'invalid hook declaration', code: 143 }); @@ -78,43 +80,45 @@ const DefaultController = { return this.sendRequest('POST', url, hook); }, - remove(hook) { - let url; - if (hook.functionName) { + remove(hook: { functionName: string } | { className: string, triggerName: string }) { + let url: string; + const putParams = { ...hook }; + if ('functionName' in hook) { url = '/hooks/functions/' + hook.functionName; - delete hook.functionName; + delete (putParams as Partial ).functionName; } else if (hook.className && hook.triggerName) { url = '/hooks/triggers/' + hook.className + '/' + hook.triggerName; - delete hook.className; - delete hook.triggerName; + delete (putParams as Partial ).className; + delete (putParams as Partial ).triggerName; } else { return Promise.reject({ error: 'invalid hook declaration', code: 143 }); } return this.sendRequest('PUT', url, { __op: 'Delete' }); }, - update(hook) { - let url; - if (hook.functionName && hook.url) { + update(hook: HookDeclaration) { + let url: string; + const postParams = { ...hook }; + if ('functionName' in hook && hook.url) { url = '/hooks/functions/' + hook.functionName; - delete hook.functionName; - } else if (hook.className && hook.triggerName && hook.url) { + delete (postParams as Partial ).functionName; + } else if ('className' in hook && hook.triggerName && hook.url) { url = '/hooks/triggers/' + hook.className + '/' + hook.triggerName; - delete hook.className; - delete hook.triggerName; + delete (postParams as Partial ).className; + delete (postParams as Partial ).triggerName; } else { return Promise.reject({ error: 'invalid hook declaration', code: 143 }); } - return this.sendRequest('PUT', url, hook); + return this.sendRequest('PUT', url, postParams); }, - sendRequest(method, url, body) { + sendRequest(method: string, url: string, body?: any) { return CoreManager.getRESTController() .request(method, url, body, { useMasterKey: true }) .then(res => { const decoded = decode(res); if (decoded) { - return Promise.resolve(decoded); + return Promise.resolve (decoded); } return Promise.reject( new ParseError(ParseError.INVALID_JSON, 'The server returned an invalid response.') diff --git a/src/ParseInstallation.js b/src/ParseInstallation.ts similarity index 91% rename from src/ParseInstallation.js rename to src/ParseInstallation.ts index 380b5c8a9..c624aa27f 100644 --- a/src/ParseInstallation.js +++ b/src/ParseInstallation.ts @@ -7,7 +7,7 @@ import ParseObject from './ParseObject'; import type { AttributeMap } from './ObjectStateMutations'; export default class Installation extends ParseObject { - constructor(attributes: ?AttributeMap) { + constructor(attributes?: AttributeMap) { super('_Installation'); if (attributes && typeof attributes === 'object') { if (!this.set(attributes || {})) { diff --git a/src/ParseLiveQuery.js b/src/ParseLiveQuery.ts similarity index 95% rename from src/ParseLiveQuery.js rename to src/ParseLiveQuery.ts index eea9e59e2..0a507d6a6 100644 --- a/src/ParseLiveQuery.js +++ b/src/ParseLiveQuery.ts @@ -3,7 +3,7 @@ */ import LiveQueryClient from './LiveQueryClient'; import CoreManager from './CoreManager'; - +import type { EventEmitter } from 'events'; function getLiveQueryClient(): Promise { return CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); } @@ -36,6 +36,10 @@ function getLiveQueryClient(): Promise { * @static */ class LiveQuery { + emitter: EventEmitter; + on: EventEmitter['on']; + emit: EventEmitter['emit']; + constructor() { const EventEmitter = CoreManager.getEventEmitter(); this.emitter = new EventEmitter(); @@ -44,14 +48,14 @@ class LiveQuery { // adding listener so process does not crash // best practice is for developer to register their own listener - this.on('error', () => {}); + this.on('error', () => { }); } /** * After open is called, the LiveQuery will try to send a connect request * to the LiveQuery server. */ - async open(): void { + async open() { const liveQueryClient = await getLiveQueryClient(); liveQueryClient.open(); } @@ -63,7 +67,7 @@ class LiveQuery { * If you call query.subscribe() after this, we'll create a new WebSocket * connection to the LiveQuery server. */ - async close(): void { + async close() { const liveQueryClient = await getLiveQueryClient(); liveQueryClient.close(); } diff --git a/src/ParseObject.js b/src/ParseObject.ts similarity index 91% rename from src/ParseObject.js rename to src/ParseObject.ts index 32701191b..270a0ba9e 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.ts @@ -36,12 +36,13 @@ import unsavedChildren from './unsavedChildren'; import type { AttributeMap, OpsMap } from './ObjectStateMutations'; import type { RequestOptions, FullOptions } from './RESTController'; -const uuidv4 = require('./uuid'); +import uuidv4 from './uuid'; export type Pointer = { __type: string, className: string, - objectId: string, + objectId?: string, + _localId?: string }; type SaveParams = { @@ -53,6 +54,7 @@ type SaveParams = { export type SaveOptions = FullOptions & { cascadeSave?: boolean, context?: AttributeMap, + batchSize?: number }; // Mapping of class names to constructors, so we can populate objects from the @@ -80,6 +82,10 @@ function getServerUrlPath() { return url.substr(url.indexOf('/')); } +type ObjectFetchOptions = { + useMasterKey?: boolean, sessionToken?: string, include?: string | string[], context?: AttributeMap, +} + /** * Creates a new model with defined attributes. * @@ -105,8 +111,8 @@ class ParseObject { * @param {object} options The options for this object instance. */ constructor( - className: ?string | { className: string, [attr: string]: mixed }, - attributes?: { [attr: string]: mixed }, + className?: string | { className: string, [attr: string]: any }, + attributes?: { [attr: string]: any }, options?: { ignoreValidation: boolean } ) { // Enable legacy initializers @@ -114,7 +120,7 @@ class ParseObject { this.initialize.apply(this, arguments); } - let toSet = null; + let toSet: { [attr: string]: any } | null = null; this._objCount = objectCount++; if (typeof className === 'string') { this.className = className; @@ -130,7 +136,7 @@ class ParseObject { } } if (attributes && typeof attributes === 'object') { - options = attributes; + options = attributes as any; } } if (toSet && !this.set(toSet, options)) { @@ -143,10 +149,11 @@ class ParseObject { * * @property {string} id */ - id: ?string; - _localId: ?string; + id?: string; + _localId?: string; _objCount: number; className: string; + static className: string; /* Prototype getters / setters */ @@ -161,7 +168,7 @@ class ParseObject { * @property {Date} createdAt * @returns {Date} */ - get createdAt(): ?Date { + get createdAt(): Date | undefined { return this._getServerData().createdAt; } @@ -171,7 +178,7 @@ class ParseObject { * @property {Date} updatedAt * @returns {Date} */ - get updatedAt(): ?Date { + get updatedAt(): Date | undefined { return this._getServerData().updatedAt; } @@ -280,7 +287,7 @@ class ParseObject { } _toFullJSON(seen?: Array , offline?: boolean): AttributeMap { - const json: { [key: string]: mixed } = this.toJSON(seen, offline); + const json: { [key: string]: any } = this.toJSON(seen, offline); json.__type = 'Object'; json.className = this.className; return json; @@ -290,7 +297,7 @@ class ParseObject { const pending = this._getPendingOps(); const dirtyObjects = this._getDirtyObjectAttributes(); const json = {}; - let attr; + let attr: string; for (attr in dirtyObjects) { let isDotNotation = false; @@ -346,7 +353,12 @@ class ParseObject { } const stateController = CoreManager.getObjectStateController(); stateController.initializeState(this._getStateIdentifier()); - const decoded = {}; + const decoded: Partial<{ + createdAt?: Date, + updatedAt?: Date, + ACL?: any // TODO: Maybe type this better? + [key: string]: any + }> = {}; for (const attr in serverData) { if (attr === 'ACL') { decoded[attr] = new ParseACL(serverData[attr]); @@ -358,10 +370,10 @@ class ParseObject { } } if (decoded.createdAt && typeof decoded.createdAt === 'string') { - decoded.createdAt = parseDate(decoded.createdAt); + decoded.createdAt = parseDate(decoded.createdAt) || undefined; } if (decoded.updatedAt && typeof decoded.updatedAt === 'string') { - decoded.updatedAt = parseDate(decoded.updatedAt); + decoded.updatedAt = parseDate(decoded.updatedAt) || undefined; } if (!decoded.updatedAt && decoded.createdAt) { decoded.updatedAt = decoded.createdAt; @@ -395,16 +407,22 @@ class ParseObject { } _handleSaveResponse(response: AttributeMap, status: number) { - const changes = {}; + const changes: Partial<{ + createdAt: string, + updatedAt: string, + [key: string]: any + }> = {}; let attr; const stateController = CoreManager.getObjectStateController(); const pending = stateController.popPendingState(this._getStateIdentifier()); - for (attr in pending) { - if (pending[attr] instanceof RelationOp) { - changes[attr] = pending[attr].applyTo(undefined, this, attr); - } else if (!(attr in response)) { - // Only SetOps and UnsetOps should not come back with results - changes[attr] = pending[attr].applyTo(undefined); + if (pending) { + for (attr in pending) { + if (pending[attr] instanceof RelationOp) { + changes[attr] = pending[attr].applyTo(undefined, this, attr); + } else if (!(attr in response)) { + // Only SetOps and UnsetOps should not come back with results + changes[attr] = pending[attr].applyTo(undefined); + } } } for (attr in response) { @@ -462,7 +480,7 @@ class ParseObject { toJSON(seen: Array | void, offline?: boolean): AttributeMap { const seenEntry = this.id ? this.className + ':' + this.id : this; seen = seen || [seenEntry]; - const json = {}; + const json: AttributeMap = {}; const attrs = this.attributes; for (const attr in attrs) { if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) { @@ -488,7 +506,7 @@ class ParseObject { * @param {object} other - An other object ot compare * @returns {boolean} */ - equals(other: mixed): boolean { + equals(other: any): boolean { if (this === other) { return true; } @@ -602,7 +620,7 @@ class ParseObject { * @param {string} attr The string name of an attribute. * @returns {*} */ - get(attr: string): mixed { + get(attr: string): any { return this.attributes[attr]; } @@ -689,7 +707,8 @@ class ParseObject { * The only supported option is error
. * @returns {(ParseObject|boolean)} true if the set succeeded. */ - set(key: mixed, value: mixed, options?: mixed): ParseObject | boolean { + set(key: any, value?: any, options?: any): ParseObject | boolean { + // TODO: Improve types here without breaking stuff. let changes = {}; const newOps = {}; if (key && typeof key === 'object') { @@ -698,13 +717,15 @@ class ParseObject { } else if (typeof key === 'string') { changes[key] = value; } else { + // Key is weird; just return ourself return this; } options = options || {}; - let readonly = []; - if (typeof this.constructor.readOnlyAttributes === 'function') { - readonly = readonly.concat(this.constructor.readOnlyAttributes()); + /** Readonly attributes of the object class */ + let readonly: string[] = []; + if (typeof ((this.constructor as any).readOnlyAttributes) === 'function') { + readonly = readonly.concat((this.constructor as any).readOnlyAttributes()); } for (const k in changes) { if (k === 'createdAt' || k === 'updatedAt') { @@ -787,7 +808,7 @@ class ParseObject { * @param options * @returns {(ParseObject | boolean)} */ - unset(attr: string, options?: { [opt: string]: mixed }): ParseObject | boolean { + unset(attr: string, options?: { [opt: string]: any }): ParseObject | boolean { options = options || {}; options.unset = true; return this.set(attr, null, options); @@ -837,7 +858,7 @@ class ParseObject { * @param item {} The item to add. * @returns {(ParseObject | boolean)} */ - add(attr: string, item: mixed): ParseObject | boolean { + add(attr: string, item: any): ParseObject | boolean { return this.set(attr, new AddOp([item])); } @@ -849,7 +870,7 @@ class ParseObject { * @param items {Object[]} The items to add. * @returns {(ParseObject | boolean)} */ - addAll(attr: string, items: Array): ParseObject | boolean { + addAll(attr: string, items: Array ): ParseObject | boolean { return this.set(attr, new AddOp(items)); } @@ -862,7 +883,7 @@ class ParseObject { * @param item {} The object to add. * @returns {(ParseObject | boolean)} */ - addUnique(attr: string, item: mixed): ParseObject | boolean { + addUnique(attr: string, item: any): ParseObject | boolean { return this.set(attr, new AddUniqueOp([item])); } @@ -875,7 +896,7 @@ class ParseObject { * @param items {Object[]} The objects to add. * @returns {(ParseObject | boolean)} */ - addAllUnique(attr: string, items: Array ): ParseObject | boolean { + addAllUnique(attr: string, items: Array ): ParseObject | boolean { return this.set(attr, new AddUniqueOp(items)); } @@ -887,7 +908,7 @@ class ParseObject { * @param item {} The object to remove. * @returns {(ParseObject | boolean)} */ - remove(attr: string, item: mixed): ParseObject | boolean { + remove(attr: string, item: any): ParseObject | boolean { return this.set(attr, new RemoveOp([item])); } @@ -899,7 +920,7 @@ class ParseObject { * @param items {Object[]} The object to remove. * @returns {(ParseObject | boolean)} */ - removeAll(attr: string, items: Array ): ParseObject | boolean { + removeAll(attr: string, items: Array ): ParseObject | boolean { return this.set(attr, new RemoveOp(items)); } @@ -912,7 +933,7 @@ class ParseObject { * @param attr {String} The key. * @returns {Parse.Op | undefined} The operation, or undefined if none. */ - op(attr: string): ?Op { + op(attr: string): Op | undefined { const pending = this._getPendingOps(); for (let i = pending.length; i--;) { if (pending[i][attr]) { @@ -926,11 +947,11 @@ class ParseObject { * * @returns {Parse.Object} */ - clone(): any { - const clone = new this.constructor(this.className); + clone(): typeof this { + const clone = new (this.constructor as new (...args: ConstructorParameters ) => this)(this.className); let attributes = this.attributes; - if (typeof this.constructor.readOnlyAttributes === 'function') { - const readonly = this.constructor.readOnlyAttributes() || []; + if (typeof (this.constructor as any).readOnlyAttributes === 'function') { + const readonly = (this.constructor as any).readOnlyAttributes() || []; // Attributes are frozen, so we have to rebuild an object, // rather than delete readonly keys const copy = {}; @@ -953,7 +974,7 @@ class ParseObject { * @returns {Parse.Object} */ newInstance(): any { - const clone = new this.constructor(this.className); + const clone = new (this.constructor as new (...args: ConstructorParameters ) => this)(this.className); clone.id = this.id; if (singleInstance) { // Just return an object with the right id @@ -1060,7 +1081,7 @@ class ParseObject { * @returns {Parse.ACL|null} An instance of Parse.ACL. * @see Parse.Object#get */ - getACL(): ?ParseACL { + getACL(): ParseACL | null { const acl = this.get('ACL'); if (acl instanceof ParseACL) { return acl; @@ -1076,7 +1097,7 @@ class ParseObject { * @returns {(ParseObject | boolean)} Whether the set passed validation. * @see Parse.Object#set */ - setACL(acl: ParseACL, options?: mixed): ParseObject | boolean { + setACL(acl: ParseACL, options?: any): ParseObject | boolean { return this.set('ACL', acl, options); } @@ -1109,8 +1130,8 @@ class ParseObject { const attributes = this.attributes; const erasable = {}; let readonly = ['createdAt', 'updatedAt']; - if (typeof this.constructor.readOnlyAttributes === 'function') { - readonly = readonly.concat(this.constructor.readOnlyAttributes()); + if (typeof (this.constructor as any).readOnlyAttributes === 'function') { + readonly = readonly.concat((this.constructor as any).readOnlyAttributes()); } for (const attr in attributes) { if (readonly.indexOf(attr) < 0) { @@ -1137,9 +1158,9 @@ class ParseObject { * @returns {Promise} A promise that is fulfilled when the fetch * completes. */ - fetch(options: RequestOptions): Promise { + fetch(options: ObjectFetchOptions): Promise { options = options || {}; - const fetchOptions = {}; + const fetchOptions: ObjectFetchOptions = {}; if (options.hasOwnProperty('useMasterKey')) { fetchOptions.useMasterKey = options.useMasterKey; } @@ -1154,13 +1175,13 @@ class ParseObject { if (Array.isArray(options.include)) { options.include.forEach(key => { if (Array.isArray(key)) { - fetchOptions.include = fetchOptions.include.concat(key); + fetchOptions.include = fetchOptions.include!.concat(key); } else { - fetchOptions.include.push(key); + (fetchOptions.include as string[]).push(key); } }); } else { - fetchOptions.include.push(options.include); + fetchOptions.include.push(options.include!); // already checked hasOwnProperty('include') } } const controller = CoreManager.getObjectController(); @@ -1185,7 +1206,7 @@ class ParseObject { * @returns {Promise} A promise that is fulfilled when the fetch * completes. */ - fetchWithInclude(keys: String | Array >, options: RequestOptions): Promise { + fetchWithInclude(keys: String | Array >, options: RequestOptions): Promise { options = options || {}; options.include = keys; return this.fetch(options); @@ -1215,7 +1236,7 @@ class ParseObject { * @returns {Promise} A promise that is fulfilled when the save * completes. */ - async saveEventually(options: SaveOptions): Promise { + async saveEventually(options: SaveOptions): Promise { try { await this.save(null, options); } catch (e) { @@ -1291,14 +1312,14 @@ class ParseObject { * completes. */ save( - arg1: ?string | { [attr: string]: mixed }, - arg2: SaveOptions | mixed, + arg1: undefined | string | { [attr: string]: any } | null, + arg2: SaveOptions | any, arg3?: SaveOptions - ): Promise { - let attrs; - let options; - if (typeof arg1 === 'object' || typeof arg1 === 'undefined') { - attrs = arg1; + ): Promise { + let attrs: { [attr: string]: any } | null; + let options: SaveOptions | undefined; + if (typeof arg1 === 'object' || typeof arg1 === 'undefined' || arg1 == null) { + attrs = (arg1 as { [attr: string]: any }) || null; if (typeof arg2 === 'object') { options = arg2; } @@ -1317,7 +1338,7 @@ class ParseObject { } options = options || {}; - const saveOptions = {}; + const saveOptions: SaveOptions = {}; if (options.hasOwnProperty('useMasterKey')) { saveOptions.useMasterKey = !!options.useMasterKey; } @@ -1334,7 +1355,7 @@ class ParseObject { const unsaved = options.cascadeSave !== false ? unsavedChildren(this) : null; return controller.save(unsaved, saveOptions).then(() => { return controller.save(this, saveOptions); - }); + }) as Promise as Promise ; } /** @@ -1359,7 +1380,7 @@ class ParseObject { * @returns {Promise} A promise that is fulfilled when the destroy * completes. */ - async destroyEventually(options: RequestOptions): Promise { + async destroyEventually(options: RequestOptions): Promise { try { await this.destroy(options); } catch (e) { @@ -1385,9 +1406,9 @@ class ParseObject { * @returns {Promise} A promise that is fulfilled when the destroy * completes. */ - destroy(options: RequestOptions): Promise { + destroy(options: RequestOptions): Promise { options = options || {}; - const destroyOptions = {}; + const destroyOptions: RequestOptions = {}; if (options.hasOwnProperty('useMasterKey')) { destroyOptions.useMasterKey = options.useMasterKey; } @@ -1400,7 +1421,7 @@ class ParseObject { if (!this.id) { return Promise.resolve(); } - return CoreManager.getObjectController().destroy(this, destroyOptions); + return CoreManager.getObjectController().destroy(this, destroyOptions) as Promise ; } /** @@ -1552,7 +1573,7 @@ class ParseObject { * @returns {Parse.Object[]} */ static fetchAll(list: Array , options: RequestOptions = {}) { - const queryOptions = {}; + const queryOptions: RequestOptions = {}; if (options.hasOwnProperty('useMasterKey')) { queryOptions.useMasterKey = options.useMasterKey; } @@ -1662,10 +1683,8 @@ class ParseObject { * @static * @returns {Parse.Object[]} */ - static fetchAllIfNeeded(list: Array , options) { - options = options || {}; - - const queryOptions = {}; + static fetchAllIfNeeded(list: Array , options: ObjectFetchOptions = {}) { + const queryOptions: ObjectFetchOptions = {}; if (options.hasOwnProperty('useMasterKey')) { queryOptions.useMasterKey = options.useMasterKey; } @@ -1678,8 +1697,8 @@ class ParseObject { return CoreManager.getObjectController().fetch(list, false, queryOptions); } - static handleIncludeOptions(options) { - let include = []; + static handleIncludeOptions(options: { include?: string | string[] }) { + let include: string[] = []; if (Array.isArray(options.include)) { options.include.forEach(key => { if (Array.isArray(key)) { @@ -1689,7 +1708,7 @@ class ParseObject { } }); } else { - include.push(options.include); + include.push(options.include!); } return include; } @@ -1740,8 +1759,8 @@ class ParseObject { * @returns {Promise} A promise that is fulfilled when the destroyAll * completes. */ - static destroyAll(list: Array , options = {}) { - const destroyOptions = {}; + static destroyAll(list: Array , options: SaveOptions = {}) { + const destroyOptions: SaveOptions = {}; if (options.hasOwnProperty('useMasterKey')) { destroyOptions.useMasterKey = options.useMasterKey; } @@ -1775,8 +1794,8 @@ class ParseObject { * @static * @returns {Parse.Object[]} */ - static saveAll(list: Array , options: RequestOptions = {}) { - const saveOptions = {}; + static saveAll(list: Array , options: SaveOptions = {}) { + const saveOptions: SaveOptions = {}; if (options.hasOwnProperty('useMasterKey')) { saveOptions.useMasterKey = options.useMasterKey; } @@ -1806,7 +1825,7 @@ class ParseObject { * @static * @returns {Parse.Object} A Parse.Object reference. */ - static createWithoutData(id: string) { + static createWithoutData(id: string): ParseObject { const obj = new this(); obj.id = id; return obj; @@ -1822,13 +1841,13 @@ class ParseObject { * @static * @returns {Parse.Object} A Parse.Object reference */ - static fromJSON(json: any, override?: boolean, dirty?: boolean) { + static fromJSON(json: any, override?: boolean, dirty?: boolean): ParseObject { if (!json.className) { throw new Error('Cannot create an object without a className'); } const constructor = classMap[json.className]; const o = constructor ? new constructor(json.className) : new ParseObject(json.className); - const otherAttributes = {}; + const otherAttributes: AttributeMap = {}; for (const attr in json) { if (attr !== 'className' && attr !== '__type') { otherAttributes[attr] = json[attr]; @@ -1877,7 +1896,7 @@ class ParseObject { if (typeof constructor !== 'function') { throw new TypeError( 'You must register the subclass constructor. ' + - 'Did you attempt to register an instance of the subclass?' + 'Did you attempt to register an instance of the subclass?' ); } classMap[className] = constructor; @@ -1935,7 +1954,7 @@ class ParseObject { * this method. * @returns {Parse.Object} A new subclass of Parse.Object. */ - static extend(className: any, protoProps: any, classProps: any) { + static extend(className: any, protoProps?: any, classProps?: any) { if (typeof className !== 'string') { if (className && typeof className.className === 'string') { return ParseObject.extend(className.className, className, protoProps); @@ -1950,7 +1969,7 @@ class ParseObject { } let parentProto = ParseObject.prototype; - if (this.hasOwnProperty('__super__') && this.__super__) { + if (this.hasOwnProperty('__super__') && (this as any).__super__) { parentProto = this.prototype; } let ParseObjectSubclass = function (attributes, options) { @@ -1976,15 +1995,16 @@ class ParseObject { if (classMap[adjustedClassName]) { ParseObjectSubclass = classMap[adjustedClassName]; } else { - ParseObjectSubclass.extend = function (name, protoProps, classProps) { + // TODO: Maybe there is a more elegant solution to this? + (ParseObjectSubclass as any).extend = function (name: string, protoProps: any, classProps: any) { if (typeof name === 'string') { return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps); } return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps); }; - ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData; - ParseObjectSubclass.className = adjustedClassName; - ParseObjectSubclass.__super__ = parentProto; + (ParseObjectSubclass as any).createWithoutData = ParseObject.createWithoutData; + (ParseObjectSubclass as any).className = adjustedClassName; + (ParseObjectSubclass as any).__super__ = parentProto; ParseObjectSubclass.prototype = Object.create(parentProto, { constructor: { value: ParseObjectSubclass, @@ -2195,17 +2215,19 @@ const DefaultController = { target: ParseObject | Array , forceFetch: boolean, options: RequestOptions - ): Promise | ParseObject> { + ): Promise | ParseObject | undefined> { const localDatastore = CoreManager.getLocalDatastore(); if (Array.isArray(target)) { if (target.length < 1) { return Promise.resolve([]); } - const objs = []; - const ids = []; - let className = null; - const results = []; - let error = null; + /** Resulting Parse.Objects that have data */ + const objs: ParseObject[] = []; + /** IDs to fetch */ + const ids: string[] = []; + let className: null | string = null; + const results: ParseObject[] = []; + let error: ParseError | null = null; target.forEach(el => { if (error) { return; @@ -2223,7 +2245,7 @@ const DefaultController = { error = new ParseError(ParseError.MISSING_OBJECT_ID, 'All objects must have an ID'); } if (forceFetch || !el.isDataAvailable()) { - ids.push(el.id); + ids.push(el.id!); // Already checked e.id above. objs.push(el); } results.push(el); @@ -2231,16 +2253,17 @@ const DefaultController = { if (error) { return Promise.reject(error); } - const query = new ParseQuery(className); + // Construct a ParseQuery that finds objects with matching IDs + const query = new ParseQuery(className!); query.containedIn('objectId', ids); if (options && options.include) { query.include(options.include); } query._limit = ids.length; - return query.find(options).then(async objects => { - const idMap = {}; + return query.find(options).then(async (objects: ParseObject[]) => { + const idMap: Record = {}; objects.forEach(o => { - idMap[o.id] = o; + idMap[o.id!] = o; }); for (let i = 0; i < objs.length; i++) { const obj = objs[i]; @@ -2253,7 +2276,7 @@ const DefaultController = { } } if (!singleInstance) { - // If single instance objects are disabled, we need to replace the + // If single instance objects are disabled, we need to replace the objects in the results array. for (let i = 0; i < results.length; i++) { const obj = results[i]; if (obj && obj.id && idMap[obj.id]) { @@ -2275,7 +2298,7 @@ const DefaultController = { ); } const RESTController = CoreManager.getRESTController(); - const params = {}; + const params: RequestOptions = {}; if (options && options.include) { params.include = options.include.join(); } @@ -2292,13 +2315,14 @@ const DefaultController = { return target; }); } - return Promise.resolve(); + // Not Array, and not ParseObject; return undefined/void. + return Promise.resolve(undefined); }, async destroy( target: ParseObject | Array , - options: RequestOptions - ): Promise | ParseObject> { + options: SaveOptions + ): Promise | ParseObject> { const batchSize = options && options.batchSize ? options.batchSize : CoreManager.get('REQUEST_BATCH_SIZE'); const localDatastore = CoreManager.getLocalDatastore(); @@ -2308,7 +2332,7 @@ const DefaultController = { if (target.length < 1) { return Promise.resolve([]); } - const batches = [[]]; + const batches: ParseObject[][] = [[]]; target.forEach(obj => { if (!obj.id) { return; @@ -2323,7 +2347,7 @@ const DefaultController = { batches.pop(); } let deleteCompleted = Promise.resolve(); - const errors = []; + const errors: ParseError[] = []; batches.forEach(batch => { deleteCompleted = deleteCompleted.then(() => { return RESTController.request( @@ -2375,7 +2399,7 @@ const DefaultController = { return Promise.resolve(target); }, - save(target: ParseObject | Array , options: RequestOptions) { + save(target: ParseObject | Array | null, options: RequestOptions) { const batchSize = options && options.batchSize ? options.batchSize : CoreManager.get('REQUEST_BATCH_SIZE'); const localDatastore = CoreManager.getLocalDatastore(); @@ -2394,13 +2418,14 @@ const DefaultController = { let unsaved = target.concat(); for (let i = 0; i < target.length; i++) { - if (target[i] instanceof ParseObject) { - unsaved = unsaved.concat(unsavedChildren(target[i], true)); + const target_i = target[i]; + if (target_i instanceof ParseObject) { + unsaved = unsaved.concat(unsavedChildren(target_i, true)); } } unsaved = unique(unsaved); - const filesSaved: Array = []; + const filesSaved: Array | undefined> = []; let pending: Array = []; unsaved.forEach(el => { if (el instanceof ParseFile) { @@ -2411,14 +2436,14 @@ const DefaultController = { }); return Promise.all(filesSaved).then(() => { - let objectError = null; + let objectError: null | ParseError = null; return continueWhile( () => { return pending.length > 0; }, () => { - const batch = []; - const nextPending = []; + const batch: ParseObject[] = []; + const nextPending: ParseObject[] = []; pending.forEach(el => { if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(el, 'id') && !el.id) { throw new ParseError( @@ -2442,11 +2467,11 @@ const DefaultController = { // Queue up tasks for each object in the batch. // When every task is ready, the API request will execute - const batchReturned = new resolvingPromise(); - const batchReady = []; - const batchTasks = []; + const batchReturned = resolvingPromise(); + const batchReady: ReturnType >[] = []; + const batchTasks: Promise [] = []; batch.forEach((obj, index) => { - const ready = new resolvingPromise(); + const ready = resolvingPromise (); batchReady.push(ready); const task = function () { ready.resolve(); @@ -2503,7 +2528,7 @@ const DefaultController = { for (const object of target) { // Make sure that it is a ParseObject before updating it into the localDataStore if (object instanceof ParseObject) { - await localDatastore._updateLocalIdForObject(mapIdForPin[object.id], object); + await localDatastore._updateLocalIdForObject(mapIdForPin[object.id!], object); await localDatastore._updateObjectIfPinned(object); } } diff --git a/src/ParseOp.js b/src/ParseOp.ts similarity index 76% rename from src/ParseOp.js rename to src/ParseOp.ts index a35485c60..5da8cde7f 100644 --- a/src/ParseOp.js +++ b/src/ParseOp.ts @@ -5,71 +5,73 @@ import arrayContainsObject from './arrayContainsObject'; import decode from './decode'; import encode from './encode'; -import ParseObject from './ParseObject'; +import ParseObject, { Pointer } from './ParseObject'; import ParseRelation from './ParseRelation'; import unique from './unique'; -export function opFromJSON(json: { [key: string]: any }): ?Op { +export function opFromJSON(json: { [key: string]: any }): Op | null { if (!json || !json.__op) { return null; } switch (json.__op) { - case 'Delete': - return new UnsetOp(); - case 'Increment': - return new IncrementOp(json.amount); - case 'Add': - return new AddOp(decode(json.objects)); - case 'AddUnique': - return new AddUniqueOp(decode(json.objects)); - case 'Remove': - return new RemoveOp(decode(json.objects)); - case 'AddRelation': { - const toAdd = decode(json.objects); - if (!Array.isArray(toAdd)) { - return new RelationOp([], []); - } - return new RelationOp(toAdd, []); - } - case 'RemoveRelation': { - const toRemove = decode(json.objects); - if (!Array.isArray(toRemove)) { - return new RelationOp([], []); + case 'Delete': + return new UnsetOp(); + case 'Increment': + return new IncrementOp(json.amount); + case 'Add': + return new AddOp(decode(json.objects)); + case 'AddUnique': + return new AddUniqueOp(decode(json.objects)); + case 'Remove': + return new RemoveOp(decode(json.objects)); + case 'AddRelation': { + const toAdd = decode(json.objects); + if (!Array.isArray(toAdd)) { + return new RelationOp([], []); + } + return new RelationOp(toAdd, []); } - return new RelationOp([], toRemove); - } - case 'Batch': { - let toAdd = []; - let toRemove = []; - for (let i = 0; i < json.ops.length; i++) { - if (json.ops[i].__op === 'AddRelation') { - toAdd = toAdd.concat(decode(json.ops[i].objects)); - } else if (json.ops[i].__op === 'RemoveRelation') { - toRemove = toRemove.concat(decode(json.ops[i].objects)); + case 'RemoveRelation': { + const toRemove = decode(json.objects); + if (!Array.isArray(toRemove)) { + return new RelationOp([], []); } + return new RelationOp([], toRemove); + } + case 'Batch': { + let toAdd = []; + let toRemove = []; + for (let i = 0; i < json.ops.length; i++) { + if (json.ops[i].__op === 'AddRelation') { + toAdd = toAdd.concat(decode(json.ops[i].objects)); + } else if (json.ops[i].__op === 'RemoveRelation') { + toRemove = toRemove.concat(decode(json.ops[i].objects)); + } + } + return new RelationOp(toAdd, toRemove); } - return new RelationOp(toAdd, toRemove); - } } return null; } export class Op { + __op?: string; + objects?: any[]; // Empty parent class - applyTo(value: mixed): mixed {} /* eslint-disable-line no-unused-vars */ - mergeWith(previous: Op): ?Op {} /* eslint-disable-line no-unused-vars */ - toJSON(): mixed {} + applyTo(...value: any): any { } /* eslint-disable-line no-unused-vars */ + mergeWith(previous: Op): Op | void { } /* eslint-disable-line no-unused-vars */ + toJSON(offline?: boolean): any { } } export class SetOp extends Op { - _value: ?mixed; + _value: any; - constructor(value: mixed) { + constructor(value: any) { super(); this._value = value; } - applyTo(): mixed { + applyTo(): any { return this._value; } @@ -77,7 +79,7 @@ export class SetOp extends Op { return new SetOp(this._value); } - toJSON(offline?: boolean) { + toJSON(offline?: boolean) : any { return encode(this._value, false, true, undefined, offline); } } @@ -107,7 +109,7 @@ export class IncrementOp extends Op { this._amount = amount; } - applyTo(value: ?mixed): number { + applyTo(value: any): number { if (typeof value === 'undefined') { return this._amount; } @@ -139,14 +141,14 @@ export class IncrementOp extends Op { } export class AddOp extends Op { - _value: Array ; + _value: Array ; - constructor(value: mixed | Array ) { + constructor(value: any | Array ) { super(); this._value = Array.isArray(value) ? value : [value]; } - applyTo(value: mixed): Array { + applyTo(value: any): Array { if (value == null) { return this._value; } @@ -172,25 +174,25 @@ export class AddOp extends Op { throw new Error('Cannot merge Add Op with the previous Op'); } - toJSON(): { __op: string, objects: mixed } { + toJSON(): { __op: string, objects: any } { return { __op: 'Add', objects: encode(this._value, false, true) }; } } export class AddUniqueOp extends Op { - _value: Array ; + _value: Array ; - constructor(value: mixed | Array ) { + constructor(value: any | Array ) { super(); this._value = unique(Array.isArray(value) ? value : [value]); } - applyTo(value: mixed | Array ): Array { + applyTo(value: any | Array ): Array { if (value == null) { return this._value || []; } if (Array.isArray(value)) { - const toAdd = []; + const toAdd: any[] = []; this._value.forEach(v => { if (v instanceof ParseObject) { if (!arrayContainsObject(value, v)) { @@ -223,20 +225,20 @@ export class AddUniqueOp extends Op { throw new Error('Cannot merge AddUnique Op with the previous Op'); } - toJSON(): { __op: string, objects: mixed } { + toJSON(): { __op: string, objects: any } { return { __op: 'AddUnique', objects: encode(this._value, false, true) }; } } export class RemoveOp extends Op { - _value: Array ; + _value: Array ; - constructor(value: mixed | Array ) { + constructor(value: any | Array ) { super(); this._value = unique(Array.isArray(value) ? value : [value]); } - applyTo(value: mixed | Array ): Array { + applyTo(value: any | Array ): Array { if (value == null) { return []; } @@ -291,13 +293,13 @@ export class RemoveOp extends Op { throw new Error('Cannot merge Remove Op with the previous Op'); } - toJSON(): { __op: string, objects: mixed } { + toJSON(): { __op: string, objects: any } { return { __op: 'Remove', objects: encode(this._value, false, true) }; } } export class RelationOp extends Op { - _targetClassName: ?string; + _targetClassName: string | null; relationsToAdd: Array ; relationsToRemove: Array ; @@ -327,16 +329,16 @@ export class RelationOp extends Op { if (this._targetClassName !== obj.className) { throw new Error( 'Tried to create a Relation with 2 different object types: ' + - this._targetClassName + - ' and ' + - obj.className + - '.' + this._targetClassName + + ' and ' + + obj.className + + '.' ); } return obj.id; } - applyTo(value: mixed, object?: { className: string, id: ?string }, key?: string): ?ParseRelation { + applyTo(value: any, object?: { className: string, id?: string }, key?: string): ParseRelation { if (!value) { if (!object || !key) { throw new Error( @@ -359,10 +361,10 @@ export class RelationOp extends Op { if (this._targetClassName !== value.targetClassName) { throw new Error( 'Related object must be a ' + - value.targetClassName + - ', but a ' + - this._targetClassName + - ' was passed in.' + value.targetClassName + + ', but a ' + + this._targetClassName + + ' was passed in.' ); } } else { @@ -386,10 +388,10 @@ export class RelationOp extends Op { if (previous._targetClassName && previous._targetClassName !== this._targetClassName) { throw new Error( 'Related object must be of class ' + - previous._targetClassName + - ', but ' + - (this._targetClassName || 'null') + - ' was passed in.' + previous._targetClassName + + ', but ' + + (this._targetClassName || 'null') + + ' was passed in.' ); } const newAdd = previous.relationsToAdd.concat([]); @@ -427,18 +429,19 @@ export class RelationOp extends Op { throw new Error('Cannot merge Relation Op with the previous Op'); } - toJSON(): { __op?: string, objects?: mixed, ops?: mixed } { - const idToPointer = id => { - return { + toJSON(): { __op?: string, objects?: any, ops?: any } { + const idToPointer = (id: string) => { + const ret: Pointer = { __type: 'Pointer', - className: this._targetClassName, + className: this._targetClassName!, objectId: id, }; + return ret; }; - let adds = null; - let removes = null; - let pointers = null; + let pointers: null | (Pointer[]) = null; + let adds: null | { __op: string, objects: null | (Pointer[]) } = null; + let removes: null | { __op: string, objects: null | (Pointer[]) } = null; if (this.relationsToAdd.length > 0) { pointers = this.relationsToAdd.map(idToPointer); @@ -452,7 +455,6 @@ export class RelationOp extends Op { if (adds && removes) { return { __op: 'Batch', ops: [adds, removes] }; } - return adds || removes || {}; } } diff --git a/src/ParsePolygon.js b/src/ParsePolygon.ts similarity index 94% rename from src/ParsePolygon.js rename to src/ParsePolygon.ts index a7887ea02..27a8ed367 100644 --- a/src/ParsePolygon.js +++ b/src/ParsePolygon.ts @@ -24,12 +24,12 @@ import ParseGeoPoint from './ParseGeoPoint'; * @alias Parse.Polygon */ class ParsePolygon { - _coordinates: Array >; + _coordinates: Array<[number, number]>; /** * @param {(number[][] | Parse.GeoPoint[])} coordinates An Array of coordinate pairs */ - constructor(coordinates: Array > | Array ) { + constructor(coordinates: Array | [number, number]> | Array ) { this._coordinates = ParsePolygon._validate(coordinates); } @@ -67,7 +67,7 @@ class ParsePolygon { * @param {(Parse.Polygon | object)} other * @returns {boolean} */ - equals(other: mixed): boolean { + equals(other: ParsePolygon | any): boolean { if (!(other instanceof ParsePolygon) || this.coordinates.length !== other.coordinates.length) { return false; } @@ -138,14 +138,14 @@ class ParsePolygon { * @throws {TypeError} * @returns {number[][]} Array of coordinates if validated. */ - static _validate(coords: Array > | Array ): Array > { + static _validate(coords: Array > | Array ): Array<[number, number]> { if (!Array.isArray(coords)) { throw new TypeError('Coordinates must be an Array'); } if (coords.length < 3) { throw new TypeError('Polygon must have at least 3 GeoPoints or Points'); } - const points = []; + const points: [number, number][] = []; for (let i = 0; i < coords.length; i += 1) { const coord = coords[i]; let geoPoint; diff --git a/src/ParseQuery.js b/src/ParseQuery.ts similarity index 92% rename from src/ParseQuery.js rename to src/ParseQuery.ts index 9be6b37a9..70a8d9adb 100644 --- a/src/ParseQuery.js +++ b/src/ParseQuery.ts @@ -14,10 +14,47 @@ import { DEFAULT_PIN } from './LocalDatastoreUtils'; import type LiveQuerySubscription from './LiveQuerySubscription'; import type { RequestOptions, FullOptions } from './RESTController'; -type BatchOptions = FullOptions & { batchSize?: number }; +/** + * * batchSize: How many objects to yield in each batch (default: 100) + * useMasterKey: In Cloud Code and Node only, causes the Master Key to + * be used for this request. + * sessionToken: A valid session token, used for making a request on + * behalf of a specific user. + * context: A dictionary that is accessible in Cloud Code `beforeFind` trigger. + * + */ +type BatchOptions = FullOptions & { + batchSize?: number + useMasterKey?: boolean, + sessionToken?: string, + context?: { [key: string]: any }, + json?: boolean +}; + +/** + * Valid options are: + * useMasterKey: In Cloud Code and Node only, causes the Master Key to be used for this request. + * sessionToken: A valid session token, used for making a request on behalf of a specific user. + * context: A dictionary that is accessible in Cloud Code `beforeFind` trigger. + * json: Return raw json without converting to Parse.Object + */ +type QueryOptions = { + useMasterKey?: boolean, + sessionToken?: string, + context?: { [key: string]: any }, + json?: boolean +} +type FullTextQueryOptions = { + /** @param {string} options.language The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer.*/ + language?: string, + /** @param {boolean} options.caseSensitive A boolean flag to enable or disable case sensitive search.*/ + caseSensitive?: boolean, + /** @param {boolean} options.diacriticSensitive A boolean flag to enable or disable diacritic sensitive search.*/ + diacriticSensitive?: boolean +} export type WhereClause = { - [attr: string]: mixed, + [attr: string]: any, }; export type QueryJSON = { @@ -31,7 +68,7 @@ export type QueryJSON = { order?: string, className?: string, count?: number, - hint?: mixed, + hint?: any, explain?: boolean, readPreference?: string, includeReadPreference?: string, @@ -59,8 +96,8 @@ function quote(s: string): string { * @private * @returns {string} */ -function _getClassNameFromQueries(queries: Array ): ?string { - let className = null; +function _getClassNameFromQueries(queries: Array ): string | null { + let className: string | null = null; queries.forEach(q => { if (!className) { className = q.className; @@ -233,20 +270,20 @@ class ParseQuery { _skip: number; _count: boolean; _order: Array ; - _readPreference: string; - _includeReadPreference: string; - _subqueryReadPreference: string; + _readPreference: string | null; + _includeReadPreference: string | null; + _subqueryReadPreference: string | null; _queriesLocalDatastore: boolean; _localDatastorePinName: any; - _extraOptions: { [key: string]: mixed }; - _hint: mixed; + _extraOptions: { [key: string]: any }; + _hint: any; _explain: boolean; _xhrRequest: any; /** * @param {(string | Parse.Object)} objectClass An instance of a subclass of Parse.Object, or a Parse className string. */ - constructor(objectClass: string | ParseObject) { + constructor(objectClass: string | ParseObject | (typeof ParseObject)) { if (typeof objectClass === 'string') { if (objectClass === 'User' && CoreManager.get('PERFORM_USER_REWRITE')) { this.className = '_User'; @@ -256,10 +293,11 @@ class ParseQuery { } else if (objectClass instanceof ParseObject) { this.className = objectClass.className; } else if (typeof objectClass === 'function') { - if (typeof objectClass.className === 'string') { - this.className = objectClass.className; + const objectClz = (objectClass) as typeof ParseObject; + if (typeof objectClz.className === 'string') { + this.className = objectClz.className; } else { - const obj = new objectClass(); + const obj = new objectClz(); this.className = obj.className; } } else { @@ -281,7 +319,7 @@ class ParseQuery { this._extraOptions = {}; this._xhrRequest = { task: null, - onchange: () => {}, + onchange: () => { }, }; } @@ -338,7 +376,7 @@ class ParseQuery { * @param value * @returns {Parse.Query} */ - _addCondition(key: string, condition: string, value: mixed): ParseQuery { + _addCondition(key: string, condition: string, value: any): ParseQuery { if (!this._where[key] || typeof this._where[key] === 'string') { this._where[key] = {}; } @@ -356,7 +394,7 @@ class ParseQuery { return '^' + quote(string); } - async _handleOfflineQuery(params: any) { + async _handleOfflineQuery(params: QueryJSON) { OfflineQuery.validateQuery(this); const localDatastore = CoreManager.getLocalDatastore(); const objects = await localDatastore._serializeObjectsFromPinName(this._localDatastorePinName); @@ -609,10 +647,10 @@ class ParseQuery { * @returns {Promise} A promise that is resolved with the result when * the query completes. */ - get(objectId: string, options?: FullOptions): Promise { + get(objectId: string, options?: QueryOptions): Promise { this.equalTo('objectId', objectId); - const firstOptions = {}; + const firstOptions: QueryOptions = {}; if (options && options.hasOwnProperty('useMasterKey')) { firstOptions.useMasterKey = options.useMasterKey; } @@ -630,7 +668,6 @@ class ParseQuery { if (response) { return response; } - const errorObject = new ParseError(ParseError.OBJECT_NOT_FOUND, 'Object not found.'); return Promise.reject(errorObject); }); @@ -651,10 +688,10 @@ class ParseQuery { * @returns {Promise} A promise that is resolved with the results when * the query completes. */ - find(options?: FullOptions): Promise > { + find(options?: QueryOptions): Promise > { options = options || {}; - const findOptions = {}; + const findOptions: QueryOptions = {}; if (options.hasOwnProperty('useMasterKey')) { findOptions.useMasterKey = options.useMasterKey; } @@ -678,7 +715,7 @@ class ParseQuery { if (this._explain) { return response.results; } - const results = response.results.map(data => { + const results = response.results!.map(data => { // In cases of relations, the server may send back a className // on the top level of the payload const override = response.className || this.className; @@ -692,7 +729,7 @@ class ParseQuery { if (select) { handleSelectResult(data, select); } - if (options.json) { + if (options?.json) { return data; } else { return ParseObject.fromJSON(data, !select); @@ -702,7 +739,7 @@ class ParseQuery { const count = response.count; if (typeof count === 'number') { - return { results, count }; + return { results, count } as any; } else { return results; } @@ -744,10 +781,10 @@ class ParseQuery { * @returns {Promise} A promise that is resolved with the count when * the query completes. */ - count(options?: FullOptions): Promise { + count(options?: { useMasterKey?: boolean, sessionToken?: string }): Promise { options = options || {}; - const findOptions = {}; + const findOptions: { useMasterKey?: boolean, sessionToken?: string } = {}; if (options.hasOwnProperty('useMasterKey')) { findOptions.useMasterKey = options.useMasterKey; } @@ -763,7 +800,7 @@ class ParseQuery { params.count = 1; return controller.find(this.className, params, findOptions).then(result => { - return result.count; + return result.count!; }); } @@ -778,12 +815,10 @@ class ParseQuery { * * @returns {Promise} A promise that is resolved with the query completes. */ - distinct(key: string, options?: FullOptions): Promise > { + distinct(key: string, options?: { sessionToken?: string }): Promise > { options = options || {}; - const distinctOptions = {}; - distinctOptions.useMasterKey = true; - + const distinctOptions: { sessionToken?: string, useMasterKey: boolean } = { useMasterKey: true }; if (options.hasOwnProperty('sessionToken')) { distinctOptions.sessionToken = options.sessionToken; } @@ -796,7 +831,7 @@ class ParseQuery { hint: this._hint, }; return controller.aggregate(this.className, params, distinctOptions).then(results => { - return results.results; + return results.results!; }); } @@ -810,10 +845,9 @@ class ParseQuery { * * @returns {Promise} A promise that is resolved with the query completes. */ - aggregate(pipeline: mixed, options?: FullOptions): Promise > { + aggregate(pipeline: any, options?: { sessionToken?: string }): Promise > { options = options || {}; - const aggregateOptions = {}; - aggregateOptions.useMasterKey = true; + const aggregateOptions: { sessionToken?: string, useMasterKey: boolean } = { useMasterKey: true }; if (options.hasOwnProperty('sessionToken')) { aggregateOptions.sessionToken = options.sessionToken; @@ -840,7 +874,7 @@ class ParseQuery { readPreference: this._readPreference, }; return controller.aggregate(this.className, params, aggregateOptions).then(results => { - return results.results; + return results.results!; }); } @@ -860,10 +894,8 @@ class ParseQuery { * @returns {Promise} A promise that is resolved with the object when * the query completes. */ - first(options?: FullOptions): Promise { - options = options || {}; - - const findOptions = {}; + first(options: QueryOptions = {}): Promise { + const findOptions: QueryOptions = {}; if (options.hasOwnProperty('useMasterKey')) { findOptions.useMasterKey = options.useMasterKey; } @@ -892,7 +924,7 @@ class ParseQuery { } return controller.find(this.className, params, findOptions).then(response => { - const objects = response.results; + const objects = response.results!; if (!objects[0]) { return undefined; } @@ -936,7 +968,7 @@ class ParseQuery { * iteration has completed. */ eachBatch( - callback: (objs: Array ) => Promise<*>, + callback: (objs: Array ) => void, options?: BatchOptions ): Promise { options = options || {}; @@ -974,7 +1006,7 @@ class ParseQuery { query.ascending('objectId'); - const findOptions = {}; + const findOptions: BatchOptions = {}; if (options.hasOwnProperty('useMasterKey')) { findOptions.useMasterKey = options.useMasterKey; } @@ -989,7 +1021,7 @@ class ParseQuery { } let finished = false; - let previousResults = []; + let previousResults: ParseObject[] = []; return continueWhile( () => { return !finished; @@ -1050,7 +1082,7 @@ class ParseQuery { * @param {(string|object)} value String or Object of index that should be used when executing query * @returns {Parse.Query} Returns the query, so you can chain this call. */ - hint(value: mixed): ParseQuery { + hint(value: any): ParseQuery { if (typeof value === 'undefined') { delete this._hint; } @@ -1098,7 +1130,7 @@ class ParseQuery { callback: (currentObject: ParseObject, index: number, query: ParseQuery) => any, options?: BatchOptions ): Promise > { - const array = []; + const array: any[] = []; let index = 0; await this.each(object => { return Promise.resolve(callback(object, index, this)).then(result => { @@ -1186,7 +1218,7 @@ class ParseQuery { callback: (currentObject: ParseObject, index: number, query: ParseQuery) => boolean, options?: BatchOptions ): Promise > { - const array = []; + const array: ParseObject[] = []; let index = 0; await this.each(object => { return Promise.resolve(callback(object, index, this)).then(flag => { @@ -1209,16 +1241,16 @@ class ParseQuery { * @param value The value that the Parse.Object must contain. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - equalTo(key: string | { [key: string]: any }, value: ?mixed): ParseQuery { + equalTo(key: string | { [key: string]: any }, value?: any): ParseQuery { if (key && typeof key === 'object') { Object.entries(key).forEach(([k, val]) => this.equalTo(k, val)); return this; } if (typeof value === 'undefined') { - return this.doesNotExist(key); + return this.doesNotExist(key as string); } - this._where[key] = encode(value, false, true); + this._where[key as string] = encode(value, false, true); return this; } @@ -1230,12 +1262,12 @@ class ParseQuery { * @param value The value that must not be equalled. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - notEqualTo(key: string | { [key: string]: any }, value: ?mixed): ParseQuery { + notEqualTo(key: string | { [key: string]: any }, value?: any): ParseQuery { if (key && typeof key === 'object') { Object.entries(key).forEach(([k, val]) => this.notEqualTo(k, val)); return this; } - return this._addCondition(key, '$ne', value); + return this._addCondition(key as string, '$ne', value); } /** @@ -1246,7 +1278,7 @@ class ParseQuery { * @param value The value that provides an upper bound. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - lessThan(key: string, value: mixed): ParseQuery { + lessThan(key: string, value: any): ParseQuery { return this._addCondition(key, '$lt', value); } @@ -1258,7 +1290,7 @@ class ParseQuery { * @param value The value that provides an lower bound. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - greaterThan(key: string, value: mixed): ParseQuery { + greaterThan(key: string, value: any): ParseQuery { return this._addCondition(key, '$gt', value); } @@ -1270,7 +1302,7 @@ class ParseQuery { * @param value The value that provides an upper bound. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - lessThanOrEqualTo(key: string, value: mixed): ParseQuery { + lessThanOrEqualTo(key: string, value: any): ParseQuery { return this._addCondition(key, '$lte', value); } @@ -1282,7 +1314,7 @@ class ParseQuery { * @param {*} value The value that provides an lower bound. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - greaterThanOrEqualTo(key: string, value: mixed): ParseQuery { + greaterThanOrEqualTo(key: string, value: any): ParseQuery { return this._addCondition(key, '$gte', value); } @@ -1294,7 +1326,7 @@ class ParseQuery { * @param {Array<*>} value The values that will match. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - containedIn(key: string, value: Array ): ParseQuery { + containedIn(key: string, value: Array ): ParseQuery { return this._addCondition(key, '$in', value); } @@ -1306,7 +1338,7 @@ class ParseQuery { * @param {Array<*>} value The values that will not match. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - notContainedIn(key: string, value: Array ): ParseQuery { + notContainedIn(key: string, value: Array ): ParseQuery { return this._addCondition(key, '$nin', value); } @@ -1318,7 +1350,7 @@ class ParseQuery { * @param {Array} values The values that will match. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - containedBy(key: string, values: Array ): ParseQuery { + containedBy(key: string, values: Array ): ParseQuery { return this._addCondition(key, '$containedBy', values); } @@ -1330,7 +1362,7 @@ class ParseQuery { * @param {Array} values The values that will match. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - containsAll(key: string, values: Array ): ParseQuery { + containsAll(key: string, values: Array ): ParseQuery { return this._addCondition(key, '$all', values); } @@ -1385,16 +1417,18 @@ class ParseQuery { * @param {string} modifiers The regular expression mode. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - matches(key: string, regex: RegExp, modifiers: string): ParseQuery { + matches(key: string, regex: RegExp | string, modifiers: string): ParseQuery { this._addCondition(key, '$regex', regex); if (!modifiers) { modifiers = ''; } - if (regex.ignoreCase) { - modifiers += 'i'; - } - if (regex.multiline) { - modifiers += 'm'; + if (typeof regex != 'string') { + if (regex.ignoreCase) { + modifiers += 'i'; + } + if (regex.multiline) { + modifiers += 'm'; + } } if (modifiers.length) { this._addCondition(key, '$options', modifiers); @@ -1516,8 +1550,7 @@ class ParseQuery { * @param {boolean} options.diacriticSensitive A boolean flag to enable or disable diacritic sensitive search. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - fullText(key: string, value: string, options: ?Object): ParseQuery { - options = options || {}; + fullText(key: string, value: string, options: FullTextQueryOptions = {}): ParseQuery { if (!key) { throw new Error('A key is required.'); @@ -1529,22 +1562,22 @@ class ParseQuery { throw new Error('The value being searched for must be a string.'); } - const fullOptions = {}; + const fullOptions: { $term?: string, $language?: string, $caseSensitive?: boolean, $diacriticSensitive?: boolean } = {}; fullOptions.$term = value; for (const option in options) { switch (option) { - case 'language': - fullOptions.$language = options[option]; - break; - case 'caseSensitive': - fullOptions.$caseSensitive = options[option]; - break; - case 'diacriticSensitive': - fullOptions.$diacriticSensitive = options[option]; - break; - default: - throw new Error(`Unknown option: ${option}`); + case 'language': + fullOptions.$language = options[option]; + break; + case 'caseSensitive': + fullOptions.$caseSensitive = options[option]; + break; + case 'diacriticSensitive': + fullOptions.$diacriticSensitive = options[option]; + break; + default: + throw new Error(`Unknown option: ${option}`); } } @@ -1961,8 +1994,8 @@ class ParseQuery { subqueryReadPreference?: string ): ParseQuery { this._readPreference = readPreference; - this._includeReadPreference = includeReadPreference; - this._subqueryReadPreference = subqueryReadPreference; + this._includeReadPreference = includeReadPreference || null; + this._subqueryReadPreference = subqueryReadPreference || null; return this; } @@ -1976,7 +2009,7 @@ class ParseQuery { async subscribe(sessionToken?: string): Promise { const currentUser = await CoreManager.getUserController().currentUserAsync(); if (!sessionToken) { - sessionToken = currentUser ? currentUser.getSessionToken() : undefined; + sessionToken = currentUser ? currentUser.getSessionToken() || undefined : undefined; } const liveQueryClient = await CoreManager.getLiveQueryController().getDefaultLiveQueryClient(); if (liveQueryClient.shouldOpen()) { @@ -2002,7 +2035,7 @@ class ParseQuery { */ static or(...queries: Array ): ParseQuery { const className = _getClassNameFromQueries(queries); - const query = new ParseQuery(className); + const query = new ParseQuery(className!); query._orQuery(queries); return query; } @@ -2021,7 +2054,7 @@ class ParseQuery { */ static and(...queries: Array ): ParseQuery { const className = _getClassNameFromQueries(queries); - const query = new ParseQuery(className); + const query = new ParseQuery(className!); query._andQuery(queries); return query; } @@ -2040,7 +2073,7 @@ class ParseQuery { */ static nor(...queries: Array ): ParseQuery { const className = _getClassNameFromQueries(queries); - const query = new ParseQuery(className); + const query = new ParseQuery(className!); query._norQuery(queries); return query; } @@ -2080,7 +2113,7 @@ class ParseQuery { * @param {string} name The name of query source. * @returns {Parse.Query} Returns the query, so you can chain this call. */ - fromPinWithName(name?: string): ParseQuery { + fromPinWithName(name?: string | null): ParseQuery { const localDatastore = CoreManager.getLocalDatastore(); if (localDatastore.checkIfEnabled()) { this._queriesLocalDatastore = true; @@ -2099,10 +2132,11 @@ class ParseQuery { this._xhrRequest.task._aborted = true; this._xhrRequest.task.abort(); this._xhrRequest.task = null; - this._xhrRequest.onchange = () => {}; + this._xhrRequest.onchange = () => { }; return this; } - return (this._xhrRequest.onchange = () => this.cancel()); + this._xhrRequest.onchange = () => this.cancel(); + return this; } _setRequestTask(options) { @@ -2114,12 +2148,12 @@ class ParseQuery { } const DefaultController = { - find(className: string, params: QueryJSON, options: RequestOptions): Promise > { + find(className: string, params: QueryJSON, options: RequestOptions): Promise<{ results: Array }> { const RESTController = CoreManager.getRESTController(); return RESTController.request('GET', 'classes/' + className, params, options); }, - aggregate(className: string, params: any, options: RequestOptions): Promise > { + aggregate(className: string, params: any, options: RequestOptions): Promise<{ results: Array }> { const RESTController = CoreManager.getRESTController(); return RESTController.request('GET', 'aggregate/' + className, params, options); diff --git a/src/ParseRelation.js b/src/ParseRelation.ts similarity index 92% rename from src/ParseRelation.js rename to src/ParseRelation.ts index 50c8c09f1..23b9f5c19 100644 --- a/src/ParseRelation.js +++ b/src/ParseRelation.ts @@ -20,15 +20,15 @@ import ParseQuery from './ParseQuery'; * @alias Parse.Relation */ class ParseRelation { - parent: ?ParseObject; - key: ?string; - targetClassName: ?string; + parent?: ParseObject; + key?: string; + targetClassName: string | null; /** * @param {Parse.Object} parent The parent of this relation. * @param {string} key The key for this relation on the parent. */ - constructor(parent: ?ParseObject, key: ?string) { + constructor(parent?: ParseObject, key?: string) { this.parent = parent; this.key = key; this.targetClassName = null; @@ -77,7 +77,9 @@ class ParseRelation { if (objects.length === 0) { return parent; } - parent.set(this.key, change); + if (this.key) { + parent.set(this.key, change); + } this.targetClassName = change._targetClassName; return parent; } @@ -99,7 +101,9 @@ class ParseRelation { if (objects.length === 0) { return; } - this.parent.set(this.key, change); + if (this.key) { + this.parent.set(this.key, change); + } this.targetClassName = change._targetClassName; } @@ -108,7 +112,7 @@ class ParseRelation { * * @returns {object} JSON representation of Relation */ - toJSON(): { __type: 'Relation', className: ?string } { + toJSON(): { __type: 'Relation', className: string | null } { return { __type: 'Relation', className: this.targetClassName, diff --git a/src/ParseRole.js b/src/ParseRole.ts similarity index 94% rename from src/ParseRole.js rename to src/ParseRole.ts index fee282395..9dbd00315 100644 --- a/src/ParseRole.js +++ b/src/ParseRole.ts @@ -2,9 +2,9 @@ * @flow */ +import ParseObject from './ParseObject'; import ParseACL from './ParseACL'; import ParseError from './ParseError'; -import ParseObject from './ParseObject'; import type { AttributeMap } from './ObjectStateMutations'; import type ParseRelation from './ParseRelation'; @@ -42,7 +42,7 @@ class ParseRole extends ParseObject { * * @returns {string} the name of the role. */ - getName(): ?string { + getName(): string | null { const name = this.get('name'); if (name == null || typeof name === 'string') { return name; @@ -67,7 +67,7 @@ class ParseRole extends ParseObject { * callbacks. * @returns {(ParseObject|boolean)} true if the set succeeded. */ - setName(name: string, options?: mixed): ParseObject | boolean { + setName(name: string, options?: any): ParseObject | boolean { this._validateName(name); return this.set('name', name, options); } @@ -114,8 +114,8 @@ class ParseRole extends ParseObject { } } - validate(attrs: AttributeMap, options?: mixed): ParseError | boolean { - const isInvalid = super.validate(attrs, options); + validate(attrs: AttributeMap, options?: any): ParseError | boolean { + const isInvalid = (super.validate as typeof this['validate'])(attrs, options); if (isInvalid) { return isInvalid; } @@ -142,5 +142,4 @@ class ParseRole extends ParseObject { } ParseObject.registerSubclass('_Role', ParseRole); - export default ParseRole; diff --git a/src/ParseSchema.js b/src/ParseSchema.ts similarity index 93% rename from src/ParseSchema.js rename to src/ParseSchema.ts index 089cddd32..b878736c6 100644 --- a/src/ParseSchema.js +++ b/src/ParseSchema.ts @@ -21,11 +21,13 @@ const FIELD_TYPES = [ 'Object', 'Pointer', 'Relation', -]; +] as const; +type ValidFieldType = typeof FIELD_TYPES[number]; type FieldOptions = { - required: boolean, - defaultValue: mixed, + required?: boolean, + defaultValue?: any, + targetClass?: string, }; /** @@ -47,9 +49,9 @@ type FieldOptions = { */ class ParseSchema { className: string; - _fields: { [key: string]: mixed }; - _indexes: { [key: string]: mixed }; - _clp: { [key: string]: mixed }; + _fields: { [key: string]: any }; + _indexes: { [key: string]: any }; + _clp: { [key: string]: any }; /** * @param {string} className Parse Class string. @@ -212,7 +214,7 @@ class ParseSchema { * * @returns {Parse.Schema} Returns the schema, so you can chain this call. */ - addField(name: string, type: string, options: FieldOptions = {}) { + addField(name: string, type: ValidFieldType, options: FieldOptions = {}) { type = type || 'String'; if (!name) { @@ -222,12 +224,14 @@ class ParseSchema { throw new Error(`${type} is not a valid type.`); } if (type === 'Pointer') { - return this.addPointer(name, options.targetClass, options); + return this.addPointer(name, options.targetClass!, options); } if (type === 'Relation') { - return this.addRelation(name, options.targetClass, options); + return this.addRelation(name, options.targetClass); } - const fieldOptions = { type }; + const fieldOptions: Partial & { + type: ValidFieldType, + } = { type }; if (typeof options.required === 'boolean') { fieldOptions.required = options.required; @@ -404,7 +408,9 @@ class ParseSchema { if (!targetClass) { throw new Error('You need to set the targetClass of the Pointer.'); } - const fieldOptions = { type: 'Pointer', targetClass }; + const fieldOptions: Partial & { + type: ValidFieldType, + } = { type: 'Pointer', targetClass }; if (typeof options.required === 'boolean') { fieldOptions.required = options.required; @@ -466,30 +472,30 @@ class ParseSchema { } const DefaultController = { - send(className: string, method: string, params: any = {}): Promise { + send(className: string, method: string, params: any = {}): Promise { const RESTController = CoreManager.getRESTController(); return RESTController.request(method, `schemas/${className}`, params, { useMasterKey: true, }); }, - get(className: string): Promise { + get(className: string): Promise<{ results: ParseSchema[] }> { return this.send(className, 'GET'); }, - create(className: string, params: any): Promise { + create(className: string, params: any): Promise { return this.send(className, 'POST', params); }, - update(className: string, params: any): Promise { + update(className: string, params: any): Promise { return this.send(className, 'PUT', params); }, - delete(className: string): Promise { + delete(className: string): Promise { return this.send(className, 'DELETE'); }, - purge(className: string): Promise { + purge(className: string): Promise { const RESTController = CoreManager.getRESTController(); return RESTController.request('DELETE', `purge/${className}`, {}, { useMasterKey: true }); }, diff --git a/src/ParseSession.ts b/src/ParseSession.ts index 51b001742..ab64e43a3 100644 --- a/src/ParseSession.ts +++ b/src/ParseSession.ts @@ -57,7 +57,7 @@ class ParseSession extends ParseObject { options = options || {}; const controller = CoreManager.getSessionController(); - const sessionOptions = {}; + const sessionOptions: FullOptions = {}; if (options.hasOwnProperty('useMasterKey')) { sessionOptions.useMasterKey = options.useMasterKey; } @@ -65,7 +65,7 @@ class ParseSession extends ParseObject { if (!user) { return Promise.reject('There is no current user.'); } - sessionOptions.sessionToken = user.getSessionToken(); + sessionOptions.sessionToken = user.getSessionToken() || undefined; return controller.getSession(sessionOptions); }); } diff --git a/src/ParseUser.js b/src/ParseUser.ts similarity index 92% rename from src/ParseUser.js rename to src/ParseUser.ts index 8ba9ba8c0..1bd4bf1af 100644 --- a/src/ParseUser.js +++ b/src/ParseUser.ts @@ -12,14 +12,27 @@ import Storage from './Storage'; import type { AttributeMap } from './ObjectStateMutations'; import type { RequestOptions, FullOptions } from './RESTController'; -export type AuthData = ?{ [key: string]: mixed }; +export type AuthData = { [key: string]: any }; + +export type AuthProviderType = { + authenticate?(options: { + error?: (provider: AuthProviderType, error: string | any) => void, + success?: (provider: AuthProviderType, result: AuthData) => void, + }): void, + + restoreAuthentication(authData: any): boolean; + /** Returns the AuthType of this provider */ + getAuthType(): string; + deauthenticate?(): void; +}; + const CURRENT_USER_KEY = 'currentUser'; let canUseCurrentUser = !CoreManager.get('IS_NODE'); let currentUserCacheMatchesDisk = false; -let currentUserCache = null; +let currentUserCache: ParseUser | null = null; -const authProviders = {}; +const authProviders: { [key: string]: AuthProviderType } = {}; /** * A Parse.User object is a local representation of a user persisted to the @@ -35,7 +48,7 @@ class ParseUser extends ParseObject { /** * @param {object} attributes The initial set of data to store in the user. */ - constructor(attributes: ?AttributeMap) { + constructor(attributes?: AttributeMap) { super('_User'); if (attributes && typeof attributes === 'object') { if (!this.set(attributes || {})) { @@ -54,7 +67,7 @@ class ParseUser extends ParseObject { _upgradeToRevocableSession(options: RequestOptions): Promise
{ options = options || {}; - const upgradeOptions = {}; + const upgradeOptions: RequestOptions = {}; if (options.hasOwnProperty('useMasterKey')) { upgradeOptions.useMasterKey = options.useMasterKey; } @@ -79,12 +92,12 @@ class ParseUser extends ParseObject { * @returns {Promise} A promise that is fulfilled with the user is linked */ linkWith( - provider: any, - options: { authData?: AuthData }, - saveOpts?: FullOptions = {} + provider: AuthProviderType, + options: { authData?: AuthData | null }, + saveOpts: FullOptions = {} ): Promise { saveOpts.sessionToken = saveOpts.sessionToken || this.getSessionToken() || ''; - let authType; + let authType: string; if (typeof provider === 'string') { authType = provider; if (authProviders[provider]) { @@ -121,9 +134,9 @@ class ParseUser extends ParseObject { }); } else { return new Promise((resolve, reject) => { - provider.authenticate({ + provider.authenticate!({ success: (provider, result) => { - const opts = {}; + const opts: { authData?: AuthData } = {}; opts.authData = result; this.linkWith(provider, opts, saveOpts).then( () => { @@ -152,7 +165,7 @@ class ParseUser extends ParseObject { _linkWith( provider: any, options: { authData?: AuthData }, - saveOpts?: FullOptions = {} + saveOpts: FullOptions = {} ): Promise { return this.linkWith(provider, options, saveOpts); } @@ -163,7 +176,7 @@ class ParseUser extends ParseObject { * * @param provider */ - _synchronizeAuthData(provider: string) { + _synchronizeAuthData(provider: string | AuthProviderType) { if (!this.isCurrent() || !provider) { return; } @@ -336,7 +349,7 @@ class ParseUser extends ParseObject { * * @returns {string} */ - getUsername(): ?string { + getUsername(): string | null { const username = this.get('username'); if (username == null || typeof username === 'string') { return username; @@ -368,7 +381,7 @@ class ParseUser extends ParseObject { * * @returns {string} User's Email */ - getEmail(): ?string { + getEmail(): string | null { const email = this.get('email'); if (email == null || typeof email === 'string') { return email; @@ -393,7 +406,7 @@ class ParseUser extends ParseObject { * * @returns {string} the session token, or undefined */ - getSessionToken(): ?string { + getSessionToken(): string | null { const token = this.get('sessionToken'); if (token == null || typeof token === 'string') { return token; @@ -424,10 +437,10 @@ class ParseUser extends ParseObject { * @returns {Promise} A promise that is fulfilled when the signup * finishes. */ - signUp(attrs: AttributeMap, options?: FullOptions): Promise { + signUp(attrs: AttributeMap, options?: FullOptions & { context?: AttributeMap }): Promise { options = options || {}; - const signupOptions = {}; + const signupOptions: FullOptions & { context?: AttributeMap } = {}; if (options.hasOwnProperty('useMasterKey')) { signupOptions.useMasterKey = options.useMasterKey; } @@ -456,10 +469,9 @@ class ParseUser extends ParseObject { * @returns {Promise} A promise that is fulfilled with the user when * the login is complete. */ - logIn(options?: FullOptions): Promise { - options = options || {}; + logIn(options: FullOptions & { context?: AttributeMap } = {}): Promise { - const loginOptions = { usePost: true }; + const loginOptions: { usePost: boolean, context?: AttributeMap } & FullOptions = { usePost: true }; if (options.hasOwnProperty('useMasterKey')) { loginOptions.useMasterKey = options.useMasterKey; } @@ -467,7 +479,7 @@ class ParseUser extends ParseObject { loginOptions.installationId = options.installationId; } if (options.hasOwnProperty('usePost')) { - loginOptions.usePost = options.usePost; + loginOptions.usePost = options.usePost!; } if ( options.hasOwnProperty('context') && @@ -486,11 +498,11 @@ class ParseUser extends ParseObject { * @param {...any} args * @returns {Promise} */ - async save(...args: Array ): Promise { + async save(...args: Array ): Promise { await super.save.apply(this, args); const current = await this.isCurrentAsync(); if (current) { - return CoreManager.getUserController().updateUserOnDisk(this); + return CoreManager.getUserController().updateUserOnDisk(this) as Promise ; } return this; } @@ -500,9 +512,9 @@ class ParseUser extends ParseObject { * the current user when it is destroyed * * @param {...any} args - * @returns {Parse.User} + * @returns {Parse.User|void} */ - async destroy(...args: Array ): Promise { + async destroy(...args: Array ): Promise { await super.destroy.apply(this, args); const current = await this.isCurrentAsync(); if (current) { @@ -606,7 +618,7 @@ class ParseUser extends ParseObject { * @static * @returns {Parse.Object} The currently logged in Parse.User. */ - static current(): ?ParseUser { + static current(): ParseUser | null { if (!canUseCurrentUser) { return null; } @@ -621,7 +633,7 @@ class ParseUser extends ParseObject { * @returns {Promise} A Promise that is resolved with the currently * logged in Parse User */ - static currentAsync(): Promise { + static currentAsync(): Promise { if (!canUseCurrentUser) { return Promise.resolve(null); } @@ -758,7 +770,7 @@ class ParseUser extends ParseObject { * @static * @returns {Promise} A promise that is fulfilled with the user is fetched. */ - static me(sessionToken: string, options?: RequestOptions = {}) { + static me(sessionToken: string, options: RequestOptions = {}) { const controller = CoreManager.getUserController(); const meOptions: RequestOptions = { sessionToken: sessionToken, @@ -833,7 +845,7 @@ class ParseUser extends ParseObject { static requestPasswordReset(email: string, options?: RequestOptions) { options = options || {}; - const requestOptions = {}; + const requestOptions: { useMasterKey?: boolean } = {}; if (options.hasOwnProperty('useMasterKey')) { requestOptions.useMasterKey = options.useMasterKey; } @@ -854,7 +866,7 @@ class ParseUser extends ParseObject { static requestEmailVerification(email: string, options?: RequestOptions) { options = options || {}; - const requestOptions = {}; + const requestOptions: RequestOptions = {}; if (options.hasOwnProperty('useMasterKey')) { requestOptions.useMasterKey = options.useMasterKey; } @@ -873,7 +885,7 @@ class ParseUser extends ParseObject { * @returns {Promise} A promise that is fulfilled with a user * when the password is correct. */ - static verifyPassword(username: string, password: string, options?: RequestOptions) { + static verifyPassword(username: string, password: string, options?: RequestOptions): Promise { if (typeof username !== 'string') { return Promise.reject(new ParseError(ParseError.OTHER_CAUSE, 'Username must be a string.')); } @@ -884,7 +896,7 @@ class ParseUser extends ParseObject { options = options || {}; - const verificationOption = {}; + const verificationOption: RequestOptions = {}; if (options.hasOwnProperty('useMasterKey')) { verificationOption.useMasterKey = options.useMasterKey; } @@ -1032,7 +1044,7 @@ const DefaultController = { return DefaultController.updateUserOnDisk(user); }, - currentUser(): ?ParseUser { + currentUser(): ParseUser | null { if (currentUserCache) { return currentUserCache; } @@ -1042,7 +1054,7 @@ const DefaultController = { if (Storage.async()) { throw new Error( 'Cannot call currentUser() when using a platform with an async ' + - 'storage system. Call currentUserAsync() instead.' + 'storage system. Call currentUserAsync() instead.' ); } const path = Storage.generatePath(CURRENT_USER_KEY); @@ -1056,27 +1068,27 @@ const DefaultController = { const crypto = CoreManager.getCryptoController(); userData = crypto.decrypt(userData, CoreManager.get('ENCRYPTED_KEY')); } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; + const userDataObj = JSON.parse(userData); + if (!userDataObj.className) { + userDataObj.className = '_User'; } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; + if (userDataObj._id) { + if (userDataObj.objectId !== userDataObj._id) { + userDataObj.objectId = userDataObj._id; } - delete userData._id; + delete userDataObj._id; } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; + if (userDataObj._sessionToken) { + userDataObj.sessionToken = userDataObj._sessionToken; + delete userDataObj._sessionToken; } - const current = ParseObject.fromJSON(userData); + const current = ParseObject.fromJSON(userDataObj) as ParseUser; currentUserCache = current; current._synchronizeAllAuthData(); return current; }, - currentUserAsync(): Promise { + currentUserAsync(): Promise { if (currentUserCache) { return Promise.resolve(currentUserCache); } @@ -1094,21 +1106,21 @@ const DefaultController = { const crypto = CoreManager.getCryptoController(); userData = crypto.decrypt(userData.toString(), CoreManager.get('ENCRYPTED_KEY')); } - userData = JSON.parse(userData); - if (!userData.className) { - userData.className = '_User'; + const userDataObj = JSON.parse(userData); + if (!userDataObj.className) { + userDataObj.className = '_User'; } - if (userData._id) { - if (userData.objectId !== userData._id) { - userData.objectId = userData._id; + if (userDataObj._id) { + if (userDataObj.objectId !== userDataObj._id) { + userDataObj.objectId = userDataObj._id; } - delete userData._id; + delete userDataObj._id; } - if (userData._sessionToken) { - userData.sessionToken = userData._sessionToken; - delete userData._sessionToken; + if (userDataObj._sessionToken) { + userDataObj.sessionToken = userDataObj._sessionToken; + delete userDataObj._sessionToken; } - const current = ParseObject.fromJSON(userData); + const current = ParseObject.fromJSON(userDataObj) as ParseUser; currentUserCache = current; current._synchronizeAllAuthData(); return Promise.resolve(current); @@ -1206,7 +1218,7 @@ const DefaultController = { }); }, - logOut(options: RequestOptions): Promise { + logOut(options: RequestOptions): Promise { const RESTController = CoreManager.getRESTController(); if (options.sessionToken) { return RESTController.request('POST', 'logout', {}, options); @@ -1258,7 +1270,7 @@ const DefaultController = { return Promise.resolve(user); }, - linkWith(user: ParseUser, authData: AuthData, options: FullOptions) { + linkWith(user: ParseUser, authData: AuthData, options?: FullOptions) { return user.save({ authData }, options).then(() => { if (canUseCurrentUser) { return DefaultController.setCurrentUser(user); diff --git a/src/Push.js b/src/Push.ts similarity index 86% rename from src/Push.js rename to src/Push.ts index a565396be..d6b91b977 100644 --- a/src/Push.js +++ b/src/Push.ts @@ -3,6 +3,7 @@ */ import CoreManager from './CoreManager'; +import ParseObject from './ParseObject'; import ParseQuery from './ParseQuery'; import type { WhereClause } from './ParseQuery'; @@ -49,9 +50,9 @@ export type PushData = { * be used for this request. * * @returns {Promise} A promise that is fulfilled when the push request - * completes. + * completes., returns `pushStatusId` */ -export function send(data: PushData, options?: FullOptions = {}): Promise { +export function send(data: PushData, options: FullOptions = {}): Promise { if (data.where && data.where instanceof ParseQuery) { data.where = data.where.toJSON().where; } @@ -70,7 +71,7 @@ export function send(data: PushData, options?: FullOptions = {}): Promise { const pushOptions = { useMasterKey: true }; if (options.hasOwnProperty('useMasterKey')) { - pushOptions.useMasterKey = options.useMasterKey; + pushOptions.useMasterKey = options.useMasterKey!; } return CoreManager.getPushController().send(data, pushOptions); @@ -89,10 +90,10 @@ export function send(data: PushData, options?: FullOptions = {}): Promise { * * @returns {Parse.Object} Status of Push. */ -export function getPushStatus(pushStatusId: string, options?: FullOptions = {}): Promise { +export function getPushStatus(pushStatusId: string, options: FullOptions = {}): Promise { const pushOptions = { useMasterKey: true }; if (options.hasOwnProperty('useMasterKey')) { - pushOptions.useMasterKey = options.useMasterKey; + pushOptions.useMasterKey = options.useMasterKey!; } const query = new ParseQuery('_PushStatus'); return query.get(pushStatusId, pushOptions); @@ -100,8 +101,8 @@ export function getPushStatus(pushStatusId: string, options?: FullOptions = {}): const DefaultController = { async send(data: PushData, options?: FullOptions) { - options.returnStatus = true; - const response = await CoreManager.getRESTController().request('POST', 'push', data, options); + const myOptions = { ...options, returnStatus: true }; + const response = await CoreManager.getRESTController().request('POST', 'push', data, myOptions); return response._headers?.['X-Parse-Push-Status-Id']; }, }; diff --git a/src/RESTController.js b/src/RESTController.ts similarity index 91% rename from src/RESTController.js rename to src/RESTController.ts index 75b30a759..c0ce2649f 100644 --- a/src/RESTController.js +++ b/src/RESTController.ts @@ -2,7 +2,7 @@ * @flow */ /* global XMLHttpRequest, XDomainRequest */ -const uuidv4 = require('./uuid'); +import uuidv4 from './uuid'; import CoreManager from './CoreManager'; import ParseError from './ParseError'; @@ -28,9 +28,10 @@ export type FullOptions = { installationId?: string, progress?: any, usePost?: boolean, + requestTask?: any, }; -let XHR = null; +let XHR: typeof XMLHttpRequest = null as any; if (typeof XMLHttpRequest !== 'undefined') { XHR = XMLHttpRequest; } @@ -42,12 +43,14 @@ if (process.env.PARSE_BUILD === 'weapp') { } 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; @@ -77,7 +80,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); } }); @@ -101,7 +106,7 @@ const RESTController = { const xhr = new XHR(); xhr.onreadystatechange = function () { - if (xhr.readyState !== 4 || handled || xhr._aborted) { + if (xhr.readyState !== 4 || handled || (xhr as any)._aborted) { return; } handled = true; @@ -112,8 +117,8 @@ const RESTController = { response = JSON.parse(xhr.responseText); headers = {}; if (typeof xhr.getResponseHeader === 'function' && xhr.getResponseHeader('access-control-expose-headers')) { - const responseHeaders = xhr.getResponseHeader('access-control-expose-headers').split(', '); - responseHeaders.forEach(header => { + const responseHeaders = xhr.getResponseHeader('access-control-expose-headers')?.split(', '); + responseHeaders?.forEach(header => { headers[header] = xhr.getResponseHeader(header.toLowerCase()); }); } @@ -203,7 +208,7 @@ const RESTController = { return promise; }, - request(method: string, path: string, data: mixed, options?: RequestOptions) { + request(method: string, path: string, data: any, options?: RequestOptions) { options = options || {}; let url = CoreManager.get('SERVER_URL'); if (url[url.length - 1] !== '/') { @@ -211,7 +216,18 @@ const RESTController = { } url += path; - const payload = {}; + type PayloadType = { + _context?: any, + _method?: string, + _ApplicationId: string, + _JavaScriptKey?: string, + _ClientVersion: string, + _MasterKey?: string, + _RevocableSession?: string, + _InstallationId?: string, + _SessionToken?: string, + }; + const payload: Partial = {}; if (data && typeof data === 'object') { for (const k in data) { payload[k] = data[k]; @@ -254,7 +270,7 @@ const RESTController = { } const installationId = options.installationId; - let installationIdPromise; + let installationIdPromise: Promise ; if (installationId && typeof installationId === 'string') { installationIdPromise = Promise.resolve(installationId); } else { @@ -297,7 +313,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; @@ -332,3 +348,4 @@ const RESTController = { }; module.exports = RESTController; +export default RESTController; diff --git a/src/SingleInstanceStateController.js b/src/SingleInstanceStateController.ts similarity index 92% rename from src/SingleInstanceStateController.js rename to src/SingleInstanceStateController.ts index 3dd9c2cc7..3be62c78d 100644 --- a/src/SingleInstanceStateController.js +++ b/src/SingleInstanceStateController.ts @@ -18,7 +18,7 @@ let objectState: { }, } = {}; -export function getState(obj: ObjectIdentifier): ?State { +export function getState(obj: ObjectIdentifier): State | null { const classData = objectState[obj.className]; if (classData) { return classData[obj.id] || null; @@ -41,7 +41,7 @@ export function initializeState(obj: ObjectIdentifier, initial?: State): State { return state; } -export function removeState(obj: ObjectIdentifier): ?State { +export function removeState(obj: ObjectIdentifier): State | null { const state = getState(obj); if (state === null) { return null; @@ -71,7 +71,7 @@ export function getPendingOps(obj: ObjectIdentifier): Array { return [{}]; } -export function setPendingOp(obj: ObjectIdentifier, attr: string, op: ?Op) { +export function setPendingOp(obj: ObjectIdentifier, attr: string, op?: Op) { const pendingOps = initializeState(obj).pendingOps; ObjectStateMutations.setPendingOp(pendingOps, attr, op); } @@ -81,7 +81,7 @@ export function pushPendingState(obj: ObjectIdentifier) { ObjectStateMutations.pushPendingState(pendingOps); } -export function popPendingState(obj: ObjectIdentifier): OpsMap { +export function popPendingState(obj: ObjectIdentifier): OpsMap | undefined { const pendingOps = initializeState(obj).pendingOps; return ObjectStateMutations.popPendingState(pendingOps); } @@ -99,7 +99,7 @@ export function getObjectCache(obj: ObjectIdentifier): ObjectCache { return {}; } -export function estimateAttribute(obj: ObjectIdentifier, attr: string): mixed { +export function estimateAttribute(obj: ObjectIdentifier, attr: string): any { const serverData = getServerData(obj); const pendingOps = getPendingOps(obj); return ObjectStateMutations.estimateAttribute( @@ -122,7 +122,7 @@ export function commitServerChanges(obj: ObjectIdentifier, changes: AttributeMap ObjectStateMutations.commitServerChanges(state.serverData, state.objectCache, changes); } -export function enqueueTask(obj: ObjectIdentifier, task: () => Promise): Promise { +export function enqueueTask(obj: ObjectIdentifier, task: () => Promise ): Promise { const state = initializeState(obj); return state.tasks.enqueue(task); } diff --git a/src/Socket.weapp.js b/src/Socket.weapp.ts similarity index 64% rename from src/Socket.weapp.js rename to src/Socket.weapp.ts index d8d3884e7..239180300 100644 --- a/src/Socket.weapp.js +++ b/src/Socket.weapp.ts @@ -1,36 +1,51 @@ module.exports = class SocketWeapp { - constructor(serverURL) { + onopen: () => void; + onmessage: () => void; + onclose: () => void; + onerror: () => void; + + constructor(serverURL: string) { 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(); } }; diff --git a/src/Storage.js b/src/Storage.ts similarity index 73% rename from src/Storage.js rename to src/Storage.ts index 67392c249..bcbd094c9 100644 --- a/src/Storage.js +++ b/src/Storage.ts @@ -4,13 +4,39 @@ import CoreManager from './CoreManager'; +export type StorageController = + | { + async: 0, + getItem: (path: string) => string | null, + setItem: (path: string, value: string) => void, + removeItem: (path: string) => void, + getItemAsync?: (path: string) => Promise , + setItemAsync?: (path: string, value: string) => Promise , + removeItemAsync?: (path: string) => Promise , + clear: () => void, + getAllKeys?: () => Array + getAllKeysAsync?: () => Promise > + } + | { + async: 1, + getItem?: (path: string) => string | null, + setItem?: (path: string, value: string) => void, + removeItem?: (path: string) => void, + getItemAsync: (path: string) => Promise , + setItemAsync: (path: string, value: string) => Promise , + removeItemAsync: (path: string) => Promise , + clear: () => void, + getAllKeys?: () => Array + getAllKeysAsync?: () => Promise > + }; + const Storage = { async(): boolean { const controller = CoreManager.getStorageController(); 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 +44,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 +89,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 80% rename from src/StorageController.browser.js rename to src/StorageController.browser.ts index 0fdcd37b7..275d63715 100644 --- a/src/StorageController.browser.js +++ b/src/StorageController.browser.ts @@ -7,7 +7,7 @@ const StorageController = { async: 0, - getItem(path: string): ?string { + getItem(path: string): string | null { return localStorage.getItem(path); }, @@ -25,9 +25,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 +38,4 @@ const StorageController = { }; module.exports = StorageController; +export default StorageController; diff --git a/src/StorageController.default.js b/src/StorageController.default.ts similarity index 89% rename from src/StorageController.default.js rename to src/StorageController.default.ts index cb21dd54f..c6ab92ceb 100644 --- a/src/StorageController.default.js +++ b/src/StorageController.default.ts @@ -8,7 +8,7 @@ const memMap = {}; const StorageController = { async: 0, - getItem(path: string): ?string { + getItem(path: string): string | null { if (memMap.hasOwnProperty(path)) { return memMap[path]; } @@ -37,3 +37,4 @@ const StorageController = { }; module.exports = StorageController; +export default StorageController; 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..f44b72bbb 100644 --- a/src/StorageController.react-native.js +++ b/src/StorageController.react-native.ts @@ -7,33 +7,33 @@ 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 +43,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 || null); } }); }); }, - 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 +80,9 @@ const StorageController = { }, clear() { - return CoreManager.getAsyncStorage().clear(); + return CoreManager.getAsyncStorage()!.clear(); }, }; module.exports = StorageController; +export default StorageController; diff --git a/src/StorageController.weapp.js b/src/StorageController.weapp.ts similarity index 78% rename from src/StorageController.weapp.js rename to src/StorageController.weapp.ts index 321172c4e..c987c3b40 100644 --- a/src/StorageController.weapp.js +++ b/src/StorageController.weapp.ts @@ -6,12 +6,14 @@ 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,15 +21,18 @@ 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(); }, }; diff --git a/src/TaskQueue.js b/src/TaskQueue.ts similarity index 80% rename from src/TaskQueue.js rename to src/TaskQueue.ts index eedd769fe..55d45b757 100644 --- a/src/TaskQueue.js +++ b/src/TaskQueue.ts @@ -4,8 +4,8 @@ import { resolvingPromise } from './promiseUtils'; type Task = { - task: () => Promise, - _completion: Promise, + task: () => Promise , + _completion: ReturnType >, }; class TaskQueue { @@ -15,8 +15,8 @@ class TaskQueue { this.queue = []; } - enqueue(task: () => Promise): Promise { - const taskComplete = new resolvingPromise(); + enqueue(task: () => Promise ): Promise { + const taskComplete = resolvingPromise (); this.queue.push({ task: task, _completion: taskComplete, @@ -55,3 +55,4 @@ class TaskQueue { } module.exports = TaskQueue; +export default TaskQueue; diff --git a/src/UniqueInstanceStateController.js b/src/UniqueInstanceStateController.ts similarity index 92% rename from src/UniqueInstanceStateController.js rename to src/UniqueInstanceStateController.ts index aaf21da10..56763e3ae 100644 --- a/src/UniqueInstanceStateController.js +++ b/src/UniqueInstanceStateController.ts @@ -11,7 +11,7 @@ import type { AttributeMap, ObjectCache, OpsMap, State } from './ObjectStateMuta let objectState = new WeakMap(); -export function getState(obj: ParseObject): ?State { +export function getState(obj: ParseObject): State | null { const classData = objectState.get(obj); return classData || null; } @@ -35,7 +35,7 @@ export function initializeState(obj: ParseObject, initial?: State): State { return state; } -export function removeState(obj: ParseObject): ?State { +export function removeState(obj: ParseObject): State | null { const state = getState(obj); if (state === null) { return null; @@ -65,7 +65,7 @@ export function getPendingOps(obj: ParseObject): Array { return [{}]; } -export function setPendingOp(obj: ParseObject, attr: string, op: ?Op) { +export function setPendingOp(obj: ParseObject, attr: string, op?: Op) { const pendingOps = initializeState(obj).pendingOps; ObjectStateMutations.setPendingOp(pendingOps, attr, op); } @@ -75,7 +75,7 @@ export function pushPendingState(obj: ParseObject) { ObjectStateMutations.pushPendingState(pendingOps); } -export function popPendingState(obj: ParseObject): OpsMap { +export function popPendingState(obj: ParseObject): OpsMap | undefined { const pendingOps = initializeState(obj).pendingOps; return ObjectStateMutations.popPendingState(pendingOps); } @@ -93,7 +93,7 @@ export function getObjectCache(obj: ParseObject): ObjectCache { return {}; } -export function estimateAttribute(obj: ParseObject, attr: string): mixed { +export function estimateAttribute(obj: ParseObject, attr: string): any { const serverData = getServerData(obj); const pendingOps = getPendingOps(obj); return ObjectStateMutations.estimateAttribute( @@ -116,7 +116,7 @@ export function commitServerChanges(obj: ParseObject, changes: AttributeMap) { ObjectStateMutations.commitServerChanges(state.serverData, state.objectCache, changes); } -export function enqueueTask(obj: ParseObject, task: () => Promise): Promise { +export function enqueueTask(obj: ParseObject, task: () => Promise ): Promise { const state = initializeState(obj); return state.tasks.enqueue(task); } diff --git a/src/Xhr.weapp.js b/src/Xhr.weapp.ts similarity index 81% rename from src/Xhr.weapp.js rename to src/Xhr.weapp.ts index cb9f90121..033fe5267 100644 --- a/src/Xhr.weapp.js +++ b/src/Xhr.weapp.ts @@ -1,4 +1,24 @@ module.exports = class XhrWeapp { + UNSENT: number; + OPENED: number; + HEADERS_RECEIVED: number; + LOADING: number; + DONE: number; + header: {}; + readyState: any; + status: number; + response: string; + responseType: string; + responseText: string; + responseHeader: {}; + 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,6 +102,7 @@ module.exports = class XhrWeapp { loaded: res.totalBytesWritten, total: res.totalBytesExpectedToWrite, }; + // @ts-ignore this.onprogress(event); }); } diff --git a/src/__tests__/escape-test.js b/src/__tests__/escape-test.js index 11e6e07af..767d5723a 100644 --- a/src/__tests__/escape-test.js +++ b/src/__tests__/escape-test.js @@ -1,6 +1,6 @@ jest.autoMockOff(); -const escape = require('../escape.js').default; +const escape = require('../escape').default; describe('escape', () => { it('escapes special HTML characters', () => { diff --git a/src/arrayContainsObject.js b/src/arrayContainsObject.ts similarity index 96% rename from src/arrayContainsObject.js rename to src/arrayContainsObject.ts index ca9356849..9682cf108 100644 --- a/src/arrayContainsObject.js +++ b/src/arrayContainsObject.ts @@ -1,7 +1,3 @@ -/** - * @flow - */ - import ParseObject from './ParseObject'; export default function arrayContainsObject(array: Array , object: ParseObject): boolean { diff --git a/src/canBeSerialized.js b/src/canBeSerialized.ts similarity index 100% rename from src/canBeSerialized.js rename to src/canBeSerialized.ts diff --git a/src/decode.js b/src/decode.ts similarity index 90% rename from src/decode.js rename to src/decode.ts index 557002015..4fc5efd6b 100644 --- a/src/decode.js +++ b/src/decode.ts @@ -1,7 +1,6 @@ /** * @flow */ -import ParseACL from './ParseACL'; // eslint-disable-line no-unused-vars import ParseFile from './ParseFile'; import ParseGeoPoint from './ParseGeoPoint'; import ParsePolygon from './ParsePolygon'; @@ -9,12 +8,13 @@ import ParseObject from './ParseObject'; import { opFromJSON } from './ParseOp'; import ParseRelation from './ParseRelation'; +/** Decodes values from storage type */ export default function decode(value: any): any { if (value === null || typeof value !== 'object' || value instanceof Date) { return value; } if (Array.isArray(value)) { - const dup = []; + const dup: any[] = []; value.forEach((v, i) => { dup[i] = decode(v); }); @@ -31,7 +31,7 @@ export default function decode(value: any): any { } if (value.__type === 'Relation') { // The parent and key fields will be populated by the parent - const relation = new ParseRelation(null, null); + const relation = new ParseRelation(null, null); // null, null; since tests expect this. relation.targetClassName = value.className; return relation; } diff --git a/src/encode.js b/src/encode.ts similarity index 93% rename from src/encode.js rename to src/encode.ts index 0214990f5..140d54aa5 100644 --- a/src/encode.js +++ b/src/encode.ts @@ -11,11 +11,11 @@ import { Op } from './ParseOp'; import ParseRelation from './ParseRelation'; function encode( - value: mixed, + value: any, disallowObjects: boolean, forcePointers: boolean, - seen: Array , - offline: boolean + seen: Array , + offline?: boolean ): any { if (value instanceof ParseObject) { if (disallowObjects) { @@ -56,7 +56,7 @@ function encode( if (isNaN(value)) { throw new Error('Tried to encode an invalid date.'); } - return { __type: 'Date', iso: (value: any).toJSON() }; + return { __type: 'Date', iso: (value as Date).toJSON() }; } if ( Object.prototype.toString.call(value) === '[object RegExp]' && @@ -83,10 +83,10 @@ function encode( } export default function ( - value: mixed, + value: any, disallowObjects?: boolean, forcePointers?: boolean, - seen?: Array , + seen?: Array , offline?: boolean ): any { return encode(value, !!disallowObjects, !!forcePointers, seen || [], offline); diff --git a/src/equals.js b/src/equals.ts similarity index 95% rename from src/equals.js rename to src/equals.ts index 3af927c99..234141519 100644 --- a/src/equals.js +++ b/src/equals.ts @@ -3,7 +3,7 @@ import ParseFile from './ParseFile'; import ParseGeoPoint from './ParseGeoPoint'; import ParseObject from './ParseObject'; -export default function equals(a, b) { +export default function equals(a: any, b: any): boolean { const toString = Object.prototype.toString; if (toString.call(a) === '[object Date]' || toString.call(b) === '[object Date]') { const dateA = new Date(a); diff --git a/src/escape.js b/src/escape.ts similarity index 100% rename from src/escape.js rename to src/escape.ts diff --git a/src/isRevocableSession.js b/src/isRevocableSession.ts similarity index 100% rename from src/isRevocableSession.js rename to src/isRevocableSession.ts diff --git a/src/parseDate.js b/src/parseDate.ts similarity index 90% rename from src/parseDate.js rename to src/parseDate.ts index 04ff6ea7f..8de789d67 100644 --- a/src/parseDate.js +++ b/src/parseDate.ts @@ -2,7 +2,7 @@ * @flow */ -export default function parseDate(iso8601: string): ?Date { +export default function parseDate(iso8601: string): Date | null { const regexp = new RegExp( '^([0-9]{1,4})-([0-9]{1,2})-([0-9]{1,2})' + 'T' + diff --git a/src/promiseUtils.js b/src/promiseUtils.ts similarity index 62% rename from src/promiseUtils.js rename to src/promiseUtils.ts index 176c32a0b..1302776b2 100644 --- a/src/promiseUtils.js +++ b/src/promiseUtils.ts @@ -1,30 +1,31 @@ // Create Deferred Promise -export function resolvingPromise() { - let res; - let rej; - const promise = new Promise((resolve, reject) => { +export function resolvingPromise () { + let res: (val: T) => void; + let rej: (err: any) => void; + const promise = new Promise ((resolve, reject) => { res = resolve; rej = reject; }); - promise.resolve = res; - promise.reject = rej; - return promise; + const ret: typeof promise & { resolve: (res: T) => void, reject: (err: any) => void } = promise as any; + ret.resolve = res!; + ret.reject = rej!; + return ret; } -export function when(promises) { - let objects; +export function when(promises: Promise | (Promise []), ...others: Promise []) { + let objects: Promise []; const arrayArgument = Array.isArray(promises); if (arrayArgument) { objects = promises; } else { - objects = arguments; + objects = arguments as any as Promise []; } let total = objects.length; let hadError = false; - const results = []; + const results: any[] = []; const returnValue = arrayArgument ? [results] : results; - const errors = []; + const errors: any[] = []; results.length = objects.length; errors.length = objects.length; @@ -32,7 +33,7 @@ export function when(promises) { return Promise.resolve(returnValue); } - const promise = new resolvingPromise(); + const promise = resolvingPromise(); const resolveOne = function () { total--; @@ -45,7 +46,7 @@ export function when(promises) { } }; - const chain = function (object, index) { + const chain = function (object: Promise , index: number) { if (object && typeof object.then === 'function') { object.then( function (result) { @@ -66,11 +67,10 @@ export function when(promises) { for (let i = 0; i < objects.length; i++) { chain(objects[i], i); } - return promise; } -export function continueWhile(test, emitter) { +export function continueWhile(test: () => boolean, emitter: () => Promise ) { if (test()) { return emitter().then(() => { return continueWhile(test, emitter); diff --git a/src/unique.js b/src/unique.ts similarity index 94% rename from src/unique.js rename to src/unique.ts index 169c288f5..1ee8f2cb1 100644 --- a/src/unique.js +++ b/src/unique.ts @@ -6,7 +6,7 @@ import arrayContainsObject from './arrayContainsObject'; import ParseObject from './ParseObject'; export default function unique (arr: Array ): Array { - const uniques = []; + const uniques: T[] = []; arr.forEach(value => { if (value instanceof ParseObject) { if (!arrayContainsObject(uniques, value)) { diff --git a/src/unsavedChildren.js b/src/unsavedChildren.ts similarity index 90% rename from src/unsavedChildren.js rename to src/unsavedChildren.ts index 088bd6084..9a55c722e 100644 --- a/src/unsavedChildren.js +++ b/src/unsavedChildren.ts @@ -24,7 +24,7 @@ export default function unsavedChildren( allowDeepUnsaved?: boolean ): Array { const encountered = { - objects: {}, + objects: {} as Record , files: [], }; const identifier = obj.className + ':' + obj._getId(); @@ -35,17 +35,17 @@ export default function unsavedChildren( traverse(attributes[attr], encountered, false, !!allowDeepUnsaved); } } - const unsaved = []; + const unsaved: ParseObject[] = []; for (const id in encountered.objects) { if (id !== identifier && encountered.objects[id] !== true) { - unsaved.push(encountered.objects[id]); + unsaved.push(encountered.objects[id] as ParseObject); } } return unsaved.concat(encountered.files); } function traverse( - obj: ParseObject, + obj: ParseObject | ParseFile | ParseRelation | Array , encountered: EncounterMap, shouldThrow: boolean, allowDeepUnsaved: boolean diff --git a/src/uuid.js b/src/uuid.ts similarity index 68% rename from src/uuid.js rename to src/uuid.ts index 450d4976c..8308a37e6 100644 --- a/src/uuid.js +++ b/src/uuid.ts @@ -1,8 +1,8 @@ -let uuid = null; +let uuid: () => string = null as any; if (process.env.PARSE_BUILD === 'weapp') { uuid = function () { - const s = []; + const s: string[] = []; const hexDigits = '0123456789abcdef'; for (let i = 0; i < 36; i++) { @@ -10,9 +10,8 @@ if (process.env.PARSE_BUILD === 'weapp') { } s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010 - s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 + s[19] = hexDigits.substr((Number(s[19]) & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 s[8] = s[13] = s[18] = s[23] = '-'; - return s.join(''); }; } else { @@ -21,3 +20,4 @@ if (process.env.PARSE_BUILD === 'weapp') { } module.exports = uuid; +export default uuid; diff --git a/tsconfig.json b/tsconfig.json index 051477c29..b5166cc62 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,8 +8,7 @@ "noImplicitAny": false, "allowJs": false }, - "files": [ - "src/Parse.ts", - "src/ParseSession.ts" + "include": [ + "src/*.ts" ] }