summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorDessalines <tyhou13@gmx.com>2019-03-22 18:42:57 -0700
committerDessalines <tyhou13@gmx.com>2019-03-22 18:42:57 -0700
commite570c70701e1c66dad12bef76821f6d94c717a02 (patch)
tree070d5ba322226acd5fe0d0a7757cb936462ab2ff /ui
parent816aa0b15f3766e340d8722f03e8b3a7633ab6fb (diff)
Adding login and Register
- Login and Register mostly working. - Starting to work on creating communities.
Diffstat (limited to 'ui')
-rw-r--r--ui/package.json6
-rw-r--r--ui/src/components/create-community.tsx90
-rw-r--r--ui/src/components/create-post.tsx57
-rw-r--r--ui/src/components/login.tsx57
-rw-r--r--ui/src/components/navbar.tsx54
-rw-r--r--ui/src/index.tsx7
-rw-r--r--ui/src/interfaces.ts24
-rw-r--r--ui/src/main.css5
-rw-r--r--ui/src/services.ts57
-rw-r--r--ui/src/services/UserService.ts51
-rw-r--r--ui/src/services/WebSocketService.ts37
-rw-r--r--ui/src/services/index.ts2
-rw-r--r--ui/src/utils.ts7
-rw-r--r--ui/yarn.lock24
14 files changed, 387 insertions, 91 deletions
diff --git a/ui/package.json b/ui/package.json
index ca4fa368..08443b14 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -15,11 +15,15 @@
},
"engineStrict": true,
"dependencies": {
+ "@types/js-cookie": "^2.2.1",
"classcat": "^1.1.3",
"dotenv": "^6.1.0",
"inferno": "^7.0.1",
"inferno-router": "^7.0.1",
- "moment": "^2.22.2"
+ "js-cookie": "^2.2.0",
+ "jwt-decode": "^2.2.0",
+ "moment": "^2.22.2",
+ "rxjs": "^6.4.0"
},
"devDependencies": {
"fuse-box": "3.1.3",
diff --git a/ui/src/components/create-community.tsx b/ui/src/components/create-community.tsx
new file mode 100644
index 00000000..dbacd18d
--- /dev/null
+++ b/ui/src/components/create-community.tsx
@@ -0,0 +1,90 @@
+import { Component, linkEvent } from 'inferno';
+import { Subscription } from "rxjs";
+import { retryWhen, delay, take } from 'rxjs/operators';
+import { CommunityForm, UserOperation } from '../interfaces';
+import { WebSocketService, UserService } from '../services';
+import { msgOp } from '../utils';
+
+interface State {
+ communityForm: CommunityForm;
+}
+
+let emptyState: State = {
+ communityForm: {
+ name: null,
+ }
+}
+
+export class CreateCommunity extends Component<any, State> {
+ private subscription: Subscription;
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = emptyState;
+
+ this.subscription = WebSocketService.Instance.subject
+ .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .subscribe(
+ (msg) => this.parseMessage(msg),
+ (err) => console.error(err),
+ );
+ }
+
+ componentWillUnmount() {
+ this.subscription.unsubscribe();
+ }
+
+ render() {
+ return (
+ <div class="container">
+ <div class="row">
+ <div class="col-12 col-lg-6 mb-4">
+ {this.communityForm()}
+ </div>
+ </div>
+ </div>
+ )
+ }
+
+ communityForm() {
+ return (
+ <div>
+ <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
+ <h3>Create Forum</h3>
+ <div class="form-group row">
+ <label class="col-sm-2 col-form-label">Name</label>
+ <div class="col-sm-10">
+ <input type="text" class="form-control" value={this.state.communityForm.name} onInput={linkEvent(this, this.handleCommunityNameChange)} required minLength={3} />
+ </div>
+ </div>
+ <div class="form-group row">
+ <div class="col-sm-10">
+ <button type="submit" class="btn btn-secondary">Create</button>
+ </div>
+ </div>
+ </form>
+ </div>
+ );
+ }
+
+ handleCreateCommunitySubmit(i: CreateCommunity, event) {
+ event.preventDefault();
+ WebSocketService.Instance.createCommunity(i.state.communityForm);
+ }
+
+ handleCommunityNameChange(i: CreateCommunity, event) {
+ i.state.communityForm.name = event.target.value;
+ i.setState(i.state);
+ }
+
+ parseMessage(msg: any) {
+ let op: UserOperation = msgOp(msg);
+ if (msg.error) {
+ alert(msg.error);
+ return;
+ } else {
+ }
+ }
+
+}
diff --git a/ui/src/components/create-post.tsx b/ui/src/components/create-post.tsx
new file mode 100644
index 00000000..bb6e60e2
--- /dev/null
+++ b/ui/src/components/create-post.tsx
@@ -0,0 +1,57 @@
+import { Component, linkEvent } from 'inferno';
+
+import { LoginForm, PostForm, UserOperation } from '../interfaces';
+import { WebSocketService, UserService } from '../services';
+import { msgOp } from '../utils';
+
+interface State {
+ postForm: PostForm;
+}
+
+let emptyState: State = {
+ postForm: {
+ name: null,
+ url: null,
+ attributed_to: null
+ }
+}
+
+export class CreatePost extends Component<any, State> {
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = emptyState;
+
+ WebSocketService.Instance.subject.subscribe(
+ (msg) => this.parseMessage(msg),
+ (err) => console.error(err),
+ () => console.log('complete')
+ );
+ }
+
+
+ render() {
+ return (
+ <div class="container">
+ <div class="row">
+ <div class="col-12 col-lg-6 mb-4">
+ create post
+ {/* {this.postForm()} */}
+ </div>
+ </div>
+ </div>
+ )
+ }
+
+ parseMessage(msg: any) {
+ console.log(msg);
+ let op: UserOperation = msgOp(msg);
+ if (msg.error) {
+ alert(msg.error);
+ return;
+ } else {
+ }
+ }
+
+}
diff --git a/ui/src/components/login.tsx b/ui/src/components/login.tsx
index fd6f5045..372b1557 100644
--- a/ui/src/components/login.tsx
+++ b/ui/src/components/login.tsx
@@ -1,7 +1,9 @@
import { Component, linkEvent } from 'inferno';
-
-import { LoginForm, RegisterForm } from '../interfaces';
-import { WebSocketService } from '../services';
+import { Subscription } from "rxjs";
+import { retryWhen, delay, take } from 'rxjs/operators';
+import { LoginForm, RegisterForm, UserOperation } from '../interfaces';
+import { WebSocketService, UserService } from '../services';
+import { msgOp } from '../utils';
interface State {
loginForm: LoginForm;
@@ -10,24 +12,36 @@ interface State {
let emptyState: State = {
loginForm: {
- username: null,
- password: null
+ username_or_email: undefined,
+ password: undefined
},
registerForm: {
- username: null,
- password: null,
- password_verify: null
+ username: undefined,
+ password: undefined,
+ password_verify: undefined
}
}
export class Login extends Component<any, State> {
+ private subscription: Subscription;
constructor(props, context) {
super(props, context);
this.state = emptyState;
+ this.subscription = WebSocketService.Instance.subject
+ .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
+ .subscribe(
+ (msg) => this.parseMessage(msg),
+ (err) => console.error(err),
+ );
+ }
+
+ componentWillUnmount() {
+ this.subscription.unsubscribe();
}
+
render() {
return (
<div class="container">
@@ -51,7 +65,7 @@ export class Login extends Component<any, State> {
<div class="form-group row">
<label class="col-sm-2 col-form-label">Email or Username</label>
<div class="col-sm-10">
- <input type="text" class="form-control" value={this.state.loginForm.username} onInput={linkEvent(this, this.handleLoginUsernameChange)} required minLength={3} />
+ <input type="text" class="form-control" value={this.state.loginForm.username_or_email} onInput={linkEvent(this, this.handleLoginUsernameChange)} required minLength={3} />
</div>
</div>
<div class="form-group row">
@@ -108,38 +122,55 @@ export class Login extends Component<any, State> {
}
handleLoginSubmit(i: Login, event) {
- console.log(i.state);
event.preventDefault();
WebSocketService.Instance.login(i.state.loginForm);
}
handleLoginUsernameChange(i: Login, event) {
- i.state.loginForm.username = event.target.value;
+ i.state.loginForm.username_or_email = event.target.value;
+ i.setState(i.state);
}
handleLoginPasswordChange(i: Login, event) {
i.state.loginForm.password = event.target.value;
+ i.setState(i.state);
}
handleRegisterSubmit(i: Login, event) {
- console.log(i.state);
event.preventDefault();
WebSocketService.Instance.register(i.state.registerForm);
}
handleRegisterUsernameChange(i: Login, event) {
i.state.registerForm.username = event.target.value;
+ i.setState(i.state);
}
handleRegisterEmailChange(i: Login, event) {
i.state.registerForm.email = event.target.value;
+ i.setState(i.state);
}
handleRegisterPasswordChange(i: Login, event) {
i.state.registerForm.password = event.target.value;
+ i.setState(i.state);
}
-
+
handleRegisterPasswordVerifyChange(i: Login, event) {
i.state.registerForm.password_verify = event.target.value;
+ i.setState(i.state);
+ }
+
+ parseMessage(msg: any) {
+ let op: UserOperation = msgOp(msg);
+ if (msg.error) {
+ alert(msg.error);
+ return;
+ } else {
+ if (op == UserOperation.Register || op == UserOperation.Login) {
+ UserService.Instance.login(msg.jwt);
+ this.props.history.push('/');
+ }
+ }
}
}
diff --git a/ui/src/components/navbar.tsx b/ui/src/components/navbar.tsx
index 86d5d1d2..4cf6d6d2 100644
--- a/ui/src/components/navbar.tsx
+++ b/ui/src/components/navbar.tsx
@@ -1,38 +1,62 @@
import { Component, linkEvent } from 'inferno';
import { Link } from 'inferno-router';
import { repoUrl } from '../utils';
+import { UserService } from '../services';
export class Navbar extends Component<any, any> {
constructor(props, context) {
super(props, context);
+ this.state = {isLoggedIn: UserService.Instance.loggedIn};
+
+ // Subscribe to user changes
+ UserService.Instance.sub.subscribe(user => {
+ let loggedIn: boolean = user !== null;
+ this.setState({isLoggedIn: loggedIn});
+ });
}
render() {
return (
- <div class="sticky-top">{this.navbar()}</div>
+ <div>{this.navbar()}</div>
)
}
// TODO class active corresponding to current page
+ // TODO toggle css collapse
navbar() {
return (
- <nav class="navbar navbar-light bg-light p-0 px-3 shadow">
- <a class="navbar-brand mx-1" href="#">
- rrf
- </a>
- <ul class="navbar-nav mr-auto">
- <li class="nav-item">
- <a class="nav-item nav-link" href={repoUrl}>github</a>
- </li>
- </ul>
- <ul class="navbar-nav ml-auto mr-2">
- <li class="nav-item">
- <Link class="nav-item nav-link" to="/login">Login</Link>
- </li>
- </ul>
+ <nav class="navbar navbar-expand-sm navbar-light bg-light p-0 px-3 shadow">
+ <a class="navbar-brand" href="#">rrf</a>
+ <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
+ <span class="navbar-toggler-icon"></span>
+ </button>
+ <div class="collapse navbar-collapse">
+ <ul class="navbar-nav mr-auto">
+ <li class="nav-item">
+ <a class="nav-link" href={repoUrl}>github</a>
+ </li>
+ <li class="nav-item">
+ <Link class="nav-link" to="/create_post">Create Post</Link>
+ </li>
+ <li class="nav-item">
+ <Link class="nav-link" to="/create_community">Create Forum</Link>
+ </li>
+ </ul>
+ <ul class="navbar-nav ml-auto mr-2">
+ <li class="nav-item">
+ {this.state.isLoggedIn ?
+ <a role="button" class="nav-link pointer" onClick={ linkEvent(this, this.handleLogoutClick) }>Logout</a> :
+ <Link class="nav-link" to="/login">Login</Link>
+ }
+ </li>
+ </ul>
+ </div>
</nav>
);
}
+ handleLogoutClick(i: Navbar, event) {
+ UserService.Instance.logout();
+ }
}
diff --git a/ui/src/index.tsx b/ui/src/index.tsx
index 36e5681d..1eb3a7d5 100644
--- a/ui/src/index.tsx
+++ b/ui/src/index.tsx
@@ -4,10 +4,12 @@ import { HashRouter, Route, Switch } from 'inferno-router';
import { Navbar } from './components/navbar';
import { Home } from './components/home';
import { Login } from './components/login';
+import { CreatePost } from './components/create-post';
+import { CreateCommunity } from './components/create-community';
import './main.css';
-import { WebSocketService } from './services';
+import { WebSocketService, UserService } from './services';
const container = document.getElementById('app');
@@ -16,6 +18,7 @@ class Index extends Component<any, any> {
constructor(props, context) {
super(props, context);
WebSocketService.Instance;
+ UserService.Instance;
}
render() {
@@ -26,6 +29,8 @@ class Index extends Component<any, any> {
<Switch>
<Route exact path="/" component={Home} />
<Route path={`/login`} component={Login} />
+ <Route path={`/create_post`} component={CreatePost} />
+ <Route path={`/create_community`} component={CreateCommunity} />
{/*
<Route path={`/search/:type_/:q/:page`} component={Search} />
<Route path={`/submit`} component={Submit} />
diff --git a/ui/src/interfaces.ts b/ui/src/interfaces.ts
index c1550cc1..da5c415b 100644
--- a/ui/src/interfaces.ts
+++ b/ui/src/interfaces.ts
@@ -1,7 +1,17 @@
-export interface LoginForm {
+export enum UserOperation {
+ Login, Register, CreateCommunity
+}
+
+export interface User {
+ id: number
username: string;
+}
+
+export interface LoginForm {
+ username_or_email: string;
password: string;
}
+
export interface RegisterForm {
username: string;
email?: string;
@@ -9,6 +19,14 @@ export interface RegisterForm {
password_verify: string;
}
-export enum UserOperation {
- Login, Register
+export interface CommunityForm {
+ name: string;
+ updated?: number
+}
+
+export interface PostForm {
+ name: string;
+ url: string;
+ attributed_to: string;
+ updated?: number
}
diff --git a/ui/src/main.css b/ui/src/main.css
index e69de29b..77e093e8 100644
--- a/ui/src/main.css
+++ b/ui/src/main.css
@@ -0,0 +1,5 @@
+
+
+.pointer {
+ cursor: pointer;
+}
diff --git a/ui/src/services.ts b/ui/src/services.ts
deleted file mode 100644
index b9536aed..00000000
--- a/ui/src/services.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { wsUri } from './env';
-import { LoginForm, RegisterForm, UserOperation } from './interfaces';
-
-export class WebSocketService {
- private static _instance: WebSocketService;
- private _ws;
- private conn: WebSocket;
-
- private constructor() {
- console.log("Creating WSS");
- this.connect();
- console.log(wsUri);
- }
-
- public static get Instance(){
- return this._instance || (this._instance = new this());
- }
-
- private connect() {
- this.disconnect();
- this.conn = new WebSocket(wsUri);
- console.log('Connecting...');
- this.conn.onopen = (() => {
- console.log('Connected.');
- });
- this.conn.onmessage = (e => {
- console.log('Received: ' + e.data);
- });
- this.conn.onclose = (() => {
- console.log('Disconnected.');
- this.conn = null;
- });
- }
- private disconnect() {
- if (this.conn != null) {
- console.log('Disconnecting...');
- this.conn.close();
- this.conn = null;
- }
- }
-
- public login(loginForm: LoginForm) {
- this.conn.send(this.wsSendWrapper(UserOperation.Login, loginForm));
- }
-
- public register(registerForm: RegisterForm) {
- this.conn.send(this.wsSendWrapper(UserOperation.Register, registerForm));
- }
-
- private wsSendWrapper(op: UserOperation, data: any): string {
- let send = { op: UserOperation[op], data: data };
- console.log(send);
- return JSON.stringify(send);
- }
-
-
-}
diff --git a/ui/src/services/UserService.ts b/ui/src/services/UserService.ts
new file mode 100644
index 00000000..af0d1d15
--- /dev/null
+++ b/ui/src/services/UserService.ts
@@ -0,0 +1,51 @@
+import * as Cookies from 'js-cookie';
+import { User } from '../interfaces';
+import * as jwt_decode from 'jwt-decode';
+import { Subject } from 'rxjs';
+
+export class UserService {
+ private static _instance: UserService;
+ private user: User;
+ public sub: Subject<User> = new Subject<User>();
+
+ private constructor() {
+ let jwt = Cookies.get("jwt");
+ if (jwt) {
+ this.setUser(jwt);
+ } else {
+ console.log('No JWT cookie found.');
+ }
+
+ }
+
+ public login(jwt: string) {
+ Cookies.set("jwt", jwt);
+ console.log("jwt cookie set");
+ this.setUser(jwt);
+ }
+
+ public logout() {
+ this.user = null;
+ Cookies.remove("jwt");
+ console.log("Logged out.");
+ this.sub.next(null);
+ }
+
+ public get loggedIn(): boolean {
+ return this.user !== undefined;
+ }
+
+ public get auth(): string {
+ return Cookies.get("jwt");
+ }
+
+ private setUser(jwt: string) {
+ this.user = jwt_decode(jwt);
+ this.sub.next(this.user);
+ console.log(this.user.username);
+ }
+
+ public static get Instance(){
+ return this._instance || (this._instance = new this());
+ }
+}
diff --git a/ui/src/services/WebSocketService.ts b/ui/src/services/WebSocketService.ts
new file mode 100644
index 00000000..1882b125
--- /dev/null
+++ b/ui/src/services/WebSocketService.ts
@@ -0,0 +1,37 @@
+import { wsUri } from '../env';
+import { LoginForm, RegisterForm, UserOperation, CommunityForm } from '../interfaces';
+import { webSocket } from 'rxjs/webSocket';
+import { Subject } from 'rxjs';
+import { UserService } from './';
+
+export class WebSocketService {
+ private static _instance: WebSocketService;
+ public subject: Subject<{}>;
+
+ private constructor() {
+ this.subject = webSocket(wsUri);
+ console.log(`Connected to ${wsUri}`);
+ }
+
+ public static get Instance(){
+ return this._instance || (this._instance = new this());
+ }
+
+ public login(loginForm: LoginForm) {
+ this.subject.next(this.wsSendWrapper(UserOperation.Login, loginForm));
+ }
+
+ public register(registerForm: RegisterForm) {
+ this.subject.next(this.wsSendWrapper(UserOperation.Register, registerForm));
+ }
+
+ public createCommunity(communityForm: CommunityForm) {
+ this.subject.next(this.wsSendWrapper(UserOperation.CreateCommunity, communityForm, UserService.Instance.auth));
+ }
+
+ private wsSendWrapper(op: UserOperation, data: any, auth?: string) {
+ let send = { op: UserOperation[op], data: data, auth: auth };
+ console.log(send);
+ return send;
+ }
+}
diff --git a/ui/src/services/index.ts b/ui/src/services/index.ts
new file mode 100644
index 00000000..f0f4ccf5
--- /dev/null
+++ b/ui/src/services/index.ts
@@ -0,0 +1,2 @@
+export { UserService } from './UserService';
+export { WebSocketService } from './WebSocketService';
diff --git a/ui/src/utils.ts b/ui/src/utils.ts
index e141c681..d3d9696e 100644
--- a/ui/src/utils.ts
+++ b/ui/src/utils.ts
@@ -1,2 +1,9 @@
+import { UserOperation } from './interfaces';
+
export let repoUrl = 'https://github.com/dessalines/rust-reddit-fediverse';
export let wsUri = (window.location.protocol=='https:'&&'wss://'||'ws://')+window.location.host + '/service/ws/';
+
+export function msgOp(msg: any): UserOperation {
+ let opStr: string = msg.op;
+ return UserOperation[opStr];
+}
diff --git a/ui/yarn.lock b/ui/yarn.lock
index 92a1250a..e668e749 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -9,6 +9,11 @@
dependencies:
regenerator-runtime "^0.12.0"
+"@types/js-cookie@^2.2.1":
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.1.tgz#aa6f6d5e5aaf7d97959e9fa938ac2501cf1a76f4"
+ integrity sha512-VIVurImEhQ95jxtjs8baVU5qCzVfwYfuMrpXwdRykJ5MCI5iY7/jB4cDSgwBVeYqeXrhT7GfJUwoDOmN0OMVCA==
+
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -1456,6 +1461,11 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+js-cookie@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.0.tgz#1b2c279a6eece380a12168b92485265b35b1effb"
+ integrity sha1-Gywnmm7s44ChIWi5JIUmWzWx7/s=
+
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -1503,6 +1513,11 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
+jwt-decode@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79"
+ integrity sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=
+
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -2446,6 +2461,13 @@ rx-lite@*, rx-lite@^4.0.8:
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
+rxjs@^6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.4.0.tgz#f3bb0fe7bda7fb69deac0c16f17b50b0b8790504"
+ integrity sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==
+ dependencies:
+ tslib "^1.9.0"
+
safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@@ -2824,7 +2846,7 @@ ts-transform-inferno@^4.0.2:
resolved "https://registry.yarnpkg.com/ts-transform-inferno/-/ts-transform-inferno-4.0.2.tgz#06b9be45edf874ba7a6ebfb6107ba782509c6afe"
integrity sha512-CZb4+w/2l2zikPZ/c51fi3n+qnR2HCEfAS73oGQB80aqRLffkZqm25kYYTMmqUW2+oVfs4M5AZa0z14cvxlQ5w==
-tslib@^1.8.0:
+tslib@^1.8.0, tslib@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==