From 55529d7128e08d0a70ef04c2a29ae1adb760c275 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Tue, 14 Mar 2023 10:50:46 +0000 Subject: [PATCH] Add support for OAuth login to GitHub via widget (including adding new installations) (#661) * WIP * Update vite * Add oauth landing page * Add API support for GitHub oauthing * Remove console.logs * Add support for logging and and out of GitHub * Add bridge API methods * Add base link styling * Sugar syntax main get * Update vite * changelog * Review changes * Use instance to match UI * lint --------- Co-authored-by: Justin Carlson --- changelog.d/661.bugfix | 1 + package.json | 4 +- src/Bridge.ts | 2 + src/Config/Config.ts | 8 +- src/Connections/SetupConnection.ts | 1 - src/Github/GithubInstance.ts | 5 +- src/Managers/BotUsersManager.ts | 2 +- src/UserTokenStore.ts | 7 +- src/Webhooks.ts | 106 +++-- src/Widgets/BridgeWidgetApi.ts | 121 ++++- src/Widgets/BridgeWidgetInterface.ts | 21 + tests/connections/GenericHookTest.ts | 2 +- vite.config.js | 7 + web/BridgeAPI.ts | 14 +- web/components/elements/ConnectionSearch.tsx | 17 +- web/components/elements/DropdownSearch.tsx | 2 - web/components/roomConfig/Auth.tsx | 94 ++++ web/components/roomConfig/FeedsConfig.tsx | 6 +- .../roomConfig/GithubRepoConfig.tsx | 48 +- web/components/roomConfig/RoomConfig.tsx | 33 +- web/index.tsx | 2 +- web/oauth.html | 18 + web/oauth.scss | 23 + web/oauth.tsx | 40 ++ web/styling.scss | 8 + yarn.lock | 439 ++++++++++-------- 26 files changed, 766 insertions(+), 265 deletions(-) create mode 100644 changelog.d/661.bugfix create mode 100644 web/components/roomConfig/Auth.tsx create mode 100644 web/oauth.html create mode 100644 web/oauth.scss create mode 100644 web/oauth.tsx diff --git a/changelog.d/661.bugfix b/changelog.d/661.bugfix new file mode 100644 index 00000000..7b6e7685 --- /dev/null +++ b/changelog.d/661.bugfix @@ -0,0 +1 @@ +Add support for logging into GitHub via OAuth from bridge widgets. \ No newline at end of file diff --git a/package.json b/package.json index d4a3ca63..0ce13d7d 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "sass": "^1.51.0", "ts-node": "^10.4.0", "typescript": "^4.5.2", - "vite": "^2.9.13", - "vite-svg-loader": "^3.4.0" + "vite": "^4.1.4", + "vite-svg-loader": "^4.0.0" } } diff --git a/src/Bridge.ts b/src/Bridge.ts index d0ac0a51..19ab9f53 100644 --- a/src/Bridge.ts +++ b/src/Bridge.ts @@ -742,6 +742,8 @@ export class Bridge { this.connectionManager, this.botUsersManager, this.as, + this.tokenStore, + this.github, ); } diff --git a/src/Config/Config.ts b/src/Config/Config.ts index f250a66a..4c010dfa 100644 --- a/src/Config/Config.ts +++ b/src/Config/Config.ts @@ -8,7 +8,7 @@ import { GitHubRepoConnectionOptions } from "../Connections/GithubRepo"; import { BridgeConfigActorPermission, BridgePermissions } from "../libRs"; import { ConfigError } from "../errors"; import { ApiError, ErrCode } from "../api"; -import { GITHUB_CLOUD_URL } from "../Github/GithubInstance"; +import { GithubInstance, GITHUB_CLOUD_URL } from "../Github/GithubInstance"; import { Logger } from "matrix-appservice-bridge"; const log = new Logger("Config"); @@ -95,10 +95,10 @@ export class BridgeConfigGitHub { this.baseUrl = yaml.enterpriseUrl ? new URL(yaml.enterpriseUrl) : GITHUB_CLOUD_URL; } - @hideKey() - public get publicConfig() { + public publicConfig(githubInstance?: GithubInstance) { return { userIdPrefix: this.userIdPrefix, + newInstallationUrl: githubInstance?.newInstallationUrl?.toString(), } } } @@ -705,7 +705,7 @@ For more details, see https://github.com/matrix-org/matrix-hookshot/issues/594. config = this.generic?.publicConfig; break; case "github": - config = this.github?.publicConfig; + config = this.github?.publicConfig(); break; case "gitlab": config = this.gitlab?.publicConfig; diff --git a/src/Connections/SetupConnection.ts b/src/Connections/SetupConnection.ts index ae57fb7f..7ac78e02 100644 --- a/src/Connections/SetupConnection.ts +++ b/src/Connections/SetupConnection.ts @@ -3,7 +3,6 @@ import { BotCommands, botCommand, compileBotCommands, HelpFunction } from "../Bo import { CommandConnection } from "./CommandConnection"; import { GenericHookConnection, GitHubRepoConnection, JiraProjectConnection, JiraProjectConnectionState } from "."; import { CommandError } from "../errors"; -import { randomUUID } from 'node:crypto'; import { BridgePermissionLevel } from "../Config/Config"; import markdown from "markdown-it"; import { FigmaFileConnection } from "./FigmaFileConnection"; diff --git a/src/Github/GithubInstance.ts b/src/Github/GithubInstance.ts index f0c981d7..c4c31c48 100644 --- a/src/Github/GithubInstance.ts +++ b/src/Github/GithubInstance.ts @@ -10,6 +10,7 @@ import UserAgent from "../UserAgent"; const log = new Logger("GithubInstance"); export const GITHUB_CLOUD_URL = new URL("https://api.github.com"); +export const GITHUB_CLOUD_PUBLIC_URL = new URL("https://github.com"); export class GitHubOAuthError extends Error { constructor(errorResponse: GitHubOAuthErrorResponse) { @@ -182,7 +183,7 @@ export class GithubInstance { public get newInstallationUrl() { if (this.baseUrl.hostname === GITHUB_CLOUD_URL.hostname) { // Cloud - return new URL(`/apps/${this.appSlug}/installations/new`, this.baseUrl); + return new URL(`/apps/${this.appSlug}/installations/new`, GITHUB_CLOUD_PUBLIC_URL); } // Enterprise (yes, i know right) return new URL(`/github-apps/${this.appSlug}/installations/new`, this.baseUrl); @@ -192,7 +193,7 @@ export class GithubInstance { const q = new URLSearchParams(params as Record); if (baseUrl.hostname === GITHUB_CLOUD_URL.hostname) { // Cloud doesn't use `api.` for oauth. - baseUrl = new URL("https://github.com"); + baseUrl = GITHUB_CLOUD_PUBLIC_URL; } const rawUrl = baseUrl.toString(); return rawUrl + `${rawUrl.endsWith('/') ? '' : '/'}` + `login/oauth/${action}?${q}`; diff --git a/src/Managers/BotUsersManager.ts b/src/Managers/BotUsersManager.ts index b1b4c84e..a14d5217 100644 --- a/src/Managers/BotUsersManager.ts +++ b/src/Managers/BotUsersManager.ts @@ -1,4 +1,4 @@ -import { Appservice, IAppserviceRegistration, Intent } from "matrix-bot-sdk"; +import { Appservice, Intent } from "matrix-bot-sdk"; import { Logger } from "matrix-appservice-bridge"; import { BridgeConfig } from "../Config/Config"; diff --git a/src/UserTokenStore.ts b/src/UserTokenStore.ts index e1c7d407..9035e6ca 100644 --- a/src/UserTokenStore.ts +++ b/src/UserTokenStore.ts @@ -26,8 +26,8 @@ const LEGACY_ACCOUNT_DATA_TYPE = "uk.half-shot.matrix-github.password-store:"; const LEGACY_ACCOUNT_DATA_GITLAB_TYPE = "uk.half-shot.matrix-github.gitlab.password-store:"; const log = new Logger("UserTokenStore"); -type TokenType = "github"|"gitlab"|"jira"; -const AllowedTokenTypes = ["github", "gitlab", "jira"]; +export type TokenType = "github"|"gitlab"|"jira"; +export const AllowedTokenTypes = ["github", "gitlab", "jira"]; interface StoredTokenData { encrypted: string|string[]; @@ -105,6 +105,9 @@ export class UserTokenStore extends TypedEmitter { } public async clearUserToken(type: TokenType, userId: string, instanceUrl?: string): Promise { + if (!AllowedTokenTypes.includes(type)) { + throw Error('Unknown token type'); + } const key = tokenKey(type, userId, false, instanceUrl); const obj = await this.intent.underlyingClient.getSafeAccountData(key); if (!obj || "deleted" in obj) { diff --git a/src/Webhooks.ts b/src/Webhooks.ts index a4474c00..612dc836 100644 --- a/src/Webhooks.ts +++ b/src/Webhooks.ts @@ -1,8 +1,9 @@ +/* eslint-disable camelcase */ import { BridgeConfig } from "./Config/Config"; import { Router, default as express, Request, Response } from "express"; import { EventEmitter } from "events"; import { MessageQueue, createMessageQueue } from "./MessageQueue"; -import { Logger } from "matrix-appservice-bridge"; +import { ApiError, ErrCode, Logger } from "matrix-appservice-bridge"; import qs from "querystring"; import axios from "axios"; import { IGitLabWebhookEvent, IGitLabWebhookIssueStateEvent, IGitLabWebhookMREvent, IGitLabWebhookReleaseEvent } from "./Gitlab/WebhookTypes"; @@ -185,52 +186,77 @@ export class Webhooks extends EventEmitter { } } - public async onGitHubGetOauth(req: Request , res: Response) { - log.info(`Got new oauth request`, { state: req.query.state }); + public async onGitHubGetOauth(req: Request , res: Response) { + const oauthUrl = this.config.widgets && new URL("oauth.html", this.config.widgets.parsedPublicUrl); + if (oauthUrl) { + oauthUrl.searchParams.set('service', 'github'); + oauthUrl?.searchParams.set('oauth-kind', 'account'); + } + const { setup_action, state } = req.query; + log.info("Got new oauth request", { state, setup_action }); try { if (!this.config.github || !this.config.github.oauth) { - return res.status(500).send(`

Bridge is not configured with OAuth support

`); + throw new ApiError('Bridge is not configured with OAuth support', ErrCode.DisabledFeature); } if (req.query.error) { - return res.status(500).send(`

GitHub Error: ${req.query.error} ${req.query.error_description}

`); + throw new ApiError(`GitHub Error: ${req.query.error} ${req.query.error_description}`, ErrCode.Unknown); } - if (!req.query.state) { - return res.status(400).send(`

Missing state

`); + if (setup_action !== 'install') { + if (!state) { + throw new ApiError(`Missing state`, ErrCode.BadValue); + } + if (!req.query.code) { + throw new ApiError(`Missing code`, ErrCode.BadValue); + } + const exists = await this.queue.pushWait({ + eventName: "github.oauth.response", + sender: "GithubWebhooks", + data: { + state, + }, + }); + if (!exists) { + throw new ApiError(`Could not find user which authorised this request. Has it timed out?`, undefined, 404); + } + const accessTokenUrl = GithubInstance.generateOAuthUrl(this.config.github.baseUrl, "access_token", { + client_id: this.config.github.oauth.client_id, + client_secret: this.config.github.oauth.client_secret, + code: req.query.code as string, + redirect_uri: this.config.github.oauth.redirect_uri, + state: req.query.state as string, + }); + const accessTokenRes = await axios.post(accessTokenUrl); + const result = qs.parse(accessTokenRes.data) as GitHubOAuthTokenResponse|{error: string, error_description: string, error_uri: string}; + if ("error" in result) { + throw new ApiError(`GitHub Error: ${result.error} ${result.error_description}`, ErrCode.Unknown); + } + await this.queue.push({ + eventName: "github.oauth.tokens", + sender: "GithubWebhooks", + data: { ...result, state: req.query.state as string }, + }); + } else if (oauthUrl) { + // App install. + oauthUrl.searchParams.set('oauth-kind', 'organisation'); } - if (!req.query.code) { - return res.status(400).send(`

Missing code

`); - } - const exists = await this.queue.pushWait({ - eventName: "github.oauth.response", - sender: "GithubWebhooks", - data: { - state: req.query.state, - }, - }); - if (!exists) { - return res.status(404).send(`

Could not find user which authorised this request. Has it timed out?

`); - } - const accessTokenUrl = GithubInstance.generateOAuthUrl(this.config.github.baseUrl, "access_token", { - client_id: this.config.github.oauth.client_id, - client_secret: this.config.github.oauth.client_secret, - code: req.query.code as string, - redirect_uri: this.config.github.oauth.redirect_uri, - state: req.query.state as string, - }); - const accessTokenRes = await axios.post(accessTokenUrl); - const result = qs.parse(accessTokenRes.data) as GitHubOAuthTokenResponse|{error: string, error_description: string, error_uri: string}; - if ("error" in result) { - return res.status(500).send(`

GitHub Error: ${result.error} ${result.error_description}

`); - } - await this.queue.push({ - eventName: "github.oauth.tokens", - sender: "GithubWebhooks", - data: { ...result, state: req.query.state as string }, - }); - return res.send(`

Your account has been bridged

`); } catch (ex) { - log.error("Failed to handle oauth request:", ex); - return res.status(500).send(`

Encountered an error handing oauth request

`); + if (ex instanceof ApiError) { + if (oauthUrl) { + oauthUrl?.searchParams.set('error', ex.error); + oauthUrl?.searchParams.set('errcode', ex.errcode); + } else { + return res.status(ex.statusCode).send(ex.message); + } + } else { + log.error("Failed to handle oauth request:", ex); + return res.status(500).send('Failed to handle oauth request'); + } + } + if (oauthUrl) { + // If we're serving widgets, do something prettier. + return res.redirect(oauthUrl.toString()); + } else { + return res.send(`

Your account has been bridged

`); } } diff --git a/src/Widgets/BridgeWidgetApi.ts b/src/Widgets/BridgeWidgetApi.ts index be6070c2..0e714ef5 100644 --- a/src/Widgets/BridgeWidgetApi.ts +++ b/src/Widgets/BridgeWidgetApi.ts @@ -3,13 +3,15 @@ import { AdminRoom } from "../AdminRoom"; import { Logger } from "matrix-appservice-bridge"; import { ApiError, ErrCode } from "../api"; import { BridgeConfig } from "../Config/Config"; -import { GetConnectionsForServiceResponse } from "./BridgeWidgetInterface"; +import { GetAuthPollResponse, GetAuthResponse, GetConnectionsForServiceResponse } from "./BridgeWidgetInterface"; import { ProvisioningApi, ProvisioningRequest } from "matrix-appservice-bridge"; import { IBridgeStorageProvider } from "../Stores/StorageProvider"; import { ConnectionManager } from "../ConnectionManager"; import BotUsersManager, {BotUser} from "../Managers/BotUsersManager"; import { assertUserPermissionsInRoom, GetConnectionsResponseItem } from "../provisioning/api"; import { Appservice, PowerLevelsEvent } from "matrix-bot-sdk"; +import { GithubInstance } from '../Github/GithubInstance'; +import { AllowedTokenTypes, TokenType, UserTokenStore } from '../UserTokenStore'; const log = new Logger("BridgeWidgetApi"); @@ -23,6 +25,8 @@ export class BridgeWidgetApi { private readonly connMan: ConnectionManager, private readonly botUsersManager: BotUsersManager, private readonly as: Appservice, + private readonly tokenStore: UserTokenStore, + private readonly github?: GithubInstance, ) { this.api = new ProvisioningApi( storageProvider, @@ -54,6 +58,9 @@ export class BridgeWidgetApi { this.api.addRoute("patch", '/v1/:roomId/connections/:connectionId', wrapHandler(this.updateConnection)); this.api.addRoute("delete", '/v1/:roomId/connections/:connectionId', wrapHandler(this.deleteConnection)); this.api.addRoute("get", '/v1/targets/:type', wrapHandler(this.getConnectionTargets)); + this.api.addRoute('get', '/v1/service/:service/auth', wrapHandler(this.getAuth)); + this.api.addRoute('get', '/v1/service/:service/auth/:state', wrapHandler(this.getAuthPoll)); + this.api.addRoute('post', '/v1/service/:service/auth/logout', wrapHandler(this.postAuthLogout)); } private getBotUserInRoom(roomId: string, serviceType: string): BotUser { @@ -95,7 +102,12 @@ export class BridgeWidgetApi { } private async getServiceConfig(req: ProvisioningRequest, res: Response>) { - res.send(this.config.getPublicConfigForService(req.params.service)); + // GitHub is a special case because it depends on live config. + if (req.params.service === 'github') { + res.send(this.config.github?.publicConfig(this.github)); + } else { + res.send(this.config.getPublicConfigForService(req.params.service)); + } } private async getConnectionsForRequest(req: ProvisioningRequest) { @@ -221,4 +233,109 @@ export class BridgeWidgetApi { const connections = await this.connMan.getConnectionTargets(req.userId, type, req.query); res.send(connections); } + + + private async getAuth(req: ProvisioningRequest, res: Response) { + if (!req.userId) { + throw Error('Expected userId on request'); + } + const service = req.params.service; + if (!service) { + throw Error('Expected service in parameters'); + } + + // TODO: Should this be part of the GitHub module code. + if (service === 'github') { + if (!this.config.github || !this.config.github.oauth) { + throw new ApiError('GitHub oauth is not configured', ErrCode.DisabledFeature); + } + + let user; + try { + const octokit = await this.tokenStore.getOctokitForUser(req.userId); + if (octokit !== null) { + const me = await octokit.users.getAuthenticated(); + user = { + name: me.data.login, + }; + } + } catch (e) { + // Need to authenticate + } + + if (user) { + return res.json({ + authenticated: true, + user + }); + } else { + const state = this.tokenStore.createStateForOAuth(req.userId); + const authUrl = GithubInstance.generateOAuthUrl( + this.config.github.baseUrl, + 'authorize', + { + state, + client_id: this.config.github.oauth.client_id, + redirect_uri: this.config.github.oauth.redirect_uri, + } + ); + return res.json({ + authenticated: false, + stateId: state, + authUrl + }); + } + } else { + throw new ApiError('Service not found', ErrCode.NotFound); + } + } + + private async getAuthPoll(req: ProvisioningRequest, res: Response) { + if (!req.userId) { + throw Error('Expected userId on request'); + } + const { service, state } = req.params; + + if (!service) { + throw Error('Expected service in parameters'); + } + + // N.B. Service isn't really used. + const stateUserId = this.tokenStore.getUserIdForOAuthState(state); + + if (!stateUserId || req.userId !== stateUserId) { + // If the state isn't found then either the state has been completed or the key is wrong. + // We don't actually know, so we assume the sender knows what they are doing. + res.send({ + state: 'complete', + }); + return; + } + res.send({ + state: 'waiting', + }); + return; + } + + private async postAuthLogout(req: ProvisioningRequest, res: Response<{ok: true}>) { + if (!req.userId) { + throw Error('Expected userId on request'); + } + const { service } = req.params; + + if (!service) { + throw Error('Expected service in parameters'); + } + + if (AllowedTokenTypes.includes(service)) { + const result = await this.tokenStore.clearUserToken(service as TokenType, req.userId); + if (result) { + res.send({ok: true}); + } else { + throw new ApiError("You are not logged in", ErrCode.NotFound); + } + } else { + throw new ApiError('Service not found', ErrCode.NotFound); + } + } } diff --git a/src/Widgets/BridgeWidgetInterface.ts b/src/Widgets/BridgeWidgetInterface.ts index 3fcb390a..aa292c27 100644 --- a/src/Widgets/BridgeWidgetInterface.ts +++ b/src/Widgets/BridgeWidgetInterface.ts @@ -36,3 +36,24 @@ export interface GetConnectionsForServiceResponse { const intent = as.getIntentForUserId(senderUserId); // This should fail the first time, then pass once we've tried to invite the user - intent.ensureJoined = (roomId: string) => { + intent.ensureJoined = () => { throw new MatrixError({ errcode: "FORCED_FAILURE", error: "Test forced error"}, 500) }; try { diff --git a/vite.config.js b/vite.config.js index 8d2c892f..da4ef13b 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,6 +1,7 @@ import { defineConfig } from 'vite' import preact from '@preact/preset-vite' import svgLoader from 'vite-svg-loader' +import { resolve } from 'path' // https://vitejs.dev/config/ export default defineConfig({ @@ -9,6 +10,12 @@ export default defineConfig({ base: '', build: { outDir: '../public', + rollupOptions: { + input: { + main: resolve('web', 'index.html'), + oauth: resolve('web', 'oauth.html'), + } + }, emptyOutDir: true, }, }) diff --git a/web/BridgeAPI.ts b/web/BridgeAPI.ts index 10ea73ca..83dbbd56 100644 --- a/web/BridgeAPI.ts +++ b/web/BridgeAPI.ts @@ -1,4 +1,4 @@ -import { BridgeRoomState, GetConnectionsForServiceResponse } from '../src/Widgets/BridgeWidgetInterface'; +import { BridgeRoomState, GetAuthPollResponse, GetAuthResponse, GetConnectionsForServiceResponse } from '../src/Widgets/BridgeWidgetInterface'; import { GetConnectionsResponseItem } from "../src/provisioning/api"; import { ExchangeOpenAPIRequestBody, ExchangeOpenAPIResponseBody } from "matrix-appservice-bridge"; import { WidgetApi } from 'matrix-widget-api'; @@ -138,6 +138,18 @@ export class BridgeAPI { const searchParams = filters && !!Object.keys(filters).length && new URLSearchParams(filters); return this.request('GET', `/widgetapi/v1/targets/${encodeURIComponent(type)}${searchParams ? `?${searchParams}` : ''}`, undefined, { abortController }); } + + async getAuth(service: string): Promise { + return this.request('GET', `/widgetapi/v1/service/${service}/auth`); + } + + async getAuthPoll(service: string, state: string): Promise { + return this.request('GET', `/widgetapi/v1/service/${service}/auth/${state}`); + } + + async serviceLogout(service: string): Promise { + return this.request('POST', `/widgetapi/v1/service/${service}/auth/logout`); + } } export const embedTypeParameter = 'io_element_embed_type'; diff --git a/web/components/elements/ConnectionSearch.tsx b/web/components/elements/ConnectionSearch.tsx index da651e1d..d20069bb 100644 --- a/web/components/elements/ConnectionSearch.tsx +++ b/web/components/elements/ConnectionSearch.tsx @@ -12,6 +12,7 @@ type Project = DropItem; interface IProps { serviceName: string; + addNewInstanceUrl?: string; getInstances(): Promise; getProjects(currentInstance: string, searchTerm?: string, abortController?: AbortController): Promise; onPicked: (instanceValue: string, projectValue: string) => void; @@ -27,6 +28,7 @@ interface IProps { */ export function ConnectionSearch({ serviceName, + addNewInstanceUrl, onPicked, onClear, getInstances, @@ -104,15 +106,28 @@ export function ConnectionSearch({ const searchProps = useMemo(() => ({ instance: currentInstance }), [currentInstance]); + let addNewInstance = null; + if (instances?.length === 0) { + if (addNewInstanceUrl) { + addNewInstance =

You have not connected any {serviceName} instances. + Add a new instances. +

; + } else { + addNewInstance =

You have not connected any {serviceName} instances.

; + } + } else if (addNewInstanceUrl) { + addNewInstance =

Add a new instances.

+ } // otherwise, empty + return
{!searchError && instances === null &&

Loading {serviceName} instances.

} - {instances?.length === 0 &&

You are not logged into any {serviceName} instances.

} {searchError && {searchError} } + { addNewInstance } { currentInstance && ({searchFn, searchProps, onChange, onEr // Reset if the search properties are altered. useEffect(() => { - console.log("Resetting props"); setSearchTerm(""); setSelectedItem(null); }, [searchProps]); // Search whenever the term is updated. useEffect(() => { - console.log("New search"); if (searchTerm.trim().length === 0) { return; } diff --git a/web/components/roomConfig/Auth.tsx b/web/components/roomConfig/Auth.tsx new file mode 100644 index 00000000..ab562d9a --- /dev/null +++ b/web/components/roomConfig/Auth.tsx @@ -0,0 +1,94 @@ +import { useCallback, useEffect, useState } from "preact/hooks"; +import { GetAuthResponse } from "../../../src/Widgets/BridgeWidgetInterface"; +import { BridgeAPI } from "../../BridgeAPI"; +import { Button } from "../elements"; + +const PollAuthEveryMs = 3000; + + +export const ServiceAuth = ({ + api, + service, + loginLabel = "Log in", + authState, + onAuthSucceeded, +}: { + api: BridgeAPI, + service: string, + authState: GetAuthResponse, + onAuthSucceeded: () => void, + loginLabel?: string, +}) => { + const [pollStateId, setPollStateId] = useState(); + + const pollAuth = useCallback(async (pollId) => { + try { + const res = await api.getAuthPoll(service, pollId); + if (res.state === "waiting") { + // Keep going + return; + } + // Completed. + setPollStateId(null); + onAuthSucceeded(); + } catch (ex) { + console.warn(`Failed to poll for state check`, ex); + } + }, [service, api, onAuthSucceeded]) + + useEffect(() => { + if (!pollStateId) { + return; + } + let pollTimerId: number; + const poll = async () => { + try { + await pollAuth(pollStateId); + } + finally { + // Recursively call poll + pollTimerId = window.setTimeout( + poll, + PollAuthEveryMs, + ); + } + }; + void poll(); + + // Cleanup + return () => { + window.clearTimeout(pollTimerId); + }; + + }, [api, service, pollStateId, onAuthSucceeded, pollAuth]); + + const loginToService = useCallback(() => { + if (authState.authenticated) { + // No need to do anything + return; + } + window.open(authState.authUrl, '_blank'); + setPollStateId(authState.stateId); + }, [authState]); + + const logoutOfService = useCallback(() => { + if (!authState.authenticated) { + // No need to do anything + return; + } + api.serviceLogout(service).then(() => { + onAuthSucceeded(); + }).catch((ex) => { + console.warn(`Failed to poll for state check`, ex); + }) + }, [api, onAuthSucceeded, service, authState]); + + if ('authUrl' in authState) { + return ; + } + return

+ Logged in as {authState.user?.name ?? ''}. Logout +

; +}; diff --git a/web/components/roomConfig/FeedsConfig.tsx b/web/components/roomConfig/FeedsConfig.tsx index 5efd1249..7cf23b81 100644 --- a/web/components/roomConfig/FeedsConfig.tsx +++ b/web/components/roomConfig/FeedsConfig.tsx @@ -2,7 +2,7 @@ import { FunctionComponent, createRef } from "preact"; import { useCallback } from "preact/hooks" import { BridgeConfig } from "../../BridgeAPI"; import { FeedConnectionState, FeedResponseItem } from "../../../src/Connections/FeedConnection"; -import { ConnectionConfigurationProps, RoomConfig } from "./RoomConfig"; +import { ConnectionConfigurationProps, IRoomConfigText, RoomConfig } from "./RoomConfig"; import { Button, ButtonSet, InputField } from "../elements"; import styles from "./FeedConnection.module.scss"; @@ -66,7 +66,7 @@ interface ServiceConfig { pollIntervalSeconds: number, } -const RoomConfigText = { +const roomConfigText: IRoomConfigText = { header: 'RSS/Atom feeds', createNew: 'Subscribe to a feed', listCanEdit: 'Feeds subscribed to', @@ -83,7 +83,7 @@ export const FeedsConfig: BridgeConfig = ({ api, roomId, showHeader }) => { roomId={roomId} type="feeds" connectionEventType="uk.half-shot.matrix-hookshot.feed" - text={RoomConfigText} + text={roomConfigText} listItemName={RoomConfigListItemFunc} connectionConfigComponent={ConnectionConfiguration} />; diff --git a/web/components/roomConfig/GithubRepoConfig.tsx b/web/components/roomConfig/GithubRepoConfig.tsx index 6f446960..6310d1ee 100644 --- a/web/components/roomConfig/GithubRepoConfig.tsx +++ b/web/components/roomConfig/GithubRepoConfig.tsx @@ -1,13 +1,15 @@ import GitHubIcon from "../../icons/github.png"; import { BridgeConfig } from "../../BridgeAPI"; -import { ConnectionConfigurationProps, RoomConfig } from "./RoomConfig"; +import { ConnectionConfigurationProps, IRoomConfigText, RoomConfig } from "./RoomConfig"; import { EventHookCheckbox } from '../elements/EventHookCheckbox'; import { FunctionComponent, createRef } from "preact"; import { GitHubRepoConnectionState, GitHubRepoResponseItem, GitHubRepoConnectionRepoTarget, GitHubRepoConnectionOrgTarget } from "../../../src/Connections/GithubRepo"; import { InputField, ButtonSet, Button } from "../elements"; -import { useState, useCallback, useMemo } from "preact/hooks"; +import { useState, useCallback, useMemo, useEffect } from "preact/hooks"; import { DropItem } from "../elements/DropdownSearch"; import ConnectionSearch from "../elements/ConnectionSearch"; +import { ServiceAuth } from "./Auth"; +import { GetAuthResponse } from "../../../src/Widgets/BridgeWidgetInterface"; const EventType = "uk.half-shot.matrix-hookshot.github.repository"; @@ -15,9 +17,37 @@ function getRepoFullName(state: GitHubRepoConnectionState) { return `${state.org}/${state.repo}`; } -const ConnectionConfiguration: FunctionComponent> = ({api, existingConnection, onSave, onRemove }) => { +const ConnectionConfiguration: FunctionComponent> = ({ + showAuthPrompt, loginLabel, serviceConfig, api, existingConnection, onSave, onRemove +}) => { + // Assume true if we have no auth prompt. + const [authedResponse, setAuthResponse] = useState(null); const [enabledHooks, setEnabledHooks] = useState(existingConnection?.config.enableHooks || []); + const checkAuth = useCallback(() => { + api.getAuth("github").then((res) => { + setAuthResponse(res); + }).catch(ex => { + console.warn("Could not check authed state, assuming yes", ex); + setAuthResponse({ + authenticated: true, + user: { + name: 'Unknown' + } + }); + }) + }, [api]); + + useEffect(() => { + if (!showAuthPrompt) { + return; + } + checkAuth(); + }, [showAuthPrompt, checkAuth]) + + const { newInstallationUrl } = serviceConfig; + + const toggleEnabledHook = useCallback((evt: any) => { const key = (evt.target as HTMLElement).getAttribute('x-event-name'); if (key) { @@ -74,8 +104,10 @@ const ConnectionConfiguration: FunctionComponent setConnectionState(null), [setConnectionState]); return
- {!existingConnection && } + {!existingConnection && authedResponse?.authenticated && - { canEdit && } + { canEdit && authedResponse?.authenticated && } { canEdit && existingConnection && } ; }; -const RoomConfigText = { +const roomConfigText: IRoomConfigText = { header: 'GitHub Repositories', + login: 'Log in to GitHub', createNew: 'Add new GitHub repository', listCanEdit: 'Your connected repositories', listCantEdit: 'Connected repositories', @@ -142,7 +175,8 @@ export const GithubRepoConfig: BridgeConfig = ({ api, roomId, showHeader }) => { api={api} roomId={roomId} type="github" - text={RoomConfigText} + showAuthPrompt={true} + text={roomConfigText} listItemName={RoomConfigListItemFunc} connectionEventType={EventType} connectionConfigComponent={ConnectionConfiguration} diff --git a/web/components/roomConfig/RoomConfig.tsx b/web/components/roomConfig/RoomConfig.tsx index b6bcf086..b63403c5 100644 --- a/web/components/roomConfig/RoomConfig.tsx +++ b/web/components/roomConfig/RoomConfig.tsx @@ -10,31 +10,46 @@ import { LoadingSpinner } from '../elements/LoadingSpinner'; export interface ConnectionConfigurationProps { serviceConfig: SConfig; + loginLabel?: string; + showAuthPrompt?: boolean; onSave: (newConfig: ConnectionState) => void, existingConnection?: ConnectionType; onRemove?: () => void, api: BridgeAPI; } +export interface IRoomConfigText { + header: string; + login?: string; + createNew: string; + listCanEdit: string; + listCantEdit: string; +} + interface IRoomConfigProps { api: BridgeAPI; roomId: string; type: string; + showAuthPrompt?: boolean; showHeader: boolean; headerImg: string; - text: { - header: string; - createNew: string; - listCanEdit: string; - listCantEdit: string; - }; + text: IRoomConfigText; connectionEventType: string; listItemName: (c: ConnectionType) => string, connectionConfigComponent: FunctionComponent>; } - export const RoomConfig = function(props: IRoomConfigProps) { - const { api, roomId, type, headerImg, showHeader, text, listItemName, connectionEventType } = props; + const { + api, + roomId, + type, + showAuthPrompt = false, + headerImg, + showHeader, + text, + listItemName, + connectionEventType + } = props; const ConnectionConfigComponent = props.connectionConfigComponent; const [ error, setError ] = useState(null); const [ connections, setConnections ] = useState(null); @@ -115,6 +130,8 @@ export const RoomConfig = function} } { connections === null && } diff --git a/web/index.tsx b/web/index.tsx index cdecb960..6a48d4f6 100644 --- a/web/index.tsx +++ b/web/index.tsx @@ -4,7 +4,7 @@ import App from './App'; import "./fonts/fonts.scss" import "./styling.scss"; -const root = document.getElementsByTagName('main')[0]; +const [ root ] = document.getElementsByTagName('main'); if (root) { render(, root); diff --git a/web/oauth.html b/web/oauth.html new file mode 100644 index 00000000..46258b1e --- /dev/null +++ b/web/oauth.html @@ -0,0 +1,18 @@ + + + + + + Account + + +
+ +
+ + + diff --git a/web/oauth.scss b/web/oauth.scss new file mode 100644 index 00000000..137faaad --- /dev/null +++ b/web/oauth.scss @@ -0,0 +1,23 @@ +a { + color: var(--primary-color); +} + +a:visited { + color: var(--primary-color); +} + +body { + margin-top: 5em; + margin-left: 5em; + margin-right: 5em; + + text-align: center; +} + +h1 { + font-size: 1.5em; +} + +p { + font-size: 1.25em; +} \ No newline at end of file diff --git a/web/oauth.tsx b/web/oauth.tsx new file mode 100644 index 00000000..d12107cf --- /dev/null +++ b/web/oauth.tsx @@ -0,0 +1,40 @@ +import "./fonts/fonts.scss" +import "./styling.scss"; +import "./oauth.scss"; +import { render } from 'preact'; +import 'preact/devtools'; + +const root = document.getElementsByTagName('main')[0]; + +const ServiceToName: Record = { + github: 'GitHub', + gitlab: 'GitLab', + default: '' +} + + +function RenderOAuth() { + const params = new URLSearchParams(window.location.search); + const service = params.get('service') ?? 'default'; + const error = params.get('error'); + const errcode = params.get('errcode'); + const oauthKind = params.get('oauth-kind') ?? 'account'; + + if (error) { + return <> +

Could not connect your { ServiceToName[service] } {oauthKind} to Hookshot.

+

+ {errcode} {error} +

+ ; + } + + return <> +

Your { ServiceToName[service] } {oauthKind} has been connected.

+

You may close this window.

+ ; +} + +if (root) { + render(, root); +} \ No newline at end of file diff --git a/web/styling.scss b/web/styling.scss index 0ecd9268..cb96c104 100644 --- a/web/styling.scss +++ b/web/styling.scss @@ -19,6 +19,14 @@ // } // } +a { + color: var(--primary-color); +} + +a:visited { + color: var(--primary-color); +} + body { margin: 0; } diff --git a/yarn.lock b/yarn.lock index 55bc3178..06da9cea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -658,6 +658,116 @@ enabled "2.0.x" kuler "^2.0.0" +"@esbuild/android-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" + integrity sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg== + +"@esbuild/android-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz#025b6246d3f68b7bbaa97069144fb5fb70f2fff2" + integrity sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw== + +"@esbuild/android-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz#c820e0fef982f99a85c4b8bfdd582835f04cd96e" + integrity sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ== + +"@esbuild/darwin-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz#edef4487af6b21afabba7be5132c26d22379b220" + integrity sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w== + +"@esbuild/darwin-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz#42829168730071c41ef0d028d8319eea0e2904b4" + integrity sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg== + +"@esbuild/freebsd-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz#1f4af488bfc7e9ced04207034d398e793b570a27" + integrity sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw== + +"@esbuild/freebsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz#636306f19e9bc981e06aa1d777302dad8fddaf72" + integrity sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug== + +"@esbuild/linux-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz#a003f7ff237c501e095d4f3a09e58fc7b25a4aca" + integrity sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g== + +"@esbuild/linux-arm@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz#b591e6a59d9c4fe0eeadd4874b157ab78cf5f196" + integrity sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ== + +"@esbuild/linux-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz#24333a11027ef46a18f57019450a5188918e2a54" + integrity sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg== + +"@esbuild/linux-loong64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz#d5ad459d41ed42bbd4d005256b31882ec52227d8" + integrity sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ== + +"@esbuild/linux-mips64el@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz#4e5967a665c38360b0a8205594377d4dcf9c3726" + integrity sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw== + +"@esbuild/linux-ppc64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz#206443a02eb568f9fdf0b438fbd47d26e735afc8" + integrity sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g== + +"@esbuild/linux-riscv64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz#c351e433d009bf256e798ad048152c8d76da2fc9" + integrity sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw== + +"@esbuild/linux-s390x@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz#661f271e5d59615b84b6801d1c2123ad13d9bd87" + integrity sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w== + +"@esbuild/linux-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz#e4ba18e8b149a89c982351443a377c723762b85f" + integrity sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw== + +"@esbuild/netbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz#7d4f4041e30c5c07dd24ffa295c73f06038ec775" + integrity sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA== + +"@esbuild/openbsd-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz#970fa7f8470681f3e6b1db0cc421a4af8060ec35" + integrity sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg== + +"@esbuild/sunos-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz#abc60e7c4abf8b89fb7a4fe69a1484132238022c" + integrity sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw== + +"@esbuild/win32-arm64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz#7b0ff9e8c3265537a7a7b1fd9a24e7bd39fcd87a" + integrity sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw== + +"@esbuild/win32-ia32@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz#e90fe5267d71a7b7567afdc403dfd198c292eb09" + integrity sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig== + +"@esbuild/win32-x64@0.16.17": + version "0.16.17" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz#c5a1a4bfe1b57f0c3e61b29883525c6da3e5c091" + integrity sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q== + "@eslint/eslintrc@^1.0.5": version "1.0.5" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318" @@ -2252,36 +2362,44 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -css-select@^4.1.3: - version "4.3.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" - integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== dependencies: boolbase "^1.0.0" - css-what "^6.0.1" - domhandler "^4.3.1" - domutils "^2.8.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" nth-check "^2.0.1" -css-tree@^1.1.2, css-tree@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== +css-tree@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" + mdn-data "2.0.30" + source-map-js "^1.0.1" -css-what@^6.0.1: +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + +css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== -csso@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== dependencies: - css-tree "^1.1.2" + css-tree "~2.2.0" dashdash@^1.12.0: version "1.14.1" @@ -2432,19 +2550,35 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -domelementtype@^2.0.1, domelementtype@^2.2.0: +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: +domhandler@^4.0.0, domhandler@^4.2.0: version "4.3.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== dependencies: domelementtype "^2.2.0" -domutils@^2.5.2, domutils@^2.8.0: +domhandler@^5.0.1, domhandler@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^2.5.2: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== @@ -2453,6 +2587,15 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" +domutils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" + integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.1" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -2515,6 +2658,11 @@ entities@^2.0.0, entities@^2.0.3: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" + integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + entities@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" @@ -2560,131 +2708,33 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild-android-64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz#5b94a1306df31d55055f64a62ff6b763a47b7f64" - integrity sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw== - -esbuild-android-arm64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz#78acc80773d16007de5219ccce544c036abd50b8" - integrity sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA== - -esbuild-darwin-64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz#e02b1291f629ebdc2aa46fabfacc9aa28ff6aa46" - integrity sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA== - -esbuild-darwin-arm64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz#01eb6650ec010b18c990e443a6abcca1d71290a9" - integrity sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ== - -esbuild-freebsd-64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz#790b8786729d4aac7be17648f9ea8e0e16475b5e" - integrity sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig== - -esbuild-freebsd-arm64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz#b66340ab28c09c1098e6d9d8ff656db47d7211e6" - integrity sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ== - -esbuild-linux-32@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz#7927f950986fd39f0ff319e92839455912b67f70" - integrity sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g== - -esbuild-linux-64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz#4893d07b229d9cfe34a2b3ce586399e73c3ac519" - integrity sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q== - -esbuild-linux-arm64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz#8442402e37d0b8ae946ac616784d9c1a2041056a" - integrity sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA== - -esbuild-linux-arm@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz#d5dbf32d38b7f79be0ec6b5fb2f9251fd9066986" - integrity sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA== - -esbuild-linux-mips64le@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz#95081e42f698bbe35d8ccee0e3a237594b337eb5" - integrity sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ== - -esbuild-linux-ppc64le@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz#dceb0a1b186f5df679618882a7990bd422089b47" - integrity sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q== - -esbuild-linux-riscv64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz#61fb8edb75f475f9208c4a93ab2bfab63821afd2" - integrity sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ== - -esbuild-linux-s390x@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz#34c7126a4937406bf6a5e69100185fd702d12fe0" - integrity sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ== - -esbuild-netbsd-64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz#322ea9937d9e529183ee281c7996b93eb38a5d95" - integrity sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q== - -esbuild-openbsd-64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz#1ca29bb7a2bf09592dcc26afdb45108f08a2cdbd" - integrity sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ== - -esbuild-sunos-64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz#c9446f7d8ebf45093e7bb0e7045506a88540019b" - integrity sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA== - -esbuild-windows-32@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz#f8e9b4602fd0ccbd48e5c8d117ec0ba4040f2ad1" - integrity sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw== - -esbuild-windows-64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz#280f58e69f78535f470905ce3e43db1746518107" - integrity sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw== - -esbuild-windows-arm64@0.14.38: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz#d97e9ac0f95a4c236d9173fa9f86c983d6a53f54" - integrity sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw== - -esbuild@^0.14.27: - version "0.14.38" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.38.tgz#99526b778cd9f35532955e26e1709a16cca2fb30" - integrity sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA== +esbuild@^0.16.14: + version "0.16.17" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.16.17.tgz#fc2c3914c57ee750635fee71b89f615f25065259" + integrity sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg== optionalDependencies: - esbuild-android-64 "0.14.38" - esbuild-android-arm64 "0.14.38" - esbuild-darwin-64 "0.14.38" - esbuild-darwin-arm64 "0.14.38" - esbuild-freebsd-64 "0.14.38" - esbuild-freebsd-arm64 "0.14.38" - esbuild-linux-32 "0.14.38" - esbuild-linux-64 "0.14.38" - esbuild-linux-arm "0.14.38" - esbuild-linux-arm64 "0.14.38" - esbuild-linux-mips64le "0.14.38" - esbuild-linux-ppc64le "0.14.38" - esbuild-linux-riscv64 "0.14.38" - esbuild-linux-s390x "0.14.38" - esbuild-netbsd-64 "0.14.38" - esbuild-openbsd-64 "0.14.38" - esbuild-sunos-64 "0.14.38" - esbuild-windows-32 "0.14.38" - esbuild-windows-64 "0.14.38" - esbuild-windows-arm64 "0.14.38" + "@esbuild/android-arm" "0.16.17" + "@esbuild/android-arm64" "0.16.17" + "@esbuild/android-x64" "0.16.17" + "@esbuild/darwin-arm64" "0.16.17" + "@esbuild/darwin-x64" "0.16.17" + "@esbuild/freebsd-arm64" "0.16.17" + "@esbuild/freebsd-x64" "0.16.17" + "@esbuild/linux-arm" "0.16.17" + "@esbuild/linux-arm64" "0.16.17" + "@esbuild/linux-ia32" "0.16.17" + "@esbuild/linux-loong64" "0.16.17" + "@esbuild/linux-mips64el" "0.16.17" + "@esbuild/linux-ppc64" "0.16.17" + "@esbuild/linux-riscv64" "0.16.17" + "@esbuild/linux-s390x" "0.16.17" + "@esbuild/linux-x64" "0.16.17" + "@esbuild/netbsd-x64" "0.16.17" + "@esbuild/openbsd-x64" "0.16.17" + "@esbuild/sunos-x64" "0.16.17" + "@esbuild/win32-arm64" "0.16.17" + "@esbuild/win32-ia32" "0.16.17" + "@esbuild/win32-x64" "0.16.17" escalade@^3.1.1: version "3.1.1" @@ -3667,6 +3717,13 @@ is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -4304,10 +4361,15 @@ matrix-widget-api@^1.0.0: "@types/events" "^3.0.0" events "^3.2.0" -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== mdurl@^1.0.1: version "1.0.1" @@ -4489,7 +4551,7 @@ nanoid@3.1.20: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== -nanoid@^3.1.30, nanoid@^3.3.3, nanoid@^3.3.4: +nanoid@^3.1.30, nanoid@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== @@ -4918,12 +4980,12 @@ postcss@^8.3.11: picocolors "^1.0.0" source-map-js "^1.0.1" -postcss@^8.4.13: - version "8.4.13" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.13.tgz#7c87bc268e79f7f86524235821dfdf9f73e5d575" - integrity sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA== +postcss@^8.4.21: + version "8.4.21" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4" + integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== dependencies: - nanoid "^3.3.3" + nanoid "^3.3.4" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -5221,7 +5283,7 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.20.0, resolve@^1.22.0: +resolve@^1.20.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== @@ -5230,6 +5292,15 @@ resolve@^1.20.0, resolve@^1.22.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" @@ -5255,10 +5326,10 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rollup@^2.59.0: - version "2.72.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.72.0.tgz#f94280b003bcf9f2f1f2594059a9db5abced371e" - integrity sha512-KqtR2YcO35/KKijg4nx4STO3569aqCUeGRkKWnJ6r+AvBBrVY9L4pmf4NHVrQr4mTOq6msbohflxr2kpihhaOA== +rollup@^3.10.0: + version "3.19.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.19.1.tgz#2b3a31ac1ff9f3afab2e523fa687fef5b0ee20fc" + integrity sha512-lAbrdN7neYCg/8WaoWn/ckzCtz+jr70GFfYdlf50OF7387HTg+wiuiqJRFYawwSPpqfqDNYqK7smY/ks2iAudg== optionalDependencies: fsevents "~2.3.2" @@ -5547,11 +5618,6 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" @@ -5705,18 +5771,17 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -svgo@^2.7.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" - integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== +svgo@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.0.2.tgz#5e99eeea42c68ee0dc46aa16da093838c262fe0a" + integrity sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ== dependencies: "@trysound/sax" "0.2.0" commander "^7.2.0" - css-select "^4.1.3" - css-tree "^1.1.3" - csso "^4.2.0" + css-select "^5.1.0" + css-tree "^2.2.1" + csso "^5.0.5" picocolors "^1.0.0" - stable "^0.1.8" tdigest@^0.1.1: version "0.1.1" @@ -5952,23 +6017,23 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vite-svg-loader@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/vite-svg-loader/-/vite-svg-loader-3.4.0.tgz#4638827fe86b85ecfcea1ad61dd972c351d5befd" - integrity sha512-xD3yb1FX+f4l9/TmsYIqyki8ncpcVsZ2gEJFh/wLuNNqt55C8OJ+JlcMWOA/Z9gRA+ylV/TA1wmJLxzZkCRqlA== +vite-svg-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vite-svg-loader/-/vite-svg-loader-4.0.0.tgz#1cec4337dba3c23ab13bcabb111896e251b047ac" + integrity sha512-0MMf1yzzSYlV4MGePsLVAOqXsbF5IVxbn4EEzqRnWxTQl8BJg/cfwIzfQNmNQxZp5XXwd4kyRKF1LytuHZTnqA== dependencies: "@vue/compiler-sfc" "^3.2.20" - svgo "^2.7.0" + svgo "^3.0.2" -vite@^2.9.13: - version "2.9.13" - resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.13.tgz#859cb5d4c316c0d8c6ec9866045c0f7858ca6abc" - integrity sha512-AsOBAaT0AD7Mhe8DuK+/kE4aWYFMx/i0ZNi98hJclxb4e0OhQcZYUrvLjIaQ8e59Ui7txcvKMiJC1yftqpQoDw== +vite@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/vite/-/vite-4.1.4.tgz#170d93bcff97e0ebc09764c053eebe130bfe6ca0" + integrity sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg== dependencies: - esbuild "^0.14.27" - postcss "^8.4.13" - resolve "^1.22.0" - rollup "^2.59.0" + esbuild "^0.16.14" + postcss "^8.4.21" + resolve "^1.22.1" + rollup "^3.10.0" optionalDependencies: fsevents "~2.3.2"