From 7e54d33a734aa261c4c957904d6491916d296d02 Mon Sep 17 00:00:00 2001 From: Iuri Matias Date: Sat, 22 Dec 2018 10:24:39 -0500 Subject: [PATCH] abstract client functionality into its own module --- .gitignore | 1 + package-lock.json | 65 +++++------ package.json | 2 + src/channelManager.ts | 55 ++++----- src/index.ts | 4 +- src/index2.ts | 127 +++++++++++++++++++++ src/status-js-client/channel.ts | 151 +++++++++++++++++++++++++ src/status-js-client/channelManager.ts | 92 +++++++++++++++ src/status-js-client/index.ts | 57 ++++++++++ src/{ => status-js-client}/user.ts | 0 src/{ => status-js-client}/users.ts | 0 src/ui.ts | 5 +- yarn.lock | 12 +- 13 files changed, 492 insertions(+), 79 deletions(-) create mode 100644 src/index2.ts create mode 100644 src/status-js-client/channel.ts create mode 100644 src/status-js-client/channelManager.ts create mode 100644 src/status-js-client/index.ts rename src/{ => status-js-client}/user.ts (100%) rename src/{ => status-js-client}/users.ts (100%) diff --git a/.gitignore b/.gitignore index e69d0b4..33b5c3e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules dist yarn-debug.log* yarn-error.log* +TODO diff --git a/package-lock.json b/package-lock.json index aab32a4..9f35739 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,14 +67,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "requires": { - "lodash": "^4.17.10" - } - }, "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", @@ -1222,10 +1214,10 @@ "sha3": "^1.1.0" } }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" }, "lowercase-keys": { "version": "1.0.1", @@ -1698,6 +1690,14 @@ "inherits": "^2.0.1" } }, + "rxjs": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "requires": { + "tslib": "^1.9.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2012,6 +2012,11 @@ "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2034,14 +2039,6 @@ "mime-types": "~2.1.18" } }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "requires": { - "is-typedarray": "^1.0.0" - } - }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", @@ -2389,8 +2386,17 @@ "integrity": "sha512-wAnENuZx75T5ZSrT2De2LOaUuPf2yRjq1VfcbD7+Zd79F3DZZLBJcPyCNVQ1U0fAXt0wfgCKl7sVw5pffqR9Bw==", "requires": { "underscore": "1.8.3", - "web3-core-helpers": "1.0.0-beta.36", - "websocket": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2" + "web3-core-helpers": "1.0.0-beta.36" + }, + "dependencies": { + "websocket": { + "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", + "from": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", + "requires": { + "debug": "^2.2.0", + "nan": "^2.3.3" + } + } } }, "web3-shh": { @@ -2425,16 +2431,6 @@ } } }, - "websocket": { - "version": "git://github.com/frozeman/WebSocket-Node.git#6c72925e3f8aaaea8dc8450f97627e85263999f2", - "from": "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible", - "requires": { - "debug": "^2.2.0", - "nan": "^2.3.3", - "typedarray-to-buffer": "^3.1.2", - "yaeti": "^0.0.6" - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2501,11 +2497,6 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" - }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index 10b5f11..12a0f9e 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,9 @@ "homepage": "https://github.com/status-im/status-x#readme", "dependencies": { "colors": "^1.3.2", + "lodash.isequal": "^4.5.0", "neo-blessed": "^0.2.0", + "rxjs": "^6.3.3", "status-js-api": "^1.0.5" }, "devDependencies": { diff --git a/src/channelManager.ts b/src/channelManager.ts index 6747f65..550901a 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -1,75 +1,76 @@ const Events = require("events"); -import Users from "./users"; import colors from "colors"; class ChannelManager { public channels: any[]; public events: any; - public allUsers: Users; private currentChannel: number; constructor() { this.channels = []; this.events = new Events(); this.currentChannel = 0; - this.allUsers = new Users(); } - public addChannel(channelName: string, type: string, extraData?: any) { - if (this.getChannel(channelName)) { - return; - } - - const channel = {name: channelName, pendingMessages: [], type, ...extraData}; - channel.users = new Users(); + public addChannel(channelName: string, channelObject: any) { + const channel = {name: channelName, pendingMessages: [], channel: channelObject, users: [], typingUsers: []}; this.channels.push(channel); - this.events.emit("update"); + this.currentChannel = this.channels.length; + this.events.emit("channelSwitch"); + this.events.emit("updateChannels", this.getChannelList()); } public getChannel(channelName: string) { return this.channels.find((c) => c.name === channelName); } + public updateChannelUsers(channelName: string, users: any) { + this.getChannel(channelName).users = users; + this.events.emit("updateUsers", channelName, users); + } + + public updateChannelTypingUsers(channelName: string, typingUsers: any) { + this.getChannel(channelName).typingUsers = typingUsers; + this.events.emit("updateTypingUsers", channelName, typingUsers); + } + public getCurrentChannel() { - return this.channels[this.currentChannel]; + return this.channels[this.currentChannel - 1]; } public addMessage(channelName: string, message: string, pubkey: string, username: string) { const channel = this.getChannel(channelName); - if (channelName !== this.channels[this.currentChannel].name) { + if (channelName !== this.getCurrentChannel().name) { channel.pendingMessages.push({pubkey, username, message}); } else { this.events.emit("newMessage", channelName, username, message); } - const user = this.allUsers.addOrUpdateUserKey(pubkey, username); - channel.users.addUserOrUpdate(user); - this.events.emit("update"); } public dumpPendingMessages() { - const messages = this.channels[this.currentChannel].pendingMessages.slice(0); - this.channels[this.currentChannel].pendingMessages = []; + const messages = this.getCurrentChannel().pendingMessages.slice(0); + this.getCurrentChannel().pendingMessages = []; return messages; } public switchChannelIndex(index: number) { - if (index < 0) { + if (index <= 0) { return; } - if (index >= this.channels.length) { + if (index > this.channels.length) { return; } this.currentChannel = index; - this.events.emit("update"); this.events.emit("channelSwitch"); + this.events.emit("updateChannels", this.getChannelList()); } public getChannelList() { return this.channels.map((c) => { const prefix = c.type === "channel" ? "#" : ""; - if (c.name === this.channels[this.currentChannel].name) { + if (c.name === this.getCurrentChannel().name) { return colors.green(`${prefix}${c.name}`); } if (c.pendingMessages.length === 0) { @@ -78,15 +79,7 @@ class ChannelManager { return `${prefix}${c.name} (${c.pendingMessages.length})`; }); } - - public getUsersInCurrentChannel() { - const channel = this.getCurrentChannel(); - const userKeys = channel.users.getUsers(); - const users = userKeys.map((pubkey: string) => { - return this.allUsers.users[pubkey]; - }); - return users; - } } export default ChannelManager; + diff --git a/src/index.ts b/src/index.ts index 5e7c3bd..506f688 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,8 @@ import StatusJS from "status-js-api"; import UI from "./ui"; -import ChannelManager from "./channelManager"; +import ChannelManager from "./status-js-client/channelManager"; + +import StatusJSClient from "./status-js-client"; const DEFAULT_CHANNEL = "mytest"; const CONTACT_CODE_REGEXP = /^(0x)?[0-9a-f]{130}$/i; diff --git a/src/index2.ts b/src/index2.ts new file mode 100644 index 0000000..2cc729d --- /dev/null +++ b/src/index2.ts @@ -0,0 +1,127 @@ +import StatusJSClient from "./status-js-client/index"; +import UI from "./ui"; +import ChannelManager from "./channelManager"; + +const ui = new UI(); + +ui.logEntry(` + Welcome to + _________ __ __ ____ ___ + / _____// |______ _/ |_ __ __ _____\\ \\/ / + \\_____ \\\\ __\\__ \\\\ __\\ | \\/ ___/\\ / + / \\| | / __ \\| | | | /\\___ \\ / \\ + /_______ /|__| (____ /__| |____//____ >___/\\ \\ + \\/ \\/ \\/ \\_/ + `); + +ui.logEntry(`Generating Identity....`); +ui.logEntry(`Connecting to Peers....`); +ui.logEntry(`Rejoining Channels....`); + +(async () => { + const status = new StatusJSClient(); + await status.connectToNode("ws://localhost:8546") + const userPubKey = await status.getPublicKey(); + + ui.logEntry(`PK: ${userPubKey}`); + ui.logEntry(`-----------------------------------------------------------`); + + const channelManager = new ChannelManager(); + + ui.events.on("cmd", async (cmd: string) => { + if (cmd.split(" ")[0] === "/join") { + const channelName = cmd.split(" ")[1].replace("#", ""); + + ui.logEntry("joining " + channelName); + + if (channelManager.getChannel(channelName)) { + return ui.logEntry("you already joined this channel. you can switch channel with the /s command"); + } + + let channel = await status.joinChannel(channelName); + channelManager.addChannel(channelName, channel); + + channel.messagesObserver.subscribe((msg) => { + channelManager.addMessage(channelName, msg.message, msg.pubkey, msg.username); + }); + + channel.usersObserver.subscribe((users) => { + channelManager.updateChannelUsers(channelName, users); + }) + + channel.usersTypingObserver.subscribe((typingUsers) => { + channelManager.updateChannelTypingUsers(channelName, typingUsers); + }); + + return; + } + + if (cmd.split(" ")[0] === "/s") { + const channelNumber = cmd.split(" ")[1]; + channelManager.switchChannelIndex(parseInt(channelNumber, 10)); + return; + } + + const currentChannel = channelManager.getCurrentChannel(); + if (!currentChannel) { + ui.logEntry("not in any channel; try /join #mytest"); + return; + } + + channelManager.getCurrentChannel().channel.sendMessage(cmd); + }); + + ui.events.on("typing", (currentText: string) => { + if (currentText[0] === "/") { + return; + } + + const currentChannel = channelManager.getCurrentChannel(); + if (!currentChannel) return; + + currentChannel.channel.typingEvent(); + }); + + channelManager.events.on("newMessage", (channelName: string, username: string, message: string) => { + const msg = (username + ">").green + " " + message; + ui.logEntry(msg); + }); + + channelManager.events.on("channelSwitch", () => { + const currentChannel = channelManager.getCurrentChannel(); + + ui.logEntry("-------------------"); + ui.logEntry("now viewing #" + currentChannel.name); + + channelManager.dumpPendingMessages().forEach((message: any) => { + const msg = (message.username + ">").green + " " + message.message; + ui.logEntry(msg); + }); + }); + + channelManager.events.on("updateUsers", (channelName, users) => { + if (channelName !== channelManager.getCurrentChannel().name) { + return; + } + + ui.availableUsers(users.map((x: any) => { + return {name: x.username, status: (x.online ? "on" : "offline")}; + })); + }); + + channelManager.events.on("updateTypingUsers", (channelName, typingUsers) => { + if (channelName !== channelManager.getCurrentChannel().name) { + return; + } + + if (typingUsers.length === 0) { + return ui.setStatus(""); + } + if (typingUsers.length === 1) { + return ui.setStatus(typingUsers[0] + " is typing"); + } + return ui.setStatus(typingUsers.join(", ") + " are typing"); + }); + + channelManager.events.on("updateChannels", ui.availableChannels.bind(this)); +})(); diff --git a/src/status-js-client/channel.ts b/src/status-js-client/channel.ts new file mode 100644 index 0000000..d4cc84f --- /dev/null +++ b/src/status-js-client/channel.ts @@ -0,0 +1,151 @@ +import { Observable, fromEvent, interval } from 'rxjs'; +import { throttle, map, distinctUntilChanged } from 'rxjs/operators'; +import Events from 'events'; +import isEqual from 'lodash.isequal'; + +class Channel { + private channelName: string; + private status: any; + public messagesObserver: any; + public usersTypingObserver: any; + public usersObserver: any; + private typingObserver: any; + + constructor(channelName: string, status: any) { + this.status = status; + this.channelName = channelName; + this.events = new Events(); + this.usersTyping = {}; + this.users = {}; + this.typingObserver = fromEvent(this.events, 'typing'); + this.usersTypingObserver = fromEvent(this.events, 'usersTyping').pipe( + throttle(val => interval(450)), + map(() => Object.values(this.usersTyping).map(x => x.username)), + distinctUntilChanged(isEqual), + ); + this.usersObserver = fromEvent(this.events, 'users').pipe( + throttle(val => interval(1000)), + map(() => Object.values(this.users).map((x) => { + return {username: x.username, online: x.online} + })), + distinctUntilChanged(isEqual), + ); + } + + public joinChannel(cb) { + this.status.joinChat(this.channelName, () => { + this.pingChannel(); + this.listenToMessages(); + this.listenToTyping(); + this.listenToUsers(); + cb(); + }); + } + + private pingChannel() { + this.status.sendJsonMessage(this.channelName, {type: "ping"}); + setInterval(() => { + this.status.sendJsonMessage(this.channelName, {type: "ping"}); + }, 5 * 1000); + } + + private listenToMessages() { + this.messagesObserver = Observable.create((observer) => { + this.status.onMessage(this.channelName, (err: any, data: any) => { + if (err || !data) { + console.dir("---- error ") + console.dir(err) + console.dir(data) + + return observer.error(err); + } + const msg = JSON.parse(data.payload)[1][0]; + + if (JSON.parse(data.payload)[1][1] === "content/json") { + return; + } + this.usersTyping[data.data.sig] = {username: data.username, lastTyped: 0}; // user is likley no longer typing if a message was received + observer.next({message: msg, pubkey: data.data.sig, username: data.username}); + this.events.emit('users') + }); + }); + + this.status.onMessage(this.channelName, (err: any, data: any) => { + if (JSON.parse(data.payload)[1][1] !== "content/json") { + // usersTyping[data.data.sig] = 0; // user is likley no longer typing if a message was received + return; + } + + const msg = JSON.parse(JSON.parse(data.payload)[1][0]); + const fromUser = data.data.sig; + + if (msg.type === "ping") { + this.users[fromUser] = { + username: data.username, + lastSeen: (new Date().getTime()), + online: true + } + this.events.emit('users') + + // const user = channels.allUsers.addOrUpdateUserKey(fromUser, data.username); + // const channel = channels.getChannel(channelName); + // channel.users.addUserOrUpdate(user); + // channels.events.emit("update"); + } + + if (msg.type === "typing") { + //if (fromUser === userPubKey) { + // return; // ignore typing events from self + //} + this.usersTyping[fromUser] = {username: data.username, lastTyped: (new Date().getTime()), online: true}; + this.events.emit('usersTyping') + this.events.emit('users') + } + }); + } + + private listenToUsers() { + this.events.emit('users') + setInterval(() => { + const currentTime = (new Date().getTime()); + for (const pubkey of Object.keys(this.users)) { + const user = this.users[pubkey]; + if (currentTime - user.lastSeen > 10 * 1000) { + user.online = false; + } + } + + this.events.emit('users') + }, 5000); + } + + public listenToTyping() { + this.typingObserver.pipe(throttle(val => interval(3000))).subscribe(() => { + this.status.sendJsonMessage(this.channelName, {type: "typing"}); + }) + + setInterval(() => { + const currentTime = (new Date().getTime()); + for (const pubkey of Object.keys(this.usersTyping)) { + const lastTyped = this.usersTyping[pubkey].lastTyped; + + if (currentTime - lastTyped > 3 * 1000 || currentTime < lastTyped) { + delete this.usersTyping[pubkey]; + } + } + + this.events.emit('usersTyping') + }, 500); + } + + public typingEvent() { + this.events.emit('typing') + } + + public sendMessage(msg: string) { + this.status.sendMessage(this.channelName, msg); + } + +} + +export default Channel; diff --git a/src/status-js-client/channelManager.ts b/src/status-js-client/channelManager.ts new file mode 100644 index 0000000..6747f65 --- /dev/null +++ b/src/status-js-client/channelManager.ts @@ -0,0 +1,92 @@ +const Events = require("events"); +import Users from "./users"; +import colors from "colors"; + +class ChannelManager { + public channels: any[]; + public events: any; + public allUsers: Users; + private currentChannel: number; + + constructor() { + this.channels = []; + this.events = new Events(); + this.currentChannel = 0; + this.allUsers = new Users(); + } + + public addChannel(channelName: string, type: string, extraData?: any) { + if (this.getChannel(channelName)) { + return; + } + + const channel = {name: channelName, pendingMessages: [], type, ...extraData}; + channel.users = new Users(); + this.channels.push(channel); + this.events.emit("update"); + } + + public getChannel(channelName: string) { + return this.channels.find((c) => c.name === channelName); + } + + public getCurrentChannel() { + return this.channels[this.currentChannel]; + } + + public addMessage(channelName: string, message: string, pubkey: string, username: string) { + const channel = this.getChannel(channelName); + if (channelName !== this.channels[this.currentChannel].name) { + channel.pendingMessages.push({pubkey, username, message}); + } else { + this.events.emit("newMessage", channelName, username, message); + } + const user = this.allUsers.addOrUpdateUserKey(pubkey, username); + channel.users.addUserOrUpdate(user); + + this.events.emit("update"); + } + + public dumpPendingMessages() { + const messages = this.channels[this.currentChannel].pendingMessages.slice(0); + this.channels[this.currentChannel].pendingMessages = []; + return messages; + } + + public switchChannelIndex(index: number) { + if (index < 0) { + return; + } + if (index >= this.channels.length) { + return; + } + this.currentChannel = index; + this.events.emit("update"); + this.events.emit("channelSwitch"); + } + + public getChannelList() { + return this.channels.map((c) => { + const prefix = c.type === "channel" ? "#" : ""; + + if (c.name === this.channels[this.currentChannel].name) { + return colors.green(`${prefix}${c.name}`); + } + if (c.pendingMessages.length === 0) { + return `${prefix}${c.name}`; + } + return `${prefix}${c.name} (${c.pendingMessages.length})`; + }); + } + + public getUsersInCurrentChannel() { + const channel = this.getCurrentChannel(); + const userKeys = channel.users.getUsers(); + const users = userKeys.map((pubkey: string) => { + return this.allUsers.users[pubkey]; + }); + return users; + } +} + +export default ChannelManager; diff --git a/src/status-js-client/index.ts b/src/status-js-client/index.ts new file mode 100644 index 0000000..3e4236f --- /dev/null +++ b/src/status-js-client/index.ts @@ -0,0 +1,57 @@ +import StatusJS from "status-js-api"; +import Channel from './channel'; + +interface ConfigOptions { + pingFrequency: number; +}; + +interface WhisperProvider { + send: Function; + sendAasync: Function; +} + +class StatusJSClient { + private status: any; + private userPubKey: string; + private userName: string; + private channels: any; + + constructor(options?: ConfigOptions) { + this.status = new StatusJS(); + this.userPubKey = ""; + this.userName = ""; + this.channels = {}; + } + + public async connectToNode(url: string) { + await this.status.connect(url); + this.init(); + } + + public connectToProvider(provider: WhisperProvider) { + this.init(); + } + + private async init() { + this.userPubKey = await this.status.getPublicKey(); + this.userName = await this.status.getUserName(); + } + + public async getPublicKey() { + const userPubKey = await this.status.getPublicKey(); + return userPubKey; + } + + public joinChannel(channelName: string) { + const channel = new Channel(channelName, this.status); + this.channels[channelName] = channel; + return new Promise((resolve: any, reject?: any) => { + channel.joinChannel(() => { + resolve(channel); + }); + }) + } + +} + +export default StatusJSClient; diff --git a/src/user.ts b/src/status-js-client/user.ts similarity index 100% rename from src/user.ts rename to src/status-js-client/user.ts diff --git a/src/users.ts b/src/status-js-client/users.ts similarity index 100% rename from src/users.ts rename to src/status-js-client/users.ts diff --git a/src/ui.ts b/src/ui.ts index 3f9e5ea..5ed291e 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -70,13 +70,12 @@ class UI { } public availableChannels(channels: string[]) { - this.channels.setContent(channels.map((c, i) => `(${i}) ${c}`).join("\n")); + this.channels.setContent(channels.map((c, i) => `(${i+1}) ${c}`).join("\n")); this.screen.render(); } - // TODO: to remove, might not be used anymore private setStatus(status: string) { - this.operations.setContent(status); + this.consoleState.setContent(status); this.screen.render(); } diff --git a/yarn.lock b/yarn.lock index a94f732..a1f0a95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -858,12 +858,6 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - dependencies: - lodash "^4.17.10" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2794,6 +2788,10 @@ lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.5: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -3686,7 +3684,7 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" -rxjs@^6.1.0: +rxjs@^6.1.0, rxjs@^6.3.3: version "6.3.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.3.3.tgz#3c6a7fa420e844a81390fb1158a9ec614f4bad55" dependencies: