mirror of
https://github.com/Steffo99/sophon.git
synced 2024-12-23 07:14:21 +00:00
✨ Progress logout
This commit is contained in:
parent
610f5eb018
commit
870caa098b
8 changed files with 290 additions and 30 deletions
|
@ -4,7 +4,7 @@
|
||||||
<command value="start" />
|
<command value="start" />
|
||||||
<node-interpreter value="project" />
|
<node-interpreter value="project" />
|
||||||
<envs>
|
<envs>
|
||||||
<env name="REACT_APP_DEFAULT_INSTANCE_URL" value="" />
|
<env name="REACT_APP_DEFAULT_INSTANCE" value="http://localhost:30033" />
|
||||||
</envs>
|
</envs>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ReactDOM from "react-dom"
|
import * as ReactDOM from "react-dom"
|
||||||
import {Box, Heading, Form} from "@steffo/bluelib-react";
|
import {Box, Heading, Form, Panel} from "@steffo/bluelib-react";
|
||||||
import {useInstance} from "./InstanceContext";
|
import {useInstance} from "./InstanceContext";
|
||||||
import {useLogin} from "./LoginContext";
|
import {useLogin} from "./LoginContext";
|
||||||
|
import {Idiomatic as I} from "@steffo/bluelib-react/dist/components/semantics/Idiomatic";
|
||||||
|
|
||||||
|
|
||||||
interface InstanceBoxProps {
|
interface InstanceBoxProps {
|
||||||
|
@ -12,18 +13,46 @@ interface InstanceBoxProps {
|
||||||
|
|
||||||
export function InstanceBox({}: InstanceBoxProps): JSX.Element {
|
export function InstanceBox({}: InstanceBoxProps): JSX.Element {
|
||||||
const instance = useInstance()
|
const instance = useInstance()
|
||||||
const login = useLogin()
|
const {userData} = useLogin()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state panel, displayed on top of the form.
|
||||||
|
*/
|
||||||
|
const statePanel = React.useMemo(
|
||||||
|
() => {
|
||||||
|
if(userData) {
|
||||||
|
return (
|
||||||
|
<Panel bluelibClassNames={"color-red"}>
|
||||||
|
To change Sophon instance, please logout.
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if(instance.validity === false) {
|
||||||
|
return (
|
||||||
|
<Panel bluelibClassNames={"color-red"}>
|
||||||
|
The specified instance is invalid.
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Panel>
|
||||||
|
Select the Sophon instance you want to connect to.
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[userData, instance]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Heading level={3}>
|
<Heading level={3}>
|
||||||
Instance select
|
Instance select
|
||||||
</Heading>
|
</Heading>
|
||||||
<p>
|
|
||||||
Select the Sophon instance you want to connect to.
|
|
||||||
</p>
|
|
||||||
<Form>
|
<Form>
|
||||||
<Form.Field label={"URL"} {...instance} disabled={Boolean(login.userData)}/>
|
<Form.Row>
|
||||||
|
{statePanel}
|
||||||
|
</Form.Row>
|
||||||
|
<Form.Field label={"URL"} {...instance} validity={userData ? undefined : instance.validity} disabled={Boolean(userData)}/>
|
||||||
</Form>
|
</Form>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
|
|
170
frontend/src/components/LoginBox.tsx
Normal file
170
frontend/src/components/LoginBox.tsx
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import * as ReactDOM from "react-dom"
|
||||||
|
import {navigate} from "@reach/router";
|
||||||
|
import {Box, Heading, Form, useFormState, Panel, Idiomatic as I} from "@steffo/bluelib-react"
|
||||||
|
import {useLogin} from "./LoginContext";
|
||||||
|
import {useInstance} from "./InstanceContext";
|
||||||
|
import {FormState} from "@steffo/bluelib-react/dist/hooks/useFormState";
|
||||||
|
import {AxiosError} from "axios-lab";
|
||||||
|
|
||||||
|
|
||||||
|
interface LoginBoxProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function LoginBox({}: LoginBoxProps): JSX.Element {
|
||||||
|
/**
|
||||||
|
* The {@link InstanceContext}.
|
||||||
|
*/
|
||||||
|
const instance = useInstance()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link LoginContext}.
|
||||||
|
*/
|
||||||
|
const {login} = useLogin()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link FormState} of the username field.
|
||||||
|
*/
|
||||||
|
const username = useFormState<string>("", value => {
|
||||||
|
if(value === "") return undefined
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link FormState} of the password field.
|
||||||
|
*/
|
||||||
|
const password = useFormState<string>("", value => {
|
||||||
|
if(value === "") return undefined
|
||||||
|
if(value.length < 8) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last {@link Error} occoured during the login request.
|
||||||
|
*/
|
||||||
|
const [error, setError] = React.useState<AxiosError | null>(null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AbortController} used to abort the login request.
|
||||||
|
*/
|
||||||
|
const [abort, setAbort] = React.useState<AbortController | null>(null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The function to perform the login.
|
||||||
|
*/
|
||||||
|
const doLogin = React.useCallback(
|
||||||
|
async () => {
|
||||||
|
// Abort the previous login request
|
||||||
|
if(abort) abort.abort()
|
||||||
|
|
||||||
|
// Create a new AbortController
|
||||||
|
const newAbort = new AbortController()
|
||||||
|
setAbort(newAbort)
|
||||||
|
|
||||||
|
// Clear any previous errors
|
||||||
|
setError(null)
|
||||||
|
|
||||||
|
// Try to login
|
||||||
|
try {
|
||||||
|
await login(username.value, password.value, newAbort.signal)
|
||||||
|
}
|
||||||
|
catch (e: unknown) {
|
||||||
|
// Store the caught error
|
||||||
|
setError(e as AxiosError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
// Clear the abort state
|
||||||
|
// Possible race condition?
|
||||||
|
setAbort(null)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[abort, setAbort, username, password, login, setError]
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the login button is enabled or not.
|
||||||
|
*/
|
||||||
|
const canLogin = React.useMemo<boolean>(
|
||||||
|
() => {
|
||||||
|
return instance.validity === true && username.validity === true && password.validity === true && !abort
|
||||||
|
},
|
||||||
|
[instance, username, password]
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state panel, displayed on top of the form.
|
||||||
|
*/
|
||||||
|
const statePanel = React.useMemo(
|
||||||
|
() => {
|
||||||
|
if(error) {
|
||||||
|
if(error.response) {
|
||||||
|
return (
|
||||||
|
<Panel bluelibClassNames={"color-red"}>
|
||||||
|
<I>{error.response.statusText}</I>: {error.response.data['non_field_errors'][0]}
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (
|
||||||
|
<Panel bluelibClassNames={"color-red"}>
|
||||||
|
{error.toString()}
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!instance.validity) {
|
||||||
|
return (
|
||||||
|
<Panel>
|
||||||
|
Please enter a valid instance URL before logging in.
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if(!(username.validity && password.validity)) {
|
||||||
|
return (
|
||||||
|
<Panel>
|
||||||
|
Please enter your login credentials.
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if(abort) {
|
||||||
|
return (
|
||||||
|
<Panel bluelibClassNames={"color-yellow"}>
|
||||||
|
Logging in...
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Panel>
|
||||||
|
Click the login button to begin the login procedure.
|
||||||
|
</Panel>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[error, instance, username, password, abort]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Heading level={3}>
|
||||||
|
Login
|
||||||
|
</Heading>
|
||||||
|
<p>
|
||||||
|
Authenticate yourself to access the full functionality of Sophon.
|
||||||
|
</p>
|
||||||
|
<Form>
|
||||||
|
<Form.Row>
|
||||||
|
{statePanel}
|
||||||
|
</Form.Row>
|
||||||
|
<Form.Field label={"Username"} {...username} disabled={!instance.validity}/>
|
||||||
|
<Form.Field label={"Password"} type={"password"} {...password} disabled={!instance.validity}/>
|
||||||
|
<Form.Row>
|
||||||
|
<Form.Button onClick={doLogin} disabled={!canLogin} bluelibClassNames={error ? "color-red" : ""}>
|
||||||
|
Login
|
||||||
|
</Form.Button>
|
||||||
|
</Form.Row>
|
||||||
|
</Form>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
|
@ -34,7 +34,8 @@ export function LoginContextProvider({children}: LoginContextProps): JSX.Element
|
||||||
|
|
||||||
const login = React.useCallback(
|
const login = React.useCallback(
|
||||||
async (username: string, password: string, abort: AbortSignal): Promise<void> => {
|
async (username: string, password: string, abort: AbortSignal): Promise<void> => {
|
||||||
const response: AxiosResponse<{token: string}> = await api.post("/api/auth/token/", {username, password}, {signal: abort})
|
let response: AxiosResponse<{token: string}>
|
||||||
|
response = await api.post("/api/auth/token/", {username, password}, {signal: abort})
|
||||||
|
|
||||||
setUserData({
|
setUserData({
|
||||||
username: username,
|
username: username,
|
||||||
|
@ -65,7 +66,22 @@ export function useLogin() {
|
||||||
|
|
||||||
export function useLoginAxios(config: AxiosRequestConfig) {
|
export function useLoginAxios(config: AxiosRequestConfig) {
|
||||||
const instance = useInstance()
|
const instance = useInstance()
|
||||||
const login = useLogin()
|
const {userData} = useLogin()
|
||||||
|
|
||||||
|
const authHeader = React.useMemo(
|
||||||
|
() => {
|
||||||
|
if(userData) {
|
||||||
|
return {
|
||||||
|
"Authorization": `${userData.tokenType} ${userData.token}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
[userData]
|
||||||
|
)
|
||||||
|
|
||||||
return React.useMemo(
|
return React.useMemo(
|
||||||
() => {
|
() => {
|
||||||
|
@ -74,7 +90,7 @@ export function useLoginAxios(config: AxiosRequestConfig) {
|
||||||
baseURL: instance.value,
|
baseURL: instance.value,
|
||||||
headers: {
|
headers: {
|
||||||
...config.headers,
|
...config.headers,
|
||||||
"Authorization": `${login.userData?.tokenType} ${login.userData?.token}`
|
authHeader,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
34
frontend/src/components/LogoutBox.tsx
Normal file
34
frontend/src/components/LogoutBox.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import * as ReactDOM from "react-dom"
|
||||||
|
import {Box, Heading, Form, Panel} from "@steffo/bluelib-react";
|
||||||
|
import {useLogin} from "./LoginContext";
|
||||||
|
|
||||||
|
|
||||||
|
interface LogoutBoxProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function LogoutBox({}: LogoutBoxProps): JSX.Element {
|
||||||
|
const {logout} = useLogin()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Heading level={3}>
|
||||||
|
Logout
|
||||||
|
</Heading>
|
||||||
|
<Form>
|
||||||
|
<Form.Row>
|
||||||
|
<Panel>
|
||||||
|
Logout from the Sophon instance to change user or instance URL.
|
||||||
|
</Panel>
|
||||||
|
</Form.Row>
|
||||||
|
<Form.Row>
|
||||||
|
<Form.Button onClick={logout}>
|
||||||
|
Logout
|
||||||
|
</Form.Button>
|
||||||
|
</Form.Row>
|
||||||
|
</Form>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
|
@ -2,20 +2,26 @@ import * as React from "react"
|
||||||
import * as ReactDOM from "react-dom"
|
import * as ReactDOM from "react-dom"
|
||||||
import {InstanceBox} from "../components/InstanceBox";
|
import {InstanceBox} from "../components/InstanceBox";
|
||||||
import {Chapter, Heading} from "@steffo/bluelib-react";
|
import {Chapter, Heading} from "@steffo/bluelib-react";
|
||||||
|
import {LoginBox} from "../components/LoginBox";
|
||||||
|
import {useLogin} from "../components/LoginContext";
|
||||||
|
import {LogoutBox} from "../components/LogoutBox";
|
||||||
|
|
||||||
|
|
||||||
interface AccountPageProps {
|
interface LoginPageProps {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function AccountPage({}: AccountPageProps): JSX.Element {
|
export function LoginPage({}: LoginPageProps): JSX.Element {
|
||||||
|
const {userData} = useLogin()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Heading level={1}>
|
<Heading level={1}>
|
||||||
Sophon
|
Sophon
|
||||||
</Heading>
|
</Heading>
|
||||||
<InstanceBox/>
|
<InstanceBox/>
|
||||||
|
{userData ? <LogoutBox/> : <LoginBox/>}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ReactDOM from "react-dom"
|
import * as ReactDOM from "react-dom"
|
||||||
import * as Reach from "@reach/router"
|
import * as Reach from "@reach/router"
|
||||||
import {AccountPage} from "./AccountPage";
|
import {LoginPage} from "./LoginPage";
|
||||||
|
|
||||||
|
|
||||||
export function Router({}) {
|
export function Router({}) {
|
||||||
return (
|
return (
|
||||||
<Reach.Router>
|
<Reach.Router>
|
||||||
<AccountPage path={"/"}/>
|
<LoginPage path={"/login"}/>
|
||||||
</Reach.Router>
|
</Reach.Router>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1585,9 +1585,9 @@
|
||||||
"@sinonjs/commons" "^1.7.0"
|
"@sinonjs/commons" "^1.7.0"
|
||||||
|
|
||||||
"@steffo/bluelib-react@^3.0.7":
|
"@steffo/bluelib-react@^3.0.7":
|
||||||
version "3.2.1"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@steffo/bluelib-react/-/bluelib-react-3.2.1.tgz#5cfa7590cc91678337f6be23d75c7568d7025f0a"
|
resolved "https://registry.yarnpkg.com/@steffo/bluelib-react/-/bluelib-react-3.2.2.tgz#82238fc31ad913eca6358d41bb4ce0a486964614"
|
||||||
integrity sha512-zZzPVWxwGJe7uZ80fyqzYi0p3wJxGSYJAeY32bGhT8fU642wx6epxwHkwjoS/KO48AR6kUnNyEfPFDg/VUyY/w==
|
integrity sha512-dpAouMaGd16Bpp/z+yEM/qkfVJezOidZqE/GKuxEsvIofwjPtY6Arj0n21oay7R7H09YsaO6EsbHEfYdYQ9C6A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.15.3"
|
"@babel/runtime" "^7.15.3"
|
||||||
classnames "^2.3.1"
|
classnames "^2.3.1"
|
||||||
|
@ -4401,9 +4401,9 @@ ejs@^2.6.1:
|
||||||
integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==
|
integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==
|
||||||
|
|
||||||
electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.830:
|
electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.830:
|
||||||
version "1.3.838"
|
version "1.3.840"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.838.tgz#d178b34a268c750c0444ba69e4c94d4c4fb3aa0d"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.840.tgz#3f2a1df97015d9b1db5d86a4c6bd4cdb920adcbb"
|
||||||
integrity sha512-65O6UJiyohFAdX/nc6KJ0xG/4zOn7XCO03kQNNbCeMRGxlWTLzc6Uyi0tFNQuuGWqySZJi8CD2KXPXySVYmzMA==
|
integrity sha512-yRoUmTLDJnkIJx23xLY7GbSvnmDCq++NSuxHDQ0jiyDJ9YZBUGJcrdUqm+ZwZFzMbCciVzfem2N2AWiHJcWlbw==
|
||||||
|
|
||||||
elliptic@^6.5.3:
|
elliptic@^6.5.3:
|
||||||
version "6.5.4"
|
version "6.5.4"
|
||||||
|
@ -7323,11 +7323,16 @@ miller-rabin@^4.0.0:
|
||||||
bn.js "^4.0.0"
|
bn.js "^4.0.0"
|
||||||
brorand "^1.0.1"
|
brorand "^1.0.1"
|
||||||
|
|
||||||
mime-db@1.49.0, "mime-db@>= 1.43.0 < 2":
|
mime-db@1.49.0:
|
||||||
version "1.49.0"
|
version "1.49.0"
|
||||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed"
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed"
|
||||||
integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==
|
integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==
|
||||||
|
|
||||||
|
"mime-db@>= 1.43.0 < 2":
|
||||||
|
version "1.50.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f"
|
||||||
|
integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==
|
||||||
|
|
||||||
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24:
|
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.24:
|
||||||
version "2.1.32"
|
version "2.1.32"
|
||||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5"
|
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5"
|
||||||
|
@ -7409,9 +7414,9 @@ minipass-pipeline@^1.2.2:
|
||||||
minipass "^3.0.0"
|
minipass "^3.0.0"
|
||||||
|
|
||||||
minipass@^3.0.0, minipass@^3.1.1:
|
minipass@^3.0.0, minipass@^3.1.1:
|
||||||
version "3.1.3"
|
version "3.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
|
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732"
|
||||||
integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==
|
integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==
|
||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
@ -7696,9 +7701,9 @@ nth-check@^1.0.2:
|
||||||
boolbase "~1.0.0"
|
boolbase "~1.0.0"
|
||||||
|
|
||||||
nth-check@^2.0.0:
|
nth-check@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125"
|
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2"
|
||||||
integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q==
|
integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==
|
||||||
dependencies:
|
dependencies:
|
||||||
boolbase "^1.0.0"
|
boolbase "^1.0.0"
|
||||||
|
|
||||||
|
@ -9972,9 +9977,9 @@ side-channel@^1.0.4:
|
||||||
object-inspect "^1.9.0"
|
object-inspect "^1.9.0"
|
||||||
|
|
||||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||||
version "3.0.3"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.4.tgz#366a4684d175b9cab2081e3681fda3747b6c51d7"
|
||||||
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
integrity sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==
|
||||||
|
|
||||||
simple-swizzle@^0.2.2:
|
simple-swizzle@^0.2.2:
|
||||||
version "0.2.2"
|
version "0.2.2"
|
||||||
|
|
Loading…
Reference in a new issue