Land GitHub discussions first pass

This commit is contained in:
Will Hunt 2021-09-07 17:35:18 +01:00
parent 68d082df19
commit c726335be6
14 changed files with 661 additions and 113 deletions

View File

@ -24,14 +24,14 @@
"dependencies": {
"@octokit/auth-app": "^3.3.0",
"@octokit/auth-token": "^2.4.5",
"@octokit/rest": "^18.5.2",
"@octokit/rest": "^18.10.0",
"@octokit/webhooks": "^9.1.2",
"axios": "^0.21.1",
"cors": "^2.8.5",
"express": "^4.17.1",
"ioredis": "^4.26.0",
"markdown-it": "^12.0.4",
"matrix-bot-sdk": "^0.5.17",
"matrix-bot-sdk": "^0.5.19",
"matrix-widget-api": "^0.1.0-beta.13",
"micromatch": "^4.0.4",
"mime": "^2.5.2",

View File

@ -4,19 +4,24 @@ import { UserTokenStore } from "../UserTokenStore";
import { CommentProcessor } from "../CommentProcessor";
import { MessageSenderClient } from "../MatrixSender";
import { getIntentForUser } from "../IntentUtils";
import { Discussion } from "../Github/Discussion";
import { MatrixEvent, MatrixMessageContent } from "../MatrixEvent";
import { Discussion } from "@octokit/webhooks-types";
import emoji from "node-emoji";
import markdown from "markdown-it";
import { DiscussionCommentCreatedEvent } from "@octokit/webhooks-types";
import { GithubGraphQLClient } from "../Github/GithubInstance";
import LogWrapper from "../LogWrapper";
export interface GitHubDiscussionConnectionState {
owner: string;
repo: string;
id: number;
internalId: string;
discussion: number;
category: number;
}
// const log = new LogWrapper("GitHubDiscussion");
// const md = new markdown();
const log = new LogWrapper("GitHubDiscussion");
const md = new markdown();
/**
* Handles rooms connected to a github repo.
@ -30,24 +35,33 @@ export class GitHubDiscussionConnection implements IConnection {
static readonly QueryRoomRegex = /#github_disc_(.+)_(.+)_(\d+):.*/;
readonly sentEvents = new Set<string>(); //TODO: Set some reasonable limits
public static async createDiscussionRoom(
as: Appservice, userId: string, owner: string, repo: string, discussion: Discussion,
tokenStore: UserTokenStore, commentProcessor: CommentProcessor, messageClient: MessageSenderClient
as: Appservice, userId: string|null, owner: string, repo: string, discussion: Discussion,
tokenStore: UserTokenStore, commentProcessor: CommentProcessor, messageClient: MessageSenderClient,
) {
const commentIntent = await getIntentForUser({
login: discussion.author.login,
avatarUrl: discussion.author.avatarUrl,
login: discussion.user.login,
avatarUrl: discussion.user.avatar_url,
}, as);
const state: GitHubDiscussionConnectionState = {
owner,
repo,
id: discussion.id,
internalId: discussion.node_id,
discussion: discussion.number,
category: discussion.category.id,
};
const invite = [as.botUserId];
if (userId) {
invite.push(userId);
}
const roomId = await commentIntent.underlyingClient.createRoom({
invite: [userId, as.botUserId],
preset: 'private_chat',
name: `${discussion.title} (${owner}/${name})`,
invite,
preset: 'public_chat',
name: `${discussion.title} (${owner}/${repo})`,
topic: emoji.emojify(`Under ${discussion.category.emoji} ${discussion.category.name}`),
room_alias_name: `github_disc_${owner.toLowerCase()}_${repo.toLowerCase()}_${discussion.number}`,
initial_state: [{
content: state,
@ -57,11 +71,11 @@ export class GitHubDiscussionConnection implements IConnection {
});
await commentIntent.sendEvent(roomId, {
msgtype: 'm.text',
body: discussion.bodyText,
formatted_body: discussion.bodyHTML,
body: discussion.body,
formatted_body: md.render(discussion.body),
format: 'org.matrix.custom.html',
});
await as.botIntent.joinRoom(roomId);
await as.botIntent.ensureJoined(roomId);
return new GitHubDiscussionConnection(roomId, as, state, '', tokenStore, commentProcessor, messageClient);
}
@ -72,6 +86,7 @@ export class GitHubDiscussionConnection implements IConnection {
private tokenStore: UserTokenStore,
private commentProcessor: CommentProcessor,
private messageClient: MessageSenderClient) {
}
public isInterestedInStateEvent(eventType: string, stateKey: string) {
@ -79,13 +94,16 @@ export class GitHubDiscussionConnection implements IConnection {
}
public async onMessageEvent(ev: MatrixEvent<MatrixMessageContent>) {
const octokit = this.tokenStore.getOctokitForUser(ev.sender);
if (!octokit) {
// Use Reply - Also mention user.
const octokit = await this.tokenStore.getOctokitForUser(ev.sender);
if (octokit === null) {
// TODO: Use Reply - Also mention user.
await this.as.botClient.sendNotice(this.roomId, `${ev.sender}: Cannot send comment, you are not logged into GitHub`);
return;
}
const qlClient = new GithubGraphQLClient(octokit);
const commentId = await qlClient.addDiscussionComment(this.state.internalId, ev.content.body);
log.info(`Sent ${commentId} for ${ev.event_id} (${ev.sender})`);
this.sentEvents.add(commentId);
}
public get discussionNumber() {
@ -104,7 +122,16 @@ export class GitHubDiscussionConnection implements IConnection {
return `GitHubDiscussion ${this.owner}/${this.repo}#${this.state.discussion}`;
}
public onDiscussionCommentCreated() {
this.messageClient.sendMatrixMessage()
public onDiscussionCommentCreated(data: DiscussionCommentCreatedEvent) {
if (this.sentEvents.has(data.comment.node_id)) {
return;
}
return this.messageClient.sendMatrixMessage(this.roomId, {
body: data.comment.body,
formatted_body: md.render(data.comment.body),
msgtype: 'm.text',
external_url: data.comment.html_url,
'uk.half-shot.matrix-github.discussion.comment_id': data.comment.id,
});
}
}

View File

@ -0,0 +1,156 @@
import { IConnection } from "./IConnection";
import { Appservice, Space } from "matrix-bot-sdk";
import LogWrapper from "../LogWrapper";
import { Octokit } from "@octokit/rest";
import { ReposGetResponseData } from "../Github/Types";
import axios from "axios";
import { GitHubDiscussionConnection } from "./GithubDiscussion";
const log = new LogWrapper("GitHubDiscussionSpace");
export interface GitHubDiscussionSpaceConnectionState {
owner: string;
repo: string;
}
/**
* Handles rooms connected to a github repo.
*/
export class GitHubDiscussionSpace implements IConnection {
static readonly CanonicalEventType = "uk.half-shot.matrix-github.discussion.space";
static readonly EventTypes = [
GitHubDiscussionSpace.CanonicalEventType, // Legacy event, with an awful name.
];
static readonly QueryRoomRegex = /#github_disc_(.+)_(.+):.*/;
static async onQueryRoom(result: RegExpExecArray, opts: {octokit: Octokit, as: Appservice}): Promise<Record<string, unknown>> {
if (!result) {
log.error("Invalid alias pattern");
throw Error("Could not find issue");
}
const [ owner, repo ] = result?.slice(1);
log.info(`Fetching ${owner}/${repo}`);
let repoRes: ReposGetResponseData;
try {
// TODO: Determine if the repo has discussions?
repoRes = (await opts.octokit.repos.get({
owner,
repo,
})).data;
if (!repoRes.owner) {
throw Error('Repo has no owner!');
}
} catch (ex) {
log.error("Failed to get repo:", ex);
throw Error("Could not find repo");
}
const state: GitHubDiscussionSpaceConnectionState = {
owner: repoRes.owner.login.toLowerCase(),
repo: repoRes.name.toLowerCase(),
};
// URL hack so we don't need to fetch the repo itself.
let avatarUrl = undefined;
try {
const profile = await opts.octokit.users.getByUsername({
username: owner,
});
if (profile.data.avatar_url) {
const res = await axios.get(profile.data.avatar_url as string, {
responseType: 'arraybuffer',
});
log.info(`uploading ${profile.data.avatar_url}`);
// This does exist, but headers is silly and doesn't have content-type.
// tslint:disable-next-line: no-any
console.log(res.headers);
const contentType: string = res.headers["content-type"];
const mxcUrl = await opts.as.botClient.uploadContent(
Buffer.from(res.data as ArrayBuffer),
contentType,
`avatar_${profile.data.id}.png`,
);
avatarUrl = {
type: "m.room.avatar",
state_key: "",
content: {
url: mxcUrl,
},
};
}
} catch (ex) {
log.warn("Failed to get avatar for org:", ex);
}
return {
visibility: "public",
name: `${state.owner}/${state.repo} Discussions`,
topic: `GitHub discussion index for ${state.owner}/${state.repo}`,
preset: 'public_chat',
room_alias_name: `github_disc_${owner.toLowerCase()}_${repo.toLowerCase()}`,
initial_state: [
{
type: this.CanonicalEventType,
content: state,
state_key: `${state.owner}/${state.repo}`,
},
avatarUrl,
{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: 'world_readable',
},
},
],
creation_content: {
type: "m.space",
},
power_level_content_override: {
ban: 100,
events_default: 50,
invite: 50,
kick: 100,
notifications: {
room: 100,
},
redact: 100,
state_default: 100,
users_default: 0,
},
};
}
get roomId() {
return this.space.roomId;
}
constructor(public readonly space: Space,
private state: GitHubDiscussionSpaceConnectionState,
private readonly stateKey: string) {}
public isInterestedInStateEvent(eventType: string, stateKey: string) {
return GitHubDiscussionSpace.EventTypes.includes(eventType) && this.stateKey === stateKey;
}
public get repo() {
return this.state.repo.toLowerCase();
}
public get owner() {
return this.state.owner.toLowerCase();
}
public toString() {
return `GitHubDiscussionSpace ${this.owner}/${this.repo}`;
}
public async onDiscussionCreated(discussion: GitHubDiscussionConnection) {
log.info(`Adding connection to ${this.toString()}`);
await this.space.addChildRoom(discussion.roomId);
}
}

View File

@ -64,7 +64,9 @@ export class GitHubIssueConnection implements IConnection {
owner,
repo,
issue_number: issueNumber,
})).data;
// Typing issue
// eslint-disable-next-line @typescript-eslint/no-explicit-any
})).data as any;
} catch (ex) {
log.error("Failed to get issue:", ex);
throw Error("Could not find issue");
@ -78,7 +80,7 @@ export class GitHubIssueConnection implements IConnection {
username: owner,
});
if (profile.data.avatar_url) {
const res = await axios.get(profile.data.avatar_url, {
const res = await axios.get(profile.data.avatar_url as string, {
responseType: 'arraybuffer',
});
log.info(`uploading ${profile.data.avatar_url}`);
@ -239,7 +241,8 @@ export class GitHubIssueConnection implements IConnection {
for (const comment of comments) {
await this.onCommentCreated({
comment,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
comment: comment as any,
action: "fake",
}, false);
this.state.comments_processed++;

View File

@ -95,7 +95,7 @@ export class GitHubRepoConnection implements IConnection {
username: owner,
});
if (profile.data.avatar_url) {
const res = await axios.get(profile.data.avatar_url, {
const res = await axios.get(profile.data.avatar_url as string, {
responseType: 'arraybuffer',
});
log.info(`uploading ${profile.data.avatar_url}`);

View File

@ -0,0 +1,157 @@
import { IConnection } from "./IConnection";
import { Appservice, Space } from "matrix-bot-sdk";
import LogWrapper from "../LogWrapper";
import { Octokit } from "@octokit/rest";
import axios from "axios";
import { GitHubDiscussionSpace } from ".";
const log = new LogWrapper("GitHubOwnerSpace");
export interface GitHubUserSpaceConnectionState {
username: string;
nodeId: string;
}
/**
* Handles rooms connected to a github repo.
*/
export class GitHubUserSpace implements IConnection {
static readonly CanonicalEventType = "uk.half-shot.matrix-github.user.space";
static readonly EventTypes = [
GitHubUserSpace.CanonicalEventType, // Legacy event, with an awful name.
];
static readonly QueryRoomRegex = /#github_(.+):.*/;
static async onQueryRoom(result: RegExpExecArray, opts: {octokit: Octokit, as: Appservice}): Promise<Record<string, unknown>> {
if (!result) {
log.error("Invalid alias pattern");
throw Error("Could not find issue");
}
const [ username ] = result?.slice(1);
log.info(`Fetching ${username}`);
let state: GitHubUserSpaceConnectionState;
let avatarUrl: string|undefined;
let name: string;
try {
// TODO: Determine if the repo has discussions?
const userRes = (await opts.octokit.users.getByUsername({
username,
})).data;
if (!userRes) {
throw Error('User does not exist!');
}
name = userRes.name as string;
state = {
nodeId: userRes.node_id as string,
username: userRes.login as string,
}
avatarUrl = userRes.avatar_url as string;
} catch (ex) {
log.error("Failed to get repo:", ex);
throw Error("Could not find repo");
}
let avatarState: any|undefined;
try {
if (avatarUrl) {
const res = await axios.get(avatarUrl, {
responseType: 'arraybuffer',
});
log.info(`uploading ${avatarUrl}`);
// This does exist, but headers is silly and doesn't have content-type.
const contentType: string = res.headers["content-type"];
const mxcUrl = await opts.as.botClient.uploadContent(
Buffer.from(res.data as ArrayBuffer),
contentType,
`avatar_${state.username}.png`,
);
avatarState = {
type: "m.room.avatar",
state_key: "",
content: {
url: mxcUrl,
},
};
}
} catch (ex) {
log.warn("Failed to get avatar for org:", ex);
}
return {
visibility: "public",
name: `GitHub - ${name} (${state.username.toLowerCase()})`,
topic: `GitHub page of ${state.username.toLowerCase()}`,
preset: 'public_chat',
room_alias_name: `github_${state.username.toLowerCase()}`,
initial_state: [
{
type: this.CanonicalEventType,
content: state,
state_key: state.username.toLowerCase(),
},
avatarState,
{
type: "m.room.history_visibility",
state_key: "",
content: {
history_visibility: 'world_readable',
},
},
],
creation_content: {
type: "m.space",
},
power_level_content_override: {
ban: 100,
events_default: 50,
invite: 50,
kick: 100,
notifications: {
room: 100,
},
redact: 100,
state_default: 100,
users_default: 0,
},
};
}
get roomId() {
return this.space.roomId;
}
constructor(public readonly space: Space,
private state: GitHubUserSpaceConnectionState,
private readonly stateKey: string) {}
public isInterestedInStateEvent(eventType: string, stateKey: string) {
return GitHubUserSpace.EventTypes.includes(eventType) && this.stateKey === stateKey;
}
public get owner() {
return this.state.username.toLowerCase();
}
public toString() {
return `GitHubUserSpace ${this.owner}`;
}
public async onRepoConnectionCreated(discussion: GitHubDiscussionSpace) {
log.info(`Adding connection to ${this.toString()}`);
await this.space.addChildRoom(discussion.roomId);
}
public async ensureDiscussionInSpace(discussion: GitHubDiscussionSpace) {
// TODO: Optimise
const children = await this.space.getChildEntities();
console.log("Foo", children);
if (!children[discussion.roomId]) {
await this.space.addChildRoom(discussion.roomId);
}
}
}

10
src/Connections/index.ts Normal file
View File

@ -0,0 +1,10 @@
export * from "./GithubDiscussionSpace";
export * from "./GithubDiscussion";
export * from "./GithubIssue";
export * from "./GithubProject";
export * from "./GithubRepo";
export * from "./GithubUserSpace";
export * from "./GitlabIssue";
export * from "./GitlabRepo";
export * from "./IConnection";

View File

@ -1,5 +1,5 @@
export interface Discussion {
id: number;
export interface DiscussionQLResponse {
id: string;
number: number;
author: {
login: string;

View File

@ -4,7 +4,7 @@ import { Octokit } from "@octokit/rest";
import { promises as fs } from "fs";
import { BridgeConfigGitHub } from "../Config/Config";
import LogWrapper from "../LogWrapper";
import { Discussion, DiscussionQL } from "./Discussion";
import { DiscussionQLResponse, DiscussionQL } from "./Discussion";
const log = new LogWrapper("GithubInstance");
@ -75,19 +75,19 @@ query($name: String!, $owner: String!, $number: Int!) {
}
}
}`, {name, owner, number}) as any;
return result.repository.discussion as Discussion;
return result.repository.discussion as DiscussionQLResponse;
}
public async createDiscussionComment(number: number) {
// const result = await this.query(`
// mutation($name: String!, $owner: String!, $number: Int!) {
// createDiscussion(input: {}) {
// discussion(number: $number) {
// ${DiscussionQL}
// }
// }
// }`, {name, owner, number}) as any;
// return result.repository.discussion as Discussion;
public async addDiscussionComment(discussionId: string, body: string): Promise<string> {
const result = await this.query(`
mutation addDiscussionComment($discussionId: ID!, $body: String!) {
addDiscussionComment(input: {discussionId: $discussionId, body: $body}) {
comment {
id
}
}
}`, {discussionId, body}) as any;
return result.addDiscussionComment.comment.id as string;
}
public async listDiscussions(owner: string, name: string) {

View File

@ -14,7 +14,8 @@ import { MemoryStorageProvider } from "./Stores/MemoryStorageProvider";
import { NotificationProcessor } from "./NotificationsProcessor";
import { IStorageProvider } from "./Stores/StorageProvider";
import { retry } from "./PromiseUtil";
import { IConnection } from "./Connections/IConnection";
import { IConnection, GitHubDiscussionSpace, GitHubDiscussionConnection, GitHubUserSpace
} from "./Connections";
import { GitHubRepoConnection } from "./Connections/GithubRepo";
import { GitHubIssueConnection } from "./Connections/GithubIssue";
import { GitHubProjectConnection } from "./Connections/GithubProject";
@ -25,8 +26,6 @@ import { GitLabIssueConnection } from "./Connections/GitlabIssue";
import { GetIssueResponse, GetIssueOpts } from "./Gitlab/Types"
import { GitLabClient } from "./Gitlab/Client";
import { BridgeWidgetApi } from "./Widgets/BridgeWidgetApi";
import { GitHubDiscussionConnection } from "./Connections/GithubDiscussion";
import { Discussion } from "./Github/Discussion";
import { ProjectsGetResponseData } from "./Github/Types";
import { NotifFilter, NotificationFilterStateContent } from "./NotificationFilters";
import * as GitHubWebhookTypes from "@octokit/webhooks-types";
@ -71,6 +70,16 @@ export class GithubBridge {
this.messageClient,
);
}
if (GitHubDiscussionSpace.EventTypes.includes(state.type)) {
if (!this.github) {
throw Error('GitHub is not configured');
}
return new GitHubDiscussionSpace(
await this.as.botClient.getSpace(roomId), state.content, state.stateKey
);
}
if (GitHubIssueConnection.EventTypes.includes(state.type)) {
if (!this.github) {
@ -80,6 +89,16 @@ export class GithubBridge {
await issue.syncIssueState();
return issue;
}
if (GitHubUserSpace.EventTypes.includes(state.type)) {
if (!this.github) {
throw Error('GitHub is not configured');
}
return new GitHubUserSpace(
await this.as.botClient.getSpace(roomId), state.content, state.stateKey
);
}
if (GitLabRepoConnection.EventTypes.includes(state.type)) {
if (!this.config.gitlab) {
throw Error('GitLab is not configured');
@ -125,13 +144,22 @@ export class GithubBridge {
(c instanceof GitHubRepoConnection && c.org === org && c.repo === repo)) as (GitHubIssueConnection|GitLabRepoConnection)[];
}
private getConnectionsForGithubRepo(org: string, repo: string): GitHubRepoConnection[] {
org = org.toLowerCase();
repo = repo.toLowerCase();
return this.connections.filter((c) => (c instanceof GitHubRepoConnection && c.org === org && c.repo === repo)) as GitHubRepoConnection[];
}
private getConnectionsForGithubRepoDiscussion(owner: string, repo: string): GitHubDiscussionSpace[] {
owner = owner.toLowerCase();
repo = repo.toLowerCase();
return this.connections.filter((c) => (c instanceof GitHubDiscussionSpace && c.owner === owner && c.repo === repo)) as GitHubDiscussionSpace[];
}
private getConnectionForGithubUser(user: string): GitHubUserSpace {
return this.connections.find(c => c instanceof GitHubUserSpace && c.owner === user.toLowerCase()) as GitHubUserSpace;
}
private getConnectionsForGithubDiscussion(owner: string, repo: string, discussionNumber: number) {
owner = owner.toLowerCase();
@ -411,6 +439,47 @@ export class GithubBridge {
})
});
this.queue.on<GitHubWebhookTypes.DiscussionCreatedEvent>("github.discussion.created", async ({data}) => {
if (!this.github) {
return;
}
const spaces = this.getConnectionsForGithubRepoDiscussion(data.repository.owner.login, data.repository.name);
if (spaces.length === 0) {
log.info(`Not creating discussion ${data.discussion.id} ${data.repository.owner.login}/${data.repository.name}, no target spaces`);
// We don't want to create any discussions if we have no target spaces.
return;
}
let [discussionConnection] = this.getConnectionsForGithubDiscussion(data.repository.owner.login, data.repository.name, data.discussion.id);
if (!discussionConnection) {
try {
// If we don't have an existing connection for this discussion (likely), then create one.
discussionConnection = await GitHubDiscussionConnection.createDiscussionRoom(
this.as,
null,
data.repository.owner.login,
data.repository.name,
data.discussion,
this.tokenStore,
this.commentProcessor,
this.messageClient,
);
this.connections.push(discussionConnection);
} catch (ex) {
log.error(ex);
throw Error('Failed to create discussion room');
}
}
spaces.map(async (c) => {
try {
await c.onDiscussionCreated(discussionConnection);
} catch (ex) {
log.warn(`Failed to add discussion ${c.toString()} failed to handle comment.created:`, ex);
}
})
});
// Fetch all room state
let joinedRooms: string[]|undefined;
while(joinedRooms === undefined) {
@ -459,6 +528,7 @@ export class GithubBridge {
this.connections.push(...connections);
continue;
}
// TODO: Refactor this to be a connection
try {
const accountData = await this.as.botIntent.underlyingClient.getSafeRoomAccountData<AdminAccountData>(
@ -485,6 +555,17 @@ export class GithubBridge {
log.error(`Failed to setup admin room ${roomId}:`, ex);
}
}
// Handle spaces
for (const discussion of this.connections.filter((c) => c instanceof GitHubDiscussionSpace) as GitHubDiscussionSpace[]) {
console.log(discussion);
const user = this.getConnectionForGithubUser(discussion.owner);
console.log("user:", user);
if (user) {
await user.ensureDiscussionInSpace(discussion);
}
}
if (this.config.widgets) {
await this.widgetApi.start(this.config.widgets.port);
}
@ -635,6 +716,22 @@ export class GithubBridge {
}
}
res = GitHubDiscussionSpace.QueryRoomRegex.exec(roomAlias);
if (res) {
if (!this.github) {
throw Error("GitHub is not configured on this bridge");
}
try {
return await GitHubDiscussionSpace.onQueryRoom(res, {
octokit: this.github.octokit,
as: this.as,
});
} catch (ex) {
log.error(`Could not handle alias with GitHubRepoConnection`, ex);
throw ex;
}
}
res = GitHubRepoConnection.QueryRoomRegex.exec(roomAlias);
if (res) {
if (!this.github) {
@ -655,6 +752,22 @@ export class GithubBridge {
}
res = GitHubUserSpace.QueryRoomRegex.exec(roomAlias);
if (res) {
if (!this.github) {
throw Error("GitHub is not configured on this bridge");
}
try {
return await GitHubUserSpace.onQueryRoom(res, {
octokit: this.github.octokit,
as: this.as,
});
} catch (ex) {
log.error(`Could not handle alias with GitHubRepoConnection`, ex);
throw ex;
}
}
throw Error('No regex matching query pattern');
}
@ -737,12 +850,12 @@ export class GithubBridge {
const connection = await GitHubProjectConnection.onOpenProject(project, this.as, adminRoom.userId);
this.connections.push(connection);
});
adminRoom.on("open.discussion", async (owner: string, repo: string, discussions: Discussion) => {
const connection = await GitHubDiscussionConnection.createDiscussionRoom(
this.as, adminRoom.userId, owner, repo, discussions, this.tokenStore, this.commentProcessor, this.messageClient,
);
this.connections.push(connection);
});
// adminRoom.on("open.discussion", async (owner: string, repo: string, discussions: Discussion) => {
// const connection = await GitHubDiscussionConnection.createDiscussionRoom(
// this.as, adminRoom.userId, owner, repo, discussions, this.tokenStore, this.commentProcessor, this.messageClient,
// );
// this.connections.push(connection);
// });
adminRoom.on("open.gitlab-issue", async (issueInfo: GetIssueOpts, res: GetIssueResponse, instanceName: string, instance: GitLabInstance) => {
const [ connection ] = this.getConnectionsForGitLabIssue(instance, issueInfo.projects, issueInfo.issue);
if (connection) {

View File

@ -45,7 +45,7 @@ export default class LogWrapper {
log.info(getMessageString(messageOrObject), { module });
},
warn: (module: string, ...messageOrObject: MsgType[]) => {
const error = messageOrObject[0].error || messageOrObject[1].body?.error;
const error = messageOrObject[0].error || messageOrObject[1]?.body?.error;
if (error === "Room account data not found") {
log.debug(getMessageString(messageOrObject), { module });
return; // This is just noise :|

View File

@ -116,7 +116,8 @@ export class GitHubWatcher extends EventEmitter implements NotificationWatcherTa
pull_number: rawEvent.subject.url_data.number,
owner: rawEvent.repository.owner.login,
repo: rawEvent.repository.name,
})).data;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
})).data as any;
rawEvent.subject.reviews = (await this.octoKit.pulls.listReviews({
pull_number: rawEvent.subject.url_data.number,
owner: rawEvent.repository.owner.login,

View File

@ -63,11 +63,12 @@ export class Webhooks extends EventEmitter {
}
public listen() {
const bindAddr = this.config.webhook.bindAddress || "0.0.0.0";
this.server = this.expressApp.listen(
this.config.webhook.port,
this.config.webhook.bindAddress,
bindAddr,
);
log.info(`Listening on http://${this.config.webhook.bindAddress}:${this.config.webhook.port}`);
log.info(`Listening on http://${bindAddr}:${this.config.webhook.port}`);
}
public stop() {

196
yarn.lock
View File

@ -345,14 +345,14 @@
dependencies:
"@octokit/types" "^6.0.3"
"@octokit/core@^3.2.3":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.3.1.tgz#c6bb6ba171ad84a5f430853a98892cfe8f93d8cd"
integrity sha512-Dc5NNQOYjgZU5S1goN6A/E500yXOfDUFRGQB8/2Tl16AcfvS3H9PudyOe3ZNE/MaVyHPIfC0htReHMJb1tMrvw==
"@octokit/core@^3.5.1":
version "3.5.1"
resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b"
integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==
dependencies:
"@octokit/auth-token" "^2.4.4"
"@octokit/graphql" "^4.5.8"
"@octokit/request" "^5.4.12"
"@octokit/request" "^5.6.0"
"@octokit/request-error" "^2.0.5"
"@octokit/types" "^6.0.3"
before-after-hook "^2.2.0"
@ -392,29 +392,34 @@
"@octokit/types" "^6.12.2"
btoa-lite "^1.0.0"
"@octokit/openapi-types@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-10.0.0.tgz#db4335de99509021f501fc4e026e6ff495fe1e62"
integrity sha512-k1iO2zKuEjjRS1EJb4FwSLk+iF6EGp+ZV0OMRViQoWhQ1fZTk9hg1xccZII5uyYoiqcbC73MRBmT45y1vp2PPg==
"@octokit/openapi-types@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-6.0.0.tgz#7da8d7d5a72d3282c1a3ff9f951c8133a707480d"
integrity sha512-CnDdK7ivHkBtJYzWzZm7gEkanA7gKH6a09Eguz7flHw//GacPJLmkHA3f3N++MJmlxD1Fl+mB7B32EEpSCwztQ==
"@octokit/plugin-paginate-rest@^2.6.2":
version "2.13.3"
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.13.3.tgz#f0f1792230805108762d87906fb02d573b9e070a"
integrity sha512-46lptzM9lTeSmIBt/sVP/FLSTPGx6DCzAdSX3PfeJ3mTf4h9sGC26WpaQzMEq/Z44cOcmx8VsOhO+uEgE3cjYg==
"@octokit/plugin-paginate-rest@^2.16.0":
version "2.16.0"
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.16.0.tgz#09dbda2e5fbca022e3cdf76b63618f7b357c9f0c"
integrity sha512-8YYzALPMvEZ35kgy5pdYvQ22Roz+BIuEaedO575GwE2vb/ACDqQn0xQrTJR4tnZCJn7pi8+AWPVjrFDaERIyXQ==
dependencies:
"@octokit/types" "^6.11.0"
"@octokit/types" "^6.26.0"
"@octokit/plugin-request-log@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.3.tgz#70a62be213e1edc04bb8897ee48c311482f9700d"
integrity sha512-4RFU4li238jMJAzLgAwkBAw+4Loile5haQMQr+uhFq27BmyJXcXSKvoQKqh0agsZEiUlW6iSv3FAgvmGkur7OQ==
"@octokit/plugin-request-log@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85"
integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==
"@octokit/plugin-rest-endpoint-methods@5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.0.0.tgz#cf2cdeb24ea829c31688216a5b165010b61f9a98"
integrity sha512-Jc7CLNUueIshXT+HWt6T+M0sySPjF32mSFQAK7UfAg8qGeRI6OM1GSBxDLwbXjkqy2NVdnqCedJcP1nC785JYg==
"@octokit/plugin-rest-endpoint-methods@^5.9.0":
version "5.9.0"
resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.9.0.tgz#f9a7d8411e7e4e49a65fc95b5cc23cf96bf05e1f"
integrity sha512-Rz67pg+rEJq2Qn/qfHsMiBoP7GL5NDn8Gg0ezGznZ745Ixn1gPusZYZqCXNhICYrIZaVXmusNP0iwPdphJneqQ==
dependencies:
"@octokit/types" "^6.13.0"
"@octokit/types" "^6.26.0"
deprecation "^2.3.1"
"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.2", "@octokit/request-error@^2.0.5":
@ -426,7 +431,16 @@
deprecation "^2.0.0"
once "^1.4.0"
"@octokit/request@^5.3.0", "@octokit/request@^5.4.11", "@octokit/request@^5.4.12", "@octokit/request@^5.4.14":
"@octokit/request-error@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677"
integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==
dependencies:
"@octokit/types" "^6.0.3"
deprecation "^2.0.0"
once "^1.4.0"
"@octokit/request@^5.3.0", "@octokit/request@^5.4.11", "@octokit/request@^5.4.14":
version "5.4.15"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.4.15.tgz#829da413dc7dd3aa5e2cdbb1c7d0ebe1f146a128"
integrity sha512-6UnZfZzLwNhdLRreOtTkT9n57ZwulCve8q3IT/Z477vThu6snfdkBuhxnChpOKNGxcQ71ow561Qoa6uqLdPtag==
@ -438,23 +452,42 @@
node-fetch "^2.6.1"
universal-user-agent "^6.0.0"
"@octokit/rest@^18.5.2":
version "18.5.2"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.5.2.tgz#0369e554b7076e3749005147be94c661c7a5a74b"
integrity sha512-Kz03XYfKS0yYdi61BkL9/aJ0pP2A/WK5vF/syhu9/kY30J8He3P68hv9GRpn8bULFx2K0A9MEErn4v3QEdbZcw==
"@octokit/request@^5.6.0":
version "5.6.1"
resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.1.tgz#f97aff075c37ab1d427c49082fefeef0dba2d8ce"
integrity sha512-Ls2cfs1OfXaOKzkcxnqw5MR6drMA/zWX/LIS/p8Yjdz7QKTPQLMsB3R+OvoxE6XnXeXEE2X7xe4G4l4X0gRiKQ==
dependencies:
"@octokit/core" "^3.2.3"
"@octokit/plugin-paginate-rest" "^2.6.2"
"@octokit/plugin-request-log" "^1.0.2"
"@octokit/plugin-rest-endpoint-methods" "5.0.0"
"@octokit/endpoint" "^6.0.1"
"@octokit/request-error" "^2.1.0"
"@octokit/types" "^6.16.1"
is-plain-object "^5.0.0"
node-fetch "^2.6.1"
universal-user-agent "^6.0.0"
"@octokit/types@^6.0.3", "@octokit/types@^6.10.0", "@octokit/types@^6.11.0", "@octokit/types@^6.12.2", "@octokit/types@^6.13.0", "@octokit/types@^6.7.1":
"@octokit/rest@^18.10.0":
version "18.10.0"
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.10.0.tgz#8a0add9611253e0e31d3ed5b4bc941a3795a7648"
integrity sha512-esHR5OKy38bccL/sajHqZudZCvmv4yjovMJzyXlphaUo7xykmtOdILGJ3aAm0mFHmMLmPFmDMJXf39cAjNJsrw==
dependencies:
"@octokit/core" "^3.5.1"
"@octokit/plugin-paginate-rest" "^2.16.0"
"@octokit/plugin-request-log" "^1.0.4"
"@octokit/plugin-rest-endpoint-methods" "^5.9.0"
"@octokit/types@^6.0.3", "@octokit/types@^6.10.0", "@octokit/types@^6.12.2", "@octokit/types@^6.7.1":
version "6.13.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.13.0.tgz#779e5b7566c8dde68f2f6273861dd2f0409480d0"
integrity sha512-W2J9qlVIU11jMwKHUp5/rbVUeErqelCsO5vW5PKNb7wAXQVUz87Rc+imjlEvpvbH8yUb+KHmv8NEjVZdsdpyxA==
dependencies:
"@octokit/openapi-types" "^6.0.0"
"@octokit/types@^6.16.1", "@octokit/types@^6.26.0":
version "6.26.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.26.0.tgz#b8af298485d064ad9424cb41520541c1bf820346"
integrity sha512-RDxZBAFMtqs1ZPnbUu1e7ohPNfoNhTiep4fErY7tZs995BeHu369Vsh5woMIaFbllRWEZBfvTCS4hvDnMPiHrA==
dependencies:
"@octokit/openapi-types" "^10.0.0"
"@octokit/webhooks-methods@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@octokit/webhooks-methods/-/webhooks-methods-1.0.0.tgz#274799138725c89a3466e3af2026afe755f28c4b"
@ -1156,7 +1189,7 @@ chalk@2.4.1:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
chalk@^2.0.0, chalk@^2.4.2:
chalk@^2.0.0:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@ -1262,6 +1295,11 @@ colorette@^1.2.1:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
colorette@^1.2.2:
version "1.3.0"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af"
integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==
colors@^1.2.1:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
@ -1537,6 +1575,11 @@ domelementtype@^2.0.1, domelementtype@^2.1.0:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e"
integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==
domelementtype@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
domhandler@^3.0.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a"
@ -1551,6 +1594,13 @@ domhandler@^4.0.0:
dependencies:
domelementtype "^2.1.0"
domhandler@^4.2.0:
version "4.2.2"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==
dependencies:
domelementtype "^2.2.0"
domutils@^2.0.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.5.0.tgz#42f49cffdabb92ad243278b331fd761c1c2d3039"
@ -1560,6 +1610,15 @@ domutils@^2.0.0:
domelementtype "^2.0.1"
domhandler "^4.0.0"
domutils@^2.5.2:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
domhandler "^4.2.0"
draht@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/draht/-/draht-1.0.1.tgz#c4c8879923d2130dfa9c5930e956c85d62b1e975"
@ -1670,7 +1729,7 @@ escape-html@~1.0.3:
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
escape-string-regexp@4.0.0:
escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
@ -2313,6 +2372,16 @@ htmlparser2@^4.1.0:
domutils "^2.0.0"
entities "^2.0.0"
htmlparser2@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.0.0"
domutils "^2.5.2"
entities "^2.0.0"
http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3:
version "1.6.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
@ -2733,6 +2802,11 @@ jws@^3.2.1, jws@^3.2.2:
jwa "^1.4.1"
safe-buffer "^5.0.1"
klona@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
kuler@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3"
@ -2892,10 +2966,10 @@ markdown-it@^12.0.4:
mdurl "^1.0.1"
uc.micro "^1.0.5"
matrix-bot-sdk@^0.5.17:
version "0.5.17"
resolved "https://registry.yarnpkg.com/matrix-bot-sdk/-/matrix-bot-sdk-0.5.17.tgz#ce5d1eb9e8fef5c46f0e553493798bf0cd98ea00"
integrity sha512-6Ze0D9OmE/ssOVSn7yIbApjZIKaCrRT2H0GVxOVVSUbRnNHSR0pgfCofO18SSI9zWhzpugiKyocZxNesrppa4A==
matrix-bot-sdk@^0.5.19:
version "0.5.19"
resolved "https://registry.yarnpkg.com/matrix-bot-sdk/-/matrix-bot-sdk-0.5.19.tgz#6ce13359ab53ea0af9dc3ebcbe288c5f6d9c02c6"
integrity sha512-RIPyvQPkOVp2yTKeDgp5rcn6z/DiKdHb6E8c69K+utai8ypRGtfDRj0PGqP+1XzqC9Wb1OFrESCUB5t0ffdC9g==
dependencies:
"@types/express" "^4.17.7"
chalk "^4.1.0"
@ -2910,7 +2984,7 @@ matrix-bot-sdk@^0.5.17:
morgan "^1.10.0"
request "^2.88.2"
request-promise "^4.2.6"
sanitize-html "^1.27.2"
sanitize-html "^2.3.2"
matrix-widget-api@^0.1.0-beta.13:
version "0.1.0-beta.13"
@ -3100,6 +3174,11 @@ 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.23:
version "3.1.25"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152"
integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@ -3349,14 +3428,14 @@ pify@^3.0.0:
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
postcss@^7.0.27:
version "7.0.35"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24"
integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==
postcss@^8.0.2:
version "8.3.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.6.tgz#2730dd76a97969f37f53b9a6096197be311cc4ea"
integrity sha512-wG1cc/JhRgdqB6WHEuyLTedf3KIRuD0hG6ldkFEZNCjRxiC+3i6kkWUUbiJQayP28iwG35cEmAbe98585BYV0A==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
supports-color "^6.1.0"
colorette "^1.2.2"
nanoid "^3.1.23"
source-map-js "^0.6.2"
preact@^10.5.13:
version "10.5.13"
@ -3643,15 +3722,18 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sanitize-html@^1.27.2:
version "1.27.5"
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.27.5.tgz#6c8149462adb23e360e1bb71cc0bae7f08c823c7"
integrity sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A==
sanitize-html@^2.3.2:
version "2.4.0"
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.4.0.tgz#8da7524332eb210d968971621b068b53f17ab5a3"
integrity sha512-Y1OgkUiTPMqwZNRLPERSEi39iOebn2XJLbeiGOBhaJD/yLqtLGu6GE5w7evx177LeGgSE+4p4e107LMiydOf6A==
dependencies:
htmlparser2 "^4.1.0"
lodash "^4.17.15"
deepmerge "^4.2.2"
escape-string-regexp "^4.0.0"
htmlparser2 "^6.0.0"
is-plain-object "^5.0.0"
klona "^2.0.3"
parse-srcset "^1.0.2"
postcss "^7.0.27"
postcss "^8.0.2"
semver@^5.6.0:
version "5.7.1"
@ -3804,6 +3886,11 @@ snowpack@^3.2.2:
optionalDependencies:
fsevents "^2.2.0"
source-map-js@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
source-map-support@^0.5.17, source-map-support@^0.5.19:
version "0.5.19"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
@ -3817,7 +3904,7 @@ source-map@^0.5.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
source-map@^0.6.0, source-map@^0.6.1:
source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@ -3992,13 +4079,6 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
supports-color@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
dependencies:
has-flag "^3.0.0"
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"