diff --git a/packages/etl/src/core/types/workspace-connections.ts b/packages/etl/src/core/types/workspace-connections.ts index 4fcded723b..aa93cc8458 100644 --- a/packages/etl/src/core/types/workspace-connections.ts +++ b/packages/etl/src/core/types/workspace-connections.ts @@ -34,6 +34,8 @@ export type TGitlabWorkspaceConnectionType = keyof typeof EGitlabWorkspaceConnec export type TGitlabWorkspaceConnectionData = { id: number | undefined; + name: string | undefined; + organization: string | undefined; url: string | undefined; type: string | undefined; login: string | undefined; diff --git a/packages/etl/src/gitlab/services/api.service.ts b/packages/etl/src/gitlab/services/api.service.ts index bbe3019b65..cd08385c77 100644 --- a/packages/etl/src/gitlab/services/api.service.ts +++ b/packages/etl/src/gitlab/services/api.service.ts @@ -100,4 +100,80 @@ export class GitLabService { const response = await this.client.get(`/projects/${projectId}/members`); return response.data; } + + async addWebhookToProject(projectId: string, url: string, token: string) { + try { + const response = await this.client.post( + `/projects/${projectId}/hooks`, + { url, token, push_events: true, merge_requests_events: true, pipeline_events: true, tag_push_events: true, issues_events: true }, + ); + return response.data; + } catch (error) { + throw error; + } + } + + /** + * + * @param projectId - entityId or gitlab project id + * @param hookId - webhookId or gitlab hook id + * @returns + */ + async removeWebhookFromProject(projectId: string, hookId: string) { + try { + const response = await this.client.delete( + `/projects/${projectId}/hooks/${hookId}` + ); + return response.data; + } catch (error) { + throw error; + } + } + + async addWebhookToGroup(groupId: string, url: string, token: string) { + try { + const response = await this.client.post( + `/groups/${groupId}/hooks`, + { url, token, push_events: true, merge_requests_events: true, pipeline_events: true, tag_push_events: true, issues_events: true }, + ); + return response.data; + } catch (error) { + throw error; + } + } + + /** + * + * @param groupId - entityId or gitlab group id + * @param hookId - webhookId or gitlab hook id + * @returns + */ + async removeWebhookFromGroup(groupId: string, hookId: string) { + try { + const response = await this.client.delete( + `/groups/${groupId}/hooks/${hookId}` + ); + return response.data; + } catch (error) { + throw error; + } + } + + async getGroups() { + try { + const response = await this.client.get("/groups"); + return response.data; + } catch (error) { + throw error; + } + } + + async getProjects() { + try { + const response = await this.client.get("/projects?membership=true&pages=100"); + return response.data; + } catch (error) { + throw error; + } + } } diff --git a/packages/etl/src/gitlab/services/auth.service.ts b/packages/etl/src/gitlab/services/auth.service.ts index 066e6b98f1..1762a46e92 100644 --- a/packages/etl/src/gitlab/services/auth.service.ts +++ b/packages/etl/src/gitlab/services/auth.service.ts @@ -1,4 +1,5 @@ import axios from "axios"; +import crypto from 'crypto'; import { GitLabAuthConfig, GitLabAuthorizeState, GitLabAuthPayload, GitLabTokenResponse } from "../types/auth"; const DEFAULT_SCOPES = ["api", "read_api", "read_user", "read_repository", "profile", "email"]; @@ -51,4 +52,55 @@ export class GitLabAuthService { return { response, state: decodedState }; } + + + /** + * + * @param workspaceId + * @returns workspace webhook secret + */ + + getWorkspaceWebhookSecret(workspaceId: string) { + try { + const GITLAB_CLIENT_SECRET = this.config.clientSecret; + if (!GITLAB_CLIENT_SECRET) { + throw new Error("GITLAB_CLIENT_SECRET is not defined"); + } + + if (!workspaceId) { + throw new Error("workspaceId is not defined"); + } + + // Combine the strings with a delimiter (e.g., ":") + const combined = `${workspaceId}:${GITLAB_CLIENT_SECRET}`; + + // Hash the combined string using SHA-256 + const hash = crypto.createHash('sha256').update(combined).digest('hex'); + + // Return the first 32 characters of the hash + return hash.slice(0, 32); + } catch (error) { + console.error("error getWorkspaceWebhookSecret", error); + return ""; + } + } + + + /** + * + * @param workspaceId + * @param webhookSecret + * @returns boolean + */ + verifyGitlabWebhookSecret(workspaceId: string, webhookSecret: string) { + try { + const webhookHash = this.getWorkspaceWebhookSecret(workspaceId); + if (!webhookHash) return false; + return webhookHash === webhookSecret; + } catch (error) { + console.error("error verifyGitlabWebhookSecret", error); + return false + } + } + } diff --git a/packages/etl/src/gitlab/types/common.ts b/packages/etl/src/gitlab/types/common.ts index 1927c0e3cc..2375b2a5c9 100644 --- a/packages/etl/src/gitlab/types/common.ts +++ b/packages/etl/src/gitlab/types/common.ts @@ -70,6 +70,10 @@ export interface GitlabProject { kind: string; full_path: string; }; + shared_with_groups: { + group_id: number; + group_name: string; + }[]; default_branch: string; visibility: "private" | "internal" | "public"; } @@ -147,3 +151,31 @@ export interface GitlabNote { imported?: boolean; imported_from?: string; } + +export interface GitlabWebhook { + url: string; + token: string; +} + +export interface GitlabEntityData { + id: string; + type: string; + webhookId: string; +} + +export enum GitlabEntityType { + PROJECT = "PROJECT", + GROUP = "GROUP", +} + +export interface IGitlabEntity { + id: string; + name: string; + type: GitlabEntityType; +} + + +export enum EConnectionType { + ENTITY = "ENTITY", + PLANE_PROJECT = "PLANE_PROJECT", +} \ No newline at end of file diff --git a/silo/src/apps/engine/controllers/entity.controller.ts b/silo/src/apps/engine/controllers/entity.controller.ts index 0da3f1f7f4..f87e8ce5c1 100644 --- a/silo/src/apps/engine/controllers/entity.controller.ts +++ b/silo/src/apps/engine/controllers/entity.controller.ts @@ -2,9 +2,12 @@ import { Request, Response } from "express"; import { Controller, Get, Post, Put, Delete } from "@/lib"; import { createEntityConnectionByWorkspaceConnectionId, + deleteEntityConnection, deleteEntityConnectionByWorkspaceConnectionIdAndEntityId, + getEntityConnection, getEntityConnectionByWorkspaceIdAndConnectionId, getEntityConnectionByWorkspaceIdAndConnectionIdAndEntityId, + updateEntityConnection, updateEntityConnectionByWorkspaceConnectionIdAndEntityId, } from "@/db/query/connection"; @@ -128,6 +131,80 @@ export class EntityConnectionController { } } + + @Get("/:id") + async getEntityConnectionByConnectionId(req: Request, res: Response) { + try { + const { id } = req.params; + + if (!id) { + return res.status(400).send({ + message: "Bad Request, expected id to be present.", + }); + } + + const entityConnections = await getEntityConnection(id); + + return res.status(200).send(entityConnections); + } catch (error) { + console.error(error); + return res.status(500).send("Internal Server Error"); + } + } + + @Put("/:id") + async updateEntityConnectionById(req: Request, res: Response) { + try { + const { id } = req.params; + + if (!id) { + return res.status(400).send({ + message: "Bad Request, expected id to be present.", + }); + } + + const payload = { + workspaceSlug: req.body.workspaceSlug, + projectId: req.body.projectId, + entityId: req.body.entityId, + entitySlug: req.body.entitySlug, + entityData: req.body.entityData, + config: req.body.config, + }; + + const entityConnections = await updateEntityConnection( + id, + payload + ); + + return res.status(200).send(entityConnections); + } catch (error) { + console.error(error); + return res.status(500).send("Internal Server Error"); + } + } + + + @Delete("/:id") + async deleteEntityConnectionById(req: Request, res: Response) { + try { + const { id } = req.params; + + if (!id) { + return res.status(400).send({ + message: "Bad Request, expected id to be present.", + }); + } + + const entityConnections = await deleteEntityConnection(id); + + return res.status(200).send(entityConnections); + } catch (error) { + console.error(error); + return res.status(500).send("Internal Server Error"); + } + } + @Delete("/:workspaceId/:workspaceConnectionId/:entityId") async deleteEntityConnection(req: Request, res: Response) { try { diff --git a/silo/src/apps/github/workers/github/event-handlers/issue-comment.handler.ts b/silo/src/apps/github/workers/github/event-handlers/issue-comment.handler.ts index a73c9f9a05..195bbe5275 100644 --- a/silo/src/apps/github/workers/github/event-handlers/issue-comment.handler.ts +++ b/silo/src/apps/github/workers/github/event-handlers/issue-comment.handler.ts @@ -80,14 +80,14 @@ export const syncCommentWithPlane = async ( workspaceConnection.config.userMap.map((obj) => [obj.githubUser.login, obj.planeUser.id]) ); - const planeUsers = await planeClient.users.list(entityConnection.workspaceSlug, entityConnection.projectId); + const planeUsers = await planeClient.users.list(entityConnection.workspaceSlug, entityConnection.projectId ?? ""); let comment: ExIssueComment | null = null; try { comment = await planeClient.issueComment.getIssueCommentWithExternalId( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", issue.id, data.comment.id.toString(), "GITHUB" @@ -101,7 +101,7 @@ export const syncCommentWithPlane = async ( issue.id, data.repository.full_name, entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", planeClient, ghService, userMap, @@ -112,7 +112,7 @@ export const syncCommentWithPlane = async ( if (comment) { await planeClient.issueComment.update( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", issue.id, comment.id, planeComment @@ -121,7 +121,7 @@ export const syncCommentWithPlane = async ( } else { const createdComment = await planeClient.issueComment.create( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", issue.id, planeComment ); @@ -137,7 +137,7 @@ const getPlaneIssue = async (planeClient: PlaneClient, entityConnection: GithubE try { return await planeClient.issue.getIssueWithExternalId( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", issueId.toString(), "GITHUB" ); diff --git a/silo/src/apps/github/workers/github/event-handlers/issue.handler.ts b/silo/src/apps/github/workers/github/event-handlers/issue.handler.ts index fad6a12967..c95b33be1d 100644 --- a/silo/src/apps/github/workers/github/event-handlers/issue.handler.ts +++ b/silo/src/apps/github/workers/github/event-handlers/issue.handler.ts @@ -83,13 +83,13 @@ export const syncIssueWithPlane = async (store: Store, data: GithubIssueDedupPay try { issue = await planeClient.issue.getIssueWithExternalId( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", data.issueNumber.toString(), "GITHUB" ); } catch (error) { } - const planeUsers = await planeClient.users.list(entityConnection.workspaceSlug, entityConnection.projectId); + const planeUsers = await planeClient.users.list(entityConnection.workspaceSlug, entityConnection.projectId ?? ""); const userMap: Record = Object.fromEntries( workspaceConnection.config.userMap.map((obj) => [obj.githubUser.login, obj.planeUser.id]) @@ -103,14 +103,14 @@ export const syncIssueWithPlane = async (store: Store, data: GithubIssueDedupPay data.repositoryName, userMap, entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", planeUsers, ghService, issue ? true : false ); - const states = (await planeClient.state.list(entityConnection.workspaceSlug, entityConnection.projectId)).results; - const users = await planeClient.users.list(entityConnection.workspaceSlug, entityConnection.projectId); + const states = (await planeClient.state.list(entityConnection.workspaceSlug, entityConnection.projectId ?? "")).results; + const users = await planeClient.users.list(entityConnection.workspaceSlug, entityConnection.projectId ?? ""); if (planeIssue.state) { const state = states.find((s) => s.name === planeIssue.state); @@ -120,7 +120,7 @@ export const syncIssueWithPlane = async (store: Store, data: GithubIssueDedupPay } if (planeIssue.labels) { - const labels = (await planeClient.label.list(entityConnection.workspaceSlug, entityConnection.projectId)).results; + const labels = (await planeClient.label.list(entityConnection.workspaceSlug, entityConnection.projectId ?? "")).results; const githubLabel = labels.find((l) => l.name.toLowerCase() === "github"); if (githubLabel) { @@ -137,7 +137,7 @@ export const syncIssueWithPlane = async (store: Store, data: GithubIssueDedupPay const labelPromises = labelsToCreate.map(async (label: any) => { const createdLabel = await planeClient.label.create( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", { name: label.name, color: `#${label.color}`, @@ -182,12 +182,12 @@ export const syncIssueWithPlane = async (store: Store, data: GithubIssueDedupPay } if (issue) { - await planeClient.issue.update(entityConnection.workspaceSlug, entityConnection.projectId, issue.id, planeIssue); + await planeClient.issue.update(entityConnection.workspaceSlug, entityConnection.projectId ?? "", issue.id, planeIssue); await store.set(`silo:issue:${issue.id}`, "true"); } else { const createdIssue = await planeClient.issue.create( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", planeIssue ); @@ -195,12 +195,12 @@ export const syncIssueWithPlane = async (store: Store, data: GithubIssueDedupPay const createLink = async () => { const linkTitle = `[${entityConnection.entitySlug}] ${ghIssue?.data.title} #${ghIssue?.data.number}`; const linkUrl = ghIssue?.data.html_url; - await planeClient.issue.createLink(entityConnection.workspaceSlug, entityConnection.projectId, createdIssue.id, linkTitle, linkUrl); + await planeClient.issue.createLink(entityConnection.workspaceSlug, entityConnection.projectId ?? "", createdIssue.id, linkTitle, linkUrl); } const createLinkBack = async () => { // Get the project for the issue - const project = await planeClient.project.getProject(entityConnection.workspaceSlug, entityConnection.projectId); + const project = await planeClient.project.getProject(entityConnection.workspaceSlug, entityConnection.projectId ?? ""); const comment = `Synced Issue with [Plane](${env.APP_BASE_URL}) Workspace 🔄\n\n[${project.identifier}-${createdIssue.sequence_id} ${createdIssue.name}](${env.APP_BASE_URL}/${entityConnection.workspaceSlug}/projects/${entityConnection.projectId}/issues/${createdIssue.id})`; await ghService.createIssueComment(data.owner, data.repositoryName, Number(data.issueNumber), comment); } diff --git a/silo/src/apps/github/workers/github/event-handlers/pull-request.handler.ts b/silo/src/apps/github/workers/github/event-handlers/pull-request.handler.ts index a2ae204571..a694395d71 100644 --- a/silo/src/apps/github/workers/github/event-handlers/pull-request.handler.ts +++ b/silo/src/apps/github/workers/github/event-handlers/pull-request.handler.ts @@ -59,7 +59,7 @@ const handlePullRequestOpened = async (data: GithubWebhookPayload["webhook-pull- try { const issue = await planeClient.issue.getIssueWithExternalId( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", reference.node.databaseId.toString(), "GITHUB" ); @@ -68,7 +68,7 @@ const handlePullRequestOpened = async (data: GithubWebhookPayload["webhook-pull- // Create a link in the issue for the pull request await planeClient.issue.createLink( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", issue.id, `GitHub PR: ${data.pull_request.title}`, data.pull_request.html_url diff --git a/silo/src/apps/github/workers/plane/event-handlers/issue-comment.handler.ts b/silo/src/apps/github/workers/plane/event-handlers/issue-comment.handler.ts index 23c3ec3c2b..5f8be6b0c1 100644 --- a/silo/src/apps/github/workers/plane/event-handlers/issue-comment.handler.ts +++ b/silo/src/apps/github/workers/plane/event-handlers/issue-comment.handler.ts @@ -50,7 +50,7 @@ const handleCommentSync = async (store: Store, payload: PlaneWebhookPayload) => // Get the issue associated with the comment const issue = await planeClient.issue.getIssue( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", payload.issue ); @@ -66,7 +66,7 @@ const handleCommentSync = async (store: Store, payload: PlaneWebhookPayload) => const comment = await planeClient.issueComment.getComment( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", payload.issue, payload.id ); @@ -88,7 +88,7 @@ const handleCommentSync = async (store: Store, payload: PlaneWebhookPayload) => ) { await planeClient.issueComment.update( entityConnection.workspaceSlug, - entityConnection.projectId, + entityConnection.projectId ?? "", payload.issue, payload.id, { @@ -110,8 +110,8 @@ const createOrUpdateGitHubComment = async ( entityConnection: GithubEntityConnection, credentials: TServiceCredentials ) => { - const owner = entityConnection.entitySlug.split("/")[0]; - const repo = entityConnection.entitySlug.split("/")[1]; + const owner = (entityConnection.entitySlug ?? "").split("/")[0]; + const repo = (entityConnection.entitySlug ?? "").split("/")[1]; const assetImagePrefix = imagePrefix + workspaceConnection.workspaceId + "/" + credentials.user_id; diff --git a/silo/src/apps/github/workers/plane/event-handlers/issue.handler.ts b/silo/src/apps/github/workers/plane/event-handlers/issue.handler.ts index 3b5de3d10e..e24a6f2526 100644 --- a/silo/src/apps/github/workers/plane/event-handlers/issue.handler.ts +++ b/silo/src/apps/github/workers/plane/event-handlers/issue.handler.ts @@ -58,7 +58,7 @@ const handleIssueSync = async (store: Store, payload: PlaneWebhookPayload) => { const issue = await planeClient.issue.getIssue(entityConnection.workspaceSlug, payload.project, payload.id); - const labels = await planeClient.label.list(entityConnection.workspaceSlug, entityConnection.projectId); + const labels = await planeClient.label.list(entityConnection.workspaceSlug, entityConnection.projectId ?? ""); const githubIssue = await createOrUpdateGitHubIssue( githubService, planeClient, @@ -73,7 +73,7 @@ const handleIssueSync = async (store: Store, payload: PlaneWebhookPayload) => { if (!issue.external_id || !issue.external_source || (issue.external_source && issue.external_source !== "GITHUB")) { // Add the external id and source const addExternalId = async () => { - await planeClient.issue.update(entityConnection.workspaceSlug, entityConnection.projectId, payload.id, { + await planeClient.issue.update(entityConnection.workspaceSlug, entityConnection.projectId ?? "", payload.id, { external_id: githubIssue?.data.number.toString(), external_source: "GITHUB", }); @@ -83,14 +83,8 @@ const handleIssueSync = async (store: Store, payload: PlaneWebhookPayload) => { const createLink = async () => { const linkTitle = `[${entityConnection.entitySlug}] ${githubIssue?.data.title} #${githubIssue?.data.number}`; const linkUrl = githubIssue?.data.html_url; - await planeClient.issue.createLink( - entityConnection.workspaceSlug, - entityConnection.projectId, - issue.id, - linkTitle, - linkUrl - ); - }; + await planeClient.issue.createLink(entityConnection.workspaceSlug, entityConnection.projectId ?? "", issue.id, linkTitle, linkUrl); + } // Execute all the promises await Promise.all([addExternalId(), createLink()]); @@ -118,9 +112,9 @@ const createOrUpdateGitHubIssue = async ( workspaceConnection.config.userMap.map((obj) => [obj.planeUser.id, obj.githubUser]) ); - const owner = entityConnection.entitySlug.split("/")[0]; - const repo = entityConnection.entitySlug.split("/")[1]; - const issueImagePrefix = imagePrefix + workspaceConnection.workspaceId + "/" + credentials.user_id; + const owner = (entityConnection.entitySlug ?? "").split("/")[0]; + const repo = (entityConnection.entitySlug ?? "").split("/")[1]; + const issueImagePrefix = imagePrefix + workspaceConnection.workspaceId + "/" + credentials.user_id const transformedGithubIssue = await transformPlaneIssue(issue, issueImagePrefix, labels, owner, repo, userMap); @@ -146,7 +140,7 @@ const createOrUpdateGitHubIssue = async ( } else { const createdIssue = await githubUserService.createIssue(transformedGithubIssue as GithubIssue); - const project = await planeClient.project.getProject(entityConnection.workspaceSlug, entityConnection.projectId); + const project = await planeClient.project.getProject(entityConnection.workspaceSlug, entityConnection.projectId ?? ""); const comment = `Synced Issue with [Plane](${env.APP_BASE_URL}) Workspace 🔄\n\n[${project.identifier}-${issue.sequence_id} ${issue.name}](${env.APP_BASE_URL}/${entityConnection.workspaceSlug}/projects/${entityConnection.projectId}/issues/${issue.id})`; await githubService.createIssueComment(owner, repo, Number(createdIssue.data.number), comment); diff --git a/silo/src/apps/gitlab/controller/index.ts b/silo/src/apps/gitlab/controller/index.ts index e97bca8913..e7f4020f6e 100644 --- a/silo/src/apps/gitlab/controller/index.ts +++ b/silo/src/apps/gitlab/controller/index.ts @@ -1,9 +1,9 @@ import { importTaskManger, integrationTaskManager } from "@/apps/engine/worker"; import { env } from "@/env"; -import { Controller, Get, Post } from "@/lib"; +import { Controller, Delete, Get, Post } from "@/lib"; import { ExIssueLabel } from "@plane/sdk"; import { Request, Response } from "express"; -import { createGitLabAuth, createGitLabService, GitlabWebhookEvent } from "@plane/etl/gitlab"; +import { createGitLabAuth, createGitLabService, GitlabWebhookEvent, GitlabWebhook, GitlabEntityType, IGitlabEntity, GitlabEntityData, EConnectionType } from "@plane/etl/gitlab"; import { verifyGitlabToken } from "../helpers"; import { createOrUpdateCredentials, getCredentialsByOnlyWorkspaceId, deleteCredentialsForWorkspace } from "@/db/query"; import { @@ -12,8 +12,18 @@ import { updateWorkspaceConnection, deleteEntityConnectionByWorkspaceConnectionId, deleteWorkspaceConnection, + createEntityConnectionByWorkspaceConnectionId, + getEntityConnectionByWorkspaceIdAndConnectionIdAndEntityId, + getEntityConnectionByEntityId, + getEntityConnectionByWorkspaceIdAndConnectionId, + deleteEntityConnection, + getEntityConnection, + getEntityConnectionByWorkspaceAndProjectId, + getEntityConnectionByWorkspaceConnectionAndProjectId, } from "@/db/query/connection"; import { logger } from "@/logger"; +import { EIntegrationType } from "@/types"; +import { gitlabAuthService, getGitlabClientService } from "../services"; @Controller("/api/gitlab") export class GitlabController { @@ -168,7 +178,7 @@ export class GitlabController { }); } - res.send({ message: "Successfully connected to GitLab" }); + res.redirect(`${env.APP_BASE_URL}/${authState.workspace_slug}/settings/integrations/gitlab/`); } @Post("/gitlab-webhook") @@ -243,4 +253,231 @@ export class GitlabController { Number(env.DEDUP_INTERVAL) ); } + + + @Post("/entity-connections/:workspaceId/:workspaceConnectionId") + async addEntityConnection(req: Request, res: Response, next: any) { + try { + const { workspaceId, workspaceConnectionId } = req.params; + const { entityId, entityType, workspaceSlug, entitySlug } = req.body; + + if (!workspaceId || !workspaceConnectionId || !entityId || !entityType || !workspaceSlug || !entitySlug) { + return res.status(400).send({ + message: "Bad Request, expected workspaceId, workspaceConnectionId, and entityId to be present.", + }); + } + + // Check for existing connections + const connections = await getEntityConnectionByWorkspaceIdAndConnectionIdAndEntityId( + workspaceId, + workspaceConnectionId, + entityId + ); + + if (connections.length > 0) { + return res.status(201).json({ error: "Entity connection already exists" }); + } + + + // Add webhook to gitlab + const { url, token } = this.getWorkspaceWebhookData(workspaceId) + const gitlabClientService = await getGitlabClientService(workspaceId) + + // based on enum either add to project or group + let webhookId; + console.log("inside add webhook to project", entityType, entityId, url, token) + if (entityType === GitlabEntityType.PROJECT) { + const { id: hookId } = await gitlabClientService.addWebhookToProject(entityId, url, token); + webhookId = hookId; + } else { + const { id: hookId } = await gitlabClientService.addWebhookToGroup(entityId, url, token); + webhookId = hookId; + } + + const connection = await createEntityConnectionByWorkspaceConnectionId( + workspaceId, + workspaceConnectionId, + { + workspaceId, + workspaceConnectionId, + connectionType: EConnectionType.ENTITY, + workspaceSlug, + entityId, + entitySlug, + entityData: { + id: entityId, + type: entityType, + webhookId + } + } + ); + + res.status(200).json(connection); + } catch (error) { + next(error); + } + } + + @Post("/entity-project-connections/:workspaceId/:workspaceConnectionId") + async addProjectConnection(req: Request, res: Response, next: any) { + try { + const { workspaceId, workspaceConnectionId } = req.params; + const { projectId, workspaceSlug, config } = req.body; + + if (!workspaceId || !workspaceConnectionId || !projectId || !workspaceSlug) { + return res.status(400).send({ + message: "Bad Request, expected workspaceId, workspaceConnectionId, and projectId to be present.", + }); + } + + // Check for existing connections + const connections = await getEntityConnectionByWorkspaceConnectionAndProjectId( + workspaceId, + projectId + ); + + if (connections.length > 0) { + return res.status(201).json({ error: "Entity connection already exists" }); + } + + const connection = await createEntityConnectionByWorkspaceConnectionId( + workspaceId, + workspaceConnectionId, + { + workspaceId, + workspaceConnectionId, + connectionType: EConnectionType.PLANE_PROJECT, + projectId, + workspaceSlug, + config + } + ); + + res.status(200).json(connection); + } catch (error) { + next(error); + } + } + + @Delete("/entity-connections/:workspaceId/:connectionId") + async removeEntityConnection(req: Request, res: Response, next: any) { + try { + const { workspaceId, connectionId } = req.params; + + const [entityConnection] = await getEntityConnection(connectionId); + if (!entityConnection) { + return res.status(400).json({ error: "Entity connection not found" }); + } + + const gitlabClientService = await getGitlabClientService(workspaceId) + const entityData = entityConnection.entityData as GitlabEntityData + + if (entityData.type === GitlabEntityType.PROJECT) { + await gitlabClientService.removeWebhookFromProject(entityData.id, entityData.webhookId); + } else if (entityData.type === GitlabEntityType.GROUP) { + await gitlabClientService.removeWebhookFromGroup(entityData.id, entityData.webhookId); + } + + const connection = await deleteEntityConnection(connectionId); + res.status(200).json(connection); + } catch (error) { + next(error); + } + } + + + @Post("/webhook/:workspaceId") + async webhook(req: Request, res: Response, next: any) { + try { + const workspaceId = req.params.workspaceId; + const webhookSecret = req.headers["x-gitlab-token"]?.toString(); + + if (!gitlabAuthService.verifyGitlabWebhookSecret(workspaceId, webhookSecret ?? "")) { + return res.status(400).send({ + message: "Webhook received", + }); + } else { + res.status(202).send({ + message: "Webhook received", + }); + + const webhookEvent = req.body as GitlabWebhookEvent; + + // Generate a unique job ID + const jobId = `gitlab-${webhookEvent.object_kind}-${Date.now()}`; + + // Forward the event to the task manager to process + await integrationTaskManager.registerTask( + { + route: "gitlab-webhook", + jobId: jobId, + type: webhookEvent.object_kind, + }, + webhookEvent + ); + } + } catch (error) { + next(error) + } + } + + + @Get("/entity-connections/:workspaceId") + async getAllEntityConnections(req: Request, res: Response, next: any) { + try { + const workspaceId = req.params.workspaceId; + const [workspaceConnections] = await getWorkspaceConnections(workspaceId, EIntegrationType.GITLAB); + const entityConnections = await getEntityConnectionByWorkspaceIdAndConnectionId(workspaceId, workspaceConnections.id); + res.status(200).json(entityConnections); + } catch (error) { + console.error(error) + next(error) + } + } + + @Get("/entities/:workspaceId") + async getProjectAndGroups(req: Request, res: Response, next: any) { + try { + const workspaceId = req.params.workspaceId; + const entities = []; + const gitlabClientService = await getGitlabClientService(workspaceId) + const [projects, groups] = await Promise.all([ + gitlabClientService.getProjects(), + gitlabClientService.getGroups(), + ]); + + if (projects.length) { + entities.push(...projects.map((project: IGitlabEntity) => ({ id: project.id, name: project.name, type: GitlabEntityType.PROJECT }))); + } + + if (groups.length) { + entities.push(...groups.map((group: IGitlabEntity) => ({ id: group.id, name: group.name, type: GitlabEntityType.GROUP }))); + } + res.status(200).json(entities); + } catch (error) { + console.error(error) + next(error) + } + } + + getWorkspaceWebhookData(workspaceId: string) { + try { + if (!workspaceId) { + throw new Error("workspaceId is not defined"); + } + const workspaceWebhookSecret = gitlabAuthService.getWorkspaceWebhookSecret(workspaceId); + const webhookURL = `${env.SILO_API_BASE_URL}/silo/api/gitlab/webhook/${workspaceId}`; + const gitlabWebhook: GitlabWebhook = { + url: webhookURL, + token: workspaceWebhookSecret, + }; + return gitlabWebhook; + } catch (error) { + console.error("error getWorkspaceWebhook", error); + throw error; + } + } + } + + diff --git a/silo/src/apps/gitlab/helpers/connection-details.ts b/silo/src/apps/gitlab/helpers/connection-details.ts index 054ebaee00..185d92cfeb 100644 --- a/silo/src/apps/gitlab/helpers/connection-details.ts +++ b/silo/src/apps/gitlab/helpers/connection-details.ts @@ -1,20 +1,28 @@ import { + EConnectionType, gitlabEntityConnectionSchema, GitlabMergeRequestEvent, gitlabWorkspaceConnectionSchema, + MergeRequestEvent, } from "@plane/etl/gitlab"; -import { GitlabConnectionDetails } from "../types"; -import { getEntityConnectionByEntityId, getWorkspaceConnectionById } from "@/db/query/connection"; +import { GitlabConnectionDetails, GitlabEntityConnection } from "../types"; +import { getAllEntityConnectionsByEntityIds, getEntityConnectionByEntityId, getEntityConnectionByWorkspaceConnectionAndProjectId, getWorkspaceConnectionById } from "@/db/query/connection"; import { logger } from "@/logger"; -import { verifyEntityConnection, verifyWorkspaceConnection } from "@/types"; +import { verifyEntityConnection, verifyEntityConnections, verifyWorkspaceConnection } from "@/types"; export const getGitlabConnectionDetails = async ( data: GitlabMergeRequestEvent ): Promise => { - const entityConnectionSet = await getEntityConnectionByEntityId(data.project.id.toString()); + // for connection now user can also just have a group connection + // project payload has array of groups attached to it so we need to check + // if we have any group connections among them and use that or not then check for project connection + + const projectRelatedGroups = data.project.shared_with_groups.map((group) => group.group_id?.toString()); + const entityConnectionIds = projectRelatedGroups.concat(data.project.id.toString()); + const entityConnectionSet = await getAllEntityConnectionsByEntityIds(entityConnectionIds); if (!entityConnectionSet || entityConnectionSet.length === 0) { - logger.error(`[GITLAB] Entity connection not found for project ${data.project.id}, skipping...`); + logger.error(`[GITLAB] Entity connection not found for project ${entityConnectionIds}, skipping...`); return; } @@ -30,6 +38,15 @@ export const getGitlabConnectionDetails = async ( const workspaceConnection = workspaceConnectionSet[0]; + // project connections for this workspace connection for target state mapping + let projectConnectionSet = await getEntityConnectionByWorkspaceConnectionAndProjectId(workspaceConnection.id); + projectConnectionSet = projectConnectionSet.filter((connection) => connection.connectionType === EConnectionType.PLANE_PROJECT); + + if (projectConnectionSet.length === 0) { + logger.error(`[GITLAB] Plane Project connection not found for project ${data.project.id}, skipping...`); + return; + } + const verifiedWorkspaceConnection = verifyWorkspaceConnection( gitlabWorkspaceConnectionSchema, workspaceConnection as any @@ -37,8 +54,11 @@ export const getGitlabConnectionDetails = async ( const verifiedEntityConnection = verifyEntityConnection(gitlabEntityConnectionSchema, entityConnection as any); + const verifiedProjectConnection = verifyEntityConnections(gitlabEntityConnectionSchema, projectConnectionSet as any); + return { workspaceConnection: verifiedWorkspaceConnection, entityConnection: verifiedEntityConnection, + projectConnections: verifiedProjectConnection, }; }; diff --git a/silo/src/apps/gitlab/services/auth.ts b/silo/src/apps/gitlab/services/auth.ts new file mode 100644 index 0000000000..1da15d7c00 --- /dev/null +++ b/silo/src/apps/gitlab/services/auth.ts @@ -0,0 +1,8 @@ +import { env } from "@/env"; +import { createGitLabAuth } from "@plane/etl/gitlab"; + +export const gitlabAuthService = createGitLabAuth({ + clientId: env.GITLAB_CLIENT_ID, + clientSecret: env.GITLAB_CLIENT_SECRET, + redirectUri: `${env.SILO_API_BASE_URL}/silo/api/gitlab/auth/callback`, +}); \ No newline at end of file diff --git a/silo/src/apps/gitlab/services/index.ts b/silo/src/apps/gitlab/services/index.ts new file mode 100644 index 0000000000..0360ff6eb9 --- /dev/null +++ b/silo/src/apps/gitlab/services/index.ts @@ -0,0 +1,2 @@ +export * from "./auth"; +export * from "./service" \ No newline at end of file diff --git a/silo/src/apps/gitlab/services/service.ts b/silo/src/apps/gitlab/services/service.ts new file mode 100644 index 0000000000..d88866c61b --- /dev/null +++ b/silo/src/apps/gitlab/services/service.ts @@ -0,0 +1,32 @@ +import { createOrUpdateCredentials, getCredentialsByOnlyWorkspaceId, getCredentialsByWorkspaceId } from "@/db/query"; +import { createGitLabService } from "@plane/etl/gitlab"; + +export const getGitlabClientService = async (workspaceId: string, ) => { + try { + // Create or update credentials + const credentials = await getCredentialsByOnlyWorkspaceId(workspaceId, "GITLAB",); + const { source_access_token, source_refresh_token, target_access_token, user_id: userId } = credentials[0]; + + if (!source_access_token || !source_refresh_token || !userId || !target_access_token) { + throw new Error("No gitlab credentials available for the given workspaceId and userId"); + } + + const gitlabService = createGitLabService( + source_access_token, + source_refresh_token, + async (access_token, refresh_token) => { + await createOrUpdateCredentials(workspaceId, userId, { + source_access_token: access_token, + source_refresh_token: refresh_token, + target_access_token: target_access_token, + source: "GITLAB", + }); + } + ); + return gitlabService; + } catch (error) { + console.error(error); + throw error; + } +} + diff --git a/silo/src/apps/gitlab/types.ts b/silo/src/apps/gitlab/types.ts index d207591960..bb747438a3 100644 --- a/silo/src/apps/gitlab/types.ts +++ b/silo/src/apps/gitlab/types.ts @@ -7,4 +7,5 @@ export type GitlabEntityConnection = EntityConnection { const result = await getConnectionAndCredentials(data); if (!result) return; - const [{ workspaceConnection, entityConnection }, credentials] = result; + const [{ workspaceConnection, entityConnection, projectConnections }, credentials] = result; const { closingReferences, nonClosingReferences } = getReferredIssues( data.object_attributes.title, @@ -133,9 +133,6 @@ export const handleMergeRequest = async (data: GitlabMergeRequestEvent) => { return; } - const targetState = getTargetState(data, entityConnection); - if (!targetState) return; - const referredIssues = ["MR_CLOSED", "MR_MERGED"].includes(classifyMergeRequestEvent(data)) ? closingReferences : [...closingReferences, ...nonClosingReferences]; @@ -162,10 +159,31 @@ export const handleMergeRequest = async (data: GitlabMergeRequestEvent) => { workspaceConnection.sourceHostname! ); + // we need to get the plane project attached to referred issues and then get target state for each and then do the updates + // get the exissues from identifiers it'll have the project attached + // loop through the referred issues, check if it has a plane project attached and then update the state using project connection target state + + const allReferredIssues = await Promise.all( + referredIssues.map(async (reference) => { + const issue = await planeClient.issue.getIssueByIdentifier( + entityConnection.workspaceSlug, + reference.identifier, + reference.sequence + ); + return { reference, issue }; + }) + ); + const updatedIssues = await Promise.all( - referredIssues.map((reference) => - updateIssue(planeClient, entityConnection, reference, targetState, data.project.id) - ) + allReferredIssues.map(async (referredIssue) => { + const targetProject = projectConnections.find((project) => project.projectId === referredIssue.issue.project); + if (targetProject) { + const targetState = getTargetState(data, targetProject); + if (!targetState) return null; + return updateIssue(planeClient, entityConnection, referredIssue.reference, targetState, data.project.id); + } + return null; + }) ); const validIssues = updatedIssues.filter((issue): issue is IssueWithReference => issue !== null); diff --git a/silo/src/apps/slack/worker/handlers/handle-message.ts b/silo/src/apps/slack/worker/handlers/handle-message.ts index 08369ce7dc..3d7e978b66 100644 --- a/silo/src/apps/slack/worker/handlers/handle-message.ts +++ b/silo/src/apps/slack/worker/handlers/handle-message.ts @@ -32,13 +32,13 @@ export const handleMessageEvent = async (data: SlackEventPayload) => { const eConnection = entityConnection[0]; - const members = await planeClient.users.list(workspaceConnection.workspaceSlug, eConnection.projectId); + const members = await planeClient.users.list(workspaceConnection.workspaceSlug, eConnection.projectId ?? ""); const userInfo = await slackService.getUserInfo(data.event.user); const issueId = eConnection.entitySlug; const planeUser = members.find((member) => member.email === userInfo?.user.profile.email); - await planeClient.issueComment.create(workspaceConnection.workspaceSlug, eConnection.projectId, issueId, { + await planeClient.issueComment.create(workspaceConnection.workspaceSlug, eConnection.projectId ?? "", issueId ?? "", { comment_html: `

${data.event.text}

`, external_source: "SLACK_COMMENT", external_id: data.event.event_ts, diff --git a/silo/src/apps/slack/worker/plane-webhook-handlers/handle-comment-webhook.ts b/silo/src/apps/slack/worker/plane-webhook-handlers/handle-comment-webhook.ts index 2d732b260b..4825db5878 100644 --- a/silo/src/apps/slack/worker/plane-webhook-handlers/handle-comment-webhook.ts +++ b/silo/src/apps/slack/worker/plane-webhook-handlers/handle-comment-webhook.ts @@ -26,12 +26,12 @@ const handleCommentSync = async (payload: WebhookIssueCommentPayload) => { if (credentials && credentials.length > 0 && credentials[0].source_access_token) { await slackService.sendMessageAsUser( slackData.channel, - entityConnection[0].entityId, + entityConnection[0].entityId ?? "", data.data.comment_stripped, credentials[0].source_access_token ); } else { - await slackService.sendThreadMessage(slackData.channel, entityConnection[0].entityId, data.data.comment_stripped); + await slackService.sendThreadMessage(slackData.channel, entityConnection[0].entityId ?? "", data.data.comment_stripped); } } }; diff --git a/silo/src/db/migrations/0003_typical_eternity.sql b/silo/src/db/migrations/0003_typical_eternity.sql new file mode 100644 index 0000000000..89ee75f888 --- /dev/null +++ b/silo/src/db/migrations/0003_typical_eternity.sql @@ -0,0 +1,4 @@ +ALTER TABLE "silo"."entity_connections" ALTER COLUMN "entity_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "silo"."entity_connections" ALTER COLUMN "entity_slug" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "silo"."entity_connections" ALTER COLUMN "project_id" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "silo"."entity_connections" ADD COLUMN "connection_type" varchar(50) DEFAULT 'ENTITY'; \ No newline at end of file diff --git a/silo/src/db/migrations/meta/0003_snapshot.json b/silo/src/db/migrations/meta/0003_snapshot.json new file mode 100644 index 0000000000..497235ff78 --- /dev/null +++ b/silo/src/db/migrations/meta/0003_snapshot.json @@ -0,0 +1,537 @@ +{ + "id": "8db1d4b5-7542-4785-85fe-ddfa487cdc87", + "prevId": "1bb1de16-c54b-43a3-874e-dc5d0a9ced7c", + "version": "7", + "dialect": "postgresql", + "tables": { + "silo.entity_connections": { + "name": "entity_connections", + "schema": "silo", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "connection_type": { + "name": "connection_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": false, + "default": "'ENTITY'" + }, + "entity_id": { + "name": "entity_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "entity_slug": { + "name": "entity_slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "entity_data": { + "name": "entity_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_slug": { + "name": "workspace_slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "workspace_connection_id": { + "name": "workspace_connection_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "entity_connections_workspace_id_idx": { + "name": "entity_connections_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "entity_connections_workspace_connection_id_workspace_connections_id_fk": { + "name": "entity_connections_workspace_connection_id_workspace_connections_id_fk", + "tableFrom": "entity_connections", + "tableTo": "workspace_connections", + "schemaTo": "silo", + "columnsFrom": [ + "workspace_connection_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "silo.workspace_connections": { + "name": "workspace_connections", + "schema": "silo", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_slug": { + "name": "workspace_slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "target_hostname": { + "name": "target_hostname", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "source_hostname": { + "name": "source_hostname", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "connection_type": { + "name": "connection_type", + "type": "varchar(50)", + "primaryKey": false, + "notNull": true + }, + "connection_slug": { + "name": "connection_slug", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "connection_id": { + "name": "connection_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "connection_data": { + "name": "connection_data", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "credentials_id": { + "name": "credentials_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "workspace_connections_workspace_id_idx": { + "name": "workspace_connections_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_connections_credentials_id_credentials_id_fk": { + "name": "workspace_connections_credentials_id_credentials_id_fk", + "tableFrom": "workspace_connections", + "tableTo": "credentials", + "schemaTo": "silo", + "columnsFrom": [ + "credentials_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "silo.credentials": { + "name": "credentials", + "schema": "silo", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "user_email": { + "name": "user_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_access_token": { + "name": "source_access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_refresh_token": { + "name": "source_refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source_hostname": { + "name": "source_hostname", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "target_access_token": { + "name": "target_access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_pat": { + "name": "is_pat", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + } + }, + "indexes": { + "workspace_id_idx": { + "name": "workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "silo.job_configs": { + "name": "job_configs", + "schema": "silo", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "meta": { + "name": "meta", + "type": "json", + "primaryKey": false, + "notNull": false, + "default": "'{}'::json" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "silo.jobs": { + "name": "jobs", + "schema": "silo", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "config_id": { + "name": "config_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "migration_type": { + "name": "migration_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "project_id": { + "name": "project_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_slug": { + "name": "workspace_slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "initiator_id": { + "name": "initiator_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "initiator_email": { + "name": "initiator_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "completed_batch_count": { + "name": "completed_batch_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "transformed_batch_count": { + "name": "transformed_batch_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "total_batch_count": { + "name": "total_batch_count", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 0 + }, + "start_time": { + "name": "start_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "end_time": { + "name": "end_time", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar", + "primaryKey": false, + "notNull": false, + "default": "'CREATED'" + }, + "is_cancelled": { + "name": "is_cancelled", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + } + }, + "indexes": { + "project_idx": { + "name": "project_idx", + "columns": [ + { + "expression": "project_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "jobs_config_id_job_configs_id_fk": { + "name": "jobs_config_id_job_configs_id_fk", + "tableFrom": "jobs", + "tableTo": "job_configs", + "schemaTo": "silo", + "columnsFrom": [ + "config_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": { + "silo": "silo" + }, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/silo/src/db/migrations/meta/_journal.json b/silo/src/db/migrations/meta/_journal.json index d19cfe33f8..43dc5d3b19 100644 --- a/silo/src/db/migrations/meta/_journal.json +++ b/silo/src/db/migrations/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1734517027583, "tag": "0002_lazy_spyke", "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1735053738861, + "tag": "0003_typical_eternity", + "breakpoints": true } ] } \ No newline at end of file diff --git a/silo/src/db/query/connection.ts b/silo/src/db/query/connection.ts index 1f73bbc494..cd768fc7fb 100644 --- a/silo/src/db/query/connection.ts +++ b/silo/src/db/query/connection.ts @@ -1,4 +1,4 @@ -import { SQL, and, eq } from "drizzle-orm"; +import { SQL, and, eq, inArray } from "drizzle-orm"; import { db } from "../config/db.config"; import { entityConnections, workspaceConnections } from "../schema/connection.schema"; @@ -62,6 +62,13 @@ export async function createEntityConnection(data: typeof entityConnections.$inf return await db.insert(entityConnections).values(data).returning(); } +export async function getAllEntityConnectionsByEntityIds(entityIds: string[]) { + return await db + .select() + .from(entityConnections) + .where(inArray(entityConnections.entityId, entityIds)); +} + export async function getEntityConnectionByWorkspaceId(workspaceId: string) { return await db.select().from(entityConnections).where(eq(entityConnections.workspaceId, workspaceId)); } @@ -87,6 +94,19 @@ export async function getEntityConnectionByWorkspaceAndProjectId(workspaceId: st .where(and(...conditions)); } +export async function getEntityConnectionByWorkspaceConnectionAndProjectId(workspaceConnectionId: string, projectId?: string) { + const conditions: SQL[] = [eq(entityConnections.workspaceConnectionId, workspaceConnectionId)]; + + if (projectId) { + conditions.push(eq(entityConnections.projectId, projectId)); + } + + return await db + .select() + .from(entityConnections) + .where(and(...conditions)); +} + export async function getEntityConnectionByEntitySlug(workspaceId: string, projectId: string, entitySlug: string) { return await db .select() @@ -134,16 +154,21 @@ export async function deleteEntityConnectionByWorkspaceConnectionId(workspaceCon // Get entity connection by workspaceId, and workspaceConnectionId export async function getEntityConnectionByWorkspaceIdAndConnectionId( workspaceId: string, - workspaceConnectionId: string + workspaceConnectionId: string, + connectionType?: string ) { + const conditions: SQL[] = [eq(entityConnections.workspaceId, workspaceId), + eq(entityConnections.workspaceConnectionId, workspaceConnectionId)]; + + if (connectionType) { + conditions.push(eq(entityConnections.connectionType, connectionType)); + } + return await db .select() .from(entityConnections) .where( - and( - eq(entityConnections.workspaceId, workspaceId), - eq(entityConnections.workspaceConnectionId, workspaceConnectionId) - ) + and(...conditions) ); } diff --git a/silo/src/db/schema/connection.schema.ts b/silo/src/db/schema/connection.schema.ts index f7cb6f667b..80b55a6765 100644 --- a/silo/src/db/schema/connection.schema.ts +++ b/silo/src/db/schema/connection.schema.ts @@ -51,13 +51,16 @@ export const entityConnections = schema.table( { id: uuid("id").defaultRandom().primaryKey(), + // connection type can be a plane project connection or a secondary entity connection or a mapping in both + connectionType: varchar("connection_type", { length: 50 }).default("ENTITY"), + // Entity Type can be a github repository, and entity id can be the repository id - entityId: varchar("entity_id", { length: 255 }).notNull(), - entitySlug: varchar("entity_slug", { length: 255 }).notNull(), + entityId: varchar("entity_id", { length: 255 }), + entitySlug: varchar("entity_slug", { length: 255 }), entityData: jsonb("entity_data").default({}), // Project and WorkspaceId of Plane - projectId: uuid("project_id").notNull(), + projectId: uuid("project_id"), workspaceId: uuid("workspace_id").notNull(), workspaceSlug: varchar("workspace_slug", { length: 255 }).notNull(), diff --git a/silo/src/types/index.ts b/silo/src/types/index.ts index d3f465dc7a..4998c32f50 100644 --- a/silo/src/types/index.ts +++ b/silo/src/types/index.ts @@ -40,6 +40,18 @@ export function verifyEntityConnection(schema: T, data: Ent const parsedConfig = schema.parse(data.config); return { ...data, config: parsedConfig }; } + +export function verifyEntityConnections( + schema: T, + dataArray: EntityConnection[] +): EntityConnection[] { + return dataArray.map((data) => { + const parsedConfig = schema.parse(data.config); + return { ...data, config: parsedConfig }; + }); +} + + export type Credentials = typeof credentials.$inferInsert; export type BulkIssuePayload = ExIssue & { @@ -52,3 +64,10 @@ export type BulkIssuePayload = ExIssue & { values: ExIssuePropertyValue; }[]; }; + + +export enum EIntegrationType { + GITLAB = "GITLAB", + GITHUB = "GITHUB", + SLACK = "SLACK", +} diff --git a/web/core/components/profile/connection/personal-account-view.tsx b/web/core/components/profile/connection/personal-account-view.tsx index c78907d716..34dd883f0c 100644 --- a/web/core/components/profile/connection/personal-account-view.tsx +++ b/web/core/components/profile/connection/personal-account-view.tsx @@ -59,7 +59,7 @@ export function PersonalAccountConnectView(props: TPersonalAccountConnectProps) return (
- + {provider.icon && }
{provider.name}
{provider.description}
diff --git a/web/core/components/profile/connection/user-connections-view.tsx b/web/core/components/profile/connection/user-connections-view.tsx index 52b8fca41a..228e0bf60a 100644 --- a/web/core/components/profile/connection/user-connections-view.tsx +++ b/web/core/components/profile/connection/user-connections-view.tsx @@ -36,7 +36,13 @@ export const UserConnectionsView = observer(({ workspaceId }: { workspaceId: str useEffect(() => { if (selectedWorkspace && !isLoading) { - const response = getConnectionsByWorkspaceSlug(selectedWorkspace.slug); + let response = getConnectionsByWorkspaceSlug(selectedWorkspace.slug); + response = (response || []).reduce((allConnections: any, connection: any) => { + if (connection.connectionType !== "GITLAB") { + allConnections.push(connection); + } + return allConnections; + }, []); setConnections(response); } diff --git a/web/ee/components/integrations/gitlab/authentication/connect-organization.tsx b/web/ee/components/integrations/gitlab/authentication/connect-organization.tsx index 509a7c1739..a77f944842 100644 --- a/web/ee/components/integrations/gitlab/authentication/connect-organization.tsx +++ b/web/ee/components/integrations/gitlab/authentication/connect-organization.tsx @@ -79,13 +79,10 @@ export const ConnectOrganization: FC = observer(() => { {workspaceConnection?.connectionData?.login}
-
-
{workspaceConnection?.connectionData?.login}
-
Gitlab org added by and time
-
+
{workspaceConnection?.connectionData?.organization || workspaceConnection?.connectionData?.name}
) : (
diff --git a/web/ee/components/integrations/gitlab/sections/repository-mapping/entity-item.tsx b/web/ee/components/integrations/gitlab/sections/repository-mapping/entity-item.tsx index cac2a4845f..bc06c75fbc 100644 --- a/web/ee/components/integrations/gitlab/sections/repository-mapping/entity-item.tsx +++ b/web/ee/components/integrations/gitlab/sections/repository-mapping/entity-item.tsx @@ -1,11 +1,11 @@ "use client"; -import { FC, useState } from "react"; +import { Component, FC, ReactElement, useState } from "react"; import { observer } from "mobx-react"; import Image from "next/image"; -import { IProject } from "@plane/types"; -import { Button, ModalCore } from "@plane/ui"; // plane web components +import { Button, ModalCore } from "@plane/ui"; +import { Logo } from "@/components/common"; import { FormEdit } from "@/plane-web/components/integrations/gitlab"; // plane web hooks import { useGitlabIntegration } from "@/plane-web/hooks/store"; @@ -13,19 +13,20 @@ import { useGitlabIntegration } from "@/plane-web/hooks/store"; import { TGitlabEntityConnection } from "@/plane-web/types/integrations/gitlab"; // public images import GitlabLogo from "@/public/services/gitlab.svg"; +import { EConnectionType } from "@plane/etl/gitlab"; type TEntityConnectionItem = { - project: IProject; entityConnection: TGitlabEntityConnection; }; export const EntityConnectionItem: FC = observer((props) => { // props - const { project, entityConnection } = props; + const { entityConnection } = props; // hooks const { - entity: { deleteEntity }, + getProjectById, + entityConnection: { deleteEntityConnection }, } = useGitlabIntegration(); // states @@ -43,7 +44,7 @@ export const EntityConnectionItem: FC = observer((props) const handleDeleteModalSubmit = async () => { try { setDeleteLoader(true); - await deleteEntity(entityConnection.id); + await deleteEntityConnection(entityConnection.id); setDeleteModal(false); } catch (error) { console.error("handleDeleteModalSubmit", error); @@ -52,16 +53,35 @@ export const EntityConnectionItem: FC = observer((props) } }; - return ( - <> - {/* entity edit */} + const getEntityName = (entityconnection: TGitlabEntityConnection): string => { + if (entityconnection.connectionType === EConnectionType.PLANE_PROJECT) { + return getProjectById(entityconnection.projectId)?.name || ""; + } else if (entityconnection.connectionType === EConnectionType.ENTITY) { + return entityconnection.entitySlug; + } + return ""; + } + const getEntityLogo = (entityconnection: TGitlabEntityConnection): ReactElement => { + if (entityconnection.connectionType === EConnectionType.PLANE_PROJECT) { + const project = getProjectById(entityconnection.projectId); + if (!project) return <>; + return ; + } else if (entityconnection.connectionType === EConnectionType.ENTITY) { + return Gitlab Logo; + } + return <>; + }; + + return ( + + <> {/* entity delete */}
-
Remove entity
-
Are you sure you want to remove this entity?
+
Remove Connection
+
Are you sure you want to remove this connection?
+ {/* entity edit */}
- Gitlab Logo + {getEntityLogo(entityConnection)}
- {entityConnection?.entityData?.name} ({entityConnection?.entityData?.full_name}) -
-
- Issues are synced to {project?.name || "Project"} + {getEntityName(entityConnection)}
- + {entityConnection.connectionType === EConnectionType.PLANE_PROJECT && ( + + )} diff --git a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/create-entity.tsx b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/create-entity.tsx new file mode 100644 index 0000000000..b747d54833 --- /dev/null +++ b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/create-entity.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { Dispatch, FC, SetStateAction, useState } from "react"; +import { observer } from "mobx-react"; +import { Button, ModalCore } from "@plane/ui"; +// plane web components +import { EntityForm } from "@/plane-web/components/integrations/gitlab"; +// plane web hooks +import { useGitlabIntegration } from "@/plane-web/hooks/store"; +// plane web types +import { TGitlabEntityConnection, TProjectMap } from "@/plane-web/types/integrations/gitlab"; +// local imports +import { projectMapInit } from "../root"; + +type TEntityFormCreate = { + modal: boolean; + handleModal: Dispatch>; +}; + +export const EntityFormCreate: FC = observer((props) => { + // props + const { modal, handleModal } = props; + + // hooks + const { + entityConnection: { createEntityConnection }, + } = useGitlabIntegration(); + + // states + const [isSubmitting, setIsSubmitting] = useState(false); + const [projectMap, setProjectMap] = useState(projectMapInit); + + // handlers + const handleProjectMapChange = (key: T, value: TProjectMap[T]) => { + setProjectMap((prev) => ({ ...prev, [key]: value })); + }; + + const handleSubmit = async () => { + try { + setIsSubmitting(true); + const payload: Partial = { + entityId: projectMap.entityId, + projectId: projectMap.projectId, + }; + await createEntityConnection(payload); + + handleModal(false); + } catch (error) { + console.error("handleSubmit", error); + } finally { + setIsSubmitting(false); + } + }; + + return ( + handleModal(false)}> +
+
Link Gitlab Project or Group
+ +
+ + +
+
+
Pull request automation
+
+ With Gitlab integration Enabled, you can automate issue workflows +
+
+
+ +
+ + +
+
+
+
+ ); +}); diff --git a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/create-project-entity.tsx b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/create-project-entity.tsx new file mode 100644 index 0000000000..6a779d02dd --- /dev/null +++ b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/create-project-entity.tsx @@ -0,0 +1,117 @@ +"use client"; + +import { Dispatch, FC, SetStateAction, useState } from "react"; +import { observer } from "mobx-react"; +import { Button, ModalCore } from "@plane/ui"; +// plane web components +import { ProjectForm, StateForm } from "@/plane-web/components/integrations/gitlab"; +// plane web hooks +import { useGitlabIntegration } from "@/plane-web/hooks/store"; +// plane web types +import { TGitlabEntityConnection, TProjectMap, TStateMap } from "@/plane-web/types/integrations/gitlab"; +// local imports +import { projectMapInit, stateMapInit } from "../root"; + +type TProjectEntityFormCreate = { + modal: boolean; + handleModal: Dispatch>; +}; + +export const ProjectEntityFormCreate: FC = observer((props) => { + // props + const { modal, handleModal } = props; + + // hooks + const { + workspace, + fetchStates, + entityConnection: { createProjectConnection }, + } = useGitlabIntegration(); + + // states + const [isSubmitting, setIsSubmitting] = useState(false); + const [projectMap, setProjectMap] = useState(projectMapInit); + const [stateMap, setStateMap] = useState(stateMapInit); + + // derived values + const workspaceSlug = workspace?.slug || undefined; + + // handlers + const handleProjectMapChange = (key: T, value: TProjectMap[T]) => { + if (key === "projectId") { + setProjectMap((prev) => ({ ...prev, [key]: value })); + if (workspaceSlug && value && value != projectMap.projectId) { + fetchStates(workspaceSlug, value); + setStateMap(stateMapInit); + } + } else { + setProjectMap((prev) => ({ ...prev, [key]: value })); + } + }; + + const handleStateMapChange = (key: T, value: (typeof stateMap)[T]) => { + setStateMap((prev) => ({ ...prev, [key]: value })); + }; + + const handleSubmit = async () => { + try { + setIsSubmitting(true); + + const payload: Partial = { + entityId: projectMap.entityId, + projectId: projectMap.projectId, + config: { + states: { mergeRequestEventMapping: stateMap }, + }, + }; + await createProjectConnection(payload); + + handleModal(false); + } catch (error) { + console.error("handleSubmit", error); + } finally { + setIsSubmitting(false); + } + }; + + return ( + handleModal(false)}> +
+
Link Gitlab repository and Plane project
+ +
+ + +
+
+
Pull request automation
+
+ Configure pull request state mapping from Gitlab to Plane +
+
+ { + projectMap.projectId && ( +
+ +
+ ) + } +
+ +
+ + +
+
+
+
+ ); +}); diff --git a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/edit.tsx b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/edit.tsx index c225238016..d5044b8361 100644 --- a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/edit.tsx +++ b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/edit.tsx @@ -4,18 +4,19 @@ import { Dispatch, FC, SetStateAction, useEffect, useState } from "react"; import { observer } from "mobx-react"; import { Button, ModalCore } from "@plane/ui"; // plane web components -import { ProjectForm, StateForm } from "@/plane-web/components/integrations/github"; +import { ProjectForm, StateForm } from "@/plane-web/components/integrations/gitlab"; // plane web hooks -import { useGithubIntegration } from "@/plane-web/hooks/store"; +import { useGitlabIntegration } from "@/plane-web/hooks/store"; // plane web types -import { E_STATE_MAP_KEYS, TGithubEntityConnection, TProjectMap, TStateMap } from "@/plane-web/types/integrations"; +import { E_STATE_MAP_KEYS, TProjectMap, TStateMap } from "@/plane-web/types/integrations"; // local imports import { projectMapInit, stateMapInit } from "../root"; +import { TGitlabEntityConnection } from "@/plane-web/types/integrations/gitlab"; type TFormEdit = { modal: boolean; handleModal: Dispatch>; - data: TGithubEntityConnection; + data: TGitlabEntityConnection; }; export const FormEdit: FC = observer((props) => { @@ -26,8 +27,8 @@ export const FormEdit: FC = observer((props) => { const { workspace, fetchStates, - entity: { updateEntity }, - } = useGithubIntegration(); + entityConnection: { updateEntityConnection }, + } = useGitlabIntegration(); // states const [isSubmitting, setIsSubmitting] = useState(false); @@ -58,14 +59,13 @@ export const FormEdit: FC = observer((props) => { try { setIsSubmitting(true); - const payload: Partial = { - entityId: projectMap.entityId, + const payload: Partial = { projectId: projectMap.projectId, config: { states: { mergeRequestEventMapping: stateMap }, }, }; - await updateEntity(data.id, payload); + await updateEntityConnection(data.id, payload); handleModal(false); } catch (error) { @@ -112,7 +112,7 @@ export const FormEdit: FC = observer((props) => {
Pull request automation
- With Github integration Enabled, you can automate issue workflows + With Gitlab integration Enabled, you can automate issue workflows
diff --git a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/entity-form.tsx b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/entity-form.tsx new file mode 100644 index 0000000000..4a939b054a --- /dev/null +++ b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/entity-form.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { FC } from "react"; +import { observer } from "mobx-react"; +import Image from "next/image"; +import { Briefcase } from "lucide-react"; +// components +import { Logo } from "@/components/common"; +// plane web components +import { Dropdown } from "@/plane-web/components/importers/ui"; +// plane web hooks +import { useGitlabIntegration } from "@/plane-web/hooks/store"; +// plane web types +import { TProjectMap } from "@/plane-web/types/integrations"; +// public images +import GitlabLogo from "@/public/services/gitlab.svg"; + +type TEntityForm = { + value: TProjectMap; + handleChange: (key: T, value: TProjectMap[T]) => void; +}; + +export const EntityForm: FC = observer((props) => { + // props + const { value, handleChange } = props; + // hooks + const { data: { gitlabEntityIds, gitlabEntityById } } = useGitlabIntegration(); + + // derived values + const entities = (gitlabEntityIds || []) + .map((id) => { + const entity = gitlabEntityById(id); + return entity || undefined; + }) + .filter((entity) => entity !== undefined && entity !== null); + + return ( +
+
+
Gitlab Project or Group
+ ({ + key: entity?.id.toString() || "", + label: entity?.name || "", + value: entity?.id.toString() || "", + data: entity, + }))} + value={value?.entityId || undefined} + placeHolder="Choose Entity..." + onChange={(value: string | undefined) => handleChange("entityId", value || undefined)} + iconExtractor={() => ( +
+ Gitlab Logo +
+ )} + queryExtractor={(option) => option.name} + /> +
+
+ ); +}); diff --git a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/index.ts b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/index.ts index 3cf3b3dc11..d1976c760a 100644 --- a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/index.ts +++ b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/index.ts @@ -1,4 +1,6 @@ -export * from "./create"; +export * from "./create-project-entity"; export * from "./edit"; export * from "./project-form"; export * from "./state-form"; +export * from "./create-entity"; +export * from "./entity-form"; diff --git a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/project-form.tsx b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/project-form.tsx index 247439f44f..2c9aaac40f 100644 --- a/web/ee/components/integrations/gitlab/sections/repository-mapping/form/project-form.tsx +++ b/web/ee/components/integrations/gitlab/sections/repository-mapping/form/project-form.tsx @@ -13,7 +13,6 @@ import { useGitlabIntegration } from "@/plane-web/hooks/store"; // plane web types import { TProjectMap } from "@/plane-web/types/integrations"; // public images -import GitlabLogo from "@/public/services/gitlab.svg"; type TProjectForm = { value: TProjectMap; @@ -29,17 +28,9 @@ export const ProjectForm: FC = observer((props) => { workspace, projectIdsByWorkspaceSlug, getProjectById, - data: { gitlabRepositoryIds, gitlabRepositoryById }, } = useGitlabIntegration(); // derived values - const repositories = (gitlabRepositoryIds || []) - .map((id) => { - const repository = gitlabRepositoryById(id); - return repository || undefined; - }) - .filter((repo) => repo !== undefined && repo !== null); - const workspaceSlug = workspace?.slug || undefined; const planeProjectIds = (workspaceSlug && projectIdsByWorkspaceSlug(workspaceSlug)) || []; const planeProjects = planeProjectIds @@ -48,27 +39,6 @@ export const ProjectForm: FC = observer((props) => { return (
-
-
Gitlab Repository
- ({ - key: repo?.id.toString() || "", - label: repo?.name || "", - value: repo?.id.toString() || "", - data: repo, - }))} - value={value?.entityId || undefined} - placeHolder="Choose Repository..." - onChange={(value: string | undefined) => handleChange("entityId", value || undefined)} - iconExtractor={() => ( -
- Gitlab Logo -
- )} - queryExtractor={(option) => option.name} - /> -
-
Plane Project
= observer((props) => { return (
- {planeProjectStates && + {planeProjectStates && projectId && GIT_PR_DATA.map((gitState) => ( { const { workspace, fetchProjects, - getProjectById, auth: { workspaceConnectionIds }, - data: { fetchGitlabRepositories }, - entity: { entityIds, entityById, fetchEntities }, + data: { fetchGitlabEntities }, + entityConnection: { entityConnectionIds, entityConnectionById, fetchEntityConnections }, } = useGitlabIntegration(); // states const [modalCreateOpen, setModalCreateOpen] = useState(false); + const [modalProjectCreateOpen, setModalProjectCreateOpen] = useState(false); + // derived values const workspaceId = workspace?.id || undefined; const workspaceSlug = workspace?.slug || undefined; const workspaceConnectionId = workspaceConnectionIds[0] || undefined; - const entityConnectionMap = entityIds.map((id) => entityById(id)); - const entityConnection = entityConnectionMap.reduce( - (result: { [key: string]: TGitlabEntityConnection[] }, entity) => { - if (!entity) return result; + const entityConnections = entityConnectionIds.map((id) => { + const entityConnection = entityConnectionById(id); + if (!entityConnection || (entityConnection.connectionType !== EConnectionType.ENTITY)) { + return; + } + return entityConnection; + }); - const projectId = entity?.projectId || "default"; - - if (!result[projectId]) result[projectId] = []; - result[projectId].push(entity); - - return result; - }, - {} - ); + const projectEntityConnections = entityConnectionIds.map((id) => { + const entityConnection = entityConnectionById(id); + if (!entityConnection || (entityConnection.connectionType !== EConnectionType.PLANE_PROJECT)) { + return; + } + return entityConnection; + }); // fetching external api token - const { isLoading: isGitlabReposLoading } = useSWR( - workspaceConnectionId && workspaceId ? `INTEGRATION_GITLAB_REPOS_${workspaceId}_${workspaceConnectionId}` : null, - workspaceConnectionId && workspaceId ? async () => fetchGitlabRepositories() : null, + const { isLoading: isGitlabEntitiesLoading } = useSWR( + workspaceConnectionId && workspaceId ? `INTEGRATION_GITLAB_ENTITIES_${workspaceId}_${workspaceConnectionId}` : null, + workspaceConnectionId && workspaceId ? async () => fetchGitlabEntities() : null, { errorRetryCount: 0 } ); @@ -82,55 +85,84 @@ export const RepositoryMappingRoot: FC = observer(() => { // fetching entity connections const { isLoading: isEntitiesLoading } = useSWR( workspaceId ? `INTEGRATION_GITLAB_ENTITY_CONNECTIONS_${workspaceId}` : null, - workspaceId ? async () => fetchEntities() : null, + workspaceId ? async () => fetchEntityConnections() : null, { errorRetryCount: 0 } ); + return ( -
- {/* heading */} -
-
-
Repository Mapping
-
Sync issues from Gitlab repository to Plane projects
+
+
+ {/* heading */} +
+
+
Gitlab Project & Group Connections
+
Sync issues from Gitlab projects or groups to Plane projects
+
+
- + + {/* entity connection list */} + { + isEntitiesLoading && + +
+ + +
+ +
+ } + + {entityConnectionIds && entityConnectionIds.length > 0 && ( +
+ {entityConnections.map((entityConnection, index) => { + if (!entityConnection) return null; + return ( +
+ +
+ ); + })} +
+ )} +
- {/* mapped blocks */} - {entityIds && entityIds.length > 0 && ( -
- {Object.keys(entityConnection).map((projectId, index) => { - const project = projectId ? getProjectById(projectId) : undefined; - if (!project) return null; - return ( -
-
-
- {project && project?.logo_props ? ( - - ) : ( - - )} -
-
{project?.name || "Project"}
-
- -
- {(entityConnection[projectId] || []).map((connection, index) => ( - - ))} -
-
- ); - })} + {/* Add project state mapping blocks */} +
+ {/* heading */} +
+
+
Plane Project Connections
+
Configure pull requests state mapping from Gitlab to Plane projects
+
+
- )} - + {/* Project mapping list */} + {entityConnectionIds && entityConnectionIds.length > 0 && ( +
+ {projectEntityConnections.map((entityConnection, index) => { + if (!entityConnection) return null; + return ( +
+ +
+ ); + })} +
+ )} + + {/* project entity form */} + + +
); }); diff --git a/web/ee/services/integrations/gitlab/data.service.ts b/web/ee/services/integrations/gitlab/data.service.ts index a6f2c78aac..8ae5014c52 100644 --- a/web/ee/services/integrations/gitlab/data.service.ts +++ b/web/ee/services/integrations/gitlab/data.service.ts @@ -1,6 +1,7 @@ import axios, { AxiosInstance } from "axios"; // plane web types import { TGitlabRepository } from "@/plane-web/types/integrations/gitlab"; +import { IGitlabEntity } from "@plane/etl/gitlab"; export class GitlabDataService { protected baseURL: string; @@ -23,4 +24,17 @@ export class GitlabDataService { .catch((error) => { throw error?.response?.data; }); + + /** + * @description fetch gitlab entities + * @param { string } workspaceId + * @returns { Promise } + */ + fetchGitlabEntities = async (workspaceId: string): Promise => + await this.axiosInstance + .get(`/api/gitlab/entities/${workspaceId}`) + .then((res) => res.data) + .catch((error) => { + throw error?.response?.data; + }); } diff --git a/web/ee/services/integrations/gitlab/entity.service.ts b/web/ee/services/integrations/gitlab/entity.service.ts index 1be05ec1d8..afd7d8bcb5 100644 --- a/web/ee/services/integrations/gitlab/entity.service.ts +++ b/web/ee/services/integrations/gitlab/entity.service.ts @@ -14,15 +14,14 @@ export class GitlabEntityService { /** * @description fetch entity connections * @param { string } workspaceId - * @param { string } workspaceConnectionId + * @param { string } connectionType * @returns { Promise } */ fetchEntityConnections = async ( workspaceId: string, - workspaceConnectionId: string ): Promise => await this.axiosInstance - .get(`/api/entity-connections/${workspaceId}/${workspaceConnectionId}`) + .get(`/api/gitlab/entity-connections/${workspaceId}`) .then((res) => res.data) .catch((error) => { throw error?.response?.data; @@ -30,18 +29,14 @@ export class GitlabEntityService { /** * @description fetch entity connection - * @param { string } workspaceId - * @param { string } workspaceConnectionId - * @param { string } entityId + * @param { string } connectionId * @returns { Promise } */ fetchEntityConnection = async ( - workspaceId: string, - workspaceConnectionId: string, - entityId: string + connectionId: string, ): Promise => await this.axiosInstance - .get(`/api/entity-connections/${workspaceId}/${workspaceConnectionId}/${entityId}`) + .get(`/api/entity-connections/${connectionId}`) .then((res) => res.data) .catch((error) => { throw error?.response?.data; @@ -60,7 +55,7 @@ export class GitlabEntityService { entityConnection: Partial ): Promise => await this.axiosInstance - .post(`/api/entity-connections/${workspaceId}/${workspaceConnectionId}`, entityConnection) + .post(`/api/gitlab/entity-connections/${workspaceId}/${workspaceConnectionId}`, entityConnection) .then((res) => res.data) .catch((error) => { throw error?.response?.data; @@ -68,20 +63,16 @@ export class GitlabEntityService { /** * @description update entity connection - * @param { string } workspaceId - * @param { string } workspaceConnectionId - * @param { string } entityId + * @param { string } connectionId * @param { Partial } entityConnection * @returns { Promise } */ updateEntityConnection = async ( - workspaceId: string, - workspaceConnectionId: string, - entityId: string, + connectionId: string, entityConnection: Partial ): Promise => await this.axiosInstance - .put(`/api/entity-connections/${workspaceId}/${workspaceConnectionId}/${entityId}`, entityConnection) + .put(`/api/entity-connections/${connectionId}`, entityConnection) .then((res) => res.data) .catch((error) => { throw error?.response?.data; @@ -89,20 +80,35 @@ export class GitlabEntityService { /** * @description delete entity connection - * @param { string } workspaceId - * @param { string } workspaceConnectionId - * @param { string } entityId + * @param { string } connectionId * @returns { Promise } */ deleteEntityConnection = async ( - workspaceId: string, - workspaceConnectionId: string, - entityId: string + connectionId: string, ): Promise => await this.axiosInstance - .delete(`/api/entity-connections/${workspaceId}/${workspaceConnectionId}/${entityId}`) + .delete(`/api/entity-connections/${connectionId}`) .then(() => undefined) .catch((error) => { throw error?.response?.data; }); + + /** + * @description create project entity connection + * @param { string } workspaceId + * @param { string } workspaceConnectionId + * @param { Partial } entityConnection + * @returns { Promise } + */ + createProjectEntityConnection = async ( + workspaceId: string, + workspaceConnectionId: string, + entityConnection: Partial + ): Promise => + await this.axiosInstance + .post(`/api/gitlab/entity-project-connections/${workspaceId}/${workspaceConnectionId}`, entityConnection) + .then((res) => res.data) + .catch((error) => { + throw error?.response?.data; + }); } diff --git a/web/ee/store/integrations/gitlab/data.store.ts b/web/ee/store/integrations/gitlab/data.store.ts index 99492840e1..86730d1bff 100644 --- a/web/ee/store/integrations/gitlab/data.store.ts +++ b/web/ee/store/integrations/gitlab/data.store.ts @@ -9,26 +9,33 @@ import { GitlabDataService } from "@/plane-web/services/integrations/gitlab"; import { IGitlabStore } from "@/plane-web/store/integrations"; // plane web types import { TGitlabRepository } from "@/plane-web/types/integrations/gitlab"; +import { IGitlabEntity } from "@plane/etl/gitlab"; export interface IGitlabDataStore { // store instances gitlabRepositories: Record>; // organizationId -> gitlabRepositoryId -> TGitlabRepository gitlabUsers: Record>; // organizationId -> gitlabUserId -> gitlabUser + gitlabEntities: Record>; // organizationId -> gitlabRepositoryId -> IGitlabEntity // computed gitlabRepositoryIds: string[]; gitlabUserIds: string[] | undefined; + gitlabEntityIds: string[] | undefined; // computed functions gitlabRepositoryById: (gitlabRepositoryId: string) => TGitlabRepository | undefined; gitlabUserById: (gitlabUserId: string) => object | undefined; + gitlabEntityById: (gitlabEntityId: string) => IGitlabEntity | undefined; // actions fetchGitlabRepositories: () => Promise; fetchGitlabUsers: () => Promise; + fetchGitlabEntities: () => Promise; } export class GitlabDataStore implements IGitlabDataStore { // observables gitlabRepositories: Record> = {}; // organizationId -> gitlabRepositoryId -> TGitlabRepository gitlabUsers: Record> = {}; // organizationId -> gitlabUserId -> gitlabUser + gitlabEntities: Record> = {}; // organizationId -> gitlabRepositoryId -> IGitlabEntity + // service private service: GitlabDataService; @@ -37,12 +44,15 @@ export class GitlabDataStore implements IGitlabDataStore { // observables gitlabRepositories: observable, gitlabUsers: observable, + gitlabEntities: observable, // computed gitlabRepositoryIds: computed, gitlabUserIds: computed, + gitlabEntityIds: computed, // computed functions fetchGitlabRepositories: action, fetchGitlabUsers: action, + fetchGitlabEntities: action, }); this.service = new GitlabDataService(encodeURI(SILO_BASE_URL + SILO_BASE_PATH)); @@ -77,6 +87,20 @@ export class GitlabDataStore implements IGitlabDataStore { return Object.keys(gitlabUsers); } + /** + * @description get gitlab entity ids + * @returns { string[] | undefined } + */ + get gitlabEntityIds(): string[] | undefined { + const organizationId = this.store.auth.workspaceConnectionIds[0]; + if (!organizationId) return; + + const gitlabEntities = this.gitlabEntities[organizationId]; + if (!gitlabEntities) return; + + return Object.keys(gitlabEntities); + } + // computed functions /** * @description get gitlab repository by id @@ -108,6 +132,22 @@ export class GitlabDataStore implements IGitlabDataStore { return gitlabUsers[gitlabUserId] || undefined; }); + /** + * @description get gitlab entity by id + * @param { string } gitlabEntityId + * @returns { IGitlabEntity | undefined } + */ + gitlabEntityById = computedFn((gitlabEntityId: string): IGitlabEntity | undefined => { + const organizationId = this.store.auth.workspaceConnectionIds[0]; + if (!organizationId) return; + + const gitlabEntities = this.gitlabEntities[organizationId]; + if (!gitlabEntities) return; + + return gitlabEntities[gitlabEntityId] || undefined; + }); + + // actions /** * @description fetch gitlab repositories @@ -151,4 +191,32 @@ export class GitlabDataStore implements IGitlabDataStore { throw error; } }; + + /** + * @description fetch gitlab entities + * @returns { Promise } + */ + fetchGitlabEntities = async (): Promise => { + try { + const workspaceId = this.store.workspace?.id; + const organizationId = this.store.auth.workspaceConnectionIds[0]; + + if (!workspaceId || !organizationId) return []; + + const response = await this.service.fetchGitlabEntities(workspaceId); + + if (response) { + runInAction(() => { + response.forEach((data) => { + this.gitlabEntities[organizationId] = this.gitlabEntities[organizationId] || {}; + this.gitlabEntities[organizationId][data.id] = data; + }); + }); + } + + return response; + } catch (error) { + throw error; + } + }; } diff --git a/web/ee/store/integrations/gitlab/entity.store.ts b/web/ee/store/integrations/gitlab/entity.store.ts index e5780f561e..967d147672 100644 --- a/web/ee/store/integrations/gitlab/entity.store.ts +++ b/web/ee/store/integrations/gitlab/entity.store.ts @@ -11,43 +11,46 @@ import { GitlabEntityService } from "@/plane-web/services/integrations/gitlab"; import { IGitlabStore } from "@/plane-web/store/integrations"; // plane web types import { TGitlabEntityConnection } from "@/plane-web/types/integrations/gitlab"; +import { EConnectionType, GitlabEntityType } from "@plane/etl/gitlab"; -export interface IGitlabEntityStore { +export interface IGitlabEntityConnectionStore { // store instances - entityMap: Record>>; // workspaceId -> workspaceConnectionId -> entityId -> entity - // computed - entityIds: string[]; + entityConnectionMap: Record>>; // workspaceId -> workspaceConnectionId -> connectionId -> entity + // computed + entityConnectionIds: string[]; // computed functions - entityById: (entityId: string) => TGitlabEntityConnection | undefined; + entityConnectionById: (entityId: string) => TGitlabEntityConnection | undefined; // actions - fetchEntities: () => Promise; - fetchEntity: (entityId: string) => Promise; - createEntity: (entity: Partial) => Promise; - updateEntity: ( - entityId: string, + fetchEntityConnections: () => Promise; + fetchEntityConnection: (entityId: string) => Promise; + createEntityConnection: (entity: Partial) => Promise; + createProjectConnection: (entity: Partial) => Promise; + updateEntityConnection: ( + connectionId: string, entity: Partial ) => Promise; - deleteEntity: (entityId: string) => Promise; + deleteEntityConnection: (connectionId: string) => Promise; } -export class GitlabEntityStore implements IGitlabEntityStore { +export class GitlabEntityStore implements IGitlabEntityConnectionStore { // observables - entityMap: Record>> = {}; // workspaceId -> workspaceConnectionId -> entityId -> entity + entityConnectionMap: Record>> = {}; // workspaceId -> workspaceConnectionId -> connectionId -> entity // service private service: GitlabEntityService; constructor(protected store: IGitlabStore) { makeObservable(this, { // observables - entityMap: observable, + entityConnectionMap: observable, // computed - entityIds: computed, + entityConnectionIds: computed, // actions - fetchEntities: action, - fetchEntity: action, - createEntity: action, - updateEntity: action, - deleteEntity: action, + fetchEntityConnections: action, + fetchEntityConnection: action, + createEntityConnection: action, + createProjectConnection: action, + updateEntityConnection: action, + deleteEntityConnection: action, }); this.service = new GitlabEntityService(encodeURI(SILO_BASE_URL + SILO_BASE_PATH)); @@ -58,27 +61,27 @@ export class GitlabEntityStore implements IGitlabEntityStore { * @description get entity ids * @returns { string[] } */ - get entityIds(): string[] { + get entityConnectionIds(): string[] { const workspaceId = this.store.workspace?.id || undefined; const workspaceConnectionId = this.store.auth.workspaceConnectionIds[0] || undefined; - if (!workspaceId || !workspaceConnectionId || !this.entityMap?.[workspaceId]?.[workspaceConnectionId]) return []; + if (!workspaceId || !workspaceConnectionId || !this.entityConnectionMap?.[workspaceId]?.[workspaceConnectionId]) return []; - return Object.keys(this.entityMap[workspaceId][workspaceConnectionId]); + return Object.keys(this.entityConnectionMap[workspaceId][workspaceConnectionId]); } // computed functions /** * @description get entity by id - * @param { string } entityId + * @param { string } connectionId * @returns { TGitlabEntityConnection | undefined } */ - entityById = computedFn((entityId: string): TGitlabEntityConnection | undefined => { + entityConnectionById = computedFn((connectionId: string): TGitlabEntityConnection | undefined => { const workspaceId = this.store.workspace?.id || undefined; const workspaceConnectionId = this.store.auth.workspaceConnectionIds[0] || undefined; - if (!workspaceId || !workspaceConnectionId || !entityId || !this.entityMap?.[workspaceId]?.[workspaceConnectionId]) + if (!workspaceId || !workspaceConnectionId || !connectionId || !this.entityConnectionMap?.[workspaceId]?.[workspaceConnectionId]) return undefined; - return this.entityMap[workspaceId][workspaceConnectionId][entityId]; + return this.entityConnectionMap[workspaceId][workspaceConnectionId][connectionId]; }); // actions @@ -86,18 +89,18 @@ export class GitlabEntityStore implements IGitlabEntityStore { * @description fetch entities * @returns { Promise } */ - fetchEntities = async (): Promise => { + fetchEntityConnections = async (): Promise => { try { const workspaceId = this.store.workspace?.id || undefined; const workspaceConnectionId = this.store.auth.workspaceConnectionIds[0] || undefined; if (!workspaceId || !workspaceConnectionId) return; - const response = await this.service.fetchEntityConnections(workspaceId, workspaceConnectionId); + const response = await this.service.fetchEntityConnections(workspaceId); if (response) { runInAction(() => { response.forEach((data) => { - set(this.entityMap, [workspaceId, workspaceConnectionId, data.id], data); + set(this.entityConnectionMap, [workspaceId, workspaceConnectionId, data.id], data); }); }); } @@ -110,20 +113,20 @@ export class GitlabEntityStore implements IGitlabEntityStore { /** * @description fetch entity - * @param { string } entityId + * @param { string } connectionId * @returns { Promise } */ - fetchEntity = async (entityId: string): Promise => { + fetchEntityConnection = async (connectionId: string): Promise => { try { const workspaceId = this.store.workspace?.id || undefined; const workspaceConnectionId = this.store.auth.workspaceConnectionIds[0] || undefined; - if (!workspaceId || !workspaceConnectionId || !entityId) return; + if (!workspaceId || !workspaceConnectionId || !connectionId) return; - const response = await this.service.fetchEntityConnection(workspaceId, workspaceConnectionId, entityId); + const response = await this.service.fetchEntityConnection(connectionId); if (response) { runInAction(() => { - set(this.entityMap, [workspaceId, workspaceConnectionId, response.id], response); + set(this.entityConnectionMap, [workspaceId, workspaceConnectionId, response.id], response); }); } @@ -138,18 +141,18 @@ export class GitlabEntityStore implements IGitlabEntityStore { * @param { Partial } entity * @returns { Promise } */ - createEntity = async (entity: Partial): Promise => { + createEntityConnection = async (entity: Partial): Promise => { try { const workspaceId = this.store.workspace?.id || undefined; const workspaceSlug = this.store.workspace?.slug || undefined; const workspaceConnectionId = this.store.auth.workspaceConnectionIds[0] || undefined; if (!workspaceId || !workspaceSlug || !workspaceConnectionId) return; - const gitlabRepoId = entity?.entityId || undefined; - if (!gitlabRepoId) return; + const gitlabEntityId = entity?.entityId || undefined; + if (!gitlabEntityId) return; - const gitlabRepo = this.store.data.gitlabRepositoryById(gitlabRepoId) || undefined; - if (!gitlabRepo) return; + const gitlabEntity = this.store.data.gitlabEntityById(gitlabEntityId) || undefined; + if (!gitlabEntity) return; const payload: Partial = { workspaceId: workspaceId, @@ -157,8 +160,9 @@ export class GitlabEntityStore implements IGitlabEntityStore { projectId: entity.projectId, workspaceConnectionId: workspaceConnectionId, entityId: entity.entityId, - entitySlug: gitlabRepo.full_name, - entityData: gitlabRepo, + entityType: gitlabEntity.type, + entitySlug: gitlabEntity.name, + entityData: gitlabEntity, config: entity.config, }; @@ -166,11 +170,42 @@ export class GitlabEntityStore implements IGitlabEntityStore { if (response) { runInAction(() => { - set(this.entityMap, [workspaceId, workspaceConnectionId, response.id], response); + set(this.entityConnectionMap, [workspaceId, workspaceConnectionId, response.id], response); }); } - await this.fetchEntities(); + await this.fetchEntityConnections(); + + return response; + } catch (error) { + throw error; + } + }; + + createProjectConnection = async (entity: Partial): Promise => { + try { + const workspaceId = this.store.workspace?.id || undefined; + const workspaceSlug = this.store.workspace?.slug || undefined; + const workspaceConnectionId = this.store.auth.workspaceConnectionIds[0] || undefined; + if (!workspaceId || !workspaceSlug || !workspaceConnectionId) return; + + const payload: Partial = { + workspaceId: workspaceId, + workspaceSlug: workspaceSlug, + projectId: entity.projectId, + workspaceConnectionId: workspaceConnectionId, + config: entity.config, + }; + + const response = await this.service.createProjectEntityConnection(workspaceId, workspaceConnectionId, payload); + + if (response) { + runInAction(() => { + set(this.entityConnectionMap, [workspaceId, workspaceConnectionId, response.id], response); + }); + } + + await this.fetchEntityConnections(); return response; } catch (error) { @@ -180,42 +215,33 @@ export class GitlabEntityStore implements IGitlabEntityStore { /** * @description update entity - * @param { string } entityId + * @param { string } connectionId * @param { Partial } entity * @returns { Promise } */ - updateEntity = async ( - entityId: string, + updateEntityConnection = async ( + connectionId: string, entity: Partial ): Promise => { try { const workspaceId = this.store.workspace?.id || undefined; const workspaceSlug = this.store.workspace?.slug || undefined; const workspaceConnectionId = this.store.auth.workspaceConnectionIds[0] || undefined; - if (!workspaceId || !workspaceSlug || !workspaceConnectionId || !entityId) return; - - const gitlabRepoId = entity?.entityId || undefined; - if (!gitlabRepoId) return; - - const gitlabRepo = this.store.data.gitlabRepositoryById(gitlabRepoId) || undefined; - if (!gitlabRepo) return; + if (!workspaceId || !workspaceSlug || !workspaceConnectionId || !connectionId) return; const payload: Partial = { workspaceId: workspaceId, workspaceSlug: workspaceSlug, projectId: entity.projectId, workspaceConnectionId: workspaceConnectionId, - entityId: entity.entityId, - entitySlug: gitlabRepo.full_name, - entityData: gitlabRepo, config: entity.config, }; - const response = await this.service.updateEntityConnection(workspaceId, workspaceConnectionId, entityId, payload); + const response = await this.service.updateEntityConnection(connectionId, payload); if (response) { runInAction(() => { - set(this.entityMap, [workspaceId, workspaceConnectionId, response.id], response); + set(this.entityConnectionMap, [workspaceId, workspaceConnectionId, response.id], response); }); } @@ -227,17 +253,17 @@ export class GitlabEntityStore implements IGitlabEntityStore { /** * @description delete entity - * @param { string } entityId + * @param { string } connectionId * @returns { Promise } */ - deleteEntity = async (entityId: string): Promise => { + deleteEntityConnection = async (connectionId: string): Promise => { try { const workspaceId = this.store.workspace?.id || undefined; const workspaceConnectionId = this.store.auth.workspaceConnectionIds[0] || undefined; - if (!workspaceId || !workspaceConnectionId || !entityId) return; + if (!workspaceId || !workspaceConnectionId || !connectionId) return; - await this.service.deleteEntityConnection(workspaceId, workspaceConnectionId, entityId); - unset(this.entityMap, [workspaceId, workspaceConnectionId, entityId]); + await this.service.deleteEntityConnection(connectionId); + unset(this.entityConnectionMap, [workspaceId, workspaceConnectionId, connectionId]); return; } catch (error) { diff --git a/web/ee/store/integrations/gitlab/root.store.ts b/web/ee/store/integrations/gitlab/root.store.ts index d44d2e47b6..991b28319b 100644 --- a/web/ee/store/integrations/gitlab/root.store.ts +++ b/web/ee/store/integrations/gitlab/root.store.ts @@ -8,7 +8,7 @@ import { GitlabAuthStore, IGitlabDataStore, GitlabDataStore, - IGitlabEntityStore, + IGitlabEntityConnectionStore, GitlabEntityStore, } from "@/plane-web/store/integrations"; import { RootStore } from "@/plane-web/store/root.store"; @@ -17,14 +17,14 @@ export interface IGitlabStore extends IIntegrationBaseStore { // store instances auth: IGitlabAuthStore; data: IGitlabDataStore; - entity: IGitlabEntityStore; + entityConnection: IGitlabEntityConnectionStore; } export class GitlabStore extends IntegrationBaseStore implements IGitlabStore { // store instances auth: IGitlabAuthStore; data: IGitlabDataStore; - entity: IGitlabEntityStore; + entityConnection: IGitlabEntityConnectionStore; constructor(protected store: RootStore) { super(store); @@ -33,6 +33,6 @@ export class GitlabStore extends IntegrationBaseStore implements IGitlabStore { // store instances this.auth = new GitlabAuthStore(this); this.data = new GitlabDataStore(this); - this.entity = new GitlabEntityStore(this); + this.entityConnection = new GitlabEntityStore(this); } } diff --git a/web/ee/types/integrations/gitlab.ts b/web/ee/types/integrations/gitlab.ts index 262fbace42..b7b7790f26 100644 --- a/web/ee/types/integrations/gitlab.ts +++ b/web/ee/types/integrations/gitlab.ts @@ -1,4 +1,5 @@ import { TGitlabWorkspaceConnectionData, TWorkspaceConnection } from "@plane/etl/core"; +import { EConnectionType, GitlabEntityType, IGitlabEntity } from "@plane/etl/gitlab"; import { IState } from "@plane/sdk"; // auth types @@ -48,14 +49,13 @@ export type TGitlabEntityConnection = { entityId: string; entitySlug: string; - entityData: object & { - id: number; - name: string; - full_name: string; - }; + entityData: IGitlabEntity; + entityType: GitlabEntityType; config: TGitlabEntityConnectionConfig; + connectionType: keyof typeof EConnectionType; + createdAt: string; updatedAt: string; }; diff --git a/yarn.lock b/yarn.lock index 046f75f48e..6ddd679d2b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1815,62 +1815,62 @@ prop-types "^15.8.1" react-is "^19.0.0" -"@next/env@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.22.tgz#8898ae47595badbfacebfc1585f42a4e06a97301" - integrity sha512-EQ6y1QeNQglNmNIXvwP/Bb+lf7n9WtgcWvtoFsHquVLCJUuxRs+6SfZ5EK0/EqkkLex4RrDySvKgKNN7PXip7Q== +"@next/env@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.21.tgz#09ff0813d29c596397e141205d4f5fd5c236bdd0" + integrity sha512-lXcwcJd5oR01tggjWJ6SrNNYFGuOOMB9c251wUNkjCpkoXOPkDeF/15c3mnVlBqrW4JJXb2kVxDFhC4GduJt2A== -"@next/eslint-plugin-next@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.22.tgz#4de757f8e430c18b1d1e1b92ff2fe45754d8c641" - integrity sha512-8xCmBMd+hUapMpviPp5g3oDhoWRtbE/QeN/Nvth+SZrdt7xt9TBsH8cePkRwRjXFpwHndpRDNVQROxR/1HiVbg== +"@next/eslint-plugin-next@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.21.tgz#3e5b96cec21ba76c1e316938f74e32ce1d09f554" + integrity sha512-bxfiExnMkpwo4bBhCqnDhdgFyxSp6Xt6xu4Ne7En6MpgqwiER95Or+q1WDUDX4e888taeIAdPIAVaY+Wv0kiwQ== dependencies: glob "10.3.10" -"@next/swc-darwin-arm64@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.22.tgz#2b3fcb42247ba951b19a48fc03f1d6fe65629baa" - integrity sha512-HUaLiehovgnqY4TMBZJ3pDaOsTE1spIXeR10pWgdQVPYqDGQmHJBj3h3V6yC0uuo/RoY2GC0YBFRkOX3dI9WVQ== +"@next/swc-darwin-arm64@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.21.tgz#32a31992aace1440981df9cf7cb3af7845d94fec" + integrity sha512-HwEjcKsXtvszXz5q5Z7wCtrHeTTDSTgAbocz45PHMUjU3fBYInfvhR+ZhavDRUYLonm53aHZbB09QtJVJj8T7g== -"@next/swc-darwin-x64@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.22.tgz#11ecc609e9530b3edf8ddfd1fd3bd6aca4e1bfda" - integrity sha512-ApVDANousaAGrosWvxoGdLT0uvLBUC+srqOcpXuyfglA40cP2LBFaGmBjhgpxYk5z4xmunzqQvcIgXawTzo2uQ== +"@next/swc-darwin-x64@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.21.tgz#5ab4b3f6685b6b52f810d0f5cf6e471480ddffdb" + integrity sha512-TSAA2ROgNzm4FhKbTbyJOBrsREOMVdDIltZ6aZiKvCi/v0UwFmwigBGeqXDA97TFMpR3LNNpw52CbVelkoQBxA== -"@next/swc-linux-arm64-gnu@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.22.tgz#4c08dd223e50c348f561af2285e27fb326ffabbf" - integrity sha512-3O2J99Bk9aM+d4CGn9eEayJXHuH9QLx0BctvWyuUGtJ3/mH6lkfAPRI4FidmHMBQBB4UcvLMfNf8vF0NZT7iKw== +"@next/swc-linux-arm64-gnu@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.21.tgz#8a0e1fa887aef19ca218af2af515d0a5ee67ba3f" + integrity sha512-0Dqjn0pEUz3JG+AImpnMMW/m8hRtl1GQCNbO66V1yp6RswSTiKmnHf3pTX6xMdJYSemf3O4Q9ykiL0jymu0TuA== -"@next/swc-linux-arm64-musl@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.22.tgz#0b285336f145887d421b3762f3d7c75f847ec1b3" - integrity sha512-H/hqfRz75yy60y5Eg7DxYfbmHMjv60Dsa6IWHzpJSz4MRkZNy5eDnEW9wyts9bkxwbOVZNPHeb3NkqanP+nGPg== +"@next/swc-linux-arm64-musl@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.21.tgz#ddad844406b42fa8965fe11250abc85c1fe0fd05" + integrity sha512-Ggfw5qnMXldscVntwnjfaQs5GbBbjioV4B4loP+bjqNEb42fzZlAaK+ldL0jm2CTJga9LynBMhekNfV8W4+HBw== -"@next/swc-linux-x64-gnu@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.22.tgz#a936b6cfea0364571102f0389c6368d6acf3e294" - integrity sha512-LckLwlCLcGR1hlI5eiJymR8zSHPsuruuwaZ3H2uudr25+Dpzo6cRFjp/3OR5UYJt8LSwlXv9mmY4oI2QynwpqQ== +"@next/swc-linux-x64-gnu@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.21.tgz#db55fd666f9ba27718f65caa54b622a912cdd16b" + integrity sha512-uokj0lubN1WoSa5KKdThVPRffGyiWlm/vCc/cMkWOQHw69Qt0X1o3b2PyLLx8ANqlefILZh1EdfLRz9gVpG6tg== -"@next/swc-linux-x64-musl@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.22.tgz#0359497840d0b7d8c095d0d9735bc6aec68cef5d" - integrity sha512-qGUutzmh0PoFU0fCSu0XYpOfT7ydBZgDfcETIeft46abPqP+dmePhwRGLhFKwZWxNWQCPprH26TjaTxM0Nv8mw== +"@next/swc-linux-x64-musl@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.21.tgz#dddb850353624efcd58c4c4e30ad8a1aab379642" + integrity sha512-iAEBPzWNbciah4+0yI4s7Pce6BIoxTQ0AGCkxn/UBuzJFkYyJt71MadYQkjPqCQCJAFQ26sYh7MOKdU+VQFgPg== -"@next/swc-win32-arm64-msvc@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.22.tgz#9fd249d49ffccf3388400ab24472c432cdd04c24" - integrity sha512-K6MwucMWmIvMb9GlvT0haYsfIPxfQD8yXqxwFy4uLFMeXIb2TcVYQimxkaFZv86I7sn1NOZnpOaVk5eaxThGIw== +"@next/swc-win32-arm64-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.21.tgz#290012ee57b196d3d2d04853e6bf0179cae9fbaf" + integrity sha512-plykgB3vL2hB4Z32W3ktsfqyuyGAPxqwiyrAi2Mr8LlEUhNn9VgkiAl5hODSBpzIfWweX3er1f5uNpGDygfQVQ== -"@next/swc-win32-ia32-msvc@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.22.tgz#70d8d5a48e78c7382c3e0544af28c2788ca6b551" - integrity sha512-5IhDDTPEbzPR31ZzqHe90LnNe7BlJUZvC4sA1thPJV6oN5WmtWjZ0bOYfNsyZx00FJt7gggNs6SrsX0UEIcIpA== +"@next/swc-win32-ia32-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.21.tgz#c959135a78cab18cca588d11d1e33bcf199590d4" + integrity sha512-w5bacz4Vxqrh06BjWgua3Yf7EMDb8iMcVhNrNx8KnJXt8t+Uu0Zg4JHLDL/T7DkTCEEfKXO/Er1fcfWxn2xfPA== -"@next/swc-win32-x64-msvc@14.2.22": - version "14.2.22" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.22.tgz#b034f544c1346093a235f6bba46497a1ba344fc1" - integrity sha512-nvRaB1PyG4scn9/qNzlkwEwLzuoPH3Gjp7Q/pLuwUgOTt1oPMlnCI3A3rgkt+eZnU71emOiEv/mR201HoURPGg== +"@next/swc-win32-x64-msvc@14.2.21": + version "14.2.21" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.21.tgz#21ff892286555b90538a7d1b505ea21a005d6ead" + integrity sha512-sT6+llIkzpsexGYZq8cjjthRyRGe5cJVhqh12FmlbxHqna6zsDDK8UNaV7g41T6atFHCJUPeLb3uyAwrBwy0NA== "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": version "2.1.8-no-fsevents.3" @@ -3815,181 +3815,181 @@ resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.5.2.tgz#db7257d727c891905947bd1c1a99da20e03c2ebd" integrity sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ== -"@tiptap/core@^2.1.13", "@tiptap/core@^2.11.0", "@tiptap/core@^2.4.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.11.0.tgz#72891ae9b3e4b2a67f79ab3bcb14483d30a9bd8a" - integrity sha512-0S3AWx6E2QqwdQqb6z0/q6zq2u9lA9oL3BLyAaITGSC9zt8OwjloS2k1zN6wLa9hp2rO0c0vDnWsTPeFaEaMdw== +"@tiptap/core@^2.1.13", "@tiptap/core@^2.10.4", "@tiptap/core@^2.4.0": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.10.4.tgz#0b3ad822d71f5834b281590809f430e6fd41523c" + integrity sha512-fExFRTRgb6MSpg2VvR5qO2dPTQAZWuUoU4UsBCurIVcPWcyVv4FG1YzgMyoLDKy44rebFtwUGJbfU9NzX7Q/bA== -"@tiptap/extension-blockquote@^2.1.13", "@tiptap/extension-blockquote@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.11.0.tgz#c80aec1ce7791be5a0b45071ffb7481f40c35666" - integrity sha512-DBjWbgmbAAR879WAsk0+5xxgqpOTweWNnY7kEqWv3EJtLUvECXN63smiv3o4fREwwbEJqgihBu5/YugRC5z1dg== +"@tiptap/extension-blockquote@^2.1.13", "@tiptap/extension-blockquote@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.10.4.tgz#0f9a26740009fc7430fabc423077c369feaebb42" + integrity sha512-4JSwAM3B92YWvGzu/Vd5rovPrCGwLSaSLD5rxcLyfxLSrTDQd3n7lp78pzVgGhunVECzaGF5A0ByWWpEyS0a3w== -"@tiptap/extension-bold@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.11.0.tgz#170e04df4396f3a3b830e34055b3a87f4e42a3b6" - integrity sha512-3x9BQZHYD5xFA0pCEneEMHZyIoxYo4NKcbhR4CLxGad1Xd+5g109nr1+eZ1JgvnChkeVf1eD6SaQE2A28lxR5g== +"@tiptap/extension-bold@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.10.4.tgz#ac4a791fe9c4ccf0c4fb70c304e663cc07ba9397" + integrity sha512-SdO4oFQKaERCGfwOc1CLYQRtThENam2KWfWmvpsymknokt5qYzU57ft0SE1HQV9vVYEzZ9HrWIgv2xrgu0g9kg== -"@tiptap/extension-bubble-menu@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.11.0.tgz#f2f596d01d24312962bd9c14a1c1a797931cf221" - integrity sha512-21KyB7+QSQjw72Oxzs3Duw9WErAUrigFZCyoCZNjp24wP7mFVsy1jAcnRiAi8pBVwlwHBZ29IW1PeavqCSFFVA== +"@tiptap/extension-bubble-menu@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.10.4.tgz#6d2611fd3dd4c6b848b5f028ba96567aec5bd1e3" + integrity sha512-GVtZwJaQyLBptMsmDtYl5GEobd1Uu7C9sc9Z+PdXwMuxmFfg+j07bCKCj5JJj/tjgXCSLVxWdTlDHxNrgzQHjw== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-bullet-list@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.11.0.tgz#972f31f1fffc74327c3cb9795893c13736132de3" - integrity sha512-UALypJvO+cPSk/nC1HhkX/ImS9FxbKe2Pr0iDofakvZU1U1msumLVn2M/iq+ax1Mm9thodpvJv0hGDtFRwm7lQ== +"@tiptap/extension-bullet-list@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.10.4.tgz#0b86fb2782b4a7fc012a23d3e265bcc798e70a12" + integrity sha512-JVwDPgOBYRU2ivaadOh4IaQYXQEiSw6sB36KT/bwqJF2GnEvLiMwptdRMn9Uuh6xYR3imjIZtV6uZAoneZdd6g== "@tiptap/extension-character-count@^2.6.5": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-character-count/-/extension-character-count-2.11.0.tgz#7d3992ceba72f1eeb2db3a1fc997c50dddd7cf8c" - integrity sha512-WbqVr1QY62vxpmDJP5k3bwyzoHha1sZTs0xj3L+4s1j/SB2A7tAlFdcNPPwfbPOINHQgomSAyClfTyd4Gor7HA== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-character-count/-/extension-character-count-2.10.4.tgz#dccf731a48eaf93075500709929a1bf0eff1d8f9" + integrity sha512-NoVLQI/zTEdA0EZe5+oinQ+F/WQMblZRdEWgfsUtQoLRloSAF+pFeQwDenpejdOuWqixT4vdzpboBocj4uQLsw== -"@tiptap/extension-code-block@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.11.0.tgz#ab8d19bb85ba9dccb14ad5dcc6559cb7965d173e" - integrity sha512-8of3qTOLjpveHBrrk8KVliSUVd6R2i2TNrBj0f/21HcFVAy0fP++02p6vI6UPOhwM3+p3CprGdSM48DFCu1rqw== +"@tiptap/extension-code-block@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.10.4.tgz#a86a4477acc995c5185e32dd60188f4e173f9341" + integrity sha512-qS4jnbJqghNMT2+B+GQ807ATgqkL9OQ//NlL+ZwVSe+DPDduNA9B6IB9SrWENDfOnzekpi7kcEcm+RenELARRQ== -"@tiptap/extension-code@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.11.0.tgz#eaf3a0a63ceeda462d1526e40a221223c7497b54" - integrity sha512-2roNZxcny1bGjyZ8x6VmGTuKbwfJyTZ1hiqPc/CRTQ1u42yOhbjF4ziA5kfyUoQlzygZrWH9LR5IMYGzPQ1N3w== +"@tiptap/extension-code@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.10.4.tgz#d6d1fd9e7f1b457700b6c47e115fb1cc9b9039f8" + integrity sha512-Vj/N0nbSQiV1o7X7pRySK9Fu72Dd266gm27TSlsts6IwJu5MklFvz7ezJUWoLjt2wmCV8/U/USmk/39ic9qjvg== "@tiptap/extension-collaboration-cursor@^2.6.6": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-collaboration-cursor/-/extension-collaboration-cursor-2.11.0.tgz#14f60c6d7774aa104dbc8b137320638a05ce0110" - integrity sha512-JZx9SWOxEYQIGaJKndsCPdQas8hniodSsfAFjJq1X62nl7Hlif//mrjAU8c5gRCd7KtTxf/Q8Y7cClLdfkjQmQ== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-collaboration-cursor/-/extension-collaboration-cursor-2.10.4.tgz#2d6f177d5ffbdef40af3863e22466d811d6fe0ba" + integrity sha512-U5FJnvIveqLxKZ9aLPgXj1EDzfFfLlHX6J6VZNGAc0QsK5rWCdiU7HWuG2hNTSjHayqh/2V+x30PTMc5EuCadg== "@tiptap/extension-collaboration@^2.3.2": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-collaboration/-/extension-collaboration-2.11.0.tgz#21ac1c8bc2ca6d8ba2a838e63457528627294888" - integrity sha512-pS3E//ODD80PwVXp7zOqek0q9z5AtZ6sMSK5nPneNioe7dSvCeQzToOD9V6EevK4KChUIN9wEryK8mkQs57ioA== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-collaboration/-/extension-collaboration-2.10.4.tgz#7764d430f6a3c2e1cca7d07ab6d4c140b322b322" + integrity sha512-IvVK/KGG3A0r/Du9ISv06v0VkKXB81RbCK3b/FSkV8+GPQXOqJ9L3aQ4N1LO7ZX5Cb0RbfVgSMK8WH2MIl6xKQ== -"@tiptap/extension-document@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.11.0.tgz#dcae7bc3a29b279ddbf88fc2b248817705ccb8a6" - integrity sha512-9YI0AT3mxyUZD7NHECHyV1uAjQ8KwxOS5ACwvrK1MU8TqY084LmodYNTXPKwpqbr51yvt3qZq1R7UIVu4/22Cg== +"@tiptap/extension-document@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.10.4.tgz#68575c122f6d71556dae6f9df6173903b7cdfdf9" + integrity sha512-1Pqrl6Rr9bVEHJ3zO2dM7UUA0Qn/r70JQ9YLlestjW1sbMaMuY3Ifvu2uSyUE7SAGV3gvxwNVQCrv8f0VlVEaA== -"@tiptap/extension-dropcursor@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.11.0.tgz#c81eea3f90c460d1eefed14f8e9735346340191b" - integrity sha512-p7tUtlz7KzBa+06+7W2LJ8AEiHG5chdnUIapojZ7SqQCrFRVw70R+orpkzkoictxNNHsun0A9FCUy4rz8L0+nQ== +"@tiptap/extension-dropcursor@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.10.4.tgz#6dea1c17878a8a0d68c7915f34b7b02980e91c90" + integrity sha512-0XEM/yNLaMc/sZlYOau7XpHyYiHT9LwXUe7kmze/L8eowIa/iLvmRbcnUd3rtlZ7x7wooE6UO9c7OtlREg4ZBw== -"@tiptap/extension-floating-menu@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.11.0.tgz#fa96fc2a75d94d2e21f4a6ed6b9fedd53d344a4a" - integrity sha512-dexhhUJm0x9OolbeVCa7RpxuALU3bJZC7dFpu/rPG3ZetXKhVw8hTrqUQD5w1DjXpczBzScnLgLrvnjxbG66pw== +"@tiptap/extension-floating-menu@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.10.4.tgz#e00bbeba8b67ce2511464b9b5203a399fc7b4311" + integrity sha512-K2MDiu6CwQ7+Jr6g1Lh3Tuxm1L6SefSHMpQO0UW3aRGwgEV5pjlrztnBFX4K9b7MNuQ4dJGCUK9u8Cv7Xss0qg== dependencies: tippy.js "^6.3.7" -"@tiptap/extension-gapcursor@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.11.0.tgz#01511c996656c43ed008825b66ccdf9fa365a674" - integrity sha512-1TVOthPkUYwTQnQwP0BzuIHVz09epOiXJQ3GqgNZsmTehwcMzz2vGCpx1JXhZ5DoMaREHNLCdraXb1n2FdhDNA== +"@tiptap/extension-gapcursor@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.10.4.tgz#697f0f599c5fe90f584e3f94e4866959211c8688" + integrity sha512-KbJfoaqTZePpkWAN+klpK5j0UVtELxN7H5B0J556/UCB/rnq+OsdEFHPks2Ss9TidqWzRUqcxUE50UZ7b8h7Ug== -"@tiptap/extension-hard-break@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.11.0.tgz#0e199a1c58171fe1879e730b87666e759fe55c04" - integrity sha512-7pMgPNk2FnPT0LcWaWNNxOLK3LQnRSYFgrdBGMXec3sy+y3Lit3hM+EZhbZcHpTIQTbWWs+eskh1waRMIt0ZaQ== +"@tiptap/extension-hard-break@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.10.4.tgz#57767f35950405c457789faa7b793830860096cc" + integrity sha512-nW9wubW1A/CO2Ssn9wNMP08tR9Oarg9VUGzJ5qNuz38DDNyntE1SyDS+XStkeMq5nKqJ3YKhukyAJH/PiRq4Mg== -"@tiptap/extension-heading@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.11.0.tgz#8b7530a325faffdf325d03d82a2d6947a440d662" - integrity sha512-vrYvxibsY7/Sd2wYQDZ8AfIORfFi/UHZAWI7JmaMtDkILuMLYQ+jXb7p4K2FFW/1nN7C8QqgLLFI5AfjZUusgw== +"@tiptap/extension-heading@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.10.4.tgz#6df708f74f2f2d0716f6e3d32100c29c8be3fe68" + integrity sha512-7D0h0MIvE97Gx3Qwuo2xnPDK07WfCnyh4tpOPBOus4e1g6sgxVkwDwhbkYWiwvIrf4BUVJflnke/DEDCVp6/Eg== -"@tiptap/extension-history@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.11.0.tgz#fc04cb418e4bad6c659b101d48812e43f04c3bbc" - integrity sha512-eEUEDoOtS17AHVEPbGfZ+x2L5A87SiIsppWYTkpfIH/8EnVQmzu+3i1tcT9cWvHC31d9JTG7TDptVuuHr30TJw== +"@tiptap/extension-history@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.10.4.tgz#58e1b819149354538526475896a9904e954d02a6" + integrity sha512-fg6BNxbpMMtgKaiNI/GLcCzkxIQMwSYBhO9LA0CxLvmsWGU+My4r9W3DK6HwNoRJ9+6OleDPSLo1P73fbSTtEA== -"@tiptap/extension-horizontal-rule@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.11.0.tgz#ba7dc213f93cf3920ebcff246a8aaf845cb0b683" - integrity sha512-ZbkILwmcccmwQB2VTA/dzHRMB+xoJQ8UJdafcUiaAUlQfvDgl898+AYMa2GRTZkLPvzCKjXMC9hybSyy54Lz3Q== +"@tiptap/extension-horizontal-rule@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.10.4.tgz#a2099c156c4c691f4951def225e1102ad1f69f22" + integrity sha512-s9ycm/BOGoW3L0Epnj541vdngHbFbMM488HoODd1CmVSw1C+wBWFgsukgqKjlyE3VGfZXuSb1ur9zinW0RiLJQ== "@tiptap/extension-image@^2.1.13": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.11.0.tgz#e9611a6a032804279f9b12a483a7afe696358a3f" - integrity sha512-R+JkK5ocX35ag1c42aAw6rcb9QlLUBB0ju8A7b+8qZXN5yWKE0yO/oixYFmnZN7WSnBYtzuCVDX8cvRG+BPbgA== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.10.4.tgz#577a0e9b92be845b91165d29136327c951bb967e" + integrity sha512-fPdAqP4M1zwz5jyrQNIEL4OvvGeJso45svaaBLV342yRLOpbVIgAp/RsuWSGDQTUWoGhdkHdIrbH2bUGNEbMBg== -"@tiptap/extension-italic@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.11.0.tgz#fe36d1aac51d8d5f8c276116a1e4408396440d8e" - integrity sha512-T+jjS0gOsvNzQXVTSArmUp/kt2R9OikPQaV1DI60bfjO0rknOgtG0tbwZmfbugzwc07RbpxOYFy3vBxMLDsksA== +"@tiptap/extension-italic@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.10.4.tgz#000cd53a5d54efe1c8bfa51a2bd6003268dcd01d" + integrity sha512-8MIQ+wsbyxNCZDCFTVTOXrS2AvFyOhtlBNgVU2+6r6xnJV4AcfEA3qclysqrjOlL117ped/nzDeoB0AeX0CI+Q== -"@tiptap/extension-list-item@^2.1.13", "@tiptap/extension-list-item@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.11.0.tgz#15889fd8217b998bfef78cd9079c8405b5a9abcd" - integrity sha512-Jikcg0fccpM13a3hAFLtguMcpVg4eMWI8NnC0aUULD9rFhvWZQYQYQuoK3fO6vQrAQpNhsV4oa0dfSq1btu9kg== +"@tiptap/extension-list-item@^2.1.13", "@tiptap/extension-list-item@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.10.4.tgz#3ea8e597567d448b00ac4d7b0ef9d1b91bc1d306" + integrity sha512-8K3WUD5fPyw2poQKnJGGm7zlfeIbpld92+SRF4M9wkp95EzvgexTlodvxlrL3i8zKXcQQVyExWA8kCcGPFb9bA== "@tiptap/extension-mention@^2.1.13": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.11.0.tgz#515f831975188ce33e3c0951f94a34396a3f35cd" - integrity sha512-5/Yk2rTpsoIZaNyo4f+CgsCCkQkSiNAp24HOvvCm9Dp9w1gIFm6y6dSj5RYqzEucGjOkoaBbfMcm1QxKWIj6/A== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.10.4.tgz#a01856024f948daf2e1f9ed820059f1bdb15ff73" + integrity sha512-pVouKWxSVQSy4zn6HrljPIP1AG826gkm/w18Asi8QnZvR0AMqGLh9q7qd9Kc0j8NKoCzlzK8hECGlYPEaBldow== -"@tiptap/extension-ordered-list@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.11.0.tgz#29d4afab66e85c701a7ab5b86ea1cc1f5ae7e3c7" - integrity sha512-i6pNsDHA2QvBAebwjAuvhHKwz+bZVJ929PCIJaN8mxg0ldiAmFbAsf+rwIIFHWogMp+5xEX2RBzux20usNVZ9w== +"@tiptap/extension-ordered-list@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.10.4.tgz#3da20e5087329a9999837b5860954bd14b14ef60" + integrity sha512-NaeEu+qFG2O0emc8WlwOM7DKNKOaqHWuNkuKrrmQzslgL+UQSEGlGMo6NEJ5sLLckPBDpIa0MuRm30407JE+cg== -"@tiptap/extension-paragraph@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.11.0.tgz#936bfbc6781acd5ac0dba6bcdc2fa67514ce6164" - integrity sha512-xLNC05An3SQq0bVHJtOTLa8As5r6NxDZFpK0NZqO2hTq/fAIRL/9VPeZ8E0tziXULwIvIPp+L0Taw3TvaUkRUg== +"@tiptap/extension-paragraph@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.10.4.tgz#e2c94eaee7c69b7c38f98abd50927a8a6811dc49" + integrity sha512-SRNVhT8OXqjpZtcyuOtofbtOpXXFrQrjqqCc/yXebda//2SfUTOvB16Lss77vQOWi6xr7TF1mZuowJgSTkcczw== "@tiptap/extension-placeholder@^2.3.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.11.0.tgz#a330df0bc791df82b3a84490031a8db9a68f4061" - integrity sha512-ee8vz51pW6H+1rEDMFg2FnBs2Tj5rUHlJ1JgD7Dcp3+89SVHGB3UILGfbNpAnHZvhmsTY3NcfPAcZZ80QfQFMQ== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.10.4.tgz#706e30941e3232894db647ab4254e8ba65f54920" + integrity sha512-leWG4xP7cvddR6alGZS7yojOh9941bxehgAeQDLlEisaJcNa2Od5Vbap2zipjc5sXMxZakQVChL27oH1wWhHkQ== -"@tiptap/extension-strike@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.11.0.tgz#8507cfb501f4950d869f8ddd1adb47b66680765f" - integrity sha512-71i2IZT58kY2ohlhyO+ucyAioNNCkNkuPkrVERc9lXhmcCKOff5y6ekDHQHO2jNjnejkVE5ibyDO3Z7RUXjh1A== +"@tiptap/extension-strike@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.10.4.tgz#8c450d1cb26f6cb0b79403aed2bf0bd6d1d0cd98" + integrity sha512-OibipsomFpOJWTPVX/z4Z53HgwDA93lE/loHGa+ONJfML1dO6Zd6UTwzaVO1/g8WOwRgwkYu/6JnhxLKRlP8Lg== "@tiptap/extension-task-item@^2.1.13": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.11.0.tgz#ff97f10bf39d6c27fd3f3f1ee3987d7015304c11" - integrity sha512-qu6VuRc8qF80Bwr82CItFcrKtC67LJkwpxESLEIi42zWZ5sXF/3DJEPPS/4Kk+nAc9UCBoEMFAULibPq7rRl/w== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.10.4.tgz#afc59e1632db1d7d1d8c2ff4e897a638337a949f" + integrity sha512-ucKGXdHdHCBanIJTB/nhmQ3iIL6BcSVKr7mN5BGEu6sSLYROflX7lmnMPVIRcTKJz+FGJeR6AqPFVagZAXVkGQ== "@tiptap/extension-task-list@^2.1.13": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.11.0.tgz#2f197a70aafc0c23163ed70aa314cc0300534cf6" - integrity sha512-+dZRjeXLXxyliFt3J7uQADxfOwi6ntyepVM+ri1rnmIaqVZUHJbUFodOc0LivI+Z5iZZ10u3TId8gehqWJHD+w== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.10.4.tgz#4c22d70a3d7147ef5919d368ee619dc94654df16" + integrity sha512-21bFlHlvGr5hsXUEug9p+BWPLqdziFS/4mGG6nUnrSDI1e4eEC86WZczsG+If6FEpjcCS9Eb2RHgqaA4VoJEqg== "@tiptap/extension-text-align@^2.8.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text-align/-/extension-text-align-2.11.0.tgz#b90b86ee3f6e14c9abf186cb8ab6ced20c0df14d" - integrity sha512-VRXBqO17po6ddqhoWLBa2aCX/tqHdzdKPLfjnBy1fF8hjQKbidzjMWhb4CMm31ApvJjKK/DTkM3EnyYS/XDhng== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text-align/-/extension-text-align-2.10.4.tgz#ffd9cd61bf7f00c6ed4b3b5f377a7d5bed56550b" + integrity sha512-rt2Hz3N081QAgkKKSMpyDZTKorBmXKpeHkYIw+ArVuvBYhi8x5wVyZgZ2SIMW9A5G4rx1M0czn7xNi+/P3NopQ== -"@tiptap/extension-text-style@^2.11.0", "@tiptap/extension-text-style@^2.7.1": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.11.0.tgz#2acb6207fe6acdb5479384682846980be15caa80" - integrity sha512-vuA16wMZ6J3fboL7FObwV2f5uN9Vg0WYmqU7971vxzJyaRj9VE1eeH8Kh5fq4RgwDzc13MZGvZZV4HcE1R8o8A== +"@tiptap/extension-text-style@^2.10.4", "@tiptap/extension-text-style@^2.7.1": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.10.4.tgz#07af922c266fef7da13cc83befbbd5da512b7402" + integrity sha512-ibq7avkcwHyUSG53Hf+P31rrwsKVbbiqbWZM4kXC7M2X3iUwFrtvaa+SWzyWQfE1jl2cCrD1+rfSkj/alcOKGg== -"@tiptap/extension-text@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.11.0.tgz#56ad3d9b14a34f67f7f8b82a499ef7a9a10ef0a6" - integrity sha512-LcyrP+7ZEVx3YaKzjMAeujq+4xRt4mZ3ITGph2CQ4vOKFaMI8bzSR909q18t7Qyyvek0a9VydEU1NHSaq4G5jw== +"@tiptap/extension-text@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.10.4.tgz#ca5c7618ec122cfd72399b8c593023cf4a5829d5" + integrity sha512-wPdVxCHrIS9S+8n08lgyyqRZPj9FBbyLlFt74/lV5yBC3LOorq1VKdjrTskmaj4jud7ImXoKDyBddAYTHdJ1xw== "@tiptap/extension-underline@^2.1.13": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.11.0.tgz#473025d3ed01953fb2c23f8060ea90be4c231faa" - integrity sha512-DE1piq441y1+9Aj1pvvuq1dcc5B2HZ2d1SPtO4DTMjCxrhok12biTkMxxq0q1dzA5/BouLlUW6WTPpinhmrUWA== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-2.10.4.tgz#5892a3dc7997818c0bbc0dde4e48b036d6786975" + integrity sha512-KhlCndQFMe/Gsz+3qkVn9z1utDy8y1igvdePijMjA5B8PTu0hPs2Q1d6szfLTBdtoFNkCokknxzXhSY0OFJEyQ== "@tiptap/html@^2.3.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.11.0.tgz#dd0b4195a07bbdca27aa13325b8ebac41480a6a0" - integrity sha512-9+8eSeey3gm6vMtbt+uKZfkvtwsWr577lhtTeGUsoThim9zyBnbhzHc1dQw2m1RefOoPcODrnnQPvi5nvA84Cw== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/html/-/html-2.10.4.tgz#f4cf5b93a9e5d47538c9935912006e51bbb513dc" + integrity sha512-s9gWJy2Z+mfG5s8ewodv6d/iZq3bcc6osT9zsIvQcfoHh4j6Nq/DcIuZ/7AodgRFGZrIAEu4X1U4K2/Fk+gplA== dependencies: zeed-dom "^0.15.1" -"@tiptap/pm@^2.1.13", "@tiptap/pm@^2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.11.0.tgz#c2ccb0c9b99fd5915d9390e90d3ab9e5fb408cd8" - integrity sha512-4RU6bpODkMY+ZshzdRFcuUc5jWlMW82LWXR6UOsHK/X/Mav41ZFS0Cyf+hQM6gxxTB09YFIICmGpEpULb+/CuA== +"@tiptap/pm@^2.1.13", "@tiptap/pm@^2.10.4": + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.10.4.tgz#28909a528a3ac59e4f62055d5b9b2890f6f386fc" + integrity sha512-pZ4NEkRtYoDLe0spARvXZ1N3hNv/5u6vfPdPtEbmNpoOSjSNqDC1kVM+qJY0iaCYpxbxcv7cxn3kBumcFLQpJQ== dependencies: prosemirror-changeset "^2.2.1" prosemirror-collab "^1.3.1" @@ -4011,47 +4011,47 @@ prosemirror-view "^1.37.0" "@tiptap/react@^2.1.13": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.11.0.tgz#28b9ca64bdeb6302eaca1941ef792e3acea2e40e" - integrity sha512-AALzHbqNq/gerJpkbXmN2OXFmHAs2bQENH7rXbnH70bpxVdIfQVtvjK4dIb+cQQvAuTWZvhsISnTrFY2BesT3Q== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.10.4.tgz#cdd898ff2f962ecd7be593606507f95b90fab2f8" + integrity sha512-JTeqDB+xgjo46QC9ILRXe2TcSfxKVRwhZ3vDvYoemN7giRk5a/WsCF1VQIT1fax+tCl6kfv3U1f4Mkx0DkbPkA== dependencies: - "@tiptap/extension-bubble-menu" "^2.11.0" - "@tiptap/extension-floating-menu" "^2.11.0" + "@tiptap/extension-bubble-menu" "^2.10.4" + "@tiptap/extension-floating-menu" "^2.10.4" "@types/use-sync-external-store" "^0.0.6" fast-deep-equal "^3" use-sync-external-store "^1" "@tiptap/starter-kit@^2.1.13": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.11.0.tgz#3b4a9ca9daaffed3abd030d4cd72e8d9d3eb0649" - integrity sha512-lrYmkeaAFiuUjN5nGnCowdjponrsR7eRmeTf/15/5oZsNrMN7t/fvPb014AqhG/anNasa0ism4CKZns3D+4pKQ== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.10.4.tgz#37f95fae7ea36b29825720706c49dd0533274b0b" + integrity sha512-tu/WCs9Mkr5Nt8c3/uC4VvAbQlVX0OY7ygcqdzHGUeG9zP3twdW7o5xM3kyDKR2++sbVzqu5Ll5qNU+1JZvPGQ== dependencies: - "@tiptap/core" "^2.11.0" - "@tiptap/extension-blockquote" "^2.11.0" - "@tiptap/extension-bold" "^2.11.0" - "@tiptap/extension-bullet-list" "^2.11.0" - "@tiptap/extension-code" "^2.11.0" - "@tiptap/extension-code-block" "^2.11.0" - "@tiptap/extension-document" "^2.11.0" - "@tiptap/extension-dropcursor" "^2.11.0" - "@tiptap/extension-gapcursor" "^2.11.0" - "@tiptap/extension-hard-break" "^2.11.0" - "@tiptap/extension-heading" "^2.11.0" - "@tiptap/extension-history" "^2.11.0" - "@tiptap/extension-horizontal-rule" "^2.11.0" - "@tiptap/extension-italic" "^2.11.0" - "@tiptap/extension-list-item" "^2.11.0" - "@tiptap/extension-ordered-list" "^2.11.0" - "@tiptap/extension-paragraph" "^2.11.0" - "@tiptap/extension-strike" "^2.11.0" - "@tiptap/extension-text" "^2.11.0" - "@tiptap/extension-text-style" "^2.11.0" - "@tiptap/pm" "^2.11.0" + "@tiptap/core" "^2.10.4" + "@tiptap/extension-blockquote" "^2.10.4" + "@tiptap/extension-bold" "^2.10.4" + "@tiptap/extension-bullet-list" "^2.10.4" + "@tiptap/extension-code" "^2.10.4" + "@tiptap/extension-code-block" "^2.10.4" + "@tiptap/extension-document" "^2.10.4" + "@tiptap/extension-dropcursor" "^2.10.4" + "@tiptap/extension-gapcursor" "^2.10.4" + "@tiptap/extension-hard-break" "^2.10.4" + "@tiptap/extension-heading" "^2.10.4" + "@tiptap/extension-history" "^2.10.4" + "@tiptap/extension-horizontal-rule" "^2.10.4" + "@tiptap/extension-italic" "^2.10.4" + "@tiptap/extension-list-item" "^2.10.4" + "@tiptap/extension-ordered-list" "^2.10.4" + "@tiptap/extension-paragraph" "^2.10.4" + "@tiptap/extension-strike" "^2.10.4" + "@tiptap/extension-text" "^2.10.4" + "@tiptap/extension-text-style" "^2.10.4" + "@tiptap/pm" "^2.10.4" "@tiptap/suggestion@^2.0.13": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.11.0.tgz#826140c6f1a76af2d91713dafb4b142c803eabef" - integrity sha512-f+KcczhzEEy2f7/0N/RSID+Z6NjxCX6ab26NLfWZxdaEm/J+vQ2Pqh/e5Z59vMfKiC0DJXVcO0rdv2LBh23qDw== + version "2.10.4" + resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.10.4.tgz#732320b65bc55bcc2a33a8f32072f9478ffb7436" + integrity sha512-7Bzcn1REA7OmVRxiMF2kVK9EhosXotdLAGaEvSbn4zQtHCJG0tREuYvPy53LGzVuPkBDR6Pf6sp1QbGvSne/8g== "@todesktop/client-core@^1.12.4": version "1.12.4" @@ -4731,15 +4731,15 @@ integrity sha512-FZJgC5Bxuqg7Rhsm/bx6gAruHHhDQ55r+s0JhDh8CQ16fD7NsJJ+p8YMMQDhSQoIrSmjpqqYWA96oQVMNkjRyA== "@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/eslint-plugin@^8.6.0": - version "8.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz#2b1e1b791e21d5fc27ddc93884db066444f597b5" - integrity sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q== + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz#992e5ac1553ce20d0d46aa6eccd79dc36dedc805" + integrity sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.19.0" - "@typescript-eslint/type-utils" "8.19.0" - "@typescript-eslint/utils" "8.19.0" - "@typescript-eslint/visitor-keys" "8.19.0" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/type-utils" "8.18.1" + "@typescript-eslint/utils" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" @@ -4762,14 +4762,14 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser@^8.6.0": - version "8.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.19.0.tgz#f1512e6e5c491b03aabb2718b95becde22b15292" - integrity sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw== + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.18.1.tgz#c258bae062778b7696793bc492249027a39dfb95" + integrity sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA== dependencies: - "@typescript-eslint/scope-manager" "8.19.0" - "@typescript-eslint/types" "8.19.0" - "@typescript-eslint/typescript-estree" "8.19.0" - "@typescript-eslint/visitor-keys" "8.19.0" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/typescript-estree" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" debug "^4.3.4" "@typescript-eslint/scope-manager@5.62.0": @@ -4780,13 +4780,13 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/scope-manager@8.19.0": - version "8.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz#28fa413a334f70e8b506a968531e0a7c9c3076dc" - integrity sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA== +"@typescript-eslint/scope-manager@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.18.1.tgz#52cedc3a8178d7464a70beffed3203678648e55b" + integrity sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ== dependencies: - "@typescript-eslint/types" "8.19.0" - "@typescript-eslint/visitor-keys" "8.19.0" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" "@typescript-eslint/type-utils@5.62.0": version "5.62.0" @@ -4798,13 +4798,13 @@ debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/type-utils@8.19.0": - version "8.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz#41abd7d2e4cf93b6854b1fe6cbf416fab5abf89f" - integrity sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg== +"@typescript-eslint/type-utils@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz#10f41285475c0bdee452b79ff7223f0e43a7781e" + integrity sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ== dependencies: - "@typescript-eslint/typescript-estree" "8.19.0" - "@typescript-eslint/utils" "8.19.0" + "@typescript-eslint/typescript-estree" "8.18.1" + "@typescript-eslint/utils" "8.18.1" debug "^4.3.4" ts-api-utils "^1.3.0" @@ -4813,10 +4813,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/types@8.19.0": - version "8.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.19.0.tgz#a190a25c5484a42b81eaad06989579fdeb478cbb" - integrity sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA== +"@typescript-eslint/types@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.18.1.tgz#d7f4f94d0bba9ebd088de840266fcd45408a8fff" + integrity sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw== "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" @@ -4831,13 +4831,13 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@8.19.0": - version "8.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz#6b4f48f98ffad6597379951b115710f4d68c9ccb" - integrity sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw== +"@typescript-eslint/typescript-estree@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.1.tgz#2a86cd64b211a742f78dfa7e6f4860413475367e" + integrity sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg== dependencies: - "@typescript-eslint/types" "8.19.0" - "@typescript-eslint/visitor-keys" "8.19.0" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/visitor-keys" "8.18.1" debug "^4.3.4" fast-glob "^3.3.2" is-glob "^4.0.3" @@ -4859,15 +4859,15 @@ eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/utils@8.19.0": - version "8.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.19.0.tgz#33824310e1fccc17f27fbd1030fd8bbd9a674684" - integrity sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg== +"@typescript-eslint/utils@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.18.1.tgz#c4199ea23fc823c736e2c96fd07b1f7235fa92d5" + integrity sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "8.19.0" - "@typescript-eslint/types" "8.19.0" - "@typescript-eslint/typescript-estree" "8.19.0" + "@typescript-eslint/scope-manager" "8.18.1" + "@typescript-eslint/types" "8.18.1" + "@typescript-eslint/typescript-estree" "8.18.1" "@typescript-eslint/visitor-keys@5.62.0": version "5.62.0" @@ -4877,12 +4877,12 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@8.19.0": - version "8.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz#dc313f735e64c4979c9073f51ffcefb6d9be5c77" - integrity sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w== +"@typescript-eslint/visitor-keys@8.18.1": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.1.tgz#344b4f6bc83f104f514676facf3129260df7610a" + integrity sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ== dependencies: - "@typescript-eslint/types" "8.19.0" + "@typescript-eslint/types" "8.18.1" eslint-visitor-keys "^4.2.0" "@typescript/vfs@^1.5.0": @@ -6202,9 +6202,9 @@ concurrently@^9.0.1: yargs "^17.7.2" consola@^3.2.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/consola/-/consola-3.3.3.tgz#0dd8a2314b0f7bf18a49064138ad685f3346543d" - integrity sha512-Qil5KwghMzlqd51UXM0b6fyaGHtOC22scxrwrz4A2882LyUMwQjnvaedN1HAeXzphspQ6CpHkzMAWxBTUruDLg== + version "3.3.1" + resolved "https://registry.yarnpkg.com/consola/-/consola-3.3.1.tgz#e60b4bfcd9e36cd766cc9afe2c93c14fe64c9356" + integrity sha512-GyKnPG3/I+a4RtJxgHquJXWr70g9I3c4NT3dvqh0LPHQP2nZFQBOBszb7a5u/pGzqr40AKplQA6UxM1BSynSXg== constant-case@^3.0.4: version "3.0.4" @@ -6963,9 +6963,9 @@ ejs@^3.1.10: jake "^10.8.5" electron-to-chromium@^1.5.73: - version "1.5.76" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz#db20295c5061b68f07c8ea4dfcbd701485d94a3d" - integrity sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ== + version "1.5.75" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.75.tgz#bba96eabf0e8ca36324679caa38b982800acc87d" + integrity sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q== element-resize-detector@^1.1.9: version "1.2.4" @@ -7058,9 +7058,9 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6: - version "1.23.8" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.8.tgz#99754723118355d82fcef9ce4c90ccbcd5d2a285" - integrity sha512-lfab8IzDn6EpI1ibZakcgS6WsfEBiB+43cuJo+wgylx1xKXf+Sp+YR3vFuQwC/u3sxYwV8Cxe3B0DpVUu/WiJQ== + version "1.23.7" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.7.tgz#36e3da46fdb0d2ae3b9df4235e3a3167c1605b36" + integrity sha512-OygGC8kIcDhXX+6yAZRGLqwi2CmEXCbLQixeGUgYeR+Qwlppqmo7DIDr8XibtEBZp+fJcoYpoatp5qwLMEdcqQ== dependencies: array-buffer-byte-length "^1.0.2" arraybuffer.prototype.slice "^1.0.4" @@ -7097,10 +7097,8 @@ es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23 object-inspect "^1.13.3" object-keys "^1.1.1" object.assign "^4.1.7" - own-keys "^1.0.0" regexp.prototype.flags "^1.5.3" safe-array-concat "^1.1.3" - safe-push-apply "^1.0.0" safe-regex-test "^1.1.0" string.prototype.trim "^1.2.10" string.prototype.trimend "^1.0.9" @@ -7297,11 +7295,11 @@ escape-string-regexp@^1.0.5: integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== eslint-config-next@^14.1.0: - version "14.2.22" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-14.2.22.tgz#c33cb4d1fc54673c8df475d6bca9f1f0249c69fe" - integrity sha512-4C26Xkqh5RWO9ieNOg7flfWsGiIfzblhXWQHUCa4wgswfjeFm4ku4M/Zc2IGBwA2BmrSn5kyJ8vt+JQg55g65Q== + version "14.2.21" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-14.2.21.tgz#2bc6869d5c3ddc267143d41cb4a4a2bff8817e58" + integrity sha512-bi1Mn6LxWdQod9qvOBuhBhN4ZpBYH5DuyDunbZt6lye3zlohJyM0T5/oFokRPNl2Mqt3/+uwHxr8XKOkPe852A== dependencies: - "@next/eslint-plugin-next" "14.2.22" + "@next/eslint-plugin-next" "14.2.21" "@rushstack/eslint-patch" "^1.3.3" "@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" "@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" @@ -10040,11 +10038,11 @@ next-themes@^0.2.1: integrity sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A== next@^14.2.20: - version "14.2.22" - resolved "https://registry.yarnpkg.com/next/-/next-14.2.22.tgz#0cd664916ef4c725f31fa812d870348cffd0115b" - integrity sha512-Ps2caobQ9hlEhscLPiPm3J3SYhfwfpMqzsoCMZGWxt9jBRK9hoBZj2A37i8joKhsyth2EuVKDVJCTF5/H4iEDw== + version "14.2.21" + resolved "https://registry.yarnpkg.com/next/-/next-14.2.21.tgz#f6da9e2abba1a0e4ca7a5273825daf06632554ba" + integrity sha512-rZmLwucLHr3/zfDMYbJXbw0ZeoBpirxkXuvsJbk7UPorvPYZhP7vq7aHbKnU7dQNCYIimRrbB2pp3xmf+wsYUg== dependencies: - "@next/env" "14.2.22" + "@next/env" "14.2.21" "@swc/helpers" "0.5.5" busboy "1.6.0" caniuse-lite "^1.0.30001579" @@ -10052,15 +10050,15 @@ next@^14.2.20: postcss "8.4.31" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-darwin-arm64" "14.2.22" - "@next/swc-darwin-x64" "14.2.22" - "@next/swc-linux-arm64-gnu" "14.2.22" - "@next/swc-linux-arm64-musl" "14.2.22" - "@next/swc-linux-x64-gnu" "14.2.22" - "@next/swc-linux-x64-musl" "14.2.22" - "@next/swc-win32-arm64-msvc" "14.2.22" - "@next/swc-win32-ia32-msvc" "14.2.22" - "@next/swc-win32-x64-msvc" "14.2.22" + "@next/swc-darwin-arm64" "14.2.21" + "@next/swc-darwin-x64" "14.2.21" + "@next/swc-linux-arm64-gnu" "14.2.21" + "@next/swc-linux-arm64-musl" "14.2.21" + "@next/swc-linux-x64-gnu" "14.2.21" + "@next/swc-linux-x64-musl" "14.2.21" + "@next/swc-win32-arm64-msvc" "14.2.21" + "@next/swc-win32-ia32-msvc" "14.2.21" + "@next/swc-win32-x64-msvc" "14.2.21" no-case@^3.0.4: version "3.0.4" @@ -10369,15 +10367,6 @@ overlap-area@^1.1.0: dependencies: "@daybrush/utils" "^1.7.1" -own-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" - integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== - dependencies: - get-intrinsic "^1.2.6" - object-keys "^1.1.1" - safe-push-apply "^1.0.0" - p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -10927,9 +10916,9 @@ postgres@^3.4.4: integrity sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg== posthog-js@^1.131.3: - version "1.203.2" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.203.2.tgz#ccbdb50f906ab10124cd8a9d31bd5ab2df542184" - integrity sha512-3aLpEhM4i9sQQtobRmDttJ3rTW1+gwQ9HL7QiOeDueE2T7CguYibYS7weY1UhXMerx5lh1A7+szlOJTTibifLQ== + version "1.203.1" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.203.1.tgz#e1ce0ac65227d18b615f727ea4ec593694c6a33f" + integrity sha512-r/WiSyz6VNbIKEV/30+aD5gdrYkFtmZwvqNa6h9frl8hG638v098FrXaq3EYzMcCdkQf3phaZTDIAFKegpiTjw== dependencies: core-js "^3.38.1" fflate "^0.4.8" @@ -10937,9 +10926,9 @@ posthog-js@^1.131.3: web-vitals "^4.2.0" preact@^10.19.3: - version "10.25.4" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.25.4.tgz#c1d00bee9d7b9dcd06a2311d9951973b506ae8ac" - integrity sha512-jLdZDb+Q+odkHJ+MpW/9U5cODzqnB+fy2EiHSZES7ldV5LK7yjlVzTp7R8Xy6W6y75kfK8iWYtFVH7lvjwrCMA== + version "10.25.3" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.25.3.tgz#22dfb072b088dda9a2bc6d4ca41bf46b588d325e" + integrity sha512-dzQmIFtM970z+fP9ziQ3yG4e3ULIbwZzJ734vaMVUTaKQ2+Ru1Ou/gjshOYVHCcd1rpAelC6ngjvjDXph98unQ== prebuild-install@^7.1.1: version "7.1.2" @@ -12020,14 +12009,6 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-push-apply@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" - integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== - dependencies: - es-errors "^1.3.0" - isarray "^2.0.5" - safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" @@ -12979,17 +12960,17 @@ tiptap-markdown@^0.8.9: markdown-it-task-lists "^2.1.1" prosemirror-markdown "^1.11.1" -tldts-core@^6.1.70: - version "6.1.70" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.70.tgz#a954e93237ece2e1705b438600793c86a25f8c00" - integrity sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg== +tldts-core@^6.1.69: + version "6.1.69" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.69.tgz#079ffcac8a4407bc74567e292aecf30b943674e1" + integrity sha512-nygxy9n2PBUFQUtAXAc122gGo+04/j5qr5TGQFZTHafTKYvmARVXt2cA5rgero2/dnXUfkdPtiJoKmrd3T+wdA== tldts@^6.1.32: - version "6.1.70" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.70.tgz#b571e5645ab9dc6f289453115d52602b8a384cfe" - integrity sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA== + version "6.1.69" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.69.tgz#0fe1fcb1ad09510459693e72f96062cee2411f1f" + integrity sha512-Oh/CqRQ1NXNY7cy9NkTPUauOWiTro0jEYZTioGbOmcQh6EC45oribyIMJp0OJO3677r13tO6SKdWoGZUx2BDFw== dependencies: - tldts-core "^6.1.70" + tldts-core "^6.1.69" tmp@^0.0.33: version "0.0.33"