mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 13:17:08 +00:00
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 <justinc@element.io>
This commit is contained in:
parent
d602c895f3
commit
55529d7128
1
changelog.d/661.bugfix
Normal file
1
changelog.d/661.bugfix
Normal file
@ -0,0 +1 @@
|
||||
Add support for logging into GitHub via OAuth from bridge widgets.
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -742,6 +742,8 @@ export class Bridge {
|
||||
this.connectionManager,
|
||||
this.botUsersManager,
|
||||
this.as,
|
||||
this.tokenStore,
|
||||
this.github,
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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";
|
||||
|
@ -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<string, string>);
|
||||
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}`;
|
||||
|
@ -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";
|
||||
|
@ -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<Emitter> {
|
||||
}
|
||||
|
||||
public async clearUserToken(type: TokenType, userId: string, instanceUrl?: string): Promise<boolean> {
|
||||
if (!AllowedTokenTypes.includes(type)) {
|
||||
throw Error('Unknown token type');
|
||||
}
|
||||
const key = tokenKey(type, userId, false, instanceUrl);
|
||||
const obj = await this.intent.underlyingClient.getSafeAccountData<StoredTokenData|DeletedTokenData>(key);
|
||||
if (!obj || "deleted" in obj) {
|
||||
|
@ -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,30 +186,37 @@ export class Webhooks extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public async onGitHubGetOauth(req: Request<unknown, unknown, unknown, {error?: string, error_description?: string, code?: string, state?: string}> , res: Response) {
|
||||
log.info(`Got new oauth request`, { state: req.query.state });
|
||||
public async onGitHubGetOauth(req: Request<unknown, unknown, unknown, {error?: string, error_description?: string, code?: string, state?: string, setup_action?: 'install'}> , 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(`<p>Bridge is not configured with OAuth support</p>`);
|
||||
throw new ApiError('Bridge is not configured with OAuth support', ErrCode.DisabledFeature);
|
||||
}
|
||||
if (req.query.error) {
|
||||
return res.status(500).send(`<p><b>GitHub Error</b>: ${req.query.error} ${req.query.error_description}</p>`);
|
||||
throw new ApiError(`GitHub Error: ${req.query.error} ${req.query.error_description}`, ErrCode.Unknown);
|
||||
}
|
||||
if (!req.query.state) {
|
||||
return res.status(400).send(`<p>Missing state</p>`);
|
||||
if (setup_action !== 'install') {
|
||||
if (!state) {
|
||||
throw new ApiError(`Missing state`, ErrCode.BadValue);
|
||||
}
|
||||
if (!req.query.code) {
|
||||
return res.status(400).send(`<p>Missing code</p>`);
|
||||
throw new ApiError(`Missing code`, ErrCode.BadValue);
|
||||
}
|
||||
const exists = await this.queue.pushWait<OAuthRequest, boolean>({
|
||||
eventName: "github.oauth.response",
|
||||
sender: "GithubWebhooks",
|
||||
data: {
|
||||
state: req.query.state,
|
||||
state,
|
||||
},
|
||||
});
|
||||
if (!exists) {
|
||||
return res.status(404).send(`<p>Could not find user which authorised this request. Has it timed out?</p>`);
|
||||
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,
|
||||
@ -220,17 +228,35 @@ export class Webhooks extends EventEmitter {
|
||||
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(`<p><b>GitHub Error</b>: ${result.error} ${result.error_description}</p>`);
|
||||
throw new ApiError(`GitHub Error: ${result.error} ${result.error_description}`, ErrCode.Unknown);
|
||||
}
|
||||
await this.queue.push<GitHubOAuthTokenResponse>({
|
||||
eventName: "github.oauth.tokens",
|
||||
sender: "GithubWebhooks",
|
||||
data: { ...result, state: req.query.state as string },
|
||||
});
|
||||
return res.send(`<p> Your account has been bridged </p>`);
|
||||
} else if (oauthUrl) {
|
||||
// App install.
|
||||
oauthUrl.searchParams.set('oauth-kind', 'organisation');
|
||||
}
|
||||
} catch (ex) {
|
||||
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(`<p>Encountered an error handing oauth request</p>`);
|
||||
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(`<p> Your account has been bridged </p>`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,8 +102,13 @@ export class BridgeWidgetApi {
|
||||
}
|
||||
|
||||
private async getServiceConfig(req: ProvisioningRequest, res: Response<Record<string, unknown>>) {
|
||||
// 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) {
|
||||
if (!req.userId) {
|
||||
@ -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<GetAuthResponse>) {
|
||||
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<GetAuthPollResponse>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,3 +36,24 @@ export interface GetConnectionsForServiceResponse<T extends GetConnectionsRespon
|
||||
connections: T[];
|
||||
canEdit: boolean;
|
||||
}
|
||||
|
||||
|
||||
export interface GetAuthResponseAuthenticated {
|
||||
authenticated: true;
|
||||
user: {
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface GetAuthResponseUnauthenticated {
|
||||
authenticated: false;
|
||||
authUrl: string;
|
||||
stateId: string;
|
||||
}
|
||||
|
||||
|
||||
export type GetAuthResponse = GetAuthResponseAuthenticated|GetAuthResponseUnauthenticated;
|
||||
|
||||
export interface GetAuthPollResponse {
|
||||
state: 'complete'|'waiting';
|
||||
}
|
@ -301,7 +301,7 @@ describe("GenericHookConnection", () => {
|
||||
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 {
|
||||
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
@ -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<GetAuthResponse> {
|
||||
return this.request('GET', `/widgetapi/v1/service/${service}/auth`);
|
||||
}
|
||||
|
||||
async getAuthPoll(service: string, state: string): Promise<GetAuthPollResponse> {
|
||||
return this.request('GET', `/widgetapi/v1/service/${service}/auth/${state}`);
|
||||
}
|
||||
|
||||
async serviceLogout(service: string): Promise<GetAuthResponse> {
|
||||
return this.request('POST', `/widgetapi/v1/service/${service}/auth/logout`);
|
||||
}
|
||||
}
|
||||
|
||||
export const embedTypeParameter = 'io_element_embed_type';
|
||||
|
@ -12,6 +12,7 @@ type Project = DropItem;
|
||||
|
||||
interface IProps {
|
||||
serviceName: string;
|
||||
addNewInstanceUrl?: string;
|
||||
getInstances(): Promise<Instance[]>;
|
||||
getProjects(currentInstance: string, searchTerm?: string, abortController?: AbortController): Promise<Project[]>;
|
||||
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 = <p> You have not connected any {serviceName} instances.
|
||||
<a href={addNewInstanceUrl} rel="noreferrer" target="_blank">Add a new instances</a>.
|
||||
</p>;
|
||||
} else {
|
||||
addNewInstance = <p> You have not connected any {serviceName} instances.</p>;
|
||||
}
|
||||
} else if (addNewInstanceUrl) {
|
||||
addNewInstance = <p><a href={addNewInstanceUrl} rel="noreferrer" target="_blank">Add a new instances</a>.</p>
|
||||
} // otherwise, empty
|
||||
|
||||
return <div>
|
||||
{!searchError && instances === null && <p> Loading {serviceName} instances. </p>}
|
||||
{instances?.length === 0 && <p> You are not logged into any {serviceName} instances. </p>}
|
||||
{searchError && <ErrorPane header="Search error"> {searchError} </ErrorPane> }
|
||||
<InputField visible={!!instances?.length} label={`${serviceName} Instance`} noPadding={true}>
|
||||
<select onChange={onInstancePicked}>
|
||||
{instanceListResults}
|
||||
</select>
|
||||
</InputField>
|
||||
{ addNewInstance }
|
||||
{ currentInstance && <InputField label="Project" noPadding={true}>
|
||||
<DropdownSearch
|
||||
placeholder={`Your project name, such as ${exampleProjectName}`}
|
||||
|
@ -38,14 +38,12 @@ export const DropdownSearch = function<T>({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;
|
||||
}
|
||||
|
94
web/components/roomConfig/Auth.tsx
Normal file
94
web/components/roomConfig/Auth.tsx
Normal file
@ -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<string|null>();
|
||||
|
||||
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 <Button onClick={loginToService}>
|
||||
{ loginLabel }
|
||||
</Button>;
|
||||
}
|
||||
return <p>
|
||||
Logged in as <strong>{authState.user?.name ?? ''}</strong>. <a href="#" onClick={logoutOfService}>Logout</a>
|
||||
</p>;
|
||||
};
|
@ -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}
|
||||
/>;
|
||||
|
@ -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<ConnectionConfigurationProps<never, GitHubRepoResponseItem, GitHubRepoConnectionState>> = ({api, existingConnection, onSave, onRemove }) => {
|
||||
const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<never, GitHubRepoResponseItem, GitHubRepoConnectionState>> = ({
|
||||
showAuthPrompt, loginLabel, serviceConfig, api, existingConnection, onSave, onRemove
|
||||
}) => {
|
||||
// Assume true if we have no auth prompt.
|
||||
const [authedResponse, setAuthResponse] = useState<GetAuthResponse|null>(null);
|
||||
const [enabledHooks, setEnabledHooks] = useState<string[]>(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<ConnectionConfigurationProps<ne
|
||||
const clearInstance = useCallback(() => setConnectionState(null), [setConnectionState]);
|
||||
|
||||
return <form onSubmit={handleSave}>
|
||||
{!existingConnection && <ConnectionSearch
|
||||
{authedResponse && <ServiceAuth onAuthSucceeded={checkAuth} authState={authedResponse} service="github" loginLabel={loginLabel} api={api} />}
|
||||
{!existingConnection && authedResponse?.authenticated && <ConnectionSearch
|
||||
serviceName="GitHub"
|
||||
addNewInstanceUrl={newInstallationUrl}
|
||||
getInstances={getInstances}
|
||||
getProjects={getProjects}
|
||||
onPicked={setInstance}
|
||||
@ -120,14 +152,15 @@ const ConnectionConfiguration: FunctionComponent<ConnectionConfigurationProps<ne
|
||||
</ul>
|
||||
</InputField>
|
||||
<ButtonSet>
|
||||
{ canEdit && <Button type="submit" disabled={!existingConnection && !connectionState}>{ existingConnection ? "Save" : "Add repository" }</Button>}
|
||||
{ canEdit && authedResponse?.authenticated && <Button type="submit" disabled={!existingConnection && !connectionState}>{ existingConnection ? "Save" : "Add repository" }</Button>}
|
||||
{ canEdit && existingConnection && <Button intent="remove" onClick={onRemove}>Remove repository</Button>}
|
||||
</ButtonSet>
|
||||
</form>;
|
||||
};
|
||||
|
||||
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}
|
||||
|
@ -10,31 +10,46 @@ import { LoadingSpinner } from '../elements/LoadingSpinner';
|
||||
|
||||
export interface ConnectionConfigurationProps<SConfig, ConnectionType extends GetConnectionsResponseItem, ConnectionState extends IConnectionState> {
|
||||
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<SConfig, ConnectionType extends GetConnectionsResponseItem, ConnectionState extends IConnectionState> {
|
||||
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<ConnectionConfigurationProps<SConfig, ConnectionType, ConnectionState>>;
|
||||
}
|
||||
|
||||
export const RoomConfig = function<SConfig, ConnectionType extends GetConnectionsResponseItem, ConnectionState extends IConnectionState>(props: IRoomConfigProps<SConfig, ConnectionType, ConnectionState>) {
|
||||
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|{header?: string, message: string, isWarning?: boolean, forPrevious?: boolean}>(null);
|
||||
const [ connections, setConnections ] = useState<ConnectionType[]|null>(null);
|
||||
@ -115,6 +130,8 @@ export const RoomConfig = function<SConfig, ConnectionType extends GetConnection
|
||||
api={api}
|
||||
serviceConfig={serviceConfig}
|
||||
onSave={handleSaveOnCreation}
|
||||
loginLabel={text.login}
|
||||
showAuthPrompt={showAuthPrompt}
|
||||
/>}
|
||||
</section>}
|
||||
{ connections === null && <LoadingSpinner /> }
|
||||
|
@ -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(<App />, root);
|
||||
|
18
web/oauth.html
Normal file
18
web/oauth.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Account</title>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<noscript>
|
||||
<p>
|
||||
Your account has been connected. You may now close this window.
|
||||
</p>
|
||||
</noscript>
|
||||
</main>
|
||||
<script type="module" src="/oauth.tsx"></script>
|
||||
</body>
|
||||
</html>
|
23
web/oauth.scss
Normal file
23
web/oauth.scss
Normal file
@ -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;
|
||||
}
|
40
web/oauth.tsx
Normal file
40
web/oauth.tsx
Normal file
@ -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<string,string> = {
|
||||
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 <>
|
||||
<h1>Could not connect your { ServiceToName[service] } {oauthKind} to Hookshot.</h1>
|
||||
<p>
|
||||
<code>{errcode}</code> {error}
|
||||
</p>
|
||||
</>;
|
||||
}
|
||||
|
||||
return <>
|
||||
<h1>Your { ServiceToName[service] } {oauthKind} has been connected.</h1>
|
||||
<p>You may close this window.</p>
|
||||
</>;
|
||||
}
|
||||
|
||||
if (root) {
|
||||
render(<RenderOAuth />, root);
|
||||
}
|
@ -19,6 +19,14 @@
|
||||
// }
|
||||
// }
|
||||
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
439
yarn.lock
439
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"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user