Chore: Migrate lib/encryption folder to TypeScript (#3639)

* Initial commit

* add types/bytebuffer, add type definitions to params and update interfaces

* add more types and type assertions

* update types

* change bang operator by type assertion and update class variables definitions

* add types for deferred class

* minor tweaks on types definitions

* add ts-ignore

* Update encryption.ts

* update deferred and encryption

* update encryption.ts

* Update room.ts

* update toDecrypt type

* initialize sessionKeyExportedString

* remove return types
This commit is contained in:
Gerzon Z 2022-02-16 17:14:28 -04:00 committed by GitHub
parent 97f8271127
commit 352a718631
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 178 additions and 94 deletions

View File

@ -3,7 +3,8 @@ import { MarkdownAST } from '@rocket.chat/message-parser';
import { IAttachment } from './IAttachment';
import { IReaction } from './IReaction';
import { SubscriptionType } from './ISubscription';
export type MessageType = 'jitsi_call_started' | 'discussion-created' | 'e2e' | 'load_more' | 'rm' | 'uj';
export interface IUserMessage {
_id: string;
@ -34,12 +35,16 @@ export interface ITranslations {
value: string;
}
export type E2EType = 'pending' | 'done';
export interface ILastMessage {
_id: string;
rid: string;
tshow: boolean;
t: MessageType;
tmid: string;
msg: string;
e2e: E2EType;
ts: Date;
u: IUserMessage;
_updatedAt: Date;
@ -55,8 +60,9 @@ export interface ILastMessage {
export interface IMessage {
_id: string;
rid: string;
msg?: string;
t?: SubscriptionType;
t?: MessageType;
ts: Date;
u: IUserMessage;
alias: string;

View File

@ -12,6 +12,7 @@ export enum SubscriptionType {
DIRECT = 'd',
CHANNEL = 'c',
OMNICHANNEL = 'l',
E2E = 'e2e',
THREAD = 'thread' // FIXME: this is not a type of subscription
}

View File

@ -2,9 +2,8 @@ import Model from '@nozbe/watermelondb/Model';
import { MarkdownAST } from '@rocket.chat/message-parser';
import { IAttachment } from './IAttachment';
import { IEditedBy, IUserChannel, IUserMention, IUserMessage } from './IMessage';
import { IEditedBy, IUserChannel, IUserMention, IUserMessage, MessageType } from './IMessage';
import { IReaction } from './IReaction';
import { SubscriptionType } from './ISubscription';
import { IUrl } from './IUrl';
interface IFileThread {
@ -35,8 +34,9 @@ export interface IThreadResult {
export interface IThread {
id: string;
tmsg?: string;
msg?: string;
t?: SubscriptionType;
t?: MessageType;
rid: string;
_updatedAt?: Date;
ts?: Date;

View File

@ -1,13 +1,13 @@
import Model from '@nozbe/watermelondb/Model';
import { IAttachment } from './IAttachment';
import { IEditedBy, ITranslations, IUserChannel, IUserMention, IUserMessage } from './IMessage';
import { IEditedBy, ITranslations, IUserChannel, IUserMention, IUserMessage, MessageType } from './IMessage';
import { IReaction } from './IReaction';
import { SubscriptionType } from './ISubscription';
export interface IThreadMessage {
tmsg?: string;
msg?: string;
t?: SubscriptionType;
t?: MessageType;
rid: string;
ts: Date;
u: IUserMessage;

View File

@ -99,8 +99,8 @@ export interface IUser extends IRocketChatRecord {
roles: string[];
type: string;
active: boolean;
username?: string;
name?: string;
username: string;
services?: IUserServices;
emails?: IUserEmail[];
status?: UserStatus;

View File

@ -1,7 +1,7 @@
import EJSON from 'ejson';
import SimpleCrypto from 'react-native-simple-crypto';
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { Q } from '@nozbe/watermelondb';
import { Q, Model } from '@nozbe/watermelondb';
import RocketChat from '../rocketchat';
import UserPreferences from '../userPreferences';
@ -20,9 +20,25 @@ import {
} from './constants';
import { joinVectorData, randomPassword, splitVectorData, toString, utf8ToBuffer } from './utils';
import { EncryptionRoom } from './index';
import { IMessage, ISubscription, TMessageModel, TSubscriptionModel, TThreadMessageModel, TThreadModel } from '../../definitions';
class Encryption {
ready: boolean;
privateKey: string | null;
readyPromise: Deferred;
userId: string | null;
roomInstances: {
[rid: string]: {
ready: boolean;
provideKeyToUser: Function;
handshake: Function;
decrypt: Function;
encrypt: Function;
};
};
constructor() {
this.userId = '';
this.ready = false;
this.privateKey = null;
this.roomInstances = {};
@ -37,7 +53,7 @@ class Encryption {
}
// Initialize Encryption client
initialize = userId => {
initialize = (userId: string) => {
this.userId = userId;
this.roomInstances = {};
@ -82,7 +98,7 @@ class Encryption {
};
// When a new participant join and request a new room encryption key
provideRoomKeyToUser = async (keyId, rid) => {
provideRoomKeyToUser = async (keyId: string, rid: string) => {
// If the client is not ready
if (!this.ready) {
try {
@ -100,14 +116,14 @@ class Encryption {
};
// Persist keys on UserPreferences
persistKeys = async (server, publicKey, privateKey) => {
persistKeys = async (server: string, publicKey: string, privateKey: string) => {
this.privateKey = await SimpleCrypto.RSA.importKey(EJSON.parse(privateKey));
await UserPreferences.setStringAsync(`${server}-${E2E_PUBLIC_KEY}`, EJSON.stringify(publicKey));
await UserPreferences.setStringAsync(`${server}-${E2E_PRIVATE_KEY}`, privateKey);
};
// Could not obtain public-private keypair from server.
createKeys = async (userId, server) => {
createKeys = async (userId: string, server: string) => {
// Generate new keys
const key = await SimpleCrypto.RSA.generateKeys(2048);
@ -132,7 +148,7 @@ class Encryption {
};
// Encode a private key before send it to the server
encodePrivateKey = async (privateKey, password, userId) => {
encodePrivateKey = async (privateKey: string, password: string, userId: string) => {
const masterKey = await this.generateMasterKey(password, userId);
const vector = await SimpleCrypto.utils.randomBytes(16);
@ -142,7 +158,7 @@ class Encryption {
};
// Decode a private key fetched from server
decodePrivateKey = async (privateKey, password, userId) => {
decodePrivateKey = async (privateKey: string, password: string, userId: string) => {
const masterKey = await this.generateMasterKey(password, userId);
const [vector, cipherText] = splitVectorData(EJSON.parse(privateKey));
@ -152,7 +168,7 @@ class Encryption {
};
// Generate a user master key, this is based on userId and a password
generateMasterKey = async (password, userId) => {
generateMasterKey = async (password: string, userId: string) => {
const iterations = 1000;
const hash = 'SHA256';
const keyLen = 32;
@ -166,18 +182,18 @@ class Encryption {
};
// Create a random password to local created keys
createRandomPassword = async server => {
createRandomPassword = async (server: string) => {
const password = randomPassword();
await UserPreferences.setStringAsync(`${server}-${E2E_RANDOM_PASSWORD_KEY}`, password);
return password;
};
changePassword = async (server, password) => {
changePassword = async (server: string, password: string) => {
// Cast key to the format server is expecting
const privateKey = await SimpleCrypto.RSA.exportKey(this.privateKey);
const privateKey = await SimpleCrypto.RSA.exportKey(this.privateKey as string);
// Encode the private key
const encodedPrivateKey = await this.encodePrivateKey(EJSON.stringify(privateKey), password, this.userId);
const encodedPrivateKey = await this.encodePrivateKey(EJSON.stringify(privateKey), password, this.userId as string);
const publicKey = await UserPreferences.getStringAsync(`${server}-${E2E_PUBLIC_KEY}`);
// Send the new keys to the server
@ -185,7 +201,7 @@ class Encryption {
};
// get a encryption room instance
getRoomInstance = async rid => {
getRoomInstance = async (rid: string) => {
// Prevent handshake again
if (this.roomInstances[rid]?.ready) {
return this.roomInstances[rid];
@ -193,7 +209,7 @@ class Encryption {
// If doesn't have a instance of this room
if (!this.roomInstances[rid]) {
this.roomInstances[rid] = new EncryptionRoom(rid, this.userId);
this.roomInstances[rid] = new EncryptionRoom(rid, this.userId as string);
}
const roomE2E = this.roomInstances[rid];
@ -206,7 +222,7 @@ class Encryption {
// Logic to decrypt all pending messages/threads/threadMessages
// after initialize the encryption client
decryptPendingMessages = async roomId => {
decryptPendingMessages = async (roomId?: string) => {
const db = database.active;
const messagesCollection = db.get('messages');
@ -228,8 +244,12 @@ class Encryption {
const threadMessagesToDecrypt = await threadMessagesCollection.query(...whereClause).fetch();
// Concat messages/threads/threadMessages
let toDecrypt = [...messagesToDecrypt, ...threadsToDecrypt, ...threadMessagesToDecrypt];
toDecrypt = await Promise.all(
let toDecrypt: (TThreadModel | TThreadMessageModel)[] = [
...messagesToDecrypt,
...threadsToDecrypt,
...threadMessagesToDecrypt
];
toDecrypt = (await Promise.all(
toDecrypt.map(async message => {
const { t, msg, tmsg } = message;
const { id: rid } = message.subscription;
@ -240,9 +260,10 @@ class Encryption {
msg,
tmsg
});
try {
return message.prepareUpdate(
protectedFunction(m => {
protectedFunction((m: TMessageModel) => {
Object.assign(m, newMessage);
})
);
@ -250,9 +271,9 @@ class Encryption {
return null;
}
})
);
)) as (TThreadModel | TThreadMessageModel)[];
await db.action(async () => {
await db.write(async () => {
await db.batch(...toDecrypt);
});
} catch (e) {
@ -271,19 +292,19 @@ class Encryption {
const subsEncrypted = await subCollection.query(Q.where('e2e_key_id', Q.notEq(null))).fetch();
// We can't do this on database level since lastMessage is not a database object
const subsToDecrypt = subsEncrypted.filter(
sub =>
(sub: ISubscription) =>
// Encrypted message
sub?.lastMessage?.t === E2E_MESSAGE_TYPE &&
// Message pending decrypt
sub?.lastMessage?.e2e === E2E_STATUS.PENDING
);
await Promise.all(
subsToDecrypt.map(async sub => {
subsToDecrypt.map(async (sub: TSubscriptionModel) => {
const { rid, lastMessage } = sub;
const newSub = await this.decryptSubscription({ rid, lastMessage });
try {
return sub.prepareUpdate(
protectedFunction(m => {
protectedFunction((m: TSubscriptionModel) => {
Object.assign(m, newSub);
})
);
@ -293,7 +314,7 @@ class Encryption {
})
);
await db.action(async () => {
await db.write(async () => {
await db.batch(...subsToDecrypt);
});
} catch (e) {
@ -302,7 +323,7 @@ class Encryption {
};
// Decrypt a subscription lastMessage
decryptSubscription = async subscription => {
decryptSubscription = async (subscription: Partial<ISubscription>) => {
// If the subscription doesn't have a lastMessage just return
if (!subscription?.lastMessage) {
return subscription;
@ -334,18 +355,18 @@ class Encryption {
let subRecord;
try {
subRecord = await subCollection.find(rid);
subRecord = await subCollection.find(rid as string);
} catch {
// Do nothing
}
try {
const batch = [];
const batch: (Model | null | void | false | Promise<void>)[] = [];
// If the subscription doesn't exists yet
if (!subRecord) {
// Let's create the subscription with the data received
batch.push(
subCollection.prepareCreate(s => {
subCollection.prepareCreate((s: TSubscriptionModel) => {
s._raw = sanitizedRaw({ id: rid }, subCollection.schema);
Object.assign(s, subscription);
})
@ -355,7 +376,7 @@ class Encryption {
try {
// Let's update the subscription with the received E2EKey
batch.push(
subRecord.prepareUpdate(s => {
subRecord.prepareUpdate((s: TSubscriptionModel) => {
s.E2EKey = subscription.E2EKey;
})
);
@ -366,7 +387,7 @@ class Encryption {
// If batch has some operation
if (batch.length) {
await db.action(async () => {
await db.write(async () => {
await db.batch(...batch);
});
}
@ -377,7 +398,7 @@ class Encryption {
}
// Get a instance using the subscription
const roomE2E = await this.getRoomInstance(rid);
const roomE2E = await this.getRoomInstance(rid as string);
const decryptedMessage = await roomE2E.decrypt(lastMessage);
return {
...subscription,
@ -386,7 +407,7 @@ class Encryption {
};
// Encrypt a message
encryptMessage = async message => {
encryptMessage = async (message: IMessage) => {
const { rid } = message;
const db = database.active;
const subCollection = db.get('subscriptions');
@ -419,7 +440,7 @@ class Encryption {
};
// Decrypt a message
decryptMessage = async message => {
decryptMessage = async (message: Partial<IMessage>) => {
const { t, e2e } = message;
// Prevent create a new instance if this room was encrypted sometime ago
@ -440,15 +461,15 @@ class Encryption {
}
const { rid } = message;
const roomE2E = await this.getRoomInstance(rid);
const roomE2E = await this.getRoomInstance(rid as string);
return roomE2E.decrypt(message);
};
// Decrypt multiple messages
decryptMessages = messages => Promise.all(messages.map(m => this.decryptMessage(m)));
decryptMessages = (messages: IMessage[]) => Promise.all(messages.map((m: IMessage) => this.decryptMessage(m)));
// Decrypt multiple subscriptions
decryptSubscriptions = subscriptions => Promise.all(subscriptions.map(s => this.decryptSubscription(s)));
decryptSubscriptions = (subscriptions: ISubscription[]) => Promise.all(subscriptions.map(s => this.decryptSubscription(s)));
}
const encryption = new Encryption();

View File

@ -1,7 +1,9 @@
import EJSON from 'ejson';
import { Base64 } from 'js-base64';
import SimpleCrypto from 'react-native-simple-crypto';
import ByteBuffer from 'bytebuffer';
import { IMessage } from '../../definitions';
import RocketChat from '../rocketchat';
import Deferred from '../../utils/deferred';
import debounce from '../../utils/debounce';
@ -19,13 +21,26 @@ import {
utf8ToBuffer
} from './utils';
import { Encryption } from './index';
import { IUser } from '../../definitions/IUser';
export default class EncryptionRoom {
constructor(roomId, userId) {
ready: boolean;
roomId: string;
userId: string;
establishing: boolean;
readyPromise: Deferred;
sessionKeyExportedString: string | ByteBuffer;
keyID: string;
roomKey: ArrayBuffer;
constructor(roomId: string, userId: string) {
this.ready = false;
this.roomId = roomId;
this.userId = userId;
this.establishing = false;
this.keyID = '';
this.sessionKeyExportedString = '';
this.roomKey = new ArrayBuffer(0);
this.readyPromise = new Deferred();
this.readyPromise.then(() => {
// Mark as ready
@ -57,7 +72,7 @@ export default class EncryptionRoom {
const { E2EKey, e2eKeyId } = subscription;
// If this room has a E2EKey, we import it
if (E2EKey) {
if (E2EKey && Encryption.privateKey) {
// We're establishing a new room encryption client
this.establishing = true;
await this.importRoomKey(E2EKey, Encryption.privateKey);
@ -82,25 +97,25 @@ export default class EncryptionRoom {
};
// Import roomKey as an AES Decrypt key
importRoomKey = async (E2EKey, privateKey) => {
importRoomKey = async (E2EKey: string, privateKey: string) => {
const roomE2EKey = E2EKey.slice(12);
const decryptedKey = await SimpleCrypto.RSA.decrypt(roomE2EKey, privateKey);
this.sessionKeyExportedString = toString(decryptedKey);
this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);
this.keyID = Base64.encode(this.sessionKeyExportedString as string).slice(0, 12);
// Extract K from Web Crypto Secret Key
// K is a base64URL encoded array of bytes
// Web Crypto API uses this as a private key to decrypt/encrypt things
// Reference: https://www.javadoc.io/doc/com.nimbusds/nimbus-jose-jwt/5.1/com/nimbusds/jose/jwk/OctetSequenceKey.html
const { k } = EJSON.parse(this.sessionKeyExportedString);
const { k } = EJSON.parse(this.sessionKeyExportedString as string);
this.roomKey = b64ToBuffer(k);
};
// Create a key to a room
createRoomKey = async () => {
const key = await SimpleCrypto.utils.randomBytes(16);
const key = (await SimpleCrypto.utils.randomBytes(16)) as Uint8Array;
this.roomKey = key;
// Web Crypto format of a Secret Key
@ -131,7 +146,7 @@ export default class EncryptionRoom {
// Each time you see a encrypted message of a room that you don't have a key
// this will be called again and run once in 5 seconds
requestRoomKey = debounce(
async e2eKeyId => {
async (e2eKeyId: string) => {
await RocketChat.e2eRequestRoomKey(this.roomId, e2eKeyId);
},
5000,
@ -143,22 +158,22 @@ export default class EncryptionRoom {
const result = await RocketChat.e2eGetUsersOfRoomWithoutKey(this.roomId);
if (result.success) {
const { users } = result;
await Promise.all(users.map(user => this.encryptRoomKeyForUser(user)));
await Promise.all(users.map((user: IUser) => this.encryptRoomKeyForUser(user)));
}
};
// Encrypt the room key to each user in
encryptRoomKeyForUser = async user => {
encryptRoomKeyForUser = async (user: IUser) => {
if (user?.e2e?.public_key) {
const { public_key: publicKey } = user.e2e;
const userKey = await SimpleCrypto.RSA.importKey(EJSON.parse(publicKey));
const encryptedUserKey = await SimpleCrypto.RSA.encrypt(this.sessionKeyExportedString, userKey);
const encryptedUserKey = await SimpleCrypto.RSA.encrypt(this.sessionKeyExportedString as string, userKey);
await RocketChat.e2eUpdateGroupKey(user?._id, this.roomId, this.keyID + encryptedUserKey);
}
};
// Provide this room key to a user
provideKeyToUser = async keyId => {
provideKeyToUser = async (keyId: string) => {
// Don't provide a key if the keyId received
// is different than the current one
if (this.keyID !== keyId) {
@ -169,16 +184,16 @@ export default class EncryptionRoom {
};
// Encrypt text
encryptText = async text => {
text = utf8ToBuffer(text);
encryptText = async (text: string | ArrayBuffer) => {
text = utf8ToBuffer(text as string);
const vector = await SimpleCrypto.utils.randomBytes(16);
const data = await SimpleCrypto.AES.encrypt(text, this.roomKey, vector);
const data = await SimpleCrypto.AES.encrypt(text, this.roomKey as ArrayBuffer, vector);
return this.keyID + bufferToB64(joinVectorData(vector, data));
};
// Encrypt messages
encrypt = async message => {
encrypt = async (message: IMessage) => {
if (!this.ready) {
return message;
}
@ -207,8 +222,8 @@ export default class EncryptionRoom {
};
// Decrypt text
decryptText = async msg => {
msg = b64ToBuffer(msg.slice(12));
decryptText = async (msg: string | ArrayBuffer) => {
msg = b64ToBuffer(msg.slice(12) as string);
const [vector, cipherText] = splitVectorData(msg);
const decrypted = await SimpleCrypto.AES.decrypt(cipherText, this.roomKey, vector);
@ -219,7 +234,7 @@ export default class EncryptionRoom {
};
// Decrypt messages
decrypt = async message => {
decrypt = async (message: IMessage) => {
if (!this.ready) {
return message;
}
@ -231,7 +246,7 @@ export default class EncryptionRoom {
if (t === E2E_MESSAGE_TYPE && e2e !== E2E_STATUS.DONE) {
let { msg, tmsg } = message;
// Decrypt msg
msg = await this.decryptText(msg);
msg = await this.decryptText(msg as string);
// Decrypt tmsg
if (tmsg) {

View File

@ -1,4 +1,3 @@
/* eslint-disable no-bitwise */
import ByteBuffer from 'bytebuffer';
import SimpleCrypto from 'react-native-simple-crypto';
@ -7,12 +6,13 @@ import { fromByteArray, toByteArray } from '../../utils/base64-js';
const BASE64URI = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
export const b64ToBuffer = base64 => toByteArray(base64).buffer;
// @ts-ignore
export const b64ToBuffer = (base64: string): ArrayBuffer => toByteArray(base64).buffer;
export const utf8ToBuffer = SimpleCrypto.utils.convertUtf8ToArrayBuffer;
export const bufferToB64 = arrayBuffer => fromByteArray(new Uint8Array(arrayBuffer));
export const bufferToB64 = (arrayBuffer: ArrayBuffer): string => fromByteArray(new Uint8Array(arrayBuffer));
// ArrayBuffer -> Base64 URI Safe
// https://github.com/herrjemand/Base64URL-ArrayBuffer/blob/master/lib/base64url-arraybuffer.js
export const bufferToB64URI = buffer => {
export const bufferToB64URI = (buffer: ArrayBuffer): string => {
const uintArray = new Uint8Array(buffer);
const len = uintArray.length;
let base64 = '';
@ -33,28 +33,28 @@ export const bufferToB64URI = buffer => {
return base64;
};
// SimpleCrypto.utils.convertArrayBufferToUtf8 is not working with unicode emoji
export const bufferToUtf8 = buffer => {
const uintArray = new Uint8Array(buffer);
export const bufferToUtf8 = (buffer: ArrayBuffer): string => {
const uintArray = new Uint8Array(buffer) as number[] & Uint8Array;
const encodedString = String.fromCharCode.apply(null, uintArray);
const decodedString = decodeURIComponent(escape(encodedString));
return decodedString;
return decodeURIComponent(escape(encodedString));
};
export const splitVectorData = text => {
export const splitVectorData = (text: ArrayBuffer): ArrayBuffer[] => {
const vector = text.slice(0, 16);
const data = text.slice(16);
return [vector, data];
};
export const joinVectorData = (vector, data) => {
export const joinVectorData = (vector: ArrayBuffer, data: ArrayBuffer): ArrayBufferLike => {
const output = new Uint8Array(vector.byteLength + data.byteLength);
output.set(new Uint8Array(vector), 0);
output.set(new Uint8Array(data), vector.byteLength);
return output.buffer;
};
export const toString = thing => {
export const toString = (thing: string | ByteBuffer | Buffer | ArrayBuffer | Uint8Array): string | ByteBuffer => {
if (typeof thing === 'string') {
return thing;
}
// eslint-disable-next-line new-cap
// @ts-ignore
return new ByteBuffer.wrap(thing).toString('binary');
};
export const randomPassword = () => `${random(3)}-${random(3)}-${random(3)}`.toLowerCase();
export const randomPassword = (): string => `${random(3)}-${random(3)}-${random(3)}`.toLowerCase();

View File

@ -37,7 +37,7 @@ const getLens = (b64: string) => {
};
// base64 is 4/3 + up to two characters of the original data
export const byteLength = (b64: string) => {
export const byteLength = (b64: string): number => {
const lens = getLens(b64);
const validLen = lens[0];
const placeHoldersLen = lens[1];
@ -47,7 +47,7 @@ export const byteLength = (b64: string) => {
const _byteLength = (b64: string, validLen: number, placeHoldersLen: number) =>
((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
export const toByteArray = (b64: string) => {
export const toByteArray = (b64: string): any[] | Uint8Array => {
let tmp;
const lens = getLens(b64);
const validLen = lens[0];
@ -106,7 +106,7 @@ const encodeChunk = (uint8: number[] | Uint8Array, start: number, end: number) =
return output.join('');
};
export const fromByteArray = (uint8: number[] | Uint8Array) => {
export const fromByteArray = (uint8: number[] | Uint8Array): string => {
let tmp;
const len = uint8.length;
const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes

View File

@ -1,14 +0,0 @@
// https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Promise.jsm/Deferred
export default class Deferred {
constructor() {
const promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
promise.resolve = this.resolve;
promise.reject = this.reject;
return promise;
}
}

41
app/utils/deferred.ts Normal file
View File

@ -0,0 +1,41 @@
export default class Deferred {
[Symbol.toStringTag]: 'Promise';
private promise: Promise<unknown>;
private _resolve: (value?: unknown) => void;
private _reject: (reason?: any) => void;
constructor() {
this._resolve = () => {};
this._reject = () => {};
this.promise = new Promise((resolve, reject) => {
this._resolve = resolve as (value?: unknown) => void;
this._reject = reject;
});
}
public then<TResult1, TResult2>(
onfulfilled?: ((value: unknown) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
): Promise<TResult1 | TResult2> {
return this.promise.then(onfulfilled, onrejected);
}
public catch<TResult>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null
): Promise<unknown | TResult> {
return this.promise.catch(onrejected);
}
public finally(onfinally?: (() => void) | null | undefined): Promise<unknown> {
return this.promise.finally(onfinally);
}
public resolve(value?: unknown): void {
this._resolve(value);
}
public reject(reason?: any): void {
this._reject(reason);
}
}

View File

@ -143,6 +143,7 @@
"@rocket.chat/eslint-config": "^0.4.0",
"@storybook/addon-storyshots": "5.3.21",
"@storybook/react-native": "5.3.25",
"@types/bytebuffer": "^5.0.43",
"@testing-library/react-native": "^9.0.0",
"@types/ejson": "^2.1.3",
"@types/jest": "^26.0.24",

View File

@ -4200,6 +4200,14 @@
dependencies:
"@babel/types" "^7.3.0"
"@types/bytebuffer@^5.0.43":
version "5.0.43"
resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.43.tgz#b5259fca1412106bcee0cabfbf7c104846d06738"
integrity sha512-vQnTYvy4LpSojHjKdmg4nXFI1BAiYPvZ/k3ouczZAQnbDprk1xqxJiFmFHyy8y6MuUq3slz5erNMtn6n87uVKw==
dependencies:
"@types/long" "*"
"@types/node" "*"
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
@ -4341,6 +4349,11 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.172.tgz#aad774c28e7bfd7a67de25408e03ee5a8c3d028a"
integrity sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw==
"@types/long@*":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==
"@types/minimatch@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"