ts integrated and working preliminarily

This commit is contained in:
nova 2019-12-07 11:30:51 +05:30
parent 29a31ab60d
commit d05f08630a
12 changed files with 143 additions and 46 deletions

View File

@ -1,15 +1,21 @@
{ {
"dependencies": { "dependencies": {
"@types/dompurify": "^2.0.0",
"@types/markdown-it": "^0.0.9", "@types/markdown-it": "^0.0.9",
"@types/sjcl": "^1.0.28",
"@types/sprintf-js": "^1.1.2",
"alertifyjs": "^1.12.0", "alertifyjs": "^1.12.0",
"chart.js": "^2.9.3", "chart.js": "^2.9.3",
"dompurify": "^2.0.7", "dompurify": "^2.0.7",
"fuse.js": "^3.4.6", "fuse.js": "^3.4.6",
"global": "^4.4.0",
"handlebars": "^4.5.3", "handlebars": "^4.5.3",
"loglevel": "^1.6.6", "loglevel": "^1.6.6",
"markdown-it": "^10.0.0", "markdown-it": "^10.0.0",
"sjcl": "^1.0.8", "sjcl": "^1.0.8",
"tsify": "^4.0.1" "sprintf-js": "^1.1.2",
"tsify": "^4.0.1",
"typescript": "^3.7.3"
}, },
"devDependencies": { "devDependencies": {
"browserify": "^16.5.0", "browserify": "^16.5.0",
@ -28,6 +34,7 @@
"handlebars": "global:Handlebars", "handlebars": "global:Handlebars",
"dompurify": "global:DOMPurify", "dompurify": "global:DOMPurify",
"fuse.js": "global:Fuse", "fuse.js": "global:Fuse",
"sjcl": "global:sjcl" "sjcl": "global:sjcl",
"sprintf-js": "global:sprintf"
} }
} }

View File

@ -7,13 +7,14 @@ import { Model } from "../model/AbstractModel";
import { View } from "../view/AbstractView"; import { View } from "../view/AbstractView";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import { UserView } from "../view/UserView"; import { UserView } from "../view/UserView";
import { UserModel } from "../model/UserModel";
export class UserController { export class UserController {
private _model: Model; private _model: UserModel;
private _view: UserView; private _view: UserView;
constructor(model: Model, view: UserView) { constructor(model: UserModel, view: UserView) {
this._model = model; this._model = model;
this._view = view; this._view = view;
} }
@ -41,5 +42,9 @@ export class UserController {
this.eventHandler(activeUsersMock); this.eventHandler(activeUsersMock);
} }
public getActiveUsers(): void {
this._model.getActiveUsers();
}
} }

View File

@ -40,7 +40,7 @@ userModel.attach(userView);
const userController = new UserController(userModel, userView); const userController = new UserController(userModel, userView);
userController.test(); // userController.test();
@ -50,6 +50,7 @@ log.info("hello");
const chatArea = document.getElementById('chat-area-new'); const chatArea = document.getElementById('chat-area-new');
// @ts-ignore: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'. // @ts-ignore: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.
const chatView = new ChatView(chatModel, chatArea); const chatView = new ChatView(chatModel, chatArea);
chatModel.attach(chatView);
const chatController = new ChatController(chatModel, chatView); const chatController = new ChatController(chatModel, chatView);
@ -58,6 +59,8 @@ function someFunc(vm: ActiveUserViewModel): void {
// logger.info(vm) // logger.info(vm)
} }
userController.getActiveUsers();
log.info("test"); log.info("test");
// someFunc(activeUserViewModelMock); // someFunc(activeUserViewModelMock);
@ -70,7 +73,7 @@ JsonAPI.ACTIVE_USERS_GET + 'aef';
const encryptionService: EncryptionService = new SJCLEncryptionService(); const encryptionService: EncryptionService = new SJCLEncryptionService();
let ct = encryptionService.encrypt("password","data"); let ct = encryptionService.encrypt("password","data");
console.log(encryptionService.decrypt("password", ct)); console.log(encryptionService.decrypt("password", JSON.parse(ct as string)));
Handlebars.registerHelper('avatar', function() { Handlebars.registerHelper('avatar', function() {
return '<div class="img_cont_msg"> <img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img_msg"> </div>'; return '<div class="img_cont_msg"> <img src="https://static.turbosquid.com/Preview/001292/481/WV/_D.jpg" class="rounded-circle user_img_msg"> </div>';

View File

@ -46,10 +46,10 @@ export class ChatModel implements Subject {
/** /**
* Trigger an update in each subscriber. * Trigger an update in each subscriber.
*/ */
public notify(): void { public notify(userName: string): void {
console.log('Subject: Notifying observers...'); console.log('Subject: Notifying observers...');
for (const observer of this._observers) { for (const observer of this._observers) {
observer.update(this.state); observer.update(this._messagesMap.get(userName));
} }
} }
@ -58,20 +58,22 @@ export class ChatModel implements Subject {
this.helperMethod(); this.helperMethod();
console.log(`Subject: My state has just changed`); console.log(`Subject: My state has just changed`);
console.log(chatMessageList); console.log(chatMessageList);
this.notify(); this.notify("some user");
} }
public getmessages(userName: string, passphrase: string, lastMessageTime: string | null): void { public async getmessages(userName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
const cVMs = ChatModelHelper.getMessages(userName, passphrase, lastMessageTime, this); const cVMs = await ChatModelHelper.getMessages(userName, passphrase, lastMessageTime, this);
if (cVMs != null) { if (cVMs != null) {
log.info('Subject: My state has just changed') log.info('Subject: My state has just changed')
log.debug(cVMs); log.debug(cVMs);
this._messagesMap.set(userName, cVMs); this._messagesMap.set(userName, cVMs);
this.notify(); this.notify(userName);
} }
else { else {
log.error('Messages were null'); log.error('Messages were null');
} }
return cVMs;
} }

View File

@ -10,39 +10,44 @@ import { fetchErrorHandler } from "./FetchErrorHandler";
import { EncryptionService } from "../service/EncryptionService"; import { EncryptionService } from "../service/EncryptionService";
import { SJCLEncryptionService } from "../service/SJCLEncryptionService"; import { SJCLEncryptionService } from "../service/SJCLEncryptionService";
import { ChatModel } from "./ChatModel"; import { ChatModel } from "./ChatModel"
export class ChatModelHelper { export class ChatModelHelper {
private static readonly encryptionService = new SJCLEncryptionService(); private static readonly encryptionService = new SJCLEncryptionService();
public static getMessages(userName: string, passphrase: string, lastMessageTime: string | null, chatModel: ChatModel): ChatMessageViewModel[] | null { public static async getMessages(userName: string, passphrase: string, lastMessageTime: string | null, chatModel: ChatModel): Promise<ChatMessageViewModel[]> {
switch (lastMessageTime) { switch (lastMessageTime) {
case null: { case null: {
this.getAllMessagesAjax(userName) // this.getAllMessagesAjax(userName)
.then((data: ChatMessageDTO[]) => { // .then((data: ChatMessageDTO[]) => {
log.debug(`Subject: received all messages`); // log.debug(`Subject: received all messages`);
// let userNames = data.map(ChatMessageViewModel => ChatMessageViewModel.fromUser) // // let userNames = data.map(ChatMessageViewModel => ChatMessageViewModel.fromUser)
// let sumt = data.map(chatMessageViewModel => { return this.encryptionService.decrypt(passphrase, chatMessageViewModel.messageCipher) }); // // let sumt = data.map(chatMessageViewModel => { return this.encryptionService.decrypt(passphrase, chatMessageViewModel.messageCipher) });
// return data.map(vm => this.toChatMessageVM(vm, passphrase));
// // chatModel.setUserMessages(userName, chatMessageVMs);
// // chatModel.notify();
// })
// break;
const data: ChatMessageDTO[] = await this.getAllMessagesAjax(userName);
return data.map(vm => this.toChatMessageVM(vm, passphrase)); return data.map(vm => this.toChatMessageVM(vm, passphrase));
// chatModel.setUserMessages(userName, chatMessageVMs);
// chatModel.notify();
})
break;
} }
default: { default: {
this.getNewMessagesAjax(userName, lastMessageTime) // this.getNewMessagesAjax(userName, lastMessageTime)
.then((data: ChatMessageDTO[]) => { // .then((data: ChatMessageDTO[]) => {
log.debug(`Subject: received new messages`); // log.debug(`Subject: received new messages`);
// return data.map(vm => this.toChatMessageVM(vm, passphrase));
// // chatModel.setUserMessages(userName, chatMessageVMs);
// // this.state = data;
// // chatModel.notify();
// })
// break;
const data: ChatMessageDTO[] = await this.getNewMessagesAjax(userName, lastMessageTime);
return data.map(vm => this.toChatMessageVM(vm, passphrase)); return data.map(vm => this.toChatMessageVM(vm, passphrase));
// chatModel.setUserMessages(userName, chatMessageVMs);
// this.state = data;
// chatModel.notify();
})
break;
} }
} }
return null; // return null;
} }
private static toChatMessageVM(chatMessageDTO: ChatMessageDTO, passphrase: string): ChatMessageViewModel { private static toChatMessageVM(chatMessageDTO: ChatMessageDTO, passphrase: string): ChatMessageViewModel {
@ -55,13 +60,13 @@ export class ChatModelHelper {
} }
private static async getAllMessagesAjax(toUser: string): Promise<any> { private static async getAllMessagesAjax(toUser: string): Promise<any> {
let headers = new Headers(); const headers = new Headers();
if (JsonAPI.authToken == null) { if (JsonAPI.authToken == null) {
log.error("authToken null"); log.error("authToken null");
return; return;
}; };
headers.append('X-AUTH-TOKEN', JsonAPI.authToken); headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
let response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}`, { const response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}`, {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
@ -69,18 +74,18 @@ export class ChatModelHelper {
if (fetchErrorHandler(response.clone())) { if (fetchErrorHandler(response.clone())) {
return null; return null;
} }
let data: Promise<any> = await response.json(); const data: Promise<any> = await response.json();
return data; return data;
} }
private static async getNewMessagesAjax(toUser: string, lastMessageTimeStamp: string): Promise<any> { private static async getNewMessagesAjax(toUser: string, lastMessageTimeStamp: string): Promise<any> {
let headers = new Headers(); const headers = new Headers();
if (JsonAPI.authToken == null) { if (JsonAPI.authToken == null) {
log.error("authToken null"); log.error("authToken null");
return; return;
}; };
headers.append('X-AUTH-TOKEN', JsonAPI.authToken); headers.append('X-AUTH-TOKEN', JsonAPI.authToken);
let response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}/${lastMessageTimeStamp}`, { const response = await fetch(`${JsonAPI.CHAT_MESSAGES_GET}/${toUser}/${lastMessageTimeStamp}`, {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
@ -88,7 +93,7 @@ export class ChatModelHelper {
if (fetchErrorHandler(response.clone())) { if (fetchErrorHandler(response.clone())) {
return null; return null;
} }
let data: Promise<any> = await response.json(); const data: Promise<any> = await response.json();
return data; return data;
} }
} }

View File

@ -1,3 +1,7 @@
import log = require("loglevel");
import { sprintf } from "sprintf-js";
// import sprintf = require('sprintf-js').sprintf;
export function fetchErrorHandler(response: Response) { export function fetchErrorHandler(response: Response) {
// alertify.success('Current position : ' + alertify.get('notifier', 'position')); // alertify.success('Current position : ' + alertify.get('notifier', 'position'));
if (!response.ok) { if (!response.ok) {
@ -6,13 +10,16 @@ export function fetchErrorHandler(response: Response) {
// throw new Error(response.statusText); // throw new Error(response.statusText);
// window.alert(sprintf('Some error occured. Http code is %s', response.status)); // window.alert(sprintf('Some error occured. Http code is %s', response.status));
// alertify.error(sprintf('Some error occured. Http code is %s', response.status)); // alertify.error(sprintf('Some error occured. Http code is %s', response.status));
log.error(sprintf('Some error occured. Http code is %s', response.status));
log.error();
return true; return true;
}).then(json => { }).then(json => {
// the status was not ok but there is a json body // the status was not ok but there is a json body
// throw new Error(json.error.message); // example error message returned by a REST API // throw new Error(json.error.message); // example error message returned by a REST API
// window.alert(sprintf('Error: %s (Http code %s)', json, response.status)); // window.alert(sprintf('Error: %s (Http code %s)', json, response.status));
// alertify.error(sprintf('Some error occured. Http code is %s', response.status)); // alertify.error(sprintf('Some error occured. Http code is %s', response.status));
console.log(json); log.error(sprintf('Some error occured. Http code is %s', response.status));
log.error(json);
return true; return true;
}); });
} }

View File

@ -5,6 +5,7 @@ import { fetchErrorHandler } from "./FetchErrorHandler";
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel"; import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import { JsonAPI } from "../singleton/JsonAPI"; import { JsonAPI } from "../singleton/JsonAPI";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import * as log from "loglevel";
export class UserModel implements Subject { export class UserModel implements Subject {
/** /**
@ -53,7 +54,8 @@ export class UserModel implements Subject {
* getActiveUsers * getActiveUsers
*/ */
public getActiveUsers(): void { public getActiveUsers(): void {
this.getActiveUsersAjax("", JsonAPI.ACTIVE_USERS_GET) if(JsonAPI.authToken!= null){
this.getActiveUsersAjax(JsonAPI.authToken, JsonAPI.ACTIVE_USERS_GET)
.then(data => { .then(data => {
// // activeUsers = data; // // activeUsers = data;
// sessionStorage.setItem('activeUsers', JSON.stringify(data)); // sessionStorage.setItem('activeUsers', JSON.stringify(data));
@ -63,6 +65,10 @@ export class UserModel implements Subject {
this.notify(); this.notify();
}) })
} }
else {
log.error('Auth token is null');
}
}
async getActiveUsersAjax(authToken2: string, URL: string): Promise<any> { async getActiveUsersAjax(authToken2: string, URL: string): Promise<any> {
let headers = new Headers(); let headers = new Headers();

View File

@ -11,7 +11,7 @@ export interface Subject {
detach(observer: Observer): void; detach(observer: Observer): void;
// Notify all observers about an event. // Notify all observers about an event.
notify(): void; notify(param: any | null): void;
} }
/** /**

View File

@ -8,6 +8,7 @@ export class SJCLEncryptionService implements EncryptionService {
} }
public decrypt(passphrase: string, cipher: Object): Object { public decrypt(passphrase: string, cipher: Object): Object {
return sjcl.decrypt(passphrase, cipher as sjcl.SjclCipherEncrypted, undefined, undefined); // return sjcl.decrypt(passphrase, cipher as sjcl.SjclCipherEncrypted, undefined, undefined);
return sjcl.decrypt(passphrase, JSON.stringify(cipher), undefined, undefined);
} }
} }

View File

@ -3,6 +3,6 @@ export namespace JsonAPI {
export let userName: string | null = localStorage.getItem('userName'); export let userName: string | null = localStorage.getItem('userName');
export let authToken: string | null = localStorage.getItem('authToken'); export let authToken: string | null = localStorage.getItem('authToken');
export const ACTIVE_USERS_GET = `/api/chat/get/active-users`; export const ACTIVE_USERS_GET = `/api/chat/get/active-users`;
export const CHAT_MESSAGES_GET = ``; export const CHAT_MESSAGES_GET = `/api/chat/get/messages`;
} }

View File

@ -8,6 +8,9 @@ import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
import { ChatModel } from "../model/ChatModel"; import { ChatModel } from "../model/ChatModel";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel"; import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import * as log from 'loglevel'; import * as log from 'loglevel';
import * as DOMPurify from 'dompurify';
import markdownit = require('markdown-it');
var md = new markdownit();
export class ChatView implements Observer { export class ChatView implements Observer {
private readonly _model: ChatModel; private readonly _model: ChatModel;
@ -22,10 +25,16 @@ export class ChatView implements Observer {
update(data: ChatMessageViewModel[]): void { update(data: ChatMessageViewModel[]): void {
log.info('ChatView: updating view');
let html: string = ""; let html: string = "";
data.forEach((vm: ChatMessageViewModel) => { data.forEach((vm: ChatMessageViewModel) => {
html += this._messageSendTemplate(vm); html += this._messageSendTemplate(vm);
}); });
/** Very Important!!!
* Sanitizing HTML before displaying on webpage to prevent XSS attacks!!
*/
html = DOMPurify.sanitize(md.render(html));
this._element.innerHTML = html; this._element.innerHTML = html;
log.debug(this._element.innerHTML); log.debug(this._element.innerHTML);
} }

View File

@ -2,6 +2,13 @@
# yarn lockfile v1 # yarn lockfile v1
"@types/dompurify@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-2.0.0.tgz#9616caa5bf2569aea2e4889d4f929d968c081b40"
integrity sha512-g/ilp+Bo6Ljy60i5LnjkGw00X7EIoFjoPGlxqZhV8TJ9fWEzXheioU1O+U/UzCzUA7pUDy/JNMytTQDJctpUHg==
dependencies:
"@types/trusted-types" "*"
"@types/linkify-it@*": "@types/linkify-it@*":
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806" resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
@ -14,6 +21,21 @@
dependencies: dependencies:
"@types/linkify-it" "*" "@types/linkify-it" "*"
"@types/sjcl@^1.0.28":
version "1.0.28"
resolved "https://registry.yarnpkg.com/@types/sjcl/-/sjcl-1.0.28.tgz#4693eb6943e385e844a70fb25b4699db286c7214"
integrity sha1-RpPraUPjhehEpw+yW0aZ2yhschQ=
"@types/sprintf-js@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@types/sprintf-js/-/sprintf-js-1.1.2.tgz#a4fcb84c7344f39f70dc4eec0e1e7f10a48597a3"
integrity sha512-hkgzYF+qnIl8uTO8rmUSVSfQ8BIfMXC4yJAF4n8BE758YsKBZvFC4NumnAegj7KmylP0liEZNpb9RRGFMbFejA==
"@types/trusted-types@*":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-1.0.4.tgz#922d092c84a776a59acb0bd6785fd82b59b9bad5"
integrity sha512-6jtHrHpmiXOXoJ31Cg9R+iEVwuEKPf0XHwFUI93eEPXx492/J2JHyafkleKE2EYzZprayk9FSjTyK1GDqcwDng==
"@webassemblyjs/ast@1.8.5": "@webassemblyjs/ast@1.8.5":
version "1.8.5" version "1.8.5"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
@ -1042,6 +1064,11 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0" miller-rabin "^4.0.0"
randombytes "^2.0.0" randombytes "^2.0.0"
dom-walk@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
integrity sha1-ZyIm3HTI95mtNTB9+TaroRrNYBg=
domain-browser@^1.1.1, domain-browser@^1.2.0: domain-browser@^1.1.1, domain-browser@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
@ -1462,6 +1489,14 @@ global-prefix@^1.0.1:
is-windows "^1.0.1" is-windows "^1.0.1"
which "^1.2.14" which "^1.2.14"
global@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==
dependencies:
min-document "^2.19.0"
process "^0.11.10"
globo@~1.1.0: globo@~1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/globo/-/globo-1.1.0.tgz#0d26098955dea422eb2001b104898b0a101caaf3" resolved "https://registry.yarnpkg.com/globo/-/globo-1.1.0.tgz#0d26098955dea422eb2001b104898b0a101caaf3"
@ -2061,6 +2096,13 @@ mime@^1.3.6:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
min-document@^2.19.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=
dependencies:
dom-walk "^0.1.0"
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
@ -2980,6 +3022,11 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies: dependencies:
extend-shallow "^3.0.0" extend-shallow "^3.0.0"
sprintf-js@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
sprintf-js@~1.0.2: sprintf-js@~1.0.2:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@ -3302,6 +3349,11 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^3.7.3:
version "3.7.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69"
integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==
uc.micro@^1.0.1, uc.micro@^1.0.5: uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"