ts integrated and working preliminarily
This commit is contained in:
parent
29a31ab60d
commit
d05f08630a
@ -1,15 +1,21 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@types/dompurify": "^2.0.0",
|
||||
"@types/markdown-it": "^0.0.9",
|
||||
"@types/sjcl": "^1.0.28",
|
||||
"@types/sprintf-js": "^1.1.2",
|
||||
"alertifyjs": "^1.12.0",
|
||||
"chart.js": "^2.9.3",
|
||||
"dompurify": "^2.0.7",
|
||||
"fuse.js": "^3.4.6",
|
||||
"global": "^4.4.0",
|
||||
"handlebars": "^4.5.3",
|
||||
"loglevel": "^1.6.6",
|
||||
"markdown-it": "^10.0.0",
|
||||
"sjcl": "^1.0.8",
|
||||
"tsify": "^4.0.1"
|
||||
"sprintf-js": "^1.1.2",
|
||||
"tsify": "^4.0.1",
|
||||
"typescript": "^3.7.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"browserify": "^16.5.0",
|
||||
@ -28,6 +34,7 @@
|
||||
"handlebars": "global:Handlebars",
|
||||
"dompurify": "global:DOMPurify",
|
||||
"fuse.js": "global:Fuse",
|
||||
"sjcl": "global:sjcl"
|
||||
"sjcl": "global:sjcl",
|
||||
"sprintf-js": "global:sprintf"
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,14 @@ import { Model } from "../model/AbstractModel";
|
||||
import { View } from "../view/AbstractView";
|
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import { UserView } from "../view/UserView";
|
||||
import { UserModel } from "../model/UserModel";
|
||||
|
||||
export class UserController {
|
||||
private _model: Model;
|
||||
private _model: UserModel;
|
||||
private _view: UserView;
|
||||
|
||||
|
||||
constructor(model: Model, view: UserView) {
|
||||
constructor(model: UserModel, view: UserView) {
|
||||
this._model = model;
|
||||
this._view = view;
|
||||
}
|
||||
@ -41,5 +42,9 @@ export class UserController {
|
||||
this.eventHandler(activeUsersMock);
|
||||
}
|
||||
|
||||
public getActiveUsers(): void {
|
||||
this._model.getActiveUsers();
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -40,7 +40,7 @@ userModel.attach(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');
|
||||
// @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);
|
||||
chatModel.attach(chatView);
|
||||
const chatController = new ChatController(chatModel, chatView);
|
||||
|
||||
|
||||
@ -58,6 +59,8 @@ function someFunc(vm: ActiveUserViewModel): void {
|
||||
// logger.info(vm)
|
||||
}
|
||||
|
||||
userController.getActiveUsers();
|
||||
|
||||
log.info("test");
|
||||
// someFunc(activeUserViewModelMock);
|
||||
|
||||
@ -70,7 +73,7 @@ JsonAPI.ACTIVE_USERS_GET + 'aef';
|
||||
|
||||
const encryptionService: EncryptionService = new SJCLEncryptionService();
|
||||
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() {
|
||||
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>';
|
||||
|
@ -46,10 +46,10 @@ export class ChatModel implements Subject {
|
||||
/**
|
||||
* Trigger an update in each subscriber.
|
||||
*/
|
||||
public notify(): void {
|
||||
public notify(userName: string): void {
|
||||
console.log('Subject: Notifying 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();
|
||||
console.log(`Subject: My state has just changed`);
|
||||
console.log(chatMessageList);
|
||||
this.notify();
|
||||
this.notify("some user");
|
||||
}
|
||||
|
||||
public getmessages(userName: string, passphrase: string, lastMessageTime: string | null): void {
|
||||
const cVMs = ChatModelHelper.getMessages(userName, passphrase, lastMessageTime, this);
|
||||
public async getmessages(userName: string, passphrase: string, lastMessageTime: string | null): Promise<ChatMessageViewModel[]> {
|
||||
const cVMs = await ChatModelHelper.getMessages(userName, passphrase, lastMessageTime, this);
|
||||
if (cVMs != null) {
|
||||
log.info('Subject: My state has just changed')
|
||||
log.debug(cVMs);
|
||||
this._messagesMap.set(userName, cVMs);
|
||||
this.notify();
|
||||
this.notify(userName);
|
||||
}
|
||||
else {
|
||||
log.error('Messages were null');
|
||||
}
|
||||
|
||||
return cVMs;
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,39 +10,44 @@ import { fetchErrorHandler } from "./FetchErrorHandler";
|
||||
|
||||
import { EncryptionService } from "../service/EncryptionService";
|
||||
import { SJCLEncryptionService } from "../service/SJCLEncryptionService";
|
||||
import { ChatModel } from "./ChatModel";
|
||||
|
||||
import { ChatModel } from "./ChatModel"
|
||||
export class ChatModelHelper {
|
||||
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) {
|
||||
case null: {
|
||||
this.getAllMessagesAjax(userName)
|
||||
.then((data: ChatMessageDTO[]) => {
|
||||
log.debug(`Subject: received all messages`);
|
||||
// let userNames = data.map(ChatMessageViewModel => ChatMessageViewModel.fromUser)
|
||||
// let sumt = data.map(chatMessageViewModel => { return this.encryptionService.decrypt(passphrase, chatMessageViewModel.messageCipher) });
|
||||
// this.getAllMessagesAjax(userName)
|
||||
// .then((data: ChatMessageDTO[]) => {
|
||||
// log.debug(`Subject: received all messages`);
|
||||
// // let userNames = data.map(ChatMessageViewModel => ChatMessageViewModel.fromUser)
|
||||
// // 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));
|
||||
// chatModel.setUserMessages(userName, chatMessageVMs);
|
||||
// chatModel.notify();
|
||||
})
|
||||
break;
|
||||
|
||||
}
|
||||
default: {
|
||||
this.getNewMessagesAjax(userName, lastMessageTime)
|
||||
.then((data: ChatMessageDTO[]) => {
|
||||
log.debug(`Subject: received new messages`);
|
||||
// this.getNewMessagesAjax(userName, lastMessageTime)
|
||||
// .then((data: ChatMessageDTO[]) => {
|
||||
// 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));
|
||||
// chatModel.setUserMessages(userName, chatMessageVMs);
|
||||
// this.state = data;
|
||||
// chatModel.notify();
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
// return null;
|
||||
}
|
||||
|
||||
private static toChatMessageVM(chatMessageDTO: ChatMessageDTO, passphrase: string): ChatMessageViewModel {
|
||||
@ -55,13 +60,13 @@ export class ChatModelHelper {
|
||||
}
|
||||
|
||||
private static async getAllMessagesAjax(toUser: string): Promise<any> {
|
||||
let headers = new Headers();
|
||||
const headers = new Headers();
|
||||
if (JsonAPI.authToken == null) {
|
||||
log.error("authToken null");
|
||||
return;
|
||||
};
|
||||
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',
|
||||
headers: headers
|
||||
});
|
||||
@ -69,18 +74,18 @@ export class ChatModelHelper {
|
||||
if (fetchErrorHandler(response.clone())) {
|
||||
return null;
|
||||
}
|
||||
let data: Promise<any> = await response.json();
|
||||
const data: Promise<any> = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
private static async getNewMessagesAjax(toUser: string, lastMessageTimeStamp: string): Promise<any> {
|
||||
let headers = new Headers();
|
||||
const headers = new Headers();
|
||||
if (JsonAPI.authToken == null) {
|
||||
log.error("authToken null");
|
||||
return;
|
||||
};
|
||||
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',
|
||||
headers: headers
|
||||
});
|
||||
@ -88,7 +93,7 @@ export class ChatModelHelper {
|
||||
if (fetchErrorHandler(response.clone())) {
|
||||
return null;
|
||||
}
|
||||
let data: Promise<any> = await response.json();
|
||||
const data: Promise<any> = await response.json();
|
||||
return data;
|
||||
}
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
import log = require("loglevel");
|
||||
import { sprintf } from "sprintf-js";
|
||||
// import sprintf = require('sprintf-js').sprintf;
|
||||
|
||||
export function fetchErrorHandler(response: Response) {
|
||||
// alertify.success('Current position : ' + alertify.get('notifier', 'position'));
|
||||
if (!response.ok) {
|
||||
@ -6,13 +10,16 @@ export function fetchErrorHandler(response: Response) {
|
||||
// throw new Error(response.statusText);
|
||||
// window.alert(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;
|
||||
}).then(json => {
|
||||
// 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
|
||||
// window.alert(sprintf('Error: %s (Http code %s)', json, 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;
|
||||
});
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import { fetchErrorHandler } from "./FetchErrorHandler";
|
||||
import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import { JsonAPI } from "../singleton/JsonAPI";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
import * as log from "loglevel";
|
||||
|
||||
export class UserModel implements Subject {
|
||||
/**
|
||||
@ -53,7 +54,8 @@ export class UserModel implements Subject {
|
||||
* getActiveUsers
|
||||
*/
|
||||
public getActiveUsers(): void {
|
||||
this.getActiveUsersAjax("", JsonAPI.ACTIVE_USERS_GET)
|
||||
if(JsonAPI.authToken!= null){
|
||||
this.getActiveUsersAjax(JsonAPI.authToken, JsonAPI.ACTIVE_USERS_GET)
|
||||
.then(data => {
|
||||
// // activeUsers = data;
|
||||
// sessionStorage.setItem('activeUsers', JSON.stringify(data));
|
||||
@ -63,6 +65,10 @@ export class UserModel implements Subject {
|
||||
this.notify();
|
||||
})
|
||||
}
|
||||
else {
|
||||
log.error('Auth token is null');
|
||||
}
|
||||
}
|
||||
|
||||
async getActiveUsersAjax(authToken2: string, URL: string): Promise<any> {
|
||||
let headers = new Headers();
|
||||
|
@ -11,7 +11,7 @@ export interface Subject {
|
||||
detach(observer: Observer): void;
|
||||
|
||||
// Notify all observers about an event.
|
||||
notify(): void;
|
||||
notify(param: any | null): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,7 @@ export class SJCLEncryptionService implements EncryptionService {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -3,6 +3,6 @@ export namespace JsonAPI {
|
||||
export let userName: string | null = localStorage.getItem('userName');
|
||||
export let authToken: string | null = localStorage.getItem('authToken');
|
||||
export const ACTIVE_USERS_GET = `/api/chat/get/active-users`;
|
||||
export const CHAT_MESSAGES_GET = ``;
|
||||
export const CHAT_MESSAGES_GET = `/api/chat/get/messages`;
|
||||
|
||||
}
|
@ -8,6 +8,9 @@ import { ActiveUserViewModel } from "../viewmodel/ActiveUserViewModel";
|
||||
import { ChatModel } from "../model/ChatModel";
|
||||
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
|
||||
import * as log from 'loglevel';
|
||||
import * as DOMPurify from 'dompurify';
|
||||
import markdownit = require('markdown-it');
|
||||
var md = new markdownit();
|
||||
|
||||
export class ChatView implements Observer {
|
||||
private readonly _model: ChatModel;
|
||||
@ -22,10 +25,16 @@ export class ChatView implements Observer {
|
||||
|
||||
|
||||
update(data: ChatMessageViewModel[]): void {
|
||||
log.info('ChatView: updating view');
|
||||
let html: string = "";
|
||||
data.forEach((vm: ChatMessageViewModel) => {
|
||||
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;
|
||||
log.debug(this._element.innerHTML);
|
||||
}
|
||||
|
@ -2,6 +2,13 @@
|
||||
# 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@*":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-2.1.0.tgz#ea3dd64c4805597311790b61e872cbd1ed2cd806"
|
||||
@ -14,6 +21,21 @@
|
||||
dependencies:
|
||||
"@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":
|
||||
version "1.8.5"
|
||||
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"
|
||||
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:
|
||||
version "1.2.0"
|
||||
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"
|
||||
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:
|
||||
version "1.1.0"
|
||||
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"
|
||||
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:
|
||||
version "1.0.1"
|
||||
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:
|
||||
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:
|
||||
version "1.0.3"
|
||||
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"
|
||||
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:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
|
||||
|
Loading…
Reference in New Issue
Block a user