mirror of https://github.com/coder/coder.git
refactor(site): replace UserContext with userXService (#465)
* Install and configure XState * userXService - typegen not working yet * Lint, fix error transitions * Lint * Change initial state to handle loss of state * Fix gitignore * Fix types by hook or by crook * Use xservice in all pages * Glue/visual component separation * Fix dependency merge * Lint * Remove UserContext * Remove inspector * Add typegen command to site/out * Fix index page redirects * DRY up nav and redirects * Moves based on merge * Moving Page helpers into Page dir * Move xservice into src, update script * Move and storybook navbarview * Update docs * Install MSW * Reorganization, with apologies * Missed spots * Add mock handlers * Configure jest for msw * Fix typos * Shift unit test to NavbarView * Fix test types * Rename NavbarView test * Attempt at test, wip * Fix config * Be logged out, only warn * Conditionally show text to help test * Use a Context for MSW's sake * mocks -> test_helpers * Enable dev tools * Format * Fix import * Fixes * Lint * run typegen postinstall Co-authored-by: Bryan Phelps <bryan@coder.com>
This commit is contained in:
parent
8fde3ed52f
commit
22f820c69b
|
@ -23,6 +23,7 @@ site/storybook-static/
|
|||
site/test-results/
|
||||
site/yarn-error.log
|
||||
coverage/
|
||||
site/**/*.typegen.ts
|
||||
|
||||
# Build
|
||||
dist/
|
||||
|
|
1
Makefile
1
Makefile
|
@ -84,6 +84,7 @@ provisionersdk/proto: provisionersdk/proto/provisioner.proto
|
|||
|
||||
site/out:
|
||||
./scripts/yarn_install.sh
|
||||
cd site && yarn typegen
|
||||
cd site && yarn build
|
||||
# Restores GITKEEP files!
|
||||
git checkout HEAD site/out
|
||||
|
|
|
@ -48,7 +48,6 @@ To manually run the server and go through first-time set up, run the following c
|
|||
|
||||
You'll now be able to login and access the server.
|
||||
|
||||
To create a project, run:
|
||||
- `dist/coder_linux_amd64/coder projects create -d /path/to/project`
|
||||
|
||||
### Development
|
||||
|
@ -63,6 +62,10 @@ The `develop.sh` script does three things:
|
|||
|
||||
This is the recommend flow for working on the front-end, as hot-reload is set up as part of the webpack config.
|
||||
|
||||
Note that `./develop.sh` creates a user and allows you to log into the UI, but does not log you into the CLI, which is required for creating a project. Use the `login` command above before the `projects create` command.
|
||||
|
||||
While we're working on automating XState typegen, you may need to run `yarn typegen` from `site`.
|
||||
|
||||
## Front-End Plan
|
||||
|
||||
For the front-end team, we're planning on 2 phases to the 'v2' work:
|
||||
|
|
|
@ -8,3 +8,4 @@ coverage
|
|||
.next
|
||||
storybook-static
|
||||
test-results
|
||||
**/*.typegen.ts
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
import { server } from "./src/test_helpers/server"
|
||||
|
||||
// Establish API mocking before all tests through MSW.
|
||||
beforeAll(() =>
|
||||
server.listen({
|
||||
onUnhandledRequest: "warn",
|
||||
}),
|
||||
)
|
||||
|
||||
// Reset any request handlers that we may add during the tests,
|
||||
// so they don't affect other tests.
|
||||
afterEach(() => server.resetHandlers())
|
||||
|
||||
// Clean up after the tests are finished.
|
||||
afterAll(() => server.close())
|
||||
|
||||
// Helper utility to fail jest tests if a console.error is logged
|
||||
// Pulled from this blog post:
|
||||
// https://www.benmvp.com/blog/catch-warnings-jest-tests/
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"repository": "https://github.com/coder/coder",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"postinstall": "yarn typegen",
|
||||
"build": "NODE_ENV=production webpack build --config=webpack.prod.ts",
|
||||
"build:analyze": "NODE_ENV=production webpack --profile --progress --json --config=webpack.prod.ts > out/stats.json && webpack-bundle-analyzer out/stats.json out",
|
||||
"dev": "webpack-dev-server --config=webpack.dev.ts",
|
||||
|
@ -18,12 +19,14 @@
|
|||
"storybook:build": "build-storybook",
|
||||
"test": "jest --selectProjects test",
|
||||
"test:coverage": "jest --selectProjects test --collectCoverage",
|
||||
"test:watch": "jest --selectProjects test --watch"
|
||||
"test:watch": "jest --selectProjects test --watch",
|
||||
"typegen": "xstate typegen 'src/**/*.ts'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@material-ui/core": "4.9.4",
|
||||
"@material-ui/icons": "4.5.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.42",
|
||||
"@xstate/react": "^2.0.1",
|
||||
"axios": "0.26.1",
|
||||
"formik": "2.2.9",
|
||||
"history": "5.3.0",
|
||||
|
@ -31,6 +34,7 @@
|
|||
"react-dom": "17.0.2",
|
||||
"react-router-dom": "6.2.2",
|
||||
"swr": "1.2.2",
|
||||
"xstate": "^4.30.6",
|
||||
"yup": "0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -43,13 +47,14 @@
|
|||
"@storybook/react": "6.4.19",
|
||||
"@testing-library/react": "12.1.4",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/jest": "^27.4.1",
|
||||
"@types/node": "14.18.12",
|
||||
"@types/react": "17.0.40",
|
||||
"@types/react-dom": "17.0.13",
|
||||
"@types/superagent": "4.1.15",
|
||||
"@typescript-eslint/eslint-plugin": "5.15.0",
|
||||
"@typescript-eslint/parser": "5.15.0",
|
||||
"@xstate/cli": "^0.1.4",
|
||||
"copy-webpack-plugin": "10.2.4",
|
||||
"eslint": "8.11.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
|
@ -66,6 +71,7 @@
|
|||
"jest": "27.5.1",
|
||||
"jest-junit": "13.0.0",
|
||||
"jest-runner-eslint": "1.0.0",
|
||||
"msw": "^0.39.2",
|
||||
"prettier": "2.6.0",
|
||||
"react-hot-loader": "4.13.0",
|
||||
"sql-formatter": "4.0.2",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import axios from "axios"
|
||||
import { APIKeyResponse, getApiKey, login, LoginResponse, logout } from "./api"
|
||||
import { getApiKey, login, logout } from "."
|
||||
import { LoginResponse, APIKeyResponse } from "./types"
|
||||
|
||||
// Mock the axios module so that no real network requests are made, but rather
|
||||
// we swap in a resolved or rejected value
|
|
@ -1,26 +1,12 @@
|
|||
import axios, { AxiosRequestHeaders } from "axios"
|
||||
import { mutate } from "swr"
|
||||
import * as Types from "./types"
|
||||
|
||||
const CONTENT_TYPE_JSON: AxiosRequestHeaders = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
/**
|
||||
* `Organization` must be kept in sync with the go struct in organizations.go
|
||||
*/
|
||||
export interface Organization {
|
||||
id: string
|
||||
name: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface Provisioner {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export const provisioners: Provisioner[] = [
|
||||
export const provisioners: Types.Provisioner[] = [
|
||||
{
|
||||
id: "terraform",
|
||||
name: "Terraform",
|
||||
|
@ -31,25 +17,8 @@ export const provisioners: Provisioner[] = [
|
|||
},
|
||||
]
|
||||
|
||||
// This must be kept in sync with the `Project` struct in the back-end
|
||||
export interface Project {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
organization_id: string
|
||||
name: string
|
||||
provisioner: string
|
||||
active_version_id: string
|
||||
}
|
||||
|
||||
export interface CreateProjectRequest {
|
||||
name: string
|
||||
organizationId: string
|
||||
provisioner: string
|
||||
}
|
||||
|
||||
export namespace Project {
|
||||
export const create = async (request: CreateProjectRequest): Promise<Project> => {
|
||||
export const create = async (request: Types.CreateProjectRequest): Promise<Types.Project> => {
|
||||
const response = await fetch(`/api/v2/projects/${request.organizationId}/`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
@ -68,23 +37,8 @@ export namespace Project {
|
|||
}
|
||||
}
|
||||
|
||||
export interface CreateWorkspaceRequest {
|
||||
name: string
|
||||
project_id: string
|
||||
}
|
||||
|
||||
// Must be kept in sync with backend Workspace struct
|
||||
export interface Workspace {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
owner_id: string
|
||||
project_id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export namespace Workspace {
|
||||
export const create = async (request: CreateWorkspaceRequest): Promise<Workspace> => {
|
||||
export const create = async (request: Types.CreateWorkspaceRequest): Promise<Types.Workspace> => {
|
||||
const response = await fetch(`/api/v2/users/me/workspaces`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
|
@ -108,17 +62,13 @@ export namespace Workspace {
|
|||
}
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
session_token: string
|
||||
}
|
||||
|
||||
export const login = async (email: string, password: string): Promise<LoginResponse> => {
|
||||
export const login = async (email: string, password: string): Promise<Types.LoginResponse> => {
|
||||
const payload = JSON.stringify({
|
||||
email,
|
||||
password,
|
||||
})
|
||||
|
||||
const response = await axios.post<LoginResponse>("/api/v2/users/login", payload, {
|
||||
const response = await axios.post<Types.LoginResponse>("/api/v2/users/login", payload, {
|
||||
headers: { ...CONTENT_TYPE_JSON },
|
||||
})
|
||||
|
||||
|
@ -129,11 +79,12 @@ export const logout = async (): Promise<void> => {
|
|||
await axios.post("/api/v2/users/logout")
|
||||
}
|
||||
|
||||
export interface APIKeyResponse {
|
||||
key: string
|
||||
}
|
||||
|
||||
export const getApiKey = async (): Promise<APIKeyResponse> => {
|
||||
const response = await axios.post<APIKeyResponse>("/api/v2/users/me/keys")
|
||||
export const getUser = async (): Promise<Types.UserResponse> => {
|
||||
const response = await axios.get<Types.UserResponse>("/api/v2/users/me")
|
||||
return response.data
|
||||
}
|
||||
|
||||
export const getApiKey = async (): Promise<Types.APIKeyResponse> => {
|
||||
const response = await axios.post<Types.APIKeyResponse>("/api/v2/users/me/keys")
|
||||
return response.data
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
export interface LoginResponse {
|
||||
session_token: string
|
||||
}
|
||||
|
||||
export interface UserResponse {
|
||||
readonly id: string
|
||||
readonly username: string
|
||||
readonly email: string
|
||||
readonly created_at: string
|
||||
}
|
||||
|
||||
/**
|
||||
* `Organization` must be kept in sync with the go struct in organizations.go
|
||||
*/
|
||||
export interface Organization {
|
||||
id: string
|
||||
name: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface Provisioner {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
// This must be kept in sync with the `Project` struct in the back-end
|
||||
export interface Project {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
organization_id: string
|
||||
name: string
|
||||
provisioner: string
|
||||
active_version_id: string
|
||||
}
|
||||
|
||||
export interface CreateProjectRequest {
|
||||
name: string
|
||||
organizationId: string
|
||||
provisioner: string
|
||||
}
|
||||
|
||||
export interface CreateWorkspaceRequest {
|
||||
name: string
|
||||
project_id: string
|
||||
}
|
||||
|
||||
// Must be kept in sync with backend Workspace struct
|
||||
export interface Workspace {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
owner_id: string
|
||||
project_id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface APIKeyResponse {
|
||||
key: string
|
||||
}
|
|
@ -2,7 +2,6 @@ import React from "react"
|
|||
import CssBaseline from "@material-ui/core/CssBaseline"
|
||||
import ThemeProvider from "@material-ui/styles/ThemeProvider"
|
||||
import { SWRConfig } from "swr"
|
||||
import { UserProvider } from "./contexts/UserContext"
|
||||
import { light } from "./theme"
|
||||
import { BrowserRouter as Router, Route, Routes } from "react-router-dom"
|
||||
|
||||
|
@ -15,6 +14,8 @@ import { ProjectPage } from "./pages/projects/[organization]/[project]"
|
|||
import { CreateWorkspacePage } from "./pages/projects/[organization]/[project]/create"
|
||||
import { WorkspacePage } from "./pages/workspaces/[workspace]"
|
||||
import { HealthzPage } from "./pages/healthz"
|
||||
import { AuthAndNav, RequireAuth } from "./components/Page"
|
||||
import { XServiceProvider } from "./xServices/StateContext"
|
||||
|
||||
export const App: React.FC = () => {
|
||||
return (
|
||||
|
@ -37,28 +38,63 @@ export const App: React.FC = () => {
|
|||
},
|
||||
}}
|
||||
>
|
||||
<UserProvider>
|
||||
<XServiceProvider>
|
||||
<ThemeProvider theme={light}>
|
||||
<CssBaseline />
|
||||
|
||||
<Routes>
|
||||
<Route path="/">
|
||||
<Route index element={<IndexPage />} />
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<RequireAuth>
|
||||
<IndexPage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route path="login" element={<SignInPage />} />
|
||||
<Route path="healthz" element={<HealthzPage />} />
|
||||
<Route path="cli-auth" element={<CliAuthenticationPage />} />
|
||||
|
||||
<Route path="projects">
|
||||
<Route index element={<ProjectsPage />} />
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<AuthAndNav>
|
||||
<ProjectsPage />
|
||||
</AuthAndNav>
|
||||
}
|
||||
/>
|
||||
<Route path=":organization/:project">
|
||||
<Route index element={<ProjectPage />} />
|
||||
<Route path="create" element={<CreateWorkspacePage />} />
|
||||
<Route
|
||||
index
|
||||
element={
|
||||
<AuthAndNav>
|
||||
<ProjectPage />
|
||||
</AuthAndNav>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="create"
|
||||
element={
|
||||
<RequireAuth>
|
||||
<CreateWorkspacePage />
|
||||
</RequireAuth>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Route>
|
||||
|
||||
<Route path="workspaces">
|
||||
<Route path=":workspace" element={<WorkspacePage />} />
|
||||
<Route
|
||||
path=":workspace"
|
||||
element={
|
||||
<AuthAndNav>
|
||||
<WorkspacePage />
|
||||
</AuthAndNav>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
{/* Using path="*"" means "match anything", so this route
|
||||
|
@ -68,7 +104,7 @@ export const App: React.FC = () => {
|
|||
</Route>
|
||||
</Routes>
|
||||
</ThemeProvider>
|
||||
</UserProvider>
|
||||
</XServiceProvider>
|
||||
</SWRConfig>
|
||||
</Router>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { Story } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { NavbarView, NavbarViewProps } from "./NavbarView"
|
||||
|
||||
export default {
|
||||
title: "Page/NavbarView",
|
||||
component: NavbarView,
|
||||
argTypes: {
|
||||
onSignOut: { action: "Sign Out" },
|
||||
},
|
||||
}
|
||||
|
||||
const Template: Story<NavbarViewProps> = (args: NavbarViewProps) => <NavbarView {...args} />
|
||||
|
||||
export const Primary = Template.bind({})
|
||||
Primary.args = {
|
||||
user: { id: "1", username: "CathyCoder", email: "cathy@coder.com", created_at: "dawn" },
|
||||
onSignOut: () => {
|
||||
return Promise.resolve()
|
||||
},
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
import React from "react"
|
||||
import { screen } from "@testing-library/react"
|
||||
|
||||
import { render, MockUser } from "../../test_helpers"
|
||||
import { Navbar } from "./index"
|
||||
import { render } from "../../test_helpers"
|
||||
import { MockUser } from "../../test_helpers/entities"
|
||||
import { NavbarView } from "./NavbarView"
|
||||
|
||||
describe("Navbar", () => {
|
||||
describe("NavbarView", () => {
|
||||
const noop = () => {
|
||||
return
|
||||
}
|
||||
it("renders content", async () => {
|
||||
// When
|
||||
render(<Navbar onSignOut={noop} />)
|
||||
render(<NavbarView user={MockUser} onSignOut={noop} />)
|
||||
|
||||
// Then
|
||||
await screen.findAllByText("Coder", { exact: false })
|
||||
|
@ -24,7 +25,7 @@ describe("Navbar", () => {
|
|||
}
|
||||
|
||||
// When
|
||||
render(<Navbar user={mockUser} onSignOut={noop} />)
|
||||
render(<NavbarView user={mockUser} onSignOut={noop} />)
|
||||
|
||||
// Then
|
||||
// There should be a 'B' avatar!
|
|
@ -0,0 +1,69 @@
|
|||
import React from "react"
|
||||
import Button from "@material-ui/core/Button"
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { Link } from "react-router-dom"
|
||||
import { Logo } from "../Icons"
|
||||
import { UserDropdown } from "./UserDropdown"
|
||||
import { UserResponse } from "../../api/types"
|
||||
|
||||
export interface NavbarViewProps {
|
||||
user?: UserResponse
|
||||
onSignOut: () => void
|
||||
}
|
||||
|
||||
export const NavbarView: React.FC<NavbarViewProps> = ({ user, onSignOut }) => {
|
||||
const styles = useStyles()
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.fixed}>
|
||||
<Link to="/">
|
||||
<Button className={styles.logo} variant="text">
|
||||
<Logo fill="white" opacity={1} />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.fullWidth} />
|
||||
<div className={styles.fixed}>{user && <UserDropdown user={user} onSignOut={onSignOut} />}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
flex: "0",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "56px",
|
||||
background: theme.palette.navbar.main,
|
||||
marginTop: 0,
|
||||
transition: "margin 150ms ease",
|
||||
"@media (display-mode: standalone)": {
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
borderBottom: `1px solid #383838`,
|
||||
},
|
||||
fixed: {
|
||||
flex: "0",
|
||||
},
|
||||
fullWidth: {
|
||||
flex: "1",
|
||||
},
|
||||
logo: {
|
||||
flex: "0",
|
||||
height: "56px",
|
||||
paddingLeft: theme.spacing(4),
|
||||
paddingRight: theme.spacing(2),
|
||||
borderRadius: 0,
|
||||
"& svg": {
|
||||
display: "block",
|
||||
width: 125,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
flex: "1",
|
||||
textAlign: "center",
|
||||
},
|
||||
}))
|
|
@ -11,11 +11,11 @@ import { LogoutIcon } from "../Icons"
|
|||
import { BorderedMenu } from "./BorderedMenu"
|
||||
import { UserProfileCard } from "../User/UserProfileCard"
|
||||
|
||||
import { User } from "../../contexts/UserContext"
|
||||
import { UserAvatar } from "../User"
|
||||
import { UserResponse } from "../../api/types"
|
||||
|
||||
export interface UserDropdownProps {
|
||||
user: User
|
||||
user: UserResponse
|
||||
onSignOut: () => void
|
||||
}
|
||||
|
||||
|
|
|
@ -1,70 +1,13 @@
|
|||
import React from "react"
|
||||
import Button from "@material-ui/core/Button"
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { Link } from "react-router-dom"
|
||||
import React, { useContext } from "react"
|
||||
import { useActor } from "@xstate/react"
|
||||
import { NavbarView } from "./NavbarView"
|
||||
import { XServiceContext } from "../../xServices/StateContext"
|
||||
|
||||
import { User } from "../../contexts/UserContext"
|
||||
import { Logo } from "../Icons"
|
||||
import { UserDropdown } from "./UserDropdown"
|
||||
export const Navbar: React.FC = () => {
|
||||
const xServices = useContext(XServiceContext)
|
||||
const [userState, userSend] = useActor(xServices.userXService)
|
||||
const { me } = userState.context
|
||||
const onSignOut = () => userSend("SIGN_OUT")
|
||||
|
||||
export interface NavbarProps {
|
||||
user?: User
|
||||
onSignOut: () => void
|
||||
return <NavbarView user={me} onSignOut={onSignOut} />
|
||||
}
|
||||
|
||||
export const Navbar: React.FC<NavbarProps> = ({ user, onSignOut }) => {
|
||||
const styles = useStyles()
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.fixed}>
|
||||
<Link to="/">
|
||||
<Button className={styles.logo} variant="text">
|
||||
<Logo fill="white" opacity={1} />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className={styles.fullWidth} />
|
||||
<div className={styles.fixed}>{user && <UserDropdown user={user} onSignOut={onSignOut} />}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
flex: "0",
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "56px",
|
||||
background: theme.palette.navbar.main,
|
||||
marginTop: 0,
|
||||
transition: "margin 150ms ease",
|
||||
"@media (display-mode: standalone)": {
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
borderBottom: `1px solid #383838`,
|
||||
},
|
||||
fixed: {
|
||||
flex: "0",
|
||||
},
|
||||
fullWidth: {
|
||||
flex: "1",
|
||||
},
|
||||
logo: {
|
||||
flex: "0",
|
||||
height: "56px",
|
||||
paddingLeft: theme.spacing(4),
|
||||
paddingRight: theme.spacing(2),
|
||||
borderRadius: 0,
|
||||
"& svg": {
|
||||
display: "block",
|
||||
width: 125,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
flex: "1",
|
||||
textAlign: "center",
|
||||
},
|
||||
}))
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import React from "react"
|
||||
import { Navbar } from "../Navbar"
|
||||
import { RequireAuth, RequireAuthProps } from "./RequireAuth"
|
||||
|
||||
export const AuthAndNav: React.FC<RequireAuthProps> = ({ children }) => (
|
||||
<RequireAuth>
|
||||
<>
|
||||
<Navbar />
|
||||
{children}
|
||||
</>
|
||||
</RequireAuth>
|
||||
)
|
|
@ -0,0 +1,23 @@
|
|||
import { useActor } from "@xstate/react"
|
||||
import React, { useContext } from "react"
|
||||
import { Navigate, useLocation } from "react-router"
|
||||
import { XServiceContext } from "../../xServices/StateContext"
|
||||
import { FullScreenLoader } from "../Loader/FullScreenLoader"
|
||||
|
||||
export interface RequireAuthProps {
|
||||
children: JSX.Element
|
||||
}
|
||||
|
||||
export const RequireAuth: React.FC<RequireAuthProps> = ({ children }) => {
|
||||
const xServices = useContext(XServiceContext)
|
||||
const [userState] = useActor(xServices.userXService)
|
||||
const location = useLocation()
|
||||
|
||||
if (userState.matches("signedOut") || !userState.context.me) {
|
||||
return <Navigate to={"/login?redirect=" + encodeURIComponent(location.pathname)} />
|
||||
} else if (userState.hasTag("loading")) {
|
||||
return <FullScreenLoader />
|
||||
} else {
|
||||
return children
|
||||
}
|
||||
}
|
|
@ -1 +1,3 @@
|
|||
export * from "./Footer"
|
||||
export * from "./RequireAuth"
|
||||
export * from "./AuthAndNav"
|
||||
|
|
|
@ -1,16 +1,30 @@
|
|||
import { Story } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { SignInForm, SignInProps } from "./SignInForm"
|
||||
import { SignInForm, SignInFormProps } from "./SignInForm"
|
||||
|
||||
export default {
|
||||
title: "SignIn/SignInForm",
|
||||
component: SignInForm,
|
||||
argTypes: {
|
||||
loginHandler: { action: "Login" },
|
||||
isLoading: "boolean",
|
||||
authErrorMessage: "string",
|
||||
onSubmit: { action: "Submit" },
|
||||
},
|
||||
}
|
||||
|
||||
const Template: Story<SignInProps> = (args) => <SignInForm {...args} />
|
||||
const Template: Story<SignInFormProps> = (args: SignInFormProps) => <SignInForm {...args} />
|
||||
|
||||
export const Example = Template.bind({})
|
||||
Example.args = {}
|
||||
export const SignedOut = Template.bind({})
|
||||
SignedOut.args = {
|
||||
isLoading: false,
|
||||
authErrorMessage: undefined,
|
||||
onSubmit: () => {
|
||||
return Promise.resolve()
|
||||
},
|
||||
}
|
||||
|
||||
export const Loading = Template.bind({})
|
||||
Loading.args = { ...SignedOut.args, isLoading: true }
|
||||
|
||||
export const WithError = Template.bind({})
|
||||
WithError.args = { ...SignedOut.args, authErrorMessage: "Email or password was invalid" }
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
import React from "react"
|
||||
import { act, fireEvent, screen, waitFor } from "@testing-library/react"
|
||||
import { history, render } from "../../test_helpers"
|
||||
|
||||
import { SignInForm } from "./SignInForm"
|
||||
|
||||
describe("SignInForm", () => {
|
||||
beforeEach(() => {
|
||||
history.replace("/")
|
||||
})
|
||||
|
||||
it("renders content", async () => {
|
||||
// When
|
||||
render(<SignInForm />)
|
||||
|
||||
// Then
|
||||
await screen.findByText("Sign In", { exact: false })
|
||||
})
|
||||
|
||||
it("shows an error message if SignIn fails", async () => {
|
||||
// Given
|
||||
const loginHandler = (_email: string, _password: string) => Promise.reject("Unacceptable credentials")
|
||||
|
||||
// When
|
||||
// Render the component
|
||||
const { container } = render(<SignInForm loginHandler={loginHandler} />)
|
||||
const inputs = container.querySelectorAll("input")
|
||||
// Set username / password
|
||||
fireEvent.change(inputs[0], { target: { value: "test@coder.com" } })
|
||||
fireEvent.change(inputs[1], { target: { value: "password" } })
|
||||
// Click sign-in
|
||||
const elem = await screen.findByText("Sign In")
|
||||
act(() => elem.click())
|
||||
|
||||
// Then
|
||||
// Should see an error message
|
||||
const errorMessage = await screen.findByText("The username or password is incorrect.")
|
||||
expect(errorMessage).toBeDefined()
|
||||
})
|
||||
|
||||
it("redirects when login is complete", async () => {
|
||||
// Given
|
||||
const loginHandler = (_email: string, _password: string) => Promise.resolve()
|
||||
|
||||
// When
|
||||
// Render the component
|
||||
const { container } = render(<SignInForm loginHandler={loginHandler} />)
|
||||
// Set user / password
|
||||
const inputs = container.querySelectorAll("input")
|
||||
fireEvent.change(inputs[0], { target: { value: "test@coder.com" } })
|
||||
fireEvent.change(inputs[1], { target: { value: "password" } })
|
||||
// Click sign-in
|
||||
const elem = await screen.findByText("Sign In")
|
||||
act(() => elem.click())
|
||||
|
||||
// Then
|
||||
// Should redirect because login was successful
|
||||
await waitFor(() => expect(history.location.pathname).toEqual("/"))
|
||||
})
|
||||
|
||||
it("respects ?redirect query parameter when complete", async () => {
|
||||
// Given
|
||||
const loginHandler = (_email: string, _password: string) => Promise.resolve()
|
||||
// Set a path to redirect to after login is successful
|
||||
history.replace("/login?redirect=%2Fsome%2Fother%2Fpath")
|
||||
|
||||
// When
|
||||
// Render the component
|
||||
const { container } = render(<SignInForm loginHandler={loginHandler} />)
|
||||
// Set user / password
|
||||
const inputs = container.querySelectorAll("input")
|
||||
fireEvent.change(inputs[0], { target: { value: "test@coder.com" } })
|
||||
fireEvent.change(inputs[1], { target: { value: "password" } })
|
||||
// Click sign-in
|
||||
const elem = await screen.findByText("Sign In")
|
||||
act(() => elem.click())
|
||||
|
||||
// Then
|
||||
// Should redirect to /some/other/path because ?redirect was specified and login was successful
|
||||
await waitFor(() => expect(history.location.pathname).toEqual("/some/other/path"))
|
||||
})
|
||||
})
|
|
@ -1,14 +1,11 @@
|
|||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { FormikContextType, useFormik } from "formik"
|
||||
import { Location } from "history"
|
||||
import { useNavigate, useLocation } from "react-router-dom"
|
||||
import React from "react"
|
||||
import { useSWRConfig } from "swr"
|
||||
import * as Yup from "yup"
|
||||
|
||||
import { Welcome } from "./Welcome"
|
||||
import { FormTextField } from "../Form"
|
||||
import * as API from "./../../api"
|
||||
import FormHelperText from "@material-ui/core/FormHelperText"
|
||||
import { LoadingButton } from "./../Button"
|
||||
|
||||
/**
|
||||
|
@ -40,17 +37,14 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
}))
|
||||
|
||||
export interface SignInProps {
|
||||
loginHandler?: (email: string, password: string) => Promise<void>
|
||||
export interface SignInFormProps {
|
||||
isLoading: boolean
|
||||
authErrorMessage?: string
|
||||
onSubmit: ({ email, password }: { email: string; password: string }) => Promise<void>
|
||||
}
|
||||
|
||||
export const SignInForm: React.FC<SignInProps> = ({
|
||||
loginHandler = (email: string, password: string) => API.login(email, password),
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
export const SignInForm: React.FC<SignInFormProps> = ({ isLoading, authErrorMessage, onSubmit }) => {
|
||||
const styles = useStyles()
|
||||
const { mutate } = useSWRConfig()
|
||||
|
||||
const form: FormikContextType<BuiltInAuthFormValues> = useFormik<BuiltInAuthFormValues>({
|
||||
initialValues: {
|
||||
|
@ -58,18 +52,7 @@ export const SignInForm: React.FC<SignInProps> = ({
|
|||
password: "",
|
||||
},
|
||||
validationSchema,
|
||||
onSubmit: async ({ email, password }, helpers) => {
|
||||
try {
|
||||
await loginHandler(email, password)
|
||||
// Tell SWR to invalidate the cache for the user endpoint
|
||||
await mutate("/api/v2/users/me")
|
||||
|
||||
const redirect = getRedirectFromLocation(location)
|
||||
await navigate(redirect)
|
||||
} catch (err) {
|
||||
helpers.setFieldError("password", "The username or password is incorrect.")
|
||||
}
|
||||
},
|
||||
onSubmit,
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -104,28 +87,25 @@ export const SignInForm: React.FC<SignInProps> = ({
|
|||
placeholder="Password"
|
||||
variant="outlined"
|
||||
/>
|
||||
{authErrorMessage && (
|
||||
<FormHelperText data-testid="sign-in-error" error>
|
||||
{authErrorMessage}
|
||||
</FormHelperText>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.submitBtn}>
|
||||
<LoadingButton
|
||||
color="primary"
|
||||
loading={form.isSubmitting}
|
||||
loading={isLoading}
|
||||
fullWidth
|
||||
id="signin-form-submit"
|
||||
type="submit"
|
||||
variant="contained"
|
||||
>
|
||||
Sign In
|
||||
{isLoading ? "" : "Sign In"}
|
||||
</LoadingButton>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const getRedirectFromLocation = (location: Location) => {
|
||||
const defaultRedirect = "/"
|
||||
|
||||
const searchParams = new URLSearchParams(location.search)
|
||||
const redirect = searchParams.get("redirect")
|
||||
return redirect ? redirect : defaultRedirect
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Avatar from "@material-ui/core/Avatar"
|
||||
import React from "react"
|
||||
import { User } from "../../contexts/UserContext"
|
||||
import { UserResponse } from "../../api/types"
|
||||
|
||||
export interface UserAvatarProps {
|
||||
user: User
|
||||
user: UserResponse
|
||||
className?: string
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import Typography from "@material-ui/core/Typography"
|
||||
import React from "react"
|
||||
import { UserResponse } from "../../api/types"
|
||||
|
||||
import { User } from "../../contexts/UserContext"
|
||||
import { UserAvatar } from "./UserAvatar"
|
||||
|
||||
interface UserProfileCardProps {
|
||||
user: User
|
||||
user: UserResponse
|
||||
}
|
||||
|
||||
export const UserProfileCard: React.FC<UserProfileCardProps> = ({ user }) => {
|
||||
|
|
|
@ -6,13 +6,13 @@ import CloudCircleIcon from "@material-ui/icons/CloudCircle"
|
|||
import { Link } from "react-router-dom"
|
||||
import React from "react"
|
||||
import * as Constants from "./constants"
|
||||
import * as API from "../../api"
|
||||
import * as Types from "../../api/types"
|
||||
import { WorkspaceSection } from "./WorkspaceSection"
|
||||
|
||||
export interface WorkspaceProps {
|
||||
organization: API.Organization
|
||||
workspace: API.Workspace
|
||||
project: API.Project
|
||||
organization: Types.Organization
|
||||
workspace: Types.Workspace
|
||||
project: Types.Project
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
import React from "react"
|
||||
import { SWRConfig } from "swr"
|
||||
import { screen, waitFor } from "@testing-library/react"
|
||||
import { User, UserProvider, useUser } from "./UserContext"
|
||||
import { history, MockUser, render } from "../test_helpers"
|
||||
|
||||
namespace Helpers {
|
||||
// Helper component that renders out the state of the `useUser` hook.
|
||||
// It just renders simple text in the 'error', 'me', and 'loading' states,
|
||||
// so that the test can get a peak at the state of the hook.
|
||||
const TestComponent: React.FC<{ redirectOnFailure: boolean }> = ({ redirectOnFailure }) => {
|
||||
const { me, error } = useUser(redirectOnFailure)
|
||||
|
||||
if (error) {
|
||||
return <div>{`Error: ${error.toString()}`}</div>
|
||||
}
|
||||
if (me) {
|
||||
return <div>{`Me: ${me.toString()}`}</div>
|
||||
}
|
||||
|
||||
return <div>Loading</div>
|
||||
}
|
||||
|
||||
// Helper to render a userContext, and all the scaffolding needed
|
||||
// (an SWRConfig as well as a UserPRovider)
|
||||
export const renderUserContext = (
|
||||
simulatedRequest: () => Promise<User>,
|
||||
redirectOnFailure: boolean,
|
||||
): React.ReactElement => {
|
||||
return (
|
||||
// Set up an SWRConfig that works for testing - we'll simulate a request,
|
||||
// and set up the cache to reset every test.
|
||||
<SWRConfig
|
||||
value={{
|
||||
fetcher: simulatedRequest,
|
||||
// Reset cache for every test. Without this, requests will be cached between test cases.
|
||||
provider: () => new Map(),
|
||||
}}
|
||||
>
|
||||
<UserProvider>
|
||||
<TestComponent redirectOnFailure={redirectOnFailure} />
|
||||
</UserProvider>
|
||||
</SWRConfig>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
describe("UserContext", () => {
|
||||
const failingRequest = () => Promise.reject("Failed to load user")
|
||||
const successfulRequest = () => Promise.resolve(MockUser)
|
||||
|
||||
// Reset the router to '/' before every test
|
||||
beforeEach(() => {
|
||||
history.replace("/")
|
||||
})
|
||||
|
||||
it("shouldn't redirect if user fails to load and redirectOnFailure is false", async () => {
|
||||
// When
|
||||
render(Helpers.renderUserContext(failingRequest, false))
|
||||
|
||||
// Then
|
||||
// Verify we get an error message
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText("Error:", { exact: false })).toBeDefined()
|
||||
})
|
||||
// ...and the route should be unchanged
|
||||
expect(history.location.pathname).toEqual("/")
|
||||
expect(history.location.search).toEqual("")
|
||||
})
|
||||
|
||||
it("should redirect if user fails to load and redirectOnFailure is true", async () => {
|
||||
// When
|
||||
render(Helpers.renderUserContext(failingRequest, true))
|
||||
|
||||
// Then
|
||||
// Verify we route to the login page
|
||||
await waitFor(() => expect(history.location.pathname).toEqual("/login"))
|
||||
await waitFor(() => expect(history.location.search).toEqual("?redirect=%2F"))
|
||||
})
|
||||
|
||||
it("should not redirect if user loads and redirectOnFailure is true", async () => {
|
||||
// When
|
||||
render(Helpers.renderUserContext(successfulRequest, true))
|
||||
|
||||
// Then
|
||||
// Verify the user is rendered
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText("Me:", { exact: false })).toBeDefined()
|
||||
})
|
||||
// ...and the route should be unchanged
|
||||
expect(history.location.pathname).toEqual("/")
|
||||
expect(history.location.search).toEqual("")
|
||||
})
|
||||
})
|
|
@ -1,73 +0,0 @@
|
|||
import { useLocation, useNavigate } from "react-router-dom"
|
||||
import React, { useContext, useEffect } from "react"
|
||||
import useSWR from "swr"
|
||||
|
||||
import * as API from "../api"
|
||||
|
||||
export interface User {
|
||||
readonly id: string
|
||||
readonly username: string
|
||||
readonly email: string
|
||||
readonly created_at: string
|
||||
}
|
||||
|
||||
export interface UserContext {
|
||||
readonly error?: Error
|
||||
readonly me?: User
|
||||
readonly signOut: () => Promise<void>
|
||||
}
|
||||
|
||||
const UserContext = React.createContext<UserContext>({
|
||||
signOut: () => {
|
||||
return Promise.reject("Sign out API not available")
|
||||
},
|
||||
})
|
||||
|
||||
export const useUser = (redirectOnError = false): UserContext => {
|
||||
const ctx = useContext(UserContext)
|
||||
const navigate = useNavigate()
|
||||
const { pathname } = useLocation()
|
||||
|
||||
const requestError = ctx.error
|
||||
useEffect(() => {
|
||||
if (redirectOnError && requestError) {
|
||||
navigate({
|
||||
pathname: "/login",
|
||||
search: "?redirect=" + encodeURIComponent(pathname),
|
||||
})
|
||||
}
|
||||
// Disabling exhaustive deps here because it can cause an
|
||||
// infinite useEffect loop. Should (hopefully) go away
|
||||
// when we switch to an alternate routing strategy.
|
||||
}, [redirectOnError, requestError]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
export const UserProvider: React.FC = (props) => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const { data, error, mutate } = useSWR("/api/v2/users/me")
|
||||
|
||||
const signOut = async () => {
|
||||
await API.logout()
|
||||
// Tell SWR to invalidate the cache for the user endpoint
|
||||
await mutate("/api/v2/users/me")
|
||||
navigate({
|
||||
pathname: "/login",
|
||||
search: "?redirect=" + encodeURIComponent(location.pathname),
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
error: error,
|
||||
me: data,
|
||||
signOut: signOut,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</UserContext.Provider>
|
||||
)
|
||||
}
|
|
@ -13,7 +13,7 @@ import {
|
|||
FormCloseButton,
|
||||
} from "../components/Form"
|
||||
import { LoadingButton } from "../components/Button"
|
||||
import { Organization, Project, Provisioner, CreateProjectRequest } from "./../api"
|
||||
import { Organization, Project, Provisioner, CreateProjectRequest } from "../api/types"
|
||||
|
||||
export interface CreateProjectFormProps {
|
||||
provisioners: Provisioner[]
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as Yup from "yup"
|
|||
|
||||
import { FormCloseButton, FormTextField, FormTitle, FormSection } from "../components/Form"
|
||||
import { LoadingButton } from "../components/Button"
|
||||
import { Project, Workspace, CreateWorkspaceRequest } from "../api"
|
||||
import { Project, Workspace, CreateWorkspaceRequest } from "../api/types"
|
||||
|
||||
export interface CreateWorkspaceForm {
|
||||
project: Project
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import React, { useEffect, useState } from "react"
|
||||
import React, { useContext, useEffect, useState } from "react"
|
||||
import { getApiKey } from "../api"
|
||||
import { CliAuthToken } from "../components/SignIn"
|
||||
|
||||
import { FullScreenLoader } from "../components/Loader/FullScreenLoader"
|
||||
import { useUser } from "../contexts/UserContext"
|
||||
import { useActor } from "@xstate/react"
|
||||
import { XServiceContext } from "../xServices/StateContext"
|
||||
|
||||
export const CliAuthenticationPage: React.FC = () => {
|
||||
const { me } = useUser(true)
|
||||
const xServices = useContext(XServiceContext)
|
||||
const [userState] = useActor(xServices.userXService)
|
||||
const { me } = userState.context
|
||||
|
||||
const styles = useStyles()
|
||||
|
||||
const [apiKey, setApiKey] = useState<string | null>(null)
|
||||
|
|
|
@ -1,16 +1,7 @@
|
|||
import React from "react"
|
||||
|
||||
import { Navigate } from "react-router-dom"
|
||||
import { FullScreenLoader } from "../components/Loader/FullScreenLoader"
|
||||
import { useUser } from "../contexts/UserContext"
|
||||
|
||||
export const IndexPage: React.FC = () => {
|
||||
const { me } = useUser(/* redirectOnError */ true)
|
||||
|
||||
if (me) {
|
||||
// Once the user is logged in, just redirect them to /projects as the landing page
|
||||
return <Navigate to="/projects" replace />
|
||||
}
|
||||
|
||||
return <FullScreenLoader />
|
||||
return <Navigate to="/projects" replace />
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import React from "react"
|
||||
import { act, fireEvent, screen } from "@testing-library/react"
|
||||
import { history, render } from "../test_helpers"
|
||||
import { SignInPage } from "./login"
|
||||
import { server } from "../test_helpers/server"
|
||||
import { rest } from "msw"
|
||||
|
||||
describe("SignInPage", () => {
|
||||
beforeEach(() => {
|
||||
history.replace("/login")
|
||||
// appear logged out
|
||||
server.use(
|
||||
rest.get("/api/v2/users/me", (req, res, ctx) => {
|
||||
return res(ctx.status(401), ctx.json({ message: "no user here" }))
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it("renders the sign-in form", async () => {
|
||||
// When
|
||||
render(<SignInPage />)
|
||||
|
||||
// Then
|
||||
await screen.findByText("Sign In", { exact: false })
|
||||
})
|
||||
|
||||
it("shows an error message if SignIn fails", async () => {
|
||||
// Given
|
||||
const { container } = render(<SignInPage />)
|
||||
// Make login fail
|
||||
server.use(
|
||||
rest.post("/api/v2/users/login", async (req, res, ctx) => {
|
||||
return res(ctx.status(500), ctx.json({ message: "nope" }))
|
||||
}),
|
||||
)
|
||||
|
||||
// When
|
||||
// Set username / password
|
||||
const [username, password] = container.querySelectorAll("input")
|
||||
fireEvent.change(username, { target: { value: "test@coder.com" } })
|
||||
fireEvent.change(password, { target: { value: "password" } })
|
||||
// Click sign-in
|
||||
const signInButton = await screen.findByText("Sign In")
|
||||
act(() => signInButton.click())
|
||||
|
||||
// Then
|
||||
// Finding error by test id because it comes from the backend
|
||||
const errorMessage = await screen.findByTestId("sign-in-error")
|
||||
expect(errorMessage).toBeDefined()
|
||||
expect(history.location.pathname).toEqual("/login")
|
||||
})
|
||||
})
|
|
@ -1,6 +1,10 @@
|
|||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import React from "react"
|
||||
import { useActor } from "@xstate/react"
|
||||
import React, { useContext } from "react"
|
||||
import { SignInForm } from "./../components/SignIn"
|
||||
import { Navigate, useLocation } from "react-router-dom"
|
||||
import { Location } from "history"
|
||||
import { XServiceContext } from "../xServices/StateContext"
|
||||
|
||||
export const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
|
@ -16,13 +20,36 @@ export const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
}))
|
||||
|
||||
const getRedirectFromLocation = (location: Location) => {
|
||||
const defaultRedirect = "/"
|
||||
|
||||
const searchParams = new URLSearchParams(location.search)
|
||||
const redirect = searchParams.get("redirect")
|
||||
return redirect ? redirect : defaultRedirect
|
||||
}
|
||||
|
||||
export const SignInPage: React.FC = () => {
|
||||
const styles = useStyles()
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.container}>
|
||||
<SignInForm />
|
||||
const location = useLocation()
|
||||
const xServices = useContext(XServiceContext)
|
||||
const [userState, userSend] = useActor(xServices.userXService)
|
||||
const isLoading = userState.hasTag("loading")
|
||||
const redirectTo = getRedirectFromLocation(location)
|
||||
const authErrorMessage = userState.context.authError ? (userState.context.authError as Error).message : undefined
|
||||
|
||||
const onSubmit = async ({ email, password }: { email: string; password: string }) => {
|
||||
userSend({ type: "SIGN_IN", email, password })
|
||||
}
|
||||
|
||||
if (userState.matches("signedIn")) {
|
||||
return <Navigate to={redirectTo} replace />
|
||||
} else {
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.container}>
|
||||
<SignInForm isLoading={isLoading} authErrorMessage={authErrorMessage} onSubmit={onSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ import { makeStyles } from "@material-ui/core/styles"
|
|||
import { useNavigate, useParams } from "react-router-dom"
|
||||
import useSWR from "swr"
|
||||
|
||||
import * as Types from "../../../../api/types"
|
||||
import * as API from "../../../../api"
|
||||
import { useUser } from "../../../../contexts/UserContext"
|
||||
import { ErrorSummary } from "../../../../components/ErrorSummary"
|
||||
import { FullScreenLoader } from "../../../../components/Loader/FullScreenLoader"
|
||||
import { CreateWorkspaceForm } from "../../../../forms/CreateWorkspaceForm"
|
||||
|
@ -14,13 +14,12 @@ export const CreateWorkspacePage: React.FC = () => {
|
|||
const { organization: organizationName, project: projectName } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const styles = useStyles()
|
||||
const { me } = useUser(/* redirectOnError */ true)
|
||||
|
||||
const { data: organizationInfo, error: organizationError } = useSWR<API.Organization, Error>(
|
||||
const { data: organizationInfo, error: organizationError } = useSWR<Types.Organization, Error>(
|
||||
() => `/api/v2/users/me/organizations/${organizationName}`,
|
||||
)
|
||||
|
||||
const { data: project, error: projectError } = useSWR<API.Project, Error>(() => {
|
||||
const { data: project, error: projectError } = useSWR<Types.Project, Error>(() => {
|
||||
return `/api/v2/organizations/${unsafeSWRArgument(organizationInfo).id}/projects/${projectName}`
|
||||
})
|
||||
|
||||
|
@ -28,7 +27,7 @@ export const CreateWorkspacePage: React.FC = () => {
|
|||
navigate(`/projects/${organizationName}/${projectName}`)
|
||||
}, [navigate, organizationName, projectName])
|
||||
|
||||
const onSubmit = async (req: API.CreateWorkspaceRequest) => {
|
||||
const onSubmit = async (req: Types.CreateWorkspaceRequest) => {
|
||||
const workspace = await API.Workspace.create(req)
|
||||
navigate(`/workspaces/${workspace.id}`)
|
||||
return workspace
|
||||
|
@ -42,7 +41,7 @@ export const CreateWorkspacePage: React.FC = () => {
|
|||
return <ErrorSummary error={projectError} />
|
||||
}
|
||||
|
||||
if (!me || !project) {
|
||||
if (!project) {
|
||||
return <FullScreenLoader />
|
||||
}
|
||||
|
||||
|
|
|
@ -4,13 +4,11 @@ import Paper from "@material-ui/core/Paper"
|
|||
import { Link, useNavigate, useParams } from "react-router-dom"
|
||||
import useSWR from "swr"
|
||||
|
||||
import { Organization, Project, Workspace } from "../../../../api"
|
||||
import { Organization, Project, Workspace } from "../../../../api/types"
|
||||
import { Header } from "../../../../components/Header"
|
||||
import { FullScreenLoader } from "../../../../components/Loader/FullScreenLoader"
|
||||
import { Navbar } from "../../../../components/Navbar"
|
||||
import { Footer } from "../../../../components/Page"
|
||||
import { Column, Table } from "../../../../components/Table"
|
||||
import { useUser } from "../../../../contexts/UserContext"
|
||||
import { ErrorSummary } from "../../../../components/ErrorSummary"
|
||||
import { firstOrItem } from "../../../../util/array"
|
||||
import { EmptyState } from "../../../../components/EmptyState"
|
||||
|
@ -18,7 +16,6 @@ import { unsafeSWRArgument } from "../../../../util"
|
|||
|
||||
export const ProjectPage: React.FC = () => {
|
||||
const styles = useStyles()
|
||||
const { me, signOut } = useUser(true)
|
||||
const navigate = useNavigate()
|
||||
const { project: projectName, organization: organizationName } = useParams()
|
||||
|
||||
|
@ -47,7 +44,7 @@ export const ProjectPage: React.FC = () => {
|
|||
return <ErrorSummary error={workspacesError} />
|
||||
}
|
||||
|
||||
if (!me || !projectInfo || !workspaces) {
|
||||
if (!projectInfo || !workspaces) {
|
||||
return <FullScreenLoader />
|
||||
}
|
||||
|
||||
|
@ -89,7 +86,6 @@ export const ProjectPage: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Navbar user={me} onSignOut={signOut} />
|
||||
<Header
|
||||
title={firstOrItem(projectName, "")}
|
||||
description={firstOrItem(organizationName, "")}
|
||||
|
|
|
@ -4,20 +4,17 @@ import Paper from "@material-ui/core/Paper"
|
|||
import { Link } from "react-router-dom"
|
||||
import { EmptyState } from "../../components"
|
||||
import { ErrorSummary } from "../../components/ErrorSummary"
|
||||
import { Navbar } from "../../components/Navbar"
|
||||
import { Header } from "../../components/Header"
|
||||
import { Footer } from "../../components/Page"
|
||||
import { Column, Table } from "../../components/Table"
|
||||
import { useUser } from "../../contexts/UserContext"
|
||||
import { FullScreenLoader } from "../../components/Loader/FullScreenLoader"
|
||||
|
||||
import { Organization, Project } from "./../../api"
|
||||
import { Organization, Project } from "../../api/types"
|
||||
import useSWR from "swr"
|
||||
import { CodeExample } from "../../components/CodeExample/CodeExample"
|
||||
|
||||
export const ProjectsPage: React.FC = () => {
|
||||
const styles = useStyles()
|
||||
const { me, signOut } = useUser(true)
|
||||
const { data: orgs, error: orgsError } = useSWR<Organization[], Error>("/api/v2/users/me/organizations")
|
||||
const { data: projects, error } = useSWR<Project[] | null, Error>(
|
||||
orgs ? `/api/v2/organizations/${orgs[0].id}/projects` : null,
|
||||
|
@ -31,7 +28,7 @@ export const ProjectsPage: React.FC = () => {
|
|||
return <ErrorSummary error={error} />
|
||||
}
|
||||
|
||||
if (!me || !projects || !orgs) {
|
||||
if (!projects || !orgs) {
|
||||
return <FullScreenLoader />
|
||||
}
|
||||
|
||||
|
@ -74,7 +71,6 @@ export const ProjectsPage: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Navbar user={me} onSignOut={signOut} />
|
||||
<Header title="Projects" subTitle={subTitle} />
|
||||
<Paper style={{ maxWidth: "1380px", margin: "1em auto", width: "100%" }}>
|
||||
<Table {...tableProps} />
|
||||
|
|
|
@ -2,33 +2,30 @@ import React from "react"
|
|||
import useSWR from "swr"
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { useParams } from "react-router-dom"
|
||||
import { Navbar } from "../../components/Navbar"
|
||||
import { Footer } from "../../components/Page"
|
||||
import { useUser } from "../../contexts/UserContext"
|
||||
import { firstOrItem } from "../../util/array"
|
||||
import { ErrorSummary } from "../../components/ErrorSummary"
|
||||
import { FullScreenLoader } from "../../components/Loader/FullScreenLoader"
|
||||
import { Workspace } from "../../components/Workspace"
|
||||
import { unsafeSWRArgument } from "../../util"
|
||||
import * as API from "../../api"
|
||||
import * as Types from "../../api/types"
|
||||
|
||||
export const WorkspacePage: React.FC = () => {
|
||||
const styles = useStyles()
|
||||
const { workspace: workspaceQueryParam } = useParams()
|
||||
const { me, signOut } = useUser(true)
|
||||
|
||||
const { data: workspace, error: workspaceError } = useSWR<API.Workspace, Error>(() => {
|
||||
const { data: workspace, error: workspaceError } = useSWR<Types.Workspace, Error>(() => {
|
||||
const workspaceParam = firstOrItem(workspaceQueryParam, null)
|
||||
|
||||
return `/api/v2/workspaces/${workspaceParam}`
|
||||
})
|
||||
|
||||
// Fetch parent project
|
||||
const { data: project, error: projectError } = useSWR<API.Project, Error>(() => {
|
||||
const { data: project, error: projectError } = useSWR<Types.Project, Error>(() => {
|
||||
return `/api/v2/projects/${unsafeSWRArgument(workspace).project_id}`
|
||||
})
|
||||
|
||||
const { data: organization, error: organizationError } = useSWR<API.Project, Error>(() => {
|
||||
const { data: organization, error: organizationError } = useSWR<Types.Project, Error>(() => {
|
||||
return `/api/v2/organizations/${unsafeSWRArgument(project).organization_id}`
|
||||
})
|
||||
|
||||
|
@ -44,14 +41,12 @@ export const WorkspacePage: React.FC = () => {
|
|||
return <ErrorSummary error={organizationError} />
|
||||
}
|
||||
|
||||
if (!me || !workspace || !project || !organization) {
|
||||
if (!workspace || !project || !organization) {
|
||||
return <FullScreenLoader />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<Navbar user={me} onSignOut={signOut} />
|
||||
|
||||
<div className={styles.inner}>
|
||||
<Workspace organization={organization} project={project} workspace={workspace} />
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { User } from "../contexts/UserContext"
|
||||
import { Provisioner, Organization, Project, Workspace } from "../api"
|
||||
import { Provisioner, Organization, Project, Workspace, UserResponse } from "../api/types"
|
||||
|
||||
export const MockUser: User = {
|
||||
export const MockSessionToken = { session_token: "my-session-token" }
|
||||
|
||||
export const MockAPIKey = { key: "my-api-key" }
|
||||
|
||||
export const MockUser: UserResponse = {
|
||||
id: "test-user-id",
|
||||
username: "TestUser",
|
||||
email: "test@coder.com",
|
|
@ -0,0 +1,20 @@
|
|||
import { rest } from "msw"
|
||||
import * as M from "./entities"
|
||||
|
||||
export const handlers = [
|
||||
rest.post("/api/v2/users/me/workspaces", async (req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(M.MockWorkspace))
|
||||
}),
|
||||
rest.post("/api/v2/users/login", async (req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(M.MockSessionToken))
|
||||
}),
|
||||
rest.post("/api/v2/users/logout", async (req, res, ctx) => {
|
||||
return res(ctx.status(200))
|
||||
}),
|
||||
rest.get("/api/v2/users/me", async (req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(M.MockUser))
|
||||
}),
|
||||
rest.get("/api/v2/users/me/keys", async (req, res, ctx) => {
|
||||
return res(ctx.status(200), ctx.json(M.MockAPIKey))
|
||||
}),
|
||||
]
|
|
@ -5,13 +5,16 @@ import ThemeProvider from "@material-ui/styles/ThemeProvider"
|
|||
import { dark } from "../theme"
|
||||
import { createMemoryHistory } from "history"
|
||||
import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom"
|
||||
import { XServiceProvider } from "../xServices/StateContext"
|
||||
|
||||
export const history = createMemoryHistory()
|
||||
|
||||
export const WrapperComponent: React.FC = ({ children }) => {
|
||||
return (
|
||||
<HistoryRouter history={history}>
|
||||
<ThemeProvider theme={dark}>{children}</ThemeProvider>
|
||||
<XServiceProvider>
|
||||
<ThemeProvider theme={dark}>{children}</ThemeProvider>
|
||||
</XServiceProvider>
|
||||
</HistoryRouter>
|
||||
)
|
||||
}
|
||||
|
@ -20,4 +23,4 @@ export const render = (component: React.ReactElement): RenderResult => {
|
|||
return wrappedRender(<WrapperComponent>{component}</WrapperComponent>)
|
||||
}
|
||||
|
||||
export * from "./mocks"
|
||||
export * from "./entities"
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import { setupServer } from "msw/node"
|
||||
import { handlers } from "./handlers"
|
||||
|
||||
// This configures a request mocking server with the given request handlers.
|
||||
export const server = setupServer(...handlers)
|
|
@ -0,0 +1,24 @@
|
|||
import React, { createContext } from "react"
|
||||
import { useInterpret } from "@xstate/react"
|
||||
import { ActorRefFrom } from "xstate"
|
||||
import { userMachine } from "./user/userXService"
|
||||
|
||||
interface XServiceContextType {
|
||||
userXService: ActorRefFrom<typeof userMachine>
|
||||
}
|
||||
|
||||
/**
|
||||
* Consuming this Context will not automatically cause rerenders because
|
||||
* the xServices in it are static references.
|
||||
*
|
||||
* To use one of the xServices, `useActor` will access all its state
|
||||
* (causing re-renders for any changes to that one xService) and
|
||||
* `useSelector` will access just one piece of state.
|
||||
*/
|
||||
export const XServiceContext = createContext({} as XServiceContextType)
|
||||
|
||||
export const XServiceProvider: React.FC = ({ children }) => {
|
||||
const userXService = useInterpret(userMachine, { devTools: true })
|
||||
|
||||
return <XServiceContext.Provider value={{ userXService }}>{children}</XServiceContext.Provider>
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
import { createMachine, assign } from "xstate"
|
||||
import * as Types from "../../api/types"
|
||||
import * as API from "../../api"
|
||||
|
||||
export interface UserContext {
|
||||
getUserError?: Error | unknown // unknown is a concession while I work out typing issues
|
||||
authError?: Error | unknown
|
||||
me?: Types.UserResponse
|
||||
}
|
||||
|
||||
export type UserEvent = { type: "SIGN_OUT" } | { type: "SIGN_IN"; email: string; password: string }
|
||||
|
||||
export const userMachine =
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QFdZgE4GUAuBDbYAdLAJZQB2kA8stgMSYCSA4gHID6jrioADgPalsJfuR4gAHogDM0gJyEATAHYAbNIAscxdunKArKo0AaEAE9EG+YX0BGABz3FD6atsbbc5QF9vp1Bg4+ESkFCTkUIzkdBCiROEAbvwA1iFk5FHiAkIiYkiSiHIADITSOvbSRRqq2m7StqYWCIr2+ja2tvVV+vqaRVW+-mhYeATE6eGR0Rjo-OiEvAA2+ABmcwC24xSZ+dkkwqLiUghlyoRF+opayq4X+sqK+o2Il6qEcq5y+hrfjt+qgxAARGwUIMGwwgiAFVhjE4oREikiOCALJgLKCfa5I4yIyEZTFZQaezFK5lPTPBC2IqKUqKC4EjqqKrFOSA4FBMbgyFQGEYOgzOYLZbYNboTao9G7TEHPKgY7SewlTQ3dRyVQ6GpVSmKMqEIz2ZRKjrKIqqJzs4actIUSBRBgsDhUKEAFQxOUO+WOhvshA0ahualsqnu8kpthUhGplVUIa+rUq9ktgVGNvIkxo9FilAR5CSqS25Ez7qxnvliCMGlK1Tk6vN5p+T3ML0Ub2U7iKXmcGg8tmTILGoXTEUzAvQs3mS1WG0LxelHrlBQQIbasc7aiKtnbV3sOpaUZXBjkwd1A0B5H4EDg4g5qcL1FoJdlOIQdlpH2qhmJzJ+DWbCB7WxSmUIl2wJM0CSTPwgStO8h0mHY+BlbEvReICaSMbQflkU0fkpHtfS3MC3C+aR9ENfR+2tMEwAhSY+XQJ8UPLACziVS5TXKAxPDI8MaSjIojSqPQezNRUqLg9I7UXPZn1Q5dqn1CMNEEi4HAecNIyVGoIweVQDH0tloNvUF4JHR951LRdvQcc4PCcAx12-fDOnOeRTU7C4SXsPtjNg4ImLLJddUIdiVBpOQKJ4psmh0ASQOPRUiI8RRfF8IA */
|
||||
createMachine(
|
||||
{
|
||||
tsTypes: {} as import("./userXService.typegen").Typegen0,
|
||||
schema: {
|
||||
context: {} as UserContext,
|
||||
events: {} as UserEvent,
|
||||
services: {} as {
|
||||
getMe: {
|
||||
data: Types.UserResponse
|
||||
}
|
||||
signIn: {
|
||||
data: Types.LoginResponse | undefined
|
||||
}
|
||||
},
|
||||
},
|
||||
context: {
|
||||
me: undefined,
|
||||
getUserError: undefined,
|
||||
authError: undefined,
|
||||
},
|
||||
id: "userState",
|
||||
initial: "gettingUser",
|
||||
states: {
|
||||
signedOut: {
|
||||
on: {
|
||||
SIGN_IN: {
|
||||
target: "#userState.signingIn",
|
||||
},
|
||||
},
|
||||
},
|
||||
signingIn: {
|
||||
invoke: {
|
||||
src: "signIn",
|
||||
id: "signIn",
|
||||
onDone: [
|
||||
{
|
||||
target: "#userState.gettingUser",
|
||||
actions: "clearAuthError",
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: "assignAuthError",
|
||||
target: "#userState.signedOut",
|
||||
},
|
||||
],
|
||||
},
|
||||
tags: "loading",
|
||||
},
|
||||
gettingUser: {
|
||||
invoke: {
|
||||
src: "getMe",
|
||||
id: "getMe",
|
||||
onDone: [
|
||||
{
|
||||
actions: ["assignMe", "clearGetUserError"],
|
||||
target: "#userState.signedIn",
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: "assignGetUserError",
|
||||
target: "#userState.signedOut",
|
||||
},
|
||||
],
|
||||
},
|
||||
tags: "loading",
|
||||
},
|
||||
signedIn: {
|
||||
on: {
|
||||
SIGN_OUT: {
|
||||
target: "#userState.signingOut",
|
||||
},
|
||||
},
|
||||
},
|
||||
signingOut: {
|
||||
invoke: {
|
||||
src: "signOut",
|
||||
id: "signOut",
|
||||
onDone: [
|
||||
{
|
||||
actions: ["unassignMe", "clearAuthError"],
|
||||
target: "#userState.signedOut",
|
||||
},
|
||||
],
|
||||
onError: [
|
||||
{
|
||||
actions: "assignAuthError",
|
||||
target: "#userState.signedIn",
|
||||
},
|
||||
],
|
||||
},
|
||||
tags: "loading",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
services: {
|
||||
signIn: async (_, event: UserEvent) => {
|
||||
if (event.type === "SIGN_IN") {
|
||||
return await API.login(event.email, event.password)
|
||||
}
|
||||
},
|
||||
signOut: API.logout,
|
||||
getMe: API.getUser,
|
||||
},
|
||||
actions: {
|
||||
assignMe: assign({
|
||||
me: (_, event) => event.data,
|
||||
}),
|
||||
unassignMe: assign((context: UserContext) => ({
|
||||
...context,
|
||||
me: undefined,
|
||||
})),
|
||||
assignGetUserError: assign({
|
||||
getUserError: (_, event) => event.data,
|
||||
}),
|
||||
clearGetUserError: assign((context: UserContext) => ({
|
||||
...context,
|
||||
getUserError: undefined,
|
||||
})),
|
||||
assignAuthError: assign({
|
||||
authError: (_, event) => event.data,
|
||||
}),
|
||||
clearAuthError: assign((context: UserContext) => ({
|
||||
...context,
|
||||
authError: undefined,
|
||||
})),
|
||||
},
|
||||
},
|
||||
)
|
389
site/yarn.lock
389
site/yarn.lock
|
@ -1658,6 +1658,26 @@
|
|||
call-me-maybe "^1.0.1"
|
||||
glob-to-regexp "^0.3.0"
|
||||
|
||||
"@mswjs/cookies@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-0.2.0.tgz#7ef2b5d7e444498bb27cf57720e61f76a4ce9f23"
|
||||
integrity sha512-GTKYnIfXVP8GL8HRWrse+ujqDXCLKvu7+JoL6pvZFzS/d2i9pziByoWD69cOe35JNoSrx2DPNqrhUF+vgV3qUA==
|
||||
dependencies:
|
||||
"@types/set-cookie-parser" "^2.4.0"
|
||||
set-cookie-parser "^2.4.6"
|
||||
|
||||
"@mswjs/interceptors@^0.15.1":
|
||||
version "0.15.1"
|
||||
resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.15.1.tgz#4a0009f56e51bc2cd3176f1507065c7d2f6c0d5e"
|
||||
integrity sha512-D5B+ZJNlfvBm6ZctAfRBdNJdCHYAe2Ix4My5qfbHV5WH+3lkt3mmsjiWJzEh5ZwGDauzY487TldI275If7DJVw==
|
||||
dependencies:
|
||||
"@open-draft/until" "^1.0.3"
|
||||
"@xmldom/xmldom" "^0.7.5"
|
||||
debug "^4.3.3"
|
||||
headers-polyfill "^3.0.4"
|
||||
outvariant "^1.2.1"
|
||||
strict-event-emitter "^0.2.0"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
|
@ -1700,6 +1720,11 @@
|
|||
mkdirp "^1.0.4"
|
||||
rimraf "^3.0.2"
|
||||
|
||||
"@open-draft/until@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca"
|
||||
integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==
|
||||
|
||||
"@playwright/test@1.20.0":
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.20.0.tgz#df5b1b45976d11c365e6cb60f8ec1ca7cccb84cf"
|
||||
|
@ -2812,6 +2837,11 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/cookie@^0.4.1":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
|
||||
integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==
|
||||
|
||||
"@types/cookiejar@*":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.2.tgz#66ad9331f63fe8a3d3d9d8c6e3906dd10f6446e8"
|
||||
|
@ -2920,7 +2950,7 @@
|
|||
dependencies:
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/jest@27.4.1":
|
||||
"@types/jest@^27.4.1":
|
||||
version "27.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d"
|
||||
integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==
|
||||
|
@ -2928,6 +2958,11 @@
|
|||
jest-matcher-utils "^27.0.0"
|
||||
pretty-format "^27.0.0"
|
||||
|
||||
"@types/js-levenshtein@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz#ba05426a43f9e4e30b631941e0aa17bf0c890ed5"
|
||||
integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==
|
||||
|
||||
"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
version "7.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
|
||||
|
@ -3083,6 +3118,13 @@
|
|||
"@types/mime" "^1"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/set-cookie-parser@^2.4.0":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz#b6a955219b54151bfebd4521170723df5e13caad"
|
||||
integrity sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/sockjs@^0.3.33":
|
||||
version "0.3.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f"
|
||||
|
@ -3604,6 +3646,43 @@
|
|||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe"
|
||||
integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw==
|
||||
|
||||
"@xmldom/xmldom@^0.7.5":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d"
|
||||
integrity sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A==
|
||||
|
||||
"@xstate/cli@^0.1.4":
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@xstate/cli/-/cli-0.1.4.tgz#5d909b980a5e62744f90b2790be3aa2717cfb4f8"
|
||||
integrity sha512-+MkeFGi+gouY8o+/GWG7/62c4WZAtsMlABi5NFBN2owIbwohJQbflc9jBMzN+U3Ho0QGC2gHBwsretsSsNkuuA==
|
||||
dependencies:
|
||||
"@babel/core" "^7.12.10"
|
||||
"@xstate/machine-extractor" "0.6.2"
|
||||
"@xstate/tools-shared" "1.1.2"
|
||||
chokidar "^3.5.3"
|
||||
commander "^8.0.0"
|
||||
xstate "^4.29.0"
|
||||
|
||||
"@xstate/machine-extractor@0.6.2":
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@xstate/machine-extractor/-/machine-extractor-0.6.2.tgz#2fe5edb6b965fd1f45fa68644a4ef69f125c4fc0"
|
||||
integrity sha512-zyDrBMDCpPestEpnWHwmJ42qLIVOqRVUKa491kmdix/vT8z/3P3Ib6MOSbD8lp2yaF49kIUDUCCkvQA6TcLRyA==
|
||||
|
||||
"@xstate/react@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@xstate/react/-/react-2.0.1.tgz#2b4717369d419e78a6c67f2dfcd1a3be9abce2d9"
|
||||
integrity sha512-sT3hxyzNBw+bm7uT3BP+uXzN0MnRqiaj/U9Yl4OYaMAUJXWsRvSA/ipL7EDf0gVLRGrRhJTCsC0cjWaduAAqnw==
|
||||
dependencies:
|
||||
use-isomorphic-layout-effect "^1.0.0"
|
||||
use-subscription "^1.3.0"
|
||||
|
||||
"@xstate/tools-shared@1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@xstate/tools-shared/-/tools-shared-1.1.2.tgz#84660e1ff9ba48612af2d471a21905705fa62e16"
|
||||
integrity sha512-/A0/3vI2N9Rr3uWKGpUkIv1GhVvQFsXB+vbf8RuSCcj5lnztnC2XEKCqzkric+T3BBViGVXuY7eh8UZw8OVPDw==
|
||||
dependencies:
|
||||
"@xstate/machine-extractor" "0.6.2"
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
|
||||
|
@ -4295,7 +4374,7 @@ base16@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
|
||||
integrity sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=
|
||||
|
||||
base64-js@^1.0.2:
|
||||
base64-js@^1.0.2, base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
@ -4352,6 +4431,15 @@ bindings@^1.5.0:
|
|||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bl@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
|
||||
integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
|
||||
dependencies:
|
||||
buffer "^5.5.0"
|
||||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
bluebird@^3.3.5, bluebird@^3.5.5:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
|
@ -4570,6 +4658,14 @@ buffer@^4.3.0:
|
|||
ieee754 "^1.1.4"
|
||||
isarray "^1.0.0"
|
||||
|
||||
buffer@^5.5.0:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
builtin-status-codes@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||
|
@ -4731,6 +4827,14 @@ ccount@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043"
|
||||
integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==
|
||||
|
||||
chalk@4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
|
||||
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.4.1:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
|
@ -4748,7 +4852,7 @@ chalk@^3.0.0:
|
|||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^4.0.0, chalk@^4.1.0:
|
||||
chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||
|
@ -4776,6 +4880,11 @@ character-reference-invalid@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
|
||||
integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==
|
||||
|
||||
chardet@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||
|
||||
chokidar@^2.1.8:
|
||||
version "2.1.8"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
|
||||
|
@ -4882,6 +4991,18 @@ cli-boxes@^2.2.1:
|
|||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
|
||||
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
|
||||
|
||||
cli-cursor@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
|
||||
integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
|
||||
dependencies:
|
||||
restore-cursor "^3.1.0"
|
||||
|
||||
cli-spinners@^2.5.0:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d"
|
||||
integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==
|
||||
|
||||
cli-table3@^0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.1.tgz#36ce9b7af4847f288d3cdd081fbd09bf7bd237b8"
|
||||
|
@ -4891,6 +5012,11 @@ cli-table3@^0.6.1:
|
|||
optionalDependencies:
|
||||
colors "1.4.0"
|
||||
|
||||
cli-width@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
||||
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
|
||||
|
||||
cliui@^7.0.2:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
|
||||
|
@ -4909,6 +5035,11 @@ clone-deep@^4.0.1:
|
|||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
clone@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
|
||||
|
||||
clsx@^1.0.2, clsx@^1.0.4, clsx@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||
|
@ -5036,7 +5167,7 @@ comma-separated-tokens@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea"
|
||||
integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==
|
||||
|
||||
commander@8.3.0, commander@^8.3.0:
|
||||
commander@8.3.0, commander@^8.0.0, commander@^8.3.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
||||
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
|
||||
|
@ -5160,7 +5291,7 @@ cookie-signature@1.0.6:
|
|||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
|
||||
|
||||
cookie@0.4.2:
|
||||
cookie@0.4.2, cookie@^0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
|
||||
|
@ -5466,7 +5597,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
|||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@4.3.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2:
|
||||
debug@4, debug@4.3.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3:
|
||||
version "4.3.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
|
||||
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
|
||||
|
@ -5555,6 +5686,13 @@ default-gateway@^6.0.3:
|
|||
dependencies:
|
||||
execa "^5.0.0"
|
||||
|
||||
defaults@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
|
||||
integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
|
||||
dependencies:
|
||||
clone "^1.0.2"
|
||||
|
||||
define-lazy-prop@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
|
||||
|
@ -6349,7 +6487,7 @@ eventemitter3@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
||||
|
||||
events@^3.0.0, events@^3.2.0:
|
||||
events@^3.0.0, events@^3.2.0, events@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||
|
@ -6491,6 +6629,15 @@ extend@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
|
||||
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
|
||||
|
||||
external-editor@^3.0.3:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
|
||||
integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
|
||||
dependencies:
|
||||
chardet "^0.7.0"
|
||||
iconv-lite "^0.4.24"
|
||||
tmp "^0.0.33"
|
||||
|
||||
extglob@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
|
||||
|
@ -6629,6 +6776,13 @@ figgy-pudding@^3.5.1:
|
|||
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
|
||||
integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
|
||||
|
||||
figures@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
|
||||
integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
|
||||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||
|
@ -7175,6 +7329,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
|
|||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
|
||||
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
|
||||
|
||||
graphql@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
|
||||
integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
|
||||
|
||||
gzip-size@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
|
||||
|
@ -7366,6 +7525,11 @@ he@^1.2.0:
|
|||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
headers-polyfill@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.0.4.tgz#cd70c815a441dd882372fcd6eda212ce997c9b18"
|
||||
integrity sha512-I1DOM1EdWYntdrnCvqQtcKwSSuiTzoqOExy4v1mdcFixFZABlWP4IPHdmoLtPda0abMHqDOY4H9svhQ10DFR4w==
|
||||
|
||||
hex-color-regex@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
|
||||
|
@ -7603,7 +7767,7 @@ hyphenate-style-name@^1.0.3:
|
|||
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
|
||||
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
|
||||
|
||||
iconv-lite@0.4.24:
|
||||
iconv-lite@0.4.24, iconv-lite@^0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
||||
|
@ -7617,7 +7781,7 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
|
|||
dependencies:
|
||||
postcss "^7.0.14"
|
||||
|
||||
ieee754@^1.1.4:
|
||||
ieee754@^1.1.13, ieee754@^1.1.4:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
@ -7696,6 +7860,26 @@ inline-style-parser@0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
|
||||
integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==
|
||||
|
||||
inquirer@^8.2.0:
|
||||
version "8.2.1"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.1.tgz#e00022e3e8930a92662f760f020686530a84671d"
|
||||
integrity sha512-pxhBaw9cyTFMjwKtkjePWDhvwzvrNGAw7En4hottzlPvz80GZaMZthdDU35aA6/f5FRZf3uhE057q8w1DE3V2g==
|
||||
dependencies:
|
||||
ansi-escapes "^4.2.1"
|
||||
chalk "^4.1.1"
|
||||
cli-cursor "^3.1.0"
|
||||
cli-width "^3.0.0"
|
||||
external-editor "^3.0.3"
|
||||
figures "^3.0.0"
|
||||
lodash "^4.17.21"
|
||||
mute-stream "0.0.8"
|
||||
ora "^5.4.1"
|
||||
run-async "^2.4.0"
|
||||
rxjs "^7.5.5"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
through "^2.3.6"
|
||||
|
||||
internal-slot@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
|
||||
|
@ -7965,6 +8149,11 @@ is-in-browser@^1.0.2, is-in-browser@^1.1.3:
|
|||
resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835"
|
||||
integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=
|
||||
|
||||
is-interactive@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
|
||||
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
|
||||
|
||||
is-map@^2.0.1, is-map@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
|
||||
|
@ -7975,6 +8164,11 @@ is-negative-zero@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
|
||||
integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
|
||||
|
||||
is-node-process@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.0.1.tgz#4fc7ac3a91e8aac58175fe0578abbc56f2831b23"
|
||||
integrity sha512-5IcdXuf++TTNt3oGl9EBdkvndXA8gmc4bz/Y+mdEpWh3Mcn/+kOw6hI7LD5CocqJWMzeb0I0ClndRVNdEPuJXQ==
|
||||
|
||||
is-number-object@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0"
|
||||
|
@ -8099,6 +8293,11 @@ is-typedarray@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||
|
||||
is-unicode-supported@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
|
||||
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
|
||||
|
||||
is-weakmap@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
|
||||
|
@ -8753,6 +8952,11 @@ jpeg-js@0.4.3:
|
|||
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b"
|
||||
integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==
|
||||
|
||||
js-levenshtein@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
|
||||
|
||||
js-string-escape@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
|
||||
|
@ -9156,6 +9360,14 @@ lodash@^4.0.1, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2
|
|||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
log-symbols@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
|
||||
integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
|
||||
dependencies:
|
||||
chalk "^4.1.0"
|
||||
is-unicode-supported "^0.1.0"
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
|
@ -9580,6 +9792,31 @@ ms@2.1.3, ms@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
msw@^0.39.2:
|
||||
version "0.39.2"
|
||||
resolved "https://registry.yarnpkg.com/msw/-/msw-0.39.2.tgz#832e9274db62c43cb79854d5a69dce031c700de8"
|
||||
integrity sha512-ju/HpqQpE4/qCxZ23t5Gaau0KREn4QuFzdG28nP1EpidMrymMJuIvNd32+2uGTGG031PMwrC41YW7vCxHOwyHA==
|
||||
dependencies:
|
||||
"@mswjs/cookies" "^0.2.0"
|
||||
"@mswjs/interceptors" "^0.15.1"
|
||||
"@open-draft/until" "^1.0.3"
|
||||
"@types/cookie" "^0.4.1"
|
||||
"@types/js-levenshtein" "^1.1.1"
|
||||
chalk "4.1.1"
|
||||
chokidar "^3.4.2"
|
||||
cookie "^0.4.2"
|
||||
graphql "^16.3.0"
|
||||
headers-polyfill "^3.0.4"
|
||||
inquirer "^8.2.0"
|
||||
is-node-process "^1.0.1"
|
||||
js-levenshtein "^1.1.6"
|
||||
node-fetch "^2.6.7"
|
||||
path-to-regexp "^6.2.0"
|
||||
statuses "^2.0.0"
|
||||
strict-event-emitter "^0.2.0"
|
||||
type-fest "^1.2.2"
|
||||
yargs "^17.3.1"
|
||||
|
||||
multicast-dns-service-types@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901"
|
||||
|
@ -9593,6 +9830,11 @@ multicast-dns@^6.0.1:
|
|||
dns-packet "^1.3.1"
|
||||
thunky "^1.0.2"
|
||||
|
||||
mute-stream@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
|
||||
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
|
||||
|
||||
nan@^2.12.1:
|
||||
version "2.15.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
|
||||
|
@ -9670,7 +9912,7 @@ node-dir@^0.1.10:
|
|||
dependencies:
|
||||
minimatch "^3.0.2"
|
||||
|
||||
node-fetch@2.6.7, node-fetch@^2.6.1:
|
||||
node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
||||
|
@ -9918,7 +10160,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
|||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
onetime@^5.1.2:
|
||||
onetime@^5.1.0, onetime@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
|
||||
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
|
||||
|
@ -9971,11 +10213,36 @@ optionator@^0.9.1:
|
|||
type-check "^0.4.0"
|
||||
word-wrap "^1.2.3"
|
||||
|
||||
ora@^5.4.1:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
|
||||
integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==
|
||||
dependencies:
|
||||
bl "^4.1.0"
|
||||
chalk "^4.1.0"
|
||||
cli-cursor "^3.1.0"
|
||||
cli-spinners "^2.5.0"
|
||||
is-interactive "^1.0.0"
|
||||
is-unicode-supported "^0.1.0"
|
||||
log-symbols "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
wcwidth "^1.0.1"
|
||||
|
||||
os-browserify@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
|
||||
integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
|
||||
|
||||
os-tmpdir@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
|
||||
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
|
||||
|
||||
outvariant@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.2.1.tgz#e630f6cdc1dbf398ed857e36f219de4a005ccd35"
|
||||
integrity sha512-bcILvFkvpMXh66+Ubax/inxbKRyWTUiiFIW2DWkiS79wakrLGn3Ydy+GvukadiyfZjaL6C7YhIem4EZSM282wA==
|
||||
|
||||
overlayscrollbars@^1.13.1:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/overlayscrollbars/-/overlayscrollbars-1.13.1.tgz#0b840a88737f43a946b9d87875a2f9e421d0338a"
|
||||
|
@ -10230,6 +10497,11 @@ path-to-regexp@0.1.7:
|
|||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||
|
||||
path-to-regexp@^6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38"
|
||||
integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==
|
||||
|
||||
path-type@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
|
||||
|
@ -11026,7 +11298,7 @@ read-pkg@^5.2.0:
|
|||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
readable-stream@^3.0.6, readable-stream@^3.6.0:
|
||||
readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
|
||||
|
@ -11305,6 +11577,14 @@ resolve@^2.0.0-next.3:
|
|||
is-core-module "^2.2.0"
|
||||
path-parse "^1.0.6"
|
||||
|
||||
restore-cursor@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
|
||||
integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
|
||||
dependencies:
|
||||
onetime "^5.1.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
ret@~0.1.10:
|
||||
version "0.1.15"
|
||||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||
|
@ -11367,6 +11647,11 @@ rsvp@^4.8.4:
|
|||
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
|
||||
integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
|
||||
|
||||
run-async@^2.4.0:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
|
||||
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
|
||||
|
||||
run-parallel@^1.1.9:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
|
||||
|
@ -11381,6 +11666,13 @@ run-queue@^1.0.0, run-queue@^1.0.3:
|
|||
dependencies:
|
||||
aproba "^1.1.1"
|
||||
|
||||
rxjs@^7.5.5:
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f"
|
||||
integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==
|
||||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
safe-buffer@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
|
||||
|
@ -11597,6 +11889,11 @@ set-blocking@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||
|
||||
set-cookie-parser@^2.4.6:
|
||||
version "2.4.8"
|
||||
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz#d0da0ed388bc8f24e706a391f9c9e252a13c58b2"
|
||||
integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg==
|
||||
|
||||
set-value@^2.0.0, set-value@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
|
||||
|
@ -11950,6 +12247,11 @@ static-extend@^0.1.1:
|
|||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
||||
|
||||
statuses@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
|
||||
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
||||
|
||||
store2@^2.12.0:
|
||||
version "2.13.1"
|
||||
resolved "https://registry.yarnpkg.com/store2/-/store2-2.13.1.tgz#fae7b5bb9d35fc53dc61cd262df3abb2f6e59022"
|
||||
|
@ -11987,6 +12289,13 @@ stream-shift@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
|
||||
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
|
||||
|
||||
strict-event-emitter@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.2.0.tgz#78e2f75dc6ea502e5d8a877661065a1e2deedecd"
|
||||
integrity sha512-zv7K2egoKwkQkZGEaH8m+i2D0XiKzx5jNsiSul6ja2IYFvil10A59Z9Y7PPAAe5OW53dQUf9CfsHKzjZzKkm1w==
|
||||
dependencies:
|
||||
events "^3.3.0"
|
||||
|
||||
string-length@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
|
||||
|
@ -12343,6 +12652,11 @@ through2@^2.0.0:
|
|||
readable-stream "~2.3.6"
|
||||
xtend "~4.0.1"
|
||||
|
||||
through@^2.3.6:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||
|
||||
thunky@^1.0.2:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
|
||||
|
@ -12365,6 +12679,13 @@ tinycolor2@^1.4.1:
|
|||
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
|
||||
integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.2"
|
||||
|
||||
tmpl@1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
|
||||
|
@ -12536,7 +12857,7 @@ tslib@^1.10.0, tslib@^1.8.1:
|
|||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.3.0:
|
||||
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
|
||||
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
|
||||
|
@ -12592,6 +12913,11 @@ type-fest@^0.8.1:
|
|||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
type-fest@^1.2.2:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
|
||||
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
|
||||
|
||||
type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
|
@ -12841,6 +13167,13 @@ use-latest@^1.0.0:
|
|||
dependencies:
|
||||
use-isomorphic-layout-effect "^1.0.0"
|
||||
|
||||
use-subscription@^1.3.0:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1"
|
||||
integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA==
|
||||
dependencies:
|
||||
object-assign "^4.1.1"
|
||||
|
||||
use@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
|
@ -13019,6 +13352,13 @@ wbuf@^1.1.0, wbuf@^1.7.3:
|
|||
dependencies:
|
||||
minimalistic-assert "^1.0.0"
|
||||
|
||||
wcwidth@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
|
||||
integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
|
||||
dependencies:
|
||||
defaults "^1.0.3"
|
||||
|
||||
web-namespaces@^1.0.0:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
|
||||
|
@ -13432,6 +13772,11 @@ xmlchars@^2.2.0:
|
|||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||
|
||||
xstate@^4.29.0, xstate@^4.30.6:
|
||||
version "4.30.6"
|
||||
resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.30.6.tgz#62b6dea37a500e0e1c0ff7c553a801eea5119554"
|
||||
integrity sha512-V7liK1cjkZRh6R/MSneG8S5VLGRatpOUcnNieiYJX4LbwKi9eUVUH5V04ugJYVcJ+2oKDKvEFvzk0VnSC7lTag==
|
||||
|
||||
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
|
@ -13467,6 +13812,11 @@ yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.7:
|
|||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs-parser@^21.0.0:
|
||||
version "21.0.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35"
|
||||
integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==
|
||||
|
||||
yargs@^16.2.0:
|
||||
version "16.2.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
|
||||
|
@ -13480,6 +13830,19 @@ yargs@^16.2.0:
|
|||
y18n "^5.0.5"
|
||||
yargs-parser "^20.2.2"
|
||||
|
||||
yargs@^17.3.1:
|
||||
version "17.3.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9"
|
||||
integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==
|
||||
dependencies:
|
||||
cliui "^7.0.2"
|
||||
escalade "^3.1.1"
|
||||
get-caller-file "^2.0.5"
|
||||
require-directory "^2.1.1"
|
||||
string-width "^4.2.3"
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^21.0.0"
|
||||
|
||||
yauzl@2.10.0, yauzl@^2.10.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
|
|
Loading…
Reference in New Issue